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