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