]> git.sur5r.net Git - cc65/blob - src/ca65/pseudo.c
e7e5d1a6eaec6edbf568786f1ad9d1dcb385d83d
[cc65] / src / ca65 / pseudo.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 pseudo.c                                  */
4 /*                                                                           */
5 /*              Pseudo instructions for the ca65 macroassembler              */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2007, 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 char Keyword [sizeof (SVal)+1];
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 && strcmp (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 char* 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     unsigned Len;
380
381     while (1) {
382         /* Must have a string constant */
383         if (Tok != TOK_STRCON) {
384             ErrorSkip ("String constant expected");
385             return;
386         }
387
388         /* Get the length of the string constant */
389         Len = strlen (SVal);
390
391         /* Translate into target charset and emit */
392         TgtTranslateBuf (SVal, Len);
393         EmitData ((unsigned char*) SVal, Len);
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 = GetStringId (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             unsigned Len = strlen (SVal);
501             TgtTranslateBuf (SVal, Len);
502             EmitData ((unsigned char*) SVal, Len);
503             NextTok ();
504         } else {
505             EmitByte (Expression ());
506         }
507         if (Tok != TOK_COMMA) {
508             break;
509         } else {
510             NextTok ();
511             /* Do smart handling of dangling comma */
512             if (Tok == TOK_SEP) {
513                 Error ("Unexpected end of line");
514                 break;
515             }
516         }
517     }
518 }
519
520
521
522 static void DoCase (void)
523 /* Switch the IgnoreCase option */
524 {
525     SetBoolOption (&IgnoreCase);
526     IgnoreCase = !IgnoreCase;
527 }
528
529
530
531 static void DoCharMap (void)
532 /* Allow custome character mappings */
533 {
534     long Index;
535     long Code;
536
537     /* Read the index as numerical value */
538     Index = ConstExpression ();
539     if (Index < 0 || Index > 255) {
540         /* Value out of range */
541         ErrorSkip ("Range error");
542         return;
543     }
544
545     /* Comma follows */
546     ConsumeComma ();
547
548     /* Read the character code */
549     Code = ConstExpression ();
550     if (Code < 0 || Code > 255) {
551         /* Value out of range */
552         ErrorSkip ("Range error");
553         return;
554     }
555
556     /* Set the character translation */
557     TgtTranslateSet ((unsigned) Index, (unsigned char) Code);
558 }
559
560
561
562 static void DoCode (void)
563 /* Switch to the code segment */
564 {
565     UseSeg (&CodeSegDef);
566 }
567
568
569
570 static void DoConDes (void)
571 /* Export a symbol as constructor/destructor */
572 {
573     static const char* Keys[] = {
574         "CONSTRUCTOR",
575         "DESTRUCTOR",
576         "INTERRUPTOR",
577     };
578     char Name [sizeof (SVal)];
579     long Type;
580
581     /* Symbol name follows */
582     if (Tok != TOK_IDENT) {
583         ErrorSkip ("Identifier expected");
584         return;
585     }
586     strcpy (Name, SVal);
587     NextTok ();
588
589     /* Type follows. May be encoded as identifier or numerical */
590     ConsumeComma ();
591     if (Tok == TOK_IDENT) {
592
593         /* Map the following keyword to a number, then skip it */
594         Type = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
595         NextTok ();
596
597         /* Check if we got a valid keyword */
598         if (Type < 0) {
599             Error ("Syntax error");
600             SkipUntilSep ();
601             return;
602         }
603
604     } else {
605
606         /* Read the type as numerical value */
607         Type = ConstExpression ();
608         if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) {
609             /* Value out of range */
610             Error ("Range error");
611             return;
612         }
613
614     }
615
616     /* Parse the remainder of the line and export the symbol */
617     ConDes (Name, (unsigned) Type);
618 }
619
620
621
622 static void DoConstructor (void)
623 /* Export a symbol as constructor */
624 {
625     char Name [sizeof (SVal)];
626
627     /* Symbol name follows */
628     if (Tok != TOK_IDENT) {
629         ErrorSkip ("Identifier expected");
630         return;
631     }
632     strcpy (Name, SVal);
633     NextTok ();
634
635     /* Parse the remainder of the line and export the symbol */
636     ConDes (Name, CD_TYPE_CON);
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     char Name [sizeof (SVal)];
718
719     /* Symbol name follows */
720     if (Tok != TOK_IDENT) {
721         ErrorSkip ("Identifier expected");
722         return;
723     }
724     strcpy (Name, SVal);
725     NextTok ();
726
727     /* Parse the remainder of the line and export the symbol */
728     ConDes (Name, CD_TYPE_DES);
729 }
730
731
732
733 static void DoDWord (void)
734 /* Define dwords */
735 {
736     while (1) {
737         EmitDWord (Expression ());
738         if (Tok != TOK_COMMA) {
739             break;
740         } else {
741             NextTok ();
742         }
743     }
744 }
745
746
747
748 static void DoEnd (void)
749 /* End of assembly */
750 {
751     ForcedEnd = 1;
752     NextTok ();
753 }
754
755
756
757 static void DoEndProc (void)
758 /* Leave a lexical level */
759 {
760     if (GetCurrentSymTabType () != ST_PROC) {
761         /* No local scope */
762         ErrorSkip ("No open .PROC");
763     } else {
764         SymLeaveLevel ();
765     }
766 }
767
768
769
770 static void DoEndScope (void)
771 /* Leave a lexical level */
772 {
773     if ( GetCurrentSymTabType () != ST_SCOPE) {
774         /* No local scope */
775         ErrorSkip ("No open .SCOPE");
776     } else {
777         SymLeaveLevel ();
778     }
779 }
780
781
782
783 static void DoError (void)
784 /* User error */
785 {
786     if (Tok != TOK_STRCON) {
787         ErrorSkip ("String constant expected");
788     } else {
789         Error ("User error: %s", SVal);
790         SkipUntilSep ();
791     }
792 }
793
794
795
796 static void DoExitMacro (void)
797 /* Exit a macro expansion */
798 {
799     if (!InMacExpansion ()) {
800         /* We aren't expanding a macro currently */
801         DoUnexpected ();
802     } else {
803         MacAbort ();
804     }
805 }
806
807
808
809 static void DoExport (void)
810 /* Export a symbol */
811 {
812     ExportImport (ExportWithAssign, ADDR_SIZE_DEFAULT, SF_NONE);
813 }
814
815
816
817 static void DoExportZP (void)
818 /* Export a zeropage symbol */
819 {
820     ExportImport (ExportWithAssign, ADDR_SIZE_ZP, SF_NONE);
821 }
822
823
824
825 static void DoFarAddr (void)
826 /* Define far addresses (24 bit) */
827 {
828     while (1) {
829         EmitFarAddr (Expression ());
830         if (Tok != TOK_COMMA) {
831             break;
832         } else {
833             NextTok ();
834         }
835     }
836 }
837
838
839
840 static void DoFeature (void)
841 /* Switch the Feature option */
842 {
843     /* Allow a list of comma separated keywords */
844     while (1) {
845
846         /* We expect an identifier */
847         if (Tok != TOK_IDENT) {
848             ErrorSkip ("Identifier expected");
849             return;
850         }
851
852         /* Make the string attribute lower case */
853         LocaseSVal ();
854
855         /* Set the feature and check for errors */
856         if (SetFeature (SVal) == FEAT_UNKNOWN) {
857             /* Not found */
858             ErrorSkip ("Invalid feature: `%s'", SVal);
859             return;
860         } else {
861             /* Skip the keyword */
862             NextTok ();
863         }
864
865         /* Allow more than one keyword */
866         if (Tok == TOK_COMMA) {
867             NextTok ();
868         } else {
869             break;
870         }
871     }
872 }
873
874
875
876 static void DoFileOpt (void)
877 /* Insert a file option */
878 {
879     long OptNum;
880
881     /* The option type may be given as a keyword or as a number. */
882     if (Tok == TOK_IDENT) {
883
884         /* Option given as keyword */
885         static const char* Keys [] = {
886             "AUTHOR", "COMMENT", "COMPILER"
887         };
888
889         /* Map the option to a number */
890         OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
891         if (OptNum < 0) {
892             /* Not found */
893             ErrorSkip ("File option keyword expected");
894             return;
895         }
896
897         /* Skip the keyword */
898         NextTok ();
899
900         /* Must be followed by a comma */
901         ConsumeComma ();
902
903         /* We accept only string options for now */
904         if (Tok != TOK_STRCON) {
905             ErrorSkip ("String constant expected");
906             return;
907         }
908
909         /* Insert the option */
910         switch (OptNum) {
911
912             case 0:
913                 /* Author */
914                 OptAuthor (SVal);
915                 break;
916
917             case 1:
918                 /* Comment */
919                 OptComment (SVal);
920                 break;
921
922             case 2:
923                 /* Compiler */
924                 OptCompiler (SVal);
925                 break;
926
927             default:
928                 Internal ("Invalid OptNum: %ld", OptNum);
929
930         }
931
932         /* Done */
933         NextTok ();
934
935     } else {
936
937         /* Option given as number */
938         OptNum = ConstExpression ();
939         if (!IsByteRange (OptNum)) {
940             ErrorSkip ("Range error");
941             return;
942         }
943
944         /* Must be followed by a comma */
945         ConsumeComma ();
946
947         /* We accept only string options for now */
948         if (Tok != TOK_STRCON) {
949             ErrorSkip ("String constant expected");
950             return;
951         }
952
953         /* Insert the option */
954         OptStr ((unsigned char) OptNum, SVal);
955
956         /* Done */
957         NextTok ();
958     }
959 }
960
961
962
963 static void DoForceImport (void)
964 /* Do a forced import on a symbol */
965 {
966     ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_FORCED);
967 }
968
969
970
971 static void DoGlobal (void)
972 /* Declare a global symbol */
973 {
974     ExportImport (SymGlobal, ADDR_SIZE_DEFAULT, SF_NONE);
975 }
976
977
978
979 static void DoGlobalZP (void)
980 /* Declare a global zeropage symbol */
981 {
982     ExportImport (SymGlobal, ADDR_SIZE_ZP, SF_NONE);
983 }
984
985
986
987 static void DoI16 (void)
988 /* Switch the index registers to 16 bit mode (assembler only) */
989 {
990     if (GetCPU() != CPU_65816) {
991         Error ("Command is only valid in 65816 mode");
992     } else {
993         /* Immidiate mode has two extension bytes */
994         ExtBytes [AM65I_IMM_INDEX] = 2;
995     }
996 }
997
998
999
1000 static void DoI8 (void)
1001 /* Switch the index registers to 16 bit mode (assembler only) */
1002 {
1003     if (GetCPU() != CPU_65816) {
1004         Error ("Command is only valid in 65816 mode");
1005     } else {
1006         /* Immidiate mode has one extension byte */
1007         ExtBytes [AM65I_IMM_INDEX] = 1;
1008     }
1009 }
1010
1011
1012
1013 static void DoImport (void)
1014 /* Import a symbol */
1015 {
1016     ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_NONE);
1017 }
1018
1019
1020
1021 static void DoImportZP (void)
1022 /* Import a zero page symbol */
1023 {
1024     ExportImport (SymImport, ADDR_SIZE_ZP, SF_NONE);
1025 }
1026
1027
1028
1029 static void DoIncBin (void)
1030 /* Include a binary file */
1031 {
1032     char Name [sizeof (SVal)];
1033     long Start = 0L;
1034     long Count = -1L;
1035     long Size;
1036     FILE* F;
1037
1038     /* Name must follow */
1039     if (Tok != TOK_STRCON) {
1040         ErrorSkip ("String constant expected");
1041         return;
1042     }
1043     strcpy (Name, SVal);
1044     NextTok ();
1045
1046     /* A starting offset may follow */
1047     if (Tok == TOK_COMMA) {
1048         NextTok ();
1049         Start = ConstExpression ();
1050
1051         /* And a length may follow */
1052         if (Tok == TOK_COMMA) {
1053             NextTok ();
1054             Count = ConstExpression ();
1055         }
1056
1057     }
1058
1059     /* Try to open the file */
1060     F = fopen (Name, "rb");
1061     if (F == 0) {
1062
1063         /* Search for the file in the include directories. */
1064         char* PathName = FindInclude (Name);
1065         if (PathName == 0 || (F = fopen (PathName, "r")) == 0) {
1066             /* Not found or cannot open, print an error and bail out */
1067             ErrorSkip ("Cannot open include file `%s': %s", Name, strerror (errno));
1068         }
1069
1070         /* Free the allocated memory */
1071         xfree (PathName);
1072
1073         /* If we had an error before, bail out now */
1074         if (F == 0) {
1075             return;
1076         }
1077     }
1078
1079     /* Get the size of the file */
1080     fseek (F, 0, SEEK_END);
1081     Size = ftell (F);
1082
1083     /* If a count was not given, calculate it now */
1084     if (Count < 0) {
1085         Count = Size - Start;
1086         if (Count < 0) {
1087             /* Nothing to read - flag this as a range error */
1088             ErrorSkip ("Range error");
1089             goto Done;
1090         }
1091     } else {
1092         /* Count was given, check if it is valid */
1093         if (Start + Count > Size) {
1094             ErrorSkip ("Range error");
1095             goto Done;
1096         }
1097     }
1098
1099     /* Seek to the start position */
1100     fseek (F, Start, SEEK_SET);
1101
1102     /* Read chunks and insert them into the output */
1103     while (Count > 0) {
1104
1105         unsigned char Buf [1024];
1106
1107         /* Calculate the number of bytes to read */
1108         size_t BytesToRead = (Count > (long)sizeof(Buf))? sizeof(Buf) : (size_t) Count;
1109
1110         /* Read chunk */
1111         size_t BytesRead = fread (Buf, 1, BytesToRead, F);
1112         if (BytesToRead != BytesRead) {
1113             /* Some sort of error */
1114             ErrorSkip ("Cannot read from include file `%s': %s",
1115                        Name, strerror (errno));
1116             break;
1117         }
1118
1119         /* Insert it into the output */
1120         EmitData (Buf, BytesRead);
1121
1122         /* Keep the counters current */
1123         Count -= BytesRead;
1124     }
1125
1126 Done:
1127     /* Close the file, ignore errors since it's r/o */
1128     (void) fclose (F);
1129 }
1130
1131
1132
1133 static void DoInclude (void)
1134 /* Include another file */
1135 {
1136     /* Name must follow */
1137     if (Tok != TOK_STRCON) {
1138         ErrorSkip ("String constant expected");
1139     } else {
1140         NewInputFile (SVal);
1141     }
1142 }
1143
1144
1145
1146 static void DoInterruptor (void)
1147 /* Export a symbol as interruptor */
1148 {
1149     char Name [sizeof (SVal)];
1150
1151     /* Symbol name follows */
1152     if (Tok != TOK_IDENT) {
1153         ErrorSkip ("Identifier expected");
1154         return;
1155     }
1156     strcpy (Name, SVal);
1157     NextTok ();
1158
1159     /* Parse the remainder of the line and export the symbol */
1160     ConDes (Name, CD_TYPE_INT);
1161 }
1162
1163
1164
1165 static void DoInvalid (void)
1166 /* Handle a token that is invalid here, since it should have been handled on
1167  * a much lower level of the expression hierarchy. Getting this sort of token
1168  * means that the lower level code has bugs.
1169  * This function differs to DoUnexpected in that the latter may be triggered
1170  * by the user by using keywords in the wrong location. DoUnexpected is not
1171  * an error in the assembler itself, while DoInvalid is.
1172  */
1173 {
1174     Internal ("Unexpected token: %s", Keyword);
1175 }
1176
1177
1178
1179 static void DoLineCont (void)
1180 /* Switch the use of line continuations */
1181 {
1182     SetBoolOption (&LineCont);
1183 }
1184
1185
1186
1187 static void DoList (void)
1188 /* Enable/disable the listing */
1189 {
1190     /* Get the setting */
1191     unsigned char List;
1192     SetBoolOption (&List);
1193
1194     /* Manage the counter */
1195     if (List) {
1196         EnableListing ();
1197     } else {
1198         DisableListing ();
1199     }
1200 }
1201
1202
1203
1204 static void DoListBytes (void)
1205 /* Set maximum number of bytes to list for one line */
1206 {
1207     SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
1208 }
1209
1210
1211
1212 static void DoLocalChar (void)
1213 /* Define the character that starts local labels */
1214 {
1215     if (Tok != TOK_CHARCON) {
1216         ErrorSkip ("Character constant expected");
1217     } else {
1218         if (IVal != '@' && IVal != '?') {
1219             Error ("Invalid start character for locals");
1220         } else {
1221             LocalStart = (char) IVal;
1222         }
1223         NextTok ();
1224     }
1225 }
1226
1227
1228
1229 static void DoMacPack (void)
1230 /* Insert a macro package */
1231 {
1232     int Package;
1233
1234     /* We expect an identifier */
1235     if (Tok != TOK_IDENT) {
1236         ErrorSkip ("Identifier expected");
1237         return;
1238     }
1239
1240     /* Search for the macro package name */
1241     Package = MacPackFind (SVal);
1242     if (Package < 0) {
1243         /* Not found */
1244         ErrorSkip ("Invalid macro package");
1245         return;
1246     }
1247
1248     /* Insert the package */
1249     MacPackInsert (Package);
1250 }
1251
1252
1253
1254 static void DoMacro (void)
1255 /* Start a macro definition */
1256 {
1257     MacDef (MAC_STYLE_CLASSIC);
1258 }
1259
1260
1261
1262 static void DoNull (void)
1263 /* Switch to the NULL segment */
1264 {
1265     UseSeg (&NullSegDef);
1266 }
1267
1268
1269
1270 static void DoOrg (void)
1271 /* Start absolute code */
1272 {
1273     long PC = ConstExpression ();
1274     if (PC < 0 || PC > 0xFFFFFF) {
1275         Error ("Range error");
1276         return;
1277     }
1278     EnterAbsoluteMode (PC);
1279 }
1280
1281
1282
1283 static void DoOut (void)
1284 /* Output a string */
1285 {
1286     if (Tok != TOK_STRCON) {
1287         ErrorSkip ("String constant expected");
1288     } else {
1289         /* Output the string and be sure to flush the output to keep it in
1290          * sync with any error messages if the output is redirected to a file.
1291          */
1292         printf ("%s\n", SVal);
1293         fflush (stdout);
1294         NextTok ();
1295     }
1296 }
1297
1298
1299
1300 static void DoP02 (void)
1301 /* Switch to 6502 CPU */
1302 {
1303     SetCPU (CPU_6502);
1304 }
1305
1306
1307
1308 static void DoPC02 (void)
1309 /* Switch to 65C02 CPU */
1310 {
1311     SetCPU (CPU_65C02);
1312 }
1313
1314
1315
1316 static void DoP816 (void)
1317 /* Switch to 65816 CPU */
1318 {
1319     SetCPU (CPU_65816);
1320 }
1321
1322
1323
1324 static void DoPageLength (void)
1325 /* Set the page length for the listing */
1326 {
1327     PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
1328 }
1329
1330
1331
1332 static void DoPopSeg (void)
1333 /* Pop an old segment from the segment stack */
1334 {
1335     SegDef* Def;
1336
1337     /* Must have a segment on the stack */
1338     if (CollCount (&SegStack) == 0) {
1339         ErrorSkip ("Segment stack is empty");
1340         return;
1341     }
1342
1343     /* Pop the last element */
1344     Def = CollPop (&SegStack);
1345
1346     /* Restore this segment */
1347     UseSeg (Def);
1348
1349     /* Delete the segment definition */
1350     FreeSegDef (Def);
1351 }
1352
1353
1354
1355 static void DoProc (void)
1356 /* Start a new lexical scope */
1357 {
1358     char Name[sizeof(SVal)];
1359     unsigned char AddrSize;
1360
1361     if (Tok == TOK_IDENT) {
1362
1363         SymEntry* Sym;
1364
1365         /* The new scope has a name. Remember it. */
1366         strcpy (Name, SVal);
1367
1368         /* Search for the symbol, generate a new one if needed */
1369         Sym = SymFind (CurrentScope, Name, SYM_ALLOC_NEW);
1370
1371         /* Skip the scope name */
1372         NextTok ();
1373
1374         /* Read an optional address size specifier */
1375         AddrSize = OptionalAddrSize ();
1376
1377         /* Mark the symbol as defined */
1378         SymDef (Sym, GenCurrentPC (), AddrSize, SF_LABEL);
1379
1380     } else {
1381
1382         /* A .PROC statement without a name */
1383         Warning (1, "Unnamed .PROCs are deprecated, please use .SCOPE");
1384         AnonName (Name, sizeof (Name), "PROC");
1385         AddrSize = ADDR_SIZE_DEFAULT;
1386
1387     }
1388
1389     /* Enter a new scope */
1390     SymEnterLevel (Name, ST_PROC, AddrSize);
1391 }
1392
1393
1394
1395 static void DoPSC02 (void)
1396 /* Switch to 65SC02 CPU */
1397 {
1398     SetCPU (CPU_65SC02);
1399 }
1400
1401
1402
1403 static void DoPushSeg (void)
1404 /* Push the current segment onto the segment stack */
1405 {
1406     /* Can only push a limited size of segments */
1407     if (CollCount (&SegStack) >= MAX_PUSHED_SEGMENTS) {
1408         ErrorSkip ("Segment stack overflow");
1409         return;
1410     }
1411
1412     /* Get the current segment and push it */
1413     CollAppend (&SegStack, DupSegDef (GetCurrentSegDef ()));
1414 }
1415
1416
1417
1418 static void DoReloc (void)
1419 /* Enter relocatable mode */
1420 {
1421     EnterRelocMode ();
1422 }
1423
1424
1425
1426 static void DoRepeat (void)
1427 /* Repeat some instruction block */
1428 {
1429     ParseRepeat ();
1430 }
1431
1432
1433
1434 static void DoRes (void)
1435 /* Reserve some number of storage bytes */
1436 {
1437     long Count;
1438     long Val;
1439
1440     Count = ConstExpression ();
1441     if (Count > 0xFFFF || Count < 0) {
1442         ErrorSkip ("Range error");
1443         return;
1444     }
1445     if (Tok == TOK_COMMA) {
1446         NextTok ();
1447         Val = ConstExpression ();
1448         /* We need a byte value here */
1449         if (!IsByteRange (Val)) {
1450             ErrorSkip ("Range error");
1451             return;
1452         }
1453
1454         /* Emit constant values */
1455         while (Count--) {
1456             Emit0 ((unsigned char) Val);
1457         }
1458
1459     } else {
1460         /* Emit fill fragments */
1461         EmitFill (Count);
1462     }
1463 }
1464
1465
1466
1467 static void DoROData (void)
1468 /* Switch to the r/o data segment */
1469 {
1470     UseSeg (&RODataSegDef);
1471 }
1472
1473
1474
1475 static void DoScope (void)
1476 /* Start a local scope */
1477 {
1478     char Name[sizeof (SVal)];
1479     unsigned char AddrSize;
1480
1481
1482     if (Tok == TOK_IDENT) {
1483
1484         /* The new scope has a name. Remember and skip it. */
1485         strcpy (Name, SVal);
1486         NextTok ();
1487
1488     } else {
1489
1490         /* An unnamed scope */
1491         AnonName (Name, sizeof (Name), "SCOPE");
1492
1493     }
1494
1495     /* Read an optional address size specifier */
1496     AddrSize = OptionalAddrSize ();
1497
1498     /* Enter the new scope */
1499     SymEnterLevel (Name, ST_SCOPE, AddrSize);
1500
1501 }
1502
1503
1504
1505 static void DoSegment (void)
1506 /* Switch to another segment */
1507 {
1508     char Name [sizeof (SVal)];
1509     SegDef Def;
1510     Def.Name = Name;
1511
1512     if (Tok != TOK_STRCON) {
1513         ErrorSkip ("String constant expected");
1514     } else {
1515
1516         /* Save the name of the segment and skip it */
1517         strcpy (Name, SVal);
1518         NextTok ();
1519
1520         /* Check for an optional address size modifier */
1521         Def.AddrSize = OptionalAddrSize ();
1522
1523         /* Set the segment */
1524         UseSeg (&Def);
1525     }
1526 }
1527
1528
1529
1530 static void DoSetCPU (void)
1531 /* Switch the CPU instruction set */
1532 {
1533     /* We expect an identifier */
1534     if (Tok != TOK_STRCON) {
1535         ErrorSkip ("String constant expected");
1536     } else {
1537         /* Try to find the CPU */
1538         cpu_t CPU = FindCPU (SVal);
1539
1540         /* Switch to the new CPU */
1541         SetCPU (CPU);
1542
1543         /* Skip the identifier. If the CPU switch was successful, the scanner
1544          * will treat the input now correctly for the new CPU.
1545          */
1546         NextTok ();
1547     }
1548 }
1549
1550
1551
1552 static void DoSmart (void)
1553 /* Smart mode on/off */
1554 {
1555     SetBoolOption (&SmartMode);
1556 }
1557
1558
1559
1560 static void DoSunPlus (void)
1561 /* Switch to the SUNPLUS CPU */
1562 {
1563     SetCPU (CPU_SUNPLUS);
1564 }
1565
1566
1567
1568 static void DoTag (void)
1569 /* Allocate space for a struct */
1570 {
1571     SymEntry* SizeSym;
1572     long Size;
1573
1574     /* Read the struct name */
1575     SymTable* Struct = ParseScopedSymTable ();
1576
1577     /* Check the supposed struct */
1578     if (Struct == 0) {
1579         ErrorSkip ("Unknown struct");
1580         return;
1581     }
1582     if (GetSymTabType (Struct) != ST_STRUCT) {
1583         ErrorSkip ("Not a struct");
1584         return;
1585     }
1586
1587     /* Get the symbol that defines the size of the struct */
1588     SizeSym = GetSizeOfScope (Struct);
1589
1590     /* Check if it does exist and if its value is known */
1591     if (SizeSym == 0 || !SymIsConst (SizeSym, &Size)) {
1592         ErrorSkip ("Size of struct/union is unknown");
1593         return;
1594     }
1595
1596     /* Optional multiplicator may follow */
1597     if (Tok == TOK_COMMA) {
1598         long Multiplicator;
1599         NextTok ();
1600         Multiplicator = ConstExpression ();
1601         /* Multiplicator must make sense */
1602         if (Multiplicator <= 0) {
1603             ErrorSkip ("Range error");
1604             return;
1605         }
1606         Size *= Multiplicator;
1607     }
1608
1609     /* Emit fill fragments */
1610     EmitFill (Size);
1611 }
1612
1613
1614
1615 static void DoUnexpected (void)
1616 /* Got an unexpected keyword */
1617 {
1618     Error ("Unexpected `%s'", Keyword);
1619     SkipUntilSep ();
1620 }
1621
1622
1623
1624 static void DoWarning (void)
1625 /* User warning */
1626 {
1627     if (Tok != TOK_STRCON) {
1628         ErrorSkip ("String constant expected");
1629     } else {
1630         Warning (0, "User warning: %s", SVal);
1631         SkipUntilSep ();
1632     }
1633 }
1634
1635
1636
1637 static void DoWord (void)
1638 /* Define words */
1639 {
1640     while (1) {
1641         EmitWord (Expression ());
1642         if (Tok != TOK_COMMA) {
1643             break;
1644         } else {
1645             NextTok ();
1646         }
1647     }
1648 }
1649
1650
1651
1652 static void DoZeropage (void)
1653 /* Switch to the zeropage segment */
1654 {
1655     UseSeg (&ZeropageSegDef);
1656 }
1657
1658
1659
1660 /*****************************************************************************/
1661 /*                                Table data                                 */
1662 /*****************************************************************************/
1663
1664
1665
1666 /* Control commands flags */
1667 enum {
1668     ccNone      = 0x0000,               /* No special flags */
1669     ccKeepToken = 0x0001                /* Do not skip the current token */
1670 };
1671
1672 /* Control command table */
1673 typedef struct CtrlDesc CtrlDesc;
1674 struct CtrlDesc {
1675     unsigned    Flags;                  /* Flags for this directive */
1676     void        (*Handler) (void);      /* Command handler */
1677 };
1678
1679 #define PSEUDO_COUNT    (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1680 static CtrlDesc CtrlCmdTab [] = {
1681     { ccNone,           DoA16           },
1682     { ccNone,           DoA8            },
1683     { ccNone,           DoAddr          },      /* .ADDR */
1684     { ccNone,           DoAlign         },
1685     { ccNone,           DoASCIIZ        },
1686     { ccNone,           DoAssert        },
1687     { ccNone,           DoAutoImport    },
1688     { ccNone,           DoUnexpected    },      /* .BANKBYTE */
1689     { ccNone,           DoUnexpected    },      /* .BLANK */
1690     { ccNone,           DoBss           },
1691     { ccNone,           DoByte          },
1692     { ccNone,           DoCase          },
1693     { ccNone,           DoCharMap       },
1694     { ccNone,           DoCode          },
1695     { ccNone,           DoUnexpected,   },      /* .CONCAT */
1696     { ccNone,           DoConDes        },
1697     { ccNone,           DoUnexpected    },      /* .CONST */
1698     { ccNone,           DoConstructor   },
1699     { ccNone,           DoUnexpected    },      /* .CPU */
1700     { ccNone,           DoData          },
1701     { ccNone,           DoDbg,          },
1702     { ccNone,           DoDByt          },
1703     { ccNone,           DoDebugInfo     },
1704     { ccNone,           DoDefine        },
1705     { ccNone,           DoUnexpected    },      /* .DEFINED */
1706     { ccNone,           DoDestructor    },
1707     { ccNone,           DoDWord         },
1708     { ccKeepToken,      DoConditionals  },      /* .ELSE */
1709     { ccKeepToken,      DoConditionals  },      /* .ELSEIF */
1710     { ccKeepToken,      DoEnd           },
1711     { ccNone,           DoUnexpected    },      /* .ENDENUM */
1712     { ccKeepToken,      DoConditionals  },      /* .ENDIF */
1713     { ccNone,           DoUnexpected    },      /* .ENDMACRO */
1714     { ccNone,           DoEndProc       },
1715     { ccNone,           DoUnexpected    },      /* .ENDREPEAT */
1716     { ccNone,           DoEndScope      },
1717     { ccNone,           DoUnexpected    },      /* .ENDSTRUCT */
1718     { ccNone,           DoUnexpected    },      /* .ENDUNION */
1719     { ccNone,           DoEnum          },
1720     { ccNone,           DoError         },
1721     { ccNone,           DoExitMacro     },
1722     { ccNone,           DoExport        },
1723     { ccNone,           DoExportZP      },
1724     { ccNone,           DoFarAddr       },
1725     { ccNone,           DoFeature       },
1726     { ccNone,           DoFileOpt       },
1727     { ccNone,           DoForceImport   },
1728     { ccNone,           DoUnexpected    },      /* .FORCEWORD */
1729     { ccNone,           DoGlobal        },
1730     { ccNone,           DoGlobalZP      },
1731     { ccNone,           DoUnexpected    },      /* .HIBYTE */
1732     { ccNone,           DoUnexpected    },      /* .HIWORD */
1733     { ccNone,           DoI16           },
1734     { ccNone,           DoI8            },
1735     { ccNone,           DoUnexpected    },      /* .IDENT */
1736     { ccKeepToken,      DoConditionals  },      /* .IF */
1737     { ccKeepToken,      DoConditionals  },      /* .IFBLANK */
1738     { ccKeepToken,      DoConditionals  },      /* .IFCONST */
1739     { ccKeepToken,      DoConditionals  },      /* .IFDEF */
1740     { ccKeepToken,      DoConditionals  },      /* .IFNBLANK */
1741     { ccKeepToken,      DoConditionals  },      /* .IFNCONST */
1742     { ccKeepToken,      DoConditionals  },      /* .IFNDEF */
1743     { ccKeepToken,      DoConditionals  },      /* .IFNREF */
1744     { ccKeepToken,      DoConditionals  },      /* .IFP02 */
1745     { ccKeepToken,      DoConditionals  },      /* .IFP816 */
1746     { ccKeepToken,      DoConditionals  },      /* .IFPC02 */
1747     { ccKeepToken,      DoConditionals  },      /* .IFPSC02 */
1748     { ccKeepToken,      DoConditionals  },      /* .IFREF */
1749     { ccNone,           DoImport        },
1750     { ccNone,           DoImportZP      },
1751     { ccNone,           DoIncBin        },
1752     { ccNone,           DoInclude       },
1753     { ccNone,           DoInterruptor   },
1754     { ccNone,           DoInvalid       },      /* .LEFT */
1755     { ccNone,           DoLineCont      },
1756     { ccNone,           DoList          },
1757     { ccNone,           DoListBytes     },
1758     { ccNone,           DoUnexpected    },      /* .LOBYTE */
1759     { ccNone,           DoUnexpected    },      /* .LOCAL */
1760     { ccNone,           DoLocalChar     },
1761     { ccNone,           DoUnexpected    },      /* .LOWORD */
1762     { ccNone,           DoMacPack       },
1763     { ccNone,           DoMacro         },
1764     { ccNone,           DoUnexpected    },      /* .MATCH */
1765     { ccNone,           DoInvalid       },      /* .MID */
1766     { ccNone,           DoNull          },
1767     { ccNone,           DoOrg           },
1768     { ccNone,           DoOut           },
1769     { ccNone,           DoP02           },
1770     { ccNone,           DoP816          },
1771     { ccNone,           DoPageLength    },
1772     { ccNone,           DoUnexpected    },      /* .PARAMCOUNT */
1773     { ccNone,           DoPC02          },
1774     { ccNone,           DoPopSeg        },
1775     { ccNone,           DoProc          },
1776     { ccNone,           DoPSC02         },
1777     { ccNone,           DoPushSeg       },
1778     { ccNone,           DoUnexpected    },      /* .REFERENCED */
1779     { ccNone,           DoReloc         },
1780     { ccNone,           DoRepeat        },
1781     { ccNone,           DoRes           },
1782     { ccNone,           DoInvalid       },      /* .RIGHT */
1783     { ccNone,           DoROData        },
1784     { ccNone,           DoScope         },
1785     { ccNone,           DoSegment       },
1786     { ccNone,           DoUnexpected    },      /* .SET */
1787     { ccNone,           DoSetCPU        },
1788     { ccNone,           DoUnexpected    },      /* .SIZEOF */
1789     { ccNone,           DoSmart         },
1790     { ccNone,           DoUnexpected    },      /* .SPRINTF */
1791     { ccNone,           DoUnexpected    },      /* .STRAT */
1792     { ccNone,           DoUnexpected    },      /* .STRING */
1793     { ccNone,           DoUnexpected    },      /* .STRLEN */
1794     { ccNone,           DoStruct        },
1795     { ccNone,           DoSunPlus       },
1796     { ccNone,           DoTag           },
1797     { ccNone,           DoUnexpected    },      /* .TCOUNT */
1798     { ccNone,           DoUnexpected    },      /* .TIME */
1799     { ccNone,           DoUnion         },
1800     { ccNone,           DoUnexpected    },      /* .VERSION */
1801     { ccNone,           DoWarning       },
1802     { ccNone,           DoWord          },
1803     { ccNone,           DoUnexpected    },      /* .XMATCH */
1804     { ccNone,           DoZeropage      },
1805 };
1806
1807
1808
1809 /*****************************************************************************/
1810 /*                                   Code                                    */
1811 /*****************************************************************************/
1812
1813
1814
1815 void HandlePseudo (void)
1816 /* Handle a pseudo instruction */
1817 {
1818     CtrlDesc* D;
1819
1820     /* Calculate the index into the table */
1821     unsigned Index = Tok - TOK_FIRSTPSEUDO;
1822
1823     /* Safety check */
1824     if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
1825         Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
1826                   (unsigned) PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
1827     }
1828     CHECK (Index < PSEUDO_COUNT);
1829
1830     /* Get the pseudo intruction descriptor */
1831     D = &CtrlCmdTab [Index];
1832
1833     /* Remember the instruction, then skip it if needed */
1834     if ((D->Flags & ccKeepToken) == 0) {
1835         strcpy (Keyword, SVal);
1836         NextTok ();
1837     }
1838
1839     /* Call the handler */
1840     D->Handler ();
1841 }
1842
1843
1844
1845 void SegStackCheck (void)
1846 /* Check if the segment stack is empty at end of assembly */
1847 {
1848     if (CollCount (&SegStack) != 0) {
1849         Error ("Segment stack is not empty");
1850     }
1851 }
1852
1853
1854