]> git.sur5r.net Git - cc65/blob - src/ca65/pseudo.c
New macro package
[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         "CPU"
1113     };
1114
1115     int Package;
1116
1117     /* We expect an identifier */
1118     if (Tok != TOK_IDENT) {
1119         ErrorSkip (ERR_IDENT_EXPECTED);
1120         return;
1121     }
1122
1123     /* Map the keyword to a number */
1124     Package = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
1125     if (Package < 0) {
1126         /* Not found */
1127         ErrorSkip (ERR_ILLEGAL_MACPACK);
1128         return;
1129     }
1130
1131     /* Skip the package name */
1132     NextTok ();
1133
1134     /* Insert the package */
1135     InsertMacPack (Package);
1136 }
1137
1138
1139
1140 static void DoMacro (void)
1141 /* Start a macro definition */
1142 {
1143     MacDef (MAC_STYLE_CLASSIC);
1144 }
1145
1146
1147
1148 static void DoNull (void)
1149 /* Switch to the NULL segment */
1150 {
1151     UseSeg (&NullSegDef);
1152 }
1153
1154
1155
1156 static void DoOrg (void)
1157 /* Start absolute code */
1158 {
1159     long PC = ConstExpression ();
1160     if (PC < 0 || PC > 0xFFFFFF) {
1161         Error (ERR_RANGE);
1162         return;
1163     }
1164     SetAbsPC (PC);
1165 }
1166
1167
1168
1169 static void DoOut (void)
1170 /* Output a string */
1171 {
1172     if (Tok != TOK_STRCON) {
1173         ErrorSkip (ERR_STRCON_EXPECTED);
1174     } else {
1175         /* Output the string and be sure to flush the output to keep it in
1176          * sync with any error messages if the output is redirected to a file.
1177          */
1178         printf ("%s\n", SVal);
1179         fflush (stdout);
1180         NextTok ();
1181     }
1182 }
1183
1184
1185
1186 static void DoP02 (void)
1187 /* Switch to 6502 CPU */
1188 {
1189     SetCPU (CPU_6502);
1190 }
1191
1192
1193
1194 static void DoPC02 (void)
1195 /* Switch to 65C02 CPU */
1196 {
1197     SetCPU (CPU_65C02);
1198 }
1199
1200
1201
1202 static void DoP816 (void)
1203 /* Switch to 65816 CPU */
1204 {
1205     SetCPU (CPU_65816);
1206 }
1207
1208
1209
1210 static void DoPageLength (void)
1211 /* Set the page length for the listing */
1212 {
1213     PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
1214 }
1215
1216
1217
1218 static void DoPopSeg (void)
1219 /* Pop an old segment from the segment stack */
1220 {
1221     SegDef* Def;
1222
1223     /* Must have a segment on the stack */
1224     if (CollCount (&SegStack) == 0) {
1225         ErrorSkip (ERR_SEGSTACK_EMPTY);
1226         return;
1227     }
1228
1229     /* Pop the last element */
1230     Def = CollPop (&SegStack);
1231
1232     /* Restore this segment */
1233     UseSeg (Def);
1234
1235     /* Delete the segment definition */
1236     FreeSegDef (Def);
1237 }
1238
1239
1240
1241 static void DoProc (void)
1242 /* Start a new lexical scope */
1243 {
1244     if (Tok == TOK_IDENT) {
1245         /* The new scope has a name */
1246         SymDef (SVal, GenCurrentPC (), IsZPSeg (), 1);
1247         NextTok ();
1248     }
1249     SymEnterLevel ();
1250 }
1251
1252
1253
1254 static void DoPSC02 (void)
1255 /* Switch to 65SC02 CPU */
1256 {
1257     SetCPU (CPU_65SC02);
1258 }
1259
1260
1261
1262 static void DoPushSeg (void)
1263 /* Push the current segment onto the segment stack */
1264 {
1265     /* Can only push a limited size of segments */
1266     if (CollCount (&SegStack) >= MAX_PUSHED_SEGMENTS) {
1267         ErrorSkip (ERR_SEGSTACK_OVERFLOW);
1268         return;
1269     }
1270
1271     /* Get the current segment and push it */
1272     CollAppend (&SegStack, DupSegDef (GetCurrentSeg ()));
1273 }
1274
1275
1276
1277 static void DoReloc (void)
1278 /* Enter relocatable mode */
1279 {
1280     RelocMode = 1;
1281 }
1282
1283
1284
1285 static void DoRepeat (void)
1286 /* Repeat some instruction block */
1287 {
1288     ParseRepeat ();
1289 }
1290
1291
1292
1293 static void DoRes (void)
1294 /* Reserve some number of storage bytes */
1295 {
1296     long Count;
1297     long Val;
1298
1299     Count = ConstExpression ();
1300     if (Count > 0xFFFF || Count < 0) {
1301         ErrorSkip (ERR_RANGE);
1302         return;
1303     }
1304     if (Tok == TOK_COMMA) {
1305         NextTok ();
1306         Val = ConstExpression ();
1307         /* We need a byte value here */
1308         if (!IsByteRange (Val)) {
1309             ErrorSkip (ERR_RANGE);
1310             return;
1311         }
1312
1313         /* Emit constant values */
1314         while (Count--) {
1315             Emit0 ((unsigned char) Val);
1316         }
1317
1318     } else {
1319         /* Emit fill fragments */
1320         EmitFill (Count);
1321     }
1322 }
1323
1324
1325
1326 static void DoROData (void)
1327 /* Switch to the r/o data segment */
1328 {
1329     UseSeg (&RODataSegDef);
1330 }
1331
1332
1333
1334 static void DoSegment (void)
1335 /* Switch to another segment */
1336 {
1337     static const char* AttrTab [] = {
1338         "ZEROPAGE", "DIRECT",
1339         "ABSOLUTE",
1340         "FAR", "LONG"
1341     };
1342     char Name [sizeof (SVal)];
1343     SegDef Def;
1344     Def.Name = Name;
1345     Def.Type = SEGTYPE_DEFAULT;
1346
1347     if (Tok != TOK_STRCON) {
1348         ErrorSkip (ERR_STRCON_EXPECTED);
1349     } else {
1350
1351         /* Save the name of the segment and skip it */
1352         strcpy (Name, SVal);
1353         NextTok ();
1354
1355         /* Check for an optional segment attribute */
1356         if (Tok == TOK_COMMA) {
1357             NextTok ();
1358             if (Tok != TOK_IDENT) {
1359                 ErrorSkip (ERR_IDENT_EXPECTED);
1360             } else {
1361                 int Attr = GetSubKey (AttrTab, sizeof (AttrTab) / sizeof (AttrTab [0]));
1362                 switch (Attr) {
1363
1364                     case 0:
1365                     case 1:
1366                         /* Zeropage */
1367                         Def.Type = SEGTYPE_ZP;
1368                         break;
1369
1370                     case 2:
1371                         /* Absolute */
1372                         Def.Type = SEGTYPE_ABS;
1373                         break;
1374
1375                     case 3:
1376                     case 4:
1377                         /* Far */
1378                         Def.Type = SEGTYPE_FAR;
1379                         break;
1380
1381                     default:
1382                         Error (ERR_ILLEGAL_SEG_ATTR);
1383                 }
1384                 NextTok ();
1385             }
1386         }
1387
1388         /* Set the segment */
1389         UseSeg (&Def);
1390     }
1391 }
1392
1393
1394
1395 static void DoSetCPU (void)
1396 /* Switch the CPU instruction set */
1397 {
1398     /* We expect an identifier */
1399     if (Tok != TOK_STRCON) {
1400         ErrorSkip (ERR_STRCON_EXPECTED);
1401     } else {
1402         /* Try to find the CPU, then skip the identifier */
1403         cpu_t CPU = FindCPU (SVal);
1404         NextTok ();
1405
1406         /* Switch to the new CPU */
1407         SetCPU (CPU);
1408     }
1409 }
1410
1411
1412
1413 static void DoSmart (void)
1414 /* Smart mode on/off */
1415 {
1416     SetBoolOption (&SmartMode);
1417 }
1418
1419
1420
1421 static void DoSunPlus (void)
1422 /* Switch to the SUNPLUS CPU */
1423 {
1424     SetCPU (CPU_SUNPLUS);
1425 }
1426
1427
1428
1429 static void DoUnexpected (void)
1430 /* Got an unexpected keyword */
1431 {
1432     Error (ERR_UNEXPECTED, Keyword);
1433     SkipUntilSep ();
1434 }
1435
1436
1437
1438 static void DoWarning (void)
1439 /* User warning */
1440 {
1441     if (Tok != TOK_STRCON) {
1442         ErrorSkip (ERR_STRCON_EXPECTED);
1443     } else {
1444         Warning (WARN_USER, SVal);
1445         SkipUntilSep ();
1446     }
1447 }
1448
1449
1450
1451 static void DoWord (void)
1452 /* Define words */
1453 {
1454     while (1) {
1455         EmitWord (Expression ());
1456         if (Tok != TOK_COMMA) {
1457             break;
1458         } else {
1459             NextTok ();
1460         }
1461     }
1462 }
1463
1464
1465
1466 static void DoZeropage (void)
1467 /* Switch to the zeropage segment */
1468 {
1469     UseSeg (&ZeropageSegDef);
1470 }
1471
1472
1473
1474 /*****************************************************************************/
1475 /*                                Table data                                 */
1476 /*****************************************************************************/
1477
1478
1479
1480 /* Control commands flags */
1481 enum {
1482     ccNone      = 0x0000,               /* No special flags */
1483     ccKeepToken = 0x0001                /* Do not skip the current token */
1484 };
1485
1486 /* Control command table */
1487 typedef struct CtrlDesc CtrlDesc;
1488 struct CtrlDesc {
1489     unsigned    Flags;                  /* Flags for this directive */
1490     void        (*Handler) (void);      /* Command handler */
1491 };
1492
1493 #define PSEUDO_COUNT    (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1494 static CtrlDesc CtrlCmdTab [] = {
1495     { ccNone,           DoA16           },
1496     { ccNone,           DoA8            },
1497     { ccNone,           DoAddr          },      /* .ADDR */
1498     { ccNone,           DoAlign         },
1499     { ccNone,           DoASCIIZ        },
1500     { ccNone,           DoAssert        },
1501     { ccNone,           DoAutoImport    },
1502     { ccNone,           DoUnexpected    },      /* .BLANK */
1503     { ccNone,           DoBss           },
1504     { ccNone,           DoByte          },
1505     { ccNone,           DoCase          },
1506     { ccNone,           DoCharMap       },
1507     { ccNone,           DoCode          },
1508     { ccNone,           DoUnexpected,   },      /* .CONCAT */
1509     { ccNone,           DoConDes        },
1510     { ccNone,           DoUnexpected    },      /* .CONST */
1511     { ccNone,           DoConstructor   },
1512     { ccNone,           DoUnexpected    },      /* .CPU */
1513     { ccNone,           DoData          },
1514     { ccNone,           DoDbg,          },
1515     { ccNone,           DoDByt          },
1516     { ccNone,           DoDebugInfo     },
1517     { ccNone,           DoDefine        },
1518     { ccNone,           DoUnexpected    },      /* .DEFINED */
1519     { ccNone,           DoDestructor    },
1520     { ccNone,           DoDWord         },
1521     { ccKeepToken,      DoConditionals  },      /* .ELSE */
1522     { ccKeepToken,      DoConditionals  },      /* .ELSEIF */
1523     { ccKeepToken,      DoEnd           },
1524     { ccKeepToken,      DoConditionals  },      /* .ENDIF */
1525     { ccNone,           DoUnexpected    },      /* .ENDMACRO */
1526     { ccNone,           DoEndProc       },
1527     { ccNone,           DoUnexpected    },      /* .ENDREPEAT */
1528     { ccNone,           DoError         },
1529     { ccNone,           DoExitMacro     },
1530     { ccNone,           DoExport        },
1531     { ccNone,           DoExportZP      },
1532     { ccNone,           DoFarAddr       },
1533     { ccNone,           DoFeature       },
1534     { ccNone,           DoFileOpt       },
1535     { ccNone,           DoForceImport   },
1536     { ccNone,           DoUnexpected    },      /* .FORCEWORD */
1537     { ccNone,           DoGlobal        },
1538     { ccNone,           DoGlobalZP      },
1539     { ccNone,           DoI16           },
1540     { ccNone,           DoI8            },
1541     { ccKeepToken,      DoConditionals  },      /* .IF */
1542     { ccKeepToken,      DoConditionals  },      /* .IFBLANK */
1543     { ccKeepToken,      DoConditionals  },      /* .IFCONST */
1544     { ccKeepToken,      DoConditionals  },      /* .IFDEF */
1545     { ccKeepToken,      DoConditionals  },      /* .IFNBLANK */
1546     { ccKeepToken,      DoConditionals  },      /* .IFNCONST */
1547     { ccKeepToken,      DoConditionals  },      /* .IFNDEF */
1548     { ccKeepToken,      DoConditionals  },      /* .IFNREF */
1549     { ccKeepToken,      DoConditionals  },      /* .IFP02 */
1550     { ccKeepToken,      DoConditionals  },      /* .IFP816 */
1551     { ccKeepToken,      DoConditionals  },      /* .IFPC02 */
1552     { ccKeepToken,      DoConditionals  },      /* .IFPSC02 */
1553     { ccKeepToken,      DoConditionals  },      /* .IFREF */
1554     { ccNone,           DoImport        },
1555     { ccNone,           DoImportZP      },
1556     { ccNone,           DoIncBin        },
1557     { ccNone,           DoInclude       },
1558     { ccNone,           DoInvalid       },      /* .LEFT */
1559     { ccNone,           DoLineCont      },
1560     { ccNone,           DoList          },
1561     { ccNone,           DoListBytes     },
1562     { ccNone,           DoUnexpected    },      /* .LOCAL */
1563     { ccNone,           DoLocalChar     },
1564     { ccNone,           DoMacPack       },
1565     { ccNone,           DoMacro         },
1566     { ccNone,           DoUnexpected    },      /* .MATCH */
1567     { ccNone,           DoInvalid       },      /* .MID */
1568     { ccNone,           DoNull          },
1569     { ccNone,           DoOrg           },
1570     { ccNone,           DoOut           },
1571     { ccNone,           DoP02           },
1572     { ccNone,           DoP816          },
1573     { ccNone,           DoPageLength    },
1574     { ccNone,           DoUnexpected    },      /* .PARAMCOUNT */
1575     { ccNone,           DoPC02          },
1576     { ccNone,           DoPopSeg        },
1577     { ccNone,           DoProc          },
1578     { ccNone,           DoPSC02         },
1579     { ccNone,           DoPushSeg       },
1580     { ccNone,           DoUnexpected    },      /* .REFERENCED */
1581     { ccNone,           DoReloc         },
1582     { ccNone,           DoRepeat        },
1583     { ccNone,           DoRes           },
1584     { ccNone,           DoInvalid       },      /* .RIGHT */
1585     { ccNone,           DoROData        },
1586     { ccNone,           DoSegment       },
1587     { ccNone,           DoSetCPU        },
1588     { ccNone,           DoSmart         },
1589     { ccNone,           DoUnexpected    },      /* .STRAT */
1590     { ccNone,           DoUnexpected    },      /* .STRING */
1591     { ccNone,           DoUnexpected    },      /* .STRLEN */
1592     { ccNone,           DoSunPlus       },
1593     { ccNone,           DoUnexpected    },      /* .TCOUNT */
1594     { ccNone,           DoUnexpected    },      /* .TIME */
1595     { ccNone,           DoUnexpected    },      /* .VERSION */
1596     { ccNone,           DoWarning       },
1597     { ccNone,           DoWord          },
1598     { ccNone,           DoUnexpected    },      /* .XMATCH */
1599     { ccNone,           DoZeropage      },
1600 };
1601
1602
1603
1604 /*****************************************************************************/
1605 /*                                   Code                                    */
1606 /*****************************************************************************/
1607
1608
1609
1610 int TokIsPseudo (unsigned Tok)
1611 /* Return true if the given token is a pseudo instruction token */
1612 {
1613     return (Tok >= TOK_FIRSTPSEUDO && Tok <= TOK_LASTPSEUDO);
1614 }
1615
1616
1617
1618 void HandlePseudo (void)
1619 /* Handle a pseudo instruction */
1620 {
1621     CtrlDesc* D;
1622
1623     /* Calculate the index into the table */
1624     unsigned Index = Tok - TOK_FIRSTPSEUDO;
1625
1626     /* Safety check */
1627     if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
1628         Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
1629                   PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
1630     }
1631     CHECK (Index < PSEUDO_COUNT);
1632
1633     /* Get the pseudo intruction descriptor */
1634     D = &CtrlCmdTab [Index];
1635
1636     /* Remember the instruction, then skip it if needed */
1637     if ((D->Flags & ccKeepToken) == 0) {
1638         strcpy (Keyword+1, SVal);
1639         NextTok ();
1640     }
1641
1642     /* Call the handler */
1643     D->Handler ();
1644 }
1645
1646
1647
1648 void SegStackCheck (void)
1649 /* Check if the segment stack is empty at end of assembly */
1650 {
1651     if (CollCount (&SegStack) != 0) {
1652         Error (ERR_SEGSTACK_NOT_EMPTY);
1653     }
1654 }
1655
1656
1657