]> git.sur5r.net Git - cc65/blob - src/ca65/pseudo.c
94a5c3e4c7600679cd523fdb49a4e8bf5161d46a
[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:
381         default:    ErrorSkip (ERR_SYNTAX);     break;
382     }
383 }
384
385
386
387 static void DoDByt (void)
388 /* Output double bytes */
389 {
390     while (1) {
391         EmitWord (SwapExpr (Expression ()));
392         if (Tok != TOK_COMMA) {
393             break;
394         } else {
395             NextTok ();
396         }
397     }
398 }
399
400
401
402 static void DoDebugInfo (void)
403 /* Switch debug info on or off */
404 {
405     SetBoolOption (&DbgSyms);
406 }
407
408
409
410 static void DoDefine (void)
411 /* Define a one line macro */
412 {
413     MacDef (MAC_STYLE_DEFINE);
414 }
415
416
417
418 static void DoDWord (void)
419 /* Define dwords */
420 {
421     while (1) {
422         EmitDWord (Expression ());
423         if (Tok != TOK_COMMA) {
424             break;
425         } else {
426             NextTok ();
427         }
428     }
429 }
430
431
432
433 static void DoEnd (void)
434 /* End of assembly */
435 {
436     ForcedEnd = 1;
437 }
438
439
440
441 static void DoEndProc (void)
442 /* Leave a lexical level */
443 {
444     SymLeaveLevel ();
445 }
446
447
448
449 static void DoError (void)
450 /* User error */
451 {
452     if (Tok != TOK_STRCON) {
453         ErrorSkip (ERR_STRCON_EXPECTED);
454     } else {
455         Error (ERR_USER, SVal);
456         SkipUntilSep ();
457     }
458 }
459
460
461
462 static void DoExitMacro (void)
463 /* Exit a macro expansion */
464 {
465     if (!InMacExpansion ()) {
466         /* We aren't expanding a macro currently */
467         DoUnexpected ();
468     } else {
469         MacAbort ();
470     }
471 }
472
473
474
475 static void DoExport (void)
476 /* Export a symbol */
477 {
478     ExportImport (SymExport, 0);
479 }
480
481
482
483 static void DoExportZP (void)
484 /* Export a zeropage symbol */
485 {
486     ExportImport (SymExport, 1);
487 }
488
489
490
491 static void DoFarAddr (void)
492 /* Define far addresses (24 bit) */
493 {
494     while (1) {
495         EmitFarAddr (Expression ());
496         if (Tok != TOK_COMMA) {
497             break;
498         } else {
499             NextTok ();
500         }
501     }
502 }
503
504
505
506 static void DoFeature (void)
507 /* Switch the Feature option */
508 {
509     int Feature;
510
511     static const char* Keys[] = {
512         "DOLLAR_IS_PC",
513         "LABELS_WITHOUT_COLONS",
514         "LOOSE_STRING_TERM",
515         "AT_IN_IDENTIFIERS",
516         "DOLLAR_IN_IDENTIFIERS",
517     };
518
519     /* Allow a list of comma separated keywords */
520     while (1) {
521
522         /* We expect an identifier */
523         if (Tok != TOK_IDENT) {
524             ErrorSkip (ERR_IDENT_EXPECTED);
525             return;
526         }
527
528         /* Map the keyword to a number */
529         Feature = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
530         if (Feature < 0) {
531             /* Not found */
532             ErrorSkip (ERR_ILLEGAL_FEATURE);
533             return;
534         }
535
536         /* Skip the keyword */
537         NextTok ();
538
539         /* Switch the feature on */
540         switch (Feature) {
541             case 0:     DollarIsPC      = 1;    break;
542             case 1:     NoColonLabels   = 1;    break;
543             case 2:     LooseStringTerm = 1;    break;
544             case 3:     AtInIdents      = 1;    break;
545             case 4:     DollarInIdents  = 1;    break;
546             default:    Internal ("Invalid feature: %d", Feature);
547         }
548
549         /* Allow more than one keyword */
550         if (Tok == TOK_COMMA) {
551             NextTok ();
552         } else {
553             break;
554         }
555     }
556 }
557
558
559
560 static void DoFileOpt (void)
561 /* Insert a file option */
562 {
563     long OptNum;
564
565     /* The option type may be given as a keyword or as a number. */
566     if (Tok == TOK_IDENT) {
567
568         /* Option given as keyword */
569         static const char* Keys [] = {
570             "AUTHOR", "COMMENT", "COMPILER"
571         };
572
573         /* Map the option to a number */
574         OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
575         if (OptNum < 0) {
576             /* Not found */
577             ErrorSkip (ERR_OPTION_KEY_EXPECTED);
578             return;
579         }
580
581         /* Skip the keyword */
582         NextTok ();
583
584         /* Must be followed by a comma */
585         ConsumeComma ();
586
587         /* We accept only string options for now */
588         if (Tok != TOK_STRCON) {
589             ErrorSkip (ERR_STRCON_EXPECTED);
590             return;
591         }
592
593         /* Insert the option */
594         switch (OptNum) {
595
596             case 0:
597                 /* Author */
598                 OptAuthor (SVal);
599                 break;
600
601             case 1:
602                 /* Comment */
603                 OptComment (SVal);
604                 break;
605
606             case 2:
607                 /* Compiler */
608                 OptCompiler (SVal);
609                 break;
610
611             default:
612                 Internal ("Invalid OptNum: %l", OptNum);
613
614         }
615
616         /* Done */
617         NextTok ();
618
619     } else {
620
621         /* Option given as number */
622         OptNum = ConstExpression ();
623         if (!IsByteRange (OptNum)) {
624             ErrorSkip (ERR_RANGE);
625             return;
626         }
627
628         /* Must be followed by a comma */
629         ConsumeComma ();
630
631         /* We accept only string options for now */
632         if (Tok != TOK_STRCON) {
633             ErrorSkip (ERR_STRCON_EXPECTED);
634             return;
635         }
636
637         /* Insert the option */
638         OptStr ((unsigned char) OptNum, SVal);
639
640         /* Done */
641         NextTok ();
642     }
643 }
644
645
646
647 static void DoGlobal (void)
648 /* Declare a global symbol */
649 {
650     ExportImport (SymGlobal, 0);
651 }
652
653
654
655 static void DoGlobalZP (void)
656 /* Declare a global zeropage symbol */
657 {
658     ExportImport (SymGlobal, 1);
659 }
660
661
662
663 static void DoI16 (void)
664 /* Switch the index registers to 16 bit mode (assembler only) */
665 {
666     if (GetCPU() != CPU_65816) {
667         Error (ERR_816_MODE_ONLY);
668     } else {
669         /* Immidiate mode has two extension bytes */
670         ExtBytes [AMI_IMM_INDEX] = 2;
671     }
672 }
673
674
675
676 static void DoI8 (void)
677 /* Switch the index registers to 16 bit mode (assembler only) */
678 {
679     if (GetCPU() != CPU_65816) {
680         Error (ERR_816_MODE_ONLY);
681     } else {
682         /* Immidiate mode has one extension byte */
683         ExtBytes [AMI_IMM_INDEX] = 1;
684     }
685 }
686
687
688
689 static void DoImport (void)
690 /* Import a symbol */
691 {
692     ExportImport (SymImport, 0);
693 }
694
695
696
697 static void DoImportZP (void)
698 /* Import a zero page symbol */
699 {
700     ExportImport (SymImport, 1);
701 }
702
703
704
705 static void DoIncBin (void)
706 /* Include a binary file */
707 {
708     /* Name must follow */
709     if (Tok != TOK_STRCON) {
710         ErrorSkip (ERR_STRCON_EXPECTED);
711     } else {
712         /* Try to open the file */
713         FILE* F = fopen (SVal, "rb");
714         if (F == 0) {
715             Error (ERR_CANNOT_OPEN_INCLUDE, SVal, strerror (errno));
716         } else {
717             unsigned char Buf [1024];
718             size_t Count;
719             /* Read chunks and insert them into the output */
720             while ((Count = fread (Buf, 1, sizeof (Buf), F)) > 0) {
721                 EmitData (Buf, Count);
722             }
723             /* Close the file, ignore errors since it's r/o */
724             (void) fclose (F);
725         }
726         /* Skip the name */
727         NextTok ();
728     }
729 }
730
731
732
733 static void DoInclude (void)
734 /* Include another file */
735 {
736     char Name [MAX_STR_LEN+1];
737
738     /* Name must follow */
739     if (Tok != TOK_STRCON) {
740         ErrorSkip (ERR_STRCON_EXPECTED);
741     } else {
742         strcpy (Name, SVal);
743         NextTok ();
744         NewInputFile (Name);
745     }
746 }
747
748
749
750 static void DoInvalid (void)
751 /* Handle a token that is invalid here, since it should have been handled on
752  * a much lower level of the expression hierarchy. Getting this sort of token
753  * means that the lower level code has bugs.
754  * This function differs to DoUnexpected in that the latter may be triggered
755  * by the user by using keywords in the wrong location. DoUnexpected is not
756  * an error in the assembler itself, while DoInvalid is.
757  */
758 {
759     Internal ("Unexpected token: %s", Keyword);
760 }
761
762
763
764 static void DoLineCont (void)
765 /* Switch the use of line continuations */
766 {
767     SetBoolOption (&LineCont);
768 }
769
770
771
772 static void DoList (void)
773 /* Enable/disable the listing */
774 {
775     /* Get the setting */
776     unsigned char List;
777     SetBoolOption (&List);
778
779     /* Manage the counter */
780     if (List) {
781         EnableListing ();
782     } else {
783         DisableListing ();
784     }
785 }
786
787
788
789 static void DoListBytes (void)
790 /* Set maximum number of bytes to list for one line */
791 {
792     SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
793 }
794
795
796
797 static void DoLocalChar (void)
798 /* Define the character that starts local labels */
799 {
800     if (Tok != TOK_CHARCON) {
801         ErrorSkip (ERR_CHARCON_EXPECTED);
802     } else {
803         if (IVal != '@' && IVal != '?') {
804             Error (ERR_ILLEGAL_LOCALSTART);
805         } else {
806             LocalStart = (char) IVal;
807         }
808         NextTok ();
809     }
810 }
811
812
813
814 static void DoMacPack (void)
815 /* Insert a macro package */
816 {
817     /* Macro package names */
818     static const char* Keys [] = {
819         "GENERIC",
820         "LONGBRANCH",
821     };
822
823     int Package;
824
825     /* We expect an identifier */
826     if (Tok != TOK_IDENT) {
827         ErrorSkip (ERR_IDENT_EXPECTED);
828         return;
829     }
830
831     /* Map the keyword to a number */
832     Package = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
833     if (Package < 0) {
834         /* Not found */
835         ErrorSkip (ERR_ILLEGAL_MACPACK);
836         return;
837     }
838
839     /* Skip the package name */
840     NextTok ();
841
842     /* Insert the package */
843     InsertMacPack (Package);
844 }
845
846
847
848 static void DoMacro (void)
849 /* Start a macro definition */
850 {
851     MacDef (MAC_STYLE_CLASSIC);
852 }
853
854
855
856 static void DoNull (void)
857 /* Switch to the NULL segment */
858 {
859     UseNullSeg ();
860 }
861
862
863
864 static void DoOrg (void)
865 /* Start absolute code */
866 {
867     long PC = ConstExpression ();
868     if (PC < 0 || PC > 0xFFFF) {
869         Error (ERR_RANGE);
870         return;
871     }
872     SetAbsPC (PC);
873 }
874
875
876
877 static void DoOut (void)
878 /* Output a string */
879 {
880     if (Tok != TOK_STRCON) {
881         ErrorSkip (ERR_STRCON_EXPECTED);
882     } else {
883         /* Output the string and be sure to flush the output to keep it in
884          * sync with any error messages if the output is redirected to a file.
885          */
886         printf ("%s\n", SVal);
887         fflush (stdout);
888         NextTok ();
889     }
890 }
891
892
893
894 static void DoP02 (void)
895 /* Switch to 6502 CPU */
896 {
897     SetCPU (CPU_6502);
898 }
899
900
901
902 static void DoPC02 (void)
903 /* Switch to 65C02 CPU */
904 {
905     SetCPU (CPU_65C02);
906 }
907
908
909
910 static void DoP816 (void)
911 /* Switch to 65816 CPU */
912 {
913     SetCPU (CPU_65816);
914 }
915
916
917
918 static void DoPageLength (void)
919 /* Set the page length for the listing */
920 {
921     PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
922 }
923
924
925
926 static void DoProc (void)
927 /* Start a new lexical scope */
928 {
929     if (Tok == TOK_IDENT) {
930         /* The new scope has a name */
931         SymDef (SVal, CurrentPC (), IsZPSeg ());
932         NextTok ();
933     }
934     SymEnterLevel ();
935 }
936
937
938
939 static void DoReloc (void)
940 /* Enter relocatable mode */
941 {
942     RelocMode = 1;
943 }
944
945
946
947 static void DoRepeat (void)
948 /* Repeat some instruction block */
949 {
950     ParseRepeat ();
951 }
952
953
954
955 static void DoRes (void)
956 /* Reserve some number of storage bytes */
957 {
958     long Count;
959     long Val;
960
961     Count = ConstExpression ();
962     if (Count > 0xFFFF || Count < 0) {
963         ErrorSkip (ERR_RANGE);
964         return;
965     }
966     if (Tok == TOK_COMMA) {
967         NextTok ();
968         Val = ConstExpression ();
969         /* We need a byte value here */
970         if (!IsByteRange (Val)) {
971             ErrorSkip (ERR_RANGE);
972             return;
973         }
974
975         /* Emit constant values */
976         while (Count--) {
977             Emit0 ((unsigned char) Val);
978         }
979
980     } else {
981         /* Emit fill fragments */
982         EmitFill (Count);
983     }
984 }
985
986
987
988 static void DoROData (void)
989 /* Switch to the r/o data segment */
990 {
991     UseRODataSeg ();
992 }
993
994
995
996 static void DoSegment (void)
997 /* Switch to another segment */
998 {
999     static const char* AttrTab [] = {
1000         "ZEROPAGE", "DIRECT",
1001         "ABSOLUTE",
1002         "FAR", "LONG"
1003     };
1004     char Name [sizeof (SVal)];
1005     int SegType;
1006
1007     if (Tok != TOK_STRCON) {
1008         ErrorSkip (ERR_STRCON_EXPECTED);
1009     } else {
1010
1011         /* Save the name of the segment and skip it */
1012         strcpy (Name, SVal);
1013         NextTok ();
1014
1015         /* Check for an optional segment attribute */
1016         SegType = SEGTYPE_DEFAULT;
1017         if (Tok == TOK_COMMA) {
1018             NextTok ();
1019             if (Tok != TOK_IDENT) {
1020                 ErrorSkip (ERR_IDENT_EXPECTED);
1021             } else {
1022                 int Attr = GetSubKey (AttrTab, sizeof (AttrTab) / sizeof (AttrTab [0]));
1023                 switch (Attr) {
1024
1025                     case 0:
1026                     case 1:
1027                         /* Zeropage */
1028                         SegType = SEGTYPE_ZP;
1029                         break;
1030
1031                     case 2:
1032                         /* Absolute */
1033                         SegType = SEGTYPE_ABS;
1034                         break;
1035
1036                     case 3:
1037                     case 4:
1038                         /* Far */
1039                         SegType = SEGTYPE_FAR;
1040                         break;
1041
1042                     default:
1043                         Error (ERR_ILLEGAL_SEG_ATTR);
1044                 }
1045                 NextTok ();
1046             }
1047         }
1048
1049         /* Set the segment */
1050         UseSeg (Name, SegType);
1051     }
1052 }
1053
1054
1055
1056 static void DoSmart (void)
1057 /* Smart mode on/off */
1058 {
1059     SetBoolOption (&SmartMode);
1060 }
1061
1062
1063
1064 static void DoSunPlus (void)
1065 /* Switch to the SUNPLUS CPU */
1066 {
1067     SetCPU (CPU_SUNPLUS);
1068 }
1069
1070
1071
1072 static void DoUnexpected (void)
1073 /* Got an unexpected keyword */
1074 {
1075     Error (ERR_UNEXPECTED, Keyword);
1076     SkipUntilSep ();
1077 }
1078
1079
1080
1081 static void DoWarning (void)
1082 /* User warning */
1083 {
1084     if (Tok != TOK_STRCON) {
1085         ErrorSkip (ERR_STRCON_EXPECTED);
1086     } else {
1087         Warning (WARN_USER, SVal);
1088         SkipUntilSep ();
1089     }
1090 }
1091
1092
1093
1094 static void DoWord (void)
1095 /* Define words */
1096 {
1097     while (1) {
1098         EmitWord (Expression ());
1099         if (Tok != TOK_COMMA) {
1100             break;
1101         } else {
1102             NextTok ();
1103         }
1104     }
1105 }
1106
1107
1108
1109 static void DoZeropage (void)
1110 /* Switch to the zeropage segment */
1111 {
1112     UseZeropageSeg ();
1113 }
1114
1115
1116
1117 /*****************************************************************************/
1118 /*                                Table data                                 */
1119 /*****************************************************************************/
1120
1121
1122
1123 /* Control commands flags */
1124 enum {
1125     ccNone      = 0x0000,               /* No special flags */
1126     ccKeepToken = 0x0001                /* Do not skip the current token */
1127 };
1128
1129 /* Control command table */
1130 struct CtrlDesc_ {
1131     unsigned    Flags;                  /* Flags for this directive */
1132     void        (*Handler) (void);      /* Command handler */
1133 };
1134 typedef struct CtrlDesc_ CtrlDesc;
1135
1136 #define PSEUDO_COUNT    (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1137 static CtrlDesc CtrlCmdTab [] = {
1138     { ccNone,           DoA16           },
1139     { ccNone,           DoA8            },
1140     { ccNone,           DoAddr          },      /* .ADDR */
1141     { ccNone,           DoAlign         },
1142     { ccNone,           DoASCIIZ        },
1143     { ccNone,           DoAutoImport    },
1144     { ccNone,           DoUnexpected    },      /* .BLANK */
1145     { ccNone,           DoBss           },
1146     { ccNone,           DoByte          },
1147     { ccNone,           DoCase          },
1148     { ccNone,           DoCode          },
1149     { ccNone,           DoUnexpected,   },      /* .CONCAT */
1150     { ccNone,           DoUnexpected    },      /* .CONST */
1151     { ccNone,           DoUnexpected    },      /* .CPU */
1152     { ccNone,           DoData          },
1153     { ccNone,           DoDbg,          },
1154     { ccNone,           DoDByt          },
1155     { ccNone,           DoDebugInfo     },
1156     { ccNone,           DoDefine        },
1157     { ccNone,           DoUnexpected    },      /* .DEFINED */
1158     { ccNone,           DoDWord         },
1159     { ccKeepToken,      DoConditionals  },      /* .ELSE */
1160     { ccKeepToken,      DoConditionals  },      /* .ELSEIF */
1161     { ccNone,           DoEnd           },
1162     { ccKeepToken,      DoConditionals  },      /* .ENDIF */
1163     { ccNone,           DoUnexpected    },      /* .ENDMACRO */
1164     { ccNone,           DoEndProc       },
1165     { ccNone,           DoUnexpected    },      /* .ENDREPEAT */
1166     { ccNone,           DoError         },
1167     { ccNone,           DoExitMacro     },
1168     { ccNone,           DoExport        },
1169     { ccNone,           DoExportZP      },
1170     { ccNone,           DoFarAddr       },
1171     { ccNone,           DoFeature       },
1172     { ccNone,           DoFileOpt       },
1173     { ccNone,           DoUnexpected    },      /* .FORCEWORD */
1174     { ccNone,           DoGlobal        },
1175     { ccNone,           DoGlobalZP      },
1176     { ccNone,           DoI16           },
1177     { ccNone,           DoI8            },
1178     { ccKeepToken,      DoConditionals  },      /* .IF */
1179     { ccKeepToken,      DoConditionals  },      /* .IFBLANK */
1180     { ccKeepToken,      DoConditionals  },      /* .IFCONST */
1181     { ccKeepToken,      DoConditionals  },      /* .IFDEF */
1182     { ccKeepToken,      DoConditionals  },      /* .IFNBLANK */
1183     { ccKeepToken,      DoConditionals  },      /* .IFNCONST */
1184     { ccKeepToken,      DoConditionals  },      /* .IFNDEF */
1185     { ccKeepToken,      DoConditionals  },      /* .IFNREF */
1186     { ccKeepToken,      DoConditionals  },      /* .IFP02 */
1187     { ccKeepToken,      DoConditionals  },      /* .IFP816 */
1188     { ccKeepToken,      DoConditionals  },      /* .IFPC02 */
1189     { ccKeepToken,      DoConditionals  },      /* .IFREF */
1190     { ccNone,           DoImport        },
1191     { ccNone,           DoImportZP      },
1192     { ccNone,           DoIncBin        },
1193     { ccNone,           DoInclude       },
1194     { ccNone,           DoInvalid       },      /* .LEFT */
1195     { ccNone,           DoLineCont      },
1196     { ccNone,           DoList          },
1197     { ccNone,           DoListBytes     },
1198     { ccNone,           DoUnexpected    },      /* .LOCAL */
1199     { ccNone,           DoLocalChar     },
1200     { ccNone,           DoMacPack       },
1201     { ccNone,           DoMacro         },
1202     { ccNone,           DoUnexpected    },      /* .MATCH */
1203     { ccNone,           DoInvalid       },      /* .MID */
1204     { ccNone,           DoNull          },
1205     { ccNone,           DoOrg           },
1206     { ccNone,           DoOut           },
1207     { ccNone,           DoP02           },
1208     { ccNone,           DoP816          },
1209     { ccNone,           DoPageLength    },
1210     { ccNone,           DoUnexpected    },      /* .PARAMCOUNT */
1211     { ccNone,           DoPC02          },
1212     { ccNone,           DoProc          },
1213     { ccNone,           DoUnexpected    },      /* .REFERENCED */
1214     { ccNone,           DoReloc         },
1215     { ccNone,           DoRepeat        },
1216     { ccNone,           DoRes           },
1217     { ccNone,           DoInvalid       },      /* .RIGHT */
1218     { ccNone,           DoROData        },
1219     { ccNone,           DoSegment       },
1220     { ccNone,           DoSmart         },
1221     { ccNone,           DoUnexpected    },      /* .STRAT */
1222     { ccNone,           DoUnexpected    },      /* .STRING */
1223     { ccNone,           DoUnexpected    },      /* .STRLEN */
1224     { ccNone,           DoSunPlus       },
1225     { ccNone,           DoUnexpected    },      /* .TCOUNT */
1226     { ccNone,           DoWarning       },
1227     { ccNone,           DoWord          },
1228     { ccNone,           DoUnexpected    },      /* .XMATCH */
1229     { ccNone,           DoZeropage      },
1230 };
1231
1232
1233
1234 /*****************************************************************************/
1235 /*                                   Code                                    */
1236 /*****************************************************************************/
1237
1238
1239
1240 int TokIsPseudo (unsigned Tok)
1241 /* Return true if the given token is a pseudo instruction token */
1242 {
1243     return (Tok >= TOK_FIRSTPSEUDO && Tok <= TOK_LASTPSEUDO);
1244 }
1245
1246
1247
1248 void HandlePseudo (void)
1249 /* Handle a pseudo instruction */
1250 {
1251     CtrlDesc* D;
1252
1253     /* Calculate the index into the table */
1254     unsigned Index = Tok - TOK_FIRSTPSEUDO;
1255
1256     /* Safety check */
1257     if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
1258         Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
1259                   PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
1260     }
1261     CHECK (Index < PSEUDO_COUNT);
1262
1263     /* Get the pseudo intruction descriptor */
1264     D = &CtrlCmdTab [Index];
1265
1266     /* Remember the instruction, then skip it if needed */
1267     if ((D->Flags & ccKeepToken) == 0) {
1268         strcpy (Keyword+1, SVal);
1269         NextTok ();
1270     }
1271
1272     /* Call the handler */
1273     D->Handler ();
1274 }
1275
1276
1277