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