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