]> git.sur5r.net Git - cc65/blob - src/ca65/pseudo.c
d5e76901f335d8b12f5706043cfdf87962cc19cd
[cc65] / src / ca65 / pseudo.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 pseudo.c                                  */
4 /*                                                                           */
5 /*              Pseudo instructions for the ca65 macroassembler              */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2000 Ullrich von Bassewitz                                       */
10 /*               Wacholderweg 14                                             */
11 /*               D-70597 Stuttgart                                           */
12 /* EMail:        uz@musoftware.de                                            */
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 "bitops.h"
44 #include "check.h"
45
46 /* ca65 */
47 #include "condasm.h"
48 #include "dbginfo.h"
49 #include "error.h"
50 #include "expr.h"
51 #include "global.h"
52 #include "instr.h"
53 #include "listing.h"
54 #include "macpack.h"
55 #include "macro.h"
56 #include "nexttok.h"
57 #include "objcode.h"
58 #include "options.h"
59 #include "repeat.h"
60 #include "symtab.h"
61 #include "pseudo.h"
62
63
64
65 /*****************************************************************************/
66 /*                                   Data                                    */
67 /*****************************************************************************/
68
69
70
71 /* Keyword we're about to handle */
72 static char Keyword [sizeof (SVal)+1] = ".";
73
74
75
76 /*****************************************************************************/
77 /*                               Forwards                                    */
78 /*****************************************************************************/
79
80
81
82 static void DoUnexpected (void);
83 /* Got an unexpected keyword */
84
85 static void DoInvalid (void);
86 /* Handle a token that is invalid here, since it should have been handled on
87  * a much lower level of the expression hierarchy. Getting this sort of token
88  * means that the lower level code has bugs.
89  * This function differs to DoUnexpected in that the latter may be triggered
90  * by the user by using keywords in the wrong location. DoUnexpected is not
91  * an error in the assembler itself, while DoInvalid is.
92  */
93
94
95
96 /*****************************************************************************/
97 /*                              Helper functions                             */
98 /*****************************************************************************/
99
100
101
102 static void SetBoolOption (unsigned char* Flag)
103 /* Read a on/off/+/- option and set flag accordingly */
104 {
105     static const char* Keys[] = {
106         "OFF",
107         "ON",
108     };
109
110     if (Tok == TOK_PLUS) {
111         *Flag = 1;
112         NextTok ();
113     } else if (Tok == TOK_MINUS) {
114         *Flag = 0;
115         NextTok ();
116     } else if (Tok == TOK_IDENT) {
117         /* Map the keyword to a number */
118         switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
119             case 0:     *Flag = 0; NextTok ();          break;
120             case 1:     *Flag = 1; NextTok ();          break;
121             default:    ErrorSkip (ERR_ONOFF_EXPECTED); break;
122         }
123     } else if (Tok == TOK_SEP || Tok == TOK_EOF) {
124         /* Without anything assume switch on */
125         *Flag = 1;
126     } else {
127         ErrorSkip (ERR_ONOFF_EXPECTED);
128     }
129 }
130
131
132
133 static void ExportImport (void (*SymFunc) (const char*, int), int ZP)
134 /* Export or import symbols */
135 {
136     while (1) {
137         if (Tok != TOK_IDENT) {
138             ErrorSkip (ERR_IDENT_EXPECTED);
139             break;
140         }
141         SymFunc (SVal, ZP);
142         NextTok ();
143         if (Tok == TOK_COMMA) {
144             NextTok ();
145         } else {
146             break;
147         }
148     }
149 }
150
151
152
153 static long IntArg (long Min, long Max)
154 /* Read an integer argument and check a range. Accept the token "unlimited"
155  * and return -1 in this case.
156  */
157 {
158     if (Tok == TOK_IDENT && strcmp (SVal, "unlimited") == 0) {
159         NextTok ();
160         return -1;
161     } else {
162         long Val = ConstExpression ();
163         if (Val < Min || Val > Max) {
164             Error (ERR_RANGE);
165             Val = Min;
166         }
167         return Val;
168     }
169 }
170
171
172
173 /*****************************************************************************/
174 /*                             Handler functions                             */
175 /*****************************************************************************/
176
177
178
179 static void DoA16 (void)
180 /* Switch the accu to 16 bit mode (assembler only) */
181 {
182     if (GetCPU() != CPU_65816) {
183         Error (ERR_816_MODE_ONLY);
184     } else {
185         /* Immidiate mode has two extension bytes */
186         ExtBytes [AMI_IMM_ACCU] = 2;
187     }
188 }
189
190
191
192 static void DoA8 (void)
193 /* Switch the accu to 8 bit mode (assembler only) */
194 {
195     if (GetCPU() != CPU_65816) {
196         Error (ERR_816_MODE_ONLY);
197     } else {
198         /* Immidiate mode has one extension byte */
199         ExtBytes [AMI_IMM_ACCU] = 1;
200     }
201 }
202
203
204
205 static void DoAddr (void)
206 /* Define addresses */
207 {
208     while (1) {
209         if (GetCPU() == CPU_65816) {
210             EmitWord (ForceWordExpr (Expression ()));
211         } else {
212             /* Do a range check */
213             EmitWord (Expression ());
214         }
215         if (Tok != TOK_COMMA) {
216             break;
217         } else {
218             NextTok ();
219         }
220     }
221 }
222
223
224
225 static void DoAlign (void)
226 /* Align the PC to some boundary */
227 {
228     long Val;
229     long Align;
230     unsigned Bit;
231
232     /* Read the alignment value */
233     Align = ConstExpression ();
234     if (Align <= 0 || Align > 0x10000) {
235         ErrorSkip (ERR_RANGE);
236         return;
237     }
238
239     /* Optional value follows */
240     if (Tok == TOK_COMMA) {
241         NextTok ();
242         Val = ConstExpression ();
243         /* We need a byte value here */
244         if (!IsByteRange (Val)) {
245             ErrorSkip (ERR_RANGE);
246             return;
247         }
248     } else {
249         Val = -1;
250     }
251
252     /* Check if the alignment is a power of two */
253     Bit = BitFind (Align);
254     if (Align != (0x01L << Bit)) {
255         Error (ERR_ALIGN);
256     } else {
257         SegAlign (Bit, (int) Val);
258     }
259 }
260
261
262
263 static void DoASCIIZ (void)
264 /* Define text with a zero terminator */
265 {
266     while (1) {
267         if (Tok != TOK_STRCON) {
268             ErrorSkip (ERR_STRCON_EXPECTED);
269             return;
270         }
271         EmitData ((unsigned char*) SVal, strlen (SVal));
272         NextTok ();
273         if (Tok == TOK_COMMA) {
274             NextTok ();
275         } else {
276             break;
277         }
278     }
279     Emit0 (0);
280 }
281
282
283
284 static void DoAutoImport (void)
285 /* Mark unresolved symbols as imported */
286 {
287     SetBoolOption (&AutoImport);
288 }
289
290
291
292 static void DoBss (void)
293 /* Switch to the BSS segment */
294 {
295     UseBssSeg ();
296 }
297
298
299
300 static void DoByte (void)
301 /* Define bytes */
302 {
303     while (1) {
304         if (Tok == TOK_STRCON) {
305             /* A string */
306             EmitData ((unsigned char*) SVal, strlen (SVal));
307             NextTok ();
308         } else {
309             EmitByte (Expression ());
310         }
311         if (Tok != TOK_COMMA) {
312             break;
313         } else {
314             NextTok ();
315             /* Do smart handling of dangling comma */
316             if (Tok == TOK_SEP) {
317                 Error (ERR_UNEXPECTED_EOL);
318                 break;
319             }
320         }
321     }
322 }
323
324
325
326 static void DoCase (void)
327 /* Switch the IgnoreCase option */
328 {
329     SetBoolOption (&IgnoreCase);
330     IgnoreCase = !IgnoreCase;
331 }
332
333
334
335 static void DoCode (void)
336 /* Switch to the code segment */
337 {
338     UseCodeSeg ();
339 }
340
341
342
343 static void DoData (void)
344 /* Switch to the data segment */
345 {
346     UseDataSeg ();
347 }
348
349
350
351 static void DoDbg (void)
352 /* Add debug information from high level code */
353 {
354     static const char* Keys[] = {
355         "FILE",
356         "LINE",
357         "SYM",
358     };
359     int Key;
360
361
362     /* We expect a subkey */
363     if (Tok != TOK_IDENT) {
364         ErrorSkip (ERR_IDENT_EXPECTED);
365         return;
366     }
367
368     /* Map the following keyword to a number */
369     Key = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
370
371     /* Skip the subkey */
372     NextTok ();
373
374     /* Parameters are separated by a comma */
375     ConsumeComma ();
376
377     /* Check the key and dispatch to a handler */
378     switch (Key) {
379         case 0:     DbgInfoFile ();             break;
380         case 1:     DbgInfoLine ();             break;
381         case 2:     DbgInfoSym ();              break;
382         default:    ErrorSkip (ERR_SYNTAX);     break;
383     }
384 }
385
386
387
388 static void DoDByt (void)
389 /* Output double bytes */
390 {
391     while (1) {
392         EmitWord (SwapExpr (Expression ()));
393         if (Tok != TOK_COMMA) {
394             break;
395         } else {
396             NextTok ();
397         }
398     }
399 }
400
401
402
403 static void DoDebugInfo (void)
404 /* Switch debug info on or off */
405 {
406     SetBoolOption (&DbgSyms);
407 }
408
409
410
411 static void DoDefine (void)
412 /* Define a one line macro */
413 {
414     MacDef (MAC_STYLE_DEFINE);
415 }
416
417
418
419 static void DoDWord (void)
420 /* Define dwords */
421 {
422     while (1) {
423         EmitDWord (Expression ());
424         if (Tok != TOK_COMMA) {
425             break;
426         } else {
427             NextTok ();
428         }
429     }
430 }
431
432
433
434 static void DoEnd (void)
435 /* End of assembly */
436 {
437     ForcedEnd = 1;
438 }
439
440
441
442 static void DoEndProc (void)
443 /* Leave a lexical level */
444 {
445     SymLeaveLevel ();
446 }
447
448
449
450 static void DoError (void)
451 /* User error */
452 {
453     if (Tok != TOK_STRCON) {
454         ErrorSkip (ERR_STRCON_EXPECTED);
455     } else {
456         Error (ERR_USER, SVal);
457         SkipUntilSep ();
458     }
459 }
460
461
462
463 static void DoExitMacro (void)
464 /* Exit a macro expansion */
465 {
466     if (!InMacExpansion ()) {
467         /* We aren't expanding a macro currently */
468         DoUnexpected ();
469     } else {
470         MacAbort ();
471     }
472 }
473
474
475
476 static void DoExport (void)
477 /* Export a symbol */
478 {
479     ExportImport (SymExport, 0);
480 }
481
482
483
484 static void DoExportZP (void)
485 /* Export a zeropage symbol */
486 {
487     ExportImport (SymExport, 1);
488 }
489
490
491
492 static void DoFarAddr (void)
493 /* Define far addresses (24 bit) */
494 {
495     while (1) {
496         EmitFarAddr (Expression ());
497         if (Tok != TOK_COMMA) {
498             break;
499         } else {
500             NextTok ();
501         }
502     }
503 }
504
505
506
507 static void DoFeature (void)
508 /* Switch the Feature option */
509 {
510     int Feature;
511
512     static const char* Keys[] = {
513         "DOLLAR_IS_PC",
514         "LABELS_WITHOUT_COLONS",
515         "LOOSE_STRING_TERM",
516         "AT_IN_IDENTIFIERS",
517         "DOLLAR_IN_IDENTIFIERS",
518     };
519
520     /* Allow a list of comma separated keywords */
521     while (1) {
522
523         /* We expect an identifier */
524         if (Tok != TOK_IDENT) {
525             ErrorSkip (ERR_IDENT_EXPECTED);
526             return;
527         }
528
529         /* Map the keyword to a number */
530         Feature = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
531         if (Feature < 0) {
532             /* Not found */
533             ErrorSkip (ERR_ILLEGAL_FEATURE);
534             return;
535         }
536
537         /* Skip the keyword */
538         NextTok ();
539
540         /* Switch the feature on */
541         switch (Feature) {
542             case 0:     DollarIsPC      = 1;    break;
543             case 1:     NoColonLabels   = 1;    break;
544             case 2:     LooseStringTerm = 1;    break;
545             case 3:     AtInIdents      = 1;    break;
546             case 4:     DollarInIdents  = 1;    break;
547             default:    Internal ("Invalid feature: %d", Feature);
548         }
549
550         /* Allow more than one keyword */
551         if (Tok == TOK_COMMA) {
552             NextTok ();
553         } else {
554             break;
555         }
556     }
557 }
558
559
560
561 static void DoFileOpt (void)
562 /* Insert a file option */
563 {
564     long OptNum;
565
566     /* The option type may be given as a keyword or as a number. */
567     if (Tok == TOK_IDENT) {
568
569         /* Option given as keyword */
570         static const char* Keys [] = {
571             "AUTHOR", "COMMENT", "COMPILER"
572         };
573
574         /* Map the option to a number */
575         OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
576         if (OptNum < 0) {
577             /* Not found */
578             ErrorSkip (ERR_OPTION_KEY_EXPECTED);
579             return;
580         }
581
582         /* Skip the keyword */
583         NextTok ();
584
585         /* Must be followed by a comma */
586         ConsumeComma ();
587
588         /* We accept only string options for now */
589         if (Tok != TOK_STRCON) {
590             ErrorSkip (ERR_STRCON_EXPECTED);
591             return;
592         }
593
594         /* Insert the option */
595         switch (OptNum) {
596
597             case 0:
598                 /* Author */
599                 OptAuthor (SVal);
600                 break;
601
602             case 1:
603                 /* Comment */
604                 OptComment (SVal);
605                 break;
606
607             case 2:
608                 /* Compiler */
609                 OptCompiler (SVal);
610                 break;
611
612             default:
613                 Internal ("Invalid OptNum: %l", OptNum);
614
615         }
616
617         /* Done */
618         NextTok ();
619
620     } else {
621
622         /* Option given as number */
623         OptNum = ConstExpression ();
624         if (!IsByteRange (OptNum)) {
625             ErrorSkip (ERR_RANGE);
626             return;
627         }
628
629         /* Must be followed by a comma */
630         ConsumeComma ();
631
632         /* We accept only string options for now */
633         if (Tok != TOK_STRCON) {
634             ErrorSkip (ERR_STRCON_EXPECTED);
635             return;
636         }
637
638         /* Insert the option */
639         OptStr ((unsigned char) OptNum, SVal);
640
641         /* Done */
642         NextTok ();
643     }
644 }
645
646
647
648 static void DoGlobal (void)
649 /* Declare a global symbol */
650 {
651     ExportImport (SymGlobal, 0);
652 }
653
654
655
656 static void DoGlobalZP (void)
657 /* Declare a global zeropage symbol */
658 {
659     ExportImport (SymGlobal, 1);
660 }
661
662
663
664 static void DoI16 (void)
665 /* Switch the index registers to 16 bit mode (assembler only) */
666 {
667     if (GetCPU() != CPU_65816) {
668         Error (ERR_816_MODE_ONLY);
669     } else {
670         /* Immidiate mode has two extension bytes */
671         ExtBytes [AMI_IMM_INDEX] = 2;
672     }
673 }
674
675
676
677 static void DoI8 (void)
678 /* Switch the index registers to 16 bit mode (assembler only) */
679 {
680     if (GetCPU() != CPU_65816) {
681         Error (ERR_816_MODE_ONLY);
682     } else {
683         /* Immidiate mode has one extension byte */
684         ExtBytes [AMI_IMM_INDEX] = 1;
685     }
686 }
687
688
689
690 static void DoImport (void)
691 /* Import a symbol */
692 {
693     ExportImport (SymImport, 0);
694 }
695
696
697
698 static void DoImportZP (void)
699 /* Import a zero page symbol */
700 {
701     ExportImport (SymImport, 1);
702 }
703
704
705
706 static void DoIncBin (void)
707 /* Include a binary file */
708 {
709     /* Name must follow */
710     if (Tok != TOK_STRCON) {
711         ErrorSkip (ERR_STRCON_EXPECTED);
712     } else {
713         /* Try to open the file */
714         FILE* F = fopen (SVal, "rb");
715         if (F == 0) {
716             Error (ERR_CANNOT_OPEN_INCLUDE, SVal, strerror (errno));
717         } else {
718             unsigned char Buf [1024];
719             size_t Count;
720             /* Read chunks and insert them into the output */
721             while ((Count = fread (Buf, 1, sizeof (Buf), F)) > 0) {
722                 EmitData (Buf, Count);
723             }
724             /* Close the file, ignore errors since it's r/o */
725             (void) fclose (F);
726         }
727         /* Skip the name */
728         NextTok ();
729     }
730 }
731
732
733
734 static void DoInclude (void)
735 /* Include another file */
736 {
737     char Name [MAX_STR_LEN+1];
738
739     /* Name must follow */
740     if (Tok != TOK_STRCON) {
741         ErrorSkip (ERR_STRCON_EXPECTED);
742     } else {
743         strcpy (Name, SVal);
744         NextTok ();
745         NewInputFile (Name);
746     }
747 }
748
749
750
751 static void DoInvalid (void)
752 /* Handle a token that is invalid here, since it should have been handled on
753  * a much lower level of the expression hierarchy. Getting this sort of token
754  * means that the lower level code has bugs.
755  * This function differs to DoUnexpected in that the latter may be triggered
756  * by the user by using keywords in the wrong location. DoUnexpected is not
757  * an error in the assembler itself, while DoInvalid is.
758  */
759 {
760     Internal ("Unexpected token: %s", Keyword);
761 }
762
763
764
765 static void DoLineCont (void)
766 /* Switch the use of line continuations */
767 {
768     SetBoolOption (&LineCont);
769 }
770
771
772
773 static void DoList (void)
774 /* Enable/disable the listing */
775 {
776     /* Get the setting */
777     unsigned char List;
778     SetBoolOption (&List);
779
780     /* Manage the counter */
781     if (List) {
782         EnableListing ();
783     } else {
784         DisableListing ();
785     }
786 }
787
788
789
790 static void DoListBytes (void)
791 /* Set maximum number of bytes to list for one line */
792 {
793     SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
794 }
795
796
797
798 static void DoLocalChar (void)
799 /* Define the character that starts local labels */
800 {
801     if (Tok != TOK_CHARCON) {
802         ErrorSkip (ERR_CHARCON_EXPECTED);
803     } else {
804         if (IVal != '@' && IVal != '?') {
805             Error (ERR_ILLEGAL_LOCALSTART);
806         } else {
807             LocalStart = (char) IVal;
808         }
809         NextTok ();
810     }
811 }
812
813
814
815 static void DoMacPack (void)
816 /* Insert a macro package */
817 {
818     /* Macro package names */
819     static const char* Keys [] = {
820         "GENERIC",
821         "LONGBRANCH",
822     };
823
824     int Package;
825
826     /* We expect an identifier */
827     if (Tok != TOK_IDENT) {
828         ErrorSkip (ERR_IDENT_EXPECTED);
829         return;
830     }
831
832     /* Map the keyword to a number */
833     Package = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
834     if (Package < 0) {
835         /* Not found */
836         ErrorSkip (ERR_ILLEGAL_MACPACK);
837         return;
838     }
839
840     /* Skip the package name */
841     NextTok ();
842
843     /* Insert the package */
844     InsertMacPack (Package);
845 }
846
847
848
849 static void DoMacro (void)
850 /* Start a macro definition */
851 {
852     MacDef (MAC_STYLE_CLASSIC);
853 }
854
855
856
857 static void DoNull (void)
858 /* Switch to the NULL segment */
859 {
860     UseNullSeg ();
861 }
862
863
864
865 static void DoOrg (void)
866 /* Start absolute code */
867 {
868     long PC = ConstExpression ();
869     if (PC < 0 || PC > 0xFFFF) {
870         Error (ERR_RANGE);
871         return;
872     }
873     SetAbsPC (PC);
874 }
875
876
877
878 static void DoOut (void)
879 /* Output a string */
880 {
881     if (Tok != TOK_STRCON) {
882         ErrorSkip (ERR_STRCON_EXPECTED);
883     } else {
884         /* Output the string and be sure to flush the output to keep it in
885          * sync with any error messages if the output is redirected to a file.
886          */
887         printf ("%s\n", SVal);
888         fflush (stdout);
889         NextTok ();
890     }
891 }
892
893
894
895 static void DoP02 (void)
896 /* Switch to 6502 CPU */
897 {
898     SetCPU (CPU_6502);
899 }
900
901
902
903 static void DoPC02 (void)
904 /* Switch to 65C02 CPU */
905 {
906     SetCPU (CPU_65C02);
907 }
908
909
910
911 static void DoP816 (void)
912 /* Switch to 65816 CPU */
913 {
914     SetCPU (CPU_65816);
915 }
916
917
918
919 static void DoPageLength (void)
920 /* Set the page length for the listing */
921 {
922     PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
923 }
924
925
926
927 static void DoProc (void)
928 /* Start a new lexical scope */
929 {
930     if (Tok == TOK_IDENT) {
931         /* The new scope has a name */
932         SymDef (SVal, CurrentPC (), IsZPSeg ());
933         NextTok ();
934     }
935     SymEnterLevel ();
936 }
937
938
939
940 static void DoReloc (void)
941 /* Enter relocatable mode */
942 {
943     RelocMode = 1;
944 }
945
946
947
948 static void DoRepeat (void)
949 /* Repeat some instruction block */
950 {
951     ParseRepeat ();
952 }
953
954
955
956 static void DoRes (void)
957 /* Reserve some number of storage bytes */
958 {
959     long Count;
960     long Val;
961
962     Count = ConstExpression ();
963     if (Count > 0xFFFF || Count < 0) {
964         ErrorSkip (ERR_RANGE);
965         return;
966     }
967     if (Tok == TOK_COMMA) {
968         NextTok ();
969         Val = ConstExpression ();
970         /* We need a byte value here */
971         if (!IsByteRange (Val)) {
972             ErrorSkip (ERR_RANGE);
973             return;
974         }
975
976         /* Emit constant values */
977         while (Count--) {
978             Emit0 ((unsigned char) Val);
979         }
980
981     } else {
982         /* Emit fill fragments */
983         EmitFill (Count);
984     }
985 }
986
987
988
989 static void DoROData (void)
990 /* Switch to the r/o data segment */
991 {
992     UseRODataSeg ();
993 }
994
995
996
997 static void DoSegment (void)
998 /* Switch to another segment */
999 {
1000     static const char* AttrTab [] = {
1001         "ZEROPAGE", "DIRECT",
1002         "ABSOLUTE",
1003         "FAR", "LONG"
1004     };
1005     char Name [sizeof (SVal)];
1006     int SegType;
1007
1008     if (Tok != TOK_STRCON) {
1009         ErrorSkip (ERR_STRCON_EXPECTED);
1010     } else {
1011
1012         /* Save the name of the segment and skip it */
1013         strcpy (Name, SVal);
1014         NextTok ();
1015
1016         /* Check for an optional segment attribute */
1017         SegType = SEGTYPE_DEFAULT;
1018         if (Tok == TOK_COMMA) {
1019             NextTok ();
1020             if (Tok != TOK_IDENT) {
1021                 ErrorSkip (ERR_IDENT_EXPECTED);
1022             } else {
1023                 int Attr = GetSubKey (AttrTab, sizeof (AttrTab) / sizeof (AttrTab [0]));
1024                 switch (Attr) {
1025
1026                     case 0:
1027                     case 1:
1028                         /* Zeropage */
1029                         SegType = SEGTYPE_ZP;
1030                         break;
1031
1032                     case 2:
1033                         /* Absolute */
1034                         SegType = SEGTYPE_ABS;
1035                         break;
1036
1037                     case 3:
1038                     case 4:
1039                         /* Far */
1040                         SegType = SEGTYPE_FAR;
1041                         break;
1042
1043                     default:
1044                         Error (ERR_ILLEGAL_SEG_ATTR);
1045                 }
1046                 NextTok ();
1047             }
1048         }
1049
1050         /* Set the segment */
1051         UseSeg (Name, SegType);
1052     }
1053 }
1054
1055
1056
1057 static void DoSmart (void)
1058 /* Smart mode on/off */
1059 {
1060     SetBoolOption (&SmartMode);
1061 }
1062
1063
1064
1065 static void DoSunPlus (void)
1066 /* Switch to the SUNPLUS CPU */
1067 {
1068     SetCPU (CPU_SUNPLUS);
1069 }
1070
1071
1072
1073 static void DoUnexpected (void)
1074 /* Got an unexpected keyword */
1075 {
1076     Error (ERR_UNEXPECTED, Keyword);
1077     SkipUntilSep ();
1078 }
1079
1080
1081
1082 static void DoWarning (void)
1083 /* User warning */
1084 {
1085     if (Tok != TOK_STRCON) {
1086         ErrorSkip (ERR_STRCON_EXPECTED);
1087     } else {
1088         Warning (WARN_USER, SVal);
1089         SkipUntilSep ();
1090     }
1091 }
1092
1093
1094
1095 static void DoWord (void)
1096 /* Define words */
1097 {
1098     while (1) {
1099         EmitWord (Expression ());
1100         if (Tok != TOK_COMMA) {
1101             break;
1102         } else {
1103             NextTok ();
1104         }
1105     }
1106 }
1107
1108
1109
1110 static void DoZeropage (void)
1111 /* Switch to the zeropage segment */
1112 {
1113     UseZeropageSeg ();
1114 }
1115
1116
1117
1118 /*****************************************************************************/
1119 /*                                Table data                                 */
1120 /*****************************************************************************/
1121
1122
1123
1124 /* Control commands flags */
1125 enum {
1126     ccNone      = 0x0000,               /* No special flags */
1127     ccKeepToken = 0x0001                /* Do not skip the current token */
1128 };
1129
1130 /* Control command table */
1131 struct CtrlDesc_ {
1132     unsigned    Flags;                  /* Flags for this directive */
1133     void        (*Handler) (void);      /* Command handler */
1134 };
1135 typedef struct CtrlDesc_ CtrlDesc;
1136
1137 #define PSEUDO_COUNT    (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1138 static CtrlDesc CtrlCmdTab [] = {
1139     { ccNone,           DoA16           },
1140     { ccNone,           DoA8            },
1141     { ccNone,           DoAddr          },      /* .ADDR */
1142     { ccNone,           DoAlign         },
1143     { ccNone,           DoASCIIZ        },
1144     { ccNone,           DoAutoImport    },
1145     { ccNone,           DoUnexpected    },      /* .BLANK */
1146     { ccNone,           DoBss           },
1147     { ccNone,           DoByte          },
1148     { ccNone,           DoCase          },
1149     { ccNone,           DoCode          },
1150     { ccNone,           DoUnexpected,   },      /* .CONCAT */
1151     { ccNone,           DoUnexpected    },      /* .CONST */
1152     { ccNone,           DoUnexpected    },      /* .CPU */
1153     { ccNone,           DoData          },
1154     { ccNone,           DoDbg,          },
1155     { ccNone,           DoDByt          },
1156     { ccNone,           DoDebugInfo     },
1157     { ccNone,           DoDefine        },
1158     { ccNone,           DoUnexpected    },      /* .DEFINED */
1159     { ccNone,           DoDWord         },
1160     { ccKeepToken,      DoConditionals  },      /* .ELSE */
1161     { ccKeepToken,      DoConditionals  },      /* .ELSEIF */
1162     { ccNone,           DoEnd           },
1163     { ccKeepToken,      DoConditionals  },      /* .ENDIF */
1164     { ccNone,           DoUnexpected    },      /* .ENDMACRO */
1165     { ccNone,           DoEndProc       },
1166     { ccNone,           DoUnexpected    },      /* .ENDREPEAT */
1167     { ccNone,           DoError         },
1168     { ccNone,           DoExitMacro     },
1169     { ccNone,           DoExport        },
1170     { ccNone,           DoExportZP      },
1171     { ccNone,           DoFarAddr       },
1172     { ccNone,           DoFeature       },
1173     { ccNone,           DoFileOpt       },
1174     { ccNone,           DoUnexpected    },      /* .FORCEWORD */
1175     { ccNone,           DoGlobal        },
1176     { ccNone,           DoGlobalZP      },
1177     { ccNone,           DoI16           },
1178     { ccNone,           DoI8            },
1179     { ccKeepToken,      DoConditionals  },      /* .IF */
1180     { ccKeepToken,      DoConditionals  },      /* .IFBLANK */
1181     { ccKeepToken,      DoConditionals  },      /* .IFCONST */
1182     { ccKeepToken,      DoConditionals  },      /* .IFDEF */
1183     { ccKeepToken,      DoConditionals  },      /* .IFNBLANK */
1184     { ccKeepToken,      DoConditionals  },      /* .IFNCONST */
1185     { ccKeepToken,      DoConditionals  },      /* .IFNDEF */
1186     { ccKeepToken,      DoConditionals  },      /* .IFNREF */
1187     { ccKeepToken,      DoConditionals  },      /* .IFP02 */
1188     { ccKeepToken,      DoConditionals  },      /* .IFP816 */
1189     { ccKeepToken,      DoConditionals  },      /* .IFPC02 */
1190     { ccKeepToken,      DoConditionals  },      /* .IFREF */
1191     { ccNone,           DoImport        },
1192     { ccNone,           DoImportZP      },
1193     { ccNone,           DoIncBin        },
1194     { ccNone,           DoInclude       },
1195     { ccNone,           DoInvalid       },      /* .LEFT */
1196     { ccNone,           DoLineCont      },
1197     { ccNone,           DoList          },
1198     { ccNone,           DoListBytes     },
1199     { ccNone,           DoUnexpected    },      /* .LOCAL */
1200     { ccNone,           DoLocalChar     },
1201     { ccNone,           DoMacPack       },
1202     { ccNone,           DoMacro         },
1203     { ccNone,           DoUnexpected    },      /* .MATCH */
1204     { ccNone,           DoInvalid       },      /* .MID */
1205     { ccNone,           DoNull          },
1206     { ccNone,           DoOrg           },
1207     { ccNone,           DoOut           },
1208     { ccNone,           DoP02           },
1209     { ccNone,           DoP816          },
1210     { ccNone,           DoPageLength    },
1211     { ccNone,           DoUnexpected    },      /* .PARAMCOUNT */
1212     { ccNone,           DoPC02          },
1213     { ccNone,           DoProc          },
1214     { ccNone,           DoUnexpected    },      /* .REFERENCED */
1215     { ccNone,           DoReloc         },
1216     { ccNone,           DoRepeat        },
1217     { ccNone,           DoRes           },
1218     { ccNone,           DoInvalid       },      /* .RIGHT */
1219     { ccNone,           DoROData        },
1220     { ccNone,           DoSegment       },
1221     { ccNone,           DoSmart         },
1222     { ccNone,           DoUnexpected    },      /* .STRAT */
1223     { ccNone,           DoUnexpected    },      /* .STRING */
1224     { ccNone,           DoUnexpected    },      /* .STRLEN */
1225     { ccNone,           DoSunPlus       },
1226     { ccNone,           DoUnexpected    },      /* .TCOUNT */
1227     { ccNone,           DoWarning       },
1228     { ccNone,           DoWord          },
1229     { ccNone,           DoUnexpected    },      /* .XMATCH */
1230     { ccNone,           DoZeropage      },
1231 };
1232
1233
1234
1235 /*****************************************************************************/
1236 /*                                   Code                                    */
1237 /*****************************************************************************/
1238
1239
1240
1241 int TokIsPseudo (unsigned Tok)
1242 /* Return true if the given token is a pseudo instruction token */
1243 {
1244     return (Tok >= TOK_FIRSTPSEUDO && Tok <= TOK_LASTPSEUDO);
1245 }
1246
1247
1248
1249 void HandlePseudo (void)
1250 /* Handle a pseudo instruction */
1251 {
1252     CtrlDesc* D;
1253
1254     /* Calculate the index into the table */
1255     unsigned Index = Tok - TOK_FIRSTPSEUDO;
1256
1257     /* Safety check */
1258     if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
1259         Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
1260                   PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
1261     }
1262     CHECK (Index < PSEUDO_COUNT);
1263
1264     /* Get the pseudo intruction descriptor */
1265     D = &CtrlCmdTab [Index];
1266
1267     /* Remember the instruction, then skip it if needed */
1268     if ((D->Flags & ccKeepToken) == 0) {
1269         strcpy (Keyword+1, SVal);
1270         NextTok ();
1271     }
1272
1273     /* Call the handler */
1274     D->Handler ();
1275 }
1276
1277
1278