1 /*****************************************************************************/
5 /* Pseudo instructions for the ca65 macroassembler */
9 /* (C) 1998-2011, 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 /*****************************************************************************/
41 #include <sys/types.h> /* EMX needs this */
45 #include "assertion.h"
84 /*****************************************************************************/
86 /*****************************************************************************/
90 /* Keyword we're about to handle */
91 static StrBuf Keyword = STATIC_STRBUF_INITIALIZER;
94 static IntStack CPUStack = STATIC_INTSTACK_INITIALIZER;
97 #define MAX_PUSHED_SEGMENTS 16
98 static Collection SegStack = STATIC_COLLECTION_INITIALIZER;
102 /*****************************************************************************/
104 /*****************************************************************************/
108 static void DoUnexpected (void);
109 /* Got an unexpected keyword */
111 static void DoInvalid (void);
112 /* Handle a token that is invalid here, since it should have been handled on
113 * a much lower level of the expression hierarchy. Getting this sort of token
114 * means that the lower level code has bugs.
115 * This function differs to DoUnexpected in that the latter may be triggered
116 * by the user by using keywords in the wrong location. DoUnexpected is not
117 * an error in the assembler itself, while DoInvalid is.
122 /*****************************************************************************/
123 /* Helper functions */
124 /*****************************************************************************/
128 static unsigned char OptionalAddrSize (void)
129 /* If a colon follows, parse an optional address size spec and return it.
130 * Otherwise return ADDR_SIZE_DEFAULT.
133 unsigned AddrSize = ADDR_SIZE_DEFAULT;
134 if (CurTok.Tok == TOK_COLON) {
136 AddrSize = ParseAddrSize ();
137 if (!ValidAddrSizeForCPU (AddrSize)) {
138 /* Print an error and reset to default */
139 Error ("Invalid address size specification for current CPU");
140 AddrSize = ADDR_SIZE_DEFAULT;
149 static void SetBoolOption (unsigned char* Flag)
150 /* Read a on/off/+/- option and set flag accordingly */
152 static const char* Keys[] = {
157 if (CurTok.Tok == TOK_PLUS) {
160 } else if (CurTok.Tok == TOK_MINUS) {
163 } else if (CurTok.Tok == TOK_IDENT) {
164 /* Map the keyword to a number */
165 switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
166 case 0: *Flag = 0; NextTok (); break;
167 case 1: *Flag = 1; NextTok (); break;
168 default: ErrorSkip ("`on' or `off' expected"); break;
170 } else if (TokIsSep (CurTok.Tok)) {
171 /* Without anything assume switch on */
174 ErrorSkip ("`on' or `off' expected");
180 static void ExportWithAssign (SymEntry* Sym, unsigned char AddrSize, unsigned Flags)
181 /* Allow to assign the value of an export in an .export statement */
183 /* The name and optional address size spec may be followed by an assignment
186 if (CurTok.Tok == TOK_ASSIGN || CurTok.Tok == TOK_EQ) {
188 /* Assignment means the symbol is a label */
189 if (CurTok.Tok == TOK_ASSIGN) {
193 /* Skip the assignment token */
196 /* Define the symbol with the expression following the '=' */
197 SymDef (Sym, Expression(), ADDR_SIZE_DEFAULT, Flags);
201 /* Now export the symbol */
202 SymExport (Sym, AddrSize, Flags);
207 static void ExportImport (void (*Func) (SymEntry*, unsigned char, unsigned),
208 unsigned char DefAddrSize, unsigned Flags)
209 /* Export or import symbols */
212 unsigned char AddrSize;
216 /* We need an identifier here */
217 if (CurTok.Tok != TOK_IDENT) {
218 ErrorSkip ("Identifier expected");
222 /* Find the symbol table entry, allocate a new one if necessary */
223 Sym = SymFind (CurrentScope, &CurTok.SVal, SYM_ALLOC_NEW);
228 /* Get an optional address size */
229 AddrSize = OptionalAddrSize ();
230 if (AddrSize == ADDR_SIZE_DEFAULT) {
231 AddrSize = DefAddrSize;
234 /* Call the actual import/export function */
235 Func (Sym, AddrSize, Flags);
238 if (CurTok.Tok == TOK_COMMA) {
248 static long IntArg (long Min, long Max)
249 /* Read an integer argument and check a range. Accept the token "unlimited"
250 * and return -1 in this case.
253 if (CurTok.Tok == TOK_IDENT && SB_CompareStr (&CurTok.SVal, "unlimited") == 0) {
257 long Val = ConstExpression ();
258 if (Val < Min || Val > Max) {
259 Error ("Range error");
268 static void ConDes (const StrBuf* Name, unsigned Type)
269 /* Parse remaining line for constructor/destructor of the remaining type */
274 /* Find the symbol table entry, allocate a new one if necessary */
275 SymEntry* Sym = SymFind (CurrentScope, Name, SYM_ALLOC_NEW);
277 /* Optional constructor priority */
278 if (CurTok.Tok == TOK_COMMA) {
279 /* Priority value follows */
281 Prio = ConstExpression ();
282 if (Prio < CD_PRIO_MIN || Prio > CD_PRIO_MAX) {
283 /* Value out of range */
284 Error ("Range error");
288 /* Use the default priority value */
292 /* Define the symbol */
293 SymConDes (Sym, ADDR_SIZE_DEFAULT, Type, (unsigned) Prio);
298 /*****************************************************************************/
299 /* Handler functions */
300 /*****************************************************************************/
304 static void DoA16 (void)
305 /* Switch the accu to 16 bit mode (assembler only) */
307 if (GetCPU() != CPU_65816) {
308 Error ("Command is only valid in 65816 mode");
310 /* Immidiate mode has two extension bytes */
311 ExtBytes [AM65I_IMM_ACCU] = 2;
317 static void DoA8 (void)
318 /* Switch the accu to 8 bit mode (assembler only) */
320 if (GetCPU() != CPU_65816) {
321 Error ("Command is only valid in 65816 mode");
323 /* Immidiate mode has one extension byte */
324 ExtBytes [AM65I_IMM_ACCU] = 1;
330 static void DoAddr (void)
331 /* Define addresses */
334 if (GetCPU() == CPU_65816) {
335 EmitWord (GenWordExpr (Expression ()));
337 /* Do a range check */
338 EmitWord (Expression ());
340 if (CurTok.Tok != TOK_COMMA) {
350 static void DoAlign (void)
351 /* Align the PC to some boundary */
357 /* Read the alignment value */
358 Align = ConstExpression ();
359 if (Align <= 0 || Align > 0x10000) {
360 ErrorSkip ("Range error");
364 /* Optional value follows */
365 if (CurTok.Tok == TOK_COMMA) {
367 Val = ConstExpression ();
368 /* We need a byte value here */
369 if (!IsByteRange (Val)) {
370 ErrorSkip ("Range error");
377 /* Check if the alignment is a power of two */
378 Bit = BitFind (Align);
379 if (Align != (0x01L << Bit)) {
380 Error ("Alignment value must be a power of 2");
382 SegAlign (Bit, (int) Val);
388 static void DoASCIIZ (void)
389 /* Define text with a zero terminator */
392 /* Must have a string constant */
393 if (CurTok.Tok != TOK_STRCON) {
394 ErrorSkip ("String constant expected");
398 /* Translate into target charset and emit */
399 TgtTranslateStrBuf (&CurTok.SVal);
400 EmitStrBuf (&CurTok.SVal);
402 if (CurTok.Tok == TOK_COMMA) {
413 static void DoAssert (void)
414 /* Add an assertion */
416 static const char* ActionTab [] = {
419 "LDWARN", "LDWARNING",
426 /* First we have the expression that has to evaluated */
427 ExprNode* Expr = Expression ();
431 if (CurTok.Tok != TOK_IDENT) {
432 ErrorSkip ("Identifier expected");
435 switch (GetSubKey (ActionTab, sizeof (ActionTab) / sizeof (ActionTab[0]))) {
440 Action = ASSERT_ACT_WARN;
445 Action = ASSERT_ACT_ERROR;
451 Action = ASSERT_ACT_LDWARN;
456 Action = ASSERT_ACT_LDERROR;
460 Error ("Illegal assert action specifier");
461 /* Use lderror - there won't be an .o file anyway */
462 Action = ASSERT_ACT_LDERROR;
468 /* We can have an optional message. If no message is present, use
469 * "Assertion failed".
471 if (CurTok.Tok == TOK_COMMA) {
476 /* Read the message */
477 if (CurTok.Tok != TOK_STRCON) {
478 ErrorSkip ("String constant expected");
482 /* Translate the message into a string id. We can then skip the input
485 Msg = GetStrBufId (&CurTok.SVal);
490 /* Use "Assertion failed" */
491 Msg = GetStringId ("Assertion failed");
495 /* Remember the assertion */
496 AddAssertion (Expr, (AssertAction) Action, Msg);
501 static void DoAutoImport (void)
502 /* Mark unresolved symbols as imported */
504 SetBoolOption (&AutoImport);
508 static void DoBankBytes (void)
509 /* Define bytes, extracting the bank byte from each expression in the list */
512 EmitByte (FuncBankByte ());
513 if (CurTok.Tok != TOK_COMMA) {
523 static void DoBss (void)
524 /* Switch to the BSS segment */
531 static void DoByte (void)
535 if (CurTok.Tok == TOK_STRCON) {
536 /* A string, translate into target charset and emit */
537 TgtTranslateStrBuf (&CurTok.SVal);
538 EmitStrBuf (&CurTok.SVal);
541 EmitByte (Expression ());
543 if (CurTok.Tok != TOK_COMMA) {
547 /* Do smart handling of dangling comma */
548 if (CurTok.Tok == TOK_SEP) {
549 Error ("Unexpected end of line");
558 static void DoCase (void)
559 /* Switch the IgnoreCase option */
561 SetBoolOption (&IgnoreCase);
562 IgnoreCase = !IgnoreCase;
567 static void DoCharMap (void)
568 /* Allow custome character mappings */
573 /* Read the index as numerical value */
574 Index = ConstExpression ();
575 if (Index < 0 || Index > 255) {
576 /* Value out of range */
577 ErrorSkip ("Range error");
584 /* Read the character code */
585 Code = ConstExpression ();
586 if (Code < 0 || Code > 255) {
587 /* Value out of range */
588 ErrorSkip ("Range error");
592 /* Set the character translation */
593 TgtTranslateSet ((unsigned) Index, (unsigned char) Code);
598 static void DoCode (void)
599 /* Switch to the code segment */
601 UseSeg (&CodeSegDef);
606 static void DoConDes (void)
607 /* Export a symbol as constructor/destructor */
609 static const char* Keys[] = {
614 StrBuf Name = STATIC_STRBUF_INITIALIZER;
617 /* Symbol name follows */
618 if (CurTok.Tok != TOK_IDENT) {
619 ErrorSkip ("Identifier expected");
622 SB_Copy (&Name, &CurTok.SVal);
625 /* Type follows. May be encoded as identifier or numerical */
627 if (CurTok.Tok == TOK_IDENT) {
629 /* Map the following keyword to a number, then skip it */
630 Type = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
633 /* Check if we got a valid keyword */
635 ErrorSkip ("Syntax error");
641 /* Read the type as numerical value */
642 Type = ConstExpression ();
643 if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) {
644 /* Value out of range */
645 ErrorSkip ("Range error");
651 /* Parse the remainder of the line and export the symbol */
652 ConDes (&Name, (unsigned) Type);
655 /* Free string memory */
661 static void DoConstructor (void)
662 /* Export a symbol as constructor */
664 StrBuf Name = STATIC_STRBUF_INITIALIZER;
666 /* Symbol name follows */
667 if (CurTok.Tok != TOK_IDENT) {
668 ErrorSkip ("Identifier expected");
671 SB_Copy (&Name, &CurTok.SVal);
674 /* Parse the remainder of the line and export the symbol */
675 ConDes (&Name, CD_TYPE_CON);
677 /* Free string memory */
683 static void DoData (void)
684 /* Switch to the data segment */
686 UseSeg (&DataSegDef);
691 static void DoDbg (void)
692 /* Add debug information from high level code */
694 static const char* Keys[] = {
702 /* We expect a subkey */
703 if (CurTok.Tok != TOK_IDENT) {
704 ErrorSkip ("Identifier expected");
708 /* Map the following keyword to a number */
709 Key = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
711 /* Skip the subkey */
714 /* Check the key and dispatch to a handler */
716 case 0: DbgInfoFile (); break;
717 case 1: DbgInfoLine (); break;
718 case 2: DbgInfoSym (); break;
719 default: ErrorSkip ("Syntax error"); break;
725 static void DoDByt (void)
726 /* Output double bytes */
729 EmitWord (GenSwapExpr (Expression ()));
730 if (CurTok.Tok != TOK_COMMA) {
740 static void DoDebugInfo (void)
741 /* Switch debug info on or off */
743 SetBoolOption (&DbgSyms);
748 static void DoDefine (void)
749 /* Define a one line macro */
751 MacDef (MAC_STYLE_DEFINE);
756 static void DoDelMac (void)
757 /* Delete a classic macro */
759 /* We expect an identifier */
760 if (CurTok.Tok != TOK_IDENT) {
761 ErrorSkip ("Identifier expected");
763 MacUndef (&CurTok.SVal, MAC_STYLE_CLASSIC);
770 static void DoDestructor (void)
771 /* Export a symbol as destructor */
773 StrBuf Name = STATIC_STRBUF_INITIALIZER;
775 /* Symbol name follows */
776 if (CurTok.Tok != TOK_IDENT) {
777 ErrorSkip ("Identifier expected");
780 SB_Copy (&Name, &CurTok.SVal);
783 /* Parse the remainder of the line and export the symbol */
784 ConDes (&Name, CD_TYPE_DES);
786 /* Free string memory */
792 static void DoDWord (void)
796 EmitDWord (Expression ());
797 if (CurTok.Tok != TOK_COMMA) {
807 static void DoEnd (void)
808 /* End of assembly */
816 static void DoEndProc (void)
817 /* Leave a lexical level */
819 if (GetCurrentSymTabType () != ST_PROC) {
821 ErrorSkip ("No open .PROC");
829 static void DoEndScope (void)
830 /* Leave a lexical level */
832 if ( GetCurrentSymTabType () != ST_SCOPE) {
834 ErrorSkip ("No open .SCOPE");
842 static void DoError (void)
845 if (CurTok.Tok != TOK_STRCON) {
846 ErrorSkip ("String constant expected");
848 Error ("User error: %m%p", &CurTok.SVal);
855 static void DoExitMacro (void)
856 /* Exit a macro expansion */
858 if (!InMacExpansion ()) {
859 /* We aren't expanding a macro currently */
868 static void DoExport (void)
869 /* Export a symbol */
871 ExportImport (ExportWithAssign, ADDR_SIZE_DEFAULT, SF_NONE);
876 static void DoExportZP (void)
877 /* Export a zeropage symbol */
879 ExportImport (ExportWithAssign, ADDR_SIZE_ZP, SF_NONE);
884 static void DoFarAddr (void)
885 /* Define far addresses (24 bit) */
888 EmitFarAddr (Expression ());
889 if (CurTok.Tok != TOK_COMMA) {
899 static void DoFatal (void)
900 /* Fatal user error */
902 if (CurTok.Tok != TOK_STRCON) {
903 ErrorSkip ("String constant expected");
905 Fatal ("User error: %m%p", &CurTok.SVal);
912 static void DoFeature (void)
913 /* Switch the Feature option */
915 /* Allow a list of comma separated keywords */
918 /* We expect an identifier */
919 if (CurTok.Tok != TOK_IDENT) {
920 ErrorSkip ("Identifier expected");
924 /* Make the string attribute lower case */
927 /* Set the feature and check for errors */
928 if (SetFeature (&CurTok.SVal) == FEAT_UNKNOWN) {
930 ErrorSkip ("Invalid feature: `%m%p'", &CurTok.SVal);
933 /* Skip the keyword */
937 /* Allow more than one keyword */
938 if (CurTok.Tok == TOK_COMMA) {
948 static void DoFileOpt (void)
949 /* Insert a file option */
953 /* The option type may be given as a keyword or as a number. */
954 if (CurTok.Tok == TOK_IDENT) {
956 /* Option given as keyword */
957 static const char* Keys [] = {
958 "AUTHOR", "COMMENT", "COMPILER"
961 /* Map the option to a number */
962 OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
965 ErrorSkip ("File option keyword expected");
969 /* Skip the keyword */
972 /* Must be followed by a comma */
975 /* We accept only string options for now */
976 if (CurTok.Tok != TOK_STRCON) {
977 ErrorSkip ("String constant expected");
981 /* Insert the option */
986 OptAuthor (&CurTok.SVal);
991 OptComment (&CurTok.SVal);
996 OptCompiler (&CurTok.SVal);
1000 Internal ("Invalid OptNum: %ld", OptNum);
1009 /* Option given as number */
1010 OptNum = ConstExpression ();
1011 if (!IsByteRange (OptNum)) {
1012 ErrorSkip ("Range error");
1016 /* Must be followed by a comma */
1019 /* We accept only string options for now */
1020 if (CurTok.Tok != TOK_STRCON) {
1021 ErrorSkip ("String constant expected");
1025 /* Insert the option */
1026 OptStr ((unsigned char) OptNum, &CurTok.SVal);
1035 static void DoForceImport (void)
1036 /* Do a forced import on a symbol */
1038 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_FORCED);
1043 static void DoGlobal (void)
1044 /* Declare a global symbol */
1046 ExportImport (SymGlobal, ADDR_SIZE_DEFAULT, SF_NONE);
1051 static void DoGlobalZP (void)
1052 /* Declare a global zeropage symbol */
1054 ExportImport (SymGlobal, ADDR_SIZE_ZP, SF_NONE);
1058 static void DoHiBytes (void)
1059 /* Define bytes, extracting the hi byte from each expression in the list */
1062 EmitByte (FuncHiByte ());
1063 if (CurTok.Tok != TOK_COMMA) {
1073 static void DoI16 (void)
1074 /* Switch the index registers to 16 bit mode (assembler only) */
1076 if (GetCPU() != CPU_65816) {
1077 Error ("Command is only valid in 65816 mode");
1079 /* Immidiate mode has two extension bytes */
1080 ExtBytes [AM65I_IMM_INDEX] = 2;
1086 static void DoI8 (void)
1087 /* Switch the index registers to 16 bit mode (assembler only) */
1089 if (GetCPU() != CPU_65816) {
1090 Error ("Command is only valid in 65816 mode");
1092 /* Immidiate mode has one extension byte */
1093 ExtBytes [AM65I_IMM_INDEX] = 1;
1099 static void DoImport (void)
1100 /* Import a symbol */
1102 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_NONE);
1107 static void DoImportZP (void)
1108 /* Import a zero page symbol */
1110 ExportImport (SymImport, ADDR_SIZE_ZP, SF_NONE);
1115 static void DoIncBin (void)
1116 /* Include a binary file */
1118 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1119 struct stat StatBuf;
1125 /* Name must follow */
1126 if (CurTok.Tok != TOK_STRCON) {
1127 ErrorSkip ("String constant expected");
1130 SB_Copy (&Name, &CurTok.SVal);
1131 SB_Terminate (&Name);
1134 /* A starting offset may follow */
1135 if (CurTok.Tok == TOK_COMMA) {
1137 Start = ConstExpression ();
1139 /* And a length may follow */
1140 if (CurTok.Tok == TOK_COMMA) {
1142 Count = ConstExpression ();
1147 /* Try to open the file */
1148 F = fopen (SB_GetConstBuf (&Name), "rb");
1151 /* Search for the file in the binary include directory */
1152 char* PathName = SearchFile (BinSearchPath, SB_GetConstBuf (&Name));
1153 if (PathName == 0 || (F = fopen (PathName, "rb")) == 0) {
1154 /* Not found or cannot open, print an error and bail out */
1155 ErrorSkip ("Cannot open include file `%m%p': %s", &Name, strerror (errno));
1160 /* Remember the new file name */
1161 SB_CopyStr (&Name, PathName);
1163 /* Free the allocated memory */
1167 /* Get the size of the file */
1168 fseek (F, 0, SEEK_END);
1171 /* Stat the file and remember the values. There a race condition here,
1172 * since we cannot use fileno() (non standard identifier in standard
1173 * header file), and therefore not fstat. When using stat with the
1174 * file name, there's a risk that the file was deleted and recreated
1175 * while it was open. Since mtime and size are only used to check
1176 * if a file has changed in the debugger, we will ignore this problem
1179 SB_Terminate (&Name);
1180 if (stat (SB_GetConstBuf (&Name), &StatBuf) != 0) {
1181 Fatal ("Cannot stat input file `%m%p': %s", &Name, strerror (errno));
1184 /* Add the file to the input file table */
1185 AddFile (&Name, FT_BINARY, Size, StatBuf.st_mtime);
1187 /* If a count was not given, calculate it now */
1189 Count = Size - Start;
1191 /* Nothing to read - flag this as a range error */
1192 ErrorSkip ("Range error");
1196 /* Count was given, check if it is valid */
1197 if (Start + Count > Size) {
1198 ErrorSkip ("Range error");
1203 /* Seek to the start position */
1204 fseek (F, Start, SEEK_SET);
1206 /* Read chunks and insert them into the output */
1209 unsigned char Buf [1024];
1211 /* Calculate the number of bytes to read */
1212 size_t BytesToRead = (Count > (long)sizeof(Buf))? sizeof(Buf) : (size_t) Count;
1215 size_t BytesRead = fread (Buf, 1, BytesToRead, F);
1216 if (BytesToRead != BytesRead) {
1217 /* Some sort of error */
1218 ErrorSkip ("Cannot read from include file `%m%p': %s",
1219 &Name, strerror (errno));
1223 /* Insert it into the output */
1224 EmitData (Buf, BytesRead);
1226 /* Keep the counters current */
1231 /* Close the file, ignore errors since it's r/o */
1235 /* Free string memory */
1241 static void DoInclude (void)
1242 /* Include another file */
1244 /* Name must follow */
1245 if (CurTok.Tok != TOK_STRCON) {
1246 ErrorSkip ("String constant expected");
1248 SB_Terminate (&CurTok.SVal);
1249 if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) {
1250 /* Error opening the file, skip remainder of line */
1258 static void DoInterruptor (void)
1259 /* Export a symbol as interruptor */
1261 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1263 /* Symbol name follows */
1264 if (CurTok.Tok != TOK_IDENT) {
1265 ErrorSkip ("Identifier expected");
1268 SB_Copy (&Name, &CurTok.SVal);
1271 /* Parse the remainder of the line and export the symbol */
1272 ConDes (&Name, CD_TYPE_INT);
1274 /* Free string memory */
1280 static void DoInvalid (void)
1281 /* Handle a token that is invalid here, since it should have been handled on
1282 * a much lower level of the expression hierarchy. Getting this sort of token
1283 * means that the lower level code has bugs.
1284 * This function differs to DoUnexpected in that the latter may be triggered
1285 * by the user by using keywords in the wrong location. DoUnexpected is not
1286 * an error in the assembler itself, while DoInvalid is.
1289 Internal ("Unexpected token: %m%p", &Keyword);
1294 static void DoLineCont (void)
1295 /* Switch the use of line continuations */
1297 SetBoolOption (&LineCont);
1302 static void DoList (void)
1303 /* Enable/disable the listing */
1305 /* Get the setting */
1307 SetBoolOption (&List);
1309 /* Manage the counter */
1319 static void DoLoBytes (void)
1320 /* Define bytes, extracting the lo byte from each expression in the list */
1323 EmitByte (FuncLoByte ());
1324 if (CurTok.Tok != TOK_COMMA) {
1333 static void DoListBytes (void)
1334 /* Set maximum number of bytes to list for one line */
1336 SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
1341 static void DoLocalChar (void)
1342 /* Define the character that starts local labels */
1344 if (CurTok.Tok != TOK_CHARCON) {
1345 ErrorSkip ("Character constant expected");
1347 if (CurTok.IVal != '@' && CurTok.IVal != '?') {
1348 Error ("Invalid start character for locals");
1350 LocalStart = (char) CurTok.IVal;
1358 static void DoMacPack (void)
1359 /* Insert a macro package */
1363 /* We expect an identifier */
1364 if (CurTok.Tok != TOK_IDENT) {
1365 ErrorSkip ("Identifier expected");
1369 /* Search for the macro package name */
1371 Package = MacPackFind (&CurTok.SVal);
1374 ErrorSkip ("Invalid macro package");
1378 /* Insert the package. If this fails, skip the remainder of the line to
1379 * avoid additional error messages.
1381 if (MacPackInsert (Package) == 0) {
1388 static void DoMacro (void)
1389 /* Start a macro definition */
1391 MacDef (MAC_STYLE_CLASSIC);
1396 static void DoNull (void)
1397 /* Switch to the NULL segment */
1399 UseSeg (&NullSegDef);
1404 static void DoOrg (void)
1405 /* Start absolute code */
1407 long PC = ConstExpression ();
1408 if (PC < 0 || PC > 0xFFFFFF) {
1409 Error ("Range error");
1412 EnterAbsoluteMode (PC);
1417 static void DoOut (void)
1418 /* Output a string */
1420 if (CurTok.Tok != TOK_STRCON) {
1421 ErrorSkip ("String constant expected");
1423 /* Output the string and be sure to flush the output to keep it in
1424 * sync with any error messages if the output is redirected to a file.
1427 (int) SB_GetLen (&CurTok.SVal),
1428 SB_GetConstBuf (&CurTok.SVal));
1436 static void DoP02 (void)
1437 /* Switch to 6502 CPU */
1444 static void DoPC02 (void)
1445 /* Switch to 65C02 CPU */
1452 static void DoP816 (void)
1453 /* Switch to 65816 CPU */
1460 static void DoPageLength (void)
1461 /* Set the page length for the listing */
1463 PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
1468 static void DoPopCPU (void)
1469 /* Pop an old CPU setting from the CPU stack */
1471 /* Must have a CPU on the stack */
1472 if (IS_IsEmpty (&CPUStack)) {
1473 ErrorSkip ("CPU stack is empty");
1477 /* Set the CPU to the value popped from stack */
1478 SetCPU (IS_Pop (&CPUStack));
1483 static void DoPopSeg (void)
1484 /* Pop an old segment from the segment stack */
1488 /* Must have a segment on the stack */
1489 if (CollCount (&SegStack) == 0) {
1490 ErrorSkip ("Segment stack is empty");
1494 /* Pop the last element */
1495 Def = CollPop (&SegStack);
1497 /* Restore this segment */
1500 /* Delete the segment definition */
1506 static void DoProc (void)
1507 /* Start a new lexical scope */
1509 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1510 unsigned char AddrSize;
1514 if (CurTok.Tok == TOK_IDENT) {
1516 /* The new scope has a name. Remember it. */
1517 SB_Copy (&Name, &CurTok.SVal);
1519 /* Search for the symbol, generate a new one if needed */
1520 Sym = SymFind (CurrentScope, &Name, SYM_ALLOC_NEW);
1522 /* Skip the scope name */
1525 /* Read an optional address size specifier */
1526 AddrSize = OptionalAddrSize ();
1528 /* Mark the symbol as defined */
1529 SymDef (Sym, GenCurrentPC (), AddrSize, SF_LABEL);
1533 /* A .PROC statement without a name */
1534 Warning (1, "Unnamed .PROCs are deprecated, please use .SCOPE");
1535 AnonName (&Name, "PROC");
1536 AddrSize = ADDR_SIZE_DEFAULT;
1540 /* Enter a new scope */
1541 SymEnterLevel (&Name, ST_PROC, AddrSize, Sym);
1543 /* Free memory for Name */
1549 static void DoPSC02 (void)
1550 /* Switch to 65SC02 CPU */
1552 SetCPU (CPU_65SC02);
1557 static void DoPushCPU (void)
1558 /* Push the current CPU setting onto the CPU stack */
1560 /* Can only push a limited size of segments */
1561 if (IS_IsFull (&CPUStack)) {
1562 ErrorSkip ("CPU stack overflow");
1566 /* Get the current segment and push it */
1567 IS_Push (&CPUStack, GetCPU ());
1572 static void DoPushSeg (void)
1573 /* Push the current segment onto the segment stack */
1575 /* Can only push a limited size of segments */
1576 if (CollCount (&SegStack) >= MAX_PUSHED_SEGMENTS) {
1577 ErrorSkip ("Segment stack overflow");
1581 /* Get the current segment and push it */
1582 CollAppend (&SegStack, DupSegDef (GetCurrentSegDef ()));
1587 static void DoReloc (void)
1588 /* Enter relocatable mode */
1595 static void DoRepeat (void)
1596 /* Repeat some instruction block */
1603 static void DoRes (void)
1604 /* Reserve some number of storage bytes */
1609 Count = ConstExpression ();
1610 if (Count > 0xFFFF || Count < 0) {
1611 ErrorSkip ("Range error");
1614 if (CurTok.Tok == TOK_COMMA) {
1616 Val = ConstExpression ();
1617 /* We need a byte value here */
1618 if (!IsByteRange (Val)) {
1619 ErrorSkip ("Range error");
1623 /* Emit constant values */
1625 Emit0 ((unsigned char) Val);
1629 /* Emit fill fragments */
1636 static void DoROData (void)
1637 /* Switch to the r/o data segment */
1639 UseSeg (&RODataSegDef);
1644 static void DoScope (void)
1645 /* Start a local scope */
1647 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1648 unsigned char AddrSize;
1651 if (CurTok.Tok == TOK_IDENT) {
1653 /* The new scope has a name. Remember and skip it. */
1654 SB_Copy (&Name, &CurTok.SVal);
1659 /* An unnamed scope */
1660 AnonName (&Name, "SCOPE");
1664 /* Read an optional address size specifier */
1665 AddrSize = OptionalAddrSize ();
1667 /* Enter the new scope */
1668 SymEnterLevel (&Name, ST_SCOPE, AddrSize, 0);
1670 /* Free memory for Name */
1676 static void DoSegment (void)
1677 /* Switch to another segment */
1679 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1682 if (CurTok.Tok != TOK_STRCON) {
1683 ErrorSkip ("String constant expected");
1686 /* Save the name of the segment and skip it */
1687 SB_Copy (&Name, &CurTok.SVal);
1690 /* Use the name for the segment definition */
1691 SB_Terminate (&Name);
1692 Def.Name = SB_GetBuf (&Name);
1694 /* Check for an optional address size modifier */
1695 Def.AddrSize = OptionalAddrSize ();
1697 /* Set the segment */
1701 /* Free memory for Name */
1707 static void DoSetCPU (void)
1708 /* Switch the CPU instruction set */
1710 /* We expect an identifier */
1711 if (CurTok.Tok != TOK_STRCON) {
1712 ErrorSkip ("String constant expected");
1716 /* Try to find the CPU */
1717 SB_Terminate (&CurTok.SVal);
1718 CPU = FindCPU (SB_GetConstBuf (&CurTok.SVal));
1720 /* Switch to the new CPU */
1723 /* Skip the identifier. If the CPU switch was successful, the scanner
1724 * will treat the input now correctly for the new CPU.
1732 static void DoSmart (void)
1733 /* Smart mode on/off */
1735 SetBoolOption (&SmartMode);
1740 static void DoSunPlus (void)
1741 /* Switch to the SUNPLUS CPU */
1743 SetCPU (CPU_SUNPLUS);
1748 static void DoTag (void)
1749 /* Allocate space for a struct */
1754 /* Read the struct name */
1755 SymTable* Struct = ParseScopedSymTable ();
1757 /* Check the supposed struct */
1759 ErrorSkip ("Unknown struct");
1762 if (GetSymTabType (Struct) != ST_STRUCT) {
1763 ErrorSkip ("Not a struct");
1767 /* Get the symbol that defines the size of the struct */
1768 SizeSym = GetSizeOfScope (Struct);
1770 /* Check if it does exist and if its value is known */
1771 if (SizeSym == 0 || !SymIsConst (SizeSym, &Size)) {
1772 ErrorSkip ("Size of struct/union is unknown");
1776 /* Optional multiplicator may follow */
1777 if (CurTok.Tok == TOK_COMMA) {
1780 Multiplicator = ConstExpression ();
1781 /* Multiplicator must make sense */
1782 if (Multiplicator <= 0) {
1783 ErrorSkip ("Range error");
1786 Size *= Multiplicator;
1789 /* Emit fill fragments */
1795 static void DoUnDef (void)
1796 /* Undefine a define style macro */
1798 /* The function is called with the .UNDEF token in place, because we need
1799 * to disable .define macro expansions before reading the next token.
1800 * Otherwise the name of the macro would be expanded, so we would never
1803 DisableDefineStyleMacros ();
1805 EnableDefineStyleMacros ();
1807 /* We expect an identifier */
1808 if (CurTok.Tok != TOK_IDENT) {
1809 ErrorSkip ("Identifier expected");
1811 MacUndef (&CurTok.SVal, MAC_STYLE_DEFINE);
1818 static void DoUnexpected (void)
1819 /* Got an unexpected keyword */
1821 Error ("Unexpected `%m%p'", &Keyword);
1827 static void DoWarning (void)
1830 if (CurTok.Tok != TOK_STRCON) {
1831 ErrorSkip ("String constant expected");
1833 Warning (0, "User warning: %m%p", &CurTok.SVal);
1840 static void DoWord (void)
1844 EmitWord (Expression ());
1845 if (CurTok.Tok != TOK_COMMA) {
1855 static void DoZeropage (void)
1856 /* Switch to the zeropage segment */
1858 UseSeg (&ZeropageSegDef);
1863 /*****************************************************************************/
1865 /*****************************************************************************/
1869 /* Control commands flags */
1871 ccNone = 0x0000, /* No special flags */
1872 ccKeepToken = 0x0001 /* Do not skip the current token */
1875 /* Control command table */
1876 typedef struct CtrlDesc CtrlDesc;
1878 unsigned Flags; /* Flags for this directive */
1879 void (*Handler) (void); /* Command handler */
1882 #define PSEUDO_COUNT (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1883 static CtrlDesc CtrlCmdTab [] = {
1886 { ccNone, DoAddr }, /* .ADDR */
1887 { ccNone, DoAlign },
1888 { ccNone, DoASCIIZ },
1889 { ccNone, DoAssert },
1890 { ccNone, DoAutoImport },
1891 { ccNone, DoUnexpected }, /* .BANKBYTE */
1892 { ccNone, DoBankBytes },
1893 { ccNone, DoUnexpected }, /* .BLANK */
1897 { ccNone, DoCharMap },
1899 { ccNone, DoUnexpected, }, /* .CONCAT */
1900 { ccNone, DoConDes },
1901 { ccNone, DoUnexpected }, /* .CONST */
1902 { ccNone, DoConstructor },
1903 { ccNone, DoUnexpected }, /* .CPU */
1907 { ccNone, DoDebugInfo },
1908 { ccNone, DoDefine },
1909 { ccNone, DoUnexpected }, /* .DEFINED */
1910 { ccNone, DoDelMac },
1911 { ccNone, DoDestructor },
1912 { ccNone, DoDWord },
1913 { ccKeepToken, DoConditionals }, /* .ELSE */
1914 { ccKeepToken, DoConditionals }, /* .ELSEIF */
1915 { ccKeepToken, DoEnd },
1916 { ccNone, DoUnexpected }, /* .ENDENUM */
1917 { ccKeepToken, DoConditionals }, /* .ENDIF */
1918 { ccNone, DoUnexpected }, /* .ENDMACRO */
1919 { ccNone, DoEndProc },
1920 { ccNone, DoUnexpected }, /* .ENDREPEAT */
1921 { ccNone, DoEndScope },
1922 { ccNone, DoUnexpected }, /* .ENDSTRUCT */
1923 { ccNone, DoUnexpected }, /* .ENDUNION */
1925 { ccNone, DoError },
1926 { ccNone, DoExitMacro },
1927 { ccNone, DoExport },
1928 { ccNone, DoExportZP },
1929 { ccNone, DoFarAddr },
1930 { ccNone, DoFatal },
1931 { ccNone, DoFeature },
1932 { ccNone, DoFileOpt },
1933 { ccNone, DoForceImport },
1934 { ccNone, DoUnexpected }, /* .FORCEWORD */
1935 { ccNone, DoGlobal },
1936 { ccNone, DoGlobalZP },
1937 { ccNone, DoUnexpected }, /* .HIBYTE */
1938 { ccNone, DoHiBytes },
1939 { ccNone, DoUnexpected }, /* .HIWORD */
1942 { ccNone, DoUnexpected }, /* .IDENT */
1943 { ccKeepToken, DoConditionals }, /* .IF */
1944 { ccKeepToken, DoConditionals }, /* .IFBLANK */
1945 { ccKeepToken, DoConditionals }, /* .IFCONST */
1946 { ccKeepToken, DoConditionals }, /* .IFDEF */
1947 { ccKeepToken, DoConditionals }, /* .IFNBLANK */
1948 { ccKeepToken, DoConditionals }, /* .IFNCONST */
1949 { ccKeepToken, DoConditionals }, /* .IFNDEF */
1950 { ccKeepToken, DoConditionals }, /* .IFNREF */
1951 { ccKeepToken, DoConditionals }, /* .IFP02 */
1952 { ccKeepToken, DoConditionals }, /* .IFP816 */
1953 { ccKeepToken, DoConditionals }, /* .IFPC02 */
1954 { ccKeepToken, DoConditionals }, /* .IFPSC02 */
1955 { ccKeepToken, DoConditionals }, /* .IFREF */
1956 { ccNone, DoImport },
1957 { ccNone, DoImportZP },
1958 { ccNone, DoIncBin },
1959 { ccNone, DoInclude },
1960 { ccNone, DoInterruptor },
1961 { ccNone, DoInvalid }, /* .LEFT */
1962 { ccNone, DoLineCont },
1964 { ccNone, DoListBytes },
1965 { ccNone, DoUnexpected }, /* .LOBYTE */
1966 { ccNone, DoLoBytes },
1967 { ccNone, DoUnexpected }, /* .LOCAL */
1968 { ccNone, DoLocalChar },
1969 { ccNone, DoUnexpected }, /* .LOWORD */
1970 { ccNone, DoMacPack },
1971 { ccNone, DoMacro },
1972 { ccNone, DoUnexpected }, /* .MATCH */
1973 { ccNone, DoUnexpected }, /* .MAX */
1974 { ccNone, DoInvalid }, /* .MID */
1975 { ccNone, DoUnexpected }, /* .MIN */
1981 { ccNone, DoPageLength },
1982 { ccNone, DoUnexpected }, /* .PARAMCOUNT */
1984 { ccNone, DoPopCPU },
1985 { ccNone, DoPopSeg },
1987 { ccNone, DoPSC02 },
1988 { ccNone, DoPushCPU },
1989 { ccNone, DoPushSeg },
1990 { ccNone, DoUnexpected }, /* .REFERENCED */
1991 { ccNone, DoReloc },
1992 { ccNone, DoRepeat },
1994 { ccNone, DoInvalid }, /* .RIGHT */
1995 { ccNone, DoROData },
1996 { ccNone, DoScope },
1997 { ccNone, DoSegment },
1998 { ccNone, DoUnexpected }, /* .SET */
1999 { ccNone, DoSetCPU },
2000 { ccNone, DoUnexpected }, /* .SIZEOF */
2001 { ccNone, DoSmart },
2002 { ccNone, DoUnexpected }, /* .SPRINTF */
2003 { ccNone, DoUnexpected }, /* .STRAT */
2004 { ccNone, DoUnexpected }, /* .STRING */
2005 { ccNone, DoUnexpected }, /* .STRLEN */
2006 { ccNone, DoStruct },
2007 { ccNone, DoSunPlus },
2009 { ccNone, DoUnexpected }, /* .TCOUNT */
2010 { ccNone, DoUnexpected }, /* .TIME */
2011 { ccKeepToken, DoUnDef },
2012 { ccNone, DoUnion },
2013 { ccNone, DoUnexpected }, /* .VERSION */
2014 { ccNone, DoWarning },
2016 { ccNone, DoUnexpected }, /* .XMATCH */
2017 { ccNone, DoZeropage },
2022 /*****************************************************************************/
2024 /*****************************************************************************/
2028 void HandlePseudo (void)
2029 /* Handle a pseudo instruction */
2033 /* Calculate the index into the table */
2034 unsigned Index = CurTok.Tok - TOK_FIRSTPSEUDO;
2037 if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
2038 Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
2039 (unsigned) PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
2041 CHECK (Index < PSEUDO_COUNT);
2043 /* Get the pseudo intruction descriptor */
2044 D = &CtrlCmdTab [Index];
2046 /* Remember the instruction, then skip it if needed */
2047 if ((D->Flags & ccKeepToken) == 0) {
2048 SB_Copy (&Keyword, &CurTok.SVal);
2052 /* Call the handler */
2058 void CheckPseudo (void)
2059 /* Check if the stacks are empty at end of assembly */
2061 if (CollCount (&SegStack) != 0) {
2062 Warning (1, "Segment stack is not empty");
2064 if (!IS_IsEmpty (&CPUStack)) {
2065 Warning (1, "CPU stack is not empty");