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