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