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