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 "alignment.h"
46 #include "assertion.h"
52 #include "scopedefs.h"
88 /*****************************************************************************/
90 /*****************************************************************************/
94 /* Keyword we're about to handle */
95 static StrBuf Keyword = STATIC_STRBUF_INITIALIZER;
98 static IntStack CPUStack = STATIC_INTSTACK_INITIALIZER;
101 #define MAX_PUSHED_SEGMENTS 16
102 static Collection SegStack = STATIC_COLLECTION_INITIALIZER;
106 /*****************************************************************************/
108 /*****************************************************************************/
112 static void DoUnexpected (void);
113 /* Got an unexpected keyword */
115 static void DoInvalid (void);
116 /* Handle a token that is invalid here, since it should have been handled on
117 * a much lower level of the expression hierarchy. Getting this sort of token
118 * means that the lower level code has bugs.
119 * This function differs to DoUnexpected in that the latter may be triggered
120 * by the user by using keywords in the wrong location. DoUnexpected is not
121 * an error in the assembler itself, while DoInvalid is.
126 /*****************************************************************************/
127 /* Helper functions */
128 /*****************************************************************************/
132 static unsigned char OptionalAddrSize (void)
133 /* If a colon follows, parse an optional address size spec and return it.
134 * Otherwise return ADDR_SIZE_DEFAULT.
137 unsigned AddrSize = ADDR_SIZE_DEFAULT;
138 if (CurTok.Tok == TOK_COLON) {
140 AddrSize = ParseAddrSize ();
141 if (!ValidAddrSizeForCPU (AddrSize)) {
142 /* Print an error and reset to default */
143 Error ("Invalid address size specification for current CPU");
144 AddrSize = ADDR_SIZE_DEFAULT;
153 static void SetBoolOption (unsigned char* Flag)
154 /* Read a on/off/+/- option and set flag accordingly */
156 static const char* Keys[] = {
161 if (CurTok.Tok == TOK_PLUS) {
164 } else if (CurTok.Tok == TOK_MINUS) {
167 } else if (CurTok.Tok == TOK_IDENT) {
168 /* Map the keyword to a number */
169 switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
170 case 0: *Flag = 0; NextTok (); break;
171 case 1: *Flag = 1; NextTok (); break;
172 default: ErrorSkip ("`on' or `off' expected"); break;
174 } else if (TokIsSep (CurTok.Tok)) {
175 /* Without anything assume switch on */
178 ErrorSkip ("`on' or `off' expected");
184 static void ExportWithAssign (SymEntry* Sym, unsigned char AddrSize, unsigned Flags)
185 /* Allow to assign the value of an export in an .export statement */
187 /* The name and optional address size spec may be followed by an assignment
190 if (CurTok.Tok == TOK_ASSIGN || CurTok.Tok == TOK_EQ) {
192 /* Assignment means the symbol is a label */
193 if (CurTok.Tok == TOK_ASSIGN) {
197 /* Skip the assignment token */
200 /* Define the symbol with the expression following the '=' */
201 SymDef (Sym, Expression(), ADDR_SIZE_DEFAULT, Flags);
205 /* Now export the symbol */
206 SymExport (Sym, AddrSize, Flags);
211 static void ExportImport (void (*Func) (SymEntry*, unsigned char, unsigned),
212 unsigned char DefAddrSize, unsigned Flags)
213 /* Export or import symbols */
216 unsigned char AddrSize;
220 /* We need an identifier here */
221 if (CurTok.Tok != TOK_IDENT) {
222 ErrorSkip ("Identifier expected");
226 /* Find the symbol table entry, allocate a new one if necessary */
227 Sym = SymFind (CurrentScope, &CurTok.SVal, SYM_ALLOC_NEW);
232 /* Get an optional address size */
233 AddrSize = OptionalAddrSize ();
234 if (AddrSize == ADDR_SIZE_DEFAULT) {
235 AddrSize = DefAddrSize;
238 /* Call the actual import/export function */
239 Func (Sym, AddrSize, Flags);
242 if (CurTok.Tok == TOK_COMMA) {
252 static long IntArg (long Min, long Max)
253 /* Read an integer argument and check a range. Accept the token "unlimited"
254 * and return -1 in this case.
257 if (CurTok.Tok == TOK_IDENT && SB_CompareStr (&CurTok.SVal, "unlimited") == 0) {
261 long Val = ConstExpression ();
262 if (Val < Min || Val > Max) {
263 Error ("Range error");
272 static void ConDes (const StrBuf* Name, unsigned Type)
273 /* Parse remaining line for constructor/destructor of the remaining type */
278 /* Find the symbol table entry, allocate a new one if necessary */
279 SymEntry* Sym = SymFind (CurrentScope, Name, SYM_ALLOC_NEW);
281 /* Optional constructor priority */
282 if (CurTok.Tok == TOK_COMMA) {
283 /* Priority value follows */
285 Prio = ConstExpression ();
286 if (Prio < CD_PRIO_MIN || Prio > CD_PRIO_MAX) {
287 /* Value out of range */
288 Error ("Range error");
292 /* Use the default priority value */
296 /* Define the symbol */
297 SymConDes (Sym, ADDR_SIZE_DEFAULT, Type, (unsigned) Prio);
302 static StrBuf* GenArrayType (StrBuf* Type, unsigned SpanSize,
303 const char* ElementType,
304 unsigned ElementTypeLen)
305 /* Create an array (or single data) of the given type. SpanSize is the size
306 * of the span, ElementType is a string that encodes the element data type.
307 * The function returns Type.
310 /* Get the size of the element type */
311 unsigned ElementSize = GT_GET_SIZE (ElementType[0]);
313 /* Get the number of array elements */
314 unsigned ElementCount = SpanSize / ElementSize;
316 /* The span size must be divideable by the element size */
317 CHECK ((SpanSize % ElementSize) == 0);
319 /* Encode the array */
320 GT_AddArray (Type, ElementCount);
321 SB_AppendBuf (Type, ElementType, ElementTypeLen);
323 /* Return the pointer to the created array type */
329 /*****************************************************************************/
330 /* Handler functions */
331 /*****************************************************************************/
335 static void DoA16 (void)
336 /* Switch the accu to 16 bit mode (assembler only) */
338 if (GetCPU() != CPU_65816) {
339 Error ("Command is only valid in 65816 mode");
341 /* Immidiate mode has two extension bytes */
342 ExtBytes [AM65I_IMM_ACCU] = 2;
348 static void DoA8 (void)
349 /* Switch the accu to 8 bit mode (assembler only) */
351 if (GetCPU() != CPU_65816) {
352 Error ("Command is only valid in 65816 mode");
354 /* Immidiate mode has one extension byte */
355 ExtBytes [AM65I_IMM_ACCU] = 1;
361 static void DoAddr (void)
362 /* Define addresses */
364 /* Element type for the generated array */
365 static const char EType[2] = { GT_PTR, GT_VOID };
367 /* Record type information */
368 Span* S = OpenSpan ();
369 StrBuf Type = STATIC_STRBUF_INITIALIZER;
371 /* Parse arguments */
373 ExprNode* Expr = Expression ();
374 if (GetCPU () == CPU_65816 || ForceRange) {
375 /* Do a range check */
376 Expr = GenWordExpr (Expr);
379 if (CurTok.Tok != TOK_COMMA) {
386 /* Close the span, then add type information to it */
388 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
390 /* Free the strings */
396 static void DoAlign (void)
397 /* Align the PC to some boundary */
402 /* Read the alignment value */
403 Alignment = ConstExpression ();
404 if (Alignment <= 0 || (unsigned long) Alignment > MAX_ALIGNMENT) {
405 ErrorSkip ("Range error");
409 /* Optional value follows */
410 if (CurTok.Tok == TOK_COMMA) {
412 FillVal = ConstExpression ();
413 /* We need a byte value here */
414 if (!IsByteRange (FillVal)) {
415 ErrorSkip ("Range error");
422 /* Generate the alignment */
423 SegAlign (Alignment, (int) FillVal);
428 static void DoASCIIZ (void)
429 /* Define text with a zero terminator */
432 /* Must have a string constant */
433 if (CurTok.Tok != TOK_STRCON) {
434 ErrorSkip ("String constant expected");
438 /* Translate into target charset and emit */
439 TgtTranslateStrBuf (&CurTok.SVal);
440 EmitStrBuf (&CurTok.SVal);
442 if (CurTok.Tok == TOK_COMMA) {
453 static void DoAssert (void)
454 /* Add an assertion */
456 static const char* ActionTab [] = {
459 "LDWARN", "LDWARNING",
466 /* First we have the expression that has to evaluated */
467 ExprNode* Expr = Expression ();
471 if (CurTok.Tok != TOK_IDENT) {
472 ErrorSkip ("Identifier expected");
475 switch (GetSubKey (ActionTab, sizeof (ActionTab) / sizeof (ActionTab[0]))) {
480 Action = ASSERT_ACT_WARN;
485 Action = ASSERT_ACT_ERROR;
491 Action = ASSERT_ACT_LDWARN;
496 Action = ASSERT_ACT_LDERROR;
500 Error ("Illegal assert action specifier");
501 /* Use lderror - there won't be an .o file anyway */
502 Action = ASSERT_ACT_LDERROR;
508 /* We can have an optional message. If no message is present, use
509 * "Assertion failed".
511 if (CurTok.Tok == TOK_COMMA) {
516 /* Read the message */
517 if (CurTok.Tok != TOK_STRCON) {
518 ErrorSkip ("String constant expected");
522 /* Translate the message into a string id. We can then skip the input
525 Msg = GetStrBufId (&CurTok.SVal);
530 /* Use "Assertion failed" */
531 Msg = GetStringId ("Assertion failed");
535 /* Remember the assertion */
536 AddAssertion (Expr, (AssertAction) Action, Msg);
541 static void DoAutoImport (void)
542 /* Mark unresolved symbols as imported */
544 SetBoolOption (&AutoImport);
548 static void DoBankBytes (void)
549 /* Define bytes, extracting the bank byte from each expression in the list */
552 EmitByte (FuncBankByte ());
553 if (CurTok.Tok != TOK_COMMA) {
563 static void DoBss (void)
564 /* Switch to the BSS segment */
571 static void DoByte (void)
574 /* Element type for the generated array */
575 static const char EType[1] = { GT_BYTE };
577 /* Record type information */
578 Span* S = OpenSpan ();
579 StrBuf Type = STATIC_STRBUF_INITIALIZER;
581 /* Parse arguments */
583 if (CurTok.Tok == TOK_STRCON) {
584 /* A string, translate into target charset and emit */
585 TgtTranslateStrBuf (&CurTok.SVal);
586 EmitStrBuf (&CurTok.SVal);
589 EmitByte (BoundedExpr (Expression, 1));
591 if (CurTok.Tok != TOK_COMMA) {
595 /* Do smart handling of dangling comma */
596 if (CurTok.Tok == TOK_SEP) {
597 Error ("Unexpected end of line");
603 /* Close the span, then add type information to it */
605 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
607 /* Free the type string */
613 static void DoCase (void)
614 /* Switch the IgnoreCase option */
616 SetBoolOption (&IgnoreCase);
617 IgnoreCase = !IgnoreCase;
622 static void DoCharMap (void)
623 /* Allow custome character mappings */
628 /* Read the index as numerical value */
629 Index = ConstExpression ();
630 if (Index < 0 || Index > 255) {
631 /* Value out of range */
632 ErrorSkip ("Range error");
639 /* Read the character code */
640 Code = ConstExpression ();
641 if (Code < 0 || Code > 255) {
642 /* Value out of range */
643 ErrorSkip ("Range error");
647 /* Set the character translation */
648 TgtTranslateSet ((unsigned) Index, (unsigned char) Code);
653 static void DoCode (void)
654 /* Switch to the code segment */
656 UseSeg (&CodeSegDef);
661 static void DoConDes (void)
662 /* Export a symbol as constructor/destructor */
664 static const char* Keys[] = {
669 StrBuf Name = STATIC_STRBUF_INITIALIZER;
672 /* Symbol name follows */
673 if (CurTok.Tok != TOK_IDENT) {
674 ErrorSkip ("Identifier expected");
677 SB_Copy (&Name, &CurTok.SVal);
680 /* Type follows. May be encoded as identifier or numerical */
682 if (CurTok.Tok == TOK_IDENT) {
684 /* Map the following keyword to a number, then skip it */
685 Type = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
688 /* Check if we got a valid keyword */
690 ErrorSkip ("Syntax error");
696 /* Read the type as numerical value */
697 Type = ConstExpression ();
698 if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) {
699 /* Value out of range */
700 ErrorSkip ("Range error");
706 /* Parse the remainder of the line and export the symbol */
707 ConDes (&Name, (unsigned) Type);
710 /* Free string memory */
716 static void DoConstructor (void)
717 /* Export a symbol as constructor */
719 StrBuf Name = STATIC_STRBUF_INITIALIZER;
721 /* Symbol name follows */
722 if (CurTok.Tok != TOK_IDENT) {
723 ErrorSkip ("Identifier expected");
726 SB_Copy (&Name, &CurTok.SVal);
729 /* Parse the remainder of the line and export the symbol */
730 ConDes (&Name, CD_TYPE_CON);
732 /* Free string memory */
738 static void DoData (void)
739 /* Switch to the data segment */
741 UseSeg (&DataSegDef);
746 static void DoDbg (void)
747 /* Add debug information from high level code */
749 static const char* Keys[] = {
758 /* We expect a subkey */
759 if (CurTok.Tok != TOK_IDENT) {
760 ErrorSkip ("Identifier expected");
764 /* Map the following keyword to a number */
765 Key = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
767 /* Skip the subkey */
770 /* Check the key and dispatch to a handler */
772 case 0: DbgInfoFile (); break;
773 case 1: DbgInfoFunc (); break;
774 case 2: DbgInfoLine (); break;
775 case 3: DbgInfoSym (); break;
776 default: ErrorSkip ("Syntax error"); break;
782 static void DoDByt (void)
783 /* Output double bytes */
785 /* Element type for the generated array */
786 static const char EType[1] = { GT_DBYTE };
788 /* Record type information */
789 Span* S = OpenSpan ();
790 StrBuf Type = STATIC_STRBUF_INITIALIZER;
792 /* Parse arguments */
794 EmitWord (GenSwapExpr (BoundedExpr (Expression, 2)));
795 if (CurTok.Tok != TOK_COMMA) {
802 /* Close the span, then add type information to it */
804 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
806 /* Free the type string */
812 static void DoDebugInfo (void)
813 /* Switch debug info on or off */
815 SetBoolOption (&DbgSyms);
820 static void DoDefine (void)
821 /* Define a one line macro */
823 MacDef (MAC_STYLE_DEFINE);
828 static void DoDelMac (void)
829 /* Delete a classic macro */
831 /* We expect an identifier */
832 if (CurTok.Tok != TOK_IDENT) {
833 ErrorSkip ("Identifier expected");
835 MacUndef (&CurTok.SVal, MAC_STYLE_CLASSIC);
842 static void DoDestructor (void)
843 /* Export a symbol as destructor */
845 StrBuf Name = STATIC_STRBUF_INITIALIZER;
847 /* Symbol name follows */
848 if (CurTok.Tok != TOK_IDENT) {
849 ErrorSkip ("Identifier expected");
852 SB_Copy (&Name, &CurTok.SVal);
855 /* Parse the remainder of the line and export the symbol */
856 ConDes (&Name, CD_TYPE_DES);
858 /* Free string memory */
864 static void DoDWord (void)
868 EmitDWord (BoundedExpr (Expression, 4));
869 if (CurTok.Tok != TOK_COMMA) {
879 static void DoEnd (void)
880 /* End of assembly */
888 static void DoEndProc (void)
889 /* Leave a lexical level */
891 if (CurrentScope->Type != SCOPE_SCOPE || CurrentScope->Label == 0) {
893 ErrorSkip ("No open .PROC");
901 static void DoEndScope (void)
902 /* Leave a lexical level */
904 if (CurrentScope->Type != SCOPE_SCOPE || CurrentScope->Label != 0) {
906 ErrorSkip ("No open .SCOPE");
914 static void DoError (void)
917 if (CurTok.Tok != TOK_STRCON) {
918 ErrorSkip ("String constant expected");
920 Error ("User error: %m%p", &CurTok.SVal);
927 static void DoExitMacro (void)
928 /* Exit a macro expansion */
930 if (!InMacExpansion ()) {
931 /* We aren't expanding a macro currently */
940 static void DoExport (void)
941 /* Export a symbol */
943 ExportImport (ExportWithAssign, ADDR_SIZE_DEFAULT, SF_NONE);
948 static void DoExportZP (void)
949 /* Export a zeropage symbol */
951 ExportImport (ExportWithAssign, ADDR_SIZE_ZP, SF_NONE);
956 static void DoFarAddr (void)
957 /* Define far addresses (24 bit) */
959 /* Element type for the generated array */
960 static const char EType[2] = { GT_FAR_PTR, GT_VOID };
962 /* Record type information */
963 Span* S = OpenSpan ();
964 StrBuf Type = STATIC_STRBUF_INITIALIZER;
966 /* Parse arguments */
968 EmitFarAddr (BoundedExpr (Expression, 3));
969 if (CurTok.Tok != TOK_COMMA) {
976 /* Close the span, then add type information to it */
978 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
980 /* Free the type string */
986 static void DoFatal (void)
987 /* Fatal user error */
989 if (CurTok.Tok != TOK_STRCON) {
990 ErrorSkip ("String constant expected");
992 Fatal ("User error: %m%p", &CurTok.SVal);
999 static void DoFeature (void)
1000 /* Switch the Feature option */
1002 /* Allow a list of comma separated keywords */
1005 /* We expect an identifier */
1006 if (CurTok.Tok != TOK_IDENT) {
1007 ErrorSkip ("Identifier expected");
1011 /* Make the string attribute lower case */
1014 /* Set the feature and check for errors */
1015 if (SetFeature (&CurTok.SVal) == FEAT_UNKNOWN) {
1017 ErrorSkip ("Invalid feature: `%m%p'", &CurTok.SVal);
1020 /* Skip the keyword */
1024 /* Allow more than one keyword */
1025 if (CurTok.Tok == TOK_COMMA) {
1035 static void DoFileOpt (void)
1036 /* Insert a file option */
1040 /* The option type may be given as a keyword or as a number. */
1041 if (CurTok.Tok == TOK_IDENT) {
1043 /* Option given as keyword */
1044 static const char* Keys [] = {
1045 "AUTHOR", "COMMENT", "COMPILER"
1048 /* Map the option to a number */
1049 OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
1052 ErrorSkip ("File option keyword expected");
1056 /* Skip the keyword */
1059 /* Must be followed by a comma */
1062 /* We accept only string options for now */
1063 if (CurTok.Tok != TOK_STRCON) {
1064 ErrorSkip ("String constant expected");
1068 /* Insert the option */
1073 OptAuthor (&CurTok.SVal);
1078 OptComment (&CurTok.SVal);
1083 OptCompiler (&CurTok.SVal);
1087 Internal ("Invalid OptNum: %ld", OptNum);
1096 /* Option given as number */
1097 OptNum = ConstExpression ();
1098 if (!IsByteRange (OptNum)) {
1099 ErrorSkip ("Range error");
1103 /* Must be followed by a comma */
1106 /* We accept only string options for now */
1107 if (CurTok.Tok != TOK_STRCON) {
1108 ErrorSkip ("String constant expected");
1112 /* Insert the option */
1113 OptStr ((unsigned char) OptNum, &CurTok.SVal);
1122 static void DoForceImport (void)
1123 /* Do a forced import on a symbol */
1125 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_FORCED);
1130 static void DoGlobal (void)
1131 /* Declare a global symbol */
1133 ExportImport (SymGlobal, ADDR_SIZE_DEFAULT, SF_NONE);
1138 static void DoGlobalZP (void)
1139 /* Declare a global zeropage symbol */
1141 ExportImport (SymGlobal, ADDR_SIZE_ZP, SF_NONE);
1145 static void DoHiBytes (void)
1146 /* Define bytes, extracting the hi byte from each expression in the list */
1149 EmitByte (FuncHiByte ());
1150 if (CurTok.Tok != TOK_COMMA) {
1160 static void DoI16 (void)
1161 /* Switch the index registers to 16 bit mode (assembler only) */
1163 if (GetCPU() != CPU_65816) {
1164 Error ("Command is only valid in 65816 mode");
1166 /* Immidiate mode has two extension bytes */
1167 ExtBytes [AM65I_IMM_INDEX] = 2;
1173 static void DoI8 (void)
1174 /* Switch the index registers to 16 bit mode (assembler only) */
1176 if (GetCPU() != CPU_65816) {
1177 Error ("Command is only valid in 65816 mode");
1179 /* Immidiate mode has one extension byte */
1180 ExtBytes [AM65I_IMM_INDEX] = 1;
1186 static void DoImport (void)
1187 /* Import a symbol */
1189 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_NONE);
1194 static void DoImportZP (void)
1195 /* Import a zero page symbol */
1197 ExportImport (SymImport, ADDR_SIZE_ZP, SF_NONE);
1202 static void DoIncBin (void)
1203 /* Include a binary file */
1205 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1206 struct stat StatBuf;
1212 /* Name must follow */
1213 if (CurTok.Tok != TOK_STRCON) {
1214 ErrorSkip ("String constant expected");
1217 SB_Copy (&Name, &CurTok.SVal);
1218 SB_Terminate (&Name);
1221 /* A starting offset may follow */
1222 if (CurTok.Tok == TOK_COMMA) {
1224 Start = ConstExpression ();
1226 /* And a length may follow */
1227 if (CurTok.Tok == TOK_COMMA) {
1229 Count = ConstExpression ();
1234 /* Try to open the file */
1235 F = fopen (SB_GetConstBuf (&Name), "rb");
1238 /* Search for the file in the binary include directory */
1239 char* PathName = SearchFile (BinSearchPath, SB_GetConstBuf (&Name));
1240 if (PathName == 0 || (F = fopen (PathName, "rb")) == 0) {
1241 /* Not found or cannot open, print an error and bail out */
1242 ErrorSkip ("Cannot open include file `%m%p': %s", &Name, strerror (errno));
1247 /* Remember the new file name */
1248 SB_CopyStr (&Name, PathName);
1250 /* Free the allocated memory */
1254 /* Get the size of the file */
1255 fseek (F, 0, SEEK_END);
1258 /* Stat the file and remember the values. There a race condition here,
1259 * since we cannot use fileno() (non standard identifier in standard
1260 * header file), and therefore not fstat. When using stat with the
1261 * file name, there's a risk that the file was deleted and recreated
1262 * while it was open. Since mtime and size are only used to check
1263 * if a file has changed in the debugger, we will ignore this problem
1266 SB_Terminate (&Name);
1267 if (stat (SB_GetConstBuf (&Name), &StatBuf) != 0) {
1268 Fatal ("Cannot stat input file `%m%p': %s", &Name, strerror (errno));
1271 /* Add the file to the input file table */
1272 AddFile (&Name, FT_BINARY, Size, StatBuf.st_mtime);
1274 /* If a count was not given, calculate it now */
1276 Count = Size - Start;
1278 /* Nothing to read - flag this as a range error */
1279 ErrorSkip ("Range error");
1283 /* Count was given, check if it is valid */
1284 if (Start + Count > Size) {
1285 ErrorSkip ("Range error");
1290 /* Seek to the start position */
1291 fseek (F, Start, SEEK_SET);
1293 /* Read chunks and insert them into the output */
1296 unsigned char Buf [1024];
1298 /* Calculate the number of bytes to read */
1299 size_t BytesToRead = (Count > (long)sizeof(Buf))? sizeof(Buf) : (size_t) Count;
1302 size_t BytesRead = fread (Buf, 1, BytesToRead, F);
1303 if (BytesToRead != BytesRead) {
1304 /* Some sort of error */
1305 ErrorSkip ("Cannot read from include file `%m%p': %s",
1306 &Name, strerror (errno));
1310 /* Insert it into the output */
1311 EmitData (Buf, BytesRead);
1313 /* Keep the counters current */
1318 /* Close the file, ignore errors since it's r/o */
1322 /* Free string memory */
1328 static void DoInclude (void)
1329 /* Include another file */
1331 /* Name must follow */
1332 if (CurTok.Tok != TOK_STRCON) {
1333 ErrorSkip ("String constant expected");
1335 SB_Terminate (&CurTok.SVal);
1336 if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) {
1337 /* Error opening the file, skip remainder of line */
1345 static void DoInterruptor (void)
1346 /* Export a symbol as interruptor */
1348 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1350 /* Symbol name follows */
1351 if (CurTok.Tok != TOK_IDENT) {
1352 ErrorSkip ("Identifier expected");
1355 SB_Copy (&Name, &CurTok.SVal);
1358 /* Parse the remainder of the line and export the symbol */
1359 ConDes (&Name, CD_TYPE_INT);
1361 /* Free string memory */
1367 static void DoInvalid (void)
1368 /* Handle a token that is invalid here, since it should have been handled on
1369 * a much lower level of the expression hierarchy. Getting this sort of token
1370 * means that the lower level code has bugs.
1371 * This function differs to DoUnexpected in that the latter may be triggered
1372 * by the user by using keywords in the wrong location. DoUnexpected is not
1373 * an error in the assembler itself, while DoInvalid is.
1376 Internal ("Unexpected token: %m%p", &Keyword);
1381 static void DoLineCont (void)
1382 /* Switch the use of line continuations */
1384 SetBoolOption (&LineCont);
1389 static void DoList (void)
1390 /* Enable/disable the listing */
1392 /* Get the setting */
1394 SetBoolOption (&List);
1396 /* Manage the counter */
1406 static void DoLoBytes (void)
1407 /* Define bytes, extracting the lo byte from each expression in the list */
1410 EmitByte (FuncLoByte ());
1411 if (CurTok.Tok != TOK_COMMA) {
1420 static void DoListBytes (void)
1421 /* Set maximum number of bytes to list for one line */
1423 SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
1428 static void DoLocalChar (void)
1429 /* Define the character that starts local labels */
1431 if (CurTok.Tok != TOK_CHARCON) {
1432 ErrorSkip ("Character constant expected");
1434 if (CurTok.IVal != '@' && CurTok.IVal != '?') {
1435 Error ("Invalid start character for locals");
1437 LocalStart = (char) CurTok.IVal;
1445 static void DoMacPack (void)
1446 /* Insert a macro package */
1450 /* We expect an identifier */
1451 if (CurTok.Tok != TOK_IDENT) {
1452 ErrorSkip ("Identifier expected");
1456 /* Search for the macro package name */
1458 Package = MacPackFind (&CurTok.SVal);
1461 ErrorSkip ("Invalid macro package");
1465 /* Insert the package. If this fails, skip the remainder of the line to
1466 * avoid additional error messages.
1468 if (MacPackInsert (Package) == 0) {
1475 static void DoMacro (void)
1476 /* Start a macro definition */
1478 MacDef (MAC_STYLE_CLASSIC);
1483 static void DoNull (void)
1484 /* Switch to the NULL segment */
1486 UseSeg (&NullSegDef);
1491 static void DoOrg (void)
1492 /* Start absolute code */
1494 long PC = ConstExpression ();
1495 if (PC < 0 || PC > 0xFFFFFF) {
1496 Error ("Range error");
1499 EnterAbsoluteMode (PC);
1504 static void DoOut (void)
1505 /* Output a string */
1507 if (CurTok.Tok != TOK_STRCON) {
1508 ErrorSkip ("String constant expected");
1510 /* Output the string and be sure to flush the output to keep it in
1511 * sync with any error messages if the output is redirected to a file.
1514 (int) SB_GetLen (&CurTok.SVal),
1515 SB_GetConstBuf (&CurTok.SVal));
1523 static void DoP02 (void)
1524 /* Switch to 6502 CPU */
1531 static void DoPC02 (void)
1532 /* Switch to 65C02 CPU */
1539 static void DoP816 (void)
1540 /* Switch to 65816 CPU */
1547 static void DoPageLength (void)
1548 /* Set the page length for the listing */
1550 PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
1555 static void DoPopCPU (void)
1556 /* Pop an old CPU setting from the CPU stack */
1558 /* Must have a CPU on the stack */
1559 if (IS_IsEmpty (&CPUStack)) {
1560 ErrorSkip ("CPU stack is empty");
1564 /* Set the CPU to the value popped from stack */
1565 SetCPU (IS_Pop (&CPUStack));
1570 static void DoPopSeg (void)
1571 /* Pop an old segment from the segment stack */
1575 /* Must have a segment on the stack */
1576 if (CollCount (&SegStack) == 0) {
1577 ErrorSkip ("Segment stack is empty");
1581 /* Pop the last element */
1582 Def = CollPop (&SegStack);
1584 /* Restore this segment */
1587 /* Delete the segment definition */
1593 static void DoProc (void)
1594 /* Start a new lexical scope */
1596 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1597 unsigned char AddrSize;
1601 if (CurTok.Tok == TOK_IDENT) {
1603 /* The new scope has a name. Remember it. */
1604 SB_Copy (&Name, &CurTok.SVal);
1606 /* Search for the symbol, generate a new one if needed */
1607 Sym = SymFind (CurrentScope, &Name, SYM_ALLOC_NEW);
1609 /* Skip the scope name */
1612 /* Read an optional address size specifier */
1613 AddrSize = OptionalAddrSize ();
1615 /* Mark the symbol as defined */
1616 SymDef (Sym, GenCurrentPC (), AddrSize, SF_LABEL);
1620 /* A .PROC statement without a name */
1621 Warning (1, "Unnamed .PROCs are deprecated, please use .SCOPE");
1622 AnonName (&Name, "PROC");
1623 AddrSize = ADDR_SIZE_DEFAULT;
1627 /* Enter a new scope */
1628 SymEnterLevel (&Name, SCOPE_SCOPE, AddrSize, Sym);
1630 /* Free memory for Name */
1636 static void DoPSC02 (void)
1637 /* Switch to 65SC02 CPU */
1639 SetCPU (CPU_65SC02);
1644 static void DoPushCPU (void)
1645 /* Push the current CPU setting onto the CPU stack */
1647 /* Can only push a limited size of segments */
1648 if (IS_IsFull (&CPUStack)) {
1649 ErrorSkip ("CPU stack overflow");
1653 /* Get the current segment and push it */
1654 IS_Push (&CPUStack, GetCPU ());
1659 static void DoPushSeg (void)
1660 /* Push the current segment onto the segment stack */
1662 /* Can only push a limited size of segments */
1663 if (CollCount (&SegStack) >= MAX_PUSHED_SEGMENTS) {
1664 ErrorSkip ("Segment stack overflow");
1668 /* Get the current segment and push it */
1669 CollAppend (&SegStack, DupSegDef (GetCurrentSegDef ()));
1674 static void DoReloc (void)
1675 /* Enter relocatable mode */
1682 static void DoRepeat (void)
1683 /* Repeat some instruction block */
1690 static void DoRes (void)
1691 /* Reserve some number of storage bytes */
1696 Count = ConstExpression ();
1697 if (Count > 0xFFFF || Count < 0) {
1698 ErrorSkip ("Range error");
1701 if (CurTok.Tok == TOK_COMMA) {
1703 Val = ConstExpression ();
1704 /* We need a byte value here */
1705 if (!IsByteRange (Val)) {
1706 ErrorSkip ("Range error");
1710 /* Emit constant values */
1712 Emit0 ((unsigned char) Val);
1716 /* Emit fill fragments */
1723 static void DoROData (void)
1724 /* Switch to the r/o data segment */
1726 UseSeg (&RODataSegDef);
1731 static void DoScope (void)
1732 /* Start a local scope */
1734 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1735 unsigned char AddrSize;
1738 if (CurTok.Tok == TOK_IDENT) {
1740 /* The new scope has a name. Remember and skip it. */
1741 SB_Copy (&Name, &CurTok.SVal);
1746 /* An unnamed scope */
1747 AnonName (&Name, "SCOPE");
1751 /* Read an optional address size specifier */
1752 AddrSize = OptionalAddrSize ();
1754 /* Enter the new scope */
1755 SymEnterLevel (&Name, SCOPE_SCOPE, AddrSize, 0);
1757 /* Free memory for Name */
1763 static void DoSegment (void)
1764 /* Switch to another segment */
1766 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1769 if (CurTok.Tok != TOK_STRCON) {
1770 ErrorSkip ("String constant expected");
1773 /* Save the name of the segment and skip it */
1774 SB_Copy (&Name, &CurTok.SVal);
1777 /* Use the name for the segment definition */
1778 SB_Terminate (&Name);
1779 Def.Name = SB_GetBuf (&Name);
1781 /* Check for an optional address size modifier */
1782 Def.AddrSize = OptionalAddrSize ();
1784 /* Set the segment */
1788 /* Free memory for Name */
1794 static void DoSetCPU (void)
1795 /* Switch the CPU instruction set */
1797 /* We expect an identifier */
1798 if (CurTok.Tok != TOK_STRCON) {
1799 ErrorSkip ("String constant expected");
1803 /* Try to find the CPU */
1804 SB_Terminate (&CurTok.SVal);
1805 CPU = FindCPU (SB_GetConstBuf (&CurTok.SVal));
1807 /* Switch to the new CPU */
1810 /* Skip the identifier. If the CPU switch was successful, the scanner
1811 * will treat the input now correctly for the new CPU.
1819 static void DoSmart (void)
1820 /* Smart mode on/off */
1822 SetBoolOption (&SmartMode);
1827 static void DoSunPlus (void)
1828 /* Switch to the SUNPLUS CPU */
1830 SetCPU (CPU_SUNPLUS);
1835 static void DoTag (void)
1836 /* Allocate space for a struct */
1841 /* Read the struct name */
1842 SymTable* Struct = ParseScopedSymTable ();
1844 /* Check the supposed struct */
1846 ErrorSkip ("Unknown struct");
1849 if (GetSymTabType (Struct) != SCOPE_STRUCT) {
1850 ErrorSkip ("Not a struct");
1854 /* Get the symbol that defines the size of the struct */
1855 SizeSym = GetSizeOfScope (Struct);
1857 /* Check if it does exist and if its value is known */
1858 if (SizeSym == 0 || !SymIsConst (SizeSym, &Size)) {
1859 ErrorSkip ("Size of struct/union is unknown");
1863 /* Optional multiplicator may follow */
1864 if (CurTok.Tok == TOK_COMMA) {
1867 Multiplicator = ConstExpression ();
1868 /* Multiplicator must make sense */
1869 if (Multiplicator <= 0) {
1870 ErrorSkip ("Range error");
1873 Size *= Multiplicator;
1876 /* Emit fill fragments */
1882 static void DoUnDef (void)
1883 /* Undefine a define style macro */
1885 /* The function is called with the .UNDEF token in place, because we need
1886 * to disable .define macro expansions before reading the next token.
1887 * Otherwise the name of the macro would be expanded, so we would never
1890 DisableDefineStyleMacros ();
1892 EnableDefineStyleMacros ();
1894 /* We expect an identifier */
1895 if (CurTok.Tok != TOK_IDENT) {
1896 ErrorSkip ("Identifier expected");
1898 MacUndef (&CurTok.SVal, MAC_STYLE_DEFINE);
1905 static void DoUnexpected (void)
1906 /* Got an unexpected keyword */
1908 Error ("Unexpected `%m%p'", &Keyword);
1914 static void DoWarning (void)
1917 if (CurTok.Tok != TOK_STRCON) {
1918 ErrorSkip ("String constant expected");
1920 Warning (0, "User warning: %m%p", &CurTok.SVal);
1927 static void DoWord (void)
1930 /* Element type for the generated array */
1931 static const char EType[1] = { GT_WORD };
1933 /* Record type information */
1934 Span* S = OpenSpan ();
1935 StrBuf Type = STATIC_STRBUF_INITIALIZER;
1937 /* Parse arguments */
1939 EmitWord (BoundedExpr (Expression, 2));
1940 if (CurTok.Tok != TOK_COMMA) {
1947 /* Close the span, then add type information to it */
1949 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
1951 /* Free the type string */
1957 static void DoZeropage (void)
1958 /* Switch to the zeropage segment */
1960 UseSeg (&ZeropageSegDef);
1965 /*****************************************************************************/
1967 /*****************************************************************************/
1971 /* Control commands flags */
1973 ccNone = 0x0000, /* No special flags */
1974 ccKeepToken = 0x0001 /* Do not skip the current token */
1977 /* Control command table */
1978 typedef struct CtrlDesc CtrlDesc;
1980 unsigned Flags; /* Flags for this directive */
1981 void (*Handler) (void); /* Command handler */
1984 #define PSEUDO_COUNT (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1985 static CtrlDesc CtrlCmdTab [] = {
1988 { ccNone, DoAddr }, /* .ADDR */
1989 { ccNone, DoAlign },
1990 { ccNone, DoASCIIZ },
1991 { ccNone, DoAssert },
1992 { ccNone, DoAutoImport },
1993 { ccNone, DoUnexpected }, /* .BANK */
1994 { ccNone, DoUnexpected }, /* .BANKBYTE */
1995 { ccNone, DoBankBytes },
1996 { ccNone, DoUnexpected }, /* .BLANK */
2000 { ccNone, DoCharMap },
2002 { ccNone, DoUnexpected, }, /* .CONCAT */
2003 { ccNone, DoConDes },
2004 { ccNone, DoUnexpected }, /* .CONST */
2005 { ccNone, DoConstructor },
2006 { ccNone, DoUnexpected }, /* .CPU */
2010 { ccNone, DoDebugInfo },
2011 { ccNone, DoDefine },
2012 { ccNone, DoUnexpected }, /* .DEFINED */
2013 { ccNone, DoDelMac },
2014 { ccNone, DoDestructor },
2015 { ccNone, DoDWord },
2016 { ccKeepToken, DoConditionals }, /* .ELSE */
2017 { ccKeepToken, DoConditionals }, /* .ELSEIF */
2018 { ccKeepToken, DoEnd },
2019 { ccNone, DoUnexpected }, /* .ENDENUM */
2020 { ccKeepToken, DoConditionals }, /* .ENDIF */
2021 { ccNone, DoUnexpected }, /* .ENDMACRO */
2022 { ccNone, DoEndProc },
2023 { ccNone, DoUnexpected }, /* .ENDREPEAT */
2024 { ccNone, DoEndScope },
2025 { ccNone, DoUnexpected }, /* .ENDSTRUCT */
2026 { ccNone, DoUnexpected }, /* .ENDUNION */
2028 { ccNone, DoError },
2029 { ccNone, DoExitMacro },
2030 { ccNone, DoExport },
2031 { ccNone, DoExportZP },
2032 { ccNone, DoFarAddr },
2033 { ccNone, DoFatal },
2034 { ccNone, DoFeature },
2035 { ccNone, DoFileOpt },
2036 { ccNone, DoForceImport },
2037 { ccNone, DoUnexpected }, /* .FORCEWORD */
2038 { ccNone, DoGlobal },
2039 { ccNone, DoGlobalZP },
2040 { ccNone, DoUnexpected }, /* .HIBYTE */
2041 { ccNone, DoHiBytes },
2042 { ccNone, DoUnexpected }, /* .HIWORD */
2045 { ccNone, DoUnexpected }, /* .IDENT */
2046 { ccKeepToken, DoConditionals }, /* .IF */
2047 { ccKeepToken, DoConditionals }, /* .IFBLANK */
2048 { ccKeepToken, DoConditionals }, /* .IFCONST */
2049 { ccKeepToken, DoConditionals }, /* .IFDEF */
2050 { ccKeepToken, DoConditionals }, /* .IFNBLANK */
2051 { ccKeepToken, DoConditionals }, /* .IFNCONST */
2052 { ccKeepToken, DoConditionals }, /* .IFNDEF */
2053 { ccKeepToken, DoConditionals }, /* .IFNREF */
2054 { ccKeepToken, DoConditionals }, /* .IFP02 */
2055 { ccKeepToken, DoConditionals }, /* .IFP816 */
2056 { ccKeepToken, DoConditionals }, /* .IFPC02 */
2057 { ccKeepToken, DoConditionals }, /* .IFPSC02 */
2058 { ccKeepToken, DoConditionals }, /* .IFREF */
2059 { ccNone, DoImport },
2060 { ccNone, DoImportZP },
2061 { ccNone, DoIncBin },
2062 { ccNone, DoInclude },
2063 { ccNone, DoInterruptor },
2064 { ccNone, DoInvalid }, /* .LEFT */
2065 { ccNone, DoLineCont },
2067 { ccNone, DoListBytes },
2068 { ccNone, DoUnexpected }, /* .LOBYTE */
2069 { ccNone, DoLoBytes },
2070 { ccNone, DoUnexpected }, /* .LOCAL */
2071 { ccNone, DoLocalChar },
2072 { ccNone, DoUnexpected }, /* .LOWORD */
2073 { ccNone, DoMacPack },
2074 { ccNone, DoMacro },
2075 { ccNone, DoUnexpected }, /* .MATCH */
2076 { ccNone, DoUnexpected }, /* .MAX */
2077 { ccNone, DoInvalid }, /* .MID */
2078 { ccNone, DoUnexpected }, /* .MIN */
2084 { ccNone, DoPageLength },
2085 { ccNone, DoUnexpected }, /* .PARAMCOUNT */
2087 { ccNone, DoPopCPU },
2088 { ccNone, DoPopSeg },
2090 { ccNone, DoPSC02 },
2091 { ccNone, DoPushCPU },
2092 { ccNone, DoPushSeg },
2093 { ccNone, DoUnexpected }, /* .REFERENCED */
2094 { ccNone, DoReloc },
2095 { ccNone, DoRepeat },
2097 { ccNone, DoInvalid }, /* .RIGHT */
2098 { ccNone, DoROData },
2099 { ccNone, DoScope },
2100 { ccNone, DoSegment },
2101 { ccNone, DoUnexpected }, /* .SET */
2102 { ccNone, DoSetCPU },
2103 { ccNone, DoUnexpected }, /* .SIZEOF */
2104 { ccNone, DoSmart },
2105 { ccNone, DoUnexpected }, /* .SPRINTF */
2106 { ccNone, DoUnexpected }, /* .STRAT */
2107 { ccNone, DoUnexpected }, /* .STRING */
2108 { ccNone, DoUnexpected }, /* .STRLEN */
2109 { ccNone, DoStruct },
2110 { ccNone, DoSunPlus },
2112 { ccNone, DoUnexpected }, /* .TCOUNT */
2113 { ccNone, DoUnexpected }, /* .TIME */
2114 { ccKeepToken, DoUnDef },
2115 { ccNone, DoUnion },
2116 { ccNone, DoUnexpected }, /* .VERSION */
2117 { ccNone, DoWarning },
2119 { ccNone, DoUnexpected }, /* .XMATCH */
2120 { ccNone, DoZeropage },
2125 /*****************************************************************************/
2127 /*****************************************************************************/
2131 void HandlePseudo (void)
2132 /* Handle a pseudo instruction */
2136 /* Calculate the index into the table */
2137 unsigned Index = CurTok.Tok - TOK_FIRSTPSEUDO;
2140 if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
2141 Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
2142 (unsigned) PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
2144 CHECK (Index < PSEUDO_COUNT);
2146 /* Get the pseudo intruction descriptor */
2147 D = &CtrlCmdTab [Index];
2149 /* Remember the instruction, then skip it if needed */
2150 if ((D->Flags & ccKeepToken) == 0) {
2151 SB_Copy (&Keyword, &CurTok.SVal);
2155 /* Call the handler */
2161 void CheckPseudo (void)
2162 /* Check if the stacks are empty at end of assembly */
2164 if (CollCount (&SegStack) != 0) {
2165 Warning (1, "Segment stack is not empty");
2167 if (!IS_IsEmpty (&CPUStack)) {
2168 Warning (1, "CPU stack is not empty");