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