]> git.sur5r.net Git - cc65/blob - src/ca65/pseudo.c
Don't use SF_TRAMPOLINE, change symbol references instead.
[cc65] / src / ca65 / pseudo.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 pseudo.c                                  */
4 /*                                                                           */
5 /*              Pseudo instructions for the ca65 macroassembler              */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2003 Ullrich von Bassewitz                                       */
10 /*               Römerstraße 52                                              */
11 /*               D-70794 Filderstadt                                         */
12 /* EMail:        uz@cc65.org                                                 */
13 /*                                                                           */
14 /*                                                                           */
15 /* This software is provided 'as-is', without any expressed or implied       */
16 /* warranty.  In no event will the authors be held liable for any damages    */
17 /* arising from the use of this software.                                    */
18 /*                                                                           */
19 /* Permission is granted to anyone to use this software for any purpose,     */
20 /* including commercial applications, and to alter it and redistribute it    */
21 /* freely, subject to the following restrictions:                            */
22 /*                                                                           */
23 /* 1. The origin of this software must not be misrepresented; you must not   */
24 /*    claim that you wrote the original software. If you use this software   */
25 /*    in a product, an acknowledgment in the product documentation would be  */
26 /*    appreciated but is not required.                                       */
27 /* 2. Altered source versions must be plainly marked as such, and must not   */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or altered from any source              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <ctype.h>
40 #include <errno.h>
41
42 /* common */
43 #include "assertdefs.h"
44 #include "bitops.h"
45 #include "cddefs.h"
46 #include "coll.h"
47 #include "symdefs.h"
48 #include "tgttrans.h"
49 #include "xmalloc.h"
50
51 /* ca65 */
52 #include "anonname.h"
53 #include "asserts.h"
54 #include "condasm.h"
55 #include "dbginfo.h"
56 #include "enum.h"
57 #include "error.h"
58 #include "expr.h"
59 #include "feature.h"
60 #include "global.h"
61 #include "incpath.h"
62 #include "instr.h"
63 #include "listing.h"
64 #include "macpack.h"
65 #include "macro.h"
66 #include "nexttok.h"
67 #include "objcode.h"
68 #include "options.h"
69 #include "pseudo.h"
70 #include "repeat.h"
71 #include "segment.h"
72 #include "spool.h"
73 #include "struct.h"
74 #include "symbol.h"
75 #include "symtab.h"
76
77
78
79 /*****************************************************************************/
80 /*                                   Data                                    */
81 /*****************************************************************************/
82
83
84
85 /* Keyword we're about to handle */
86 static char Keyword [sizeof (SVal)+1];
87
88 /* Segment stack */
89 #define MAX_PUSHED_SEGMENTS     16
90 static Collection SegStack = STATIC_COLLECTION_INITIALIZER;
91
92
93
94 /*****************************************************************************/
95 /*                               Forwards                                    */
96 /*****************************************************************************/
97
98
99
100 static void DoUnexpected (void);
101 /* Got an unexpected keyword */
102
103 static void DoInvalid (void);
104 /* Handle a token that is invalid here, since it should have been handled on
105  * a much lower level of the expression hierarchy. Getting this sort of token
106  * means that the lower level code has bugs.
107  * This function differs to DoUnexpected in that the latter may be triggered
108  * by the user by using keywords in the wrong location. DoUnexpected is not
109  * an error in the assembler itself, while DoInvalid is.
110  */
111
112
113
114 /*****************************************************************************/
115 /*                              Helper functions                             */
116 /*****************************************************************************/
117
118
119
120 static unsigned char OptionalAddrSize (void)
121 /* If a colon follows, parse an optional address size spec and return it.
122  * Otherwise return ADDR_SIZE_DEFAULT.
123  */
124 {
125     unsigned AddrSize = ADDR_SIZE_DEFAULT;
126     if (Tok == TOK_COLON) {
127         NextTok ();
128         AddrSize = ParseAddrSize ();
129         NextTok ();
130     }
131     return AddrSize;
132 }
133
134
135
136 static void SetBoolOption (unsigned char* Flag)
137 /* Read a on/off/+/- option and set flag accordingly */
138 {
139     static const char* Keys[] = {
140         "OFF",
141         "ON",
142     };
143
144     if (Tok == TOK_PLUS) {
145         *Flag = 1;
146         NextTok ();
147     } else if (Tok == TOK_MINUS) {
148         *Flag = 0;
149         NextTok ();
150     } else if (Tok == TOK_IDENT) {
151         /* Map the keyword to a number */
152         switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
153             case 0:     *Flag = 0; NextTok ();                  break;
154             case 1:     *Flag = 1; NextTok ();                  break;
155             default:    ErrorSkip ("`on' or `off' expected");   break;
156         }
157     } else if (TokIsSep (Tok)) {
158         /* Without anything assume switch on */
159         *Flag = 1;
160     } else {
161         ErrorSkip ("`on' or `off' expected");
162     }
163 }
164
165
166
167 static void ExportImport (void (*Func) (SymEntry*, unsigned char, unsigned),
168                           unsigned char DefAddrSize, unsigned Flags)
169 /* Export or import symbols */
170 {
171     SymEntry* Sym;
172     unsigned char AddrSize;
173
174     while (1) {
175
176         /* We need an identifier here */
177         if (Tok != TOK_IDENT) {
178             ErrorSkip ("Identifier expected");
179             return;
180         }
181
182         /* Find the symbol table entry, allocate a new one if necessary */
183         Sym = SymFind (CurrentScope, SVal, SYM_ALLOC_NEW);
184
185         /* Skip the name */
186         NextTok ();
187
188         /* Get an optional address size */
189         AddrSize = OptionalAddrSize ();
190         if (AddrSize == ADDR_SIZE_DEFAULT) {
191             AddrSize = DefAddrSize;
192         }
193
194         /* Call the actual import/export function */
195         Func (Sym, AddrSize, Flags);
196
197         /* More symbols? */
198         if (Tok == TOK_COMMA) {
199             NextTok ();
200         } else {
201             break;
202         }
203     }
204 }
205
206
207
208 static long IntArg (long Min, long Max)
209 /* Read an integer argument and check a range. Accept the token "unlimited"
210  * and return -1 in this case.
211  */
212 {
213     if (Tok == TOK_IDENT && strcmp (SVal, "unlimited") == 0) {
214         NextTok ();
215         return -1;
216     } else {
217         long Val = ConstExpression ();
218         if (Val < Min || Val > Max) {
219             Error ("Range error");
220             Val = Min;
221         }
222         return Val;
223     }
224 }
225
226
227
228 static void ConDes (const char* Name, unsigned Type)
229 /* Parse remaining line for constructor/destructor of the remaining type */
230 {
231     long Prio;
232
233
234     /* Find the symbol table entry, allocate a new one if necessary */
235     SymEntry* Sym = SymFind (CurrentScope, Name, SYM_ALLOC_NEW);
236
237     /* Optional constructor priority */
238     if (Tok == TOK_COMMA) {
239         /* Priority value follows */
240         NextTok ();
241         Prio = ConstExpression ();
242         if (Prio < CD_PRIO_MIN || Prio > CD_PRIO_MAX) {
243             /* Value out of range */
244             Error ("Range error");
245             return;
246         }
247     } else {
248         /* Use the default priority value */
249         Prio = CD_PRIO_DEF;
250     }
251
252     /* Define the symbol */
253     SymConDes (Sym, ADDR_SIZE_DEFAULT, Type, (unsigned) Prio);
254 }
255
256
257
258 /*****************************************************************************/
259 /*                             Handler functions                             */
260 /*****************************************************************************/
261
262
263
264 static void DoA16 (void)
265 /* Switch the accu to 16 bit mode (assembler only) */
266 {
267     if (GetCPU() != CPU_65816) {
268         Error ("Command is only valid in 65816 mode");
269     } else {
270         /* Immidiate mode has two extension bytes */
271         ExtBytes [AMI_IMM_ACCU] = 2;
272     }
273 }
274
275
276
277 static void DoA8 (void)
278 /* Switch the accu to 8 bit mode (assembler only) */
279 {
280     if (GetCPU() != CPU_65816) {
281         Error ("Command is only valid in 65816 mode");
282     } else {
283         /* Immidiate mode has one extension byte */
284         ExtBytes [AMI_IMM_ACCU] = 1;
285     }
286 }
287
288
289
290 static void DoAddr (void)
291 /* Define addresses */
292 {
293     while (1) {
294         if (GetCPU() == CPU_65816) {
295             EmitWord (GenWordExpr (Expression ()));
296         } else {
297             /* Do a range check */
298             EmitWord (Expression ());
299         }
300         if (Tok != TOK_COMMA) {
301             break;
302         } else {
303             NextTok ();
304         }
305     }
306 }
307
308
309
310 static void DoAlign (void)
311 /* Align the PC to some boundary */
312 {
313     long Val;
314     long Align;
315     unsigned Bit;
316
317     /* Read the alignment value */
318     Align = ConstExpression ();
319     if (Align <= 0 || Align > 0x10000) {
320         ErrorSkip ("Range error");
321         return;
322     }
323
324     /* Optional value follows */
325     if (Tok == TOK_COMMA) {
326         NextTok ();
327         Val = ConstExpression ();
328         /* We need a byte value here */
329         if (!IsByteRange (Val)) {
330             ErrorSkip ("Range error");
331             return;
332         }
333     } else {
334         Val = -1;
335     }
336
337     /* Check if the alignment is a power of two */
338     Bit = BitFind (Align);
339     if (Align != (0x01L << Bit)) {
340         Error ("Alignment value must be a power of 2");
341     } else {
342         SegAlign (Bit, (int) Val);
343     }
344 }
345
346
347
348 static void DoASCIIZ (void)
349 /* Define text with a zero terminator */
350 {
351     unsigned Len;
352
353     while (1) {
354         /* Must have a string constant */
355         if (Tok != TOK_STRCON) {
356             ErrorSkip ("String constant expected");
357             return;
358         }
359
360         /* Get the length of the string constant */
361         Len = strlen (SVal);
362
363         /* Translate into target charset and emit */
364         TgtTranslateBuf (SVal, Len);
365         EmitData ((unsigned char*) SVal, Len);
366         NextTok ();
367         if (Tok == TOK_COMMA) {
368             NextTok ();
369         } else {
370             break;
371         }
372     }
373     Emit0 (0);
374 }
375
376
377
378 static void DoAssert (void)
379 /* Add an assertion */
380 {
381     static const char* ActionTab [] = {
382         "WARN", "WARNING",
383         "ERROR"
384     };
385
386     int Action;
387
388
389     /* First we have the expression that has to evaluated */
390     ExprNode* Expr = Expression ();
391     ConsumeComma ();
392
393     /* Action follows */
394     if (Tok != TOK_IDENT) {
395         ErrorSkip ("Identifier expected");
396         return;
397     }
398     Action = GetSubKey (ActionTab, sizeof (ActionTab) / sizeof (ActionTab[0]));
399     switch (Action) {
400
401         case 0:
402         case 1:
403             /* Warning */
404             Action = ASSERT_ACT_WARN;
405             break;
406
407         case 2:
408             /* Error */
409             Action = ASSERT_ACT_ERROR;
410             break;
411
412         default:
413             Error ("Illegal assert action specifier");
414     }
415     NextTok ();
416     ConsumeComma ();
417
418     /* Read the message */
419     if (Tok != TOK_STRCON) {
420         ErrorSkip ("String constant expected");
421     } else {
422         AddAssertion (Expr, Action, GetStringId (SVal));
423         NextTok ();
424     }
425 }
426
427
428
429 static void DoAutoImport (void)
430 /* Mark unresolved symbols as imported */
431 {
432     SetBoolOption (&AutoImport);
433 }
434
435
436
437 static void DoBss (void)
438 /* Switch to the BSS segment */
439 {
440     UseSeg (&BssSegDef);
441 }
442
443
444
445 static void DoByte (void)
446 /* Define bytes */
447 {
448     while (1) {
449         if (Tok == TOK_STRCON) {
450             /* A string, translate into target charset and emit */
451             unsigned Len = strlen (SVal);
452             TgtTranslateBuf (SVal, Len);
453             EmitData ((unsigned char*) SVal, Len);
454             NextTok ();
455         } else {
456             EmitByte (Expression ());
457         }
458         if (Tok != TOK_COMMA) {
459             break;
460         } else {
461             NextTok ();
462             /* Do smart handling of dangling comma */
463             if (Tok == TOK_SEP) {
464                 Error ("Unexpected end of line");
465                 break;
466             }
467         }
468     }
469 }
470
471
472
473 static void DoCase (void)
474 /* Switch the IgnoreCase option */
475 {
476     SetBoolOption (&IgnoreCase);
477     IgnoreCase = !IgnoreCase;
478 }
479
480
481
482 static void DoCharMap (void)
483 /* Allow custome character mappings */
484 {
485     long Index;
486     long Code;
487
488     /* Read the index as numerical value */
489     Index = ConstExpression ();
490     if (Index < 1 || Index > 255) {
491         /* Value out of range */
492         ErrorSkip ("Range error");
493         return;
494     }
495
496     /* Comma follows */
497     ConsumeComma ();
498
499     /* Read the character code */
500     Code = ConstExpression ();
501     if (Code < 1 || Code > 255) {
502         /* Value out of range */
503         ErrorSkip ("Range error");
504         return;
505     }
506
507     /* Set the character translation */
508     TgtTranslateSet ((unsigned) Index, (unsigned char) Code);
509 }
510
511
512
513 static void DoCode (void)
514 /* Switch to the code segment */
515 {
516     UseSeg (&CodeSegDef);
517 }
518
519
520
521 static void DoConDes (void)
522 /* Export a symbol as constructor/destructor */
523 {
524     static const char* Keys[] = {
525         "CONSTRUCTOR",
526         "DESTRUCTOR",
527     };
528     char Name [sizeof (SVal)];
529     long Type;
530
531     /* Symbol name follows */
532     if (Tok != TOK_IDENT) {
533         ErrorSkip ("Identifier expected");
534         return;
535     }
536     strcpy (Name, SVal);
537     NextTok ();
538
539     /* Type follows. May be encoded as identifier or numerical */
540     ConsumeComma ();
541     if (Tok == TOK_IDENT) {
542
543         /* Map the following keyword to a number, then skip it */
544         Type = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
545         NextTok ();
546
547         /* Check if we got a valid keyword */
548         if (Type < 0) {
549             Error ("Syntax error");
550             SkipUntilSep ();
551             return;
552         }
553
554     } else {
555
556         /* Read the type as numerical value */
557         Type = ConstExpression ();
558         if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) {
559             /* Value out of range */
560             Error ("Range error");
561             return;
562         }
563
564     }
565
566     /* Parse the remainder of the line and export the symbol */
567     ConDes (Name, (unsigned) Type);
568 }
569
570
571
572 static void DoConstructor (void)
573 /* Export a symbol as constructor */
574 {
575     char Name [sizeof (SVal)];
576
577     /* Symbol name follows */
578     if (Tok != TOK_IDENT) {
579         ErrorSkip ("Identifier expected");
580         return;
581     }
582     strcpy (Name, SVal);
583     NextTok ();
584
585     /* Parse the remainder of the line and export the symbol */
586     ConDes (Name, CD_TYPE_CON);
587 }
588
589
590
591 static void DoData (void)
592 /* Switch to the data segment */
593 {
594     UseSeg (&DataSegDef);
595 }
596
597
598
599 static void DoDbg (void)
600 /* Add debug information from high level code */
601 {
602     static const char* Keys[] = {
603         "FILE",
604         "LINE",
605         "SYM",
606     };
607     int Key;
608
609
610     /* We expect a subkey */
611     if (Tok != TOK_IDENT) {
612         ErrorSkip ("Identifier expected");
613         return;
614     }
615
616     /* Map the following keyword to a number */
617     Key = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
618
619     /* Skip the subkey */
620     NextTok ();
621
622     /* Check the key and dispatch to a handler */
623     switch (Key) {
624         case 0:     DbgInfoFile ();             break;
625         case 1:     DbgInfoLine ();             break;
626         case 2:     DbgInfoSym ();              break;
627         default:    ErrorSkip ("Syntax error"); break;
628     }
629 }
630
631
632
633 static void DoDByt (void)
634 /* Output double bytes */
635 {
636     while (1) {
637         EmitWord (GenSwapExpr (Expression ()));
638         if (Tok != TOK_COMMA) {
639             break;
640         } else {
641             NextTok ();
642         }
643     }
644 }
645
646
647
648 static void DoDebugInfo (void)
649 /* Switch debug info on or off */
650 {
651     SetBoolOption (&DbgSyms);
652 }
653
654
655
656 static void DoDefine (void)
657 /* Define a one line macro */
658 {
659     MacDef (MAC_STYLE_DEFINE);
660 }
661
662
663
664 static void DoDestructor (void)
665 /* Export a symbol as destructor */
666 {
667     char Name [sizeof (SVal)];
668
669     /* Symbol name follows */
670     if (Tok != TOK_IDENT) {
671         ErrorSkip ("Identifier expected");
672         return;
673     }
674     strcpy (Name, SVal);
675     NextTok ();
676
677     /* Parse the remainder of the line and export the symbol */
678     ConDes (Name, CD_TYPE_DES);
679 }
680
681
682
683 static void DoDWord (void)
684 /* Define dwords */
685 {
686     while (1) {
687         EmitDWord (Expression ());
688         if (Tok != TOK_COMMA) {
689             break;
690         } else {
691             NextTok ();
692         }
693     }
694 }
695
696
697
698 static void DoEnd (void)
699 /* End of assembly */
700 {
701     ForcedEnd = 1;
702     NextTok ();
703 }
704
705
706
707 static void DoEndProc (void)
708 /* Leave a lexical level */
709 {
710     if (CurrentScope == RootScope || GetCurrentSymTabType () != ST_PROC) {
711         /* No local scope */
712         ErrorSkip ("No open .PROC");
713     } else {
714         SymLeaveLevel ();
715     }
716 }
717
718
719
720 static void DoEndScope (void)
721 /* Leave a lexical level */
722 {
723     if (CurrentScope == RootScope || GetCurrentSymTabType () != ST_SCOPE) {
724         /* No local scope */
725         ErrorSkip ("No open .SCOPE");
726     } else {
727         SymLeaveLevel ();
728     }
729 }
730
731
732
733 static void DoError (void)
734 /* User error */
735 {
736     if (Tok != TOK_STRCON) {
737         ErrorSkip ("String constant expected");
738     } else {
739         Error ("User error: %s", SVal);
740         SkipUntilSep ();
741     }
742 }
743
744
745
746 static void DoExitMacro (void)
747 /* Exit a macro expansion */
748 {
749     if (!InMacExpansion ()) {
750         /* We aren't expanding a macro currently */
751         DoUnexpected ();
752     } else {
753         MacAbort ();
754     }
755 }
756
757
758
759 static void DoExport (void)
760 /* Export a symbol */
761 {
762     ExportImport (SymExport, ADDR_SIZE_DEFAULT, SF_NONE);
763 }
764
765
766
767 static void DoExportZP (void)
768 /* Export a zeropage symbol */
769 {
770     ExportImport (SymExport, ADDR_SIZE_ZP, SF_NONE);
771 }
772
773
774
775 static void DoFarAddr (void)
776 /* Define far addresses (24 bit) */
777 {
778     while (1) {
779         EmitFarAddr (Expression ());
780         if (Tok != TOK_COMMA) {
781             break;
782         } else {
783             NextTok ();
784         }
785     }
786 }
787
788
789
790 static void DoFeature (void)
791 /* Switch the Feature option */
792 {
793     /* Allow a list of comma separated keywords */
794     while (1) {
795
796         /* We expect an identifier */
797         if (Tok != TOK_IDENT) {
798             ErrorSkip ("Identifier expected");
799             return;
800         }
801
802         /* Make the string attribute lower case */
803         LocaseSVal ();
804
805         /* Set the feature and check for errors */
806         if (SetFeature (SVal) == FEAT_UNKNOWN) {
807             /* Not found */
808             ErrorSkip ("Invalid feature: `%s'", SVal);
809             return;
810         } else {
811             /* Skip the keyword */
812             NextTok ();
813         }
814
815         /* Allow more than one keyword */
816         if (Tok == TOK_COMMA) {
817             NextTok ();
818         } else {
819             break;
820         }
821     }
822 }
823
824
825
826 static void DoFileOpt (void)
827 /* Insert a file option */
828 {
829     long OptNum;
830
831     /* The option type may be given as a keyword or as a number. */
832     if (Tok == TOK_IDENT) {
833
834         /* Option given as keyword */
835         static const char* Keys [] = {
836             "AUTHOR", "COMMENT", "COMPILER"
837         };
838
839         /* Map the option to a number */
840         OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
841         if (OptNum < 0) {
842             /* Not found */
843             ErrorSkip ("File option keyword expected");
844             return;
845         }
846
847         /* Skip the keyword */
848         NextTok ();
849
850         /* Must be followed by a comma */
851         ConsumeComma ();
852
853         /* We accept only string options for now */
854         if (Tok != TOK_STRCON) {
855             ErrorSkip ("String constant expected");
856             return;
857         }
858
859         /* Insert the option */
860         switch (OptNum) {
861
862             case 0:
863                 /* Author */
864                 OptAuthor (SVal);
865                 break;
866
867             case 1:
868                 /* Comment */
869                 OptComment (SVal);
870                 break;
871
872             case 2:
873                 /* Compiler */
874                 OptCompiler (SVal);
875                 break;
876
877             default:
878                 Internal ("Invalid OptNum: %ld", OptNum);
879
880         }
881
882         /* Done */
883         NextTok ();
884
885     } else {
886
887         /* Option given as number */
888         OptNum = ConstExpression ();
889         if (!IsByteRange (OptNum)) {
890             ErrorSkip ("Range error");
891             return;
892         }
893
894         /* Must be followed by a comma */
895         ConsumeComma ();
896
897         /* We accept only string options for now */
898         if (Tok != TOK_STRCON) {
899             ErrorSkip ("String constant expected");
900             return;
901         }
902
903         /* Insert the option */
904         OptStr ((unsigned char) OptNum, SVal);
905
906         /* Done */
907         NextTok ();
908     }
909 }
910
911
912
913 static void DoForceImport (void)
914 /* Do a forced import on a symbol */
915 {
916     ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_FORCED);
917 }
918
919
920
921 static void DoGlobal (void)
922 /* Declare a global symbol */
923 {
924     ExportImport (SymGlobal, ADDR_SIZE_DEFAULT, SF_NONE);
925 }
926
927
928
929 static void DoGlobalZP (void)
930 /* Declare a global zeropage symbol */
931 {
932     ExportImport (SymGlobal, ADDR_SIZE_ZP, SF_NONE);
933 }
934
935
936
937 static void DoI16 (void)
938 /* Switch the index registers to 16 bit mode (assembler only) */
939 {
940     if (GetCPU() != CPU_65816) {
941         Error ("Command is only valid in 65816 mode");
942     } else {
943         /* Immidiate mode has two extension bytes */
944         ExtBytes [AMI_IMM_INDEX] = 2;
945     }
946 }
947
948
949
950 static void DoI8 (void)
951 /* Switch the index registers to 16 bit mode (assembler only) */
952 {
953     if (GetCPU() != CPU_65816) {
954         Error ("Command is only valid in 65816 mode");
955     } else {
956         /* Immidiate mode has one extension byte */
957         ExtBytes [AMI_IMM_INDEX] = 1;
958     }
959 }
960
961
962
963 static void DoImport (void)
964 /* Import a symbol */
965 {
966     ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_NONE);
967 }
968
969
970
971 static void DoImportZP (void)
972 /* Import a zero page symbol */
973 {
974     ExportImport (SymImport, ADDR_SIZE_ZP, SF_NONE);
975 }
976
977
978
979 static void DoIncBin (void)
980 /* Include a binary file */
981 {
982     char Name [sizeof (SVal)];
983     long Start = 0L;
984     long Count = -1L;
985     long Size;
986     FILE* F;
987
988     /* Name must follow */
989     if (Tok != TOK_STRCON) {
990         ErrorSkip ("String constant expected");
991         return;
992     }
993     strcpy (Name, SVal);
994     NextTok ();
995
996     /* A starting offset may follow */
997     if (Tok == TOK_COMMA) {
998         NextTok ();
999         Start = ConstExpression ();
1000
1001         /* And a length may follow */
1002         if (Tok == TOK_COMMA) {
1003             NextTok ();
1004             Count = ConstExpression ();
1005         }
1006
1007     }
1008
1009     /* Try to open the file */
1010     F = fopen (Name, "rb");
1011     if (F == 0) {
1012
1013         /* Search for the file in the include directories. */
1014         char* PathName = FindInclude (Name);
1015         if (PathName == 0 || (F = fopen (PathName, "r")) == 0) {
1016             /* Not found or cannot open, print an error and bail out */
1017             ErrorSkip ("Cannot open include file `%s': %s", Name, strerror (errno));
1018         }
1019
1020         /* Free the allocated memory */
1021         xfree (PathName);
1022
1023         /* If we had an error before, bail out now */
1024         if (F == 0) {
1025             return;
1026         }
1027     }
1028
1029     /* Get the size of the file */
1030     fseek (F, 0, SEEK_END);
1031     Size = ftell (F);
1032
1033     /* If a count was not given, calculate it now */
1034     if (Count < 0) {
1035         Count = Size - Start;
1036         if (Count < 0) {
1037             /* Nothing to read - flag this as a range error */
1038             ErrorSkip ("Range error");
1039             goto Done;
1040         }
1041     } else {
1042         /* Count was given, check if it is valid */
1043         if (Start + Count > Size) {
1044             ErrorSkip ("Range error");
1045             goto Done;
1046         }
1047     }
1048
1049     /* Seek to the start position */
1050     fseek (F, Start, SEEK_SET);
1051
1052     /* Read chunks and insert them into the output */
1053     while (Count > 0) {
1054
1055         unsigned char Buf [1024];
1056
1057         /* Calculate the number of bytes to read */
1058         size_t BytesToRead = (Count > (long)sizeof(Buf))? sizeof(Buf) : (size_t) Count;
1059
1060         /* Read chunk */
1061         size_t BytesRead = fread (Buf, 1, BytesToRead, F);
1062         if (BytesToRead != BytesRead) {
1063             /* Some sort of error */
1064             ErrorSkip ("Cannot read from include file `%s': %s",
1065                        Name, strerror (errno));
1066             break;
1067         }
1068
1069         /* Insert it into the output */
1070         EmitData (Buf, BytesRead);
1071
1072         /* Keep the counters current */
1073         Count -= BytesRead;
1074     }
1075
1076 Done:
1077     /* Close the file, ignore errors since it's r/o */
1078     (void) fclose (F);
1079 }
1080
1081
1082
1083 static void DoInclude (void)
1084 /* Include another file */
1085 {
1086     char Name [MAX_STR_LEN+1];
1087
1088     /* Name must follow */
1089     if (Tok != TOK_STRCON) {
1090         ErrorSkip ("String constant expected");
1091     } else {
1092         strcpy (Name, SVal);
1093         NextTok ();
1094         NewInputFile (Name);
1095     }
1096 }
1097
1098
1099
1100 static void DoInvalid (void)
1101 /* Handle a token that is invalid here, since it should have been handled on
1102  * a much lower level of the expression hierarchy. Getting this sort of token
1103  * means that the lower level code has bugs.
1104  * This function differs to DoUnexpected in that the latter may be triggered
1105  * by the user by using keywords in the wrong location. DoUnexpected is not
1106  * an error in the assembler itself, while DoInvalid is.
1107  */
1108 {
1109     Internal ("Unexpected token: %s", Keyword);
1110 }
1111
1112
1113
1114 static void DoLineCont (void)
1115 /* Switch the use of line continuations */
1116 {
1117     SetBoolOption (&LineCont);
1118 }
1119
1120
1121
1122 static void DoList (void)
1123 /* Enable/disable the listing */
1124 {
1125     /* Get the setting */
1126     unsigned char List;
1127     SetBoolOption (&List);
1128
1129     /* Manage the counter */
1130     if (List) {
1131         EnableListing ();
1132     } else {
1133         DisableListing ();
1134     }
1135 }
1136
1137
1138
1139 static void DoListBytes (void)
1140 /* Set maximum number of bytes to list for one line */
1141 {
1142     SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
1143 }
1144
1145
1146
1147 static void DoLocalChar (void)
1148 /* Define the character that starts local labels */
1149 {
1150     if (Tok != TOK_CHARCON) {
1151         ErrorSkip ("Character constant expected");
1152     } else {
1153         if (IVal != '@' && IVal != '?') {
1154             Error ("Invalid start character for locals");
1155         } else {
1156             LocalStart = (char) IVal;
1157         }
1158         NextTok ();
1159     }
1160 }
1161
1162
1163
1164 static void DoMacPack (void)
1165 /* Insert a macro package */
1166 {
1167     /* Macro package names */
1168     static const char* Keys [] = {
1169         "GENERIC",
1170         "LONGBRANCH",
1171         "CBM",
1172         "CPU"
1173     };
1174
1175     int Package;
1176
1177     /* We expect an identifier */
1178     if (Tok != TOK_IDENT) {
1179         ErrorSkip ("Identifier expected");
1180         return;
1181     }
1182
1183     /* Map the keyword to a number */
1184     Package = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
1185     if (Package < 0) {
1186         /* Not found */
1187         ErrorSkip ("Invalid macro package");
1188         return;
1189     }
1190
1191     /* Skip the package name */
1192     NextTok ();
1193
1194     /* Insert the package */
1195     InsertMacPack (Package);
1196 }
1197
1198
1199
1200 static void DoMacro (void)
1201 /* Start a macro definition */
1202 {
1203     MacDef (MAC_STYLE_CLASSIC);
1204 }
1205
1206
1207
1208 static void DoNull (void)
1209 /* Switch to the NULL segment */
1210 {
1211     UseSeg (&NullSegDef);
1212 }
1213
1214
1215
1216 static void DoOrg (void)
1217 /* Start absolute code */
1218 {
1219     long PC = ConstExpression ();
1220     if (PC < 0 || PC > 0xFFFFFF) {
1221         Error ("Range error");
1222         return;
1223     }
1224     SetAbsPC (PC);
1225 }
1226
1227
1228
1229 static void DoOut (void)
1230 /* Output a string */
1231 {
1232     if (Tok != TOK_STRCON) {
1233         ErrorSkip ("String constant expected");
1234     } else {
1235         /* Output the string and be sure to flush the output to keep it in
1236          * sync with any error messages if the output is redirected to a file.
1237          */
1238         printf ("%s\n", SVal);
1239         fflush (stdout);
1240         NextTok ();
1241     }
1242 }
1243
1244
1245
1246 static void DoP02 (void)
1247 /* Switch to 6502 CPU */
1248 {
1249     SetCPU (CPU_6502);
1250 }
1251
1252
1253
1254 static void DoPC02 (void)
1255 /* Switch to 65C02 CPU */
1256 {
1257     SetCPU (CPU_65C02);
1258 }
1259
1260
1261
1262 static void DoP816 (void)
1263 /* Switch to 65816 CPU */
1264 {
1265     SetCPU (CPU_65816);
1266 }
1267
1268
1269
1270 static void DoPageLength (void)
1271 /* Set the page length for the listing */
1272 {
1273     PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
1274 }
1275
1276
1277
1278 static void DoPopSeg (void)
1279 /* Pop an old segment from the segment stack */
1280 {
1281     SegDef* Def;
1282
1283     /* Must have a segment on the stack */
1284     if (CollCount (&SegStack) == 0) {
1285         ErrorSkip ("Segment stack is empty");
1286         return;
1287     }
1288
1289     /* Pop the last element */
1290     Def = CollPop (&SegStack);
1291
1292     /* Restore this segment */
1293     UseSeg (Def);
1294
1295     /* Delete the segment definition */
1296     FreeSegDef (Def);
1297 }
1298
1299
1300
1301 static void DoProc (void)
1302 /* Start a new lexical scope */
1303 {
1304     char Name[sizeof(SVal)];
1305     unsigned char AddrSize;
1306
1307     if (Tok == TOK_IDENT) {
1308
1309         /* The new scope has a name. Remember it. */
1310         strcpy (Name, SVal);
1311
1312         /* Search for the symbol, generate a new one if needed */
1313         SymEntry* Sym = SymFind (CurrentScope, Name, SYM_ALLOC_NEW);
1314
1315         /* Skip the scope name */
1316         NextTok ();
1317
1318         /* Read an optional address size specifier */
1319         AddrSize = OptionalAddrSize ();
1320
1321         /* Mark the symbol as defined */
1322         SymDef (Sym, GenCurrentPC (), AddrSize, SF_LABEL);
1323
1324     } else {
1325
1326         /* A .PROC statement without a name */
1327         Warning (1, "Unnamed .PROCs are deprecated, please use .SCOPE");
1328         AnonName (Name, sizeof (Name), "PROC");
1329         AddrSize = ADDR_SIZE_DEFAULT;
1330
1331     }
1332
1333     /* Enter a new scope */
1334     SymEnterLevel (Name, ST_PROC, AddrSize);
1335 }
1336
1337
1338
1339 static void DoPSC02 (void)
1340 /* Switch to 65SC02 CPU */
1341 {
1342     SetCPU (CPU_65SC02);
1343 }
1344
1345
1346
1347 static void DoPushSeg (void)
1348 /* Push the current segment onto the segment stack */
1349 {
1350     /* Can only push a limited size of segments */
1351     if (CollCount (&SegStack) >= MAX_PUSHED_SEGMENTS) {
1352         ErrorSkip ("Segment stack overflow");
1353         return;
1354     }
1355
1356     /* Get the current segment and push it */
1357     CollAppend (&SegStack, DupSegDef (GetCurrentSegDef ()));
1358 }
1359
1360
1361
1362 static void DoReloc (void)
1363 /* Enter relocatable mode */
1364 {
1365     RelocMode = 1;
1366 }
1367
1368
1369
1370 static void DoRepeat (void)
1371 /* Repeat some instruction block */
1372 {
1373     ParseRepeat ();
1374 }
1375
1376
1377
1378 static void DoRes (void)
1379 /* Reserve some number of storage bytes */
1380 {
1381     long Count;
1382     long Val;
1383
1384     Count = ConstExpression ();
1385     if (Count > 0xFFFF || Count < 0) {
1386         ErrorSkip ("Range error");
1387         return;
1388     }
1389     if (Tok == TOK_COMMA) {
1390         NextTok ();
1391         Val = ConstExpression ();
1392         /* We need a byte value here */
1393         if (!IsByteRange (Val)) {
1394             ErrorSkip ("Range error");
1395             return;
1396         }
1397
1398         /* Emit constant values */
1399         while (Count--) {
1400             Emit0 ((unsigned char) Val);
1401         }
1402
1403     } else {
1404         /* Emit fill fragments */
1405         EmitFill (Count);
1406     }
1407 }
1408
1409
1410
1411 static void DoROData (void)
1412 /* Switch to the r/o data segment */
1413 {
1414     UseSeg (&RODataSegDef);
1415 }
1416
1417
1418
1419 static void DoScope (void)
1420 /* Start a local scope */
1421 {
1422     char Name[sizeof (SVal)];
1423     unsigned char AddrSize;
1424
1425
1426     if (Tok == TOK_IDENT) {
1427
1428         /* The new scope has a name. Remember and skip it. */
1429         strcpy (Name, SVal);
1430         NextTok ();
1431
1432     } else {
1433
1434         /* An unnamed scope */
1435         AnonName (Name, sizeof (Name), "SCOPE");
1436
1437     }
1438
1439     /* Read an optional address size specifier */
1440     AddrSize = OptionalAddrSize ();
1441
1442     /* Enter the new scope */
1443     SymEnterLevel (Name, ST_SCOPE, AddrSize);
1444
1445 }
1446
1447
1448
1449 static void DoSegment (void)
1450 /* Switch to another segment */
1451 {
1452     char Name [sizeof (SVal)];
1453     SegDef Def;
1454     Def.Name = Name;
1455
1456     if (Tok != TOK_STRCON) {
1457         ErrorSkip ("String constant expected");
1458     } else {
1459
1460         /* Save the name of the segment and skip it */
1461         strcpy (Name, SVal);
1462         NextTok ();
1463
1464         /* Check for an optional address size modifier */
1465         Def.AddrSize = OptionalAddrSize ();
1466
1467         /* Set the segment */
1468         UseSeg (&Def);
1469     }
1470 }
1471
1472
1473
1474 static void DoSetCPU (void)
1475 /* Switch the CPU instruction set */
1476 {
1477     /* We expect an identifier */
1478     if (Tok != TOK_STRCON) {
1479         ErrorSkip ("String constant expected");
1480     } else {
1481         /* Try to find the CPU, then skip the identifier */
1482         cpu_t CPU = FindCPU (SVal);
1483         NextTok ();
1484
1485         /* Switch to the new CPU */
1486         SetCPU (CPU);
1487     }
1488 }
1489
1490
1491
1492 static void DoSmart (void)
1493 /* Smart mode on/off */
1494 {
1495     SetBoolOption (&SmartMode);
1496 }
1497
1498
1499
1500 static void DoSunPlus (void)
1501 /* Switch to the SUNPLUS CPU */
1502 {
1503     SetCPU (CPU_SUNPLUS);
1504 }
1505
1506
1507
1508 static void DoTag (void)
1509 /* Allocate space for a struct */
1510 {
1511     long Size;
1512
1513     /* Read the struct name */
1514     SymTable* Struct = ParseScopedSymTable (SYM_FIND_EXISTING);
1515
1516     /* Check the supposed struct */
1517     if (Struct == 0) {
1518         ErrorSkip ("Unknown struct");
1519         return;
1520     }
1521     if (GetSymTabType (Struct) != ST_STRUCT) {
1522         ErrorSkip ("Not a struct");
1523         return;
1524     }
1525
1526     /* Get the size of the struct */
1527     Size = GetSymVal (SymFind (Struct, ".size", SYM_FIND_EXISTING));
1528
1529     /* Optional multiplicator may follow */
1530     if (Tok == TOK_COMMA) {
1531         long Multiplicator;
1532         NextTok ();
1533         Multiplicator = ConstExpression ();
1534         /* Multiplicator must make sense */
1535         if (Multiplicator <= 0) {
1536             ErrorSkip ("Range error");
1537             return;
1538         }
1539         Size *= Multiplicator;
1540     }
1541
1542     /* Emit fill fragments */
1543     EmitFill (Size);
1544 }
1545
1546
1547
1548 static void DoUnexpected (void)
1549 /* Got an unexpected keyword */
1550 {
1551     Error ("Unexpected `%s'", Keyword);
1552     SkipUntilSep ();
1553 }
1554
1555
1556
1557 static void DoWarning (void)
1558 /* User warning */
1559 {
1560     if (Tok != TOK_STRCON) {
1561         ErrorSkip ("String constant expected");
1562     } else {
1563         Warning (0, "User warning: %s", SVal);
1564         SkipUntilSep ();
1565     }
1566 }
1567
1568
1569
1570 static void DoWord (void)
1571 /* Define words */
1572 {
1573     while (1) {
1574         EmitWord (Expression ());
1575         if (Tok != TOK_COMMA) {
1576             break;
1577         } else {
1578             NextTok ();
1579         }
1580     }
1581 }
1582
1583
1584
1585 static void DoZeropage (void)
1586 /* Switch to the zeropage segment */
1587 {
1588     UseSeg (&ZeropageSegDef);
1589 }
1590
1591
1592
1593 /*****************************************************************************/
1594 /*                                Table data                                 */
1595 /*****************************************************************************/
1596
1597
1598
1599 /* Control commands flags */
1600 enum {
1601     ccNone      = 0x0000,               /* No special flags */
1602     ccKeepToken = 0x0001                /* Do not skip the current token */
1603 };
1604
1605 /* Control command table */
1606 typedef struct CtrlDesc CtrlDesc;
1607 struct CtrlDesc {
1608     unsigned    Flags;                  /* Flags for this directive */
1609     void        (*Handler) (void);      /* Command handler */
1610 };
1611
1612 #define PSEUDO_COUNT    (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1613 static CtrlDesc CtrlCmdTab [] = {
1614     { ccNone,           DoA16           },
1615     { ccNone,           DoA8            },
1616     { ccNone,           DoAddr          },      /* .ADDR */
1617     { ccNone,           DoAlign         },
1618     { ccNone,           DoASCIIZ        },
1619     { ccNone,           DoAssert        },
1620     { ccNone,           DoAutoImport    },
1621     { ccNone,           DoUnexpected    },      /* .BLANK */
1622     { ccNone,           DoBss           },
1623     { ccNone,           DoByte          },
1624     { ccNone,           DoCase          },
1625     { ccNone,           DoCharMap       },
1626     { ccNone,           DoCode          },
1627     { ccNone,           DoUnexpected,   },      /* .CONCAT */
1628     { ccNone,           DoConDes        },
1629     { ccNone,           DoUnexpected    },      /* .CONST */
1630     { ccNone,           DoConstructor   },
1631     { ccNone,           DoUnexpected    },      /* .CPU */
1632     { ccNone,           DoData          },
1633     { ccNone,           DoDbg,          },
1634     { ccNone,           DoDByt          },
1635     { ccNone,           DoDebugInfo     },
1636     { ccNone,           DoDefine        },
1637     { ccNone,           DoUnexpected    },      /* .DEFINED */
1638     { ccNone,           DoDestructor    },
1639     { ccNone,           DoDWord         },
1640     { ccKeepToken,      DoConditionals  },      /* .ELSE */
1641     { ccKeepToken,      DoConditionals  },      /* .ELSEIF */
1642     { ccKeepToken,      DoEnd           },
1643     { ccNone,           DoUnexpected    },      /* .ENDENUM */
1644     { ccKeepToken,      DoConditionals  },      /* .ENDIF */
1645     { ccNone,           DoUnexpected    },      /* .ENDMACRO */
1646     { ccNone,           DoEndProc       },
1647     { ccNone,           DoUnexpected    },      /* .ENDREPEAT */
1648     { ccNone,           DoEndScope      },
1649     { ccNone,           DoUnexpected    },      /* .ENDSTRUCT */
1650     { ccNone,           DoUnexpected    },      /* .ENDUNION */
1651     { ccNone,           DoEnum          },
1652     { ccNone,           DoError         },
1653     { ccNone,           DoExitMacro     },
1654     { ccNone,           DoExport        },
1655     { ccNone,           DoExportZP      },
1656     { ccNone,           DoFarAddr       },
1657     { ccNone,           DoFeature       },
1658     { ccNone,           DoFileOpt       },
1659     { ccNone,           DoForceImport   },
1660     { ccNone,           DoUnexpected    },      /* .FORCEWORD */
1661     { ccNone,           DoGlobal        },
1662     { ccNone,           DoGlobalZP      },
1663     { ccNone,           DoI16           },
1664     { ccNone,           DoI8            },
1665     { ccKeepToken,      DoConditionals  },      /* .IF */
1666     { ccKeepToken,      DoConditionals  },      /* .IFBLANK */
1667     { ccKeepToken,      DoConditionals  },      /* .IFCONST */
1668     { ccKeepToken,      DoConditionals  },      /* .IFDEF */
1669     { ccKeepToken,      DoConditionals  },      /* .IFNBLANK */
1670     { ccKeepToken,      DoConditionals  },      /* .IFNCONST */
1671     { ccKeepToken,      DoConditionals  },      /* .IFNDEF */
1672     { ccKeepToken,      DoConditionals  },      /* .IFNREF */
1673     { ccKeepToken,      DoConditionals  },      /* .IFP02 */
1674     { ccKeepToken,      DoConditionals  },      /* .IFP816 */
1675     { ccKeepToken,      DoConditionals  },      /* .IFPC02 */
1676     { ccKeepToken,      DoConditionals  },      /* .IFPSC02 */
1677     { ccKeepToken,      DoConditionals  },      /* .IFREF */
1678     { ccNone,           DoImport        },
1679     { ccNone,           DoImportZP      },
1680     { ccNone,           DoIncBin        },
1681     { ccNone,           DoInclude       },
1682     { ccNone,           DoInvalid       },      /* .LEFT */
1683     { ccNone,           DoLineCont      },
1684     { ccNone,           DoList          },
1685     { ccNone,           DoListBytes     },
1686     { ccNone,           DoUnexpected    },      /* .LOCAL */
1687     { ccNone,           DoLocalChar     },
1688     { ccNone,           DoMacPack       },
1689     { ccNone,           DoMacro         },
1690     { ccNone,           DoUnexpected    },      /* .MATCH */
1691     { ccNone,           DoInvalid       },      /* .MID */
1692     { ccNone,           DoNull          },
1693     { ccNone,           DoOrg           },
1694     { ccNone,           DoOut           },
1695     { ccNone,           DoP02           },
1696     { ccNone,           DoP816          },
1697     { ccNone,           DoPageLength    },
1698     { ccNone,           DoUnexpected    },      /* .PARAMCOUNT */
1699     { ccNone,           DoPC02          },
1700     { ccNone,           DoPopSeg        },
1701     { ccNone,           DoProc          },
1702     { ccNone,           DoPSC02         },
1703     { ccNone,           DoPushSeg       },
1704     { ccNone,           DoUnexpected    },      /* .REFERENCED */
1705     { ccNone,           DoReloc         },
1706     { ccNone,           DoRepeat        },
1707     { ccNone,           DoRes           },
1708     { ccNone,           DoInvalid       },      /* .RIGHT */
1709     { ccNone,           DoROData        },
1710     { ccNone,           DoScope         },
1711     { ccNone,           DoSegment       },
1712     { ccNone,           DoSetCPU        },
1713     { ccNone,           DoUnexpected    },      /* .SIZEOF */
1714     { ccNone,           DoSmart         },
1715     { ccNone,           DoUnexpected    },      /* .STRAT */
1716     { ccNone,           DoUnexpected    },      /* .STRING */
1717     { ccNone,           DoUnexpected    },      /* .STRLEN */
1718     { ccNone,           DoStruct        },
1719     { ccNone,           DoSunPlus       },
1720     { ccNone,           DoTag           },
1721     { ccNone,           DoUnexpected    },      /* .TCOUNT */
1722     { ccNone,           DoUnexpected    },      /* .TIME */
1723     { ccNone,           DoUnion         },
1724     { ccNone,           DoUnexpected    },      /* .VERSION */
1725     { ccNone,           DoWarning       },
1726     { ccNone,           DoWord          },
1727     { ccNone,           DoUnexpected    },      /* .XMATCH */
1728     { ccNone,           DoZeropage      },
1729 };
1730
1731
1732
1733 /*****************************************************************************/
1734 /*                                   Code                                    */
1735 /*****************************************************************************/
1736
1737
1738
1739 void HandlePseudo (void)
1740 /* Handle a pseudo instruction */
1741 {
1742     CtrlDesc* D;
1743
1744     /* Calculate the index into the table */
1745     unsigned Index = Tok - TOK_FIRSTPSEUDO;
1746
1747     /* Safety check */
1748     if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
1749         Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
1750                   PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
1751     }
1752     CHECK (Index < PSEUDO_COUNT);
1753
1754     /* Get the pseudo intruction descriptor */
1755     D = &CtrlCmdTab [Index];
1756
1757     /* Remember the instruction, then skip it if needed */
1758     if ((D->Flags & ccKeepToken) == 0) {
1759         strcpy (Keyword, SVal);
1760         NextTok ();
1761     }
1762
1763     /* Call the handler */
1764     D->Handler ();
1765 }
1766
1767
1768
1769 void SegStackCheck (void)
1770 /* Check if the segment stack is empty at end of assembly */
1771 {
1772     if (CollCount (&SegStack) != 0) {
1773         Error ("Segment stack is not empty");
1774     }
1775 }
1776
1777
1778