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