]> git.sur5r.net Git - cc65/blob - src/ca65/pseudo.c
Added new .FATAL pseudo op.
[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 (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 (Tok == TOK_PLUS) {
158         *Flag = 1;
159         NextTok ();
160     } else if (Tok == TOK_MINUS) {
161         *Flag = 0;
162         NextTok ();
163     } else if (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 (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 (Tok == TOK_ASSIGN || Tok == TOK_EQ) {
187
188         /* Assignment means the symbol is a label */
189         if (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 (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, &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 (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 (Tok == TOK_IDENT && SB_CompareStr (&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 (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 (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 (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 (Tok != TOK_STRCON) {
394             ErrorSkip ("String constant expected");
395             return;
396         }
397
398         /* Translate into target charset and emit */
399         TgtTranslateStrBuf (&SVal);
400         EmitStrBuf (&SVal);
401         NextTok ();
402         if (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 (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 (Tok == TOK_COMMA) {
472
473         /* Skip the comma */
474         NextTok ();
475
476         /* Read the message */
477         if (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 (&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 (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 (Tok == TOK_STRCON) {
536             /* A string, translate into target charset and emit */
537             TgtTranslateStrBuf (&SVal);
538             EmitStrBuf (&SVal);
539             NextTok ();
540         } else {
541             EmitByte (Expression ());
542         }
543         if (Tok != TOK_COMMA) {
544             break;
545         } else {
546             NextTok ();
547             /* Do smart handling of dangling comma */
548             if (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 (Tok != TOK_IDENT) {
619         ErrorSkip ("Identifier expected");
620         return;
621     }
622     SB_Copy (&Name, &SVal);
623     NextTok ();
624
625     /* Type follows. May be encoded as identifier or numerical */
626     ConsumeComma ();
627     if (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 (Tok != TOK_IDENT) {
668         ErrorSkip ("Identifier expected");
669         return;
670     }
671     SB_Copy (&Name, &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 (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 (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 (Tok != TOK_IDENT) {
763         ErrorSkip ("Identifier expected");
764         return;
765     }
766     SB_Copy (&Name, &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 (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 (Tok != TOK_STRCON) {
832         ErrorSkip ("String constant expected");
833     } else {
834         Error ("User error: %m%p", &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 (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 (Tok != TOK_STRCON) {
889         ErrorSkip ("String constant expected");
890     } else {
891         Fatal ("User error: %m%p", &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 (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 (&SVal) == FEAT_UNKNOWN) {
915             /* Not found */
916             ErrorSkip ("Invalid feature: `%m%p'", &SVal);
917             return;
918         } else {
919             /* Skip the keyword */
920             NextTok ();
921         }
922
923         /* Allow more than one keyword */
924         if (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 (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 (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 (&SVal);
973                 break;
974
975             case 1:
976                 /* Comment */
977                 OptComment (&SVal);
978                 break;
979
980             case 2:
981                 /* Compiler */
982                 OptCompiler (&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 (Tok != TOK_STRCON) {
1007             ErrorSkip ("String constant expected");
1008             return;
1009         }
1010
1011         /* Insert the option */
1012         OptStr ((unsigned char) OptNum, &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 (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 (Tok != TOK_STRCON) {
1113         ErrorSkip ("String constant expected");
1114         return;
1115     }
1116     SB_Copy (&Name, &SVal);
1117     SB_Terminate (&Name);
1118     NextTok ();
1119
1120     /* A starting offset may follow */
1121     if (Tok == TOK_COMMA) {
1122         NextTok ();
1123         Start = ConstExpression ();
1124
1125         /* And a length may follow */
1126         if (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 (Tok != TOK_STRCON) {
1232         ErrorSkip ("String constant expected");
1233     } else {
1234         SB_Terminate (&SVal);
1235         if (NewInputFile (SB_GetConstBuf (&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 (Tok != TOK_IDENT) {
1251         ErrorSkip ("Identifier expected");
1252         return;
1253     }
1254     SB_Copy (&Name, &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 (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 (Tok != TOK_CHARCON) {
1331         ErrorSkip ("Character constant expected");
1332     } else {
1333         if (IVal != '@' && IVal != '?') {
1334             Error ("Invalid start character for locals");
1335         } else {
1336             LocalStart = (char) 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 (Tok != TOK_IDENT) {
1351         ErrorSkip ("Identifier expected");
1352         return;
1353     }
1354
1355     /* Search for the macro package name */
1356     LocaseSVal ();
1357     Package = MacPackFind (&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 (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", (int) SB_GetLen (&SVal), SB_GetConstBuf (&SVal));
1413         fflush (stdout);
1414         NextTok ();
1415     }
1416 }
1417
1418
1419
1420 static void DoP02 (void)
1421 /* Switch to 6502 CPU */
1422 {
1423     SetCPU (CPU_6502);
1424 }
1425
1426
1427
1428 static void DoPC02 (void)
1429 /* Switch to 65C02 CPU */
1430 {
1431     SetCPU (CPU_65C02);
1432 }
1433
1434
1435
1436 static void DoP816 (void)
1437 /* Switch to 65816 CPU */
1438 {
1439     SetCPU (CPU_65816);
1440 }
1441
1442
1443
1444 static void DoPageLength (void)
1445 /* Set the page length for the listing */
1446 {
1447     PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
1448 }
1449
1450
1451
1452 static void DoPopCPU (void)
1453 /* Pop an old CPU setting from the CPU stack */
1454 {
1455     /* Must have a CPU on the stack */
1456     if (IS_IsEmpty (&CPUStack)) {
1457         ErrorSkip ("CPU stack is empty");
1458         return;
1459     }
1460
1461     /* Set the CPU to the value popped from stack */
1462     SetCPU (IS_Pop (&CPUStack));
1463 }
1464
1465
1466
1467 static void DoPopSeg (void)
1468 /* Pop an old segment from the segment stack */
1469 {
1470     SegDef* Def;
1471
1472     /* Must have a segment on the stack */
1473     if (CollCount (&SegStack) == 0) {
1474         ErrorSkip ("Segment stack is empty");
1475         return;
1476     }
1477
1478     /* Pop the last element */
1479     Def = CollPop (&SegStack);
1480
1481     /* Restore this segment */
1482     UseSeg (Def);
1483
1484     /* Delete the segment definition */
1485     FreeSegDef (Def);
1486 }
1487
1488
1489
1490 static void DoProc (void)
1491 /* Start a new lexical scope */
1492 {
1493     StrBuf Name = STATIC_STRBUF_INITIALIZER;
1494     unsigned char AddrSize;
1495
1496     if (Tok == TOK_IDENT) {
1497
1498         SymEntry* Sym;
1499
1500         /* The new scope has a name. Remember it. */
1501         SB_Copy (&Name, &SVal);
1502
1503         /* Search for the symbol, generate a new one if needed */
1504         Sym = SymFind (CurrentScope, &Name, SYM_ALLOC_NEW);
1505
1506         /* Skip the scope name */
1507         NextTok ();
1508
1509         /* Read an optional address size specifier */
1510         AddrSize = OptionalAddrSize ();
1511
1512         /* Mark the symbol as defined */
1513         SymDef (Sym, GenCurrentPC (), AddrSize, SF_LABEL);
1514
1515     } else {
1516
1517         /* A .PROC statement without a name */
1518         Warning (1, "Unnamed .PROCs are deprecated, please use .SCOPE");
1519         AnonName (&Name, "PROC");
1520         AddrSize = ADDR_SIZE_DEFAULT;
1521
1522     }
1523
1524     /* Enter a new scope */
1525     SymEnterLevel (&Name, ST_PROC, AddrSize);
1526
1527     /* Free memory for Name */
1528     SB_Done (&Name);
1529 }
1530
1531
1532
1533 static void DoPSC02 (void)
1534 /* Switch to 65SC02 CPU */
1535 {
1536     SetCPU (CPU_65SC02);
1537 }
1538
1539
1540
1541 static void DoPushCPU (void)
1542 /* Push the current CPU setting onto the CPU stack */
1543 {
1544     /* Can only push a limited size of segments */
1545     if (IS_IsFull (&CPUStack)) {
1546         ErrorSkip ("CPU stack overflow");
1547         return;
1548     }
1549
1550     /* Get the current segment and push it */
1551     IS_Push (&CPUStack, GetCPU ());
1552 }
1553
1554
1555
1556 static void DoPushSeg (void)
1557 /* Push the current segment onto the segment stack */
1558 {
1559     /* Can only push a limited size of segments */
1560     if (CollCount (&SegStack) >= MAX_PUSHED_SEGMENTS) {
1561         ErrorSkip ("Segment stack overflow");
1562         return;
1563     }
1564
1565     /* Get the current segment and push it */
1566     CollAppend (&SegStack, DupSegDef (GetCurrentSegDef ()));
1567 }
1568
1569
1570
1571 static void DoReloc (void)
1572 /* Enter relocatable mode */
1573 {
1574     EnterRelocMode ();
1575 }
1576
1577
1578
1579 static void DoRepeat (void)
1580 /* Repeat some instruction block */
1581 {
1582     ParseRepeat ();
1583 }
1584
1585
1586
1587 static void DoRes (void)
1588 /* Reserve some number of storage bytes */
1589 {
1590     long Count;
1591     long Val;
1592
1593     Count = ConstExpression ();
1594     if (Count > 0xFFFF || Count < 0) {
1595         ErrorSkip ("Range error");
1596         return;
1597     }
1598     if (Tok == TOK_COMMA) {
1599         NextTok ();
1600         Val = ConstExpression ();
1601         /* We need a byte value here */
1602         if (!IsByteRange (Val)) {
1603             ErrorSkip ("Range error");
1604             return;
1605         }
1606
1607         /* Emit constant values */
1608         while (Count--) {
1609             Emit0 ((unsigned char) Val);
1610         }
1611
1612     } else {
1613         /* Emit fill fragments */
1614         EmitFill (Count);
1615     }
1616 }
1617
1618
1619
1620 static void DoROData (void)
1621 /* Switch to the r/o data segment */
1622 {
1623     UseSeg (&RODataSegDef);
1624 }
1625
1626
1627
1628 static void DoScope (void)
1629 /* Start a local scope */
1630 {
1631     StrBuf Name = STATIC_STRBUF_INITIALIZER;
1632     unsigned char AddrSize;
1633
1634
1635     if (Tok == TOK_IDENT) {
1636
1637         /* The new scope has a name. Remember and skip it. */
1638         SB_Copy (&Name, &SVal);
1639         NextTok ();
1640
1641     } else {
1642
1643         /* An unnamed scope */
1644         AnonName (&Name, "SCOPE");
1645
1646     }
1647
1648     /* Read an optional address size specifier */
1649     AddrSize = OptionalAddrSize ();
1650
1651     /* Enter the new scope */
1652     SymEnterLevel (&Name, ST_SCOPE, AddrSize);
1653
1654     /* Free memory for Name */
1655     SB_Done (&Name);
1656 }
1657
1658
1659
1660 static void DoSegment (void)
1661 /* Switch to another segment */
1662 {
1663     StrBuf Name = STATIC_STRBUF_INITIALIZER;
1664     SegDef Def;
1665
1666     if (Tok != TOK_STRCON) {
1667         ErrorSkip ("String constant expected");
1668     } else {
1669
1670         /* Save the name of the segment and skip it */
1671         SB_Copy (&Name, &SVal);
1672         NextTok ();
1673
1674         /* Use the name for the segment definition */
1675         SB_Terminate (&Name);
1676         Def.Name = SB_GetBuf (&Name);
1677
1678         /* Check for an optional address size modifier */
1679         Def.AddrSize = OptionalAddrSize ();
1680
1681         /* Set the segment */
1682         UseSeg (&Def);
1683     }
1684
1685     /* Free memory for Name */
1686     SB_Done (&Name);
1687 }
1688
1689
1690
1691 static void DoSetCPU (void)
1692 /* Switch the CPU instruction set */
1693 {
1694     /* We expect an identifier */
1695     if (Tok != TOK_STRCON) {
1696         ErrorSkip ("String constant expected");
1697     } else {
1698         cpu_t CPU;
1699
1700         /* Try to find the CPU */
1701         SB_Terminate (&SVal);
1702         CPU = FindCPU (SB_GetConstBuf (&SVal));
1703
1704         /* Switch to the new CPU */
1705         SetCPU (CPU);
1706
1707         /* Skip the identifier. If the CPU switch was successful, the scanner
1708          * will treat the input now correctly for the new CPU.
1709          */
1710         NextTok ();
1711     }
1712 }
1713
1714
1715
1716 static void DoSmart (void)
1717 /* Smart mode on/off */
1718 {
1719     SetBoolOption (&SmartMode);
1720 }
1721
1722
1723
1724 static void DoSunPlus (void)
1725 /* Switch to the SUNPLUS CPU */
1726 {
1727     SetCPU (CPU_SUNPLUS);
1728 }
1729
1730
1731
1732 static void DoTag (void)
1733 /* Allocate space for a struct */
1734 {
1735     SymEntry* SizeSym;
1736     long Size;
1737
1738     /* Read the struct name */
1739     SymTable* Struct = ParseScopedSymTable ();
1740
1741     /* Check the supposed struct */
1742     if (Struct == 0) {
1743         ErrorSkip ("Unknown struct");
1744         return;
1745     }
1746     if (GetSymTabType (Struct) != ST_STRUCT) {
1747         ErrorSkip ("Not a struct");
1748         return;
1749     }
1750
1751     /* Get the symbol that defines the size of the struct */
1752     SizeSym = GetSizeOfScope (Struct);
1753
1754     /* Check if it does exist and if its value is known */
1755     if (SizeSym == 0 || !SymIsConst (SizeSym, &Size)) {
1756         ErrorSkip ("Size of struct/union is unknown");
1757         return;
1758     }
1759
1760     /* Optional multiplicator may follow */
1761     if (Tok == TOK_COMMA) {
1762         long Multiplicator;
1763         NextTok ();
1764         Multiplicator = ConstExpression ();
1765         /* Multiplicator must make sense */
1766         if (Multiplicator <= 0) {
1767             ErrorSkip ("Range error");
1768             return;
1769         }
1770         Size *= Multiplicator;
1771     }
1772
1773     /* Emit fill fragments */
1774     EmitFill (Size);
1775 }
1776
1777
1778
1779 static void DoUnexpected (void)
1780 /* Got an unexpected keyword */
1781 {
1782     Error ("Unexpected `%m%p'", &Keyword);
1783     SkipUntilSep ();
1784 }
1785
1786
1787
1788 static void DoWarning (void)
1789 /* User warning */
1790 {
1791     if (Tok != TOK_STRCON) {
1792         ErrorSkip ("String constant expected");
1793     } else {
1794         Warning (0, "User warning: %m%p", &SVal);
1795         SkipUntilSep ();
1796     }
1797 }
1798
1799
1800
1801 static void DoWord (void)
1802 /* Define words */
1803 {
1804     while (1) {
1805         EmitWord (Expression ());
1806         if (Tok != TOK_COMMA) {
1807             break;
1808         } else {
1809             NextTok ();
1810         }
1811     }
1812 }
1813
1814
1815
1816 static void DoZeropage (void)
1817 /* Switch to the zeropage segment */
1818 {
1819     UseSeg (&ZeropageSegDef);
1820 }
1821
1822
1823
1824 /*****************************************************************************/
1825 /*                                Table data                                 */
1826 /*****************************************************************************/
1827
1828
1829
1830 /* Control commands flags */
1831 enum {
1832     ccNone      = 0x0000,               /* No special flags */
1833     ccKeepToken = 0x0001                /* Do not skip the current token */
1834 };
1835
1836 /* Control command table */
1837 typedef struct CtrlDesc CtrlDesc;
1838 struct CtrlDesc {
1839     unsigned    Flags;                  /* Flags for this directive */
1840     void        (*Handler) (void);      /* Command handler */
1841 };
1842
1843 #define PSEUDO_COUNT    (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1844 static CtrlDesc CtrlCmdTab [] = {
1845     { ccNone,           DoA16           },
1846     { ccNone,           DoA8            },
1847     { ccNone,           DoAddr          },      /* .ADDR */
1848     { ccNone,           DoAlign         },
1849     { ccNone,           DoASCIIZ        },
1850     { ccNone,           DoAssert        },
1851     { ccNone,           DoAutoImport    },
1852     { ccNone,           DoUnexpected    },      /* .BANKBYTE */
1853     { ccNone,           DoBankBytes     },
1854     { ccNone,           DoUnexpected    },      /* .BLANK */
1855     { ccNone,           DoBss           },
1856     { ccNone,           DoByte          },
1857     { ccNone,           DoCase          },
1858     { ccNone,           DoCharMap       },
1859     { ccNone,           DoCode          },
1860     { ccNone,           DoUnexpected,   },      /* .CONCAT */
1861     { ccNone,           DoConDes        },
1862     { ccNone,           DoUnexpected    },      /* .CONST */
1863     { ccNone,           DoConstructor   },
1864     { ccNone,           DoUnexpected    },      /* .CPU */
1865     { ccNone,           DoData          },
1866     { ccNone,           DoDbg,          },
1867     { ccNone,           DoDByt          },
1868     { ccNone,           DoDebugInfo     },
1869     { ccNone,           DoDefine        },
1870     { ccNone,           DoUnexpected    },      /* .DEFINED */
1871     { ccNone,           DoDestructor    },
1872     { ccNone,           DoDWord         },
1873     { ccKeepToken,      DoConditionals  },      /* .ELSE */
1874     { ccKeepToken,      DoConditionals  },      /* .ELSEIF */
1875     { ccKeepToken,      DoEnd           },
1876     { ccNone,           DoUnexpected    },      /* .ENDENUM */
1877     { ccKeepToken,      DoConditionals  },      /* .ENDIF */
1878     { ccNone,           DoUnexpected    },      /* .ENDMACRO */
1879     { ccNone,           DoEndProc       },
1880     { ccNone,           DoUnexpected    },      /* .ENDREPEAT */
1881     { ccNone,           DoEndScope      },
1882     { ccNone,           DoUnexpected    },      /* .ENDSTRUCT */
1883     { ccNone,           DoUnexpected    },      /* .ENDUNION */
1884     { ccNone,           DoEnum          },
1885     { ccNone,           DoError         },
1886     { ccNone,           DoExitMacro     },
1887     { ccNone,           DoExport        },
1888     { ccNone,           DoExportZP      },
1889     { ccNone,           DoFarAddr       },
1890     { ccNone,           DoFatal         },
1891     { ccNone,           DoFeature       },
1892     { ccNone,           DoFileOpt       },
1893     { ccNone,           DoForceImport   },
1894     { ccNone,           DoUnexpected    },      /* .FORCEWORD */
1895     { ccNone,           DoGlobal        },
1896     { ccNone,           DoGlobalZP      },
1897     { ccNone,           DoUnexpected    },      /* .HIBYTE */
1898     { ccNone,           DoHiBytes       },
1899     { ccNone,           DoUnexpected    },      /* .HIWORD */
1900     { ccNone,           DoI16           },
1901     { ccNone,           DoI8            },
1902     { ccNone,           DoUnexpected    },      /* .IDENT */
1903     { ccKeepToken,      DoConditionals  },      /* .IF */
1904     { ccKeepToken,      DoConditionals  },      /* .IFBLANK */
1905     { ccKeepToken,      DoConditionals  },      /* .IFCONST */
1906     { ccKeepToken,      DoConditionals  },      /* .IFDEF */
1907     { ccKeepToken,      DoConditionals  },      /* .IFNBLANK */
1908     { ccKeepToken,      DoConditionals  },      /* .IFNCONST */
1909     { ccKeepToken,      DoConditionals  },      /* .IFNDEF */
1910     { ccKeepToken,      DoConditionals  },      /* .IFNREF */
1911     { ccKeepToken,      DoConditionals  },      /* .IFP02 */
1912     { ccKeepToken,      DoConditionals  },      /* .IFP816 */
1913     { ccKeepToken,      DoConditionals  },      /* .IFPC02 */
1914     { ccKeepToken,      DoConditionals  },      /* .IFPSC02 */
1915     { ccKeepToken,      DoConditionals  },      /* .IFREF */
1916     { ccNone,           DoImport        },
1917     { ccNone,           DoImportZP      },
1918     { ccNone,           DoIncBin        },
1919     { ccNone,           DoInclude       },
1920     { ccNone,           DoInterruptor   },
1921     { ccNone,           DoInvalid       },      /* .LEFT */
1922     { ccNone,           DoLineCont      },
1923     { ccNone,           DoList          },
1924     { ccNone,           DoListBytes     },
1925     { ccNone,           DoUnexpected    },      /* .LOBYTE */
1926     { ccNone,           DoLoBytes       },
1927     { ccNone,           DoUnexpected    },      /* .LOCAL */
1928     { ccNone,           DoLocalChar     },
1929     { ccNone,           DoUnexpected    },      /* .LOWORD */
1930     { ccNone,           DoMacPack       },
1931     { ccNone,           DoMacro         },
1932     { ccNone,           DoUnexpected    },      /* .MATCH */
1933     { ccNone,           DoUnexpected    },      /* .MAX */
1934     { ccNone,           DoInvalid       },      /* .MID */
1935     { ccNone,           DoUnexpected    },      /* .MIN */
1936     { ccNone,           DoNull          },
1937     { ccNone,           DoOrg           },
1938     { ccNone,           DoOut           },
1939     { ccNone,           DoP02           },
1940     { ccNone,           DoP816          },
1941     { ccNone,           DoPageLength    },
1942     { ccNone,           DoUnexpected    },      /* .PARAMCOUNT */
1943     { ccNone,           DoPC02          },
1944     { ccNone,           DoPopCPU        },
1945     { ccNone,           DoPopSeg        },
1946     { ccNone,           DoProc          },
1947     { ccNone,           DoPSC02         },
1948     { ccNone,           DoPushCPU       },
1949     { ccNone,           DoPushSeg       },
1950     { ccNone,           DoUnexpected    },      /* .REFERENCED */
1951     { ccNone,           DoReloc         },
1952     { ccNone,           DoRepeat        },
1953     { ccNone,           DoRes           },
1954     { ccNone,           DoInvalid       },      /* .RIGHT */
1955     { ccNone,           DoROData        },
1956     { ccNone,           DoScope         },
1957     { ccNone,           DoSegment       },
1958     { ccNone,           DoUnexpected    },      /* .SET */
1959     { ccNone,           DoSetCPU        },
1960     { ccNone,           DoUnexpected    },      /* .SIZEOF */
1961     { ccNone,           DoSmart         },
1962     { ccNone,           DoUnexpected    },      /* .SPRINTF */
1963     { ccNone,           DoUnexpected    },      /* .STRAT */
1964     { ccNone,           DoUnexpected    },      /* .STRING */
1965     { ccNone,           DoUnexpected    },      /* .STRLEN */
1966     { ccNone,           DoStruct        },
1967     { ccNone,           DoSunPlus       },
1968     { ccNone,           DoTag           },
1969     { ccNone,           DoUnexpected    },      /* .TCOUNT */
1970     { ccNone,           DoUnexpected    },      /* .TIME */
1971     { ccNone,           DoUnion         },
1972     { ccNone,           DoUnexpected    },      /* .VERSION */
1973     { ccNone,           DoWarning       },
1974     { ccNone,           DoWord          },
1975     { ccNone,           DoUnexpected    },      /* .XMATCH */
1976     { ccNone,           DoZeropage      },
1977 };
1978
1979
1980
1981 /*****************************************************************************/
1982 /*                                   Code                                    */
1983 /*****************************************************************************/
1984
1985
1986
1987 void HandlePseudo (void)
1988 /* Handle a pseudo instruction */
1989 {
1990     CtrlDesc* D;
1991
1992     /* Calculate the index into the table */
1993     unsigned Index = Tok - TOK_FIRSTPSEUDO;
1994
1995     /* Safety check */
1996     if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
1997         Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
1998                   (unsigned) PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
1999     }
2000     CHECK (Index < PSEUDO_COUNT);
2001
2002     /* Get the pseudo intruction descriptor */
2003     D = &CtrlCmdTab [Index];
2004
2005     /* Remember the instruction, then skip it if needed */
2006     if ((D->Flags & ccKeepToken) == 0) {
2007         SB_Copy (&Keyword, &SVal);
2008         NextTok ();
2009     }
2010
2011     /* Call the handler */
2012     D->Handler ();
2013 }
2014
2015
2016
2017 void CheckPseudo (void)
2018 /* Check if the stacks are empty at end of assembly */
2019 {
2020     if (CollCount (&SegStack) != 0) {
2021         Warning (1, "Segment stack is not empty");
2022     }
2023     if (!IS_IsEmpty (&CPUStack)) {
2024         Warning (1, "CPU stack is not empty");
2025     }
2026 }
2027
2028
2029