1/*****************************************************************************/
2/* */
3/* pseudo.c */
4/* */
5/* Pseudo instructions for the ca65 macroassembler */
6/* */
7/* */
8/* */
9/* (C) 1998-2012, Ullrich von Bassewitz */
10/* Roemerstrasse 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 "alignment.h"
44#include "assertion.h"
45#include "bitops.h"
46#include "cddefs.h"
47#include "coll.h"
48#include "filestat.h"
49#include "gentype.h"
50#include "intstack.h"
51#include "scopedefs.h"
52#include "symdefs.h"
53#include "tgttrans.h"
54#include "xmalloc.h"
55
56/* ca65 */
57#include "anonname.h"
58#include "asserts.h"
59#include "condasm.h"
60#include "dbginfo.h"
61#include "enum.h"
62#include "error.h"
63#include "expr.h"
64#include "feature.h"
65#include "filetab.h"
66#include "global.h"
67#include "incpath.h"
68#include "instr.h"
69#include "listing.h"
70#include "macro.h"
71#include "nexttok.h"
72#include "objcode.h"
73#include "options.h"
74#include "pseudo.h"
75#include "repeat.h"
76#include "segment.h"
77#include "sizeof.h"
78#include "span.h"
79#include "spool.h"
80#include "struct.h"
81#include "symbol.h"
82#include "symtab.h"
83
84
85
86/*****************************************************************************/
87/* Data */
88/*****************************************************************************/
89
90
91
92/* Keyword we're about to handle */
93static StrBuf Keyword = STATIC_STRBUF_INITIALIZER;
94
95/* CPU stack */
96static IntStack CPUStack = STATIC_INTSTACK_INITIALIZER;
97
98/* Segment stack */
99#define MAX_PUSHED_SEGMENTS 16
100static Collection SegStack = STATIC_COLLECTION_INITIALIZER;
101
102
103
104/*****************************************************************************/
105/* Forwards */
106/*****************************************************************************/
107
108
109
110static void DoUnexpected (void);
111/* Got an unexpected keyword */
112
113static void DoInvalid (void);
114/* Handle a token that is invalid here, since it should have been handled on
115** a much lower level of the expression hierarchy. Getting this sort of token
116** means that the lower level code has bugs.
117** This function differs to DoUnexpected in that the latter may be triggered
118** by the user by using keywords in the wrong location. DoUnexpected is not
119** an error in the assembler itself, while DoInvalid is.
120*/
121
122
123
124/*****************************************************************************/
125/* Helper functions */
126/*****************************************************************************/
127
128
129
130static unsigned char OptionalAddrSize (void)
131/* If a colon follows, parse an optional address size spec and return it.
132** Otherwise return ADDR_SIZE_DEFAULT.
133*/
134{
135 unsigned AddrSize = ADDR_SIZE_DEFAULT;
136 if (CurTok.Tok == TOK_COLON) {
137 NextTok ();
138 AddrSize = ParseAddrSize ();
139 if (!ValidAddrSizeForCPU (AddrSize)) {
140 /* Print an error and reset to default */
141 Error ("Invalid address size specification for current CPU");
142 AddrSize = ADDR_SIZE_DEFAULT;
143 }
144 NextTok ();
145 }
146 return AddrSize;
147}
148
149
150
151static void SetBoolOption (unsigned char* Flag)
152/* Read a on/off/+/- option and set flag accordingly */
153{
154 static const char* const Keys[] = {
155 "OFF",
156 "ON",
157 };
158
159 if (CurTok.Tok == TOK_PLUS) {
160 *Flag = 1;
161 NextTok ();
162 } else if (CurTok.Tok == TOK_MINUS) {
163 *Flag = 0;
164 NextTok ();
165 } else if (CurTok.Tok == TOK_IDENT) {
166 /* Map the keyword to a number */
167 switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
168 case 0: *Flag = 0; NextTok (); break;
169 case 1: *Flag = 1; NextTok (); break;
170 default: ErrorSkip ("'on' or 'off' expected"); break;
171 }
172 } else if (TokIsSep (CurTok.Tok)) {
173 /* Without anything assume switch on */
174 *Flag = 1;
175 } else {
176 ErrorSkip ("'on' or 'off' expected");
177 }
178}
179
180
181
182static void ExportWithAssign (SymEntry* Sym, unsigned char AddrSize, unsigned Flags)
183/* Allow to assign the value of an export in an .export statement */
184{
185 /* The name and optional address size spec may be followed by an assignment
186 ** or equal token.
187 */
188 if (CurTok.Tok == TOK_ASSIGN || CurTok.Tok == TOK_EQ) {
189
190 /* Assignment means the symbol is a label */
191 if (CurTok.Tok == TOK_ASSIGN) {
192 Flags |= SF_LABEL;
193 }
194
195 /* Skip the assignment token */
196 NextTok ();
197
198 /* Define the symbol with the expression following the '=' */
199 SymDef (Sym, Expression(), ADDR_SIZE_DEFAULT, Flags);
200
201 }
202
203 /* Now export the symbol */
204 SymExport (Sym, AddrSize, Flags);
205}
206
207
208
209static void ExportImport (void (*Func) (SymEntry*, unsigned char, unsigned),
210 unsigned char DefAddrSize, unsigned Flags)
211/* Export or import symbols */
212{
213 SymEntry* Sym;
214 unsigned char AddrSize;
215
216 while (1) {
217
218 /* We need an identifier here */
219 if (CurTok.Tok != TOK_IDENT) {
220 ErrorSkip ("Identifier expected");
221 return;
222 }
223
224 /* Find the symbol table entry, allocate a new one if necessary */
225 Sym = SymFind (CurrentScope, &CurTok.SVal, SYM_ALLOC_NEW);
226
227 /* Skip the name */
228 NextTok ();
229
230 /* Get an optional address size */
231 AddrSize = OptionalAddrSize ();
232 if (AddrSize == ADDR_SIZE_DEFAULT) {
233 AddrSize = DefAddrSize;
234 }
235
236 /* Call the actual import/export function */
237 Func (Sym, AddrSize, Flags);
238
239 /* More symbols? */
240 if (CurTok.Tok == TOK_COMMA) {
241 NextTok ();
242 } else {
243 break;
244 }
245 }
246}
247
248
249
250static long IntArg (long Min, long Max)
251/* Read an integer argument and check a range. Accept the token "unlimited"
252** and return -1 in this case.
253*/
254{
255 if (CurTok.Tok == TOK_IDENT && SB_CompareStr (&CurTok.SVal, "unlimited") == 0) {
256 NextTok ();
257 return -1;
258 } else {
259 long Val = ConstExpression ();
260 if (Val < Min || Val > Max) {
261 Error ("Range error");
262 Val = Min;
263 }
264 return Val;
265 }
266}
267
268
269
270static void ConDes (const StrBuf* Name, unsigned Type)
271/* Parse remaining line for constructor/destructor of the remaining type */
272{
273 long Prio;
274
275
276 /* Find the symbol table entry, allocate a new one if necessary */
277 SymEntry* Sym = SymFind (CurrentScope, Name, SYM_ALLOC_NEW);
278
279 /* Optional constructor priority */
280 if (CurTok.Tok == TOK_COMMA) {
281 /* Priority value follows */
282 NextTok ();
283 Prio = ConstExpression ();
284 if (Prio < CD_PRIO_MIN || Prio > CD_PRIO_MAX) {
285 /* Value out of range */
286 Error ("Range error");
287 return;
288 }
289 } else {
290 /* Use the default priority value */
291 Prio = CD_PRIO_DEF;
292 }
293
294 /* Define the symbol */
295 SymConDes (Sym, ADDR_SIZE_DEFAULT, Type, (unsigned) Prio);
296}
297
298
299
300static StrBuf* GenArrayType (StrBuf* Type, unsigned SpanSize,
301 const char* ElementType,
302 unsigned ElementTypeLen)
303/* Create an array (or single data) of the given type. SpanSize is the size
304** of the span, ElementType is a string that encodes the element data type.
305** The function returns Type.
306*/
307{
308 /* Get the size of the element type */
309 unsigned ElementSize = GT_GET_SIZE (ElementType[0]);
310
311 /* Get the number of array elements */
312 unsigned ElementCount = SpanSize / ElementSize;
313
314 /* The span size must be divideable by the element size */
315 CHECK ((SpanSize % ElementSize) == 0);
316
317 /* Encode the array */
318 GT_AddArray (Type, ElementCount);
319 SB_AppendBuf (Type, ElementType, ElementTypeLen);
320
321 /* Return the pointer to the created array type */
322 return Type;
323}
324
325
326
327/*****************************************************************************/
328/* Handler functions */
329/*****************************************************************************/
330
331
332
333static void DoA16 (void)
334/* Switch the accu to 16 bit mode (assembler only) */
335{
336 if (GetCPU() != CPU_65816) {
337 Error ("Command is only valid in 65816 mode");
338 } else {
339 /* Immidiate mode has two extension bytes */
340 ExtBytes [AM65I_IMM_ACCU] = 2;
341 }
342}
343
344
345
346static void DoA8 (void)
347/* Switch the accu to 8 bit mode (assembler only) */
348{
349 if (GetCPU() != CPU_65816) {
350 Error ("Command is only valid in 65816 mode");
351 } else {
352 /* Immidiate mode has one extension byte */
353 ExtBytes [AM65I_IMM_ACCU] = 1;
354 }
355}
356
357
358
359static void DoAddr (void)
360/* Define addresses */
361{
362 /* Element type for the generated array */
363 static const char EType[2] = { GT_PTR, GT_VOID };
364
365 /* Record type information */
366 Span* S = OpenSpan ();
367 StrBuf Type = STATIC_STRBUF_INITIALIZER;
368
369 /* Parse arguments */
370 while (1) {
371 ExprNode* Expr = Expression ();
372 if (GetCPU () == CPU_65816 || ForceRange) {
373 /* Do a range check */
374 Expr = GenWordExpr (Expr);
375 }
376 EmitWord (Expr);
377 if (CurTok.Tok != TOK_COMMA) {
378 break;
379 } else {
380 NextTok ();
381 }
382 }
383
384 /* Close the span, then add type information to it */
385 S = CloseSpan (S);
386 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
387
388 /* Free the strings */
389 SB_Done (&Type);
390}
391
392
393
394static void DoAlign (void)
395/* Align the PC to some boundary */
396{
397 long FillVal;
398 long Alignment;
399
400 /* Read the alignment value */
401 Alignment = ConstExpression ();
402 if (Alignment <= 0 || (unsigned long) Alignment > MAX_ALIGNMENT) {
403 ErrorSkip ("Range error");
404 return;
405 }
406
407 /* Optional value follows */
408 if (CurTok.Tok == TOK_COMMA) {
409 NextTok ();
410 FillVal = ConstExpression ();
411 /* We need a byte value here */
412 if (!IsByteRange (FillVal)) {
413 ErrorSkip ("Range error");
414 return;
415 }
416 } else {
417 FillVal = -1;
418 }
419
420 /* Generate the alignment */
421 SegAlign (Alignment, (int) FillVal);
422}
423
424
425
426static void DoASCIIZ (void)
427/* Define text with a zero terminator */
428{
429 while (1) {
430 /* Must have a string constant */
431 if (CurTok.Tok != TOK_STRCON) {
432 ErrorSkip ("String constant expected");
433 return;
434 }
435
436 /* Translate into target charset and emit */
437 TgtTranslateStrBuf (&CurTok.SVal);
438 EmitStrBuf (&CurTok.SVal);
439 NextTok ();
440 if (CurTok.Tok == TOK_COMMA) {
441 NextTok ();
442 } else {
443 break;
444 }
445 }
446 Emit0 (0);
447}
448
449
450
451static void DoAssert (void)
452/* Add an assertion */
453{
454 static const char* const ActionTab [] = {
455 "WARN", "WARNING",
456 "ERROR",
457 "LDWARN", "LDWARNING",
458 "LDERROR",
459 };
460
461 AssertAction Action;
462 unsigned Msg;
463
464 /* First we have the expression that has to evaluated */
465 ExprNode* Expr = Expression ();
466 ConsumeComma ();
467
468 /* Action follows */
469 if (CurTok.Tok != TOK_IDENT) {
470 ErrorSkip ("Identifier expected");
471 return;
472 }
473 switch (GetSubKey (ActionTab, sizeof (ActionTab) / sizeof (ActionTab[0]))) {
474
475 case 0:
476 case 1:
477 /* Warning */
478 Action = ASSERT_ACT_WARN;
479 break;
480
481 case 2:
482 /* Error */
483 Action = ASSERT_ACT_ERROR;
484 break;
485
486 case 3:
487 case 4:
488 /* Linker warning */
489 Action = ASSERT_ACT_LDWARN;
490 break;
491
492 case 5:
493 /* Linker error */
494 Action = ASSERT_ACT_LDERROR;
495 break;
496
497 default:
498 Error ("Illegal assert action specifier");
499 /* Use lderror - there won't be an .o file anyway */
500 Action = ASSERT_ACT_LDERROR;
501 break;
502
503 }
504 NextTok ();
505
506 /* We can have an optional message. If no message is present, use
507 ** "Assertion failed".
508 */
509 if (CurTok.Tok == TOK_COMMA) {
510
511 /* Skip the comma */
512 NextTok ();
513
514 /* Read the message */
515 if (CurTok.Tok != TOK_STRCON) {
516 ErrorSkip ("String constant expected");
517 return;
518 }
519
520 /* Translate the message into a string id. We can then skip the input
521 ** string.
522 */
523 Msg = GetStrBufId (&CurTok.SVal);
524 NextTok ();
525
526 } else {
527
528 /* Use "Assertion failed" */
529 Msg = GetStringId ("Assertion failed");
530
531 }
532
533 /* Remember the assertion */
534 AddAssertion (Expr, (AssertAction) Action, Msg);
535}
536
537
538
539static void DoAutoImport (void)
540/* Mark unresolved symbols as imported */
541{
542 SetBoolOption (&AutoImport);
543}
544
545
546static void DoBankBytes (void)
547/* Define bytes, extracting the bank byte from each expression in the list */
548{
549 while (1) {
550 EmitByte (FuncBankByte ());
551 if (CurTok.Tok != TOK_COMMA) {
552 break;
553 } else {
554 NextTok ();
555 }
556 }
557}
558
559
560
561static void DoBss (void)
562/* Switch to the BSS segment */
563{
564 UseSeg (&BssSegDef);
565}
566
567
568
569static void DoByte (void)
570/* Define bytes */
571{
572 /* Element type for the generated array */
573 static const char EType[1] = { GT_BYTE };
574
575 /* Record type information */
576 Span* S = OpenSpan ();
577 StrBuf Type = AUTO_STRBUF_INITIALIZER;
578
579 /* Parse arguments */
580 while (1) {
581 if (CurTok.Tok == TOK_STRCON) {
582 /* A string, translate into target charset and emit */
583 TgtTranslateStrBuf (&CurTok.SVal);
584 EmitStrBuf (&CurTok.SVal);
585 NextTok ();
586 } else {
587 EmitByte (BoundedExpr (Expression, 1));
588 }
589 if (CurTok.Tok != TOK_COMMA) {
590 break;
591 } else {
592 NextTok ();
593 /* Do smart handling of dangling comma */
594 if (CurTok.Tok == TOK_SEP) {
595 Error ("Unexpected end of line");
596 break;
597 }
598 }
599 }
600
601 /* Close the span, then add type information to it.
602 ** Note: empty string operands emit nothing;
603 ** so, add a type only if there's a span.
604 */
605 S = CloseSpan (S);
606 if (S != 0) {
607 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
608 }
609
610 /* Free the type string */
611 SB_Done (&Type);
612}
613
614
615
616static void DoCase (void)
617/* Switch the IgnoreCase option */
618{
619 SetBoolOption (&IgnoreCase);
620 IgnoreCase = !IgnoreCase;
621}
622
623
624
625static void DoCharMap (void)
626/* Allow custom character mappings */
627{
628 long Index;
629 long Code;
630
631 /* Read the index as numerical value */
632 Index = ConstExpression ();
633 if (Index < 0 || Index > 255) {
634 /* Value out of range */
635 ErrorSkip ("Index range error");
636 return;
637 }
638
639 /* Comma follows */
640 ConsumeComma ();
641
642 /* Read the character code */
643 Code = ConstExpression ();
644 if (Code < 0 || Code > 255) {
645 /* Value out of range */
646 ErrorSkip ("Code range error");
647 return;
648 }
649
650 /* Set the character translation */
651 TgtTranslateSet ((unsigned) Index, (unsigned char) Code);
652}
653
654
655
656static void DoCode (void)
657/* Switch to the code segment */
658{
659 UseSeg (&CodeSegDef);
660}
661
662
663
664static void DoConDes (void)
665/* Export a symbol as constructor/destructor */
666{
667 static const char* const Keys[] = {
668 "CONSTRUCTOR",
669 "DESTRUCTOR",
670 "INTERRUPTOR",
671 };
672 StrBuf Name = STATIC_STRBUF_INITIALIZER;
673 long Type;
674
675 /* Symbol name follows */
676 if (CurTok.Tok != TOK_IDENT) {
677 ErrorSkip ("Identifier expected");
678 return;
679 }
680 SB_Copy (&Name, &CurTok.SVal);
681 NextTok ();
682
683 /* Type follows. May be encoded as identifier or numerical */
684 ConsumeComma ();
685 if (CurTok.Tok == TOK_IDENT) {
686
687 /* Map the following keyword to a number, then skip it */
688 Type = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
689 NextTok ();
690
691 /* Check if we got a valid keyword */
692 if (Type < 0) {
693 ErrorSkip ("Syntax error");
694 goto ExitPoint;
695 }
696
697 } else {
698
699 /* Read the type as numerical value */
700 Type = ConstExpression ();
701 if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) {
702 /* Value out of range */
703 ErrorSkip ("Range error");
704 goto ExitPoint;
705 }
706
707 }
708
709 /* Parse the remainder of the line and export the symbol */
710 ConDes (&Name, (unsigned) Type);
711
712ExitPoint:
713 /* Free string memory */
714 SB_Done (&Name);
715}
716
717
718
719static void DoConstructor (void)
720/* Export a symbol as constructor */
721{
722 StrBuf Name = STATIC_STRBUF_INITIALIZER;
723
724 /* Symbol name follows */
725 if (CurTok.Tok != TOK_IDENT) {
726 ErrorSkip ("Identifier expected");
727 return;
728 }
729 SB_Copy (&Name, &CurTok.SVal);
730 NextTok ();
731
732 /* Parse the remainder of the line and export the symbol */
733 ConDes (&Name, CD_TYPE_CON);
734
735 /* Free string memory */
736 SB_Done (&Name);
737}
738
739
740
741static void DoData (void)
742/* Switch to the data segment */
743{
744 UseSeg (&DataSegDef);
745}
746
747
748
749static void DoDbg (void)
750/* Add debug information from high level code */
751{
752 static const char* const Keys[] = {
753 "FILE",
754 "FUNC",
755 "LINE",
756 "SYM",
757 };
758 int Key;
759
760
761 /* We expect a subkey */
762 if (CurTok.Tok != TOK_IDENT) {
763 ErrorSkip ("Identifier expected");
764 return;
765 }
766
767 /* Map the following keyword to a number */
768 Key = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
769
770 /* Skip the subkey */
771 NextTok ();
772
773 /* Check the key and dispatch to a handler */
774 switch (Key) {
775 case 0: DbgInfoFile (); break;
776 case 1: DbgInfoFunc (); break;
777 case 2: DbgInfoLine (); break;
778 case 3: DbgInfoSym (); break;
779 default: ErrorSkip ("Syntax error"); break;
780 }
781}
782
783
784
785static void DoDByt (void)
786/* Output double bytes */
787{
788 /* Element type for the generated array */
789 static const char EType[1] = { GT_DBYTE };
790
791 /* Record type information */
792 Span* S = OpenSpan ();
793 StrBuf Type = STATIC_STRBUF_INITIALIZER;
794
795 /* Parse arguments */
796 while (1) {
797 EmitWord (GenSwapExpr (BoundedExpr (Expression, 2)));
798 if (CurTok.Tok != TOK_COMMA) {
799 break;
800 } else {
801 NextTok ();
802 }
803 }
804
805 /* Close the span, then add type information to it */
806 S = CloseSpan (S);
807 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
808
809 /* Free the type string */
810 SB_Done (&Type);
811}
812
813
814
815static void DoDebugInfo (void)
816/* Switch debug info on or off */
817{
818 SetBoolOption (&DbgSyms);
819}
820
821
822
823static void DoDefine (void)
824/* Define a one line macro */
825{
826 MacDef (MAC_STYLE_DEFINE);
827}
828
829
830
831static void DoDelMac (void)
832/* Delete a classic macro */
833{
834 /* We expect an identifier */
835 if (CurTok.Tok != TOK_IDENT) {
836 ErrorSkip ("Identifier expected");
837 } else {
838 MacUndef (&CurTok.SVal, MAC_STYLE_CLASSIC);
839 NextTok ();
840 }
841}
842
843
844
845static void DoDestructor (void)
846/* Export a symbol as destructor */
847{
848 StrBuf Name = STATIC_STRBUF_INITIALIZER;
849
850 /* Symbol name follows */
851 if (CurTok.Tok != TOK_IDENT) {
852 ErrorSkip ("Identifier expected");
853 return;
854 }
855 SB_Copy (&Name, &CurTok.SVal);
856 NextTok ();
857
858 /* Parse the remainder of the line and export the symbol */
859 ConDes (&Name, CD_TYPE_DES);
860
861 /* Free string memory */
862 SB_Done (&Name);
863}
864
865
866
867static void DoDWord (void)
868/* Define dwords */
869{
870 while (1) {
871 EmitDWord (BoundedExpr (Expression, 4));
872 if (CurTok.Tok != TOK_COMMA) {
873 break;
874 } else {
875 NextTok ();
876 }
877 }
878}
879
880
881
882static void DoEnd (void)
883/* End of assembly */
884{
885 ForcedEnd = 1;
886 NextTok ();
887}
888
889
890
891static void DoEndProc (void)
892/* Leave a lexical level */
893{
894 if (CurrentScope->Type != SCOPE_SCOPE || CurrentScope->Label == 0) {
895 /* No local scope */
896 ErrorSkip ("No open .PROC");
897 } else {
898 SymLeaveLevel ();
899 }
900}
901
902
903
904static void DoEndScope (void)
905/* Leave a lexical level */
906{
907 if (CurrentScope->Type != SCOPE_SCOPE || CurrentScope->Label != 0) {
908 /* No local scope */
909 ErrorSkip ("No open .SCOPE");
910 } else {
911 SymLeaveLevel ();
912 }
913}
914
915
916
917static void DoError (void)
918/* User error */
919{
920 if (CurTok.Tok != TOK_STRCON) {
921 ErrorSkip ("String constant expected");
922 } else {
923 Error ("User error: %m%p", &CurTok.SVal);
924 SkipUntilSep ();
925 }
926}
927
928
929
930static void DoExitMacro (void)
931/* Exit a macro expansion */
932{
933 if (!InMacExpansion ()) {
934 /* We aren't expanding a macro currently */
935 DoUnexpected ();
936 } else {
937 MacAbort ();
938 }
939}
940
941
942
943static void DoExport (void)
944/* Export a symbol */
945{
946 ExportImport (ExportWithAssign, ADDR_SIZE_DEFAULT, SF_NONE);
947}
948
949
950
951static void DoExportZP (void)
952/* Export a zeropage symbol */
953{
954 ExportImport (ExportWithAssign, ADDR_SIZE_ZP, SF_NONE);
955}
956
957
958
959static void DoFarAddr (void)
960/* Define far addresses (24 bit) */
961{
962 /* Element type for the generated array */
963 static const char EType[2] = { GT_FAR_PTR, GT_VOID };
964
965 /* Record type information */
966 Span* S = OpenSpan ();
967 StrBuf Type = STATIC_STRBUF_INITIALIZER;
968
969 /* Parse arguments */
970 while (1) {
971 EmitFarAddr (BoundedExpr (Expression, 3));
972 if (CurTok.Tok != TOK_COMMA) {
973 break;
974 } else {
975 NextTok ();
976 }
977 }
978
979 /* Close the span, then add type information to it */
980 S = CloseSpan (S);
981 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
982
983 /* Free the type string */
984 SB_Done (&Type);
985}
986
987
988
989static void DoFatal (void)
990/* Fatal user error */
991{
992 if (CurTok.Tok != TOK_STRCON) {
993 ErrorSkip ("String constant expected");
994 } else {
995 Fatal ("User error: %m%p", &CurTok.SVal);
996 SkipUntilSep ();
997 }
998}
999
1000
1001
1002static void DoFeature (void)
1003/* Switch the Feature option */
1004{
1005 /* Allow a list of comma separated keywords */
1006 while (1) {
1007
1008 /* We expect an identifier */
1009 if (CurTok.Tok != TOK_IDENT) {
1010 ErrorSkip ("Identifier expected");
1011 return;
1012 }
1013
1014 /* Make the string attribute lower case */
1015 LocaseSVal ();
1016
1017 /* Set the feature and check for errors */
1018 if (SetFeature (&CurTok.SVal) == FEAT_UNKNOWN) {
1019 /* Not found */
1020 ErrorSkip ("Invalid feature: '%m%p'", &CurTok.SVal);
1021 return;
1022 } else {
1023 /* Skip the keyword */
1024 NextTok ();
1025 }
1026
1027 /* Allow more than one keyword */
1028 if (CurTok.Tok == TOK_COMMA) {
1029 NextTok ();
1030 } else {
1031 break;
1032 }
1033 }
1034}
1035
1036
1037
1038static void DoFileOpt (void)
1039/* Insert a file option */
1040{
1041 long OptNum;
1042
1043 /* The option type may be given as a keyword or as a number. */
1044 if (CurTok.Tok == TOK_IDENT) {
1045
1046 /* Option given as keyword */
1047 static const char* const Keys [] = {
1048 "AUTHOR", "COMMENT", "COMPILER"
1049 };
1050
1051 /* Map the option to a number */
1052 OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
1053 if (OptNum < 0) {
1054 /* Not found */
1055 ErrorSkip ("File option keyword expected");
1056 return;
1057 }
1058
1059 /* Skip the keyword */
1060 NextTok ();
1061
1062 /* Must be followed by a comma */
1063 ConsumeComma ();
1064
1065 /* We accept only string options for now */
1066 if (CurTok.Tok != TOK_STRCON) {
1067 ErrorSkip ("String constant expected");
1068 return;
1069 }
1070
1071 /* Insert the option */
1072 switch (OptNum) {
1073
1074 case 0:
1075 /* Author */
1076 OptAuthor (&CurTok.SVal);
1077 break;
1078
1079 case 1:
1080 /* Comment */
1081 OptComment (&CurTok.SVal);
1082 break;
1083
1084 case 2:
1085 /* Compiler */
1086 OptCompiler (&CurTok.SVal);
1087 break;
1088
1089 default:
1090 Internal ("Invalid OptNum: %ld", OptNum);
1091
1092 }
1093
1094 /* Done */
1095 NextTok ();
1096
1097 } else {
1098
1099 /* Option given as number */
1100 OptNum = ConstExpression ();
1101 if (!IsByteRange (OptNum)) {
1102 ErrorSkip ("Range error");
1103 return;
1104 }
1105
1106 /* Must be followed by a comma */
1107 ConsumeComma ();
1108
1109 /* We accept only string options for now */
1110 if (CurTok.Tok != TOK_STRCON) {
1111 ErrorSkip ("String constant expected");
1112 return;
1113 }
1114
1115 /* Insert the option */
1116 OptStr ((unsigned char) OptNum, &CurTok.SVal);
1117
1118 /* Done */
1119 NextTok ();
1120 }
1121}
1122
1123
1124
1125static void DoForceImport (void)
1126/* Do a forced import on a symbol */
1127{
1128 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_FORCED);
1129}
1130
1131
1132
1133static void DoGlobal (void)
1134/* Declare a global symbol */
1135{
1136 ExportImport (SymGlobal, ADDR_SIZE_DEFAULT, SF_NONE);
1137}
1138
1139
1140
1141static void DoGlobalZP (void)
1142/* Declare a global zeropage symbol */
1143{
1144 ExportImport (SymGlobal, ADDR_SIZE_ZP, SF_NONE);
1145}
1146
1147
1148static void DoHiBytes (void)
1149/* Define bytes, extracting the hi byte from each expression in the list */
1150{
1151 while (1) {
1152 EmitByte (FuncHiByte ());
1153 if (CurTok.Tok != TOK_COMMA) {
1154 break;
1155 } else {
1156 NextTok ();
1157 }
1158 }
1159}
1160
1161
1162
1163static void DoI16 (void)
1164/* Switch the index registers to 16 bit mode (assembler only) */
1165{
1166 if (GetCPU() != CPU_65816) {
1167 Error ("Command is only valid in 65816 mode");
1168 } else {
1169 /* Immidiate mode has two extension bytes */
1170 ExtBytes [AM65I_IMM_INDEX] = 2;
1171 }
1172}
1173
1174
1175
1176static void DoI8 (void)
1177/* Switch the index registers to 16 bit mode (assembler only) */
1178{
1179 if (GetCPU() != CPU_65816) {
1180 Error ("Command is only valid in 65816 mode");
1181 } else {
1182 /* Immidiate mode has one extension byte */
1183 ExtBytes [AM65I_IMM_INDEX] = 1;
1184 }
1185}
1186
1187
1188
1189static void DoImport (void)
1190/* Import a symbol */
1191{
1192 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_NONE);
1193}
1194
1195
1196
1197static void DoImportZP (void)
1198/* Import a zero page symbol */
1199{
1200 ExportImport (SymImport, ADDR_SIZE_ZP, SF_NONE);
1201}
1202
1203
1204
1205static void DoIncBin (void)
1206/* Include a binary file */
1207{
1208 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1209 struct stat StatBuf;
1210 long Start = 0L;
1211 long Count = -1L;
1212 long Size;
1213 FILE* F;
1214
1215 /* Name must follow */
1216 if (CurTok.Tok != TOK_STRCON) {
1217 ErrorSkip ("String constant expected");
1218 return;
1219 }
1220 SB_Copy (&Name, &CurTok.SVal);
1221 SB_Terminate (&Name);
1222 NextTok ();
1223
1224 /* A starting offset may follow */
1225 if (CurTok.Tok == TOK_COMMA) {
1226 NextTok ();
1227 Start = ConstExpression ();
1228
1229 /* And a length may follow */
1230 if (CurTok.Tok == TOK_COMMA) {
1231 NextTok ();
1232 Count = ConstExpression ();
1233 }
1234
1235 }
1236
1237 /* Try to open the file */
1238 F = fopen (SB_GetConstBuf (&Name), "rb");
1239 if (F == 0) {
1240
1241 /* Search for the file in the binary include directory */
1242 char* PathName = SearchFile (BinSearchPath, SB_GetConstBuf (&Name));
1243 if (PathName == 0 || (F = fopen (PathName, "rb")) == 0) {
1244 /* Not found or cannot open, print an error and bail out */
1245 ErrorSkip ("Cannot open include file '%m%p': %s", &Name, strerror (errno));
1246 xfree (PathName);
1247 goto ExitPoint;
1248 }
1249
1250 /* Remember the new file name */
1251 SB_CopyStr (&Name, PathName);
1252
1253 /* Free the allocated memory */
1254 xfree (PathName);
1255 }
1256
1257 /* Get the size of the file */
1258 fseek (F, 0, SEEK_END);
1259 Size = ftell (F);
1260
1261 /* Stat the file and remember the values. There's a race condition here,
1262 ** since we cannot use fileno() (non-standard identifier in standard
1263 ** header file), and therefore not fstat. When using stat with the
1264 ** file name, there's a risk that the file was deleted and recreated
1265 ** while it was open. Since mtime and size are only used to check
1266 ** if a file has changed in the debugger, we will ignore this problem
1267 ** here.
1268 */
1269 SB_Terminate (&Name);
1270 if (FileStat (SB_GetConstBuf (&Name), &StatBuf) != 0) {
1271 Fatal ("Cannot stat input file '%m%p': %s", &Name, strerror (errno));
1272 }
1273
1274 /* Add the file to the input file table */
1275 AddFile (&Name, FT_BINARY, Size, (unsigned long) StatBuf.st_mtime);
1276
1277 /* If a count was not given, calculate it now */
1278 if (Count < 0) {
1279 Count = Size - Start;
1280 if (Count < 0) {
1281 /* Nothing to read - flag this as a range error */
1282 ErrorSkip ("Range error");
1283 goto Done;
1284 }
1285 } else {
1286 /* Count was given, check if it is valid */
1287 if (Start + Count > Size) {
1288 ErrorSkip ("Range error");
1289 goto Done;
1290 }
1291 }
1292
1293 /* Seek to the start position */
1294 fseek (F, Start, SEEK_SET);
1295
1296 /* Read chunks and insert them into the output */
1297 while (Count > 0) {
1298
1299 unsigned char Buf [1024];
1300
1301 /* Calculate the number of bytes to read */
1302 size_t BytesToRead = (Count > (long)sizeof(Buf))? sizeof(Buf) : (size_t) Count;
1303
1304 /* Read chunk */
1305 size_t BytesRead = fread (Buf, 1, BytesToRead, F);
1306 if (BytesToRead != BytesRead) {
1307 /* Some sort of error */
1308 ErrorSkip ("Cannot read from include file '%m%p': %s",
1309 &Name, strerror (errno));
1310 break;
1311 }
1312
1313 /* Insert it into the output */
1314 EmitData (Buf, BytesRead);
1315
1316 /* Keep the counters current */
1317 Count -= BytesRead;
1318 }
1319
1320Done:
1321 /* Close the file, ignore errors since it's r/o */
1322 (void) fclose (F);
1323
1324ExitPoint:
1325 /* Free string memory */
1326 SB_Done (&Name);
1327}
1328
1329
1330
1331static void DoInclude (void)
1332/* Include another file */
1333{
1334 /* Name must follow */
1335 if (CurTok.Tok != TOK_STRCON) {
1336 ErrorSkip ("String constant expected");
1337 } else {
1338 SB_Terminate (&CurTok.SVal);
1339 if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) {
1340 /* Error opening the file, skip remainder of line */
1341 SkipUntilSep ();
1342 }
1343 }
1344}
1345
1346
1347
1348static void DoInterruptor (void)
1349/* Export a symbol as interruptor */
1350{
1351 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1352
1353 /* Symbol name follows */
1354 if (CurTok.Tok != TOK_IDENT) {
1355 ErrorSkip ("Identifier expected");
1356 return;
1357 }
1358 SB_Copy (&Name, &CurTok.SVal);
1359 NextTok ();
1360
1361 /* Parse the remainder of the line and export the symbol */
1362 ConDes (&Name, CD_TYPE_INT);
1363
1364 /* Free string memory */
1365 SB_Done (&Name);
1366}
1367
1368
1369
1370static void DoInvalid (void)
1371/* Handle a token that is invalid here, since it should have been handled on
1372** a much lower level of the expression hierarchy. Getting this sort of token
1373** means that the lower level code has bugs.
1374** This function differs to DoUnexpected in that the latter may be triggered
1375** by the user by using keywords in the wrong location. DoUnexpected is not
1376** an error in the assembler itself, while DoInvalid is.
1377*/
1378{
1379 Internal ("Unexpected token: %m%p", &Keyword);
1380}
1381
1382
1383
1384static void DoLineCont (void)
1385/* Switch the use of line continuations */
1386{
1387 SetBoolOption (&LineCont);
1388}
1389
1390
1391
1392static void DoList (void)
1393/* Enable/disable the listing */
1394{
1395 /* Get the setting */
1396 unsigned char List = 0;
1397 SetBoolOption (&List);
1398
1399 /* Manage the counter */
1400 if (List) {
1401 EnableListing ();
1402 } else {
1403 DisableListing ();
1404 }
1405}
1406
1407
1408
1409static void DoLoBytes (void)
1410/* Define bytes, extracting the lo byte from each expression in the list */
1411{
1412 while (1) {
1413 EmitByte (FuncLoByte ());
1414 if (CurTok.Tok != TOK_COMMA) {
1415 break;
1416 } else {
1417 NextTok ();
1418 }
1419 }
1420}
1421
1422
1423static void DoListBytes (void)
1424/* Set maximum number of bytes to list for one line */
1425{
1426 SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
1427}
1428
1429
1430
1431static void DoLocalChar (void)
1432/* Define the character that starts local labels */
1433{
1434 if (CurTok.Tok != TOK_CHARCON) {
1435 ErrorSkip ("Character constant expected");
1436 } else {
1437 if (CurTok.IVal != '@' && CurTok.IVal != '?') {
1438 Error ("Invalid start character for locals");
1439 } else {
1440 LocalStart = (char) CurTok.IVal;
1441 }
1442 NextTok ();
1443 }
1444}
1445
1446
1447
1448static void DoMacPack (void)
1449/* Insert a macro package */
1450{
1451 /* We expect an identifier */
1452 if (CurTok.Tok != TOK_IDENT) {
1453 ErrorSkip ("Identifier expected");
1454 } else {
1455 SB_AppendStr (&CurTok.SVal, ".mac");
1456 SB_Terminate (&CurTok.SVal);
1457 if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) {
1458 /* Error opening the file, skip remainder of line */
1459 SkipUntilSep ();
1460 }
1461 }
1462}
1463
1464
1465
1466static void DoMacro (void)
1467/* Start a macro definition */
1468{
1469 MacDef (MAC_STYLE_CLASSIC);
1470}
1471
1472
1473
1474static void DoNull (void)
1475/* Switch to the NULL segment */
1476{
1477 UseSeg (&NullSegDef);
1478}
1479
1480
1481
1482static void DoOrg (void)
1483/* Start absolute code */
1484{
1485 long PC = ConstExpression ();
1486 if (PC < 0 || PC > 0xFFFFFF) {
1487 Error ("Range error");
1488 return;
1489 }
1490 EnterAbsoluteMode (PC);
1491}
1492
1493
1494
1495static void DoOut (void)
1496/* Output a string */
1497{
1498 if (CurTok.Tok != TOK_STRCON) {
1499 ErrorSkip ("String constant expected");
1500 } else {
1501 /* Output the string and be sure to flush the output to keep it in
1502 ** sync with any error messages if the output is redirected to a file.
1503 */
1504 printf ("%.*s\n",
1505 (int) SB_GetLen (&CurTok.SVal),
1506 SB_GetConstBuf (&CurTok.SVal));
1507 fflush (stdout);
1508 NextTok ();
1509 }
1510}
1511
1512
1513
1514static void DoP02 (void)
1515/* Switch to 6502 CPU */
1516{
1517 SetCPU (CPU_6502);
1518}
1519
1520
1521
1522static void DoPC02 (void)
1523/* Switch to 65C02 CPU */
1524{
1525 SetCPU (CPU_65C02);
1526}
1527
1528
1529
1530static void DoP816 (void)
1531/* Switch to 65816 CPU */
1532{
1533 SetCPU (CPU_65816);
1534}
1535
1536
1537
1538static void DoP4510 (void)
1539/* Switch to 4510 CPU */
1540{
1541 SetCPU (CPU_4510);
1542}
1543
1544
1545
1546static void DoPageLength (void)
1547/* Set the page length for the listing */
1548{
1549 PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
1550}
1551
1552
1553
1554static void DoPopCPU (void)
1555/* Pop an old CPU setting from the CPU stack */
1556{
1557 /* Must have a CPU on the stack */
1558 if (IS_IsEmpty (&CPUStack)) {
1559 ErrorSkip ("CPU stack is empty");
1560 return;
1561 }
1562
1563 /* Set the CPU to the value popped from stack */
1564 SetCPU (IS_Pop (&CPUStack));
1565}
1566
1567
1568
1569static void DoPopSeg (void)
1570/* Pop an old segment from the segment stack */
1571{
1572 SegDef* Def;
1573
1574 /* Must have a segment on the stack */
1575 if (CollCount (&SegStack) == 0) {
1576 ErrorSkip ("Segment stack is empty");
1577 return;
1578 }
1579
1580 /* Pop the last element */
1581 Def = CollPop (&SegStack);
1582
1583 /* Restore this segment */
1584 UseSeg (Def);
1585
1586 /* Delete the segment definition */
1587 FreeSegDef (Def);
1588}
1589
1590
1591
1592static void DoProc (void)
1593/* Start a new lexical scope */
1594{
1595 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1596 unsigned char AddrSize;
1597 SymEntry* Sym = 0;
1598
1599
1600 if (CurTok.Tok == TOK_IDENT) {
1601
1602 /* The new scope has a name. Remember it. */
1603 SB_Copy (&Name, &CurTok.SVal);
1604
1605 /* Search for the symbol, generate a new one if needed */
1606 Sym = SymFind (CurrentScope, &Name, SYM_ALLOC_NEW);
1607
1608 /* Skip the scope name */
1609 NextTok ();
1610
1611 /* Read an optional address size specifier */
1612 AddrSize = OptionalAddrSize ();
1613
1614 /* Mark the symbol as defined */
1615 SymDef (Sym, GenCurrentPC (), AddrSize, SF_LABEL);
1616
1617 } else {
1618
1619 /* A .PROC statement without a name */
1620 Warning (1, "Unnamed .PROCs are deprecated, please use .SCOPE");
1621 AnonName (&Name, "PROC");
1622 AddrSize = ADDR_SIZE_DEFAULT;
1623
1624 }
1625
1626 /* Enter a new scope */
1627 SymEnterLevel (&Name, SCOPE_SCOPE, AddrSize, Sym);
1628
1629 /* Free memory for Name */
1630 SB_Done (&Name);
1631}
1632
1633
1634
1635static void DoPSC02 (void)
1636/* Switch to 65SC02 CPU */
1637{
1638 SetCPU (CPU_65SC02);
1639}
1640
1641
1642
1643static void DoPushCPU (void)
1644/* Push the current CPU setting onto the CPU stack */
1645{
1646 /* Can only push a limited size of segments */
1647 if (IS_IsFull (&CPUStack)) {
1648 ErrorSkip ("CPU stack overflow");
1649 return;
1650 }
1651
1652 /* Get the current segment and push it */
1653 IS_Push (&CPUStack, GetCPU ());
1654}
1655
1656
1657
1658static void DoPushSeg (void)
1659/* Push the current segment onto the segment stack */
1660{
1661 /* Can only push a limited size of segments */
1662 if (CollCount (&SegStack) >= MAX_PUSHED_SEGMENTS) {
1663 ErrorSkip ("Segment stack overflow");
1664 return;
1665 }
1666
1667 /* Get the current segment and push it */
1668 CollAppend (&SegStack, DupSegDef (GetCurrentSegDef ()));
1669}
1670
1671
1672
1673static void DoReloc (void)
1674/* Enter relocatable mode */
1675{
1676 EnterRelocMode ();
1677}
1678
1679
1680
1681static void DoRepeat (void)
1682/* Repeat some instruction block */
1683{
1684 ParseRepeat ();
1685}
1686
1687
1688
1689static void DoRes (void)
1690/* Reserve some number of storage bytes */
1691{
1692 long Count;
1693 long Val;
1694
1695 Count = ConstExpression ();
1696 if (Count > 0xFFFF || Count < 0) {
1697 ErrorSkip ("Range error");
1698 return;
1699 }
1700 if (CurTok.Tok == TOK_COMMA) {
1701 NextTok ();
1702 Val = ConstExpression ();
1703 /* We need a byte value here */
1704 if (!IsByteRange (Val)) {
1705 ErrorSkip ("Range error");
1706 return;
1707 }
1708
1709 /* Emit constant values */
1710 while (Count--) {
1711 Emit0 ((unsigned char) Val);
1712 }
1713
1714 } else {
1715 /* Emit fill fragments */
1716 EmitFill (Count);
1717 }
1718}
1719
1720
1721
1722static void DoROData (void)
1723/* Switch to the r/o data segment */
1724{
1725 UseSeg (&RODataSegDef);
1726}
1727
1728
1729
1730static void DoScope (void)
1731/* Start a local scope */
1732{
1733 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1734 unsigned char AddrSize;
1735
1736
1737 if (CurTok.Tok == TOK_IDENT) {
1738
1739 /* The new scope has a name. Remember and skip it. */
1740 SB_Copy (&Name, &CurTok.SVal);
1741 NextTok ();
1742
1743 } else {
1744
1745 /* An unnamed scope */
1746 AnonName (&Name, "SCOPE");
1747
1748 }
1749
1750 /* Read an optional address size specifier */
1751 AddrSize = OptionalAddrSize ();
1752
1753 /* Enter the new scope */
1754 SymEnterLevel (&Name, SCOPE_SCOPE, AddrSize, 0);
1755
1756 /* Free memory for Name */
1757 SB_Done (&Name);
1758}
1759
1760
1761
1762static void DoSegment (void)
1763/* Switch to another segment */
1764{
1765 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1766 SegDef Def;
1767
1768 if (CurTok.Tok != TOK_STRCON) {
1769 ErrorSkip ("String constant expected");
1770 } else {
1771
1772 /* Save the name of the segment and skip it */
1773 SB_Copy (&Name, &CurTok.SVal);
1774 NextTok ();
1775
1776 /* Use the name for the segment definition */
1777 SB_Terminate (&Name);
1778 Def.Name = SB_GetBuf (&Name);
1779
1780 /* Check for an optional address size modifier */
1781 Def.AddrSize = OptionalAddrSize ();
1782
1783 /* Set the segment */
1784 UseSeg (&Def);
1785 }
1786
1787 /* Free memory for Name */
1788 SB_Done (&Name);
1789}
1790
1791
1792
1793static void DoSetCPU (void)
1794/* Switch the CPU instruction set */
1795{
1796 /* We expect an identifier */
1797 if (CurTok.Tok != TOK_STRCON) {
1798 ErrorSkip ("String constant expected");
1799 } else {
1800 cpu_t CPU;
1801
1802 /* Try to find the CPU */
1803 SB_Terminate (&CurTok.SVal);
1804 CPU = FindCPU (SB_GetConstBuf (&CurTok.SVal));
1805
1806 /* Switch to the new CPU */
1807 SetCPU (CPU);
1808
1809 /* Skip the identifier. If the CPU switch was successful, the scanner
1810 ** will treat the input now correctly for the new CPU.
1811 */
1812 NextTok ();
1813 }
1814}
1815
1816
1817
1818static void DoSmart (void)
1819/* Smart mode on/off */
1820{
1821 SetBoolOption (&SmartMode);
1822}
1823
1824
1825
1826static void DoTag (void)
1827/* Allocate space for a struct */
1828{
1829 SymEntry* SizeSym;
1830 long Size;
1831
1832 /* Read the struct name */
1833 SymTable* Struct = ParseScopedSymTable ();
1834
1835 /* Check the supposed struct */
1836 if (Struct == 0) {
1837 ErrorSkip ("Unknown struct");
1838 return;
1839 }
1840 if (GetSymTabType (Struct) != SCOPE_STRUCT) {
1841 ErrorSkip ("Not a struct");
1842 return;
1843 }
1844
1845 /* Get the symbol that defines the size of the struct */
1846 SizeSym = GetSizeOfScope (Struct);
1847
1848 /* Check if it does exist and if its value is known */
1849 if (SizeSym == 0 || !SymIsConst (SizeSym, &Size)) {
1850 ErrorSkip ("Size of struct/union is unknown");
1851 return;
1852 }
1853
1854 /* Optional multiplicator may follow */
1855 if (CurTok.Tok == TOK_COMMA) {
1856 long Multiplicator;
1857 NextTok ();
1858 Multiplicator = ConstExpression ();
1859 /* Multiplicator must make sense */
1860 if (Multiplicator <= 0) {
1861 ErrorSkip ("Range error");
1862 return;
1863 }
1864 Size *= Multiplicator;
1865 }
1866
1867 /* Emit fill fragments */
1868 EmitFill (Size);
1869}
1870
1871
1872
1873static void DoUnDef (void)
1874/* Undefine a define style macro */
1875{
1876 /* The function is called with the .UNDEF token in place, because we need
1877 ** to disable .define macro expansions before reading the next token.
1878 ** Otherwise the name of the macro would be expanded, so we would never
1879 ** see it.
1880 */
1881 DisableDefineStyleMacros ();
1882 NextTok ();
1883 EnableDefineStyleMacros ();
1884
1885 /* We expect an identifier */
1886 if (CurTok.Tok != TOK_IDENT) {
1887 ErrorSkip ("Identifier expected");
1888 } else {
1889 MacUndef (&CurTok.SVal, MAC_STYLE_DEFINE);
1890 NextTok ();
1891 }
1892}
1893
1894
1895
1896static void DoUnexpected (void)
1897/* Got an unexpected keyword */
1898{
1899 Error ("Unexpected '%m%p'", &Keyword);
1900 SkipUntilSep ();
1901}
1902
1903
1904
1905static void DoWarning (void)
1906/* User warning */
1907{
1908 if (CurTok.Tok != TOK_STRCON) {
1909 ErrorSkip ("String constant expected");
1910 } else {
1911 Warning (0, "User warning: %m%p", &CurTok.SVal);
1912 SkipUntilSep ();
1913 }
1914}
1915
1916
1917
1918static void DoWord (void)
1919/* Define words */
1920{
1921 /* Element type for the generated array */
1922 static const char EType[1] = { GT_WORD };
1923
1924 /* Record type information */
1925 Span* S = OpenSpan ();
1926 StrBuf Type = STATIC_STRBUF_INITIALIZER;
1927
1928 /* Parse arguments */
1929 while (1) {
1930 EmitWord (BoundedExpr (Expression, 2));
1931 if (CurTok.Tok != TOK_COMMA) {
1932 break;
1933 } else {
1934 NextTok ();
1935 }
1936 }
1937
1938 /* Close the span, then add type information to it */
1939 S = CloseSpan (S);
1940 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
1941
1942 /* Free the type string */
1943 SB_Done (&Type);
1944}
1945
1946
1947
1948static void DoZeropage (void)
1949/* Switch to the zeropage segment */
1950{
1951 UseSeg (&ZeropageSegDef);
1952}
1953
1954
1955
1956/*****************************************************************************/
1957/* Table data */
1958/*****************************************************************************/
1959
1960
1961
1962/* Control commands flags */
1963enum {
1964 ccNone = 0x0000, /* No special flags */
1965 ccKeepToken = 0x0001 /* Do not skip the current token */
1966};
1967
1968/* Control command table */
1969typedef struct CtrlDesc CtrlDesc;
1970struct CtrlDesc {
1971 unsigned Flags; /* Flags for this directive */
1972 void (*Handler) (void); /* Command handler */
1973};
1974
1975#define PSEUDO_COUNT (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1976static CtrlDesc CtrlCmdTab [] = {
1977 { ccNone, DoA16 },
1978 { ccNone, DoA8 },
1979 { ccNone, DoAddr }, /* .ADDR */
1980 { ccNone, DoUnexpected }, /* .ADDRSIZE */
1981 { ccNone, DoAlign },
1982 { ccNone, DoASCIIZ },
1983 { ccNone, DoUnexpected }, /* .ASIZE */
1984 { ccNone, DoAssert },
1985 { ccNone, DoAutoImport },
1986 { ccNone, DoUnexpected }, /* .BANK */
1987 { ccNone, DoUnexpected }, /* .BANKBYTE */
1988 { ccNone, DoBankBytes },
1989 { ccNone, DoUnexpected }, /* .BLANK */
1990 { ccNone, DoBss },
1991 { ccNone, DoByte },
1992 { ccNone, DoCase },
1993 { ccNone, DoCharMap },
1994 { ccNone, DoCode },
1995 { ccNone, DoUnexpected, }, /* .CONCAT */
1996 { ccNone, DoConDes },
1997 { ccNone, DoUnexpected }, /* .CONST */
1998 { ccNone, DoConstructor },
1999 { ccNone, DoUnexpected }, /* .CPU */
2000 { ccNone, DoData },
2001 { ccNone, DoDbg, },
2002 { ccNone, DoDByt },
2003 { ccNone, DoDebugInfo },
2004 { ccNone, DoDefine },
2005 { ccNone, DoUnexpected }, /* .DEFINED */
2006 { ccNone, DoUnexpected }, /* .DEFINEDMACRO */
2007 { ccNone, DoDelMac },
2008 { ccNone, DoDestructor },
2009 { ccNone, DoDWord },
2010 { ccKeepToken, DoConditionals }, /* .ELSE */
2011 { ccKeepToken, DoConditionals }, /* .ELSEIF */
2012 { ccKeepToken, DoEnd },
2013 { ccNone, DoUnexpected }, /* .ENDENUM */
2014 { ccKeepToken, DoConditionals }, /* .ENDIF */
2015 { ccNone, DoUnexpected }, /* .ENDMACRO */
2016 { ccNone, DoEndProc },
2017 { ccNone, DoUnexpected }, /* .ENDREPEAT */
2018 { ccNone, DoEndScope },
2019 { ccNone, DoUnexpected }, /* .ENDSTRUCT */
2020 { ccNone, DoUnexpected }, /* .ENDUNION */
2021 { ccNone, DoEnum },
2022 { ccNone, DoError },
2023 { ccNone, DoExitMacro },
2024 { ccNone, DoExport },
2025 { ccNone, DoExportZP },
2026 { ccNone, DoFarAddr },
2027 { ccNone, DoFatal },
2028 { ccNone, DoFeature },
2029 { ccNone, DoFileOpt },
2030 { ccNone, DoForceImport },
2031 { ccNone, DoUnexpected }, /* .FORCEWORD */
2032 { ccNone, DoGlobal },
2033 { ccNone, DoGlobalZP },
2034 { ccNone, DoUnexpected }, /* .HIBYTE */
2035 { ccNone, DoHiBytes },
2036 { ccNone, DoUnexpected }, /* .HIWORD */
2037 { ccNone, DoI16 },
2038 { ccNone, DoI8 },
2039 { ccNone, DoUnexpected }, /* .IDENT */
2040 { ccKeepToken, DoConditionals }, /* .IF */
2041 { ccKeepToken, DoConditionals }, /* .IFBLANK */
2042 { ccKeepToken, DoConditionals }, /* .IFCONST */
2043 { ccKeepToken, DoConditionals }, /* .IFDEF */
2044 { ccKeepToken, DoConditionals }, /* .IFNBLANK */
2045 { ccKeepToken, DoConditionals }, /* .IFNCONST */
2046 { ccKeepToken, DoConditionals }, /* .IFNDEF */
2047 { ccKeepToken, DoConditionals }, /* .IFNREF */
2048 { ccKeepToken, DoConditionals }, /* .IFP02 */
2049 { ccKeepToken, DoConditionals }, /* .IFP4510 */
2050 { ccKeepToken, DoConditionals }, /* .IFP816 */
2051 { ccKeepToken, DoConditionals }, /* .IFPC02 */
2052 { ccKeepToken, DoConditionals }, /* .IFPSC02 */
2053 { ccKeepToken, DoConditionals }, /* .IFREF */
2054 { ccNone, DoImport },
2055 { ccNone, DoImportZP },
2056 { ccNone, DoIncBin },
2057 { ccNone, DoInclude },
2058 { ccNone, DoInterruptor },
2059 { ccNone, DoUnexpected }, /* .ISIZE */
2060 { ccNone, DoUnexpected }, /* .ISMNEMONIC */
2061 { ccNone, DoInvalid }, /* .LEFT */
2062 { ccNone, DoLineCont },
2063 { ccNone, DoList },
2064 { ccNone, DoListBytes },
2065 { ccNone, DoUnexpected }, /* .LOBYTE */
2066 { ccNone, DoLoBytes },
2067 { ccNone, DoUnexpected }, /* .LOCAL */
2068 { ccNone, DoLocalChar },
2069 { ccNone, DoUnexpected }, /* .LOWORD */
2070 { ccNone, DoMacPack },
2071 { ccNone, DoMacro },
2072 { ccNone, DoUnexpected }, /* .MATCH */
2073 { ccNone, DoUnexpected }, /* .MAX */
2074 { ccNone, DoInvalid }, /* .MID */
2075 { ccNone, DoUnexpected }, /* .MIN */
2076 { ccNone, DoNull },
2077 { ccNone, DoOrg },
2078 { ccNone, DoOut },
2079 { ccNone, DoP02 },
2080 { ccNone, DoP4510 },
2081 { ccNone, DoP816 },
2082 { ccNone, DoPageLength },
2083 { ccNone, DoUnexpected }, /* .PARAMCOUNT */
2084 { ccNone, DoPC02 },
2085 { ccNone, DoPopCPU },
2086 { ccNone, DoPopSeg },
2087 { ccNone, DoProc },
2088 { ccNone, DoPSC02 },
2089 { ccNone, DoPushCPU },
2090 { ccNone, DoPushSeg },
2091 { ccNone, DoUnexpected }, /* .REFERENCED */
2092 { ccNone, DoReloc },
2093 { ccNone, DoRepeat },
2094 { ccNone, DoRes },
2095 { ccNone, DoInvalid }, /* .RIGHT */
2096 { ccNone, DoROData },
2097 { ccNone, DoScope },
2098 { ccNone, DoSegment },
2099 { ccNone, DoUnexpected }, /* .SET */
2100 { ccNone, DoSetCPU },
2101 { ccNone, DoUnexpected }, /* .SIZEOF */
2102 { ccNone, DoSmart },
2103 { ccNone, DoUnexpected }, /* .SPRINTF */
2104 { ccNone, DoUnexpected }, /* .STRAT */
2105 { ccNone, DoUnexpected }, /* .STRING */
2106 { ccNone, DoUnexpected }, /* .STRLEN */
2107 { ccNone, DoStruct },
2108 { ccNone, DoTag },
2109 { ccNone, DoUnexpected }, /* .TCOUNT */
2110 { ccNone, DoUnexpected }, /* .TIME */
2111 { ccKeepToken, DoUnDef },
2112 { ccNone, DoUnion },
2113 { ccNone, DoUnexpected }, /* .VERSION */
2114 { ccNone, DoWarning },
2115 { ccNone, DoWord },
2116 { ccNone, DoUnexpected }, /* .XMATCH */
2117 { ccNone, DoZeropage },
2118};
2119
2120
2121
2122/*****************************************************************************/
2123/* Code */
2124/*****************************************************************************/
2125
2126
2127
2128void HandlePseudo (void)
2129/* Handle a pseudo instruction */
2130{
2131 CtrlDesc* D;
2132
2133 /* Calculate the index into the table */
2134 unsigned Index = CurTok.Tok - TOK_FIRSTPSEUDO;
2135
2136 /* Safety check */
2137 if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
2138 Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
2139 (unsigned) PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
2140 }
2141 CHECK (Index < PSEUDO_COUNT);
2142
2143 /* Get the pseudo intruction descriptor */
2144 D = &CtrlCmdTab [Index];
2145
2146 /* Remember the instruction, then skip it if needed */
2147 if ((D->Flags & ccKeepToken) == 0) {
2148 SB_Copy (&Keyword, &CurTok.SVal);
2149 NextTok ();
2150 }
2151
2152 /* Call the handler */
2153 D->Handler ();
2154}
2155
2156
2157
2158void CheckPseudo (void)
2159/* Check if the stacks are empty at end of assembly */
2160{
2161 if (CollCount (&SegStack) != 0) {
2162 Warning (1, "Segment stack is not empty");
2163 }
2164 if (!IS_IsEmpty (&CPUStack)) {
2165 Warning (1, "CPU stack is not empty");
2166 }
2167}
2168