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