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