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 ();
130 if (!ValidAddrSizeForCPU (AddrSize)) {
131 /* Print an error and reset to default */
132 Error ("Invalid address size specification for current CPU");
133 AddrSize = ADDR_SIZE_DEFAULT;
142 static void SetBoolOption (unsigned char* Flag)
143 /* Read a on/off/+/- option and set flag accordingly */
145 static const char* Keys[] = {
150 if (Tok == TOK_PLUS) {
153 } else if (Tok == TOK_MINUS) {
156 } else if (Tok == TOK_IDENT) {
157 /* Map the keyword to a number */
158 switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
159 case 0: *Flag = 0; NextTok (); break;
160 case 1: *Flag = 1; NextTok (); break;
161 default: ErrorSkip ("`on' or `off' expected"); break;
163 } else if (TokIsSep (Tok)) {
164 /* Without anything assume switch on */
167 ErrorSkip ("`on' or `off' expected");
173 static void ExportWithAssign (SymEntry* Sym, unsigned char AddrSize, unsigned Flags)
174 /* Allow to assign the value of an export in an .export statement */
176 /* The name and optional address size spec may be followed by an assignment
179 if (Tok == TOK_ASSIGN || Tok == TOK_EQ) {
181 /* Assignment means the symbol is a label */
182 if (Tok == TOK_ASSIGN) {
186 /* Skip the assignment token */
189 /* Define the symbol with the expression following the '=' */
190 SymDef (Sym, Expression(), ADDR_SIZE_DEFAULT, Flags);
194 /* Now export the symbol */
195 SymExport (Sym, AddrSize, Flags);
200 static void ExportImport (void (*Func) (SymEntry*, unsigned char, unsigned),
201 unsigned char DefAddrSize, unsigned Flags)
202 /* Export or import symbols */
205 unsigned char AddrSize;
209 /* We need an identifier here */
210 if (Tok != TOK_IDENT) {
211 ErrorSkip ("Identifier expected");
215 /* Find the symbol table entry, allocate a new one if necessary */
216 Sym = SymFind (CurrentScope, &SVal, SYM_ALLOC_NEW);
221 /* Get an optional address size */
222 AddrSize = OptionalAddrSize ();
223 if (AddrSize == ADDR_SIZE_DEFAULT) {
224 AddrSize = DefAddrSize;
227 /* Call the actual import/export function */
228 Func (Sym, AddrSize, Flags);
231 if (Tok == TOK_COMMA) {
241 static long IntArg (long Min, long Max)
242 /* Read an integer argument and check a range. Accept the token "unlimited"
243 * and return -1 in this case.
246 if (Tok == TOK_IDENT && SB_CompareStr (&SVal, "unlimited") == 0) {
250 long Val = ConstExpression ();
251 if (Val < Min || Val > Max) {
252 Error ("Range error");
261 static void ConDes (const StrBuf* Name, unsigned Type)
262 /* Parse remaining line for constructor/destructor of the remaining type */
267 /* Find the symbol table entry, allocate a new one if necessary */
268 SymEntry* Sym = SymFind (CurrentScope, Name, SYM_ALLOC_NEW);
270 /* Optional constructor priority */
271 if (Tok == TOK_COMMA) {
272 /* Priority value follows */
274 Prio = ConstExpression ();
275 if (Prio < CD_PRIO_MIN || Prio > CD_PRIO_MAX) {
276 /* Value out of range */
277 Error ("Range error");
281 /* Use the default priority value */
285 /* Define the symbol */
286 SymConDes (Sym, ADDR_SIZE_DEFAULT, Type, (unsigned) Prio);
291 /*****************************************************************************/
292 /* Handler functions */
293 /*****************************************************************************/
297 static void DoA16 (void)
298 /* Switch the accu to 16 bit mode (assembler only) */
300 if (GetCPU() != CPU_65816) {
301 Error ("Command is only valid in 65816 mode");
303 /* Immidiate mode has two extension bytes */
304 ExtBytes [AM65I_IMM_ACCU] = 2;
310 static void DoA8 (void)
311 /* Switch the accu to 8 bit mode (assembler only) */
313 if (GetCPU() != CPU_65816) {
314 Error ("Command is only valid in 65816 mode");
316 /* Immidiate mode has one extension byte */
317 ExtBytes [AM65I_IMM_ACCU] = 1;
323 static void DoAddr (void)
324 /* Define addresses */
327 if (GetCPU() == CPU_65816) {
328 EmitWord (GenWordExpr (Expression ()));
330 /* Do a range check */
331 EmitWord (Expression ());
333 if (Tok != TOK_COMMA) {
343 static void DoAlign (void)
344 /* Align the PC to some boundary */
350 /* Read the alignment value */
351 Align = ConstExpression ();
352 if (Align <= 0 || Align > 0x10000) {
353 ErrorSkip ("Range error");
357 /* Optional value follows */
358 if (Tok == TOK_COMMA) {
360 Val = ConstExpression ();
361 /* We need a byte value here */
362 if (!IsByteRange (Val)) {
363 ErrorSkip ("Range error");
370 /* Check if the alignment is a power of two */
371 Bit = BitFind (Align);
372 if (Align != (0x01L << Bit)) {
373 Error ("Alignment value must be a power of 2");
375 SegAlign (Bit, (int) Val);
381 static void DoASCIIZ (void)
382 /* Define text with a zero terminator */
385 /* Must have a string constant */
386 if (Tok != TOK_STRCON) {
387 ErrorSkip ("String constant expected");
391 /* Translate into target charset and emit */
392 TgtTranslateStrBuf (&SVal);
395 if (Tok == TOK_COMMA) {
406 static void DoAssert (void)
407 /* Add an assertion */
409 static const char* ActionTab [] = {
417 /* First we have the expression that has to evaluated */
418 ExprNode* Expr = Expression ();
422 if (Tok != TOK_IDENT) {
423 ErrorSkip ("Identifier expected");
426 Action = GetSubKey (ActionTab, sizeof (ActionTab) / sizeof (ActionTab[0]));
432 Action = ASSERT_ACT_WARN;
437 Action = ASSERT_ACT_ERROR;
441 Error ("Illegal assert action specifier");
445 /* We can have an optional message. If no message is present, use
446 * "Assertion failed".
448 if (Tok == TOK_COMMA) {
453 /* Read the message */
454 if (Tok != TOK_STRCON) {
455 ErrorSkip ("String constant expected");
459 /* Translate the message into a string id. We can then skip the input
462 Msg = GetStrBufId (&SVal);
467 /* Use "Assertion failed" */
468 Msg = GetStringId ("Assertion failed");
472 /* Remember the assertion */
473 AddAssertion (Expr, Action, Msg);
478 static void DoAutoImport (void)
479 /* Mark unresolved symbols as imported */
481 SetBoolOption (&AutoImport);
485 static void DoBankBytes (void)
486 /* Define bytes, extracting the bank byte from each expression in the list */
489 EmitByte (FuncBankByte ());
490 if (Tok != TOK_COMMA) {
500 static void DoBss (void)
501 /* Switch to the BSS segment */
508 static void DoByte (void)
512 if (Tok == TOK_STRCON) {
513 /* A string, translate into target charset and emit */
514 TgtTranslateStrBuf (&SVal);
518 EmitByte (Expression ());
520 if (Tok != TOK_COMMA) {
524 /* Do smart handling of dangling comma */
525 if (Tok == TOK_SEP) {
526 Error ("Unexpected end of line");
535 static void DoCase (void)
536 /* Switch the IgnoreCase option */
538 SetBoolOption (&IgnoreCase);
539 IgnoreCase = !IgnoreCase;
544 static void DoCharMap (void)
545 /* Allow custome character mappings */
550 /* Read the index as numerical value */
551 Index = ConstExpression ();
552 if (Index < 0 || Index > 255) {
553 /* Value out of range */
554 ErrorSkip ("Range error");
561 /* Read the character code */
562 Code = ConstExpression ();
563 if (Code < 0 || Code > 255) {
564 /* Value out of range */
565 ErrorSkip ("Range error");
569 /* Set the character translation */
570 TgtTranslateSet ((unsigned) Index, (unsigned char) Code);
575 static void DoCode (void)
576 /* Switch to the code segment */
578 UseSeg (&CodeSegDef);
583 static void DoConDes (void)
584 /* Export a symbol as constructor/destructor */
586 static const char* Keys[] = {
591 StrBuf Name = STATIC_STRBUF_INITIALIZER;
594 /* Symbol name follows */
595 if (Tok != TOK_IDENT) {
596 ErrorSkip ("Identifier expected");
599 SB_Copy (&Name, &SVal);
602 /* Type follows. May be encoded as identifier or numerical */
604 if (Tok == TOK_IDENT) {
606 /* Map the following keyword to a number, then skip it */
607 Type = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
610 /* Check if we got a valid keyword */
612 ErrorSkip ("Syntax error");
618 /* Read the type as numerical value */
619 Type = ConstExpression ();
620 if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) {
621 /* Value out of range */
622 ErrorSkip ("Range error");
628 /* Parse the remainder of the line and export the symbol */
629 ConDes (&Name, (unsigned) Type);
632 /* Free string memory */
638 static void DoConstructor (void)
639 /* Export a symbol as constructor */
641 StrBuf Name = STATIC_STRBUF_INITIALIZER;
643 /* Symbol name follows */
644 if (Tok != TOK_IDENT) {
645 ErrorSkip ("Identifier expected");
648 SB_Copy (&Name, &SVal);
651 /* Parse the remainder of the line and export the symbol */
652 ConDes (&Name, CD_TYPE_CON);
654 /* Free string memory */
660 static void DoData (void)
661 /* Switch to the data segment */
663 UseSeg (&DataSegDef);
668 static void DoDbg (void)
669 /* Add debug information from high level code */
671 static const char* Keys[] = {
679 /* We expect a subkey */
680 if (Tok != TOK_IDENT) {
681 ErrorSkip ("Identifier expected");
685 /* Map the following keyword to a number */
686 Key = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
688 /* Skip the subkey */
691 /* Check the key and dispatch to a handler */
693 case 0: DbgInfoFile (); break;
694 case 1: DbgInfoLine (); break;
695 case 2: DbgInfoSym (); break;
696 default: ErrorSkip ("Syntax error"); break;
702 static void DoDByt (void)
703 /* Output double bytes */
706 EmitWord (GenSwapExpr (Expression ()));
707 if (Tok != TOK_COMMA) {
717 static void DoDebugInfo (void)
718 /* Switch debug info on or off */
720 SetBoolOption (&DbgSyms);
725 static void DoDefine (void)
726 /* Define a one line macro */
728 MacDef (MAC_STYLE_DEFINE);
733 static void DoDestructor (void)
734 /* Export a symbol as destructor */
736 StrBuf Name = STATIC_STRBUF_INITIALIZER;
738 /* Symbol name follows */
739 if (Tok != TOK_IDENT) {
740 ErrorSkip ("Identifier expected");
743 SB_Copy (&Name, &SVal);
746 /* Parse the remainder of the line and export the symbol */
747 ConDes (&Name, CD_TYPE_DES);
749 /* Free string memory */
755 static void DoDWord (void)
759 EmitDWord (Expression ());
760 if (Tok != TOK_COMMA) {
770 static void DoEnd (void)
771 /* End of assembly */
779 static void DoEndProc (void)
780 /* Leave a lexical level */
782 if (GetCurrentSymTabType () != ST_PROC) {
784 ErrorSkip ("No open .PROC");
792 static void DoEndScope (void)
793 /* Leave a lexical level */
795 if ( GetCurrentSymTabType () != ST_SCOPE) {
797 ErrorSkip ("No open .SCOPE");
805 static void DoError (void)
808 if (Tok != TOK_STRCON) {
809 ErrorSkip ("String constant expected");
811 Error ("User error: %m%p", &SVal);
818 static void DoExitMacro (void)
819 /* Exit a macro expansion */
821 if (!InMacExpansion ()) {
822 /* We aren't expanding a macro currently */
831 static void DoExport (void)
832 /* Export a symbol */
834 ExportImport (ExportWithAssign, ADDR_SIZE_DEFAULT, SF_NONE);
839 static void DoExportZP (void)
840 /* Export a zeropage symbol */
842 ExportImport (ExportWithAssign, ADDR_SIZE_ZP, SF_NONE);
847 static void DoFarAddr (void)
848 /* Define far addresses (24 bit) */
851 EmitFarAddr (Expression ());
852 if (Tok != TOK_COMMA) {
862 static void DoFeature (void)
863 /* Switch the Feature option */
865 /* Allow a list of comma separated keywords */
868 /* We expect an identifier */
869 if (Tok != TOK_IDENT) {
870 ErrorSkip ("Identifier expected");
874 /* Make the string attribute lower case */
877 /* Set the feature and check for errors */
878 if (SetFeature (&SVal) == FEAT_UNKNOWN) {
880 ErrorSkip ("Invalid feature: `%m%p'", &SVal);
883 /* Skip the keyword */
887 /* Allow more than one keyword */
888 if (Tok == TOK_COMMA) {
898 static void DoFileOpt (void)
899 /* Insert a file option */
903 /* The option type may be given as a keyword or as a number. */
904 if (Tok == TOK_IDENT) {
906 /* Option given as keyword */
907 static const char* Keys [] = {
908 "AUTHOR", "COMMENT", "COMPILER"
911 /* Map the option to a number */
912 OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
915 ErrorSkip ("File option keyword expected");
919 /* Skip the keyword */
922 /* Must be followed by a comma */
925 /* We accept only string options for now */
926 if (Tok != TOK_STRCON) {
927 ErrorSkip ("String constant expected");
931 /* Insert the option */
950 Internal ("Invalid OptNum: %ld", OptNum);
959 /* Option given as number */
960 OptNum = ConstExpression ();
961 if (!IsByteRange (OptNum)) {
962 ErrorSkip ("Range error");
966 /* Must be followed by a comma */
969 /* We accept only string options for now */
970 if (Tok != TOK_STRCON) {
971 ErrorSkip ("String constant expected");
975 /* Insert the option */
976 OptStr ((unsigned char) OptNum, &SVal);
985 static void DoForceImport (void)
986 /* Do a forced import on a symbol */
988 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_FORCED);
993 static void DoGlobal (void)
994 /* Declare a global symbol */
996 ExportImport (SymGlobal, ADDR_SIZE_DEFAULT, SF_NONE);
1001 static void DoGlobalZP (void)
1002 /* Declare a global zeropage symbol */
1004 ExportImport (SymGlobal, ADDR_SIZE_ZP, SF_NONE);
1008 static void DoHiBytes (void)
1009 /* Define bytes, extracting the hi byte from each expression in the list */
1012 EmitByte (FuncHiByte ());
1013 if (Tok != TOK_COMMA) {
1023 static void DoI16 (void)
1024 /* Switch the index registers to 16 bit mode (assembler only) */
1026 if (GetCPU() != CPU_65816) {
1027 Error ("Command is only valid in 65816 mode");
1029 /* Immidiate mode has two extension bytes */
1030 ExtBytes [AM65I_IMM_INDEX] = 2;
1036 static void DoI8 (void)
1037 /* Switch the index registers to 16 bit mode (assembler only) */
1039 if (GetCPU() != CPU_65816) {
1040 Error ("Command is only valid in 65816 mode");
1042 /* Immidiate mode has one extension byte */
1043 ExtBytes [AM65I_IMM_INDEX] = 1;
1049 static void DoImport (void)
1050 /* Import a symbol */
1052 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_NONE);
1057 static void DoImportZP (void)
1058 /* Import a zero page symbol */
1060 ExportImport (SymImport, ADDR_SIZE_ZP, SF_NONE);
1065 static void DoIncBin (void)
1066 /* Include a binary file */
1068 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1074 /* Name must follow */
1075 if (Tok != TOK_STRCON) {
1076 ErrorSkip ("String constant expected");
1079 SB_Copy (&Name, &SVal);
1080 SB_Terminate (&Name);
1083 /* A starting offset may follow */
1084 if (Tok == TOK_COMMA) {
1086 Start = ConstExpression ();
1088 /* And a length may follow */
1089 if (Tok == TOK_COMMA) {
1091 Count = ConstExpression ();
1096 /* Try to open the file */
1097 F = fopen (SB_GetConstBuf (&Name), "rb");
1100 /* Search for the file in the include directories. */
1101 char* PathName = FindInclude (SB_GetConstBuf (&Name));
1102 if (PathName == 0 || (F = fopen (PathName, "r")) == 0) {
1103 /* Not found or cannot open, print an error and bail out */
1104 ErrorSkip ("Cannot open include file `%m%p': %s", &Name, strerror (errno));
1107 /* Free the allocated memory */
1110 /* If we had an error before, bail out now */
1116 /* Get the size of the file */
1117 fseek (F, 0, SEEK_END);
1120 /* If a count was not given, calculate it now */
1122 Count = Size - Start;
1124 /* Nothing to read - flag this as a range error */
1125 ErrorSkip ("Range error");
1129 /* Count was given, check if it is valid */
1130 if (Start + Count > Size) {
1131 ErrorSkip ("Range error");
1136 /* Seek to the start position */
1137 fseek (F, Start, SEEK_SET);
1139 /* Read chunks and insert them into the output */
1142 unsigned char Buf [1024];
1144 /* Calculate the number of bytes to read */
1145 size_t BytesToRead = (Count > (long)sizeof(Buf))? sizeof(Buf) : (size_t) Count;
1148 size_t BytesRead = fread (Buf, 1, BytesToRead, F);
1149 if (BytesToRead != BytesRead) {
1150 /* Some sort of error */
1151 ErrorSkip ("Cannot read from include file `%m%p': %s",
1152 &Name, strerror (errno));
1156 /* Insert it into the output */
1157 EmitData (Buf, BytesRead);
1159 /* Keep the counters current */
1164 /* Close the file, ignore errors since it's r/o */
1168 /* Free string memory */
1174 static void DoInclude (void)
1175 /* Include another file */
1177 /* Name must follow */
1178 if (Tok != TOK_STRCON) {
1179 ErrorSkip ("String constant expected");
1181 SB_Terminate (&SVal);
1182 if (NewInputFile (SB_GetConstBuf (&SVal)) == 0) {
1183 /* Error opening the file, skip remainder of line */
1191 static void DoInterruptor (void)
1192 /* Export a symbol as interruptor */
1194 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1196 /* Symbol name follows */
1197 if (Tok != TOK_IDENT) {
1198 ErrorSkip ("Identifier expected");
1201 SB_Copy (&Name, &SVal);
1204 /* Parse the remainder of the line and export the symbol */
1205 ConDes (&Name, CD_TYPE_INT);
1207 /* Free string memory */
1213 static void DoInvalid (void)
1214 /* Handle a token that is invalid here, since it should have been handled on
1215 * a much lower level of the expression hierarchy. Getting this sort of token
1216 * means that the lower level code has bugs.
1217 * This function differs to DoUnexpected in that the latter may be triggered
1218 * by the user by using keywords in the wrong location. DoUnexpected is not
1219 * an error in the assembler itself, while DoInvalid is.
1222 Internal ("Unexpected token: %m%p", &Keyword);
1227 static void DoLineCont (void)
1228 /* Switch the use of line continuations */
1230 SetBoolOption (&LineCont);
1235 static void DoList (void)
1236 /* Enable/disable the listing */
1238 /* Get the setting */
1240 SetBoolOption (&List);
1242 /* Manage the counter */
1252 static void DoLoBytes (void)
1253 /* Define bytes, extracting the lo byte from each expression in the list */
1256 EmitByte (FuncLoByte ());
1257 if (Tok != TOK_COMMA) {
1266 static void DoListBytes (void)
1267 /* Set maximum number of bytes to list for one line */
1269 SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
1274 static void DoLocalChar (void)
1275 /* Define the character that starts local labels */
1277 if (Tok != TOK_CHARCON) {
1278 ErrorSkip ("Character constant expected");
1280 if (IVal != '@' && IVal != '?') {
1281 Error ("Invalid start character for locals");
1283 LocalStart = (char) IVal;
1291 static void DoMacPack (void)
1292 /* Insert a macro package */
1296 /* We expect an identifier */
1297 if (Tok != TOK_IDENT) {
1298 ErrorSkip ("Identifier expected");
1302 /* Search for the macro package name */
1304 Package = MacPackFind (&SVal);
1307 ErrorSkip ("Invalid macro package");
1311 /* Insert the package. If this fails, skip the remainder of the line to
1312 * avoid additional error messages.
1314 if (MacPackInsert (Package) == 0) {
1321 static void DoMacro (void)
1322 /* Start a macro definition */
1324 MacDef (MAC_STYLE_CLASSIC);
1329 static void DoNull (void)
1330 /* Switch to the NULL segment */
1332 UseSeg (&NullSegDef);
1337 static void DoOrg (void)
1338 /* Start absolute code */
1340 long PC = ConstExpression ();
1341 if (PC < 0 || PC > 0xFFFFFF) {
1342 Error ("Range error");
1345 EnterAbsoluteMode (PC);
1350 static void DoOut (void)
1351 /* Output a string */
1353 if (Tok != TOK_STRCON) {
1354 ErrorSkip ("String constant expected");
1356 /* Output the string and be sure to flush the output to keep it in
1357 * sync with any error messages if the output is redirected to a file.
1359 printf ("%*s\n", SB_GetLen (&SVal), SB_GetConstBuf (&SVal));
1367 static void DoP02 (void)
1368 /* Switch to 6502 CPU */
1375 static void DoPC02 (void)
1376 /* Switch to 65C02 CPU */
1383 static void DoP816 (void)
1384 /* Switch to 65816 CPU */
1391 static void DoPageLength (void)
1392 /* Set the page length for the listing */
1394 PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
1399 static void DoPopSeg (void)
1400 /* Pop an old segment from the segment stack */
1404 /* Must have a segment on the stack */
1405 if (CollCount (&SegStack) == 0) {
1406 ErrorSkip ("Segment stack is empty");
1410 /* Pop the last element */
1411 Def = CollPop (&SegStack);
1413 /* Restore this segment */
1416 /* Delete the segment definition */
1422 static void DoProc (void)
1423 /* Start a new lexical scope */
1425 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1426 unsigned char AddrSize;
1428 if (Tok == TOK_IDENT) {
1432 /* The new scope has a name. Remember it. */
1433 SB_Copy (&Name, &SVal);
1435 /* Search for the symbol, generate a new one if needed */
1436 Sym = SymFind (CurrentScope, &Name, SYM_ALLOC_NEW);
1438 /* Skip the scope name */
1441 /* Read an optional address size specifier */
1442 AddrSize = OptionalAddrSize ();
1444 /* Mark the symbol as defined */
1445 SymDef (Sym, GenCurrentPC (), AddrSize, SF_LABEL);
1449 /* A .PROC statement without a name */
1450 Warning (1, "Unnamed .PROCs are deprecated, please use .SCOPE");
1451 AnonName (&Name, "PROC");
1452 AddrSize = ADDR_SIZE_DEFAULT;
1456 /* Enter a new scope */
1457 SymEnterLevel (&Name, ST_PROC, AddrSize);
1459 /* Free memory for Name */
1465 static void DoPSC02 (void)
1466 /* Switch to 65SC02 CPU */
1468 SetCPU (CPU_65SC02);
1473 static void DoPushSeg (void)
1474 /* Push the current segment onto the segment stack */
1476 /* Can only push a limited size of segments */
1477 if (CollCount (&SegStack) >= MAX_PUSHED_SEGMENTS) {
1478 ErrorSkip ("Segment stack overflow");
1482 /* Get the current segment and push it */
1483 CollAppend (&SegStack, DupSegDef (GetCurrentSegDef ()));
1488 static void DoReloc (void)
1489 /* Enter relocatable mode */
1496 static void DoRepeat (void)
1497 /* Repeat some instruction block */
1504 static void DoRes (void)
1505 /* Reserve some number of storage bytes */
1510 Count = ConstExpression ();
1511 if (Count > 0xFFFF || Count < 0) {
1512 ErrorSkip ("Range error");
1515 if (Tok == TOK_COMMA) {
1517 Val = ConstExpression ();
1518 /* We need a byte value here */
1519 if (!IsByteRange (Val)) {
1520 ErrorSkip ("Range error");
1524 /* Emit constant values */
1526 Emit0 ((unsigned char) Val);
1530 /* Emit fill fragments */
1537 static void DoROData (void)
1538 /* Switch to the r/o data segment */
1540 UseSeg (&RODataSegDef);
1545 static void DoScope (void)
1546 /* Start a local scope */
1548 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1549 unsigned char AddrSize;
1552 if (Tok == TOK_IDENT) {
1554 /* The new scope has a name. Remember and skip it. */
1555 SB_Copy (&Name, &SVal);
1560 /* An unnamed scope */
1561 AnonName (&Name, "SCOPE");
1565 /* Read an optional address size specifier */
1566 AddrSize = OptionalAddrSize ();
1568 /* Enter the new scope */
1569 SymEnterLevel (&Name, ST_SCOPE, AddrSize);
1571 /* Free memory for Name */
1577 static void DoSegment (void)
1578 /* Switch to another segment */
1580 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1583 if (Tok != TOK_STRCON) {
1584 ErrorSkip ("String constant expected");
1587 /* Save the name of the segment and skip it */
1588 SB_Copy (&Name, &SVal);
1591 /* Use the name for the segment definition */
1592 SB_Terminate (&Name);
1593 Def.Name = SB_GetBuf (&Name);
1595 /* Check for an optional address size modifier */
1596 Def.AddrSize = OptionalAddrSize ();
1598 /* Set the segment */
1602 /* Free memory for Name */
1608 static void DoSetCPU (void)
1609 /* Switch the CPU instruction set */
1611 /* We expect an identifier */
1612 if (Tok != TOK_STRCON) {
1613 ErrorSkip ("String constant expected");
1617 /* Try to find the CPU */
1618 SB_Terminate (&SVal);
1619 CPU = FindCPU (SB_GetConstBuf (&SVal));
1621 /* Switch to the new CPU */
1624 /* Skip the identifier. If the CPU switch was successful, the scanner
1625 * will treat the input now correctly for the new CPU.
1633 static void DoSmart (void)
1634 /* Smart mode on/off */
1636 SetBoolOption (&SmartMode);
1641 static void DoSunPlus (void)
1642 /* Switch to the SUNPLUS CPU */
1644 SetCPU (CPU_SUNPLUS);
1649 static void DoTag (void)
1650 /* Allocate space for a struct */
1655 /* Read the struct name */
1656 SymTable* Struct = ParseScopedSymTable ();
1658 /* Check the supposed struct */
1660 ErrorSkip ("Unknown struct");
1663 if (GetSymTabType (Struct) != ST_STRUCT) {
1664 ErrorSkip ("Not a struct");
1668 /* Get the symbol that defines the size of the struct */
1669 SizeSym = GetSizeOfScope (Struct);
1671 /* Check if it does exist and if its value is known */
1672 if (SizeSym == 0 || !SymIsConst (SizeSym, &Size)) {
1673 ErrorSkip ("Size of struct/union is unknown");
1677 /* Optional multiplicator may follow */
1678 if (Tok == TOK_COMMA) {
1681 Multiplicator = ConstExpression ();
1682 /* Multiplicator must make sense */
1683 if (Multiplicator <= 0) {
1684 ErrorSkip ("Range error");
1687 Size *= Multiplicator;
1690 /* Emit fill fragments */
1696 static void DoUnexpected (void)
1697 /* Got an unexpected keyword */
1699 Error ("Unexpected `%m%p'", &Keyword);
1705 static void DoWarning (void)
1708 if (Tok != TOK_STRCON) {
1709 ErrorSkip ("String constant expected");
1711 Warning (0, "User warning: %m%p", &SVal);
1718 static void DoWord (void)
1722 EmitWord (Expression ());
1723 if (Tok != TOK_COMMA) {
1733 static void DoZeropage (void)
1734 /* Switch to the zeropage segment */
1736 UseSeg (&ZeropageSegDef);
1741 /*****************************************************************************/
1743 /*****************************************************************************/
1747 /* Control commands flags */
1749 ccNone = 0x0000, /* No special flags */
1750 ccKeepToken = 0x0001 /* Do not skip the current token */
1753 /* Control command table */
1754 typedef struct CtrlDesc CtrlDesc;
1756 unsigned Flags; /* Flags for this directive */
1757 void (*Handler) (void); /* Command handler */
1760 #define PSEUDO_COUNT (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1761 static CtrlDesc CtrlCmdTab [] = {
1764 { ccNone, DoAddr }, /* .ADDR */
1765 { ccNone, DoAlign },
1766 { ccNone, DoASCIIZ },
1767 { ccNone, DoAssert },
1768 { ccNone, DoAutoImport },
1769 { ccNone, DoUnexpected }, /* .BANKBYTE */
1770 { ccNone, DoBankBytes },
1771 { ccNone, DoUnexpected }, /* .BLANK */
1775 { ccNone, DoCharMap },
1777 { ccNone, DoUnexpected, }, /* .CONCAT */
1778 { ccNone, DoConDes },
1779 { ccNone, DoUnexpected }, /* .CONST */
1780 { ccNone, DoConstructor },
1781 { ccNone, DoUnexpected }, /* .CPU */
1785 { ccNone, DoDebugInfo },
1786 { ccNone, DoDefine },
1787 { ccNone, DoUnexpected }, /* .DEFINED */
1788 { ccNone, DoDestructor },
1789 { ccNone, DoDWord },
1790 { ccKeepToken, DoConditionals }, /* .ELSE */
1791 { ccKeepToken, DoConditionals }, /* .ELSEIF */
1792 { ccKeepToken, DoEnd },
1793 { ccNone, DoUnexpected }, /* .ENDENUM */
1794 { ccKeepToken, DoConditionals }, /* .ENDIF */
1795 { ccNone, DoUnexpected }, /* .ENDMACRO */
1796 { ccNone, DoEndProc },
1797 { ccNone, DoUnexpected }, /* .ENDREPEAT */
1798 { ccNone, DoEndScope },
1799 { ccNone, DoUnexpected }, /* .ENDSTRUCT */
1800 { ccNone, DoUnexpected }, /* .ENDUNION */
1802 { ccNone, DoError },
1803 { ccNone, DoExitMacro },
1804 { ccNone, DoExport },
1805 { ccNone, DoExportZP },
1806 { ccNone, DoFarAddr },
1807 { ccNone, DoFeature },
1808 { ccNone, DoFileOpt },
1809 { ccNone, DoForceImport },
1810 { ccNone, DoUnexpected }, /* .FORCEWORD */
1811 { ccNone, DoGlobal },
1812 { ccNone, DoGlobalZP },
1813 { ccNone, DoUnexpected }, /* .HIBYTE */
1814 { ccNone, DoHiBytes },
1815 { ccNone, DoUnexpected }, /* .HIWORD */
1818 { ccNone, DoUnexpected }, /* .IDENT */
1819 { ccKeepToken, DoConditionals }, /* .IF */
1820 { ccKeepToken, DoConditionals }, /* .IFBLANK */
1821 { ccKeepToken, DoConditionals }, /* .IFCONST */
1822 { ccKeepToken, DoConditionals }, /* .IFDEF */
1823 { ccKeepToken, DoConditionals }, /* .IFNBLANK */
1824 { ccKeepToken, DoConditionals }, /* .IFNCONST */
1825 { ccKeepToken, DoConditionals }, /* .IFNDEF */
1826 { ccKeepToken, DoConditionals }, /* .IFNREF */
1827 { ccKeepToken, DoConditionals }, /* .IFP02 */
1828 { ccKeepToken, DoConditionals }, /* .IFP816 */
1829 { ccKeepToken, DoConditionals }, /* .IFPC02 */
1830 { ccKeepToken, DoConditionals }, /* .IFPSC02 */
1831 { ccKeepToken, DoConditionals }, /* .IFREF */
1832 { ccNone, DoImport },
1833 { ccNone, DoImportZP },
1834 { ccNone, DoIncBin },
1835 { ccNone, DoInclude },
1836 { ccNone, DoInterruptor },
1837 { ccNone, DoInvalid }, /* .LEFT */
1838 { ccNone, DoLineCont },
1840 { ccNone, DoListBytes },
1841 { ccNone, DoUnexpected }, /* .LOBYTE */
1842 { ccNone, DoLoBytes },
1843 { ccNone, DoUnexpected }, /* .LOCAL */
1844 { ccNone, DoLocalChar },
1845 { ccNone, DoUnexpected }, /* .LOWORD */
1846 { ccNone, DoMacPack },
1847 { ccNone, DoMacro },
1848 { ccNone, DoUnexpected }, /* .MATCH */
1849 { ccNone, DoInvalid }, /* .MID */
1855 { ccNone, DoPageLength },
1856 { ccNone, DoUnexpected }, /* .PARAMCOUNT */
1858 { ccNone, DoPopSeg },
1860 { ccNone, DoPSC02 },
1861 { ccNone, DoPushSeg },
1862 { ccNone, DoUnexpected }, /* .REFERENCED */
1863 { ccNone, DoReloc },
1864 { ccNone, DoRepeat },
1866 { ccNone, DoInvalid }, /* .RIGHT */
1867 { ccNone, DoROData },
1868 { ccNone, DoScope },
1869 { ccNone, DoSegment },
1870 { ccNone, DoUnexpected }, /* .SET */
1871 { ccNone, DoSetCPU },
1872 { ccNone, DoUnexpected }, /* .SIZEOF */
1873 { ccNone, DoSmart },
1874 { ccNone, DoUnexpected }, /* .SPRINTF */
1875 { ccNone, DoUnexpected }, /* .STRAT */
1876 { ccNone, DoUnexpected }, /* .STRING */
1877 { ccNone, DoUnexpected }, /* .STRLEN */
1878 { ccNone, DoStruct },
1879 { ccNone, DoSunPlus },
1881 { ccNone, DoUnexpected }, /* .TCOUNT */
1882 { ccNone, DoUnexpected }, /* .TIME */
1883 { ccNone, DoUnion },
1884 { ccNone, DoUnexpected }, /* .VERSION */
1885 { ccNone, DoWarning },
1887 { ccNone, DoUnexpected }, /* .XMATCH */
1888 { ccNone, DoZeropage },
1893 /*****************************************************************************/
1895 /*****************************************************************************/
1899 void HandlePseudo (void)
1900 /* Handle a pseudo instruction */
1904 /* Calculate the index into the table */
1905 unsigned Index = Tok - TOK_FIRSTPSEUDO;
1908 if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
1909 Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
1910 (unsigned) PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
1912 CHECK (Index < PSEUDO_COUNT);
1914 /* Get the pseudo intruction descriptor */
1915 D = &CtrlCmdTab [Index];
1917 /* Remember the instruction, then skip it if needed */
1918 if ((D->Flags & ccKeepToken) == 0) {
1919 SB_Copy (&Keyword, &SVal);
1923 /* Call the handler */
1929 void SegStackCheck (void)
1930 /* Check if the segment stack is empty at end of assembly */
1932 if (CollCount (&SegStack) != 0) {
1933 Error ("Segment stack is not empty");