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