1 /*****************************************************************************/
5 /* Pseudo instructions for the ca65 macroassembler */
9 /* (C) 1998-2008, 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 "assertdefs.h"
80 /*****************************************************************************/
82 /*****************************************************************************/
86 /* Keyword we're about to handle */
87 static StrBuf Keyword = STATIC_STRBUF_INITIALIZER;
90 #define MAX_PUSHED_SEGMENTS 16
91 static Collection SegStack = STATIC_COLLECTION_INITIALIZER;
95 /*****************************************************************************/
97 /*****************************************************************************/
101 static void DoUnexpected (void);
102 /* Got an unexpected keyword */
104 static void DoInvalid (void);
105 /* Handle a token that is invalid here, since it should have been handled on
106 * a much lower level of the expression hierarchy. Getting this sort of token
107 * means that the lower level code has bugs.
108 * This function differs to DoUnexpected in that the latter may be triggered
109 * by the user by using keywords in the wrong location. DoUnexpected is not
110 * an error in the assembler itself, while DoInvalid is.
115 /*****************************************************************************/
116 /* Helper functions */
117 /*****************************************************************************/
121 static unsigned char OptionalAddrSize (void)
122 /* If a colon follows, parse an optional address size spec and return it.
123 * Otherwise return ADDR_SIZE_DEFAULT.
126 unsigned AddrSize = ADDR_SIZE_DEFAULT;
127 if (Tok == TOK_COLON) {
129 AddrSize = ParseAddrSize ();
137 static void SetBoolOption (unsigned char* Flag)
138 /* Read a on/off/+/- option and set flag accordingly */
140 static const char* Keys[] = {
145 if (Tok == TOK_PLUS) {
148 } else if (Tok == TOK_MINUS) {
151 } else if (Tok == TOK_IDENT) {
152 /* Map the keyword to a number */
153 switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
154 case 0: *Flag = 0; NextTok (); break;
155 case 1: *Flag = 1; NextTok (); break;
156 default: ErrorSkip ("`on' or `off' expected"); break;
158 } else if (TokIsSep (Tok)) {
159 /* Without anything assume switch on */
162 ErrorSkip ("`on' or `off' expected");
168 static void ExportWithAssign (SymEntry* Sym, unsigned char AddrSize, unsigned Flags)
169 /* Allow to assign the value of an export in an .export statement */
171 /* The name and optional address size spec may be followed by an assignment
174 if (Tok == TOK_ASSIGN || Tok == TOK_EQ) {
176 /* Assignment means the symbol is a label */
177 if (Tok == TOK_ASSIGN) {
181 /* Skip the assignment token */
184 /* Define the symbol with the expression following the '=' */
185 SymDef (Sym, Expression(), ADDR_SIZE_DEFAULT, Flags);
189 /* Now export the symbol */
190 SymExport (Sym, AddrSize, Flags);
195 static void ExportImport (void (*Func) (SymEntry*, unsigned char, unsigned),
196 unsigned char DefAddrSize, unsigned Flags)
197 /* Export or import symbols */
200 unsigned char AddrSize;
204 /* We need an identifier here */
205 if (Tok != TOK_IDENT) {
206 ErrorSkip ("Identifier expected");
210 /* Find the symbol table entry, allocate a new one if necessary */
211 Sym = SymFind (CurrentScope, &SVal, SYM_ALLOC_NEW);
216 /* Get an optional address size */
217 AddrSize = OptionalAddrSize ();
218 if (AddrSize == ADDR_SIZE_DEFAULT) {
219 AddrSize = DefAddrSize;
222 /* Call the actual import/export function */
223 Func (Sym, AddrSize, Flags);
226 if (Tok == TOK_COMMA) {
236 static long IntArg (long Min, long Max)
237 /* Read an integer argument and check a range. Accept the token "unlimited"
238 * and return -1 in this case.
241 if (Tok == TOK_IDENT && SB_CompareStr (&SVal, "unlimited") == 0) {
245 long Val = ConstExpression ();
246 if (Val < Min || Val > Max) {
247 Error ("Range error");
256 static void ConDes (const StrBuf* Name, unsigned Type)
257 /* Parse remaining line for constructor/destructor of the remaining type */
262 /* Find the symbol table entry, allocate a new one if necessary */
263 SymEntry* Sym = SymFind (CurrentScope, Name, SYM_ALLOC_NEW);
265 /* Optional constructor priority */
266 if (Tok == TOK_COMMA) {
267 /* Priority value follows */
269 Prio = ConstExpression ();
270 if (Prio < CD_PRIO_MIN || Prio > CD_PRIO_MAX) {
271 /* Value out of range */
272 Error ("Range error");
276 /* Use the default priority value */
280 /* Define the symbol */
281 SymConDes (Sym, ADDR_SIZE_DEFAULT, Type, (unsigned) Prio);
286 /*****************************************************************************/
287 /* Handler functions */
288 /*****************************************************************************/
292 static void DoA16 (void)
293 /* Switch the accu to 16 bit mode (assembler only) */
295 if (GetCPU() != CPU_65816) {
296 Error ("Command is only valid in 65816 mode");
298 /* Immidiate mode has two extension bytes */
299 ExtBytes [AM65I_IMM_ACCU] = 2;
305 static void DoA8 (void)
306 /* Switch the accu to 8 bit mode (assembler only) */
308 if (GetCPU() != CPU_65816) {
309 Error ("Command is only valid in 65816 mode");
311 /* Immidiate mode has one extension byte */
312 ExtBytes [AM65I_IMM_ACCU] = 1;
318 static void DoAddr (void)
319 /* Define addresses */
322 if (GetCPU() == CPU_65816) {
323 EmitWord (GenWordExpr (Expression ()));
325 /* Do a range check */
326 EmitWord (Expression ());
328 if (Tok != TOK_COMMA) {
338 static void DoAlign (void)
339 /* Align the PC to some boundary */
345 /* Read the alignment value */
346 Align = ConstExpression ();
347 if (Align <= 0 || Align > 0x10000) {
348 ErrorSkip ("Range error");
352 /* Optional value follows */
353 if (Tok == TOK_COMMA) {
355 Val = ConstExpression ();
356 /* We need a byte value here */
357 if (!IsByteRange (Val)) {
358 ErrorSkip ("Range error");
365 /* Check if the alignment is a power of two */
366 Bit = BitFind (Align);
367 if (Align != (0x01L << Bit)) {
368 Error ("Alignment value must be a power of 2");
370 SegAlign (Bit, (int) Val);
376 static void DoASCIIZ (void)
377 /* Define text with a zero terminator */
380 /* Must have a string constant */
381 if (Tok != TOK_STRCON) {
382 ErrorSkip ("String constant expected");
386 /* Translate into target charset and emit */
387 TgtTranslateStrBuf (&SVal);
390 if (Tok == TOK_COMMA) {
401 static void DoAssert (void)
402 /* Add an assertion */
404 static const char* ActionTab [] = {
412 /* First we have the expression that has to evaluated */
413 ExprNode* Expr = Expression ();
417 if (Tok != TOK_IDENT) {
418 ErrorSkip ("Identifier expected");
421 Action = GetSubKey (ActionTab, sizeof (ActionTab) / sizeof (ActionTab[0]));
427 Action = ASSERT_ACT_WARN;
432 Action = ASSERT_ACT_ERROR;
436 Error ("Illegal assert action specifier");
440 /* We can have an optional message. If no message is present, use
441 * "Assertion failed".
443 if (Tok == TOK_COMMA) {
448 /* Read the message */
449 if (Tok != TOK_STRCON) {
450 ErrorSkip ("String constant expected");
454 /* Translate the message into a string id. We can then skip the input
457 Msg = GetStrBufId (&SVal);
462 /* Use "Assertion failed" */
463 Msg = GetStringId ("Assertion failed");
467 /* Remember the assertion */
468 AddAssertion (Expr, Action, Msg);
473 static void DoAutoImport (void)
474 /* Mark unresolved symbols as imported */
476 SetBoolOption (&AutoImport);
481 static void DoBss (void)
482 /* Switch to the BSS segment */
489 static void DoByte (void)
493 if (Tok == TOK_STRCON) {
494 /* A string, translate into target charset and emit */
495 TgtTranslateStrBuf (&SVal);
499 EmitByte (Expression ());
501 if (Tok != TOK_COMMA) {
505 /* Do smart handling of dangling comma */
506 if (Tok == TOK_SEP) {
507 Error ("Unexpected end of line");
516 static void DoCase (void)
517 /* Switch the IgnoreCase option */
519 SetBoolOption (&IgnoreCase);
520 IgnoreCase = !IgnoreCase;
525 static void DoCharMap (void)
526 /* Allow custome character mappings */
531 /* Read the index as numerical value */
532 Index = ConstExpression ();
533 if (Index < 0 || Index > 255) {
534 /* Value out of range */
535 ErrorSkip ("Range error");
542 /* Read the character code */
543 Code = ConstExpression ();
544 if (Code < 0 || Code > 255) {
545 /* Value out of range */
546 ErrorSkip ("Range error");
550 /* Set the character translation */
551 TgtTranslateSet ((unsigned) Index, (unsigned char) Code);
556 static void DoCode (void)
557 /* Switch to the code segment */
559 UseSeg (&CodeSegDef);
564 static void DoConDes (void)
565 /* Export a symbol as constructor/destructor */
567 static const char* Keys[] = {
572 StrBuf Name = STATIC_STRBUF_INITIALIZER;
575 /* Symbol name follows */
576 if (Tok != TOK_IDENT) {
577 ErrorSkip ("Identifier expected");
580 SB_Copy (&Name, &SVal);
583 /* Type follows. May be encoded as identifier or numerical */
585 if (Tok == TOK_IDENT) {
587 /* Map the following keyword to a number, then skip it */
588 Type = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
591 /* Check if we got a valid keyword */
593 ErrorSkip ("Syntax error");
599 /* Read the type as numerical value */
600 Type = ConstExpression ();
601 if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) {
602 /* Value out of range */
603 ErrorSkip ("Range error");
609 /* Parse the remainder of the line and export the symbol */
610 ConDes (&Name, (unsigned) Type);
613 /* Free string memory */
619 static void DoConstructor (void)
620 /* Export a symbol as constructor */
622 StrBuf Name = STATIC_STRBUF_INITIALIZER;
624 /* Symbol name follows */
625 if (Tok != TOK_IDENT) {
626 ErrorSkip ("Identifier expected");
629 SB_Copy (&Name, &SVal);
632 /* Parse the remainder of the line and export the symbol */
633 ConDes (&Name, CD_TYPE_CON);
635 /* Free string memory */
641 static void DoData (void)
642 /* Switch to the data segment */
644 UseSeg (&DataSegDef);
649 static void DoDbg (void)
650 /* Add debug information from high level code */
652 static const char* Keys[] = {
660 /* We expect a subkey */
661 if (Tok != TOK_IDENT) {
662 ErrorSkip ("Identifier expected");
666 /* Map the following keyword to a number */
667 Key = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
669 /* Skip the subkey */
672 /* Check the key and dispatch to a handler */
674 case 0: DbgInfoFile (); break;
675 case 1: DbgInfoLine (); break;
676 case 2: DbgInfoSym (); break;
677 default: ErrorSkip ("Syntax error"); break;
683 static void DoDByt (void)
684 /* Output double bytes */
687 EmitWord (GenSwapExpr (Expression ()));
688 if (Tok != TOK_COMMA) {
698 static void DoDebugInfo (void)
699 /* Switch debug info on or off */
701 SetBoolOption (&DbgSyms);
706 static void DoDefine (void)
707 /* Define a one line macro */
709 MacDef (MAC_STYLE_DEFINE);
714 static void DoDestructor (void)
715 /* Export a symbol as destructor */
717 StrBuf Name = STATIC_STRBUF_INITIALIZER;
719 /* Symbol name follows */
720 if (Tok != TOK_IDENT) {
721 ErrorSkip ("Identifier expected");
724 SB_Copy (&Name, &SVal);
727 /* Parse the remainder of the line and export the symbol */
728 ConDes (&Name, CD_TYPE_DES);
730 /* Free string memory */
736 static void DoDWord (void)
740 EmitDWord (Expression ());
741 if (Tok != TOK_COMMA) {
751 static void DoEnd (void)
752 /* End of assembly */
760 static void DoEndProc (void)
761 /* Leave a lexical level */
763 if (GetCurrentSymTabType () != ST_PROC) {
765 ErrorSkip ("No open .PROC");
773 static void DoEndScope (void)
774 /* Leave a lexical level */
776 if ( GetCurrentSymTabType () != ST_SCOPE) {
778 ErrorSkip ("No open .SCOPE");
786 static void DoError (void)
789 if (Tok != TOK_STRCON) {
790 ErrorSkip ("String constant expected");
792 Error ("User error: %m%p", &SVal);
799 static void DoExitMacro (void)
800 /* Exit a macro expansion */
802 if (!InMacExpansion ()) {
803 /* We aren't expanding a macro currently */
812 static void DoExport (void)
813 /* Export a symbol */
815 ExportImport (ExportWithAssign, ADDR_SIZE_DEFAULT, SF_NONE);
820 static void DoExportZP (void)
821 /* Export a zeropage symbol */
823 ExportImport (ExportWithAssign, ADDR_SIZE_ZP, SF_NONE);
828 static void DoFarAddr (void)
829 /* Define far addresses (24 bit) */
832 EmitFarAddr (Expression ());
833 if (Tok != TOK_COMMA) {
843 static void DoFeature (void)
844 /* Switch the Feature option */
846 /* Allow a list of comma separated keywords */
849 /* We expect an identifier */
850 if (Tok != TOK_IDENT) {
851 ErrorSkip ("Identifier expected");
855 /* Make the string attribute lower case */
858 /* Set the feature and check for errors */
859 if (SetFeature (&SVal) == FEAT_UNKNOWN) {
861 ErrorSkip ("Invalid feature: `%m%p'", &SVal);
864 /* Skip the keyword */
868 /* Allow more than one keyword */
869 if (Tok == TOK_COMMA) {
879 static void DoFileOpt (void)
880 /* Insert a file option */
884 /* The option type may be given as a keyword or as a number. */
885 if (Tok == TOK_IDENT) {
887 /* Option given as keyword */
888 static const char* Keys [] = {
889 "AUTHOR", "COMMENT", "COMPILER"
892 /* Map the option to a number */
893 OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
896 ErrorSkip ("File option keyword expected");
900 /* Skip the keyword */
903 /* Must be followed by a comma */
906 /* We accept only string options for now */
907 if (Tok != TOK_STRCON) {
908 ErrorSkip ("String constant expected");
912 /* Insert the option */
931 Internal ("Invalid OptNum: %ld", OptNum);
940 /* Option given as number */
941 OptNum = ConstExpression ();
942 if (!IsByteRange (OptNum)) {
943 ErrorSkip ("Range error");
947 /* Must be followed by a comma */
950 /* We accept only string options for now */
951 if (Tok != TOK_STRCON) {
952 ErrorSkip ("String constant expected");
956 /* Insert the option */
957 OptStr ((unsigned char) OptNum, &SVal);
966 static void DoForceImport (void)
967 /* Do a forced import on a symbol */
969 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_FORCED);
974 static void DoGlobal (void)
975 /* Declare a global symbol */
977 ExportImport (SymGlobal, ADDR_SIZE_DEFAULT, SF_NONE);
982 static void DoGlobalZP (void)
983 /* Declare a global zeropage symbol */
985 ExportImport (SymGlobal, ADDR_SIZE_ZP, SF_NONE);
990 static void DoI16 (void)
991 /* Switch the index registers to 16 bit mode (assembler only) */
993 if (GetCPU() != CPU_65816) {
994 Error ("Command is only valid in 65816 mode");
996 /* Immidiate mode has two extension bytes */
997 ExtBytes [AM65I_IMM_INDEX] = 2;
1003 static void DoI8 (void)
1004 /* Switch the index registers to 16 bit mode (assembler only) */
1006 if (GetCPU() != CPU_65816) {
1007 Error ("Command is only valid in 65816 mode");
1009 /* Immidiate mode has one extension byte */
1010 ExtBytes [AM65I_IMM_INDEX] = 1;
1016 static void DoImport (void)
1017 /* Import a symbol */
1019 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_NONE);
1024 static void DoImportZP (void)
1025 /* Import a zero page symbol */
1027 ExportImport (SymImport, ADDR_SIZE_ZP, SF_NONE);
1032 static void DoIncBin (void)
1033 /* Include a binary file */
1035 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1041 /* Name must follow */
1042 if (Tok != TOK_STRCON) {
1043 ErrorSkip ("String constant expected");
1046 SB_Copy (&Name, &SVal);
1047 SB_Terminate (&Name);
1050 /* A starting offset may follow */
1051 if (Tok == TOK_COMMA) {
1053 Start = ConstExpression ();
1055 /* And a length may follow */
1056 if (Tok == TOK_COMMA) {
1058 Count = ConstExpression ();
1063 /* Try to open the file */
1064 F = fopen (SB_GetConstBuf (&Name), "rb");
1067 /* Search for the file in the include directories. */
1068 char* PathName = FindInclude (SB_GetConstBuf (&Name));
1069 if (PathName == 0 || (F = fopen (PathName, "r")) == 0) {
1070 /* Not found or cannot open, print an error and bail out */
1071 ErrorSkip ("Cannot open include file `%m%p': %s", &Name, strerror (errno));
1074 /* Free the allocated memory */
1077 /* If we had an error before, bail out now */
1083 /* Get the size of the file */
1084 fseek (F, 0, SEEK_END);
1087 /* If a count was not given, calculate it now */
1089 Count = Size - Start;
1091 /* Nothing to read - flag this as a range error */
1092 ErrorSkip ("Range error");
1096 /* Count was given, check if it is valid */
1097 if (Start + Count > Size) {
1098 ErrorSkip ("Range error");
1103 /* Seek to the start position */
1104 fseek (F, Start, SEEK_SET);
1106 /* Read chunks and insert them into the output */
1109 unsigned char Buf [1024];
1111 /* Calculate the number of bytes to read */
1112 size_t BytesToRead = (Count > (long)sizeof(Buf))? sizeof(Buf) : (size_t) Count;
1115 size_t BytesRead = fread (Buf, 1, BytesToRead, F);
1116 if (BytesToRead != BytesRead) {
1117 /* Some sort of error */
1118 ErrorSkip ("Cannot read from include file `%m%p': %s",
1119 &Name, strerror (errno));
1123 /* Insert it into the output */
1124 EmitData (Buf, BytesRead);
1126 /* Keep the counters current */
1131 /* Close the file, ignore errors since it's r/o */
1135 /* Free string memory */
1141 static void DoInclude (void)
1142 /* Include another file */
1144 /* Name must follow */
1145 if (Tok != TOK_STRCON) {
1146 ErrorSkip ("String constant expected");
1148 SB_Terminate (&SVal);
1149 NewInputFile (SB_GetConstBuf (&SVal));
1155 static void DoInterruptor (void)
1156 /* Export a symbol as interruptor */
1158 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1160 /* Symbol name follows */
1161 if (Tok != TOK_IDENT) {
1162 ErrorSkip ("Identifier expected");
1165 SB_Copy (&Name, &SVal);
1168 /* Parse the remainder of the line and export the symbol */
1169 ConDes (&Name, CD_TYPE_INT);
1171 /* Free string memory */
1177 static void DoInvalid (void)
1178 /* Handle a token that is invalid here, since it should have been handled on
1179 * a much lower level of the expression hierarchy. Getting this sort of token
1180 * means that the lower level code has bugs.
1181 * This function differs to DoUnexpected in that the latter may be triggered
1182 * by the user by using keywords in the wrong location. DoUnexpected is not
1183 * an error in the assembler itself, while DoInvalid is.
1186 Internal ("Unexpected token: %m%p", &Keyword);
1191 static void DoLineCont (void)
1192 /* Switch the use of line continuations */
1194 SetBoolOption (&LineCont);
1199 static void DoList (void)
1200 /* Enable/disable the listing */
1202 /* Get the setting */
1204 SetBoolOption (&List);
1206 /* Manage the counter */
1216 static void DoListBytes (void)
1217 /* Set maximum number of bytes to list for one line */
1219 SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
1224 static void DoLocalChar (void)
1225 /* Define the character that starts local labels */
1227 if (Tok != TOK_CHARCON) {
1228 ErrorSkip ("Character constant expected");
1230 if (IVal != '@' && IVal != '?') {
1231 Error ("Invalid start character for locals");
1233 LocalStart = (char) IVal;
1241 static void DoMacPack (void)
1242 /* Insert a macro package */
1246 /* We expect an identifier */
1247 if (Tok != TOK_IDENT) {
1248 ErrorSkip ("Identifier expected");
1252 /* Search for the macro package name */
1254 Package = MacPackFind (&SVal);
1257 ErrorSkip ("Invalid macro package");
1261 /* Insert the package */
1262 MacPackInsert (Package);
1267 static void DoMacro (void)
1268 /* Start a macro definition */
1270 MacDef (MAC_STYLE_CLASSIC);
1275 static void DoNull (void)
1276 /* Switch to the NULL segment */
1278 UseSeg (&NullSegDef);
1283 static void DoOrg (void)
1284 /* Start absolute code */
1286 long PC = ConstExpression ();
1287 if (PC < 0 || PC > 0xFFFFFF) {
1288 Error ("Range error");
1291 EnterAbsoluteMode (PC);
1296 static void DoOut (void)
1297 /* Output a string */
1299 if (Tok != TOK_STRCON) {
1300 ErrorSkip ("String constant expected");
1302 /* Output the string and be sure to flush the output to keep it in
1303 * sync with any error messages if the output is redirected to a file.
1305 printf ("%m%p\n", &SVal);
1313 static void DoP02 (void)
1314 /* Switch to 6502 CPU */
1321 static void DoPC02 (void)
1322 /* Switch to 65C02 CPU */
1329 static void DoP816 (void)
1330 /* Switch to 65816 CPU */
1337 static void DoPageLength (void)
1338 /* Set the page length for the listing */
1340 PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
1345 static void DoPopSeg (void)
1346 /* Pop an old segment from the segment stack */
1350 /* Must have a segment on the stack */
1351 if (CollCount (&SegStack) == 0) {
1352 ErrorSkip ("Segment stack is empty");
1356 /* Pop the last element */
1357 Def = CollPop (&SegStack);
1359 /* Restore this segment */
1362 /* Delete the segment definition */
1368 static void DoProc (void)
1369 /* Start a new lexical scope */
1371 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1372 unsigned char AddrSize;
1374 if (Tok == TOK_IDENT) {
1378 /* The new scope has a name. Remember it. */
1379 SB_Copy (&Name, &SVal);
1381 /* Search for the symbol, generate a new one if needed */
1382 Sym = SymFind (CurrentScope, &Name, SYM_ALLOC_NEW);
1384 /* Skip the scope name */
1387 /* Read an optional address size specifier */
1388 AddrSize = OptionalAddrSize ();
1390 /* Mark the symbol as defined */
1391 SymDef (Sym, GenCurrentPC (), AddrSize, SF_LABEL);
1395 /* A .PROC statement without a name */
1396 Warning (1, "Unnamed .PROCs are deprecated, please use .SCOPE");
1397 AnonName (&Name, "PROC");
1398 AddrSize = ADDR_SIZE_DEFAULT;
1402 /* Enter a new scope */
1403 SymEnterLevel (&Name, ST_PROC, AddrSize);
1405 /* Free memory for Name */
1411 static void DoPSC02 (void)
1412 /* Switch to 65SC02 CPU */
1414 SetCPU (CPU_65SC02);
1419 static void DoPushSeg (void)
1420 /* Push the current segment onto the segment stack */
1422 /* Can only push a limited size of segments */
1423 if (CollCount (&SegStack) >= MAX_PUSHED_SEGMENTS) {
1424 ErrorSkip ("Segment stack overflow");
1428 /* Get the current segment and push it */
1429 CollAppend (&SegStack, DupSegDef (GetCurrentSegDef ()));
1434 static void DoReloc (void)
1435 /* Enter relocatable mode */
1442 static void DoRepeat (void)
1443 /* Repeat some instruction block */
1450 static void DoRes (void)
1451 /* Reserve some number of storage bytes */
1456 Count = ConstExpression ();
1457 if (Count > 0xFFFF || Count < 0) {
1458 ErrorSkip ("Range error");
1461 if (Tok == TOK_COMMA) {
1463 Val = ConstExpression ();
1464 /* We need a byte value here */
1465 if (!IsByteRange (Val)) {
1466 ErrorSkip ("Range error");
1470 /* Emit constant values */
1472 Emit0 ((unsigned char) Val);
1476 /* Emit fill fragments */
1483 static void DoROData (void)
1484 /* Switch to the r/o data segment */
1486 UseSeg (&RODataSegDef);
1491 static void DoScope (void)
1492 /* Start a local scope */
1494 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1495 unsigned char AddrSize;
1498 if (Tok == TOK_IDENT) {
1500 /* The new scope has a name. Remember and skip it. */
1501 SB_Copy (&Name, &SVal);
1506 /* An unnamed scope */
1507 AnonName (&Name, "SCOPE");
1511 /* Read an optional address size specifier */
1512 AddrSize = OptionalAddrSize ();
1514 /* Enter the new scope */
1515 SymEnterLevel (&Name, ST_SCOPE, AddrSize);
1517 /* Free memory for Name */
1523 static void DoSegment (void)
1524 /* Switch to another segment */
1526 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1529 if (Tok != TOK_STRCON) {
1530 ErrorSkip ("String constant expected");
1533 /* Save the name of the segment and skip it */
1534 SB_Copy (&Name, &SVal);
1537 /* Use the name for the segment definition */
1538 SB_Terminate (&Name);
1539 Def.Name = SB_GetBuf (&Name);
1541 /* Check for an optional address size modifier */
1542 Def.AddrSize = OptionalAddrSize ();
1544 /* Set the segment */
1548 /* Free memory for Name */
1554 static void DoSetCPU (void)
1555 /* Switch the CPU instruction set */
1557 /* We expect an identifier */
1558 if (Tok != TOK_STRCON) {
1559 ErrorSkip ("String constant expected");
1561 /* Try to find the CPU */
1562 SB_Terminate (&SVal);
1563 cpu_t CPU = FindCPU (SB_GetConstBuf (&SVal));
1565 /* Switch to the new CPU */
1568 /* Skip the identifier. If the CPU switch was successful, the scanner
1569 * will treat the input now correctly for the new CPU.
1577 static void DoSmart (void)
1578 /* Smart mode on/off */
1580 SetBoolOption (&SmartMode);
1585 static void DoSunPlus (void)
1586 /* Switch to the SUNPLUS CPU */
1588 SetCPU (CPU_SUNPLUS);
1593 static void DoTag (void)
1594 /* Allocate space for a struct */
1599 /* Read the struct name */
1600 SymTable* Struct = ParseScopedSymTable ();
1602 /* Check the supposed struct */
1604 ErrorSkip ("Unknown struct");
1607 if (GetSymTabType (Struct) != ST_STRUCT) {
1608 ErrorSkip ("Not a struct");
1612 /* Get the symbol that defines the size of the struct */
1613 SizeSym = GetSizeOfScope (Struct);
1615 /* Check if it does exist and if its value is known */
1616 if (SizeSym == 0 || !SymIsConst (SizeSym, &Size)) {
1617 ErrorSkip ("Size of struct/union is unknown");
1621 /* Optional multiplicator may follow */
1622 if (Tok == TOK_COMMA) {
1625 Multiplicator = ConstExpression ();
1626 /* Multiplicator must make sense */
1627 if (Multiplicator <= 0) {
1628 ErrorSkip ("Range error");
1631 Size *= Multiplicator;
1634 /* Emit fill fragments */
1640 static void DoUnexpected (void)
1641 /* Got an unexpected keyword */
1643 Error ("Unexpected `%m%p'", &Keyword);
1649 static void DoWarning (void)
1652 if (Tok != TOK_STRCON) {
1653 ErrorSkip ("String constant expected");
1655 Warning (0, "User warning: %m%p", &SVal);
1662 static void DoWord (void)
1666 EmitWord (Expression ());
1667 if (Tok != TOK_COMMA) {
1677 static void DoZeropage (void)
1678 /* Switch to the zeropage segment */
1680 UseSeg (&ZeropageSegDef);
1685 /*****************************************************************************/
1687 /*****************************************************************************/
1691 /* Control commands flags */
1693 ccNone = 0x0000, /* No special flags */
1694 ccKeepToken = 0x0001 /* Do not skip the current token */
1697 /* Control command table */
1698 typedef struct CtrlDesc CtrlDesc;
1700 unsigned Flags; /* Flags for this directive */
1701 void (*Handler) (void); /* Command handler */
1704 #define PSEUDO_COUNT (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1705 static CtrlDesc CtrlCmdTab [] = {
1708 { ccNone, DoAddr }, /* .ADDR */
1709 { ccNone, DoAlign },
1710 { ccNone, DoASCIIZ },
1711 { ccNone, DoAssert },
1712 { ccNone, DoAutoImport },
1713 { ccNone, DoUnexpected }, /* .BANKBYTE */
1714 { ccNone, DoUnexpected }, /* .BLANK */
1718 { ccNone, DoCharMap },
1720 { ccNone, DoUnexpected, }, /* .CONCAT */
1721 { ccNone, DoConDes },
1722 { ccNone, DoUnexpected }, /* .CONST */
1723 { ccNone, DoConstructor },
1724 { ccNone, DoUnexpected }, /* .CPU */
1728 { ccNone, DoDebugInfo },
1729 { ccNone, DoDefine },
1730 { ccNone, DoUnexpected }, /* .DEFINED */
1731 { ccNone, DoDestructor },
1732 { ccNone, DoDWord },
1733 { ccKeepToken, DoConditionals }, /* .ELSE */
1734 { ccKeepToken, DoConditionals }, /* .ELSEIF */
1735 { ccKeepToken, DoEnd },
1736 { ccNone, DoUnexpected }, /* .ENDENUM */
1737 { ccKeepToken, DoConditionals }, /* .ENDIF */
1738 { ccNone, DoUnexpected }, /* .ENDMACRO */
1739 { ccNone, DoEndProc },
1740 { ccNone, DoUnexpected }, /* .ENDREPEAT */
1741 { ccNone, DoEndScope },
1742 { ccNone, DoUnexpected }, /* .ENDSTRUCT */
1743 { ccNone, DoUnexpected }, /* .ENDUNION */
1745 { ccNone, DoError },
1746 { ccNone, DoExitMacro },
1747 { ccNone, DoExport },
1748 { ccNone, DoExportZP },
1749 { ccNone, DoFarAddr },
1750 { ccNone, DoFeature },
1751 { ccNone, DoFileOpt },
1752 { ccNone, DoForceImport },
1753 { ccNone, DoUnexpected }, /* .FORCEWORD */
1754 { ccNone, DoGlobal },
1755 { ccNone, DoGlobalZP },
1756 { ccNone, DoUnexpected }, /* .HIBYTE */
1757 { ccNone, DoUnexpected }, /* .HIWORD */
1760 { ccNone, DoUnexpected }, /* .IDENT */
1761 { ccKeepToken, DoConditionals }, /* .IF */
1762 { ccKeepToken, DoConditionals }, /* .IFBLANK */
1763 { ccKeepToken, DoConditionals }, /* .IFCONST */
1764 { ccKeepToken, DoConditionals }, /* .IFDEF */
1765 { ccKeepToken, DoConditionals }, /* .IFNBLANK */
1766 { ccKeepToken, DoConditionals }, /* .IFNCONST */
1767 { ccKeepToken, DoConditionals }, /* .IFNDEF */
1768 { ccKeepToken, DoConditionals }, /* .IFNREF */
1769 { ccKeepToken, DoConditionals }, /* .IFP02 */
1770 { ccKeepToken, DoConditionals }, /* .IFP816 */
1771 { ccKeepToken, DoConditionals }, /* .IFPC02 */
1772 { ccKeepToken, DoConditionals }, /* .IFPSC02 */
1773 { ccKeepToken, DoConditionals }, /* .IFREF */
1774 { ccNone, DoImport },
1775 { ccNone, DoImportZP },
1776 { ccNone, DoIncBin },
1777 { ccNone, DoInclude },
1778 { ccNone, DoInterruptor },
1779 { ccNone, DoInvalid }, /* .LEFT */
1780 { ccNone, DoLineCont },
1782 { ccNone, DoListBytes },
1783 { ccNone, DoUnexpected }, /* .LOBYTE */
1784 { ccNone, DoUnexpected }, /* .LOCAL */
1785 { ccNone, DoLocalChar },
1786 { ccNone, DoUnexpected }, /* .LOWORD */
1787 { ccNone, DoMacPack },
1788 { ccNone, DoMacro },
1789 { ccNone, DoUnexpected }, /* .MATCH */
1790 { ccNone, DoInvalid }, /* .MID */
1796 { ccNone, DoPageLength },
1797 { ccNone, DoUnexpected }, /* .PARAMCOUNT */
1799 { ccNone, DoPopSeg },
1801 { ccNone, DoPSC02 },
1802 { ccNone, DoPushSeg },
1803 { ccNone, DoUnexpected }, /* .REFERENCED */
1804 { ccNone, DoReloc },
1805 { ccNone, DoRepeat },
1807 { ccNone, DoInvalid }, /* .RIGHT */
1808 { ccNone, DoROData },
1809 { ccNone, DoScope },
1810 { ccNone, DoSegment },
1811 { ccNone, DoUnexpected }, /* .SET */
1812 { ccNone, DoSetCPU },
1813 { ccNone, DoUnexpected }, /* .SIZEOF */
1814 { ccNone, DoSmart },
1815 { ccNone, DoUnexpected }, /* .SPRINTF */
1816 { ccNone, DoUnexpected }, /* .STRAT */
1817 { ccNone, DoUnexpected }, /* .STRING */
1818 { ccNone, DoUnexpected }, /* .STRLEN */
1819 { ccNone, DoStruct },
1820 { ccNone, DoSunPlus },
1822 { ccNone, DoUnexpected }, /* .TCOUNT */
1823 { ccNone, DoUnexpected }, /* .TIME */
1824 { ccNone, DoUnion },
1825 { ccNone, DoUnexpected }, /* .VERSION */
1826 { ccNone, DoWarning },
1828 { ccNone, DoUnexpected }, /* .XMATCH */
1829 { ccNone, DoZeropage },
1834 /*****************************************************************************/
1836 /*****************************************************************************/
1840 void HandlePseudo (void)
1841 /* Handle a pseudo instruction */
1845 /* Calculate the index into the table */
1846 unsigned Index = Tok - TOK_FIRSTPSEUDO;
1849 if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
1850 Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
1851 (unsigned) PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
1853 CHECK (Index < PSEUDO_COUNT);
1855 /* Get the pseudo intruction descriptor */
1856 D = &CtrlCmdTab [Index];
1858 /* Remember the instruction, then skip it if needed */
1859 if ((D->Flags & ccKeepToken) == 0) {
1860 SB_Copy (&Keyword, &SVal);
1864 /* Call the handler */
1870 void SegStackCheck (void)
1871 /* Check if the segment stack is empty at end of assembly */
1873 if (CollCount (&SegStack) != 0) {
1874 Error ("Segment stack is not empty");