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