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