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