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