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