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