]> git.sur5r.net Git - cc65/blob - src/ca65/pseudo.c
22c6e533b26cbf4b20930c4e546f980d870d4764
[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     /* Parameters are separated by a comma */
486     ConsumeComma ();
487
488     /* Check the key and dispatch to a handler */
489     switch (Key) {
490         case 0:     DbgInfoFile ();             break;
491         case 1:     DbgInfoLine ();             break;
492         case 2:     DbgInfoSym ();              break;
493         default:    ErrorSkip (ERR_SYNTAX);     break;
494     }
495 }
496
497
498
499 static void DoDByt (void)
500 /* Output double bytes */
501 {
502     while (1) {
503         EmitWord (SwapExpr (Expression ()));
504         if (Tok != TOK_COMMA) {
505             break;
506         } else {
507             NextTok ();
508         }
509     }
510 }
511
512
513
514 static void DoDebugInfo (void)
515 /* Switch debug info on or off */
516 {
517     SetBoolOption (&DbgSyms);
518 }
519
520
521
522 static void DoDefine (void)
523 /* Define a one line macro */
524 {
525     MacDef (MAC_STYLE_DEFINE);
526 }
527
528
529
530 static void DoDestructor (void)
531 /* Export a symbol as destructor */
532 {
533     char Name [sizeof (SVal)];
534
535     /* Symbol name follows */
536     if (Tok != TOK_IDENT) {
537         ErrorSkip (ERR_IDENT_EXPECTED);
538         return;
539     }
540     strcpy (Name, SVal);
541     NextTok ();
542
543     /* Parse the remainder of the line and export the symbol */
544     ConDes (Name, CD_TYPE_DES);
545 }
546
547
548
549 static void DoDWord (void)
550 /* Define dwords */
551 {
552     while (1) {
553         EmitDWord (Expression ());
554         if (Tok != TOK_COMMA) {
555             break;
556         } else {
557             NextTok ();
558         }
559     }
560 }
561
562
563
564 static void DoEnd (void)
565 /* End of assembly */
566 {
567     ForcedEnd = 1;
568 }
569
570
571
572 static void DoEndProc (void)
573 /* Leave a lexical level */
574 {
575     SymLeaveLevel ();
576 }
577
578
579
580 static void DoError (void)
581 /* User error */
582 {
583     if (Tok != TOK_STRCON) {
584         ErrorSkip (ERR_STRCON_EXPECTED);
585     } else {
586         Error (ERR_USER, SVal);
587         SkipUntilSep ();
588     }
589 }
590
591
592
593 static void DoExitMacro (void)
594 /* Exit a macro expansion */
595 {
596     if (!InMacExpansion ()) {
597         /* We aren't expanding a macro currently */
598         DoUnexpected ();
599     } else {
600         MacAbort ();
601     }
602 }
603
604
605
606 static void DoExport (void)
607 /* Export a symbol */
608 {
609     ExportImport (SymExport, 0);
610 }
611
612
613
614 static void DoExportZP (void)
615 /* Export a zeropage symbol */
616 {
617     ExportImport (SymExport, 1);
618 }
619
620
621
622 static void DoFarAddr (void)
623 /* Define far addresses (24 bit) */
624 {
625     while (1) {
626         EmitFarAddr (Expression ());
627         if (Tok != TOK_COMMA) {
628             break;
629         } else {
630             NextTok ();
631         }
632     }
633 }
634
635
636
637 static void DoFeature (void)
638 /* Switch the Feature option */
639 {
640     /* Allow a list of comma separated keywords */
641     while (1) {
642
643         /* We expect an identifier */
644         if (Tok != TOK_IDENT) {
645             ErrorSkip (ERR_IDENT_EXPECTED);
646             return;
647         }
648
649         /* Make the string attribute lower case */
650         LocaseSVal ();
651
652         /* Set the feature and check for errors */
653         if (SetFeature (SVal) == FEAT_UNKNOWN) {
654             /* Not found */
655             ErrorSkip (ERR_ILLEGAL_FEATURE);
656             return;
657         } else {
658             /* Skip the keyword */
659             NextTok ();
660         }
661
662         /* Allow more than one keyword */
663         if (Tok == TOK_COMMA) {
664             NextTok ();
665         } else {
666             break;
667         }
668     }
669 }
670
671
672
673 static void DoFileOpt (void)
674 /* Insert a file option */
675 {
676     long OptNum;
677
678     /* The option type may be given as a keyword or as a number. */
679     if (Tok == TOK_IDENT) {
680
681         /* Option given as keyword */
682         static const char* Keys [] = {
683             "AUTHOR", "COMMENT", "COMPILER"
684         };
685
686         /* Map the option to a number */
687         OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
688         if (OptNum < 0) {
689             /* Not found */
690             ErrorSkip (ERR_OPTION_KEY_EXPECTED);
691             return;
692         }
693
694         /* Skip the keyword */
695         NextTok ();
696
697         /* Must be followed by a comma */
698         ConsumeComma ();
699
700         /* We accept only string options for now */
701         if (Tok != TOK_STRCON) {
702             ErrorSkip (ERR_STRCON_EXPECTED);
703             return;
704         }
705
706         /* Insert the option */
707         switch (OptNum) {
708
709             case 0:
710                 /* Author */
711                 OptAuthor (SVal);
712                 break;
713
714             case 1:
715                 /* Comment */
716                 OptComment (SVal);
717                 break;
718
719             case 2:
720                 /* Compiler */
721                 OptCompiler (SVal);
722                 break;
723
724             default:
725                 Internal ("Invalid OptNum: %l", OptNum);
726
727         }
728
729         /* Done */
730         NextTok ();
731
732     } else {
733
734         /* Option given as number */
735         OptNum = ConstExpression ();
736         if (!IsByteRange (OptNum)) {
737             ErrorSkip (ERR_RANGE);
738             return;
739         }
740
741         /* Must be followed by a comma */
742         ConsumeComma ();
743
744         /* We accept only string options for now */
745         if (Tok != TOK_STRCON) {
746             ErrorSkip (ERR_STRCON_EXPECTED);
747             return;
748         }
749
750         /* Insert the option */
751         OptStr ((unsigned char) OptNum, SVal);
752
753         /* Done */
754         NextTok ();
755     }
756 }
757
758
759
760 static void DoGlobal (void)
761 /* Declare a global symbol */
762 {
763     ExportImport (SymGlobal, 0);
764 }
765
766
767
768 static void DoGlobalZP (void)
769 /* Declare a global zeropage symbol */
770 {
771     ExportImport (SymGlobal, 1);
772 }
773
774
775
776 static void DoI16 (void)
777 /* Switch the index registers to 16 bit mode (assembler only) */
778 {
779     if (GetCPU() != CPU_65816) {
780         Error (ERR_816_MODE_ONLY);
781     } else {
782         /* Immidiate mode has two extension bytes */
783         ExtBytes [AMI_IMM_INDEX] = 2;
784     }
785 }
786
787
788
789 static void DoI8 (void)
790 /* Switch the index registers to 16 bit mode (assembler only) */
791 {
792     if (GetCPU() != CPU_65816) {
793         Error (ERR_816_MODE_ONLY);
794     } else {
795         /* Immidiate mode has one extension byte */
796         ExtBytes [AMI_IMM_INDEX] = 1;
797     }
798 }
799
800
801
802 static void DoImport (void)
803 /* Import a symbol */
804 {
805     ExportImport (SymImport, 0);
806 }
807
808
809
810 static void DoImportZP (void)
811 /* Import a zero page symbol */
812 {
813     ExportImport (SymImport, 1);
814 }
815
816
817
818 static void DoIncBin (void)
819 /* Include a binary file */
820 {
821     char Name [sizeof (SVal)];
822     long Start = 0L;
823     long Count = -1L;
824     long Size;
825     FILE* F;
826
827     /* Name must follow */
828     if (Tok != TOK_STRCON) {
829         ErrorSkip (ERR_STRCON_EXPECTED);
830         return;
831     }
832     strcpy (Name, SVal);
833     NextTok ();
834
835     /* A starting offset may follow */
836     if (Tok == TOK_COMMA) {
837         NextTok ();
838         Start = ConstExpression ();
839
840         /* And a length may follow */
841         if (Tok == TOK_COMMA) {
842             NextTok ();
843             Count = ConstExpression ();
844         }
845
846     }
847
848     /* Try to open the file */
849     F = fopen (Name, "rb");
850     if (F == 0) {
851         ErrorSkip (ERR_CANNOT_OPEN_INCLUDE, Name, strerror (errno));
852         return;
853     }
854
855     /* Get the size of the file */
856     fseek (F, 0, SEEK_END);
857     Size = ftell (F);
858
859     /* If a count was not given, calculate it now */
860     if (Count < 0) {
861         Count = Size - Start;
862         if (Count < 0) {
863             /* Nothing to read - flag this as a range error */
864             ErrorSkip (ERR_RANGE);
865             goto Done;
866         }
867     } else {
868         /* Count was given, check if it is valid */
869         if (Start + Count > Size) {
870             ErrorSkip (ERR_RANGE);
871             goto Done;
872         }
873     }
874
875     /* Seek to the start position */
876     fseek (F, Start, SEEK_SET);
877
878     /* Read chunks and insert them into the output */
879     while (Count > 0) {
880
881         unsigned char Buf [1024];
882
883         /* Calculate the number of bytes to read */
884         size_t BytesToRead = (Count > (long)sizeof(Buf))? sizeof(Buf) : (size_t) Count;
885
886         /* Read chunk */
887         size_t BytesRead = fread (Buf, 1, BytesToRead, F);
888         if (BytesToRead != BytesRead) {
889             /* Some sort of error */
890             ErrorSkip (ERR_CANNOT_READ_INCLUDE, Name, strerror (errno));
891             break;
892         }
893
894         /* Insert it into the output */
895         EmitData (Buf, Count);
896
897         /* Keep the counters current */
898         Count -= BytesRead;
899     }
900
901 Done:
902     /* Close the file, ignore errors since it's r/o */
903     (void) fclose (F);
904 }
905
906
907
908 static void DoInclude (void)
909 /* Include another file */
910 {
911     char Name [MAX_STR_LEN+1];
912
913     /* Name must follow */
914     if (Tok != TOK_STRCON) {
915         ErrorSkip (ERR_STRCON_EXPECTED);
916     } else {
917         strcpy (Name, SVal);
918         NextTok ();
919         NewInputFile (Name);
920     }
921 }
922
923
924
925 static void DoInvalid (void)
926 /* Handle a token that is invalid here, since it should have been handled on
927  * a much lower level of the expression hierarchy. Getting this sort of token
928  * means that the lower level code has bugs.
929  * This function differs to DoUnexpected in that the latter may be triggered
930  * by the user by using keywords in the wrong location. DoUnexpected is not
931  * an error in the assembler itself, while DoInvalid is.
932  */
933 {
934     Internal ("Unexpected token: %s", Keyword);
935 }
936
937
938
939 static void DoLineCont (void)
940 /* Switch the use of line continuations */
941 {
942     SetBoolOption (&LineCont);
943 }
944
945
946
947 static void DoList (void)
948 /* Enable/disable the listing */
949 {
950     /* Get the setting */
951     unsigned char List;
952     SetBoolOption (&List);
953
954     /* Manage the counter */
955     if (List) {
956         EnableListing ();
957     } else {
958         DisableListing ();
959     }
960 }
961
962
963
964 static void DoListBytes (void)
965 /* Set maximum number of bytes to list for one line */
966 {
967     SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
968 }
969
970
971
972 static void DoLocalChar (void)
973 /* Define the character that starts local labels */
974 {
975     if (Tok != TOK_CHARCON) {
976         ErrorSkip (ERR_CHARCON_EXPECTED);
977     } else {
978         if (IVal != '@' && IVal != '?') {
979             Error (ERR_ILLEGAL_LOCALSTART);
980         } else {
981             LocalStart = (char) IVal;
982         }
983         NextTok ();
984     }
985 }
986
987
988
989 static void DoMacPack (void)
990 /* Insert a macro package */
991 {
992     /* Macro package names */
993     static const char* Keys [] = {
994         "GENERIC",
995         "LONGBRANCH",
996     };
997
998     int Package;
999
1000     /* We expect an identifier */
1001     if (Tok != TOK_IDENT) {
1002         ErrorSkip (ERR_IDENT_EXPECTED);
1003         return;
1004     }
1005
1006     /* Map the keyword to a number */
1007     Package = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
1008     if (Package < 0) {
1009         /* Not found */
1010         ErrorSkip (ERR_ILLEGAL_MACPACK);
1011         return;
1012     }
1013
1014     /* Skip the package name */
1015     NextTok ();
1016
1017     /* Insert the package */
1018     InsertMacPack (Package);
1019 }
1020
1021
1022
1023 static void DoMacro (void)
1024 /* Start a macro definition */
1025 {
1026     MacDef (MAC_STYLE_CLASSIC);
1027 }
1028
1029
1030
1031 static void DoNull (void)
1032 /* Switch to the NULL segment */
1033 {
1034     UseNullSeg ();
1035 }
1036
1037
1038
1039 static void DoOrg (void)
1040 /* Start absolute code */
1041 {
1042     long PC = ConstExpression ();
1043     if (PC < 0 || PC > 0xFFFFFF) {
1044         Error (ERR_RANGE);
1045         return;
1046     }
1047     SetAbsPC (PC);
1048 }
1049
1050
1051
1052 static void DoOut (void)
1053 /* Output a string */
1054 {
1055     if (Tok != TOK_STRCON) {
1056         ErrorSkip (ERR_STRCON_EXPECTED);
1057     } else {
1058         /* Output the string and be sure to flush the output to keep it in
1059          * sync with any error messages if the output is redirected to a file.
1060          */
1061         printf ("%s\n", SVal);
1062         fflush (stdout);
1063         NextTok ();
1064     }
1065 }
1066
1067
1068
1069 static void DoP02 (void)
1070 /* Switch to 6502 CPU */
1071 {
1072     SetCPU (CPU_6502);
1073 }
1074
1075
1076
1077 static void DoPC02 (void)
1078 /* Switch to 65C02 CPU */
1079 {
1080     SetCPU (CPU_65C02);
1081 }
1082
1083
1084
1085 static void DoP816 (void)
1086 /* Switch to 65816 CPU */
1087 {
1088     SetCPU (CPU_65816);
1089 }
1090
1091
1092
1093 static void DoPageLength (void)
1094 /* Set the page length for the listing */
1095 {
1096     PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
1097 }
1098
1099
1100
1101 static void DoProc (void)
1102 /* Start a new lexical scope */
1103 {
1104     if (Tok == TOK_IDENT) {
1105         /* The new scope has a name */
1106         SymDef (SVal, CurrentPC (), IsZPSeg ());
1107         NextTok ();
1108     }
1109     SymEnterLevel ();
1110 }
1111
1112
1113
1114 static void DoReloc (void)
1115 /* Enter relocatable mode */
1116 {
1117     RelocMode = 1;
1118 }
1119
1120
1121
1122 static void DoRepeat (void)
1123 /* Repeat some instruction block */
1124 {
1125     ParseRepeat ();
1126 }
1127
1128
1129
1130 static void DoRes (void)
1131 /* Reserve some number of storage bytes */
1132 {
1133     long Count;
1134     long Val;
1135
1136     Count = ConstExpression ();
1137     if (Count > 0xFFFF || Count < 0) {
1138         ErrorSkip (ERR_RANGE);
1139         return;
1140     }
1141     if (Tok == TOK_COMMA) {
1142         NextTok ();
1143         Val = ConstExpression ();
1144         /* We need a byte value here */
1145         if (!IsByteRange (Val)) {
1146             ErrorSkip (ERR_RANGE);
1147             return;
1148         }
1149
1150         /* Emit constant values */
1151         while (Count--) {
1152             Emit0 ((unsigned char) Val);
1153         }
1154
1155     } else {
1156         /* Emit fill fragments */
1157         EmitFill (Count);
1158     }
1159 }
1160
1161
1162
1163 static void DoROData (void)
1164 /* Switch to the r/o data segment */
1165 {
1166     UseRODataSeg ();
1167 }
1168
1169
1170
1171 static void DoSegment (void)
1172 /* Switch to another segment */
1173 {
1174     static const char* AttrTab [] = {
1175         "ZEROPAGE", "DIRECT",
1176         "ABSOLUTE",
1177         "FAR", "LONG"
1178     };
1179     char Name [sizeof (SVal)];
1180     int SegType;
1181
1182     if (Tok != TOK_STRCON) {
1183         ErrorSkip (ERR_STRCON_EXPECTED);
1184     } else {
1185
1186         /* Save the name of the segment and skip it */
1187         strcpy (Name, SVal);
1188         NextTok ();
1189
1190         /* Check for an optional segment attribute */
1191         SegType = SEGTYPE_DEFAULT;
1192         if (Tok == TOK_COMMA) {
1193             NextTok ();
1194             if (Tok != TOK_IDENT) {
1195                 ErrorSkip (ERR_IDENT_EXPECTED);
1196             } else {
1197                 int Attr = GetSubKey (AttrTab, sizeof (AttrTab) / sizeof (AttrTab [0]));
1198                 switch (Attr) {
1199
1200                     case 0:
1201                     case 1:
1202                         /* Zeropage */
1203                         SegType = SEGTYPE_ZP;
1204                         break;
1205
1206                     case 2:
1207                         /* Absolute */
1208                         SegType = SEGTYPE_ABS;
1209                         break;
1210
1211                     case 3:
1212                     case 4:
1213                         /* Far */
1214                         SegType = SEGTYPE_FAR;
1215                         break;
1216
1217                     default:
1218                         Error (ERR_ILLEGAL_SEG_ATTR);
1219                 }
1220                 NextTok ();
1221             }
1222         }
1223
1224         /* Set the segment */
1225         UseSeg (Name, SegType);
1226     }
1227 }
1228
1229
1230
1231 static void DoSmart (void)
1232 /* Smart mode on/off */
1233 {
1234     SetBoolOption (&SmartMode);
1235 }
1236
1237
1238
1239 static void DoSunPlus (void)
1240 /* Switch to the SUNPLUS CPU */
1241 {
1242     SetCPU (CPU_SUNPLUS);
1243 }
1244
1245
1246
1247 static void DoUnexpected (void)
1248 /* Got an unexpected keyword */
1249 {
1250     Error (ERR_UNEXPECTED, Keyword);
1251     SkipUntilSep ();
1252 }
1253
1254
1255
1256 static void DoWarning (void)
1257 /* User warning */
1258 {
1259     if (Tok != TOK_STRCON) {
1260         ErrorSkip (ERR_STRCON_EXPECTED);
1261     } else {
1262         Warning (WARN_USER, SVal);
1263         SkipUntilSep ();
1264     }
1265 }
1266
1267
1268
1269 static void DoWord (void)
1270 /* Define words */
1271 {
1272     while (1) {
1273         EmitWord (Expression ());
1274         if (Tok != TOK_COMMA) {
1275             break;
1276         } else {
1277             NextTok ();
1278         }
1279     }
1280 }
1281
1282
1283
1284 static void DoZeropage (void)
1285 /* Switch to the zeropage segment */
1286 {
1287     UseZeropageSeg ();
1288 }
1289
1290
1291
1292 /*****************************************************************************/
1293 /*                                Table data                                 */
1294 /*****************************************************************************/
1295
1296
1297
1298 /* Control commands flags */
1299 enum {
1300     ccNone      = 0x0000,               /* No special flags */
1301     ccKeepToken = 0x0001                /* Do not skip the current token */
1302 };
1303
1304 /* Control command table */
1305 struct CtrlDesc_ {
1306     unsigned    Flags;                  /* Flags for this directive */
1307     void        (*Handler) (void);      /* Command handler */
1308 };
1309 typedef struct CtrlDesc_ CtrlDesc;
1310
1311 #define PSEUDO_COUNT    (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1312 static CtrlDesc CtrlCmdTab [] = {
1313     { ccNone,           DoA16           },
1314     { ccNone,           DoA8            },
1315     { ccNone,           DoAddr          },      /* .ADDR */
1316     { ccNone,           DoAlign         },
1317     { ccNone,           DoASCIIZ        },
1318     { ccNone,           DoAutoImport    },
1319     { ccNone,           DoUnexpected    },      /* .BLANK */
1320     { ccNone,           DoBss           },
1321     { ccNone,           DoByte          },
1322     { ccNone,           DoCase          },
1323     { ccNone,           DoCode          },
1324     { ccNone,           DoUnexpected,   },      /* .CONCAT */
1325     { ccNone,           DoConDes        },
1326     { ccNone,           DoUnexpected    },      /* .CONST */
1327     { ccNone,           DoConstructor   },
1328     { ccNone,           DoUnexpected    },      /* .CPU */
1329     { ccNone,           DoData          },
1330     { ccNone,           DoDbg,          },
1331     { ccNone,           DoDByt          },
1332     { ccNone,           DoDebugInfo     },
1333     { ccNone,           DoDefine        },
1334     { ccNone,           DoUnexpected    },      /* .DEFINED */
1335     { ccNone,           DoDestructor    },
1336     { ccNone,           DoDWord         },
1337     { ccKeepToken,      DoConditionals  },      /* .ELSE */
1338     { ccKeepToken,      DoConditionals  },      /* .ELSEIF */
1339     { ccNone,           DoEnd           },
1340     { ccKeepToken,      DoConditionals  },      /* .ENDIF */
1341     { ccNone,           DoUnexpected    },      /* .ENDMACRO */
1342     { ccNone,           DoEndProc       },
1343     { ccNone,           DoUnexpected    },      /* .ENDREPEAT */
1344     { ccNone,           DoError         },
1345     { ccNone,           DoExitMacro     },
1346     { ccNone,           DoExport        },
1347     { ccNone,           DoExportZP      },
1348     { ccNone,           DoFarAddr       },
1349     { ccNone,           DoFeature       },
1350     { ccNone,           DoFileOpt       },
1351     { ccNone,           DoUnexpected    },      /* .FORCEWORD */
1352     { ccNone,           DoGlobal        },
1353     { ccNone,           DoGlobalZP      },
1354     { ccNone,           DoI16           },
1355     { ccNone,           DoI8            },
1356     { ccKeepToken,      DoConditionals  },      /* .IF */
1357     { ccKeepToken,      DoConditionals  },      /* .IFBLANK */
1358     { ccKeepToken,      DoConditionals  },      /* .IFCONST */
1359     { ccKeepToken,      DoConditionals  },      /* .IFDEF */
1360     { ccKeepToken,      DoConditionals  },      /* .IFNBLANK */
1361     { ccKeepToken,      DoConditionals  },      /* .IFNCONST */
1362     { ccKeepToken,      DoConditionals  },      /* .IFNDEF */
1363     { ccKeepToken,      DoConditionals  },      /* .IFNREF */
1364     { ccKeepToken,      DoConditionals  },      /* .IFP02 */
1365     { ccKeepToken,      DoConditionals  },      /* .IFP816 */
1366     { ccKeepToken,      DoConditionals  },      /* .IFPC02 */
1367     { ccKeepToken,      DoConditionals  },      /* .IFREF */
1368     { ccNone,           DoImport        },
1369     { ccNone,           DoImportZP      },
1370     { ccNone,           DoIncBin        },
1371     { ccNone,           DoInclude       },
1372     { ccNone,           DoInvalid       },      /* .LEFT */
1373     { ccNone,           DoLineCont      },
1374     { ccNone,           DoList          },
1375     { ccNone,           DoListBytes     },
1376     { ccNone,           DoUnexpected    },      /* .LOCAL */
1377     { ccNone,           DoLocalChar     },
1378     { ccNone,           DoMacPack       },
1379     { ccNone,           DoMacro         },
1380     { ccNone,           DoUnexpected    },      /* .MATCH */
1381     { ccNone,           DoInvalid       },      /* .MID */
1382     { ccNone,           DoNull          },
1383     { ccNone,           DoOrg           },
1384     { ccNone,           DoOut           },
1385     { ccNone,           DoP02           },
1386     { ccNone,           DoP816          },
1387     { ccNone,           DoPageLength    },
1388     { ccNone,           DoUnexpected    },      /* .PARAMCOUNT */
1389     { ccNone,           DoPC02          },
1390     { ccNone,           DoProc          },
1391     { ccNone,           DoUnexpected    },      /* .REFERENCED */
1392     { ccNone,           DoReloc         },
1393     { ccNone,           DoRepeat        },
1394     { ccNone,           DoRes           },
1395     { ccNone,           DoInvalid       },      /* .RIGHT */
1396     { ccNone,           DoROData        },
1397     { ccNone,           DoSegment       },
1398     { ccNone,           DoSmart         },
1399     { ccNone,           DoUnexpected    },      /* .STRAT */
1400     { ccNone,           DoUnexpected    },      /* .STRING */
1401     { ccNone,           DoUnexpected    },      /* .STRLEN */
1402     { ccNone,           DoSunPlus       },
1403     { ccNone,           DoUnexpected    },      /* .TCOUNT */
1404     { ccNone,           DoWarning       },
1405     { ccNone,           DoWord          },
1406     { ccNone,           DoUnexpected    },      /* .XMATCH */
1407     { ccNone,           DoZeropage      },
1408 };
1409
1410
1411
1412 /*****************************************************************************/
1413 /*                                   Code                                    */
1414 /*****************************************************************************/
1415
1416
1417
1418 int TokIsPseudo (unsigned Tok)
1419 /* Return true if the given token is a pseudo instruction token */
1420 {
1421     return (Tok >= TOK_FIRSTPSEUDO && Tok <= TOK_LASTPSEUDO);
1422 }
1423
1424
1425
1426 void HandlePseudo (void)
1427 /* Handle a pseudo instruction */
1428 {
1429     CtrlDesc* D;
1430
1431     /* Calculate the index into the table */
1432     unsigned Index = Tok - TOK_FIRSTPSEUDO;
1433
1434     /* Safety check */
1435     if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
1436         Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
1437                   PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
1438     }
1439     CHECK (Index < PSEUDO_COUNT);
1440
1441     /* Get the pseudo intruction descriptor */
1442     D = &CtrlCmdTab [Index];
1443
1444     /* Remember the instruction, then skip it if needed */
1445     if ((D->Flags & ccKeepToken) == 0) {
1446         strcpy (Keyword+1, SVal);
1447         NextTok ();
1448     }
1449
1450     /* Call the handler */
1451     D->Handler ();
1452 }
1453
1454
1455