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