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"
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 [] = {
412 "LDWARN", "LDWARNING",
419 /* First we have the expression that has to evaluated */
420 ExprNode* Expr = Expression ();
424 if (Tok != TOK_IDENT) {
425 ErrorSkip ("Identifier expected");
428 switch (GetSubKey (ActionTab, sizeof (ActionTab) / sizeof (ActionTab[0]))) {
433 Action = ASSERT_ACT_WARN;
438 Action = ASSERT_ACT_ERROR;
444 Action = ASSERT_ACT_LDWARN;
449 Action = ASSERT_ACT_LDERROR;
453 Error ("Illegal assert action specifier");
454 /* Use lderror - there won't be an .o file anyway */
455 Action = ASSERT_ACT_LDERROR;
461 /* We can have an optional message. If no message is present, use
462 * "Assertion failed".
464 if (Tok == TOK_COMMA) {
469 /* Read the message */
470 if (Tok != TOK_STRCON) {
471 ErrorSkip ("String constant expected");
475 /* Translate the message into a string id. We can then skip the input
478 Msg = GetStrBufId (&SVal);
483 /* Use "Assertion failed" */
484 Msg = GetStringId ("Assertion failed");
488 /* Remember the assertion */
489 AddAssertion (Expr, (AssertAction) Action, Msg);
494 static void DoAutoImport (void)
495 /* Mark unresolved symbols as imported */
497 SetBoolOption (&AutoImport);
501 static void DoBankBytes (void)
502 /* Define bytes, extracting the bank byte from each expression in the list */
505 EmitByte (FuncBankByte ());
506 if (Tok != TOK_COMMA) {
516 static void DoBss (void)
517 /* Switch to the BSS segment */
524 static void DoByte (void)
528 if (Tok == TOK_STRCON) {
529 /* A string, translate into target charset and emit */
530 TgtTranslateStrBuf (&SVal);
534 EmitByte (Expression ());
536 if (Tok != TOK_COMMA) {
540 /* Do smart handling of dangling comma */
541 if (Tok == TOK_SEP) {
542 Error ("Unexpected end of line");
551 static void DoCase (void)
552 /* Switch the IgnoreCase option */
554 SetBoolOption (&IgnoreCase);
555 IgnoreCase = !IgnoreCase;
560 static void DoCharMap (void)
561 /* Allow custome character mappings */
566 /* Read the index as numerical value */
567 Index = ConstExpression ();
568 if (Index < 0 || Index > 255) {
569 /* Value out of range */
570 ErrorSkip ("Range error");
577 /* Read the character code */
578 Code = ConstExpression ();
579 if (Code < 0 || Code > 255) {
580 /* Value out of range */
581 ErrorSkip ("Range error");
585 /* Set the character translation */
586 TgtTranslateSet ((unsigned) Index, (unsigned char) Code);
591 static void DoCode (void)
592 /* Switch to the code segment */
594 UseSeg (&CodeSegDef);
599 static void DoConDes (void)
600 /* Export a symbol as constructor/destructor */
602 static const char* Keys[] = {
607 StrBuf Name = STATIC_STRBUF_INITIALIZER;
610 /* Symbol name follows */
611 if (Tok != TOK_IDENT) {
612 ErrorSkip ("Identifier expected");
615 SB_Copy (&Name, &SVal);
618 /* Type follows. May be encoded as identifier or numerical */
620 if (Tok == TOK_IDENT) {
622 /* Map the following keyword to a number, then skip it */
623 Type = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
626 /* Check if we got a valid keyword */
628 ErrorSkip ("Syntax error");
634 /* Read the type as numerical value */
635 Type = ConstExpression ();
636 if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) {
637 /* Value out of range */
638 ErrorSkip ("Range error");
644 /* Parse the remainder of the line and export the symbol */
645 ConDes (&Name, (unsigned) Type);
648 /* Free string memory */
654 static void DoConstructor (void)
655 /* Export a symbol as constructor */
657 StrBuf Name = STATIC_STRBUF_INITIALIZER;
659 /* Symbol name follows */
660 if (Tok != TOK_IDENT) {
661 ErrorSkip ("Identifier expected");
664 SB_Copy (&Name, &SVal);
667 /* Parse the remainder of the line and export the symbol */
668 ConDes (&Name, CD_TYPE_CON);
670 /* Free string memory */
676 static void DoData (void)
677 /* Switch to the data segment */
679 UseSeg (&DataSegDef);
684 static void DoDbg (void)
685 /* Add debug information from high level code */
687 static const char* Keys[] = {
695 /* We expect a subkey */
696 if (Tok != TOK_IDENT) {
697 ErrorSkip ("Identifier expected");
701 /* Map the following keyword to a number */
702 Key = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
704 /* Skip the subkey */
707 /* Check the key and dispatch to a handler */
709 case 0: DbgInfoFile (); break;
710 case 1: DbgInfoLine (); break;
711 case 2: DbgInfoSym (); break;
712 default: ErrorSkip ("Syntax error"); break;
718 static void DoDByt (void)
719 /* Output double bytes */
722 EmitWord (GenSwapExpr (Expression ()));
723 if (Tok != TOK_COMMA) {
733 static void DoDebugInfo (void)
734 /* Switch debug info on or off */
736 SetBoolOption (&DbgSyms);
741 static void DoDefine (void)
742 /* Define a one line macro */
744 MacDef (MAC_STYLE_DEFINE);
749 static void DoDestructor (void)
750 /* Export a symbol as destructor */
752 StrBuf Name = STATIC_STRBUF_INITIALIZER;
754 /* Symbol name follows */
755 if (Tok != TOK_IDENT) {
756 ErrorSkip ("Identifier expected");
759 SB_Copy (&Name, &SVal);
762 /* Parse the remainder of the line and export the symbol */
763 ConDes (&Name, CD_TYPE_DES);
765 /* Free string memory */
771 static void DoDWord (void)
775 EmitDWord (Expression ());
776 if (Tok != TOK_COMMA) {
786 static void DoEnd (void)
787 /* End of assembly */
795 static void DoEndProc (void)
796 /* Leave a lexical level */
798 if (GetCurrentSymTabType () != ST_PROC) {
800 ErrorSkip ("No open .PROC");
808 static void DoEndScope (void)
809 /* Leave a lexical level */
811 if ( GetCurrentSymTabType () != ST_SCOPE) {
813 ErrorSkip ("No open .SCOPE");
821 static void DoError (void)
824 if (Tok != TOK_STRCON) {
825 ErrorSkip ("String constant expected");
827 Error ("User error: %m%p", &SVal);
834 static void DoExitMacro (void)
835 /* Exit a macro expansion */
837 if (!InMacExpansion ()) {
838 /* We aren't expanding a macro currently */
847 static void DoExport (void)
848 /* Export a symbol */
850 ExportImport (ExportWithAssign, ADDR_SIZE_DEFAULT, SF_NONE);
855 static void DoExportZP (void)
856 /* Export a zeropage symbol */
858 ExportImport (ExportWithAssign, ADDR_SIZE_ZP, SF_NONE);
863 static void DoFarAddr (void)
864 /* Define far addresses (24 bit) */
867 EmitFarAddr (Expression ());
868 if (Tok != TOK_COMMA) {
878 static void DoFeature (void)
879 /* Switch the Feature option */
881 /* Allow a list of comma separated keywords */
884 /* We expect an identifier */
885 if (Tok != TOK_IDENT) {
886 ErrorSkip ("Identifier expected");
890 /* Make the string attribute lower case */
893 /* Set the feature and check for errors */
894 if (SetFeature (&SVal) == FEAT_UNKNOWN) {
896 ErrorSkip ("Invalid feature: `%m%p'", &SVal);
899 /* Skip the keyword */
903 /* Allow more than one keyword */
904 if (Tok == TOK_COMMA) {
914 static void DoFileOpt (void)
915 /* Insert a file option */
919 /* The option type may be given as a keyword or as a number. */
920 if (Tok == TOK_IDENT) {
922 /* Option given as keyword */
923 static const char* Keys [] = {
924 "AUTHOR", "COMMENT", "COMPILER"
927 /* Map the option to a number */
928 OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
931 ErrorSkip ("File option keyword expected");
935 /* Skip the keyword */
938 /* Must be followed by a comma */
941 /* We accept only string options for now */
942 if (Tok != TOK_STRCON) {
943 ErrorSkip ("String constant expected");
947 /* Insert the option */
966 Internal ("Invalid OptNum: %ld", OptNum);
975 /* Option given as number */
976 OptNum = ConstExpression ();
977 if (!IsByteRange (OptNum)) {
978 ErrorSkip ("Range error");
982 /* Must be followed by a comma */
985 /* We accept only string options for now */
986 if (Tok != TOK_STRCON) {
987 ErrorSkip ("String constant expected");
991 /* Insert the option */
992 OptStr ((unsigned char) OptNum, &SVal);
1001 static void DoForceImport (void)
1002 /* Do a forced import on a symbol */
1004 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_FORCED);
1009 static void DoGlobal (void)
1010 /* Declare a global symbol */
1012 ExportImport (SymGlobal, ADDR_SIZE_DEFAULT, SF_NONE);
1017 static void DoGlobalZP (void)
1018 /* Declare a global zeropage symbol */
1020 ExportImport (SymGlobal, ADDR_SIZE_ZP, SF_NONE);
1024 static void DoHiBytes (void)
1025 /* Define bytes, extracting the hi byte from each expression in the list */
1028 EmitByte (FuncHiByte ());
1029 if (Tok != TOK_COMMA) {
1039 static void DoI16 (void)
1040 /* Switch the index registers to 16 bit mode (assembler only) */
1042 if (GetCPU() != CPU_65816) {
1043 Error ("Command is only valid in 65816 mode");
1045 /* Immidiate mode has two extension bytes */
1046 ExtBytes [AM65I_IMM_INDEX] = 2;
1052 static void DoI8 (void)
1053 /* Switch the index registers to 16 bit mode (assembler only) */
1055 if (GetCPU() != CPU_65816) {
1056 Error ("Command is only valid in 65816 mode");
1058 /* Immidiate mode has one extension byte */
1059 ExtBytes [AM65I_IMM_INDEX] = 1;
1065 static void DoImport (void)
1066 /* Import a symbol */
1068 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_NONE);
1073 static void DoImportZP (void)
1074 /* Import a zero page symbol */
1076 ExportImport (SymImport, ADDR_SIZE_ZP, SF_NONE);
1081 static void DoIncBin (void)
1082 /* Include a binary file */
1084 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1090 /* Name must follow */
1091 if (Tok != TOK_STRCON) {
1092 ErrorSkip ("String constant expected");
1095 SB_Copy (&Name, &SVal);
1096 SB_Terminate (&Name);
1099 /* A starting offset may follow */
1100 if (Tok == TOK_COMMA) {
1102 Start = ConstExpression ();
1104 /* And a length may follow */
1105 if (Tok == TOK_COMMA) {
1107 Count = ConstExpression ();
1112 /* Try to open the file */
1113 F = fopen (SB_GetConstBuf (&Name), "rb");
1116 /* Search for the file in the binary include directory */
1117 char* PathName = FindInclude (SB_GetConstBuf (&Name), INC_BIN);
1118 if (PathName == 0 || (F = fopen (PathName, "rb")) == 0) {
1119 /* Not found or cannot open, print an error and bail out */
1120 ErrorSkip ("Cannot open include file `%m%p': %s", &Name, strerror (errno));
1123 /* Free the allocated memory */
1126 /* If we had an error before, bail out now */
1132 /* Get the size of the file */
1133 fseek (F, 0, SEEK_END);
1136 /* If a count was not given, calculate it now */
1138 Count = Size - Start;
1140 /* Nothing to read - flag this as a range error */
1141 ErrorSkip ("Range error");
1145 /* Count was given, check if it is valid */
1146 if (Start + Count > Size) {
1147 ErrorSkip ("Range error");
1152 /* Seek to the start position */
1153 fseek (F, Start, SEEK_SET);
1155 /* Read chunks and insert them into the output */
1158 unsigned char Buf [1024];
1160 /* Calculate the number of bytes to read */
1161 size_t BytesToRead = (Count > (long)sizeof(Buf))? sizeof(Buf) : (size_t) Count;
1164 size_t BytesRead = fread (Buf, 1, BytesToRead, F);
1165 if (BytesToRead != BytesRead) {
1166 /* Some sort of error */
1167 ErrorSkip ("Cannot read from include file `%m%p': %s",
1168 &Name, strerror (errno));
1172 /* Insert it into the output */
1173 EmitData (Buf, BytesRead);
1175 /* Keep the counters current */
1180 /* Close the file, ignore errors since it's r/o */
1184 /* Free string memory */
1190 static void DoInclude (void)
1191 /* Include another file */
1193 /* Name must follow */
1194 if (Tok != TOK_STRCON) {
1195 ErrorSkip ("String constant expected");
1197 SB_Terminate (&SVal);
1198 if (NewInputFile (SB_GetConstBuf (&SVal)) == 0) {
1199 /* Error opening the file, skip remainder of line */
1207 static void DoInterruptor (void)
1208 /* Export a symbol as interruptor */
1210 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1212 /* Symbol name follows */
1213 if (Tok != TOK_IDENT) {
1214 ErrorSkip ("Identifier expected");
1217 SB_Copy (&Name, &SVal);
1220 /* Parse the remainder of the line and export the symbol */
1221 ConDes (&Name, CD_TYPE_INT);
1223 /* Free string memory */
1229 static void DoInvalid (void)
1230 /* Handle a token that is invalid here, since it should have been handled on
1231 * a much lower level of the expression hierarchy. Getting this sort of token
1232 * means that the lower level code has bugs.
1233 * This function differs to DoUnexpected in that the latter may be triggered
1234 * by the user by using keywords in the wrong location. DoUnexpected is not
1235 * an error in the assembler itself, while DoInvalid is.
1238 Internal ("Unexpected token: %m%p", &Keyword);
1243 static void DoLineCont (void)
1244 /* Switch the use of line continuations */
1246 SetBoolOption (&LineCont);
1251 static void DoList (void)
1252 /* Enable/disable the listing */
1254 /* Get the setting */
1256 SetBoolOption (&List);
1258 /* Manage the counter */
1268 static void DoLoBytes (void)
1269 /* Define bytes, extracting the lo byte from each expression in the list */
1272 EmitByte (FuncLoByte ());
1273 if (Tok != TOK_COMMA) {
1282 static void DoListBytes (void)
1283 /* Set maximum number of bytes to list for one line */
1285 SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
1290 static void DoLocalChar (void)
1291 /* Define the character that starts local labels */
1293 if (Tok != TOK_CHARCON) {
1294 ErrorSkip ("Character constant expected");
1296 if (IVal != '@' && IVal != '?') {
1297 Error ("Invalid start character for locals");
1299 LocalStart = (char) IVal;
1307 static void DoMacPack (void)
1308 /* Insert a macro package */
1312 /* We expect an identifier */
1313 if (Tok != TOK_IDENT) {
1314 ErrorSkip ("Identifier expected");
1318 /* Search for the macro package name */
1320 Package = MacPackFind (&SVal);
1323 ErrorSkip ("Invalid macro package");
1327 /* Insert the package. If this fails, skip the remainder of the line to
1328 * avoid additional error messages.
1330 if (MacPackInsert (Package) == 0) {
1337 static void DoMacro (void)
1338 /* Start a macro definition */
1340 MacDef (MAC_STYLE_CLASSIC);
1345 static void DoNull (void)
1346 /* Switch to the NULL segment */
1348 UseSeg (&NullSegDef);
1353 static void DoOrg (void)
1354 /* Start absolute code */
1356 long PC = ConstExpression ();
1357 if (PC < 0 || PC > 0xFFFFFF) {
1358 Error ("Range error");
1361 EnterAbsoluteMode (PC);
1366 static void DoOut (void)
1367 /* Output a string */
1369 if (Tok != TOK_STRCON) {
1370 ErrorSkip ("String constant expected");
1372 /* Output the string and be sure to flush the output to keep it in
1373 * sync with any error messages if the output is redirected to a file.
1375 printf ("%.*s\n", (int) SB_GetLen (&SVal), SB_GetConstBuf (&SVal));
1383 static void DoP02 (void)
1384 /* Switch to 6502 CPU */
1391 static void DoPC02 (void)
1392 /* Switch to 65C02 CPU */
1399 static void DoP816 (void)
1400 /* Switch to 65816 CPU */
1407 static void DoPageLength (void)
1408 /* Set the page length for the listing */
1410 PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
1415 static void DoPopSeg (void)
1416 /* Pop an old segment from the segment stack */
1420 /* Must have a segment on the stack */
1421 if (CollCount (&SegStack) == 0) {
1422 ErrorSkip ("Segment stack is empty");
1426 /* Pop the last element */
1427 Def = CollPop (&SegStack);
1429 /* Restore this segment */
1432 /* Delete the segment definition */
1438 static void DoProc (void)
1439 /* Start a new lexical scope */
1441 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1442 unsigned char AddrSize;
1444 if (Tok == TOK_IDENT) {
1448 /* The new scope has a name. Remember it. */
1449 SB_Copy (&Name, &SVal);
1451 /* Search for the symbol, generate a new one if needed */
1452 Sym = SymFind (CurrentScope, &Name, SYM_ALLOC_NEW);
1454 /* Skip the scope name */
1457 /* Read an optional address size specifier */
1458 AddrSize = OptionalAddrSize ();
1460 /* Mark the symbol as defined */
1461 SymDef (Sym, GenCurrentPC (), AddrSize, SF_LABEL);
1465 /* A .PROC statement without a name */
1466 Warning (1, "Unnamed .PROCs are deprecated, please use .SCOPE");
1467 AnonName (&Name, "PROC");
1468 AddrSize = ADDR_SIZE_DEFAULT;
1472 /* Enter a new scope */
1473 SymEnterLevel (&Name, ST_PROC, AddrSize);
1475 /* Free memory for Name */
1481 static void DoPSC02 (void)
1482 /* Switch to 65SC02 CPU */
1484 SetCPU (CPU_65SC02);
1489 static void DoPushSeg (void)
1490 /* Push the current segment onto the segment stack */
1492 /* Can only push a limited size of segments */
1493 if (CollCount (&SegStack) >= MAX_PUSHED_SEGMENTS) {
1494 ErrorSkip ("Segment stack overflow");
1498 /* Get the current segment and push it */
1499 CollAppend (&SegStack, DupSegDef (GetCurrentSegDef ()));
1504 static void DoReloc (void)
1505 /* Enter relocatable mode */
1512 static void DoRepeat (void)
1513 /* Repeat some instruction block */
1520 static void DoRes (void)
1521 /* Reserve some number of storage bytes */
1526 Count = ConstExpression ();
1527 if (Count > 0xFFFF || Count < 0) {
1528 ErrorSkip ("Range error");
1531 if (Tok == TOK_COMMA) {
1533 Val = ConstExpression ();
1534 /* We need a byte value here */
1535 if (!IsByteRange (Val)) {
1536 ErrorSkip ("Range error");
1540 /* Emit constant values */
1542 Emit0 ((unsigned char) Val);
1546 /* Emit fill fragments */
1553 static void DoROData (void)
1554 /* Switch to the r/o data segment */
1556 UseSeg (&RODataSegDef);
1561 static void DoScope (void)
1562 /* Start a local scope */
1564 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1565 unsigned char AddrSize;
1568 if (Tok == TOK_IDENT) {
1570 /* The new scope has a name. Remember and skip it. */
1571 SB_Copy (&Name, &SVal);
1576 /* An unnamed scope */
1577 AnonName (&Name, "SCOPE");
1581 /* Read an optional address size specifier */
1582 AddrSize = OptionalAddrSize ();
1584 /* Enter the new scope */
1585 SymEnterLevel (&Name, ST_SCOPE, AddrSize);
1587 /* Free memory for Name */
1593 static void DoSegment (void)
1594 /* Switch to another segment */
1596 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1599 if (Tok != TOK_STRCON) {
1600 ErrorSkip ("String constant expected");
1603 /* Save the name of the segment and skip it */
1604 SB_Copy (&Name, &SVal);
1607 /* Use the name for the segment definition */
1608 SB_Terminate (&Name);
1609 Def.Name = SB_GetBuf (&Name);
1611 /* Check for an optional address size modifier */
1612 Def.AddrSize = OptionalAddrSize ();
1614 /* Set the segment */
1618 /* Free memory for Name */
1624 static void DoSetCPU (void)
1625 /* Switch the CPU instruction set */
1627 /* We expect an identifier */
1628 if (Tok != TOK_STRCON) {
1629 ErrorSkip ("String constant expected");
1633 /* Try to find the CPU */
1634 SB_Terminate (&SVal);
1635 CPU = FindCPU (SB_GetConstBuf (&SVal));
1637 /* Switch to the new CPU */
1640 /* Skip the identifier. If the CPU switch was successful, the scanner
1641 * will treat the input now correctly for the new CPU.
1649 static void DoSmart (void)
1650 /* Smart mode on/off */
1652 SetBoolOption (&SmartMode);
1657 static void DoSunPlus (void)
1658 /* Switch to the SUNPLUS CPU */
1660 SetCPU (CPU_SUNPLUS);
1665 static void DoTag (void)
1666 /* Allocate space for a struct */
1671 /* Read the struct name */
1672 SymTable* Struct = ParseScopedSymTable ();
1674 /* Check the supposed struct */
1676 ErrorSkip ("Unknown struct");
1679 if (GetSymTabType (Struct) != ST_STRUCT) {
1680 ErrorSkip ("Not a struct");
1684 /* Get the symbol that defines the size of the struct */
1685 SizeSym = GetSizeOfScope (Struct);
1687 /* Check if it does exist and if its value is known */
1688 if (SizeSym == 0 || !SymIsConst (SizeSym, &Size)) {
1689 ErrorSkip ("Size of struct/union is unknown");
1693 /* Optional multiplicator may follow */
1694 if (Tok == TOK_COMMA) {
1697 Multiplicator = ConstExpression ();
1698 /* Multiplicator must make sense */
1699 if (Multiplicator <= 0) {
1700 ErrorSkip ("Range error");
1703 Size *= Multiplicator;
1706 /* Emit fill fragments */
1712 static void DoUnexpected (void)
1713 /* Got an unexpected keyword */
1715 Error ("Unexpected `%m%p'", &Keyword);
1721 static void DoWarning (void)
1724 if (Tok != TOK_STRCON) {
1725 ErrorSkip ("String constant expected");
1727 Warning (0, "User warning: %m%p", &SVal);
1734 static void DoWord (void)
1738 EmitWord (Expression ());
1739 if (Tok != TOK_COMMA) {
1749 static void DoZeropage (void)
1750 /* Switch to the zeropage segment */
1752 UseSeg (&ZeropageSegDef);
1757 /*****************************************************************************/
1759 /*****************************************************************************/
1763 /* Control commands flags */
1765 ccNone = 0x0000, /* No special flags */
1766 ccKeepToken = 0x0001 /* Do not skip the current token */
1769 /* Control command table */
1770 typedef struct CtrlDesc CtrlDesc;
1772 unsigned Flags; /* Flags for this directive */
1773 void (*Handler) (void); /* Command handler */
1776 #define PSEUDO_COUNT (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1777 static CtrlDesc CtrlCmdTab [] = {
1780 { ccNone, DoAddr }, /* .ADDR */
1781 { ccNone, DoAlign },
1782 { ccNone, DoASCIIZ },
1783 { ccNone, DoAssert },
1784 { ccNone, DoAutoImport },
1785 { ccNone, DoUnexpected }, /* .BANKBYTE */
1786 { ccNone, DoBankBytes },
1787 { ccNone, DoUnexpected }, /* .BLANK */
1791 { ccNone, DoCharMap },
1793 { ccNone, DoUnexpected, }, /* .CONCAT */
1794 { ccNone, DoConDes },
1795 { ccNone, DoUnexpected }, /* .CONST */
1796 { ccNone, DoConstructor },
1797 { ccNone, DoUnexpected }, /* .CPU */
1801 { ccNone, DoDebugInfo },
1802 { ccNone, DoDefine },
1803 { ccNone, DoUnexpected }, /* .DEFINED */
1804 { ccNone, DoDestructor },
1805 { ccNone, DoDWord },
1806 { ccKeepToken, DoConditionals }, /* .ELSE */
1807 { ccKeepToken, DoConditionals }, /* .ELSEIF */
1808 { ccKeepToken, DoEnd },
1809 { ccNone, DoUnexpected }, /* .ENDENUM */
1810 { ccKeepToken, DoConditionals }, /* .ENDIF */
1811 { ccNone, DoUnexpected }, /* .ENDMACRO */
1812 { ccNone, DoEndProc },
1813 { ccNone, DoUnexpected }, /* .ENDREPEAT */
1814 { ccNone, DoEndScope },
1815 { ccNone, DoUnexpected }, /* .ENDSTRUCT */
1816 { ccNone, DoUnexpected }, /* .ENDUNION */
1818 { ccNone, DoError },
1819 { ccNone, DoExitMacro },
1820 { ccNone, DoExport },
1821 { ccNone, DoExportZP },
1822 { ccNone, DoFarAddr },
1823 { ccNone, DoFeature },
1824 { ccNone, DoFileOpt },
1825 { ccNone, DoForceImport },
1826 { ccNone, DoUnexpected }, /* .FORCEWORD */
1827 { ccNone, DoGlobal },
1828 { ccNone, DoGlobalZP },
1829 { ccNone, DoUnexpected }, /* .HIBYTE */
1830 { ccNone, DoHiBytes },
1831 { ccNone, DoUnexpected }, /* .HIWORD */
1834 { ccNone, DoUnexpected }, /* .IDENT */
1835 { ccKeepToken, DoConditionals }, /* .IF */
1836 { ccKeepToken, DoConditionals }, /* .IFBLANK */
1837 { ccKeepToken, DoConditionals }, /* .IFCONST */
1838 { ccKeepToken, DoConditionals }, /* .IFDEF */
1839 { ccKeepToken, DoConditionals }, /* .IFNBLANK */
1840 { ccKeepToken, DoConditionals }, /* .IFNCONST */
1841 { ccKeepToken, DoConditionals }, /* .IFNDEF */
1842 { ccKeepToken, DoConditionals }, /* .IFNREF */
1843 { ccKeepToken, DoConditionals }, /* .IFP02 */
1844 { ccKeepToken, DoConditionals }, /* .IFP816 */
1845 { ccKeepToken, DoConditionals }, /* .IFPC02 */
1846 { ccKeepToken, DoConditionals }, /* .IFPSC02 */
1847 { ccKeepToken, DoConditionals }, /* .IFREF */
1848 { ccNone, DoImport },
1849 { ccNone, DoImportZP },
1850 { ccNone, DoIncBin },
1851 { ccNone, DoInclude },
1852 { ccNone, DoInterruptor },
1853 { ccNone, DoInvalid }, /* .LEFT */
1854 { ccNone, DoLineCont },
1856 { ccNone, DoListBytes },
1857 { ccNone, DoUnexpected }, /* .LOBYTE */
1858 { ccNone, DoLoBytes },
1859 { ccNone, DoUnexpected }, /* .LOCAL */
1860 { ccNone, DoLocalChar },
1861 { ccNone, DoUnexpected }, /* .LOWORD */
1862 { ccNone, DoMacPack },
1863 { ccNone, DoMacro },
1864 { ccNone, DoUnexpected }, /* .MATCH */
1865 { ccNone, DoInvalid }, /* .MID */
1871 { ccNone, DoPageLength },
1872 { ccNone, DoUnexpected }, /* .PARAMCOUNT */
1874 { ccNone, DoPopSeg },
1876 { ccNone, DoPSC02 },
1877 { ccNone, DoPushSeg },
1878 { ccNone, DoUnexpected }, /* .REFERENCED */
1879 { ccNone, DoReloc },
1880 { ccNone, DoRepeat },
1882 { ccNone, DoInvalid }, /* .RIGHT */
1883 { ccNone, DoROData },
1884 { ccNone, DoScope },
1885 { ccNone, DoSegment },
1886 { ccNone, DoUnexpected }, /* .SET */
1887 { ccNone, DoSetCPU },
1888 { ccNone, DoUnexpected }, /* .SIZEOF */
1889 { ccNone, DoSmart },
1890 { ccNone, DoUnexpected }, /* .SPRINTF */
1891 { ccNone, DoUnexpected }, /* .STRAT */
1892 { ccNone, DoUnexpected }, /* .STRING */
1893 { ccNone, DoUnexpected }, /* .STRLEN */
1894 { ccNone, DoStruct },
1895 { ccNone, DoSunPlus },
1897 { ccNone, DoUnexpected }, /* .TCOUNT */
1898 { ccNone, DoUnexpected }, /* .TIME */
1899 { ccNone, DoUnion },
1900 { ccNone, DoUnexpected }, /* .VERSION */
1901 { ccNone, DoWarning },
1903 { ccNone, DoUnexpected }, /* .XMATCH */
1904 { ccNone, DoZeropage },
1909 /*****************************************************************************/
1911 /*****************************************************************************/
1915 void HandlePseudo (void)
1916 /* Handle a pseudo instruction */
1920 /* Calculate the index into the table */
1921 unsigned Index = Tok - TOK_FIRSTPSEUDO;
1924 if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
1925 Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
1926 (unsigned) PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
1928 CHECK (Index < PSEUDO_COUNT);
1930 /* Get the pseudo intruction descriptor */
1931 D = &CtrlCmdTab [Index];
1933 /* Remember the instruction, then skip it if needed */
1934 if ((D->Flags & ccKeepToken) == 0) {
1935 SB_Copy (&Keyword, &SVal);
1939 /* Call the handler */
1945 void SegStackCheck (void)
1946 /* Check if the segment stack is empty at end of assembly */
1948 if (CollCount (&SegStack) != 0) {
1949 Error ("Segment stack is not empty");