]> git.sur5r.net Git - cc65/blob - src/ca65/pseudo.c
Changed most "backticks" (grave accents) into apostrophes.
[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* const 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* const 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 = AUTO_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     ** Note: empty string operands emit nothing;
603     ** so, add a type only if there's a span.
604     */
605     S = CloseSpan (S);
606     if (S != 0) {
607         SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
608     }
609
610     /* Free the type string */
611     SB_Done (&Type);
612 }
613
614
615
616 static void DoCase (void)
617 /* Switch the IgnoreCase option */
618 {
619     SetBoolOption (&IgnoreCase);
620     IgnoreCase = !IgnoreCase;
621 }
622
623
624
625 static void DoCharMap (void)
626 /* Allow custom character mappings */
627 {
628     long Index;
629     long Code;
630
631     /* Read the index as numerical value */
632     Index = ConstExpression ();
633     if (Index < 0 || Index > 255) {
634         /* Value out of range */
635         ErrorSkip ("Index range error");
636         return;
637     }
638
639     /* Comma follows */
640     ConsumeComma ();
641
642     /* Read the character code */
643     Code = ConstExpression ();
644     if (Code < 0 || Code > 255) {
645         /* Value out of range */
646         ErrorSkip ("Code range error");
647         return;
648     }
649
650     /* Set the character translation */
651     TgtTranslateSet ((unsigned) Index, (unsigned char) Code);
652 }
653
654
655
656 static void DoCode (void)
657 /* Switch to the code segment */
658 {
659     UseSeg (&CodeSegDef);
660 }
661
662
663
664 static void DoConDes (void)
665 /* Export a symbol as constructor/destructor */
666 {
667     static const char* const Keys[] = {
668         "CONSTRUCTOR",
669         "DESTRUCTOR",
670         "INTERRUPTOR",
671     };
672     StrBuf Name = STATIC_STRBUF_INITIALIZER;
673     long Type;
674
675     /* Symbol name follows */
676     if (CurTok.Tok != TOK_IDENT) {
677         ErrorSkip ("Identifier expected");
678         return;
679     }
680     SB_Copy (&Name, &CurTok.SVal);
681     NextTok ();
682
683     /* Type follows. May be encoded as identifier or numerical */
684     ConsumeComma ();
685     if (CurTok.Tok == TOK_IDENT) {
686
687         /* Map the following keyword to a number, then skip it */
688         Type = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
689         NextTok ();
690
691         /* Check if we got a valid keyword */
692         if (Type < 0) {
693             ErrorSkip ("Syntax error");
694             goto ExitPoint;
695         }
696
697     } else {
698
699         /* Read the type as numerical value */
700         Type = ConstExpression ();
701         if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) {
702             /* Value out of range */
703             ErrorSkip ("Range error");
704             goto ExitPoint;
705         }
706
707     }
708
709     /* Parse the remainder of the line and export the symbol */
710     ConDes (&Name, (unsigned) Type);
711
712 ExitPoint:
713     /* Free string memory */
714     SB_Done (&Name);
715 }
716
717
718
719 static void DoConstructor (void)
720 /* Export a symbol as constructor */
721 {
722     StrBuf Name = STATIC_STRBUF_INITIALIZER;
723
724     /* Symbol name follows */
725     if (CurTok.Tok != TOK_IDENT) {
726         ErrorSkip ("Identifier expected");
727         return;
728     }
729     SB_Copy (&Name, &CurTok.SVal);
730     NextTok ();
731
732     /* Parse the remainder of the line and export the symbol */
733     ConDes (&Name, CD_TYPE_CON);
734
735     /* Free string memory */
736     SB_Done (&Name);
737 }
738
739
740
741 static void DoData (void)
742 /* Switch to the data segment */
743 {
744     UseSeg (&DataSegDef);
745 }
746
747
748
749 static void DoDbg (void)
750 /* Add debug information from high level code */
751 {
752     static const char* const Keys[] = {
753         "FILE",
754         "FUNC",
755         "LINE",
756         "SYM",
757     };
758     int Key;
759
760
761     /* We expect a subkey */
762     if (CurTok.Tok != TOK_IDENT) {
763         ErrorSkip ("Identifier expected");
764         return;
765     }
766
767     /* Map the following keyword to a number */
768     Key = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
769
770     /* Skip the subkey */
771     NextTok ();
772
773     /* Check the key and dispatch to a handler */
774     switch (Key) {
775         case 0:     DbgInfoFile ();             break;
776         case 1:     DbgInfoFunc ();             break;
777         case 2:     DbgInfoLine ();             break;
778         case 3:     DbgInfoSym ();              break;
779         default:    ErrorSkip ("Syntax error"); break;
780     }
781 }
782
783
784
785 static void DoDByt (void)
786 /* Output double bytes */
787 {
788     /* Element type for the generated array */
789     static const char EType[1] = { GT_DBYTE };
790
791     /* Record type information */
792     Span* S = OpenSpan ();
793     StrBuf Type = STATIC_STRBUF_INITIALIZER;
794
795     /* Parse arguments */
796     while (1) {
797         EmitWord (GenSwapExpr (BoundedExpr (Expression, 2)));
798         if (CurTok.Tok != TOK_COMMA) {
799             break;
800         } else {
801             NextTok ();
802         }
803     }
804
805     /* Close the span, then add type information to it */
806     S = CloseSpan (S);
807     SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
808
809     /* Free the type string */
810     SB_Done (&Type);
811 }
812
813
814
815 static void DoDebugInfo (void)
816 /* Switch debug info on or off */
817 {
818     SetBoolOption (&DbgSyms);
819 }
820
821
822
823 static void DoDefine (void)
824 /* Define a one line macro */
825 {
826     MacDef (MAC_STYLE_DEFINE);
827 }
828
829
830
831 static void DoDelMac (void)
832 /* Delete a classic macro */
833 {
834     /* We expect an identifier */
835     if (CurTok.Tok != TOK_IDENT) {
836         ErrorSkip ("Identifier expected");
837     } else {
838         MacUndef (&CurTok.SVal, MAC_STYLE_CLASSIC);
839         NextTok ();
840     }
841 }
842
843
844
845 static void DoDestructor (void)
846 /* Export a symbol as destructor */
847 {
848     StrBuf Name = STATIC_STRBUF_INITIALIZER;
849
850     /* Symbol name follows */
851     if (CurTok.Tok != TOK_IDENT) {
852         ErrorSkip ("Identifier expected");
853         return;
854     }
855     SB_Copy (&Name, &CurTok.SVal);
856     NextTok ();
857
858     /* Parse the remainder of the line and export the symbol */
859     ConDes (&Name, CD_TYPE_DES);
860
861     /* Free string memory */
862     SB_Done (&Name);
863 }
864
865
866
867 static void DoDWord (void)
868 /* Define dwords */
869 {
870     while (1) {
871         EmitDWord (BoundedExpr (Expression, 4));
872         if (CurTok.Tok != TOK_COMMA) {
873             break;
874         } else {
875             NextTok ();
876         }
877     }
878 }
879
880
881
882 static void DoEnd (void)
883 /* End of assembly */
884 {
885     ForcedEnd = 1;
886     NextTok ();
887 }
888
889
890
891 static void DoEndProc (void)
892 /* Leave a lexical level */
893 {
894     if (CurrentScope->Type != SCOPE_SCOPE || CurrentScope->Label == 0) {
895         /* No local scope */
896         ErrorSkip ("No open .PROC");
897     } else {
898         SymLeaveLevel ();
899     }
900 }
901
902
903
904 static void DoEndScope (void)
905 /* Leave a lexical level */
906 {
907     if (CurrentScope->Type != SCOPE_SCOPE || CurrentScope->Label != 0) {
908         /* No local scope */
909         ErrorSkip ("No open .SCOPE");
910     } else {
911         SymLeaveLevel ();
912     }
913 }
914
915
916
917 static void DoError (void)
918 /* User error */
919 {
920     if (CurTok.Tok != TOK_STRCON) {
921         ErrorSkip ("String constant expected");
922     } else {
923         Error ("User error: %m%p", &CurTok.SVal);
924         SkipUntilSep ();
925     }
926 }
927
928
929
930 static void DoExitMacro (void)
931 /* Exit a macro expansion */
932 {
933     if (!InMacExpansion ()) {
934         /* We aren't expanding a macro currently */
935         DoUnexpected ();
936     } else {
937         MacAbort ();
938     }
939 }
940
941
942
943 static void DoExport (void)
944 /* Export a symbol */
945 {
946     ExportImport (ExportWithAssign, ADDR_SIZE_DEFAULT, SF_NONE);
947 }
948
949
950
951 static void DoExportZP (void)
952 /* Export a zeropage symbol */
953 {
954     ExportImport (ExportWithAssign, ADDR_SIZE_ZP, SF_NONE);
955 }
956
957
958
959 static void DoFarAddr (void)
960 /* Define far addresses (24 bit) */
961 {
962     /* Element type for the generated array */
963     static const char EType[2] = { GT_FAR_PTR, GT_VOID };
964
965     /* Record type information */
966     Span* S = OpenSpan ();
967     StrBuf Type = STATIC_STRBUF_INITIALIZER;
968
969     /* Parse arguments */
970     while (1) {
971         EmitFarAddr (BoundedExpr (Expression, 3));
972         if (CurTok.Tok != TOK_COMMA) {
973             break;
974         } else {
975             NextTok ();
976         }
977     }
978
979     /* Close the span, then add type information to it */
980     S = CloseSpan (S);
981     SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
982
983     /* Free the type string */
984     SB_Done (&Type);
985 }
986
987
988
989 static void DoFatal (void)
990 /* Fatal user error */
991 {
992     if (CurTok.Tok != TOK_STRCON) {
993         ErrorSkip ("String constant expected");
994     } else {
995         Fatal ("User error: %m%p", &CurTok.SVal);
996         SkipUntilSep ();
997     }
998 }
999
1000
1001
1002 static void DoFeature (void)
1003 /* Switch the Feature option */
1004 {
1005     /* Allow a list of comma separated keywords */
1006     while (1) {
1007
1008         /* We expect an identifier */
1009         if (CurTok.Tok != TOK_IDENT) {
1010             ErrorSkip ("Identifier expected");
1011             return;
1012         }
1013
1014         /* Make the string attribute lower case */
1015         LocaseSVal ();
1016
1017         /* Set the feature and check for errors */
1018         if (SetFeature (&CurTok.SVal) == FEAT_UNKNOWN) {
1019             /* Not found */
1020             ErrorSkip ("Invalid feature: '%m%p'", &CurTok.SVal);
1021             return;
1022         } else {
1023             /* Skip the keyword */
1024             NextTok ();
1025         }
1026
1027         /* Allow more than one keyword */
1028         if (CurTok.Tok == TOK_COMMA) {
1029             NextTok ();
1030         } else {
1031             break;
1032         }
1033     }
1034 }
1035
1036
1037
1038 static void DoFileOpt (void)
1039 /* Insert a file option */
1040 {
1041     long OptNum;
1042
1043     /* The option type may be given as a keyword or as a number. */
1044     if (CurTok.Tok == TOK_IDENT) {
1045
1046         /* Option given as keyword */
1047         static const char* const Keys [] = {
1048             "AUTHOR", "COMMENT", "COMPILER"
1049         };
1050
1051         /* Map the option to a number */
1052         OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
1053         if (OptNum < 0) {
1054             /* Not found */
1055             ErrorSkip ("File option keyword expected");
1056             return;
1057         }
1058
1059         /* Skip the keyword */
1060         NextTok ();
1061
1062         /* Must be followed by a comma */
1063         ConsumeComma ();
1064
1065         /* We accept only string options for now */
1066         if (CurTok.Tok != TOK_STRCON) {
1067             ErrorSkip ("String constant expected");
1068             return;
1069         }
1070
1071         /* Insert the option */
1072         switch (OptNum) {
1073
1074             case 0:
1075                 /* Author */
1076                 OptAuthor (&CurTok.SVal);
1077                 break;
1078
1079             case 1:
1080                 /* Comment */
1081                 OptComment (&CurTok.SVal);
1082                 break;
1083
1084             case 2:
1085                 /* Compiler */
1086                 OptCompiler (&CurTok.SVal);
1087                 break;
1088
1089             default:
1090                 Internal ("Invalid OptNum: %ld", OptNum);
1091
1092         }
1093
1094         /* Done */
1095         NextTok ();
1096
1097     } else {
1098
1099         /* Option given as number */
1100         OptNum = ConstExpression ();
1101         if (!IsByteRange (OptNum)) {
1102             ErrorSkip ("Range error");
1103             return;
1104         }
1105
1106         /* Must be followed by a comma */
1107         ConsumeComma ();
1108
1109         /* We accept only string options for now */
1110         if (CurTok.Tok != TOK_STRCON) {
1111             ErrorSkip ("String constant expected");
1112             return;
1113         }
1114
1115         /* Insert the option */
1116         OptStr ((unsigned char) OptNum, &CurTok.SVal);
1117
1118         /* Done */
1119         NextTok ();
1120     }
1121 }
1122
1123
1124
1125 static void DoForceImport (void)
1126 /* Do a forced import on a symbol */
1127 {
1128     ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_FORCED);
1129 }
1130
1131
1132
1133 static void DoGlobal (void)
1134 /* Declare a global symbol */
1135 {
1136     ExportImport (SymGlobal, ADDR_SIZE_DEFAULT, SF_NONE);
1137 }
1138
1139
1140
1141 static void DoGlobalZP (void)
1142 /* Declare a global zeropage symbol */
1143 {
1144     ExportImport (SymGlobal, ADDR_SIZE_ZP, SF_NONE);
1145 }
1146
1147
1148 static void DoHiBytes (void)
1149 /* Define bytes, extracting the hi byte from each expression in the list */
1150 {
1151     while (1) {
1152         EmitByte (FuncHiByte ());
1153         if (CurTok.Tok != TOK_COMMA) {
1154             break;
1155         } else {
1156             NextTok ();
1157         }
1158     }
1159 }
1160
1161
1162
1163 static void DoI16 (void)
1164 /* Switch the index registers to 16 bit mode (assembler only) */
1165 {
1166     if (GetCPU() != CPU_65816) {
1167         Error ("Command is only valid in 65816 mode");
1168     } else {
1169         /* Immidiate mode has two extension bytes */
1170         ExtBytes [AM65I_IMM_INDEX] = 2;
1171     }
1172 }
1173
1174
1175
1176 static void DoI8 (void)
1177 /* Switch the index registers to 16 bit mode (assembler only) */
1178 {
1179     if (GetCPU() != CPU_65816) {
1180         Error ("Command is only valid in 65816 mode");
1181     } else {
1182         /* Immidiate mode has one extension byte */
1183         ExtBytes [AM65I_IMM_INDEX] = 1;
1184     }
1185 }
1186
1187
1188
1189 static void DoImport (void)
1190 /* Import a symbol */
1191 {
1192     ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_NONE);
1193 }
1194
1195
1196
1197 static void DoImportZP (void)
1198 /* Import a zero page symbol */
1199 {
1200     ExportImport (SymImport, ADDR_SIZE_ZP, SF_NONE);
1201 }
1202
1203
1204
1205 static void DoIncBin (void)
1206 /* Include a binary file */
1207 {
1208     StrBuf Name = STATIC_STRBUF_INITIALIZER;
1209     struct stat StatBuf;
1210     long Start = 0L;
1211     long Count = -1L;
1212     long Size;
1213     FILE* F;
1214
1215     /* Name must follow */
1216     if (CurTok.Tok != TOK_STRCON) {
1217         ErrorSkip ("String constant expected");
1218         return;
1219     }
1220     SB_Copy (&Name, &CurTok.SVal);
1221     SB_Terminate (&Name);
1222     NextTok ();
1223
1224     /* A starting offset may follow */
1225     if (CurTok.Tok == TOK_COMMA) {
1226         NextTok ();
1227         Start = ConstExpression ();
1228
1229         /* And a length may follow */
1230         if (CurTok.Tok == TOK_COMMA) {
1231             NextTok ();
1232             Count = ConstExpression ();
1233         }
1234
1235     }
1236
1237     /* Try to open the file */
1238     F = fopen (SB_GetConstBuf (&Name), "rb");
1239     if (F == 0) {
1240
1241         /* Search for the file in the binary include directory */
1242         char* PathName = SearchFile (BinSearchPath, SB_GetConstBuf (&Name));
1243         if (PathName == 0 || (F = fopen (PathName, "rb")) == 0) {
1244             /* Not found or cannot open, print an error and bail out */
1245             ErrorSkip ("Cannot open include file '%m%p': %s", &Name, strerror (errno));
1246             xfree (PathName);
1247             goto ExitPoint;
1248         }
1249
1250         /* Remember the new file name */
1251         SB_CopyStr (&Name, PathName);
1252
1253         /* Free the allocated memory */
1254         xfree (PathName);
1255     }
1256
1257     /* Get the size of the file */
1258     fseek (F, 0, SEEK_END);
1259     Size = ftell (F);
1260
1261     /* Stat the file and remember the values. There's a race condition here,
1262     ** since we cannot use fileno() (non-standard identifier in standard
1263     ** header file), and therefore not fstat. When using stat with the
1264     ** file name, there's a risk that the file was deleted and recreated
1265     ** while it was open. Since mtime and size are only used to check
1266     ** if a file has changed in the debugger, we will ignore this problem
1267     ** here.
1268     */
1269     SB_Terminate (&Name);
1270     if (FileStat (SB_GetConstBuf (&Name), &StatBuf) != 0) {
1271         Fatal ("Cannot stat input file '%m%p': %s", &Name, strerror (errno));
1272     }
1273
1274     /* Add the file to the input file table */
1275     AddFile (&Name, FT_BINARY, Size, (unsigned long) StatBuf.st_mtime);
1276
1277     /* If a count was not given, calculate it now */
1278     if (Count < 0) {
1279         Count = Size - Start;
1280         if (Count < 0) {
1281             /* Nothing to read - flag this as a range error */
1282             ErrorSkip ("Range error");
1283             goto Done;
1284         }
1285     } else {
1286         /* Count was given, check if it is valid */
1287         if (Start + Count > Size) {
1288             ErrorSkip ("Range error");
1289             goto Done;
1290         }
1291     }
1292
1293     /* Seek to the start position */
1294     fseek (F, Start, SEEK_SET);
1295
1296     /* Read chunks and insert them into the output */
1297     while (Count > 0) {
1298
1299         unsigned char Buf [1024];
1300
1301         /* Calculate the number of bytes to read */
1302         size_t BytesToRead = (Count > (long)sizeof(Buf))? sizeof(Buf) : (size_t) Count;
1303
1304         /* Read chunk */
1305         size_t BytesRead = fread (Buf, 1, BytesToRead, F);
1306         if (BytesToRead != BytesRead) {
1307             /* Some sort of error */
1308             ErrorSkip ("Cannot read from include file '%m%p': %s",
1309                        &Name, strerror (errno));
1310             break;
1311         }
1312
1313         /* Insert it into the output */
1314         EmitData (Buf, BytesRead);
1315
1316         /* Keep the counters current */
1317         Count -= BytesRead;
1318     }
1319
1320 Done:
1321     /* Close the file, ignore errors since it's r/o */
1322     (void) fclose (F);
1323
1324 ExitPoint:
1325     /* Free string memory */
1326     SB_Done (&Name);
1327 }
1328
1329
1330
1331 static void DoInclude (void)
1332 /* Include another file */
1333 {
1334     /* Name must follow */
1335     if (CurTok.Tok != TOK_STRCON) {
1336         ErrorSkip ("String constant expected");
1337     } else {
1338         SB_Terminate (&CurTok.SVal);
1339         if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) {
1340             /* Error opening the file, skip remainder of line */
1341             SkipUntilSep ();
1342         }
1343     }
1344 }
1345
1346
1347
1348 static void DoInterruptor (void)
1349 /* Export a symbol as interruptor */
1350 {
1351     StrBuf Name = STATIC_STRBUF_INITIALIZER;
1352
1353     /* Symbol name follows */
1354     if (CurTok.Tok != TOK_IDENT) {
1355         ErrorSkip ("Identifier expected");
1356         return;
1357     }
1358     SB_Copy (&Name, &CurTok.SVal);
1359     NextTok ();
1360
1361     /* Parse the remainder of the line and export the symbol */
1362     ConDes (&Name, CD_TYPE_INT);
1363
1364     /* Free string memory */
1365     SB_Done (&Name);
1366 }
1367
1368
1369
1370 static void DoInvalid (void)
1371 /* Handle a token that is invalid here, since it should have been handled on
1372 ** a much lower level of the expression hierarchy. Getting this sort of token
1373 ** means that the lower level code has bugs.
1374 ** This function differs to DoUnexpected in that the latter may be triggered
1375 ** by the user by using keywords in the wrong location. DoUnexpected is not
1376 ** an error in the assembler itself, while DoInvalid is.
1377 */
1378 {
1379     Internal ("Unexpected token: %m%p", &Keyword);
1380 }
1381
1382
1383
1384 static void DoLineCont (void)
1385 /* Switch the use of line continuations */
1386 {
1387     SetBoolOption (&LineCont);
1388 }
1389
1390
1391
1392 static void DoList (void)
1393 /* Enable/disable the listing */
1394 {
1395     /* Get the setting */
1396     unsigned char List = 0;
1397     SetBoolOption (&List);
1398
1399     /* Manage the counter */
1400     if (List) {
1401         EnableListing ();
1402     } else {
1403         DisableListing ();
1404     }
1405 }
1406
1407
1408
1409 static void DoLoBytes (void)
1410 /* Define bytes, extracting the lo byte from each expression in the list */
1411 {
1412     while (1) {
1413         EmitByte (FuncLoByte ());
1414         if (CurTok.Tok != TOK_COMMA) {
1415             break;
1416         } else {
1417             NextTok ();
1418         }
1419     }
1420 }
1421
1422
1423 static void DoListBytes (void)
1424 /* Set maximum number of bytes to list for one line */
1425 {
1426     SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
1427 }
1428
1429
1430
1431 static void DoLocalChar (void)
1432 /* Define the character that starts local labels */
1433 {
1434     if (CurTok.Tok != TOK_CHARCON) {
1435         ErrorSkip ("Character constant expected");
1436     } else {
1437         if (CurTok.IVal != '@' && CurTok.IVal != '?') {
1438             Error ("Invalid start character for locals");
1439         } else {
1440             LocalStart = (char) CurTok.IVal;
1441         }
1442         NextTok ();
1443     }
1444 }
1445
1446
1447
1448 static void DoMacPack (void)
1449 /* Insert a macro package */
1450 {
1451     /* We expect an identifier */
1452     if (CurTok.Tok != TOK_IDENT) {
1453         ErrorSkip ("Identifier expected");
1454     } else {
1455         SB_AppendStr (&CurTok.SVal, ".mac");
1456         SB_Terminate (&CurTok.SVal);
1457         if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) {
1458             /* Error opening the file, skip remainder of line */
1459             SkipUntilSep ();
1460         }
1461     }
1462 }
1463
1464
1465
1466 static void DoMacro (void)
1467 /* Start a macro definition */
1468 {
1469     MacDef (MAC_STYLE_CLASSIC);
1470 }
1471
1472
1473
1474 static void DoNull (void)
1475 /* Switch to the NULL segment */
1476 {
1477     UseSeg (&NullSegDef);
1478 }
1479
1480
1481
1482 static void DoOrg (void)
1483 /* Start absolute code */
1484 {
1485     long PC = ConstExpression ();
1486     if (PC < 0 || PC > 0xFFFFFF) {
1487         Error ("Range error");
1488         return;
1489     }
1490     EnterAbsoluteMode (PC);
1491 }
1492
1493
1494
1495 static void DoOut (void)
1496 /* Output a string */
1497 {
1498     if (CurTok.Tok != TOK_STRCON) {
1499         ErrorSkip ("String constant expected");
1500     } else {
1501         /* Output the string and be sure to flush the output to keep it in
1502         ** sync with any error messages if the output is redirected to a file.
1503         */
1504         printf ("%.*s\n",
1505                 (int) SB_GetLen (&CurTok.SVal),
1506                 SB_GetConstBuf (&CurTok.SVal));
1507         fflush (stdout);
1508         NextTok ();
1509     }
1510 }
1511
1512
1513
1514 static void DoP02 (void)
1515 /* Switch to 6502 CPU */
1516 {
1517     SetCPU (CPU_6502);
1518 }
1519
1520
1521
1522 static void DoPC02 (void)
1523 /* Switch to 65C02 CPU */
1524 {
1525     SetCPU (CPU_65C02);
1526 }
1527
1528
1529
1530 static void DoP816 (void)
1531 /* Switch to 65816 CPU */
1532 {
1533     SetCPU (CPU_65816);
1534 }
1535
1536
1537
1538 static void DoP4510 (void)
1539 /* Switch to 4510 CPU */
1540 {
1541     SetCPU (CPU_4510);
1542 }
1543
1544
1545
1546 static void DoPageLength (void)
1547 /* Set the page length for the listing */
1548 {
1549     PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
1550 }
1551
1552
1553
1554 static void DoPopCPU (void)
1555 /* Pop an old CPU setting from the CPU stack */
1556 {
1557     /* Must have a CPU on the stack */
1558     if (IS_IsEmpty (&CPUStack)) {
1559         ErrorSkip ("CPU stack is empty");
1560         return;
1561     }
1562
1563     /* Set the CPU to the value popped from stack */
1564     SetCPU (IS_Pop (&CPUStack));
1565 }
1566
1567
1568
1569 static void DoPopSeg (void)
1570 /* Pop an old segment from the segment stack */
1571 {
1572     SegDef* Def;
1573
1574     /* Must have a segment on the stack */
1575     if (CollCount (&SegStack) == 0) {
1576         ErrorSkip ("Segment stack is empty");
1577         return;
1578     }
1579
1580     /* Pop the last element */
1581     Def = CollPop (&SegStack);
1582
1583     /* Restore this segment */
1584     UseSeg (Def);
1585
1586     /* Delete the segment definition */
1587     FreeSegDef (Def);
1588 }
1589
1590
1591
1592 static void DoProc (void)
1593 /* Start a new lexical scope */
1594 {
1595     StrBuf Name = STATIC_STRBUF_INITIALIZER;
1596     unsigned char AddrSize;
1597     SymEntry* Sym = 0;
1598
1599
1600     if (CurTok.Tok == TOK_IDENT) {
1601
1602         /* The new scope has a name. Remember it. */
1603         SB_Copy (&Name, &CurTok.SVal);
1604
1605         /* Search for the symbol, generate a new one if needed */
1606         Sym = SymFind (CurrentScope, &Name, SYM_ALLOC_NEW);
1607
1608         /* Skip the scope name */
1609         NextTok ();
1610
1611         /* Read an optional address size specifier */
1612         AddrSize = OptionalAddrSize ();
1613
1614         /* Mark the symbol as defined */
1615         SymDef (Sym, GenCurrentPC (), AddrSize, SF_LABEL);
1616
1617     } else {
1618
1619         /* A .PROC statement without a name */
1620         Warning (1, "Unnamed .PROCs are deprecated, please use .SCOPE");
1621         AnonName (&Name, "PROC");
1622         AddrSize = ADDR_SIZE_DEFAULT;
1623
1624     }
1625
1626     /* Enter a new scope */
1627     SymEnterLevel (&Name, SCOPE_SCOPE, AddrSize, Sym);
1628
1629     /* Free memory for Name */
1630     SB_Done (&Name);
1631 }
1632
1633
1634
1635 static void DoPSC02 (void)
1636 /* Switch to 65SC02 CPU */
1637 {
1638     SetCPU (CPU_65SC02);
1639 }
1640
1641
1642
1643 static void DoPushCPU (void)
1644 /* Push the current CPU setting onto the CPU stack */
1645 {
1646     /* Can only push a limited size of segments */
1647     if (IS_IsFull (&CPUStack)) {
1648         ErrorSkip ("CPU stack overflow");
1649         return;
1650     }
1651
1652     /* Get the current segment and push it */
1653     IS_Push (&CPUStack, GetCPU ());
1654 }
1655
1656
1657
1658 static void DoPushSeg (void)
1659 /* Push the current segment onto the segment stack */
1660 {
1661     /* Can only push a limited size of segments */
1662     if (CollCount (&SegStack) >= MAX_PUSHED_SEGMENTS) {
1663         ErrorSkip ("Segment stack overflow");
1664         return;
1665     }
1666
1667     /* Get the current segment and push it */
1668     CollAppend (&SegStack, DupSegDef (GetCurrentSegDef ()));
1669 }
1670
1671
1672
1673 static void DoReloc (void)
1674 /* Enter relocatable mode */
1675 {
1676     EnterRelocMode ();
1677 }
1678
1679
1680
1681 static void DoRepeat (void)
1682 /* Repeat some instruction block */
1683 {
1684     ParseRepeat ();
1685 }
1686
1687
1688
1689 static void DoRes (void)
1690 /* Reserve some number of storage bytes */
1691 {
1692     long Count;
1693     long Val;
1694
1695     Count = ConstExpression ();
1696     if (Count > 0xFFFF || Count < 0) {
1697         ErrorSkip ("Range error");
1698         return;
1699     }
1700     if (CurTok.Tok == TOK_COMMA) {
1701         NextTok ();
1702         Val = ConstExpression ();
1703         /* We need a byte value here */
1704         if (!IsByteRange (Val)) {
1705             ErrorSkip ("Range error");
1706             return;
1707         }
1708
1709         /* Emit constant values */
1710         while (Count--) {
1711             Emit0 ((unsigned char) Val);
1712         }
1713
1714     } else {
1715         /* Emit fill fragments */
1716         EmitFill (Count);
1717     }
1718 }
1719
1720
1721
1722 static void DoROData (void)
1723 /* Switch to the r/o data segment */
1724 {
1725     UseSeg (&RODataSegDef);
1726 }
1727
1728
1729
1730 static void DoScope (void)
1731 /* Start a local scope */
1732 {
1733     StrBuf Name = STATIC_STRBUF_INITIALIZER;
1734     unsigned char AddrSize;
1735
1736
1737     if (CurTok.Tok == TOK_IDENT) {
1738
1739         /* The new scope has a name. Remember and skip it. */
1740         SB_Copy (&Name, &CurTok.SVal);
1741         NextTok ();
1742
1743     } else {
1744
1745         /* An unnamed scope */
1746         AnonName (&Name, "SCOPE");
1747
1748     }
1749
1750     /* Read an optional address size specifier */
1751     AddrSize = OptionalAddrSize ();
1752
1753     /* Enter the new scope */
1754     SymEnterLevel (&Name, SCOPE_SCOPE, AddrSize, 0);
1755
1756     /* Free memory for Name */
1757     SB_Done (&Name);
1758 }
1759
1760
1761
1762 static void DoSegment (void)
1763 /* Switch to another segment */
1764 {
1765     StrBuf Name = STATIC_STRBUF_INITIALIZER;
1766     SegDef Def;
1767
1768     if (CurTok.Tok != TOK_STRCON) {
1769         ErrorSkip ("String constant expected");
1770     } else {
1771
1772         /* Save the name of the segment and skip it */
1773         SB_Copy (&Name, &CurTok.SVal);
1774         NextTok ();
1775
1776         /* Use the name for the segment definition */
1777         SB_Terminate (&Name);
1778         Def.Name = SB_GetBuf (&Name);
1779
1780         /* Check for an optional address size modifier */
1781         Def.AddrSize = OptionalAddrSize ();
1782
1783         /* Set the segment */
1784         UseSeg (&Def);
1785     }
1786
1787     /* Free memory for Name */
1788     SB_Done (&Name);
1789 }
1790
1791
1792
1793 static void DoSetCPU (void)
1794 /* Switch the CPU instruction set */
1795 {
1796     /* We expect an identifier */
1797     if (CurTok.Tok != TOK_STRCON) {
1798         ErrorSkip ("String constant expected");
1799     } else {
1800         cpu_t CPU;
1801
1802         /* Try to find the CPU */
1803         SB_Terminate (&CurTok.SVal);
1804         CPU = FindCPU (SB_GetConstBuf (&CurTok.SVal));
1805
1806         /* Switch to the new CPU */
1807         SetCPU (CPU);
1808
1809         /* Skip the identifier. If the CPU switch was successful, the scanner
1810         ** will treat the input now correctly for the new CPU.
1811         */
1812         NextTok ();
1813     }
1814 }
1815
1816
1817
1818 static void DoSmart (void)
1819 /* Smart mode on/off */
1820 {
1821     SetBoolOption (&SmartMode);
1822 }
1823
1824
1825
1826 static void DoTag (void)
1827 /* Allocate space for a struct */
1828 {
1829     SymEntry* SizeSym;
1830     long Size;
1831
1832     /* Read the struct name */
1833     SymTable* Struct = ParseScopedSymTable ();
1834
1835     /* Check the supposed struct */
1836     if (Struct == 0) {
1837         ErrorSkip ("Unknown struct");
1838         return;
1839     }
1840     if (GetSymTabType (Struct) != SCOPE_STRUCT) {
1841         ErrorSkip ("Not a struct");
1842         return;
1843     }
1844
1845     /* Get the symbol that defines the size of the struct */
1846     SizeSym = GetSizeOfScope (Struct);
1847
1848     /* Check if it does exist and if its value is known */
1849     if (SizeSym == 0 || !SymIsConst (SizeSym, &Size)) {
1850         ErrorSkip ("Size of struct/union is unknown");
1851         return;
1852     }
1853
1854     /* Optional multiplicator may follow */
1855     if (CurTok.Tok == TOK_COMMA) {
1856         long Multiplicator;
1857         NextTok ();
1858         Multiplicator = ConstExpression ();
1859         /* Multiplicator must make sense */
1860         if (Multiplicator <= 0) {
1861             ErrorSkip ("Range error");
1862             return;
1863         }
1864         Size *= Multiplicator;
1865     }
1866
1867     /* Emit fill fragments */
1868     EmitFill (Size);
1869 }
1870
1871
1872
1873 static void DoUnDef (void)
1874 /* Undefine a define style macro */
1875 {
1876     /* The function is called with the .UNDEF token in place, because we need
1877     ** to disable .define macro expansions before reading the next token.
1878     ** Otherwise the name of the macro would be expanded, so we would never
1879     ** see it.
1880     */
1881     DisableDefineStyleMacros ();
1882     NextTok ();
1883     EnableDefineStyleMacros ();
1884
1885     /* We expect an identifier */
1886     if (CurTok.Tok != TOK_IDENT) {
1887         ErrorSkip ("Identifier expected");
1888     } else {
1889         MacUndef (&CurTok.SVal, MAC_STYLE_DEFINE);
1890         NextTok ();
1891     }
1892 }
1893
1894
1895
1896 static void DoUnexpected (void)
1897 /* Got an unexpected keyword */
1898 {
1899     Error ("Unexpected '%m%p'", &Keyword);
1900     SkipUntilSep ();
1901 }
1902
1903
1904
1905 static void DoWarning (void)
1906 /* User warning */
1907 {
1908     if (CurTok.Tok != TOK_STRCON) {
1909         ErrorSkip ("String constant expected");
1910     } else {
1911         Warning (0, "User warning: %m%p", &CurTok.SVal);
1912         SkipUntilSep ();
1913     }
1914 }
1915
1916
1917
1918 static void DoWord (void)
1919 /* Define words */
1920 {
1921     /* Element type for the generated array */
1922     static const char EType[1] = { GT_WORD };
1923
1924     /* Record type information */
1925     Span* S = OpenSpan ();
1926     StrBuf Type = STATIC_STRBUF_INITIALIZER;
1927
1928     /* Parse arguments */
1929     while (1) {
1930         EmitWord (BoundedExpr (Expression, 2));
1931         if (CurTok.Tok != TOK_COMMA) {
1932             break;
1933         } else {
1934             NextTok ();
1935         }
1936     }
1937
1938     /* Close the span, then add type information to it */
1939     S = CloseSpan (S);
1940     SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
1941
1942     /* Free the type string */
1943     SB_Done (&Type);
1944 }
1945
1946
1947
1948 static void DoZeropage (void)
1949 /* Switch to the zeropage segment */
1950 {
1951     UseSeg (&ZeropageSegDef);
1952 }
1953
1954
1955
1956 /*****************************************************************************/
1957 /*                                Table data                                 */
1958 /*****************************************************************************/
1959
1960
1961
1962 /* Control commands flags */
1963 enum {
1964     ccNone      = 0x0000,               /* No special flags */
1965     ccKeepToken = 0x0001                /* Do not skip the current token */
1966 };
1967
1968 /* Control command table */
1969 typedef struct CtrlDesc CtrlDesc;
1970 struct CtrlDesc {
1971     unsigned    Flags;                  /* Flags for this directive */
1972     void        (*Handler) (void);      /* Command handler */
1973 };
1974
1975 #define PSEUDO_COUNT    (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1976 static CtrlDesc CtrlCmdTab [] = {
1977     { ccNone,           DoA16           },
1978     { ccNone,           DoA8            },
1979     { ccNone,           DoAddr          },      /* .ADDR */
1980     { ccNone,           DoUnexpected    },      /* .ADDRSIZE */
1981     { ccNone,           DoAlign         },
1982     { ccNone,           DoASCIIZ        },
1983     { ccNone,           DoUnexpected    },      /* .ASIZE */
1984     { ccNone,           DoAssert        },
1985     { ccNone,           DoAutoImport    },
1986     { ccNone,           DoUnexpected    },      /* .BANK */
1987     { ccNone,           DoUnexpected    },      /* .BANKBYTE */
1988     { ccNone,           DoBankBytes     },
1989     { ccNone,           DoUnexpected    },      /* .BLANK */
1990     { ccNone,           DoBss           },
1991     { ccNone,           DoByte          },
1992     { ccNone,           DoCase          },
1993     { ccNone,           DoCharMap       },
1994     { ccNone,           DoCode          },
1995     { ccNone,           DoUnexpected,   },      /* .CONCAT */
1996     { ccNone,           DoConDes        },
1997     { ccNone,           DoUnexpected    },      /* .CONST */
1998     { ccNone,           DoConstructor   },
1999     { ccNone,           DoUnexpected    },      /* .CPU */
2000     { ccNone,           DoData          },
2001     { ccNone,           DoDbg,          },
2002     { ccNone,           DoDByt          },
2003     { ccNone,           DoDebugInfo     },
2004     { ccNone,           DoDefine        },
2005     { ccNone,           DoUnexpected    },      /* .DEFINED */
2006     { ccNone,           DoUnexpected    },      /* .DEFINEDMACRO */
2007     { ccNone,           DoDelMac        },
2008     { ccNone,           DoDestructor    },
2009     { ccNone,           DoDWord         },
2010     { ccKeepToken,      DoConditionals  },      /* .ELSE */
2011     { ccKeepToken,      DoConditionals  },      /* .ELSEIF */
2012     { ccKeepToken,      DoEnd           },
2013     { ccNone,           DoUnexpected    },      /* .ENDENUM */
2014     { ccKeepToken,      DoConditionals  },      /* .ENDIF */
2015     { ccNone,           DoUnexpected    },      /* .ENDMACRO */
2016     { ccNone,           DoEndProc       },
2017     { ccNone,           DoUnexpected    },      /* .ENDREPEAT */
2018     { ccNone,           DoEndScope      },
2019     { ccNone,           DoUnexpected    },      /* .ENDSTRUCT */
2020     { ccNone,           DoUnexpected    },      /* .ENDUNION */
2021     { ccNone,           DoEnum          },
2022     { ccNone,           DoError         },
2023     { ccNone,           DoExitMacro     },
2024     { ccNone,           DoExport        },
2025     { ccNone,           DoExportZP      },
2026     { ccNone,           DoFarAddr       },
2027     { ccNone,           DoFatal         },
2028     { ccNone,           DoFeature       },
2029     { ccNone,           DoFileOpt       },
2030     { ccNone,           DoForceImport   },
2031     { ccNone,           DoUnexpected    },      /* .FORCEWORD */
2032     { ccNone,           DoGlobal        },
2033     { ccNone,           DoGlobalZP      },
2034     { ccNone,           DoUnexpected    },      /* .HIBYTE */
2035     { ccNone,           DoHiBytes       },
2036     { ccNone,           DoUnexpected    },      /* .HIWORD */
2037     { ccNone,           DoI16           },
2038     { ccNone,           DoI8            },
2039     { ccNone,           DoUnexpected    },      /* .IDENT */
2040     { ccKeepToken,      DoConditionals  },      /* .IF */
2041     { ccKeepToken,      DoConditionals  },      /* .IFBLANK */
2042     { ccKeepToken,      DoConditionals  },      /* .IFCONST */
2043     { ccKeepToken,      DoConditionals  },      /* .IFDEF */
2044     { ccKeepToken,      DoConditionals  },      /* .IFNBLANK */
2045     { ccKeepToken,      DoConditionals  },      /* .IFNCONST */
2046     { ccKeepToken,      DoConditionals  },      /* .IFNDEF */
2047     { ccKeepToken,      DoConditionals  },      /* .IFNREF */
2048     { ccKeepToken,      DoConditionals  },      /* .IFP02 */
2049     { ccKeepToken,      DoConditionals  },      /* .IFP4510 */
2050     { ccKeepToken,      DoConditionals  },      /* .IFP816 */
2051     { ccKeepToken,      DoConditionals  },      /* .IFPC02 */
2052     { ccKeepToken,      DoConditionals  },      /* .IFPSC02 */
2053     { ccKeepToken,      DoConditionals  },      /* .IFREF */
2054     { ccNone,           DoImport        },
2055     { ccNone,           DoImportZP      },
2056     { ccNone,           DoIncBin        },
2057     { ccNone,           DoInclude       },
2058     { ccNone,           DoInterruptor   },
2059     { ccNone,           DoUnexpected    },      /* .ISIZE */
2060     { ccNone,           DoUnexpected    },      /* .ISMNEMONIC */
2061     { ccNone,           DoInvalid       },      /* .LEFT */
2062     { ccNone,           DoLineCont      },
2063     { ccNone,           DoList          },
2064     { ccNone,           DoListBytes     },
2065     { ccNone,           DoUnexpected    },      /* .LOBYTE */
2066     { ccNone,           DoLoBytes       },
2067     { ccNone,           DoUnexpected    },      /* .LOCAL */
2068     { ccNone,           DoLocalChar     },
2069     { ccNone,           DoUnexpected    },      /* .LOWORD */
2070     { ccNone,           DoMacPack       },
2071     { ccNone,           DoMacro         },
2072     { ccNone,           DoUnexpected    },      /* .MATCH */
2073     { ccNone,           DoUnexpected    },      /* .MAX */
2074     { ccNone,           DoInvalid       },      /* .MID */
2075     { ccNone,           DoUnexpected    },      /* .MIN */
2076     { ccNone,           DoNull          },
2077     { ccNone,           DoOrg           },
2078     { ccNone,           DoOut           },
2079     { ccNone,           DoP02           },
2080     { ccNone,           DoP4510         },
2081     { ccNone,           DoP816          },
2082     { ccNone,           DoPageLength    },
2083     { ccNone,           DoUnexpected    },      /* .PARAMCOUNT */
2084     { ccNone,           DoPC02          },
2085     { ccNone,           DoPopCPU        },
2086     { ccNone,           DoPopSeg        },
2087     { ccNone,           DoProc          },
2088     { ccNone,           DoPSC02         },
2089     { ccNone,           DoPushCPU       },
2090     { ccNone,           DoPushSeg       },
2091     { ccNone,           DoUnexpected    },      /* .REFERENCED */
2092     { ccNone,           DoReloc         },
2093     { ccNone,           DoRepeat        },
2094     { ccNone,           DoRes           },
2095     { ccNone,           DoInvalid       },      /* .RIGHT */
2096     { ccNone,           DoROData        },
2097     { ccNone,           DoScope         },
2098     { ccNone,           DoSegment       },
2099     { ccNone,           DoUnexpected    },      /* .SET */
2100     { ccNone,           DoSetCPU        },
2101     { ccNone,           DoUnexpected    },      /* .SIZEOF */
2102     { ccNone,           DoSmart         },
2103     { ccNone,           DoUnexpected    },      /* .SPRINTF */
2104     { ccNone,           DoUnexpected    },      /* .STRAT */
2105     { ccNone,           DoUnexpected    },      /* .STRING */
2106     { ccNone,           DoUnexpected    },      /* .STRLEN */
2107     { ccNone,           DoStruct        },
2108     { ccNone,           DoTag           },
2109     { ccNone,           DoUnexpected    },      /* .TCOUNT */
2110     { ccNone,           DoUnexpected    },      /* .TIME */
2111     { ccKeepToken,      DoUnDef         },
2112     { ccNone,           DoUnion         },
2113     { ccNone,           DoUnexpected    },      /* .VERSION */
2114     { ccNone,           DoWarning       },
2115     { ccNone,           DoWord          },
2116     { ccNone,           DoUnexpected    },      /* .XMATCH */
2117     { ccNone,           DoZeropage      },
2118 };
2119
2120
2121
2122 /*****************************************************************************/
2123 /*                                   Code                                    */
2124 /*****************************************************************************/
2125
2126
2127
2128 void HandlePseudo (void)
2129 /* Handle a pseudo instruction */
2130 {
2131     CtrlDesc* D;
2132
2133     /* Calculate the index into the table */
2134     unsigned Index = CurTok.Tok - TOK_FIRSTPSEUDO;
2135
2136     /* Safety check */
2137     if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
2138         Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
2139                   (unsigned) PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
2140     }
2141     CHECK (Index < PSEUDO_COUNT);
2142
2143     /* Get the pseudo intruction descriptor */
2144     D = &CtrlCmdTab [Index];
2145
2146     /* Remember the instruction, then skip it if needed */
2147     if ((D->Flags & ccKeepToken) == 0) {
2148         SB_Copy (&Keyword, &CurTok.SVal);
2149         NextTok ();
2150     }
2151
2152     /* Call the handler */
2153     D->Handler ();
2154 }
2155
2156
2157
2158 void CheckPseudo (void)
2159 /* Check if the stacks are empty at end of assembly */
2160 {
2161     if (CollCount (&SegStack) != 0) {
2162         Warning (1, "Segment stack is not empty");
2163     }
2164     if (!IS_IsEmpty (&CPUStack)) {
2165         Warning (1, "CPU stack is not empty");
2166     }
2167 }