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"
86 /*****************************************************************************/
88 /*****************************************************************************/
92 /* Keyword we're about to handle */
93 static StrBuf Keyword = STATIC_STRBUF_INITIALIZER;
96 static IntStack CPUStack = STATIC_INTSTACK_INITIALIZER;
99 #define MAX_PUSHED_SEGMENTS 16
100 static Collection SegStack = STATIC_COLLECTION_INITIALIZER;
104 /*****************************************************************************/
106 /*****************************************************************************/
110 static void DoUnexpected (void);
111 /* Got an unexpected keyword */
113 static void DoInvalid (void);
114 /* Handle a token that is invalid here, since it should have been handled on
115 ** a much lower level of the expression hierarchy. Getting this sort of token
116 ** means that the lower level code has bugs.
117 ** This function differs to DoUnexpected in that the latter may be triggered
118 ** by the user by using keywords in the wrong location. DoUnexpected is not
119 ** an error in the assembler itself, while DoInvalid is.
124 /*****************************************************************************/
125 /* Helper functions */
126 /*****************************************************************************/
130 static unsigned char OptionalAddrSize (void)
131 /* If a colon follows, parse an optional address size spec and return it.
132 ** Otherwise return ADDR_SIZE_DEFAULT.
135 unsigned AddrSize = ADDR_SIZE_DEFAULT;
136 if (CurTok.Tok == TOK_COLON) {
138 AddrSize = ParseAddrSize ();
139 if (!ValidAddrSizeForCPU (AddrSize)) {
140 /* Print an error and reset to default */
141 Error ("Invalid address size specification for current CPU");
142 AddrSize = ADDR_SIZE_DEFAULT;
151 static void SetBoolOption (unsigned char* Flag)
152 /* Read a on/off/+/- option and set flag accordingly */
154 static const char* Keys[] = {
159 if (CurTok.Tok == TOK_PLUS) {
162 } else if (CurTok.Tok == TOK_MINUS) {
165 } else if (CurTok.Tok == TOK_IDENT) {
166 /* Map the keyword to a number */
167 switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
168 case 0: *Flag = 0; NextTok (); break;
169 case 1: *Flag = 1; NextTok (); break;
170 default: ErrorSkip ("`on' or `off' expected"); break;
172 } else if (TokIsSep (CurTok.Tok)) {
173 /* Without anything assume switch on */
176 ErrorSkip ("`on' or `off' expected");
182 static void ExportWithAssign (SymEntry* Sym, unsigned char AddrSize, unsigned Flags)
183 /* Allow to assign the value of an export in an .export statement */
185 /* The name and optional address size spec may be followed by an assignment
188 if (CurTok.Tok == TOK_ASSIGN || CurTok.Tok == TOK_EQ) {
190 /* Assignment means the symbol is a label */
191 if (CurTok.Tok == TOK_ASSIGN) {
195 /* Skip the assignment token */
198 /* Define the symbol with the expression following the '=' */
199 SymDef (Sym, Expression(), ADDR_SIZE_DEFAULT, Flags);
203 /* Now export the symbol */
204 SymExport (Sym, AddrSize, Flags);
209 static void ExportImport (void (*Func) (SymEntry*, unsigned char, unsigned),
210 unsigned char DefAddrSize, unsigned Flags)
211 /* Export or import symbols */
214 unsigned char AddrSize;
218 /* We need an identifier here */
219 if (CurTok.Tok != TOK_IDENT) {
220 ErrorSkip ("Identifier expected");
224 /* Find the symbol table entry, allocate a new one if necessary */
225 Sym = SymFind (CurrentScope, &CurTok.SVal, SYM_ALLOC_NEW);
230 /* Get an optional address size */
231 AddrSize = OptionalAddrSize ();
232 if (AddrSize == ADDR_SIZE_DEFAULT) {
233 AddrSize = DefAddrSize;
236 /* Call the actual import/export function */
237 Func (Sym, AddrSize, Flags);
240 if (CurTok.Tok == TOK_COMMA) {
250 static long IntArg (long Min, long Max)
251 /* Read an integer argument and check a range. Accept the token "unlimited"
252 ** and return -1 in this case.
255 if (CurTok.Tok == TOK_IDENT && SB_CompareStr (&CurTok.SVal, "unlimited") == 0) {
259 long Val = ConstExpression ();
260 if (Val < Min || Val > Max) {
261 Error ("Range error");
270 static void ConDes (const StrBuf* Name, unsigned Type)
271 /* Parse remaining line for constructor/destructor of the remaining type */
276 /* Find the symbol table entry, allocate a new one if necessary */
277 SymEntry* Sym = SymFind (CurrentScope, Name, SYM_ALLOC_NEW);
279 /* Optional constructor priority */
280 if (CurTok.Tok == TOK_COMMA) {
281 /* Priority value follows */
283 Prio = ConstExpression ();
284 if (Prio < CD_PRIO_MIN || Prio > CD_PRIO_MAX) {
285 /* Value out of range */
286 Error ("Range error");
290 /* Use the default priority value */
294 /* Define the symbol */
295 SymConDes (Sym, ADDR_SIZE_DEFAULT, Type, (unsigned) Prio);
300 static StrBuf* GenArrayType (StrBuf* Type, unsigned SpanSize,
301 const char* ElementType,
302 unsigned ElementTypeLen)
303 /* Create an array (or single data) of the given type. SpanSize is the size
304 ** of the span, ElementType is a string that encodes the element data type.
305 ** The function returns Type.
308 /* Get the size of the element type */
309 unsigned ElementSize = GT_GET_SIZE (ElementType[0]);
311 /* Get the number of array elements */
312 unsigned ElementCount = SpanSize / ElementSize;
314 /* The span size must be divideable by the element size */
315 CHECK ((SpanSize % ElementSize) == 0);
317 /* Encode the array */
318 GT_AddArray (Type, ElementCount);
319 SB_AppendBuf (Type, ElementType, ElementTypeLen);
321 /* Return the pointer to the created array type */
327 /*****************************************************************************/
328 /* Handler functions */
329 /*****************************************************************************/
333 static void DoA16 (void)
334 /* Switch the accu to 16 bit mode (assembler only) */
336 if (GetCPU() != CPU_65816) {
337 Error ("Command is only valid in 65816 mode");
339 /* Immidiate mode has two extension bytes */
340 ExtBytes [AM65I_IMM_ACCU] = 2;
346 static void DoA8 (void)
347 /* Switch the accu to 8 bit mode (assembler only) */
349 if (GetCPU() != CPU_65816) {
350 Error ("Command is only valid in 65816 mode");
352 /* Immidiate mode has one extension byte */
353 ExtBytes [AM65I_IMM_ACCU] = 1;
359 static void DoAddr (void)
360 /* Define addresses */
362 /* Element type for the generated array */
363 static const char EType[2] = { GT_PTR, GT_VOID };
365 /* Record type information */
366 Span* S = OpenSpan ();
367 StrBuf Type = STATIC_STRBUF_INITIALIZER;
369 /* Parse arguments */
371 ExprNode* Expr = Expression ();
372 if (GetCPU () == CPU_65816 || ForceRange) {
373 /* Do a range check */
374 Expr = GenWordExpr (Expr);
377 if (CurTok.Tok != TOK_COMMA) {
384 /* Close the span, then add type information to it */
386 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
388 /* Free the strings */
394 static void DoAlign (void)
395 /* Align the PC to some boundary */
400 /* Read the alignment value */
401 Alignment = ConstExpression ();
402 if (Alignment <= 0 || (unsigned long) Alignment > MAX_ALIGNMENT) {
403 ErrorSkip ("Range error");
407 /* Optional value follows */
408 if (CurTok.Tok == TOK_COMMA) {
410 FillVal = ConstExpression ();
411 /* We need a byte value here */
412 if (!IsByteRange (FillVal)) {
413 ErrorSkip ("Range error");
420 /* Generate the alignment */
421 SegAlign (Alignment, (int) FillVal);
426 static void DoASCIIZ (void)
427 /* Define text with a zero terminator */
430 /* Must have a string constant */
431 if (CurTok.Tok != TOK_STRCON) {
432 ErrorSkip ("String constant expected");
436 /* Translate into target charset and emit */
437 TgtTranslateStrBuf (&CurTok.SVal);
438 EmitStrBuf (&CurTok.SVal);
440 if (CurTok.Tok == TOK_COMMA) {
451 static void DoAssert (void)
452 /* Add an assertion */
454 static const char* ActionTab [] = {
457 "LDWARN", "LDWARNING",
464 /* First we have the expression that has to evaluated */
465 ExprNode* Expr = Expression ();
469 if (CurTok.Tok != TOK_IDENT) {
470 ErrorSkip ("Identifier expected");
473 switch (GetSubKey (ActionTab, sizeof (ActionTab) / sizeof (ActionTab[0]))) {
478 Action = ASSERT_ACT_WARN;
483 Action = ASSERT_ACT_ERROR;
489 Action = ASSERT_ACT_LDWARN;
494 Action = ASSERT_ACT_LDERROR;
498 Error ("Illegal assert action specifier");
499 /* Use lderror - there won't be an .o file anyway */
500 Action = ASSERT_ACT_LDERROR;
506 /* We can have an optional message. If no message is present, use
507 ** "Assertion failed".
509 if (CurTok.Tok == TOK_COMMA) {
514 /* Read the message */
515 if (CurTok.Tok != TOK_STRCON) {
516 ErrorSkip ("String constant expected");
520 /* Translate the message into a string id. We can then skip the input
523 Msg = GetStrBufId (&CurTok.SVal);
528 /* Use "Assertion failed" */
529 Msg = GetStringId ("Assertion failed");
533 /* Remember the assertion */
534 AddAssertion (Expr, (AssertAction) Action, Msg);
539 static void DoAutoImport (void)
540 /* Mark unresolved symbols as imported */
542 SetBoolOption (&AutoImport);
546 static void DoBankBytes (void)
547 /* Define bytes, extracting the bank byte from each expression in the list */
550 EmitByte (FuncBankByte ());
551 if (CurTok.Tok != TOK_COMMA) {
561 static void DoBss (void)
562 /* Switch to the BSS segment */
569 static void DoByte (void)
572 /* Element type for the generated array */
573 static const char EType[1] = { GT_BYTE };
575 /* Record type information */
576 Span* S = OpenSpan ();
577 StrBuf Type = STATIC_STRBUF_INITIALIZER;
579 /* Parse arguments */
581 if (CurTok.Tok == TOK_STRCON) {
582 /* A string, translate into target charset and emit */
583 TgtTranslateStrBuf (&CurTok.SVal);
584 EmitStrBuf (&CurTok.SVal);
587 EmitByte (BoundedExpr (Expression, 1));
589 if (CurTok.Tok != TOK_COMMA) {
593 /* Do smart handling of dangling comma */
594 if (CurTok.Tok == TOK_SEP) {
595 Error ("Unexpected end of line");
601 /* Close the span, then add type information to it */
603 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
605 /* Free the type string */
611 static void DoCase (void)
612 /* Switch the IgnoreCase option */
614 SetBoolOption (&IgnoreCase);
615 IgnoreCase = !IgnoreCase;
620 static void DoCharMap (void)
621 /* Allow custome character mappings */
626 /* Read the index as numerical value */
627 Index = ConstExpression ();
628 if (Index <= 0 || Index > 255) {
629 /* Value out of range */
630 ErrorSkip ("Range error");
637 /* Read the character code */
638 Code = ConstExpression ();
639 if (Code < 0 || Code > 255) {
640 /* Value out of range */
641 ErrorSkip ("Range error");
645 /* Set the character translation */
646 TgtTranslateSet ((unsigned) Index, (unsigned char) Code);
651 static void DoCode (void)
652 /* Switch to the code segment */
654 UseSeg (&CodeSegDef);
659 static void DoConDes (void)
660 /* Export a symbol as constructor/destructor */
662 static const char* Keys[] = {
667 StrBuf Name = STATIC_STRBUF_INITIALIZER;
670 /* Symbol name follows */
671 if (CurTok.Tok != TOK_IDENT) {
672 ErrorSkip ("Identifier expected");
675 SB_Copy (&Name, &CurTok.SVal);
678 /* Type follows. May be encoded as identifier or numerical */
680 if (CurTok.Tok == TOK_IDENT) {
682 /* Map the following keyword to a number, then skip it */
683 Type = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
686 /* Check if we got a valid keyword */
688 ErrorSkip ("Syntax error");
694 /* Read the type as numerical value */
695 Type = ConstExpression ();
696 if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) {
697 /* Value out of range */
698 ErrorSkip ("Range error");
704 /* Parse the remainder of the line and export the symbol */
705 ConDes (&Name, (unsigned) Type);
708 /* Free string memory */
714 static void DoConstructor (void)
715 /* Export a symbol as constructor */
717 StrBuf Name = STATIC_STRBUF_INITIALIZER;
719 /* Symbol name follows */
720 if (CurTok.Tok != TOK_IDENT) {
721 ErrorSkip ("Identifier expected");
724 SB_Copy (&Name, &CurTok.SVal);
727 /* Parse the remainder of the line and export the symbol */
728 ConDes (&Name, CD_TYPE_CON);
730 /* Free string memory */
736 static void DoData (void)
737 /* Switch to the data segment */
739 UseSeg (&DataSegDef);
744 static void DoDbg (void)
745 /* Add debug information from high level code */
747 static const char* Keys[] = {
756 /* We expect a subkey */
757 if (CurTok.Tok != TOK_IDENT) {
758 ErrorSkip ("Identifier expected");
762 /* Map the following keyword to a number */
763 Key = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
765 /* Skip the subkey */
768 /* Check the key and dispatch to a handler */
770 case 0: DbgInfoFile (); break;
771 case 1: DbgInfoFunc (); break;
772 case 2: DbgInfoLine (); break;
773 case 3: DbgInfoSym (); break;
774 default: ErrorSkip ("Syntax error"); break;
780 static void DoDByt (void)
781 /* Output double bytes */
783 /* Element type for the generated array */
784 static const char EType[1] = { GT_DBYTE };
786 /* Record type information */
787 Span* S = OpenSpan ();
788 StrBuf Type = STATIC_STRBUF_INITIALIZER;
790 /* Parse arguments */
792 EmitWord (GenSwapExpr (BoundedExpr (Expression, 2)));
793 if (CurTok.Tok != TOK_COMMA) {
800 /* Close the span, then add type information to it */
802 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
804 /* Free the type string */
810 static void DoDebugInfo (void)
811 /* Switch debug info on or off */
813 SetBoolOption (&DbgSyms);
818 static void DoDefine (void)
819 /* Define a one line macro */
821 MacDef (MAC_STYLE_DEFINE);
826 static void DoDelMac (void)
827 /* Delete a classic macro */
829 /* We expect an identifier */
830 if (CurTok.Tok != TOK_IDENT) {
831 ErrorSkip ("Identifier expected");
833 MacUndef (&CurTok.SVal, MAC_STYLE_CLASSIC);
840 static void DoDestructor (void)
841 /* Export a symbol as destructor */
843 StrBuf Name = STATIC_STRBUF_INITIALIZER;
845 /* Symbol name follows */
846 if (CurTok.Tok != TOK_IDENT) {
847 ErrorSkip ("Identifier expected");
850 SB_Copy (&Name, &CurTok.SVal);
853 /* Parse the remainder of the line and export the symbol */
854 ConDes (&Name, CD_TYPE_DES);
856 /* Free string memory */
862 static void DoDWord (void)
866 EmitDWord (BoundedExpr (Expression, 4));
867 if (CurTok.Tok != TOK_COMMA) {
877 static void DoEnd (void)
878 /* End of assembly */
886 static void DoEndProc (void)
887 /* Leave a lexical level */
889 if (CurrentScope->Type != SCOPE_SCOPE || CurrentScope->Label == 0) {
891 ErrorSkip ("No open .PROC");
899 static void DoEndScope (void)
900 /* Leave a lexical level */
902 if (CurrentScope->Type != SCOPE_SCOPE || CurrentScope->Label != 0) {
904 ErrorSkip ("No open .SCOPE");
912 static void DoError (void)
915 if (CurTok.Tok != TOK_STRCON) {
916 ErrorSkip ("String constant expected");
918 Error ("User error: %m%p", &CurTok.SVal);
925 static void DoExitMacro (void)
926 /* Exit a macro expansion */
928 if (!InMacExpansion ()) {
929 /* We aren't expanding a macro currently */
938 static void DoExport (void)
939 /* Export a symbol */
941 ExportImport (ExportWithAssign, ADDR_SIZE_DEFAULT, SF_NONE);
946 static void DoExportZP (void)
947 /* Export a zeropage symbol */
949 ExportImport (ExportWithAssign, ADDR_SIZE_ZP, SF_NONE);
954 static void DoFarAddr (void)
955 /* Define far addresses (24 bit) */
957 /* Element type for the generated array */
958 static const char EType[2] = { GT_FAR_PTR, GT_VOID };
960 /* Record type information */
961 Span* S = OpenSpan ();
962 StrBuf Type = STATIC_STRBUF_INITIALIZER;
964 /* Parse arguments */
966 EmitFarAddr (BoundedExpr (Expression, 3));
967 if (CurTok.Tok != TOK_COMMA) {
974 /* Close the span, then add type information to it */
976 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
978 /* Free the type string */
984 static void DoFatal (void)
985 /* Fatal user error */
987 if (CurTok.Tok != TOK_STRCON) {
988 ErrorSkip ("String constant expected");
990 Fatal ("User error: %m%p", &CurTok.SVal);
997 static void DoFeature (void)
998 /* Switch the Feature option */
1000 /* Allow a list of comma separated keywords */
1003 /* We expect an identifier */
1004 if (CurTok.Tok != TOK_IDENT) {
1005 ErrorSkip ("Identifier expected");
1009 /* Make the string attribute lower case */
1012 /* Set the feature and check for errors */
1013 if (SetFeature (&CurTok.SVal) == FEAT_UNKNOWN) {
1015 ErrorSkip ("Invalid feature: `%m%p'", &CurTok.SVal);
1018 /* Skip the keyword */
1022 /* Allow more than one keyword */
1023 if (CurTok.Tok == TOK_COMMA) {
1033 static void DoFileOpt (void)
1034 /* Insert a file option */
1038 /* The option type may be given as a keyword or as a number. */
1039 if (CurTok.Tok == TOK_IDENT) {
1041 /* Option given as keyword */
1042 static const char* Keys [] = {
1043 "AUTHOR", "COMMENT", "COMPILER"
1046 /* Map the option to a number */
1047 OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
1050 ErrorSkip ("File option keyword expected");
1054 /* Skip the keyword */
1057 /* Must be followed by a comma */
1060 /* We accept only string options for now */
1061 if (CurTok.Tok != TOK_STRCON) {
1062 ErrorSkip ("String constant expected");
1066 /* Insert the option */
1071 OptAuthor (&CurTok.SVal);
1076 OptComment (&CurTok.SVal);
1081 OptCompiler (&CurTok.SVal);
1085 Internal ("Invalid OptNum: %ld", OptNum);
1094 /* Option given as number */
1095 OptNum = ConstExpression ();
1096 if (!IsByteRange (OptNum)) {
1097 ErrorSkip ("Range error");
1101 /* Must be followed by a comma */
1104 /* We accept only string options for now */
1105 if (CurTok.Tok != TOK_STRCON) {
1106 ErrorSkip ("String constant expected");
1110 /* Insert the option */
1111 OptStr ((unsigned char) OptNum, &CurTok.SVal);
1120 static void DoForceImport (void)
1121 /* Do a forced import on a symbol */
1123 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_FORCED);
1128 static void DoGlobal (void)
1129 /* Declare a global symbol */
1131 ExportImport (SymGlobal, ADDR_SIZE_DEFAULT, SF_NONE);
1136 static void DoGlobalZP (void)
1137 /* Declare a global zeropage symbol */
1139 ExportImport (SymGlobal, ADDR_SIZE_ZP, SF_NONE);
1143 static void DoHiBytes (void)
1144 /* Define bytes, extracting the hi byte from each expression in the list */
1147 EmitByte (FuncHiByte ());
1148 if (CurTok.Tok != TOK_COMMA) {
1158 static void DoI16 (void)
1159 /* Switch the index registers to 16 bit mode (assembler only) */
1161 if (GetCPU() != CPU_65816) {
1162 Error ("Command is only valid in 65816 mode");
1164 /* Immidiate mode has two extension bytes */
1165 ExtBytes [AM65I_IMM_INDEX] = 2;
1171 static void DoI8 (void)
1172 /* Switch the index registers to 16 bit mode (assembler only) */
1174 if (GetCPU() != CPU_65816) {
1175 Error ("Command is only valid in 65816 mode");
1177 /* Immidiate mode has one extension byte */
1178 ExtBytes [AM65I_IMM_INDEX] = 1;
1184 static void DoImport (void)
1185 /* Import a symbol */
1187 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_NONE);
1192 static void DoImportZP (void)
1193 /* Import a zero page symbol */
1195 ExportImport (SymImport, ADDR_SIZE_ZP, SF_NONE);
1200 static void DoIncBin (void)
1201 /* Include a binary file */
1203 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1204 struct stat StatBuf;
1210 /* Name must follow */
1211 if (CurTok.Tok != TOK_STRCON) {
1212 ErrorSkip ("String constant expected");
1215 SB_Copy (&Name, &CurTok.SVal);
1216 SB_Terminate (&Name);
1219 /* A starting offset may follow */
1220 if (CurTok.Tok == TOK_COMMA) {
1222 Start = ConstExpression ();
1224 /* And a length may follow */
1225 if (CurTok.Tok == TOK_COMMA) {
1227 Count = ConstExpression ();
1232 /* Try to open the file */
1233 F = fopen (SB_GetConstBuf (&Name), "rb");
1236 /* Search for the file in the binary include directory */
1237 char* PathName = SearchFile (BinSearchPath, SB_GetConstBuf (&Name));
1238 if (PathName == 0 || (F = fopen (PathName, "rb")) == 0) {
1239 /* Not found or cannot open, print an error and bail out */
1240 ErrorSkip ("Cannot open include file `%m%p': %s", &Name, strerror (errno));
1245 /* Remember the new file name */
1246 SB_CopyStr (&Name, PathName);
1248 /* Free the allocated memory */
1252 /* Get the size of the file */
1253 fseek (F, 0, SEEK_END);
1256 /* Stat the file and remember the values. There's a race condition here,
1257 ** since we cannot use fileno() (non-standard identifier in standard
1258 ** header file), and therefore not fstat. When using stat with the
1259 ** file name, there's a risk that the file was deleted and recreated
1260 ** while it was open. Since mtime and size are only used to check
1261 ** if a file has changed in the debugger, we will ignore this problem
1264 SB_Terminate (&Name);
1265 if (FileStat (SB_GetConstBuf (&Name), &StatBuf) != 0) {
1266 Fatal ("Cannot stat input file `%m%p': %s", &Name, strerror (errno));
1269 /* Add the file to the input file table */
1270 AddFile (&Name, FT_BINARY, Size, (unsigned long) StatBuf.st_mtime);
1272 /* If a count was not given, calculate it now */
1274 Count = Size - Start;
1276 /* Nothing to read - flag this as a range error */
1277 ErrorSkip ("Range error");
1281 /* Count was given, check if it is valid */
1282 if (Start + Count > Size) {
1283 ErrorSkip ("Range error");
1288 /* Seek to the start position */
1289 fseek (F, Start, SEEK_SET);
1291 /* Read chunks and insert them into the output */
1294 unsigned char Buf [1024];
1296 /* Calculate the number of bytes to read */
1297 size_t BytesToRead = (Count > (long)sizeof(Buf))? sizeof(Buf) : (size_t) Count;
1300 size_t BytesRead = fread (Buf, 1, BytesToRead, F);
1301 if (BytesToRead != BytesRead) {
1302 /* Some sort of error */
1303 ErrorSkip ("Cannot read from include file `%m%p': %s",
1304 &Name, strerror (errno));
1308 /* Insert it into the output */
1309 EmitData (Buf, BytesRead);
1311 /* Keep the counters current */
1316 /* Close the file, ignore errors since it's r/o */
1320 /* Free string memory */
1326 static void DoInclude (void)
1327 /* Include another file */
1329 /* Name must follow */
1330 if (CurTok.Tok != TOK_STRCON) {
1331 ErrorSkip ("String constant expected");
1333 SB_Terminate (&CurTok.SVal);
1334 if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) {
1335 /* Error opening the file, skip remainder of line */
1343 static void DoInterruptor (void)
1344 /* Export a symbol as interruptor */
1346 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1348 /* Symbol name follows */
1349 if (CurTok.Tok != TOK_IDENT) {
1350 ErrorSkip ("Identifier expected");
1353 SB_Copy (&Name, &CurTok.SVal);
1356 /* Parse the remainder of the line and export the symbol */
1357 ConDes (&Name, CD_TYPE_INT);
1359 /* Free string memory */
1365 static void DoInvalid (void)
1366 /* Handle a token that is invalid here, since it should have been handled on
1367 ** a much lower level of the expression hierarchy. Getting this sort of token
1368 ** means that the lower level code has bugs.
1369 ** This function differs to DoUnexpected in that the latter may be triggered
1370 ** by the user by using keywords in the wrong location. DoUnexpected is not
1371 ** an error in the assembler itself, while DoInvalid is.
1374 Internal ("Unexpected token: %m%p", &Keyword);
1379 static void DoLineCont (void)
1380 /* Switch the use of line continuations */
1382 SetBoolOption (&LineCont);
1387 static void DoList (void)
1388 /* Enable/disable the listing */
1390 /* Get the setting */
1392 SetBoolOption (&List);
1394 /* Manage the counter */
1404 static void DoLoBytes (void)
1405 /* Define bytes, extracting the lo byte from each expression in the list */
1408 EmitByte (FuncLoByte ());
1409 if (CurTok.Tok != TOK_COMMA) {
1418 static void DoListBytes (void)
1419 /* Set maximum number of bytes to list for one line */
1421 SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
1426 static void DoLocalChar (void)
1427 /* Define the character that starts local labels */
1429 if (CurTok.Tok != TOK_CHARCON) {
1430 ErrorSkip ("Character constant expected");
1432 if (CurTok.IVal != '@' && CurTok.IVal != '?') {
1433 Error ("Invalid start character for locals");
1435 LocalStart = (char) CurTok.IVal;
1443 static void DoMacPack (void)
1444 /* Insert a macro package */
1446 /* We expect an identifier */
1447 if (CurTok.Tok != TOK_IDENT) {
1448 ErrorSkip ("Identifier expected");
1450 SB_AppendStr (&CurTok.SVal, ".mac");
1451 SB_Terminate (&CurTok.SVal);
1452 if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) {
1453 /* Error opening the file, skip remainder of line */
1461 static void DoMacro (void)
1462 /* Start a macro definition */
1464 MacDef (MAC_STYLE_CLASSIC);
1469 static void DoNull (void)
1470 /* Switch to the NULL segment */
1472 UseSeg (&NullSegDef);
1477 static void DoOrg (void)
1478 /* Start absolute code */
1480 long PC = ConstExpression ();
1481 if (PC < 0 || PC > 0xFFFFFF) {
1482 Error ("Range error");
1485 EnterAbsoluteMode (PC);
1490 static void DoOut (void)
1491 /* Output a string */
1493 if (CurTok.Tok != TOK_STRCON) {
1494 ErrorSkip ("String constant expected");
1496 /* Output the string and be sure to flush the output to keep it in
1497 ** sync with any error messages if the output is redirected to a file.
1500 (int) SB_GetLen (&CurTok.SVal),
1501 SB_GetConstBuf (&CurTok.SVal));
1509 static void DoP02 (void)
1510 /* Switch to 6502 CPU */
1517 static void DoPC02 (void)
1518 /* Switch to 65C02 CPU */
1525 static void DoP816 (void)
1526 /* Switch to 65816 CPU */
1533 static void DoPageLength (void)
1534 /* Set the page length for the listing */
1536 PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
1541 static void DoPopCPU (void)
1542 /* Pop an old CPU setting from the CPU stack */
1544 /* Must have a CPU on the stack */
1545 if (IS_IsEmpty (&CPUStack)) {
1546 ErrorSkip ("CPU stack is empty");
1550 /* Set the CPU to the value popped from stack */
1551 SetCPU (IS_Pop (&CPUStack));
1556 static void DoPopSeg (void)
1557 /* Pop an old segment from the segment stack */
1561 /* Must have a segment on the stack */
1562 if (CollCount (&SegStack) == 0) {
1563 ErrorSkip ("Segment stack is empty");
1567 /* Pop the last element */
1568 Def = CollPop (&SegStack);
1570 /* Restore this segment */
1573 /* Delete the segment definition */
1579 static void DoProc (void)
1580 /* Start a new lexical scope */
1582 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1583 unsigned char AddrSize;
1587 if (CurTok.Tok == TOK_IDENT) {
1589 /* The new scope has a name. Remember it. */
1590 SB_Copy (&Name, &CurTok.SVal);
1592 /* Search for the symbol, generate a new one if needed */
1593 Sym = SymFind (CurrentScope, &Name, SYM_ALLOC_NEW);
1595 /* Skip the scope name */
1598 /* Read an optional address size specifier */
1599 AddrSize = OptionalAddrSize ();
1601 /* Mark the symbol as defined */
1602 SymDef (Sym, GenCurrentPC (), AddrSize, SF_LABEL);
1606 /* A .PROC statement without a name */
1607 Warning (1, "Unnamed .PROCs are deprecated, please use .SCOPE");
1608 AnonName (&Name, "PROC");
1609 AddrSize = ADDR_SIZE_DEFAULT;
1613 /* Enter a new scope */
1614 SymEnterLevel (&Name, SCOPE_SCOPE, AddrSize, Sym);
1616 /* Free memory for Name */
1622 static void DoPSC02 (void)
1623 /* Switch to 65SC02 CPU */
1625 SetCPU (CPU_65SC02);
1630 static void DoPushCPU (void)
1631 /* Push the current CPU setting onto the CPU stack */
1633 /* Can only push a limited size of segments */
1634 if (IS_IsFull (&CPUStack)) {
1635 ErrorSkip ("CPU stack overflow");
1639 /* Get the current segment and push it */
1640 IS_Push (&CPUStack, GetCPU ());
1645 static void DoPushSeg (void)
1646 /* Push the current segment onto the segment stack */
1648 /* Can only push a limited size of segments */
1649 if (CollCount (&SegStack) >= MAX_PUSHED_SEGMENTS) {
1650 ErrorSkip ("Segment stack overflow");
1654 /* Get the current segment and push it */
1655 CollAppend (&SegStack, DupSegDef (GetCurrentSegDef ()));
1660 static void DoReloc (void)
1661 /* Enter relocatable mode */
1668 static void DoRepeat (void)
1669 /* Repeat some instruction block */
1676 static void DoRes (void)
1677 /* Reserve some number of storage bytes */
1682 Count = ConstExpression ();
1683 if (Count > 0xFFFF || Count < 0) {
1684 ErrorSkip ("Range error");
1687 if (CurTok.Tok == TOK_COMMA) {
1689 Val = ConstExpression ();
1690 /* We need a byte value here */
1691 if (!IsByteRange (Val)) {
1692 ErrorSkip ("Range error");
1696 /* Emit constant values */
1698 Emit0 ((unsigned char) Val);
1702 /* Emit fill fragments */
1709 static void DoROData (void)
1710 /* Switch to the r/o data segment */
1712 UseSeg (&RODataSegDef);
1717 static void DoScope (void)
1718 /* Start a local scope */
1720 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1721 unsigned char AddrSize;
1724 if (CurTok.Tok == TOK_IDENT) {
1726 /* The new scope has a name. Remember and skip it. */
1727 SB_Copy (&Name, &CurTok.SVal);
1732 /* An unnamed scope */
1733 AnonName (&Name, "SCOPE");
1737 /* Read an optional address size specifier */
1738 AddrSize = OptionalAddrSize ();
1740 /* Enter the new scope */
1741 SymEnterLevel (&Name, SCOPE_SCOPE, AddrSize, 0);
1743 /* Free memory for Name */
1749 static void DoSegment (void)
1750 /* Switch to another segment */
1752 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1755 if (CurTok.Tok != TOK_STRCON) {
1756 ErrorSkip ("String constant expected");
1759 /* Save the name of the segment and skip it */
1760 SB_Copy (&Name, &CurTok.SVal);
1763 /* Use the name for the segment definition */
1764 SB_Terminate (&Name);
1765 Def.Name = SB_GetBuf (&Name);
1767 /* Check for an optional address size modifier */
1768 Def.AddrSize = OptionalAddrSize ();
1770 /* Set the segment */
1774 /* Free memory for Name */
1780 static void DoSetCPU (void)
1781 /* Switch the CPU instruction set */
1783 /* We expect an identifier */
1784 if (CurTok.Tok != TOK_STRCON) {
1785 ErrorSkip ("String constant expected");
1789 /* Try to find the CPU */
1790 SB_Terminate (&CurTok.SVal);
1791 CPU = FindCPU (SB_GetConstBuf (&CurTok.SVal));
1793 /* Switch to the new CPU */
1796 /* Skip the identifier. If the CPU switch was successful, the scanner
1797 ** will treat the input now correctly for the new CPU.
1805 static void DoSmart (void)
1806 /* Smart mode on/off */
1808 SetBoolOption (&SmartMode);
1813 static void DoTag (void)
1814 /* Allocate space for a struct */
1819 /* Read the struct name */
1820 SymTable* Struct = ParseScopedSymTable ();
1822 /* Check the supposed struct */
1824 ErrorSkip ("Unknown struct");
1827 if (GetSymTabType (Struct) != SCOPE_STRUCT) {
1828 ErrorSkip ("Not a struct");
1832 /* Get the symbol that defines the size of the struct */
1833 SizeSym = GetSizeOfScope (Struct);
1835 /* Check if it does exist and if its value is known */
1836 if (SizeSym == 0 || !SymIsConst (SizeSym, &Size)) {
1837 ErrorSkip ("Size of struct/union is unknown");
1841 /* Optional multiplicator may follow */
1842 if (CurTok.Tok == TOK_COMMA) {
1845 Multiplicator = ConstExpression ();
1846 /* Multiplicator must make sense */
1847 if (Multiplicator <= 0) {
1848 ErrorSkip ("Range error");
1851 Size *= Multiplicator;
1854 /* Emit fill fragments */
1860 static void DoUnDef (void)
1861 /* Undefine a define style macro */
1863 /* The function is called with the .UNDEF token in place, because we need
1864 ** to disable .define macro expansions before reading the next token.
1865 ** Otherwise the name of the macro would be expanded, so we would never
1868 DisableDefineStyleMacros ();
1870 EnableDefineStyleMacros ();
1872 /* We expect an identifier */
1873 if (CurTok.Tok != TOK_IDENT) {
1874 ErrorSkip ("Identifier expected");
1876 MacUndef (&CurTok.SVal, MAC_STYLE_DEFINE);
1883 static void DoUnexpected (void)
1884 /* Got an unexpected keyword */
1886 Error ("Unexpected `%m%p'", &Keyword);
1892 static void DoWarning (void)
1895 if (CurTok.Tok != TOK_STRCON) {
1896 ErrorSkip ("String constant expected");
1898 Warning (0, "User warning: %m%p", &CurTok.SVal);
1905 static void DoWord (void)
1908 /* Element type for the generated array */
1909 static const char EType[1] = { GT_WORD };
1911 /* Record type information */
1912 Span* S = OpenSpan ();
1913 StrBuf Type = STATIC_STRBUF_INITIALIZER;
1915 /* Parse arguments */
1917 EmitWord (BoundedExpr (Expression, 2));
1918 if (CurTok.Tok != TOK_COMMA) {
1925 /* Close the span, then add type information to it */
1927 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
1929 /* Free the type string */
1935 static void DoZeropage (void)
1936 /* Switch to the zeropage segment */
1938 UseSeg (&ZeropageSegDef);
1943 /*****************************************************************************/
1945 /*****************************************************************************/
1949 /* Control commands flags */
1951 ccNone = 0x0000, /* No special flags */
1952 ccKeepToken = 0x0001 /* Do not skip the current token */
1955 /* Control command table */
1956 typedef struct CtrlDesc CtrlDesc;
1958 unsigned Flags; /* Flags for this directive */
1959 void (*Handler) (void); /* Command handler */
1962 #define PSEUDO_COUNT (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1963 static CtrlDesc CtrlCmdTab [] = {
1966 { ccNone, DoAddr }, /* .ADDR */
1967 { ccNone, DoUnexpected }, /* .ADDRSIZE */
1968 { ccNone, DoAlign },
1969 { ccNone, DoASCIIZ },
1970 { ccNone, DoAssert },
1971 { ccNone, DoAutoImport },
1972 { ccNone, DoUnexpected }, /* .BANK */
1973 { ccNone, DoUnexpected }, /* .BANKBYTE */
1974 { ccNone, DoBankBytes },
1975 { ccNone, DoUnexpected }, /* .BLANK */
1979 { ccNone, DoCharMap },
1981 { ccNone, DoUnexpected, }, /* .CONCAT */
1982 { ccNone, DoConDes },
1983 { ccNone, DoUnexpected }, /* .CONST */
1984 { ccNone, DoConstructor },
1985 { ccNone, DoUnexpected }, /* .CPU */
1989 { ccNone, DoDebugInfo },
1990 { ccNone, DoDefine },
1991 { ccNone, DoUnexpected }, /* .DEFINED */
1992 { ccNone, DoUnexpected }, /* .DEFINEDMACRO */
1993 { ccNone, DoDelMac },
1994 { ccNone, DoDestructor },
1995 { ccNone, DoDWord },
1996 { ccKeepToken, DoConditionals }, /* .ELSE */
1997 { ccKeepToken, DoConditionals }, /* .ELSEIF */
1998 { ccKeepToken, DoEnd },
1999 { ccNone, DoUnexpected }, /* .ENDENUM */
2000 { ccKeepToken, DoConditionals }, /* .ENDIF */
2001 { ccNone, DoUnexpected }, /* .ENDMACRO */
2002 { ccNone, DoEndProc },
2003 { ccNone, DoUnexpected }, /* .ENDREPEAT */
2004 { ccNone, DoEndScope },
2005 { ccNone, DoUnexpected }, /* .ENDSTRUCT */
2006 { ccNone, DoUnexpected }, /* .ENDUNION */
2008 { ccNone, DoError },
2009 { ccNone, DoExitMacro },
2010 { ccNone, DoExport },
2011 { ccNone, DoExportZP },
2012 { ccNone, DoFarAddr },
2013 { ccNone, DoFatal },
2014 { ccNone, DoFeature },
2015 { ccNone, DoFileOpt },
2016 { ccNone, DoForceImport },
2017 { ccNone, DoUnexpected }, /* .FORCEWORD */
2018 { ccNone, DoGlobal },
2019 { ccNone, DoGlobalZP },
2020 { ccNone, DoUnexpected }, /* .HIBYTE */
2021 { ccNone, DoHiBytes },
2022 { ccNone, DoUnexpected }, /* .HIWORD */
2025 { ccNone, DoUnexpected }, /* .IDENT */
2026 { ccKeepToken, DoConditionals }, /* .IF */
2027 { ccKeepToken, DoConditionals }, /* .IFBLANK */
2028 { ccKeepToken, DoConditionals }, /* .IFCONST */
2029 { ccKeepToken, DoConditionals }, /* .IFDEF */
2030 { ccKeepToken, DoConditionals }, /* .IFNBLANK */
2031 { ccKeepToken, DoConditionals }, /* .IFNCONST */
2032 { ccKeepToken, DoConditionals }, /* .IFNDEF */
2033 { ccKeepToken, DoConditionals }, /* .IFNREF */
2034 { ccKeepToken, DoConditionals }, /* .IFP02 */
2035 { ccKeepToken, DoConditionals }, /* .IFP816 */
2036 { ccKeepToken, DoConditionals }, /* .IFPC02 */
2037 { ccKeepToken, DoConditionals }, /* .IFPSC02 */
2038 { ccKeepToken, DoConditionals }, /* .IFREF */
2039 { ccNone, DoImport },
2040 { ccNone, DoImportZP },
2041 { ccNone, DoIncBin },
2042 { ccNone, DoInclude },
2043 { ccNone, DoInterruptor },
2044 { ccNone, DoUnexpected }, /* .ISMNEMONIC */
2045 { ccNone, DoInvalid }, /* .LEFT */
2046 { ccNone, DoLineCont },
2048 { ccNone, DoListBytes },
2049 { ccNone, DoUnexpected }, /* .LOBYTE */
2050 { ccNone, DoLoBytes },
2051 { ccNone, DoUnexpected }, /* .LOCAL */
2052 { ccNone, DoLocalChar },
2053 { ccNone, DoUnexpected }, /* .LOWORD */
2054 { ccNone, DoMacPack },
2055 { ccNone, DoMacro },
2056 { ccNone, DoUnexpected }, /* .MATCH */
2057 { ccNone, DoUnexpected }, /* .MAX */
2058 { ccNone, DoInvalid }, /* .MID */
2059 { ccNone, DoUnexpected }, /* .MIN */
2065 { ccNone, DoPageLength },
2066 { ccNone, DoUnexpected }, /* .PARAMCOUNT */
2068 { ccNone, DoPopCPU },
2069 { ccNone, DoPopSeg },
2071 { ccNone, DoPSC02 },
2072 { ccNone, DoPushCPU },
2073 { ccNone, DoPushSeg },
2074 { ccNone, DoUnexpected }, /* .REFERENCED */
2075 { ccNone, DoReloc },
2076 { ccNone, DoRepeat },
2078 { ccNone, DoInvalid }, /* .RIGHT */
2079 { ccNone, DoROData },
2080 { ccNone, DoScope },
2081 { ccNone, DoSegment },
2082 { ccNone, DoUnexpected }, /* .SET */
2083 { ccNone, DoSetCPU },
2084 { ccNone, DoUnexpected }, /* .SIZEOF */
2085 { ccNone, DoSmart },
2086 { ccNone, DoUnexpected }, /* .SPRINTF */
2087 { ccNone, DoUnexpected }, /* .STRAT */
2088 { ccNone, DoUnexpected }, /* .STRING */
2089 { ccNone, DoUnexpected }, /* .STRLEN */
2090 { ccNone, DoStruct },
2092 { ccNone, DoUnexpected }, /* .TCOUNT */
2093 { ccNone, DoUnexpected }, /* .TIME */
2094 { ccKeepToken, DoUnDef },
2095 { ccNone, DoUnion },
2096 { ccNone, DoUnexpected }, /* .VERSION */
2097 { ccNone, DoWarning },
2099 { ccNone, DoUnexpected }, /* .XMATCH */
2100 { ccNone, DoZeropage },
2105 /*****************************************************************************/
2107 /*****************************************************************************/
2111 void HandlePseudo (void)
2112 /* Handle a pseudo instruction */
2116 /* Calculate the index into the table */
2117 unsigned Index = CurTok.Tok - TOK_FIRSTPSEUDO;
2120 if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
2121 Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
2122 (unsigned) PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
2124 CHECK (Index < PSEUDO_COUNT);
2126 /* Get the pseudo intruction descriptor */
2127 D = &CtrlCmdTab [Index];
2129 /* Remember the instruction, then skip it if needed */
2130 if ((D->Flags & ccKeepToken) == 0) {
2131 SB_Copy (&Keyword, &CurTok.SVal);
2135 /* Call the handler */
2141 void CheckPseudo (void)
2142 /* Check if the stacks are empty at end of assembly */
2144 if (CollCount (&SegStack) != 0) {
2145 Warning (1, "Segment stack is not empty");
2147 if (!IS_IsEmpty (&CPUStack)) {
2148 Warning (1, "CPU stack is not empty");