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