1 /*****************************************************************************/
5 /* Pseudo instructions for the ca65 macroassembler */
9 /* (C) 1998-2012, 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 "alignment.h"
44 #include "assertion.h"
51 #include "scopedefs.h"
87 /*****************************************************************************/
89 /*****************************************************************************/
93 /* Keyword we're about to handle */
94 static StrBuf Keyword = STATIC_STRBUF_INITIALIZER;
97 static IntStack CPUStack = STATIC_INTSTACK_INITIALIZER;
100 #define MAX_PUSHED_SEGMENTS 16
101 static Collection SegStack = STATIC_COLLECTION_INITIALIZER;
105 /*****************************************************************************/
107 /*****************************************************************************/
111 static void DoUnexpected (void);
112 /* Got an unexpected keyword */
114 static void DoInvalid (void);
115 /* Handle a token that is invalid here, since it should have been handled on
116 * a much lower level of the expression hierarchy. Getting this sort of token
117 * means that the lower level code has bugs.
118 * This function differs to DoUnexpected in that the latter may be triggered
119 * by the user by using keywords in the wrong location. DoUnexpected is not
120 * an error in the assembler itself, while DoInvalid is.
125 /*****************************************************************************/
126 /* Helper functions */
127 /*****************************************************************************/
131 static unsigned char OptionalAddrSize (void)
132 /* If a colon follows, parse an optional address size spec and return it.
133 * Otherwise return ADDR_SIZE_DEFAULT.
136 unsigned AddrSize = ADDR_SIZE_DEFAULT;
137 if (CurTok.Tok == TOK_COLON) {
139 AddrSize = ParseAddrSize ();
140 if (!ValidAddrSizeForCPU (AddrSize)) {
141 /* Print an error and reset to default */
142 Error ("Invalid address size specification for current CPU");
143 AddrSize = ADDR_SIZE_DEFAULT;
152 static void SetBoolOption (unsigned char* Flag)
153 /* Read a on/off/+/- option and set flag accordingly */
155 static const char* Keys[] = {
160 if (CurTok.Tok == TOK_PLUS) {
163 } else if (CurTok.Tok == TOK_MINUS) {
166 } else if (CurTok.Tok == TOK_IDENT) {
167 /* Map the keyword to a number */
168 switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
169 case 0: *Flag = 0; NextTok (); break;
170 case 1: *Flag = 1; NextTok (); break;
171 default: ErrorSkip ("`on' or `off' expected"); break;
173 } else if (TokIsSep (CurTok.Tok)) {
174 /* Without anything assume switch on */
177 ErrorSkip ("`on' or `off' expected");
183 static void ExportWithAssign (SymEntry* Sym, unsigned char AddrSize, unsigned Flags)
184 /* Allow to assign the value of an export in an .export statement */
186 /* The name and optional address size spec may be followed by an assignment
189 if (CurTok.Tok == TOK_ASSIGN || CurTok.Tok == TOK_EQ) {
191 /* Assignment means the symbol is a label */
192 if (CurTok.Tok == TOK_ASSIGN) {
196 /* Skip the assignment token */
199 /* Define the symbol with the expression following the '=' */
200 SymDef (Sym, Expression(), ADDR_SIZE_DEFAULT, Flags);
204 /* Now export the symbol */
205 SymExport (Sym, AddrSize, Flags);
210 static void ExportImport (void (*Func) (SymEntry*, unsigned char, unsigned),
211 unsigned char DefAddrSize, unsigned Flags)
212 /* Export or import symbols */
215 unsigned char AddrSize;
219 /* We need an identifier here */
220 if (CurTok.Tok != TOK_IDENT) {
221 ErrorSkip ("Identifier expected");
225 /* Find the symbol table entry, allocate a new one if necessary */
226 Sym = SymFind (CurrentScope, &CurTok.SVal, SYM_ALLOC_NEW);
231 /* Get an optional address size */
232 AddrSize = OptionalAddrSize ();
233 if (AddrSize == ADDR_SIZE_DEFAULT) {
234 AddrSize = DefAddrSize;
237 /* Call the actual import/export function */
238 Func (Sym, AddrSize, Flags);
241 if (CurTok.Tok == TOK_COMMA) {
251 static long IntArg (long Min, long Max)
252 /* Read an integer argument and check a range. Accept the token "unlimited"
253 * and return -1 in this case.
256 if (CurTok.Tok == TOK_IDENT && SB_CompareStr (&CurTok.SVal, "unlimited") == 0) {
260 long Val = ConstExpression ();
261 if (Val < Min || Val > Max) {
262 Error ("Range error");
271 static void ConDes (const StrBuf* Name, unsigned Type)
272 /* Parse remaining line for constructor/destructor of the remaining type */
277 /* Find the symbol table entry, allocate a new one if necessary */
278 SymEntry* Sym = SymFind (CurrentScope, Name, SYM_ALLOC_NEW);
280 /* Optional constructor priority */
281 if (CurTok.Tok == TOK_COMMA) {
282 /* Priority value follows */
284 Prio = ConstExpression ();
285 if (Prio < CD_PRIO_MIN || Prio > CD_PRIO_MAX) {
286 /* Value out of range */
287 Error ("Range error");
291 /* Use the default priority value */
295 /* Define the symbol */
296 SymConDes (Sym, ADDR_SIZE_DEFAULT, Type, (unsigned) Prio);
301 static StrBuf* GenArrayType (StrBuf* Type, unsigned SpanSize,
302 const char* ElementType,
303 unsigned ElementTypeLen)
304 /* Create an array (or single data) of the given type. SpanSize is the size
305 * of the span, ElementType is a string that encodes the element data type.
306 * The function returns Type.
309 /* Get the size of the element type */
310 unsigned ElementSize = GT_GET_SIZE (ElementType[0]);
312 /* Get the number of array elements */
313 unsigned ElementCount = SpanSize / ElementSize;
315 /* The span size must be divideable by the element size */
316 CHECK ((SpanSize % ElementSize) == 0);
318 /* Encode the array */
319 GT_AddArray (Type, ElementCount);
320 SB_AppendBuf (Type, ElementType, ElementTypeLen);
322 /* Return the pointer to the created array type */
328 /*****************************************************************************/
329 /* Handler functions */
330 /*****************************************************************************/
334 static void DoA16 (void)
335 /* Switch the accu to 16 bit mode (assembler only) */
337 if (GetCPU() != CPU_65816) {
338 Error ("Command is only valid in 65816 mode");
340 /* Immidiate mode has two extension bytes */
341 ExtBytes [AM65I_IMM_ACCU] = 2;
347 static void DoA8 (void)
348 /* Switch the accu to 8 bit mode (assembler only) */
350 if (GetCPU() != CPU_65816) {
351 Error ("Command is only valid in 65816 mode");
353 /* Immidiate mode has one extension byte */
354 ExtBytes [AM65I_IMM_ACCU] = 1;
360 static void DoAddr (void)
361 /* Define addresses */
363 /* Element type for the generated array */
364 static const char EType[2] = { GT_PTR, GT_VOID };
366 /* Record type information */
367 Span* S = OpenSpan ();
368 StrBuf Type = STATIC_STRBUF_INITIALIZER;
370 /* Parse arguments */
372 ExprNode* Expr = Expression ();
373 if (GetCPU () == CPU_65816 || ForceRange) {
374 /* Do a range check */
375 Expr = GenWordExpr (Expr);
378 if (CurTok.Tok != TOK_COMMA) {
385 /* Close the span, then add type information to it */
387 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
389 /* Free the strings */
395 static void DoAlign (void)
396 /* Align the PC to some boundary */
401 /* Read the alignment value */
402 Alignment = ConstExpression ();
403 if (Alignment <= 0 || (unsigned long) Alignment > MAX_ALIGNMENT) {
404 ErrorSkip ("Range error");
408 /* Optional value follows */
409 if (CurTok.Tok == TOK_COMMA) {
411 FillVal = ConstExpression ();
412 /* We need a byte value here */
413 if (!IsByteRange (FillVal)) {
414 ErrorSkip ("Range error");
421 /* Generate the alignment */
422 SegAlign (Alignment, (int) FillVal);
427 static void DoASCIIZ (void)
428 /* Define text with a zero terminator */
431 /* Must have a string constant */
432 if (CurTok.Tok != TOK_STRCON) {
433 ErrorSkip ("String constant expected");
437 /* Translate into target charset and emit */
438 TgtTranslateStrBuf (&CurTok.SVal);
439 EmitStrBuf (&CurTok.SVal);
441 if (CurTok.Tok == TOK_COMMA) {
452 static void DoAssert (void)
453 /* Add an assertion */
455 static const char* ActionTab [] = {
458 "LDWARN", "LDWARNING",
465 /* First we have the expression that has to evaluated */
466 ExprNode* Expr = Expression ();
470 if (CurTok.Tok != TOK_IDENT) {
471 ErrorSkip ("Identifier expected");
474 switch (GetSubKey (ActionTab, sizeof (ActionTab) / sizeof (ActionTab[0]))) {
479 Action = ASSERT_ACT_WARN;
484 Action = ASSERT_ACT_ERROR;
490 Action = ASSERT_ACT_LDWARN;
495 Action = ASSERT_ACT_LDERROR;
499 Error ("Illegal assert action specifier");
500 /* Use lderror - there won't be an .o file anyway */
501 Action = ASSERT_ACT_LDERROR;
507 /* We can have an optional message. If no message is present, use
508 * "Assertion failed".
510 if (CurTok.Tok == TOK_COMMA) {
515 /* Read the message */
516 if (CurTok.Tok != TOK_STRCON) {
517 ErrorSkip ("String constant expected");
521 /* Translate the message into a string id. We can then skip the input
524 Msg = GetStrBufId (&CurTok.SVal);
529 /* Use "Assertion failed" */
530 Msg = GetStringId ("Assertion failed");
534 /* Remember the assertion */
535 AddAssertion (Expr, (AssertAction) Action, Msg);
540 static void DoAutoImport (void)
541 /* Mark unresolved symbols as imported */
543 SetBoolOption (&AutoImport);
547 static void DoBankBytes (void)
548 /* Define bytes, extracting the bank byte from each expression in the list */
551 EmitByte (FuncBankByte ());
552 if (CurTok.Tok != TOK_COMMA) {
562 static void DoBss (void)
563 /* Switch to the BSS segment */
570 static void DoByte (void)
573 /* Element type for the generated array */
574 static const char EType[1] = { GT_BYTE };
576 /* Record type information */
577 Span* S = OpenSpan ();
578 StrBuf Type = STATIC_STRBUF_INITIALIZER;
580 /* Parse arguments */
582 if (CurTok.Tok == TOK_STRCON) {
583 /* A string, translate into target charset and emit */
584 TgtTranslateStrBuf (&CurTok.SVal);
585 EmitStrBuf (&CurTok.SVal);
588 EmitByte (BoundedExpr (Expression, 1));
590 if (CurTok.Tok != TOK_COMMA) {
594 /* Do smart handling of dangling comma */
595 if (CurTok.Tok == TOK_SEP) {
596 Error ("Unexpected end of line");
602 /* Close the span, then add type information to it */
604 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
606 /* Free the type string */
612 static void DoCase (void)
613 /* Switch the IgnoreCase option */
615 SetBoolOption (&IgnoreCase);
616 IgnoreCase = !IgnoreCase;
621 static void DoCharMap (void)
622 /* Allow custome character mappings */
627 /* Read the index as numerical value */
628 Index = ConstExpression ();
629 if (Index <= 0 || Index > 255) {
630 /* Value out of range */
631 ErrorSkip ("Range error");
638 /* Read the character code */
639 Code = ConstExpression ();
640 if (Code <= 0 || Code > 255) {
641 /* Value out of range */
642 ErrorSkip ("Range error");
646 /* Set the character translation */
647 TgtTranslateSet ((unsigned) Index, (unsigned char) Code);
652 static void DoCode (void)
653 /* Switch to the code segment */
655 UseSeg (&CodeSegDef);
660 static void DoConDes (void)
661 /* Export a symbol as constructor/destructor */
663 static const char* Keys[] = {
668 StrBuf Name = STATIC_STRBUF_INITIALIZER;
671 /* Symbol name follows */
672 if (CurTok.Tok != TOK_IDENT) {
673 ErrorSkip ("Identifier expected");
676 SB_Copy (&Name, &CurTok.SVal);
679 /* Type follows. May be encoded as identifier or numerical */
681 if (CurTok.Tok == TOK_IDENT) {
683 /* Map the following keyword to a number, then skip it */
684 Type = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
687 /* Check if we got a valid keyword */
689 ErrorSkip ("Syntax error");
695 /* Read the type as numerical value */
696 Type = ConstExpression ();
697 if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) {
698 /* Value out of range */
699 ErrorSkip ("Range error");
705 /* Parse the remainder of the line and export the symbol */
706 ConDes (&Name, (unsigned) Type);
709 /* Free string memory */
715 static void DoConstructor (void)
716 /* Export a symbol as constructor */
718 StrBuf Name = STATIC_STRBUF_INITIALIZER;
720 /* Symbol name follows */
721 if (CurTok.Tok != TOK_IDENT) {
722 ErrorSkip ("Identifier expected");
725 SB_Copy (&Name, &CurTok.SVal);
728 /* Parse the remainder of the line and export the symbol */
729 ConDes (&Name, CD_TYPE_CON);
731 /* Free string memory */
737 static void DoData (void)
738 /* Switch to the data segment */
740 UseSeg (&DataSegDef);
745 static void DoDbg (void)
746 /* Add debug information from high level code */
748 static const char* Keys[] = {
757 /* We expect a subkey */
758 if (CurTok.Tok != TOK_IDENT) {
759 ErrorSkip ("Identifier expected");
763 /* Map the following keyword to a number */
764 Key = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
766 /* Skip the subkey */
769 /* Check the key and dispatch to a handler */
771 case 0: DbgInfoFile (); break;
772 case 1: DbgInfoFunc (); break;
773 case 2: DbgInfoLine (); break;
774 case 3: DbgInfoSym (); break;
775 default: ErrorSkip ("Syntax error"); break;
781 static void DoDByt (void)
782 /* Output double bytes */
784 /* Element type for the generated array */
785 static const char EType[1] = { GT_DBYTE };
787 /* Record type information */
788 Span* S = OpenSpan ();
789 StrBuf Type = STATIC_STRBUF_INITIALIZER;
791 /* Parse arguments */
793 EmitWord (GenSwapExpr (BoundedExpr (Expression, 2)));
794 if (CurTok.Tok != TOK_COMMA) {
801 /* Close the span, then add type information to it */
803 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
805 /* Free the type string */
811 static void DoDebugInfo (void)
812 /* Switch debug info on or off */
814 SetBoolOption (&DbgSyms);
819 static void DoDefine (void)
820 /* Define a one line macro */
822 MacDef (MAC_STYLE_DEFINE);
827 static void DoDelMac (void)
828 /* Delete a classic macro */
830 /* We expect an identifier */
831 if (CurTok.Tok != TOK_IDENT) {
832 ErrorSkip ("Identifier expected");
834 MacUndef (&CurTok.SVal, MAC_STYLE_CLASSIC);
841 static void DoDestructor (void)
842 /* Export a symbol as destructor */
844 StrBuf Name = STATIC_STRBUF_INITIALIZER;
846 /* Symbol name follows */
847 if (CurTok.Tok != TOK_IDENT) {
848 ErrorSkip ("Identifier expected");
851 SB_Copy (&Name, &CurTok.SVal);
854 /* Parse the remainder of the line and export the symbol */
855 ConDes (&Name, CD_TYPE_DES);
857 /* Free string memory */
863 static void DoDWord (void)
867 EmitDWord (BoundedExpr (Expression, 4));
868 if (CurTok.Tok != TOK_COMMA) {
878 static void DoEnd (void)
879 /* End of assembly */
887 static void DoEndProc (void)
888 /* Leave a lexical level */
890 if (CurrentScope->Type != SCOPE_SCOPE || CurrentScope->Label == 0) {
892 ErrorSkip ("No open .PROC");
900 static void DoEndScope (void)
901 /* Leave a lexical level */
903 if (CurrentScope->Type != SCOPE_SCOPE || CurrentScope->Label != 0) {
905 ErrorSkip ("No open .SCOPE");
913 static void DoError (void)
916 if (CurTok.Tok != TOK_STRCON) {
917 ErrorSkip ("String constant expected");
919 Error ("User error: %m%p", &CurTok.SVal);
926 static void DoExitMacro (void)
927 /* Exit a macro expansion */
929 if (!InMacExpansion ()) {
930 /* We aren't expanding a macro currently */
939 static void DoExport (void)
940 /* Export a symbol */
942 ExportImport (ExportWithAssign, ADDR_SIZE_DEFAULT, SF_NONE);
947 static void DoExportZP (void)
948 /* Export a zeropage symbol */
950 ExportImport (ExportWithAssign, ADDR_SIZE_ZP, SF_NONE);
955 static void DoFarAddr (void)
956 /* Define far addresses (24 bit) */
958 /* Element type for the generated array */
959 static const char EType[2] = { GT_FAR_PTR, GT_VOID };
961 /* Record type information */
962 Span* S = OpenSpan ();
963 StrBuf Type = STATIC_STRBUF_INITIALIZER;
965 /* Parse arguments */
967 EmitFarAddr (BoundedExpr (Expression, 3));
968 if (CurTok.Tok != TOK_COMMA) {
975 /* Close the span, then add type information to it */
977 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
979 /* Free the type string */
985 static void DoFatal (void)
986 /* Fatal user error */
988 if (CurTok.Tok != TOK_STRCON) {
989 ErrorSkip ("String constant expected");
991 Fatal ("User error: %m%p", &CurTok.SVal);
998 static void DoFeature (void)
999 /* Switch the Feature option */
1001 /* Allow a list of comma separated keywords */
1004 /* We expect an identifier */
1005 if (CurTok.Tok != TOK_IDENT) {
1006 ErrorSkip ("Identifier expected");
1010 /* Make the string attribute lower case */
1013 /* Set the feature and check for errors */
1014 if (SetFeature (&CurTok.SVal) == FEAT_UNKNOWN) {
1016 ErrorSkip ("Invalid feature: `%m%p'", &CurTok.SVal);
1019 /* Skip the keyword */
1023 /* Allow more than one keyword */
1024 if (CurTok.Tok == TOK_COMMA) {
1034 static void DoFileOpt (void)
1035 /* Insert a file option */
1039 /* The option type may be given as a keyword or as a number. */
1040 if (CurTok.Tok == TOK_IDENT) {
1042 /* Option given as keyword */
1043 static const char* Keys [] = {
1044 "AUTHOR", "COMMENT", "COMPILER"
1047 /* Map the option to a number */
1048 OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
1051 ErrorSkip ("File option keyword expected");
1055 /* Skip the keyword */
1058 /* Must be followed by a comma */
1061 /* We accept only string options for now */
1062 if (CurTok.Tok != TOK_STRCON) {
1063 ErrorSkip ("String constant expected");
1067 /* Insert the option */
1072 OptAuthor (&CurTok.SVal);
1077 OptComment (&CurTok.SVal);
1082 OptCompiler (&CurTok.SVal);
1086 Internal ("Invalid OptNum: %ld", OptNum);
1095 /* Option given as number */
1096 OptNum = ConstExpression ();
1097 if (!IsByteRange (OptNum)) {
1098 ErrorSkip ("Range error");
1102 /* Must be followed by a comma */
1105 /* We accept only string options for now */
1106 if (CurTok.Tok != TOK_STRCON) {
1107 ErrorSkip ("String constant expected");
1111 /* Insert the option */
1112 OptStr ((unsigned char) OptNum, &CurTok.SVal);
1121 static void DoForceImport (void)
1122 /* Do a forced import on a symbol */
1124 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_FORCED);
1129 static void DoGlobal (void)
1130 /* Declare a global symbol */
1132 ExportImport (SymGlobal, ADDR_SIZE_DEFAULT, SF_NONE);
1137 static void DoGlobalZP (void)
1138 /* Declare a global zeropage symbol */
1140 ExportImport (SymGlobal, ADDR_SIZE_ZP, SF_NONE);
1144 static void DoHiBytes (void)
1145 /* Define bytes, extracting the hi byte from each expression in the list */
1148 EmitByte (FuncHiByte ());
1149 if (CurTok.Tok != TOK_COMMA) {
1159 static void DoI16 (void)
1160 /* Switch the index registers to 16 bit mode (assembler only) */
1162 if (GetCPU() != CPU_65816) {
1163 Error ("Command is only valid in 65816 mode");
1165 /* Immidiate mode has two extension bytes */
1166 ExtBytes [AM65I_IMM_INDEX] = 2;
1172 static void DoI8 (void)
1173 /* Switch the index registers to 16 bit mode (assembler only) */
1175 if (GetCPU() != CPU_65816) {
1176 Error ("Command is only valid in 65816 mode");
1178 /* Immidiate mode has one extension byte */
1179 ExtBytes [AM65I_IMM_INDEX] = 1;
1185 static void DoImport (void)
1186 /* Import a symbol */
1188 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_NONE);
1193 static void DoImportZP (void)
1194 /* Import a zero page symbol */
1196 ExportImport (SymImport, ADDR_SIZE_ZP, SF_NONE);
1201 static void DoIncBin (void)
1202 /* Include a binary file */
1204 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1205 struct stat StatBuf;
1211 /* Name must follow */
1212 if (CurTok.Tok != TOK_STRCON) {
1213 ErrorSkip ("String constant expected");
1216 SB_Copy (&Name, &CurTok.SVal);
1217 SB_Terminate (&Name);
1220 /* A starting offset may follow */
1221 if (CurTok.Tok == TOK_COMMA) {
1223 Start = ConstExpression ();
1225 /* And a length may follow */
1226 if (CurTok.Tok == TOK_COMMA) {
1228 Count = ConstExpression ();
1233 /* Try to open the file */
1234 F = fopen (SB_GetConstBuf (&Name), "rb");
1237 /* Search for the file in the binary include directory */
1238 char* PathName = SearchFile (BinSearchPath, SB_GetConstBuf (&Name));
1239 if (PathName == 0 || (F = fopen (PathName, "rb")) == 0) {
1240 /* Not found or cannot open, print an error and bail out */
1241 ErrorSkip ("Cannot open include file `%m%p': %s", &Name, strerror (errno));
1246 /* Remember the new file name */
1247 SB_CopyStr (&Name, PathName);
1249 /* Free the allocated memory */
1253 /* Get the size of the file */
1254 fseek (F, 0, SEEK_END);
1257 /* Stat the file and remember the values. There a race condition here,
1258 * since we cannot use fileno() (non standard identifier in standard
1259 * header file), and therefore not fstat. When using stat with the
1260 * file name, there's a risk that the file was deleted and recreated
1261 * while it was open. Since mtime and size are only used to check
1262 * if a file has changed in the debugger, we will ignore this problem
1265 SB_Terminate (&Name);
1266 if (FileStat (SB_GetConstBuf (&Name), &StatBuf) != 0) {
1267 Fatal ("Cannot stat input file `%m%p': %s", &Name, strerror (errno));
1270 /* Add the file to the input file table */
1271 AddFile (&Name, FT_BINARY, Size, StatBuf.st_mtime);
1273 /* If a count was not given, calculate it now */
1275 Count = Size - Start;
1277 /* Nothing to read - flag this as a range error */
1278 ErrorSkip ("Range error");
1282 /* Count was given, check if it is valid */
1283 if (Start + Count > Size) {
1284 ErrorSkip ("Range error");
1289 /* Seek to the start position */
1290 fseek (F, Start, SEEK_SET);
1292 /* Read chunks and insert them into the output */
1295 unsigned char Buf [1024];
1297 /* Calculate the number of bytes to read */
1298 size_t BytesToRead = (Count > (long)sizeof(Buf))? sizeof(Buf) : (size_t) Count;
1301 size_t BytesRead = fread (Buf, 1, BytesToRead, F);
1302 if (BytesToRead != BytesRead) {
1303 /* Some sort of error */
1304 ErrorSkip ("Cannot read from include file `%m%p': %s",
1305 &Name, strerror (errno));
1309 /* Insert it into the output */
1310 EmitData (Buf, BytesRead);
1312 /* Keep the counters current */
1317 /* Close the file, ignore errors since it's r/o */
1321 /* Free string memory */
1327 static void DoInclude (void)
1328 /* Include another file */
1330 /* Name must follow */
1331 if (CurTok.Tok != TOK_STRCON) {
1332 ErrorSkip ("String constant expected");
1334 SB_Terminate (&CurTok.SVal);
1335 if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) {
1336 /* Error opening the file, skip remainder of line */
1344 static void DoInterruptor (void)
1345 /* Export a symbol as interruptor */
1347 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1349 /* Symbol name follows */
1350 if (CurTok.Tok != TOK_IDENT) {
1351 ErrorSkip ("Identifier expected");
1354 SB_Copy (&Name, &CurTok.SVal);
1357 /* Parse the remainder of the line and export the symbol */
1358 ConDes (&Name, CD_TYPE_INT);
1360 /* Free string memory */
1366 static void DoInvalid (void)
1367 /* Handle a token that is invalid here, since it should have been handled on
1368 * a much lower level of the expression hierarchy. Getting this sort of token
1369 * means that the lower level code has bugs.
1370 * This function differs to DoUnexpected in that the latter may be triggered
1371 * by the user by using keywords in the wrong location. DoUnexpected is not
1372 * an error in the assembler itself, while DoInvalid is.
1375 Internal ("Unexpected token: %m%p", &Keyword);
1380 static void DoLineCont (void)
1381 /* Switch the use of line continuations */
1383 SetBoolOption (&LineCont);
1388 static void DoList (void)
1389 /* Enable/disable the listing */
1391 /* Get the setting */
1393 SetBoolOption (&List);
1395 /* Manage the counter */
1405 static void DoLoBytes (void)
1406 /* Define bytes, extracting the lo byte from each expression in the list */
1409 EmitByte (FuncLoByte ());
1410 if (CurTok.Tok != TOK_COMMA) {
1419 static void DoListBytes (void)
1420 /* Set maximum number of bytes to list for one line */
1422 SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
1427 static void DoLocalChar (void)
1428 /* Define the character that starts local labels */
1430 if (CurTok.Tok != TOK_CHARCON) {
1431 ErrorSkip ("Character constant expected");
1433 if (CurTok.IVal != '@' && CurTok.IVal != '?') {
1434 Error ("Invalid start character for locals");
1436 LocalStart = (char) CurTok.IVal;
1444 static void DoMacPack (void)
1445 /* Insert a macro package */
1449 /* We expect an identifier */
1450 if (CurTok.Tok != TOK_IDENT) {
1451 ErrorSkip ("Identifier expected");
1455 /* Search for the macro package name */
1457 Package = MacPackFind (&CurTok.SVal);
1460 ErrorSkip ("Invalid macro package");
1464 /* Insert the package. If this fails, skip the remainder of the line to
1465 * avoid additional error messages.
1467 if (MacPackInsert (Package) == 0) {
1474 static void DoMacro (void)
1475 /* Start a macro definition */
1477 MacDef (MAC_STYLE_CLASSIC);
1482 static void DoNull (void)
1483 /* Switch to the NULL segment */
1485 UseSeg (&NullSegDef);
1490 static void DoOrg (void)
1491 /* Start absolute code */
1493 long PC = ConstExpression ();
1494 if (PC < 0 || PC > 0xFFFFFF) {
1495 Error ("Range error");
1498 EnterAbsoluteMode (PC);
1503 static void DoOut (void)
1504 /* Output a string */
1506 if (CurTok.Tok != TOK_STRCON) {
1507 ErrorSkip ("String constant expected");
1509 /* Output the string and be sure to flush the output to keep it in
1510 * sync with any error messages if the output is redirected to a file.
1513 (int) SB_GetLen (&CurTok.SVal),
1514 SB_GetConstBuf (&CurTok.SVal));
1522 static void DoP02 (void)
1523 /* Switch to 6502 CPU */
1530 static void DoPC02 (void)
1531 /* Switch to 65C02 CPU */
1538 static void DoP816 (void)
1539 /* Switch to 65816 CPU */
1546 static void DoPageLength (void)
1547 /* Set the page length for the listing */
1549 PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
1554 static void DoPopCPU (void)
1555 /* Pop an old CPU setting from the CPU stack */
1557 /* Must have a CPU on the stack */
1558 if (IS_IsEmpty (&CPUStack)) {
1559 ErrorSkip ("CPU stack is empty");
1563 /* Set the CPU to the value popped from stack */
1564 SetCPU (IS_Pop (&CPUStack));
1569 static void DoPopSeg (void)
1570 /* Pop an old segment from the segment stack */
1574 /* Must have a segment on the stack */
1575 if (CollCount (&SegStack) == 0) {
1576 ErrorSkip ("Segment stack is empty");
1580 /* Pop the last element */
1581 Def = CollPop (&SegStack);
1583 /* Restore this segment */
1586 /* Delete the segment definition */
1592 static void DoProc (void)
1593 /* Start a new lexical scope */
1595 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1596 unsigned char AddrSize;
1600 if (CurTok.Tok == TOK_IDENT) {
1602 /* The new scope has a name. Remember it. */
1603 SB_Copy (&Name, &CurTok.SVal);
1605 /* Search for the symbol, generate a new one if needed */
1606 Sym = SymFind (CurrentScope, &Name, SYM_ALLOC_NEW);
1608 /* Skip the scope name */
1611 /* Read an optional address size specifier */
1612 AddrSize = OptionalAddrSize ();
1614 /* Mark the symbol as defined */
1615 SymDef (Sym, GenCurrentPC (), AddrSize, SF_LABEL);
1619 /* A .PROC statement without a name */
1620 Warning (1, "Unnamed .PROCs are deprecated, please use .SCOPE");
1621 AnonName (&Name, "PROC");
1622 AddrSize = ADDR_SIZE_DEFAULT;
1626 /* Enter a new scope */
1627 SymEnterLevel (&Name, SCOPE_SCOPE, AddrSize, Sym);
1629 /* Free memory for Name */
1635 static void DoPSC02 (void)
1636 /* Switch to 65SC02 CPU */
1638 SetCPU (CPU_65SC02);
1643 static void DoPushCPU (void)
1644 /* Push the current CPU setting onto the CPU stack */
1646 /* Can only push a limited size of segments */
1647 if (IS_IsFull (&CPUStack)) {
1648 ErrorSkip ("CPU stack overflow");
1652 /* Get the current segment and push it */
1653 IS_Push (&CPUStack, GetCPU ());
1658 static void DoPushSeg (void)
1659 /* Push the current segment onto the segment stack */
1661 /* Can only push a limited size of segments */
1662 if (CollCount (&SegStack) >= MAX_PUSHED_SEGMENTS) {
1663 ErrorSkip ("Segment stack overflow");
1667 /* Get the current segment and push it */
1668 CollAppend (&SegStack, DupSegDef (GetCurrentSegDef ()));
1673 static void DoReloc (void)
1674 /* Enter relocatable mode */
1681 static void DoRepeat (void)
1682 /* Repeat some instruction block */
1689 static void DoRes (void)
1690 /* Reserve some number of storage bytes */
1695 Count = ConstExpression ();
1696 if (Count > 0xFFFF || Count < 0) {
1697 ErrorSkip ("Range error");
1700 if (CurTok.Tok == TOK_COMMA) {
1702 Val = ConstExpression ();
1703 /* We need a byte value here */
1704 if (!IsByteRange (Val)) {
1705 ErrorSkip ("Range error");
1709 /* Emit constant values */
1711 Emit0 ((unsigned char) Val);
1715 /* Emit fill fragments */
1722 static void DoROData (void)
1723 /* Switch to the r/o data segment */
1725 UseSeg (&RODataSegDef);
1730 static void DoScope (void)
1731 /* Start a local scope */
1733 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1734 unsigned char AddrSize;
1737 if (CurTok.Tok == TOK_IDENT) {
1739 /* The new scope has a name. Remember and skip it. */
1740 SB_Copy (&Name, &CurTok.SVal);
1745 /* An unnamed scope */
1746 AnonName (&Name, "SCOPE");
1750 /* Read an optional address size specifier */
1751 AddrSize = OptionalAddrSize ();
1753 /* Enter the new scope */
1754 SymEnterLevel (&Name, SCOPE_SCOPE, AddrSize, 0);
1756 /* Free memory for Name */
1762 static void DoSegment (void)
1763 /* Switch to another segment */
1765 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1768 if (CurTok.Tok != TOK_STRCON) {
1769 ErrorSkip ("String constant expected");
1772 /* Save the name of the segment and skip it */
1773 SB_Copy (&Name, &CurTok.SVal);
1776 /* Use the name for the segment definition */
1777 SB_Terminate (&Name);
1778 Def.Name = SB_GetBuf (&Name);
1780 /* Check for an optional address size modifier */
1781 Def.AddrSize = OptionalAddrSize ();
1783 /* Set the segment */
1787 /* Free memory for Name */
1793 static void DoSetCPU (void)
1794 /* Switch the CPU instruction set */
1796 /* We expect an identifier */
1797 if (CurTok.Tok != TOK_STRCON) {
1798 ErrorSkip ("String constant expected");
1802 /* Try to find the CPU */
1803 SB_Terminate (&CurTok.SVal);
1804 CPU = FindCPU (SB_GetConstBuf (&CurTok.SVal));
1806 /* Switch to the new CPU */
1809 /* Skip the identifier. If the CPU switch was successful, the scanner
1810 * will treat the input now correctly for the new CPU.
1818 static void DoSmart (void)
1819 /* Smart mode on/off */
1821 SetBoolOption (&SmartMode);
1826 static void DoSunPlus (void)
1827 /* Switch to the SUNPLUS CPU */
1829 SetCPU (CPU_SUNPLUS);
1834 static void DoTag (void)
1835 /* Allocate space for a struct */
1840 /* Read the struct name */
1841 SymTable* Struct = ParseScopedSymTable ();
1843 /* Check the supposed struct */
1845 ErrorSkip ("Unknown struct");
1848 if (GetSymTabType (Struct) != SCOPE_STRUCT) {
1849 ErrorSkip ("Not a struct");
1853 /* Get the symbol that defines the size of the struct */
1854 SizeSym = GetSizeOfScope (Struct);
1856 /* Check if it does exist and if its value is known */
1857 if (SizeSym == 0 || !SymIsConst (SizeSym, &Size)) {
1858 ErrorSkip ("Size of struct/union is unknown");
1862 /* Optional multiplicator may follow */
1863 if (CurTok.Tok == TOK_COMMA) {
1866 Multiplicator = ConstExpression ();
1867 /* Multiplicator must make sense */
1868 if (Multiplicator <= 0) {
1869 ErrorSkip ("Range error");
1872 Size *= Multiplicator;
1875 /* Emit fill fragments */
1881 static void DoUnDef (void)
1882 /* Undefine a define style macro */
1884 /* The function is called with the .UNDEF token in place, because we need
1885 * to disable .define macro expansions before reading the next token.
1886 * Otherwise the name of the macro would be expanded, so we would never
1889 DisableDefineStyleMacros ();
1891 EnableDefineStyleMacros ();
1893 /* We expect an identifier */
1894 if (CurTok.Tok != TOK_IDENT) {
1895 ErrorSkip ("Identifier expected");
1897 MacUndef (&CurTok.SVal, MAC_STYLE_DEFINE);
1904 static void DoUnexpected (void)
1905 /* Got an unexpected keyword */
1907 Error ("Unexpected `%m%p'", &Keyword);
1913 static void DoWarning (void)
1916 if (CurTok.Tok != TOK_STRCON) {
1917 ErrorSkip ("String constant expected");
1919 Warning (0, "User warning: %m%p", &CurTok.SVal);
1926 static void DoWord (void)
1929 /* Element type for the generated array */
1930 static const char EType[1] = { GT_WORD };
1932 /* Record type information */
1933 Span* S = OpenSpan ();
1934 StrBuf Type = STATIC_STRBUF_INITIALIZER;
1936 /* Parse arguments */
1938 EmitWord (BoundedExpr (Expression, 2));
1939 if (CurTok.Tok != TOK_COMMA) {
1946 /* Close the span, then add type information to it */
1948 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
1950 /* Free the type string */
1956 static void DoZeropage (void)
1957 /* Switch to the zeropage segment */
1959 UseSeg (&ZeropageSegDef);
1964 /*****************************************************************************/
1966 /*****************************************************************************/
1970 /* Control commands flags */
1972 ccNone = 0x0000, /* No special flags */
1973 ccKeepToken = 0x0001 /* Do not skip the current token */
1976 /* Control command table */
1977 typedef struct CtrlDesc CtrlDesc;
1979 unsigned Flags; /* Flags for this directive */
1980 void (*Handler) (void); /* Command handler */
1983 #define PSEUDO_COUNT (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1984 static CtrlDesc CtrlCmdTab [] = {
1987 { ccNone, DoAddr }, /* .ADDR */
1988 { ccNone, DoAlign },
1989 { ccNone, DoASCIIZ },
1990 { ccNone, DoAssert },
1991 { ccNone, DoAutoImport },
1992 { ccNone, DoUnexpected }, /* .BANK */
1993 { ccNone, DoUnexpected }, /* .BANKBYTE */
1994 { ccNone, DoBankBytes },
1995 { ccNone, DoUnexpected }, /* .BLANK */
1999 { ccNone, DoCharMap },
2001 { ccNone, DoUnexpected, }, /* .CONCAT */
2002 { ccNone, DoConDes },
2003 { ccNone, DoUnexpected }, /* .CONST */
2004 { ccNone, DoConstructor },
2005 { ccNone, DoUnexpected }, /* .CPU */
2009 { ccNone, DoDebugInfo },
2010 { ccNone, DoDefine },
2011 { ccNone, DoUnexpected }, /* .DEFINED */
2012 { ccNone, DoDelMac },
2013 { ccNone, DoDestructor },
2014 { ccNone, DoDWord },
2015 { ccKeepToken, DoConditionals }, /* .ELSE */
2016 { ccKeepToken, DoConditionals }, /* .ELSEIF */
2017 { ccKeepToken, DoEnd },
2018 { ccNone, DoUnexpected }, /* .ENDENUM */
2019 { ccKeepToken, DoConditionals }, /* .ENDIF */
2020 { ccNone, DoUnexpected }, /* .ENDMACRO */
2021 { ccNone, DoEndProc },
2022 { ccNone, DoUnexpected }, /* .ENDREPEAT */
2023 { ccNone, DoEndScope },
2024 { ccNone, DoUnexpected }, /* .ENDSTRUCT */
2025 { ccNone, DoUnexpected }, /* .ENDUNION */
2027 { ccNone, DoError },
2028 { ccNone, DoExitMacro },
2029 { ccNone, DoExport },
2030 { ccNone, DoExportZP },
2031 { ccNone, DoFarAddr },
2032 { ccNone, DoFatal },
2033 { ccNone, DoFeature },
2034 { ccNone, DoFileOpt },
2035 { ccNone, DoForceImport },
2036 { ccNone, DoUnexpected }, /* .FORCEWORD */
2037 { ccNone, DoGlobal },
2038 { ccNone, DoGlobalZP },
2039 { ccNone, DoUnexpected }, /* .HIBYTE */
2040 { ccNone, DoHiBytes },
2041 { ccNone, DoUnexpected }, /* .HIWORD */
2044 { ccNone, DoUnexpected }, /* .IDENT */
2045 { ccKeepToken, DoConditionals }, /* .IF */
2046 { ccKeepToken, DoConditionals }, /* .IFBLANK */
2047 { ccKeepToken, DoConditionals }, /* .IFCONST */
2048 { ccKeepToken, DoConditionals }, /* .IFDEF */
2049 { ccKeepToken, DoConditionals }, /* .IFNBLANK */
2050 { ccKeepToken, DoConditionals }, /* .IFNCONST */
2051 { ccKeepToken, DoConditionals }, /* .IFNDEF */
2052 { ccKeepToken, DoConditionals }, /* .IFNREF */
2053 { ccKeepToken, DoConditionals }, /* .IFP02 */
2054 { ccKeepToken, DoConditionals }, /* .IFP816 */
2055 { ccKeepToken, DoConditionals }, /* .IFPC02 */
2056 { ccKeepToken, DoConditionals }, /* .IFPSC02 */
2057 { ccKeepToken, DoConditionals }, /* .IFREF */
2058 { ccNone, DoImport },
2059 { ccNone, DoImportZP },
2060 { ccNone, DoIncBin },
2061 { ccNone, DoInclude },
2062 { ccNone, DoInterruptor },
2063 { ccNone, DoInvalid }, /* .LEFT */
2064 { ccNone, DoLineCont },
2066 { ccNone, DoListBytes },
2067 { ccNone, DoUnexpected }, /* .LOBYTE */
2068 { ccNone, DoLoBytes },
2069 { ccNone, DoUnexpected }, /* .LOCAL */
2070 { ccNone, DoLocalChar },
2071 { ccNone, DoUnexpected }, /* .LOWORD */
2072 { ccNone, DoMacPack },
2073 { ccNone, DoMacro },
2074 { ccNone, DoUnexpected }, /* .MATCH */
2075 { ccNone, DoUnexpected }, /* .MAX */
2076 { ccNone, DoInvalid }, /* .MID */
2077 { ccNone, DoUnexpected }, /* .MIN */
2083 { ccNone, DoPageLength },
2084 { ccNone, DoUnexpected }, /* .PARAMCOUNT */
2086 { ccNone, DoPopCPU },
2087 { ccNone, DoPopSeg },
2089 { ccNone, DoPSC02 },
2090 { ccNone, DoPushCPU },
2091 { ccNone, DoPushSeg },
2092 { ccNone, DoUnexpected }, /* .REFERENCED */
2093 { ccNone, DoReloc },
2094 { ccNone, DoRepeat },
2096 { ccNone, DoInvalid }, /* .RIGHT */
2097 { ccNone, DoROData },
2098 { ccNone, DoScope },
2099 { ccNone, DoSegment },
2100 { ccNone, DoUnexpected }, /* .SET */
2101 { ccNone, DoSetCPU },
2102 { ccNone, DoUnexpected }, /* .SIZEOF */
2103 { ccNone, DoSmart },
2104 { ccNone, DoUnexpected }, /* .SPRINTF */
2105 { ccNone, DoUnexpected }, /* .STRAT */
2106 { ccNone, DoUnexpected }, /* .STRING */
2107 { ccNone, DoUnexpected }, /* .STRLEN */
2108 { ccNone, DoStruct },
2109 { ccNone, DoSunPlus },
2111 { ccNone, DoUnexpected }, /* .TCOUNT */
2112 { ccNone, DoUnexpected }, /* .TIME */
2113 { ccKeepToken, DoUnDef },
2114 { ccNone, DoUnion },
2115 { ccNone, DoUnexpected }, /* .VERSION */
2116 { ccNone, DoWarning },
2118 { ccNone, DoUnexpected }, /* .XMATCH */
2119 { ccNone, DoZeropage },
2124 /*****************************************************************************/
2126 /*****************************************************************************/
2130 void HandlePseudo (void)
2131 /* Handle a pseudo instruction */
2135 /* Calculate the index into the table */
2136 unsigned Index = CurTok.Tok - TOK_FIRSTPSEUDO;
2139 if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
2140 Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
2141 (unsigned) PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
2143 CHECK (Index < PSEUDO_COUNT);
2145 /* Get the pseudo intruction descriptor */
2146 D = &CtrlCmdTab [Index];
2148 /* Remember the instruction, then skip it if needed */
2149 if ((D->Flags & ccKeepToken) == 0) {
2150 SB_Copy (&Keyword, &CurTok.SVal);
2154 /* Call the handler */
2160 void CheckPseudo (void)
2161 /* Check if the stacks are empty at end of assembly */
2163 if (CollCount (&SegStack) != 0) {
2164 Warning (1, "Segment stack is not empty");
2166 if (!IS_IsEmpty (&CPUStack)) {
2167 Warning (1, "CPU stack is not empty");