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