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