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