1 /*****************************************************************************/
5 /* Pseudo instructions for the ca65 macroassembler */
9 /* (C) 1998-2010, Ullrich von Bassewitz */
10 /* Roemerstrasse 52 */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
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. */
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: */
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 */
32 /*****************************************************************************/
43 #include "assertion.h"
81 /*****************************************************************************/
83 /*****************************************************************************/
87 /* Keyword we're about to handle */
88 static StrBuf Keyword = STATIC_STRBUF_INITIALIZER;
91 static IntStack CPUStack = STATIC_INTSTACK_INITIALIZER;
94 #define MAX_PUSHED_SEGMENTS 16
95 static Collection SegStack = STATIC_COLLECTION_INITIALIZER;
99 /*****************************************************************************/
101 /*****************************************************************************/
105 static void DoUnexpected (void);
106 /* Got an unexpected keyword */
108 static void DoInvalid (void);
109 /* Handle a token that is invalid here, since it should have been handled on
110 * a much lower level of the expression hierarchy. Getting this sort of token
111 * means that the lower level code has bugs.
112 * This function differs to DoUnexpected in that the latter may be triggered
113 * by the user by using keywords in the wrong location. DoUnexpected is not
114 * an error in the assembler itself, while DoInvalid is.
119 /*****************************************************************************/
120 /* Helper functions */
121 /*****************************************************************************/
125 static unsigned char OptionalAddrSize (void)
126 /* If a colon follows, parse an optional address size spec and return it.
127 * Otherwise return ADDR_SIZE_DEFAULT.
130 unsigned AddrSize = ADDR_SIZE_DEFAULT;
131 if (Tok == TOK_COLON) {
133 AddrSize = ParseAddrSize ();
134 if (!ValidAddrSizeForCPU (AddrSize)) {
135 /* Print an error and reset to default */
136 Error ("Invalid address size specification for current CPU");
137 AddrSize = ADDR_SIZE_DEFAULT;
146 static void SetBoolOption (unsigned char* Flag)
147 /* Read a on/off/+/- option and set flag accordingly */
149 static const char* Keys[] = {
154 if (Tok == TOK_PLUS) {
157 } else if (Tok == TOK_MINUS) {
160 } else if (Tok == TOK_IDENT) {
161 /* Map the keyword to a number */
162 switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
163 case 0: *Flag = 0; NextTok (); break;
164 case 1: *Flag = 1; NextTok (); break;
165 default: ErrorSkip ("`on' or `off' expected"); break;
167 } else if (TokIsSep (Tok)) {
168 /* Without anything assume switch on */
171 ErrorSkip ("`on' or `off' expected");
177 static void ExportWithAssign (SymEntry* Sym, unsigned char AddrSize, unsigned Flags)
178 /* Allow to assign the value of an export in an .export statement */
180 /* The name and optional address size spec may be followed by an assignment
183 if (Tok == TOK_ASSIGN || Tok == TOK_EQ) {
185 /* Assignment means the symbol is a label */
186 if (Tok == TOK_ASSIGN) {
190 /* Skip the assignment token */
193 /* Define the symbol with the expression following the '=' */
194 SymDef (Sym, Expression(), ADDR_SIZE_DEFAULT, Flags);
198 /* Now export the symbol */
199 SymExport (Sym, AddrSize, Flags);
204 static void ExportImport (void (*Func) (SymEntry*, unsigned char, unsigned),
205 unsigned char DefAddrSize, unsigned Flags)
206 /* Export or import symbols */
209 unsigned char AddrSize;
213 /* We need an identifier here */
214 if (Tok != TOK_IDENT) {
215 ErrorSkip ("Identifier expected");
219 /* Find the symbol table entry, allocate a new one if necessary */
220 Sym = SymFind (CurrentScope, &SVal, SYM_ALLOC_NEW);
225 /* Get an optional address size */
226 AddrSize = OptionalAddrSize ();
227 if (AddrSize == ADDR_SIZE_DEFAULT) {
228 AddrSize = DefAddrSize;
231 /* Call the actual import/export function */
232 Func (Sym, AddrSize, Flags);
235 if (Tok == TOK_COMMA) {
245 static long IntArg (long Min, long Max)
246 /* Read an integer argument and check a range. Accept the token "unlimited"
247 * and return -1 in this case.
250 if (Tok == TOK_IDENT && SB_CompareStr (&SVal, "unlimited") == 0) {
254 long Val = ConstExpression ();
255 if (Val < Min || Val > Max) {
256 Error ("Range error");
265 static void ConDes (const StrBuf* Name, unsigned Type)
266 /* Parse remaining line for constructor/destructor of the remaining type */
271 /* Find the symbol table entry, allocate a new one if necessary */
272 SymEntry* Sym = SymFind (CurrentScope, Name, SYM_ALLOC_NEW);
274 /* Optional constructor priority */
275 if (Tok == TOK_COMMA) {
276 /* Priority value follows */
278 Prio = ConstExpression ();
279 if (Prio < CD_PRIO_MIN || Prio > CD_PRIO_MAX) {
280 /* Value out of range */
281 Error ("Range error");
285 /* Use the default priority value */
289 /* Define the symbol */
290 SymConDes (Sym, ADDR_SIZE_DEFAULT, Type, (unsigned) Prio);
295 /*****************************************************************************/
296 /* Handler functions */
297 /*****************************************************************************/
301 static void DoA16 (void)
302 /* Switch the accu to 16 bit mode (assembler only) */
304 if (GetCPU() != CPU_65816) {
305 Error ("Command is only valid in 65816 mode");
307 /* Immidiate mode has two extension bytes */
308 ExtBytes [AM65I_IMM_ACCU] = 2;
314 static void DoA8 (void)
315 /* Switch the accu to 8 bit mode (assembler only) */
317 if (GetCPU() != CPU_65816) {
318 Error ("Command is only valid in 65816 mode");
320 /* Immidiate mode has one extension byte */
321 ExtBytes [AM65I_IMM_ACCU] = 1;
327 static void DoAddr (void)
328 /* Define addresses */
331 if (GetCPU() == CPU_65816) {
332 EmitWord (GenWordExpr (Expression ()));
334 /* Do a range check */
335 EmitWord (Expression ());
337 if (Tok != TOK_COMMA) {
347 static void DoAlign (void)
348 /* Align the PC to some boundary */
354 /* Read the alignment value */
355 Align = ConstExpression ();
356 if (Align <= 0 || Align > 0x10000) {
357 ErrorSkip ("Range error");
361 /* Optional value follows */
362 if (Tok == TOK_COMMA) {
364 Val = ConstExpression ();
365 /* We need a byte value here */
366 if (!IsByteRange (Val)) {
367 ErrorSkip ("Range error");
374 /* Check if the alignment is a power of two */
375 Bit = BitFind (Align);
376 if (Align != (0x01L << Bit)) {
377 Error ("Alignment value must be a power of 2");
379 SegAlign (Bit, (int) Val);
385 static void DoASCIIZ (void)
386 /* Define text with a zero terminator */
389 /* Must have a string constant */
390 if (Tok != TOK_STRCON) {
391 ErrorSkip ("String constant expected");
395 /* Translate into target charset and emit */
396 TgtTranslateStrBuf (&SVal);
399 if (Tok == TOK_COMMA) {
410 static void DoAssert (void)
411 /* Add an assertion */
413 static const char* ActionTab [] = {
416 "LDWARN", "LDWARNING",
423 /* First we have the expression that has to evaluated */
424 ExprNode* Expr = Expression ();
428 if (Tok != TOK_IDENT) {
429 ErrorSkip ("Identifier expected");
432 switch (GetSubKey (ActionTab, sizeof (ActionTab) / sizeof (ActionTab[0]))) {
437 Action = ASSERT_ACT_WARN;
442 Action = ASSERT_ACT_ERROR;
448 Action = ASSERT_ACT_LDWARN;
453 Action = ASSERT_ACT_LDERROR;
457 Error ("Illegal assert action specifier");
458 /* Use lderror - there won't be an .o file anyway */
459 Action = ASSERT_ACT_LDERROR;
465 /* We can have an optional message. If no message is present, use
466 * "Assertion failed".
468 if (Tok == TOK_COMMA) {
473 /* Read the message */
474 if (Tok != TOK_STRCON) {
475 ErrorSkip ("String constant expected");
479 /* Translate the message into a string id. We can then skip the input
482 Msg = GetStrBufId (&SVal);
487 /* Use "Assertion failed" */
488 Msg = GetStringId ("Assertion failed");
492 /* Remember the assertion */
493 AddAssertion (Expr, (AssertAction) Action, Msg);
498 static void DoAutoImport (void)
499 /* Mark unresolved symbols as imported */
501 SetBoolOption (&AutoImport);
505 static void DoBankBytes (void)
506 /* Define bytes, extracting the bank byte from each expression in the list */
509 EmitByte (FuncBankByte ());
510 if (Tok != TOK_COMMA) {
520 static void DoBss (void)
521 /* Switch to the BSS segment */
528 static void DoByte (void)
532 if (Tok == TOK_STRCON) {
533 /* A string, translate into target charset and emit */
534 TgtTranslateStrBuf (&SVal);
538 EmitByte (Expression ());
540 if (Tok != TOK_COMMA) {
544 /* Do smart handling of dangling comma */
545 if (Tok == TOK_SEP) {
546 Error ("Unexpected end of line");
555 static void DoCase (void)
556 /* Switch the IgnoreCase option */
558 SetBoolOption (&IgnoreCase);
559 IgnoreCase = !IgnoreCase;
564 static void DoCharMap (void)
565 /* Allow custome character mappings */
570 /* Read the index as numerical value */
571 Index = ConstExpression ();
572 if (Index < 0 || Index > 255) {
573 /* Value out of range */
574 ErrorSkip ("Range error");
581 /* Read the character code */
582 Code = ConstExpression ();
583 if (Code < 0 || Code > 255) {
584 /* Value out of range */
585 ErrorSkip ("Range error");
589 /* Set the character translation */
590 TgtTranslateSet ((unsigned) Index, (unsigned char) Code);
595 static void DoCode (void)
596 /* Switch to the code segment */
598 UseSeg (&CodeSegDef);
603 static void DoConDes (void)
604 /* Export a symbol as constructor/destructor */
606 static const char* Keys[] = {
611 StrBuf Name = STATIC_STRBUF_INITIALIZER;
614 /* Symbol name follows */
615 if (Tok != TOK_IDENT) {
616 ErrorSkip ("Identifier expected");
619 SB_Copy (&Name, &SVal);
622 /* Type follows. May be encoded as identifier or numerical */
624 if (Tok == TOK_IDENT) {
626 /* Map the following keyword to a number, then skip it */
627 Type = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
630 /* Check if we got a valid keyword */
632 ErrorSkip ("Syntax error");
638 /* Read the type as numerical value */
639 Type = ConstExpression ();
640 if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) {
641 /* Value out of range */
642 ErrorSkip ("Range error");
648 /* Parse the remainder of the line and export the symbol */
649 ConDes (&Name, (unsigned) Type);
652 /* Free string memory */
658 static void DoConstructor (void)
659 /* Export a symbol as constructor */
661 StrBuf Name = STATIC_STRBUF_INITIALIZER;
663 /* Symbol name follows */
664 if (Tok != TOK_IDENT) {
665 ErrorSkip ("Identifier expected");
668 SB_Copy (&Name, &SVal);
671 /* Parse the remainder of the line and export the symbol */
672 ConDes (&Name, CD_TYPE_CON);
674 /* Free string memory */
680 static void DoData (void)
681 /* Switch to the data segment */
683 UseSeg (&DataSegDef);
688 static void DoDbg (void)
689 /* Add debug information from high level code */
691 static const char* Keys[] = {
699 /* We expect a subkey */
700 if (Tok != TOK_IDENT) {
701 ErrorSkip ("Identifier expected");
705 /* Map the following keyword to a number */
706 Key = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
708 /* Skip the subkey */
711 /* Check the key and dispatch to a handler */
713 case 0: DbgInfoFile (); break;
714 case 1: DbgInfoLine (); break;
715 case 2: DbgInfoSym (); break;
716 default: ErrorSkip ("Syntax error"); break;
722 static void DoDByt (void)
723 /* Output double bytes */
726 EmitWord (GenSwapExpr (Expression ()));
727 if (Tok != TOK_COMMA) {
737 static void DoDebugInfo (void)
738 /* Switch debug info on or off */
740 SetBoolOption (&DbgSyms);
745 static void DoDefine (void)
746 /* Define a one line macro */
748 MacDef (MAC_STYLE_DEFINE);
753 static void DoDestructor (void)
754 /* Export a symbol as destructor */
756 StrBuf Name = STATIC_STRBUF_INITIALIZER;
758 /* Symbol name follows */
759 if (Tok != TOK_IDENT) {
760 ErrorSkip ("Identifier expected");
763 SB_Copy (&Name, &SVal);
766 /* Parse the remainder of the line and export the symbol */
767 ConDes (&Name, CD_TYPE_DES);
769 /* Free string memory */
775 static void DoDWord (void)
779 EmitDWord (Expression ());
780 if (Tok != TOK_COMMA) {
790 static void DoEnd (void)
791 /* End of assembly */
799 static void DoEndProc (void)
800 /* Leave a lexical level */
802 if (GetCurrentSymTabType () != ST_PROC) {
804 ErrorSkip ("No open .PROC");
812 static void DoEndScope (void)
813 /* Leave a lexical level */
815 if ( GetCurrentSymTabType () != ST_SCOPE) {
817 ErrorSkip ("No open .SCOPE");
825 static void DoError (void)
828 if (Tok != TOK_STRCON) {
829 ErrorSkip ("String constant expected");
831 Error ("User error: %m%p", &SVal);
838 static void DoExitMacro (void)
839 /* Exit a macro expansion */
841 if (!InMacExpansion ()) {
842 /* We aren't expanding a macro currently */
851 static void DoExport (void)
852 /* Export a symbol */
854 ExportImport (ExportWithAssign, ADDR_SIZE_DEFAULT, SF_NONE);
859 static void DoExportZP (void)
860 /* Export a zeropage symbol */
862 ExportImport (ExportWithAssign, ADDR_SIZE_ZP, SF_NONE);
867 static void DoFarAddr (void)
868 /* Define far addresses (24 bit) */
871 EmitFarAddr (Expression ());
872 if (Tok != TOK_COMMA) {
882 static void DoFeature (void)
883 /* Switch the Feature option */
885 /* Allow a list of comma separated keywords */
888 /* We expect an identifier */
889 if (Tok != TOK_IDENT) {
890 ErrorSkip ("Identifier expected");
894 /* Make the string attribute lower case */
897 /* Set the feature and check for errors */
898 if (SetFeature (&SVal) == FEAT_UNKNOWN) {
900 ErrorSkip ("Invalid feature: `%m%p'", &SVal);
903 /* Skip the keyword */
907 /* Allow more than one keyword */
908 if (Tok == TOK_COMMA) {
918 static void DoFileOpt (void)
919 /* Insert a file option */
923 /* The option type may be given as a keyword or as a number. */
924 if (Tok == TOK_IDENT) {
926 /* Option given as keyword */
927 static const char* Keys [] = {
928 "AUTHOR", "COMMENT", "COMPILER"
931 /* Map the option to a number */
932 OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
935 ErrorSkip ("File option keyword expected");
939 /* Skip the keyword */
942 /* Must be followed by a comma */
945 /* We accept only string options for now */
946 if (Tok != TOK_STRCON) {
947 ErrorSkip ("String constant expected");
951 /* Insert the option */
970 Internal ("Invalid OptNum: %ld", OptNum);
979 /* Option given as number */
980 OptNum = ConstExpression ();
981 if (!IsByteRange (OptNum)) {
982 ErrorSkip ("Range error");
986 /* Must be followed by a comma */
989 /* We accept only string options for now */
990 if (Tok != TOK_STRCON) {
991 ErrorSkip ("String constant expected");
995 /* Insert the option */
996 OptStr ((unsigned char) OptNum, &SVal);
1005 static void DoForceImport (void)
1006 /* Do a forced import on a symbol */
1008 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_FORCED);
1013 static void DoGlobal (void)
1014 /* Declare a global symbol */
1016 ExportImport (SymGlobal, ADDR_SIZE_DEFAULT, SF_NONE);
1021 static void DoGlobalZP (void)
1022 /* Declare a global zeropage symbol */
1024 ExportImport (SymGlobal, ADDR_SIZE_ZP, SF_NONE);
1028 static void DoHiBytes (void)
1029 /* Define bytes, extracting the hi byte from each expression in the list */
1032 EmitByte (FuncHiByte ());
1033 if (Tok != TOK_COMMA) {
1043 static void DoI16 (void)
1044 /* Switch the index registers to 16 bit mode (assembler only) */
1046 if (GetCPU() != CPU_65816) {
1047 Error ("Command is only valid in 65816 mode");
1049 /* Immidiate mode has two extension bytes */
1050 ExtBytes [AM65I_IMM_INDEX] = 2;
1056 static void DoI8 (void)
1057 /* Switch the index registers to 16 bit mode (assembler only) */
1059 if (GetCPU() != CPU_65816) {
1060 Error ("Command is only valid in 65816 mode");
1062 /* Immidiate mode has one extension byte */
1063 ExtBytes [AM65I_IMM_INDEX] = 1;
1069 static void DoImport (void)
1070 /* Import a symbol */
1072 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_NONE);
1077 static void DoImportZP (void)
1078 /* Import a zero page symbol */
1080 ExportImport (SymImport, ADDR_SIZE_ZP, SF_NONE);
1085 static void DoIncBin (void)
1086 /* Include a binary file */
1088 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1094 /* Name must follow */
1095 if (Tok != TOK_STRCON) {
1096 ErrorSkip ("String constant expected");
1099 SB_Copy (&Name, &SVal);
1100 SB_Terminate (&Name);
1103 /* A starting offset may follow */
1104 if (Tok == TOK_COMMA) {
1106 Start = ConstExpression ();
1108 /* And a length may follow */
1109 if (Tok == TOK_COMMA) {
1111 Count = ConstExpression ();
1116 /* Try to open the file */
1117 F = fopen (SB_GetConstBuf (&Name), "rb");
1120 /* Search for the file in the binary include directory */
1121 char* PathName = FindInclude (SB_GetConstBuf (&Name), INC_BIN);
1122 if (PathName == 0 || (F = fopen (PathName, "rb")) == 0) {
1123 /* Not found or cannot open, print an error and bail out */
1124 ErrorSkip ("Cannot open include file `%m%p': %s", &Name, strerror (errno));
1127 /* Free the allocated memory */
1130 /* If we had an error before, bail out now */
1136 /* Get the size of the file */
1137 fseek (F, 0, SEEK_END);
1140 /* If a count was not given, calculate it now */
1142 Count = Size - Start;
1144 /* Nothing to read - flag this as a range error */
1145 ErrorSkip ("Range error");
1149 /* Count was given, check if it is valid */
1150 if (Start + Count > Size) {
1151 ErrorSkip ("Range error");
1156 /* Seek to the start position */
1157 fseek (F, Start, SEEK_SET);
1159 /* Read chunks and insert them into the output */
1162 unsigned char Buf [1024];
1164 /* Calculate the number of bytes to read */
1165 size_t BytesToRead = (Count > (long)sizeof(Buf))? sizeof(Buf) : (size_t) Count;
1168 size_t BytesRead = fread (Buf, 1, BytesToRead, F);
1169 if (BytesToRead != BytesRead) {
1170 /* Some sort of error */
1171 ErrorSkip ("Cannot read from include file `%m%p': %s",
1172 &Name, strerror (errno));
1176 /* Insert it into the output */
1177 EmitData (Buf, BytesRead);
1179 /* Keep the counters current */
1184 /* Close the file, ignore errors since it's r/o */
1188 /* Free string memory */
1194 static void DoInclude (void)
1195 /* Include another file */
1197 /* Name must follow */
1198 if (Tok != TOK_STRCON) {
1199 ErrorSkip ("String constant expected");
1201 SB_Terminate (&SVal);
1202 if (NewInputFile (SB_GetConstBuf (&SVal)) == 0) {
1203 /* Error opening the file, skip remainder of line */
1211 static void DoInterruptor (void)
1212 /* Export a symbol as interruptor */
1214 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1216 /* Symbol name follows */
1217 if (Tok != TOK_IDENT) {
1218 ErrorSkip ("Identifier expected");
1221 SB_Copy (&Name, &SVal);
1224 /* Parse the remainder of the line and export the symbol */
1225 ConDes (&Name, CD_TYPE_INT);
1227 /* Free string memory */
1233 static void DoInvalid (void)
1234 /* Handle a token that is invalid here, since it should have been handled on
1235 * a much lower level of the expression hierarchy. Getting this sort of token
1236 * means that the lower level code has bugs.
1237 * This function differs to DoUnexpected in that the latter may be triggered
1238 * by the user by using keywords in the wrong location. DoUnexpected is not
1239 * an error in the assembler itself, while DoInvalid is.
1242 Internal ("Unexpected token: %m%p", &Keyword);
1247 static void DoLineCont (void)
1248 /* Switch the use of line continuations */
1250 SetBoolOption (&LineCont);
1255 static void DoList (void)
1256 /* Enable/disable the listing */
1258 /* Get the setting */
1260 SetBoolOption (&List);
1262 /* Manage the counter */
1272 static void DoLoBytes (void)
1273 /* Define bytes, extracting the lo byte from each expression in the list */
1276 EmitByte (FuncLoByte ());
1277 if (Tok != TOK_COMMA) {
1286 static void DoListBytes (void)
1287 /* Set maximum number of bytes to list for one line */
1289 SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
1294 static void DoLocalChar (void)
1295 /* Define the character that starts local labels */
1297 if (Tok != TOK_CHARCON) {
1298 ErrorSkip ("Character constant expected");
1300 if (IVal != '@' && IVal != '?') {
1301 Error ("Invalid start character for locals");
1303 LocalStart = (char) IVal;
1311 static void DoMacPack (void)
1312 /* Insert a macro package */
1316 /* We expect an identifier */
1317 if (Tok != TOK_IDENT) {
1318 ErrorSkip ("Identifier expected");
1322 /* Search for the macro package name */
1324 Package = MacPackFind (&SVal);
1327 ErrorSkip ("Invalid macro package");
1331 /* Insert the package. If this fails, skip the remainder of the line to
1332 * avoid additional error messages.
1334 if (MacPackInsert (Package) == 0) {
1341 static void DoMacro (void)
1342 /* Start a macro definition */
1344 MacDef (MAC_STYLE_CLASSIC);
1349 static void DoNull (void)
1350 /* Switch to the NULL segment */
1352 UseSeg (&NullSegDef);
1357 static void DoOrg (void)
1358 /* Start absolute code */
1360 long PC = ConstExpression ();
1361 if (PC < 0 || PC > 0xFFFFFF) {
1362 Error ("Range error");
1365 EnterAbsoluteMode (PC);
1370 static void DoOut (void)
1371 /* Output a string */
1373 if (Tok != TOK_STRCON) {
1374 ErrorSkip ("String constant expected");
1376 /* Output the string and be sure to flush the output to keep it in
1377 * sync with any error messages if the output is redirected to a file.
1379 printf ("%.*s\n", (int) SB_GetLen (&SVal), SB_GetConstBuf (&SVal));
1387 static void DoP02 (void)
1388 /* Switch to 6502 CPU */
1395 static void DoPC02 (void)
1396 /* Switch to 65C02 CPU */
1403 static void DoP816 (void)
1404 /* Switch to 65816 CPU */
1411 static void DoPageLength (void)
1412 /* Set the page length for the listing */
1414 PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
1419 static void DoPopCPU (void)
1420 /* Pop an old CPU setting from the CPU stack */
1422 /* Must have a CPU on the stack */
1423 if (IS_IsEmpty (&CPUStack)) {
1424 ErrorSkip ("CPU stack is empty");
1428 /* Set the CPU to the value popped from stack */
1429 SetCPU (IS_Pop (&CPUStack));
1434 static void DoPopSeg (void)
1435 /* Pop an old segment from the segment stack */
1439 /* Must have a segment on the stack */
1440 if (CollCount (&SegStack) == 0) {
1441 ErrorSkip ("Segment stack is empty");
1445 /* Pop the last element */
1446 Def = CollPop (&SegStack);
1448 /* Restore this segment */
1451 /* Delete the segment definition */
1457 static void DoProc (void)
1458 /* Start a new lexical scope */
1460 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1461 unsigned char AddrSize;
1463 if (Tok == TOK_IDENT) {
1467 /* The new scope has a name. Remember it. */
1468 SB_Copy (&Name, &SVal);
1470 /* Search for the symbol, generate a new one if needed */
1471 Sym = SymFind (CurrentScope, &Name, SYM_ALLOC_NEW);
1473 /* Skip the scope name */
1476 /* Read an optional address size specifier */
1477 AddrSize = OptionalAddrSize ();
1479 /* Mark the symbol as defined */
1480 SymDef (Sym, GenCurrentPC (), AddrSize, SF_LABEL);
1484 /* A .PROC statement without a name */
1485 Warning (1, "Unnamed .PROCs are deprecated, please use .SCOPE");
1486 AnonName (&Name, "PROC");
1487 AddrSize = ADDR_SIZE_DEFAULT;
1491 /* Enter a new scope */
1492 SymEnterLevel (&Name, ST_PROC, AddrSize);
1494 /* Free memory for Name */
1500 static void DoPSC02 (void)
1501 /* Switch to 65SC02 CPU */
1503 SetCPU (CPU_65SC02);
1508 static void DoPushCPU (void)
1509 /* Push the current CPU setting onto the CPU stack */
1511 /* Can only push a limited size of segments */
1512 if (IS_IsFull (&CPUStack)) {
1513 ErrorSkip ("CPU stack overflow");
1517 /* Get the current segment and push it */
1518 IS_Push (&CPUStack, GetCPU ());
1523 static void DoPushSeg (void)
1524 /* Push the current segment onto the segment stack */
1526 /* Can only push a limited size of segments */
1527 if (CollCount (&SegStack) >= MAX_PUSHED_SEGMENTS) {
1528 ErrorSkip ("Segment stack overflow");
1532 /* Get the current segment and push it */
1533 CollAppend (&SegStack, DupSegDef (GetCurrentSegDef ()));
1538 static void DoReloc (void)
1539 /* Enter relocatable mode */
1546 static void DoRepeat (void)
1547 /* Repeat some instruction block */
1554 static void DoRes (void)
1555 /* Reserve some number of storage bytes */
1560 Count = ConstExpression ();
1561 if (Count > 0xFFFF || Count < 0) {
1562 ErrorSkip ("Range error");
1565 if (Tok == TOK_COMMA) {
1567 Val = ConstExpression ();
1568 /* We need a byte value here */
1569 if (!IsByteRange (Val)) {
1570 ErrorSkip ("Range error");
1574 /* Emit constant values */
1576 Emit0 ((unsigned char) Val);
1580 /* Emit fill fragments */
1587 static void DoROData (void)
1588 /* Switch to the r/o data segment */
1590 UseSeg (&RODataSegDef);
1595 static void DoScope (void)
1596 /* Start a local scope */
1598 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1599 unsigned char AddrSize;
1602 if (Tok == TOK_IDENT) {
1604 /* The new scope has a name. Remember and skip it. */
1605 SB_Copy (&Name, &SVal);
1610 /* An unnamed scope */
1611 AnonName (&Name, "SCOPE");
1615 /* Read an optional address size specifier */
1616 AddrSize = OptionalAddrSize ();
1618 /* Enter the new scope */
1619 SymEnterLevel (&Name, ST_SCOPE, AddrSize);
1621 /* Free memory for Name */
1627 static void DoSegment (void)
1628 /* Switch to another segment */
1630 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1633 if (Tok != TOK_STRCON) {
1634 ErrorSkip ("String constant expected");
1637 /* Save the name of the segment and skip it */
1638 SB_Copy (&Name, &SVal);
1641 /* Use the name for the segment definition */
1642 SB_Terminate (&Name);
1643 Def.Name = SB_GetBuf (&Name);
1645 /* Check for an optional address size modifier */
1646 Def.AddrSize = OptionalAddrSize ();
1648 /* Set the segment */
1652 /* Free memory for Name */
1658 static void DoSetCPU (void)
1659 /* Switch the CPU instruction set */
1661 /* We expect an identifier */
1662 if (Tok != TOK_STRCON) {
1663 ErrorSkip ("String constant expected");
1667 /* Try to find the CPU */
1668 SB_Terminate (&SVal);
1669 CPU = FindCPU (SB_GetConstBuf (&SVal));
1671 /* Switch to the new CPU */
1674 /* Skip the identifier. If the CPU switch was successful, the scanner
1675 * will treat the input now correctly for the new CPU.
1683 static void DoSmart (void)
1684 /* Smart mode on/off */
1686 SetBoolOption (&SmartMode);
1691 static void DoSunPlus (void)
1692 /* Switch to the SUNPLUS CPU */
1694 SetCPU (CPU_SUNPLUS);
1699 static void DoTag (void)
1700 /* Allocate space for a struct */
1705 /* Read the struct name */
1706 SymTable* Struct = ParseScopedSymTable ();
1708 /* Check the supposed struct */
1710 ErrorSkip ("Unknown struct");
1713 if (GetSymTabType (Struct) != ST_STRUCT) {
1714 ErrorSkip ("Not a struct");
1718 /* Get the symbol that defines the size of the struct */
1719 SizeSym = GetSizeOfScope (Struct);
1721 /* Check if it does exist and if its value is known */
1722 if (SizeSym == 0 || !SymIsConst (SizeSym, &Size)) {
1723 ErrorSkip ("Size of struct/union is unknown");
1727 /* Optional multiplicator may follow */
1728 if (Tok == TOK_COMMA) {
1731 Multiplicator = ConstExpression ();
1732 /* Multiplicator must make sense */
1733 if (Multiplicator <= 0) {
1734 ErrorSkip ("Range error");
1737 Size *= Multiplicator;
1740 /* Emit fill fragments */
1746 static void DoUnexpected (void)
1747 /* Got an unexpected keyword */
1749 Error ("Unexpected `%m%p'", &Keyword);
1755 static void DoWarning (void)
1758 if (Tok != TOK_STRCON) {
1759 ErrorSkip ("String constant expected");
1761 Warning (0, "User warning: %m%p", &SVal);
1768 static void DoWord (void)
1772 EmitWord (Expression ());
1773 if (Tok != TOK_COMMA) {
1783 static void DoZeropage (void)
1784 /* Switch to the zeropage segment */
1786 UseSeg (&ZeropageSegDef);
1791 /*****************************************************************************/
1793 /*****************************************************************************/
1797 /* Control commands flags */
1799 ccNone = 0x0000, /* No special flags */
1800 ccKeepToken = 0x0001 /* Do not skip the current token */
1803 /* Control command table */
1804 typedef struct CtrlDesc CtrlDesc;
1806 unsigned Flags; /* Flags for this directive */
1807 void (*Handler) (void); /* Command handler */
1810 #define PSEUDO_COUNT (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1811 static CtrlDesc CtrlCmdTab [] = {
1814 { ccNone, DoAddr }, /* .ADDR */
1815 { ccNone, DoAlign },
1816 { ccNone, DoASCIIZ },
1817 { ccNone, DoAssert },
1818 { ccNone, DoAutoImport },
1819 { ccNone, DoUnexpected }, /* .BANKBYTE */
1820 { ccNone, DoBankBytes },
1821 { ccNone, DoUnexpected }, /* .BLANK */
1825 { ccNone, DoCharMap },
1827 { ccNone, DoUnexpected, }, /* .CONCAT */
1828 { ccNone, DoConDes },
1829 { ccNone, DoUnexpected }, /* .CONST */
1830 { ccNone, DoConstructor },
1831 { ccNone, DoUnexpected }, /* .CPU */
1835 { ccNone, DoDebugInfo },
1836 { ccNone, DoDefine },
1837 { ccNone, DoUnexpected }, /* .DEFINED */
1838 { ccNone, DoDestructor },
1839 { ccNone, DoDWord },
1840 { ccKeepToken, DoConditionals }, /* .ELSE */
1841 { ccKeepToken, DoConditionals }, /* .ELSEIF */
1842 { ccKeepToken, DoEnd },
1843 { ccNone, DoUnexpected }, /* .ENDENUM */
1844 { ccKeepToken, DoConditionals }, /* .ENDIF */
1845 { ccNone, DoUnexpected }, /* .ENDMACRO */
1846 { ccNone, DoEndProc },
1847 { ccNone, DoUnexpected }, /* .ENDREPEAT */
1848 { ccNone, DoEndScope },
1849 { ccNone, DoUnexpected }, /* .ENDSTRUCT */
1850 { ccNone, DoUnexpected }, /* .ENDUNION */
1852 { ccNone, DoError },
1853 { ccNone, DoExitMacro },
1854 { ccNone, DoExport },
1855 { ccNone, DoExportZP },
1856 { ccNone, DoFarAddr },
1857 { ccNone, DoFeature },
1858 { ccNone, DoFileOpt },
1859 { ccNone, DoForceImport },
1860 { ccNone, DoUnexpected }, /* .FORCEWORD */
1861 { ccNone, DoGlobal },
1862 { ccNone, DoGlobalZP },
1863 { ccNone, DoUnexpected }, /* .HIBYTE */
1864 { ccNone, DoHiBytes },
1865 { ccNone, DoUnexpected }, /* .HIWORD */
1868 { ccNone, DoUnexpected }, /* .IDENT */
1869 { ccKeepToken, DoConditionals }, /* .IF */
1870 { ccKeepToken, DoConditionals }, /* .IFBLANK */
1871 { ccKeepToken, DoConditionals }, /* .IFCONST */
1872 { ccKeepToken, DoConditionals }, /* .IFDEF */
1873 { ccKeepToken, DoConditionals }, /* .IFNBLANK */
1874 { ccKeepToken, DoConditionals }, /* .IFNCONST */
1875 { ccKeepToken, DoConditionals }, /* .IFNDEF */
1876 { ccKeepToken, DoConditionals }, /* .IFNREF */
1877 { ccKeepToken, DoConditionals }, /* .IFP02 */
1878 { ccKeepToken, DoConditionals }, /* .IFP816 */
1879 { ccKeepToken, DoConditionals }, /* .IFPC02 */
1880 { ccKeepToken, DoConditionals }, /* .IFPSC02 */
1881 { ccKeepToken, DoConditionals }, /* .IFREF */
1882 { ccNone, DoImport },
1883 { ccNone, DoImportZP },
1884 { ccNone, DoIncBin },
1885 { ccNone, DoInclude },
1886 { ccNone, DoInterruptor },
1887 { ccNone, DoInvalid }, /* .LEFT */
1888 { ccNone, DoLineCont },
1890 { ccNone, DoListBytes },
1891 { ccNone, DoUnexpected }, /* .LOBYTE */
1892 { ccNone, DoLoBytes },
1893 { ccNone, DoUnexpected }, /* .LOCAL */
1894 { ccNone, DoLocalChar },
1895 { ccNone, DoUnexpected }, /* .LOWORD */
1896 { ccNone, DoMacPack },
1897 { ccNone, DoMacro },
1898 { ccNone, DoUnexpected }, /* .MATCH */
1899 { ccNone, DoUnexpected }, /* .MAX */
1900 { ccNone, DoInvalid }, /* .MID */
1901 { ccNone, DoUnexpected }, /* .MIN */
1907 { ccNone, DoPageLength },
1908 { ccNone, DoUnexpected }, /* .PARAMCOUNT */
1910 { ccNone, DoPopCPU },
1911 { ccNone, DoPopSeg },
1913 { ccNone, DoPSC02 },
1914 { ccNone, DoPushCPU },
1915 { ccNone, DoPushSeg },
1916 { ccNone, DoUnexpected }, /* .REFERENCED */
1917 { ccNone, DoReloc },
1918 { ccNone, DoRepeat },
1920 { ccNone, DoInvalid }, /* .RIGHT */
1921 { ccNone, DoROData },
1922 { ccNone, DoScope },
1923 { ccNone, DoSegment },
1924 { ccNone, DoUnexpected }, /* .SET */
1925 { ccNone, DoSetCPU },
1926 { ccNone, DoUnexpected }, /* .SIZEOF */
1927 { ccNone, DoSmart },
1928 { ccNone, DoUnexpected }, /* .SPRINTF */
1929 { ccNone, DoUnexpected }, /* .STRAT */
1930 { ccNone, DoUnexpected }, /* .STRING */
1931 { ccNone, DoUnexpected }, /* .STRLEN */
1932 { ccNone, DoStruct },
1933 { ccNone, DoSunPlus },
1935 { ccNone, DoUnexpected }, /* .TCOUNT */
1936 { ccNone, DoUnexpected }, /* .TIME */
1937 { ccNone, DoUnion },
1938 { ccNone, DoUnexpected }, /* .VERSION */
1939 { ccNone, DoWarning },
1941 { ccNone, DoUnexpected }, /* .XMATCH */
1942 { ccNone, DoZeropage },
1947 /*****************************************************************************/
1949 /*****************************************************************************/
1953 void HandlePseudo (void)
1954 /* Handle a pseudo instruction */
1958 /* Calculate the index into the table */
1959 unsigned Index = Tok - TOK_FIRSTPSEUDO;
1962 if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
1963 Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
1964 (unsigned) PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
1966 CHECK (Index < PSEUDO_COUNT);
1968 /* Get the pseudo intruction descriptor */
1969 D = &CtrlCmdTab [Index];
1971 /* Remember the instruction, then skip it if needed */
1972 if ((D->Flags & ccKeepToken) == 0) {
1973 SB_Copy (&Keyword, &SVal);
1977 /* Call the handler */
1983 void CheckPseudo (void)
1984 /* Check if the stacks are empty at end of assembly */
1986 if (CollCount (&SegStack) != 0) {
1987 Warning (1, "Segment stack is not empty");
1989 if (!IS_IsEmpty (&CPUStack)) {
1990 Warning (1, "CPU stack is not empty");