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* const 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* const 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 = AUTO_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.
602 ** Note: empty string operands emit nothing;
603 ** so, add a type only if there's a span.
607 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
610 /* Free the type string */
616 static void DoCase (void)
617 /* Switch the IgnoreCase option */
619 SetBoolOption (&IgnoreCase);
620 IgnoreCase = !IgnoreCase;
625 static void DoCharMap (void)
626 /* Allow custom character mappings */
631 /* Read the index as numerical value */
632 Index = ConstExpression ();
633 if (Index < 0 || Index > 255) {
634 /* Value out of range */
635 ErrorSkip ("Index range error");
642 /* Read the character code */
643 Code = ConstExpression ();
644 if (Code < 0 || Code > 255) {
645 /* Value out of range */
646 ErrorSkip ("Code range error");
650 /* Set the character translation */
651 TgtTranslateSet ((unsigned) Index, (unsigned char) Code);
656 static void DoCode (void)
657 /* Switch to the code segment */
659 UseSeg (&CodeSegDef);
664 static void DoConDes (void)
665 /* Export a symbol as constructor/destructor */
667 static const char* const Keys[] = {
672 StrBuf Name = STATIC_STRBUF_INITIALIZER;
675 /* Symbol name follows */
676 if (CurTok.Tok != TOK_IDENT) {
677 ErrorSkip ("Identifier expected");
680 SB_Copy (&Name, &CurTok.SVal);
683 /* Type follows. May be encoded as identifier or numerical */
685 if (CurTok.Tok == TOK_IDENT) {
687 /* Map the following keyword to a number, then skip it */
688 Type = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
691 /* Check if we got a valid keyword */
693 ErrorSkip ("Syntax error");
699 /* Read the type as numerical value */
700 Type = ConstExpression ();
701 if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) {
702 /* Value out of range */
703 ErrorSkip ("Range error");
709 /* Parse the remainder of the line and export the symbol */
710 ConDes (&Name, (unsigned) Type);
713 /* Free string memory */
719 static void DoConstructor (void)
720 /* Export a symbol as constructor */
722 StrBuf Name = STATIC_STRBUF_INITIALIZER;
724 /* Symbol name follows */
725 if (CurTok.Tok != TOK_IDENT) {
726 ErrorSkip ("Identifier expected");
729 SB_Copy (&Name, &CurTok.SVal);
732 /* Parse the remainder of the line and export the symbol */
733 ConDes (&Name, CD_TYPE_CON);
735 /* Free string memory */
741 static void DoData (void)
742 /* Switch to the data segment */
744 UseSeg (&DataSegDef);
749 static void DoDbg (void)
750 /* Add debug information from high level code */
752 static const char* const Keys[] = {
761 /* We expect a subkey */
762 if (CurTok.Tok != TOK_IDENT) {
763 ErrorSkip ("Identifier expected");
767 /* Map the following keyword to a number */
768 Key = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
770 /* Skip the subkey */
773 /* Check the key and dispatch to a handler */
775 case 0: DbgInfoFile (); break;
776 case 1: DbgInfoFunc (); break;
777 case 2: DbgInfoLine (); break;
778 case 3: DbgInfoSym (); break;
779 default: ErrorSkip ("Syntax error"); break;
785 static void DoDByt (void)
786 /* Output double bytes */
788 /* Element type for the generated array */
789 static const char EType[1] = { GT_DBYTE };
791 /* Record type information */
792 Span* S = OpenSpan ();
793 StrBuf Type = STATIC_STRBUF_INITIALIZER;
795 /* Parse arguments */
797 EmitWord (GenSwapExpr (BoundedExpr (Expression, 2)));
798 if (CurTok.Tok != TOK_COMMA) {
805 /* Close the span, then add type information to it */
807 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
809 /* Free the type string */
815 static void DoDebugInfo (void)
816 /* Switch debug info on or off */
818 SetBoolOption (&DbgSyms);
823 static void DoDefine (void)
824 /* Define a one line macro */
826 MacDef (MAC_STYLE_DEFINE);
831 static void DoDelMac (void)
832 /* Delete a classic macro */
834 /* We expect an identifier */
835 if (CurTok.Tok != TOK_IDENT) {
836 ErrorSkip ("Identifier expected");
838 MacUndef (&CurTok.SVal, MAC_STYLE_CLASSIC);
845 static void DoDestructor (void)
846 /* Export a symbol as destructor */
848 StrBuf Name = STATIC_STRBUF_INITIALIZER;
850 /* Symbol name follows */
851 if (CurTok.Tok != TOK_IDENT) {
852 ErrorSkip ("Identifier expected");
855 SB_Copy (&Name, &CurTok.SVal);
858 /* Parse the remainder of the line and export the symbol */
859 ConDes (&Name, CD_TYPE_DES);
861 /* Free string memory */
867 static void DoDWord (void)
871 EmitDWord (BoundedExpr (Expression, 4));
872 if (CurTok.Tok != TOK_COMMA) {
882 static void DoEnd (void)
883 /* End of assembly */
891 static void DoEndProc (void)
892 /* Leave a lexical level */
894 if (CurrentScope->Type != SCOPE_SCOPE || CurrentScope->Label == 0) {
896 ErrorSkip ("No open .PROC");
904 static void DoEndScope (void)
905 /* Leave a lexical level */
907 if (CurrentScope->Type != SCOPE_SCOPE || CurrentScope->Label != 0) {
909 ErrorSkip ("No open .SCOPE");
917 static void DoError (void)
920 if (CurTok.Tok != TOK_STRCON) {
921 ErrorSkip ("String constant expected");
923 Error ("User error: %m%p", &CurTok.SVal);
930 static void DoExitMacro (void)
931 /* Exit a macro expansion */
933 if (!InMacExpansion ()) {
934 /* We aren't expanding a macro currently */
943 static void DoExport (void)
944 /* Export a symbol */
946 ExportImport (ExportWithAssign, ADDR_SIZE_DEFAULT, SF_NONE);
951 static void DoExportZP (void)
952 /* Export a zeropage symbol */
954 ExportImport (ExportWithAssign, ADDR_SIZE_ZP, SF_NONE);
959 static void DoFarAddr (void)
960 /* Define far addresses (24 bit) */
962 /* Element type for the generated array */
963 static const char EType[2] = { GT_FAR_PTR, GT_VOID };
965 /* Record type information */
966 Span* S = OpenSpan ();
967 StrBuf Type = STATIC_STRBUF_INITIALIZER;
969 /* Parse arguments */
971 EmitFarAddr (BoundedExpr (Expression, 3));
972 if (CurTok.Tok != TOK_COMMA) {
979 /* Close the span, then add type information to it */
981 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
983 /* Free the type string */
989 static void DoFatal (void)
990 /* Fatal user error */
992 if (CurTok.Tok != TOK_STRCON) {
993 ErrorSkip ("String constant expected");
995 Fatal ("User error: %m%p", &CurTok.SVal);
1002 static void DoFeature (void)
1003 /* Switch the Feature option */
1005 /* Allow a list of comma separated keywords */
1008 /* We expect an identifier */
1009 if (CurTok.Tok != TOK_IDENT) {
1010 ErrorSkip ("Identifier expected");
1014 /* Make the string attribute lower case */
1017 /* Set the feature and check for errors */
1018 if (SetFeature (&CurTok.SVal) == FEAT_UNKNOWN) {
1020 ErrorSkip ("Invalid feature: '%m%p'", &CurTok.SVal);
1023 /* Skip the keyword */
1027 /* Allow more than one keyword */
1028 if (CurTok.Tok == TOK_COMMA) {
1038 static void DoFileOpt (void)
1039 /* Insert a file option */
1043 /* The option type may be given as a keyword or as a number. */
1044 if (CurTok.Tok == TOK_IDENT) {
1046 /* Option given as keyword */
1047 static const char* const Keys [] = {
1048 "AUTHOR", "COMMENT", "COMPILER"
1051 /* Map the option to a number */
1052 OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
1055 ErrorSkip ("File option keyword expected");
1059 /* Skip the keyword */
1062 /* Must be followed by a comma */
1065 /* We accept only string options for now */
1066 if (CurTok.Tok != TOK_STRCON) {
1067 ErrorSkip ("String constant expected");
1071 /* Insert the option */
1076 OptAuthor (&CurTok.SVal);
1081 OptComment (&CurTok.SVal);
1086 OptCompiler (&CurTok.SVal);
1090 Internal ("Invalid OptNum: %ld", OptNum);
1099 /* Option given as number */
1100 OptNum = ConstExpression ();
1101 if (!IsByteRange (OptNum)) {
1102 ErrorSkip ("Range error");
1106 /* Must be followed by a comma */
1109 /* We accept only string options for now */
1110 if (CurTok.Tok != TOK_STRCON) {
1111 ErrorSkip ("String constant expected");
1115 /* Insert the option */
1116 OptStr ((unsigned char) OptNum, &CurTok.SVal);
1125 static void DoForceImport (void)
1126 /* Do a forced import on a symbol */
1128 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_FORCED);
1133 static void DoGlobal (void)
1134 /* Declare a global symbol */
1136 ExportImport (SymGlobal, ADDR_SIZE_DEFAULT, SF_NONE);
1141 static void DoGlobalZP (void)
1142 /* Declare a global zeropage symbol */
1144 ExportImport (SymGlobal, ADDR_SIZE_ZP, SF_NONE);
1148 static void DoHiBytes (void)
1149 /* Define bytes, extracting the hi byte from each expression in the list */
1152 EmitByte (FuncHiByte ());
1153 if (CurTok.Tok != TOK_COMMA) {
1163 static void DoI16 (void)
1164 /* Switch the index registers to 16 bit mode (assembler only) */
1166 if (GetCPU() != CPU_65816) {
1167 Error ("Command is only valid in 65816 mode");
1169 /* Immidiate mode has two extension bytes */
1170 ExtBytes [AM65I_IMM_INDEX] = 2;
1176 static void DoI8 (void)
1177 /* Switch the index registers to 16 bit mode (assembler only) */
1179 if (GetCPU() != CPU_65816) {
1180 Error ("Command is only valid in 65816 mode");
1182 /* Immidiate mode has one extension byte */
1183 ExtBytes [AM65I_IMM_INDEX] = 1;
1189 static void DoImport (void)
1190 /* Import a symbol */
1192 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_NONE);
1197 static void DoImportZP (void)
1198 /* Import a zero page symbol */
1200 ExportImport (SymImport, ADDR_SIZE_ZP, SF_NONE);
1205 static void DoIncBin (void)
1206 /* Include a binary file */
1208 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1209 struct stat StatBuf;
1215 /* Name must follow */
1216 if (CurTok.Tok != TOK_STRCON) {
1217 ErrorSkip ("String constant expected");
1220 SB_Copy (&Name, &CurTok.SVal);
1221 SB_Terminate (&Name);
1224 /* A starting offset may follow */
1225 if (CurTok.Tok == TOK_COMMA) {
1227 Start = ConstExpression ();
1229 /* And a length may follow */
1230 if (CurTok.Tok == TOK_COMMA) {
1232 Count = ConstExpression ();
1237 /* Try to open the file */
1238 F = fopen (SB_GetConstBuf (&Name), "rb");
1241 /* Search for the file in the binary include directory */
1242 char* PathName = SearchFile (BinSearchPath, SB_GetConstBuf (&Name));
1243 if (PathName == 0 || (F = fopen (PathName, "rb")) == 0) {
1244 /* Not found or cannot open, print an error and bail out */
1245 ErrorSkip ("Cannot open include file '%m%p': %s", &Name, strerror (errno));
1250 /* Remember the new file name */
1251 SB_CopyStr (&Name, PathName);
1253 /* Free the allocated memory */
1257 /* Get the size of the file */
1258 fseek (F, 0, SEEK_END);
1261 /* Stat the file and remember the values. There's a race condition here,
1262 ** since we cannot use fileno() (non-standard identifier in standard
1263 ** header file), and therefore not fstat. When using stat with the
1264 ** file name, there's a risk that the file was deleted and recreated
1265 ** while it was open. Since mtime and size are only used to check
1266 ** if a file has changed in the debugger, we will ignore this problem
1269 SB_Terminate (&Name);
1270 if (FileStat (SB_GetConstBuf (&Name), &StatBuf) != 0) {
1271 Fatal ("Cannot stat input file '%m%p': %s", &Name, strerror (errno));
1274 /* Add the file to the input file table */
1275 AddFile (&Name, FT_BINARY, Size, (unsigned long) StatBuf.st_mtime);
1277 /* If a count was not given, calculate it now */
1279 Count = Size - Start;
1281 /* Nothing to read - flag this as a range error */
1282 ErrorSkip ("Range error");
1286 /* Count was given, check if it is valid */
1287 if (Start + Count > Size) {
1288 ErrorSkip ("Range error");
1293 /* Seek to the start position */
1294 fseek (F, Start, SEEK_SET);
1296 /* Read chunks and insert them into the output */
1299 unsigned char Buf [1024];
1301 /* Calculate the number of bytes to read */
1302 size_t BytesToRead = (Count > (long)sizeof(Buf))? sizeof(Buf) : (size_t) Count;
1305 size_t BytesRead = fread (Buf, 1, BytesToRead, F);
1306 if (BytesToRead != BytesRead) {
1307 /* Some sort of error */
1308 ErrorSkip ("Cannot read from include file '%m%p': %s",
1309 &Name, strerror (errno));
1313 /* Insert it into the output */
1314 EmitData (Buf, BytesRead);
1316 /* Keep the counters current */
1321 /* Close the file, ignore errors since it's r/o */
1325 /* Free string memory */
1331 static void DoInclude (void)
1332 /* Include another file */
1334 /* Name must follow */
1335 if (CurTok.Tok != TOK_STRCON) {
1336 ErrorSkip ("String constant expected");
1338 SB_Terminate (&CurTok.SVal);
1339 if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) {
1340 /* Error opening the file, skip remainder of line */
1348 static void DoInterruptor (void)
1349 /* Export a symbol as interruptor */
1351 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1353 /* Symbol name follows */
1354 if (CurTok.Tok != TOK_IDENT) {
1355 ErrorSkip ("Identifier expected");
1358 SB_Copy (&Name, &CurTok.SVal);
1361 /* Parse the remainder of the line and export the symbol */
1362 ConDes (&Name, CD_TYPE_INT);
1364 /* Free string memory */
1370 static void DoInvalid (void)
1371 /* Handle a token that is invalid here, since it should have been handled on
1372 ** a much lower level of the expression hierarchy. Getting this sort of token
1373 ** means that the lower level code has bugs.
1374 ** This function differs to DoUnexpected in that the latter may be triggered
1375 ** by the user by using keywords in the wrong location. DoUnexpected is not
1376 ** an error in the assembler itself, while DoInvalid is.
1379 Internal ("Unexpected token: %m%p", &Keyword);
1384 static void DoLineCont (void)
1385 /* Switch the use of line continuations */
1387 SetBoolOption (&LineCont);
1392 static void DoList (void)
1393 /* Enable/disable the listing */
1395 /* Get the setting */
1396 unsigned char List = 0;
1397 SetBoolOption (&List);
1399 /* Manage the counter */
1409 static void DoLoBytes (void)
1410 /* Define bytes, extracting the lo byte from each expression in the list */
1413 EmitByte (FuncLoByte ());
1414 if (CurTok.Tok != TOK_COMMA) {
1423 static void DoListBytes (void)
1424 /* Set maximum number of bytes to list for one line */
1426 SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
1431 static void DoLocalChar (void)
1432 /* Define the character that starts local labels */
1434 if (CurTok.Tok != TOK_CHARCON) {
1435 ErrorSkip ("Character constant expected");
1437 if (CurTok.IVal != '@' && CurTok.IVal != '?') {
1438 Error ("Invalid start character for locals");
1440 LocalStart = (char) CurTok.IVal;
1448 static void DoMacPack (void)
1449 /* Insert a macro package */
1451 /* We expect an identifier */
1452 if (CurTok.Tok != TOK_IDENT) {
1453 ErrorSkip ("Identifier expected");
1455 SB_AppendStr (&CurTok.SVal, ".mac");
1456 SB_Terminate (&CurTok.SVal);
1457 if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) {
1458 /* Error opening the file, skip remainder of line */
1466 static void DoMacro (void)
1467 /* Start a macro definition */
1469 MacDef (MAC_STYLE_CLASSIC);
1474 static void DoNull (void)
1475 /* Switch to the NULL segment */
1477 UseSeg (&NullSegDef);
1482 static void DoOrg (void)
1483 /* Start absolute code */
1485 long PC = ConstExpression ();
1486 if (PC < 0 || PC > 0xFFFFFF) {
1487 Error ("Range error");
1490 EnterAbsoluteMode (PC);
1495 static void DoOut (void)
1496 /* Output a string */
1498 if (CurTok.Tok != TOK_STRCON) {
1499 ErrorSkip ("String constant expected");
1501 /* Output the string and be sure to flush the output to keep it in
1502 ** sync with any error messages if the output is redirected to a file.
1505 (int) SB_GetLen (&CurTok.SVal),
1506 SB_GetConstBuf (&CurTok.SVal));
1514 static void DoP02 (void)
1515 /* Switch to 6502 CPU */
1522 static void DoPC02 (void)
1523 /* Switch to 65C02 CPU */
1530 static void DoP816 (void)
1531 /* Switch to 65816 CPU */
1538 static void DoP4510 (void)
1539 /* Switch to 4510 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 DoTag (void)
1827 /* Allocate space for a struct */
1832 /* Read the struct name */
1833 SymTable* Struct = ParseScopedSymTable ();
1835 /* Check the supposed struct */
1837 ErrorSkip ("Unknown struct");
1840 if (GetSymTabType (Struct) != SCOPE_STRUCT) {
1841 ErrorSkip ("Not a struct");
1845 /* Get the symbol that defines the size of the struct */
1846 SizeSym = GetSizeOfScope (Struct);
1848 /* Check if it does exist and if its value is known */
1849 if (SizeSym == 0 || !SymIsConst (SizeSym, &Size)) {
1850 ErrorSkip ("Size of struct/union is unknown");
1854 /* Optional multiplicator may follow */
1855 if (CurTok.Tok == TOK_COMMA) {
1858 Multiplicator = ConstExpression ();
1859 /* Multiplicator must make sense */
1860 if (Multiplicator <= 0) {
1861 ErrorSkip ("Range error");
1864 Size *= Multiplicator;
1867 /* Emit fill fragments */
1873 static void DoUnDef (void)
1874 /* Undefine a define style macro */
1876 /* The function is called with the .UNDEF token in place, because we need
1877 ** to disable .define macro expansions before reading the next token.
1878 ** Otherwise the name of the macro would be expanded, so we would never
1881 DisableDefineStyleMacros ();
1883 EnableDefineStyleMacros ();
1885 /* We expect an identifier */
1886 if (CurTok.Tok != TOK_IDENT) {
1887 ErrorSkip ("Identifier expected");
1889 MacUndef (&CurTok.SVal, MAC_STYLE_DEFINE);
1896 static void DoUnexpected (void)
1897 /* Got an unexpected keyword */
1899 Error ("Unexpected '%m%p'", &Keyword);
1905 static void DoWarning (void)
1908 if (CurTok.Tok != TOK_STRCON) {
1909 ErrorSkip ("String constant expected");
1911 Warning (0, "User warning: %m%p", &CurTok.SVal);
1918 static void DoWord (void)
1921 /* Element type for the generated array */
1922 static const char EType[1] = { GT_WORD };
1924 /* Record type information */
1925 Span* S = OpenSpan ();
1926 StrBuf Type = STATIC_STRBUF_INITIALIZER;
1928 /* Parse arguments */
1930 EmitWord (BoundedExpr (Expression, 2));
1931 if (CurTok.Tok != TOK_COMMA) {
1938 /* Close the span, then add type information to it */
1940 SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
1942 /* Free the type string */
1948 static void DoZeropage (void)
1949 /* Switch to the zeropage segment */
1951 UseSeg (&ZeropageSegDef);
1956 /*****************************************************************************/
1958 /*****************************************************************************/
1962 /* Control commands flags */
1964 ccNone = 0x0000, /* No special flags */
1965 ccKeepToken = 0x0001 /* Do not skip the current token */
1968 /* Control command table */
1969 typedef struct CtrlDesc CtrlDesc;
1971 unsigned Flags; /* Flags for this directive */
1972 void (*Handler) (void); /* Command handler */
1975 #define PSEUDO_COUNT (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1976 static CtrlDesc CtrlCmdTab [] = {
1979 { ccNone, DoAddr }, /* .ADDR */
1980 { ccNone, DoUnexpected }, /* .ADDRSIZE */
1981 { ccNone, DoAlign },
1982 { ccNone, DoASCIIZ },
1983 { ccNone, DoUnexpected }, /* .ASIZE */
1984 { ccNone, DoAssert },
1985 { ccNone, DoAutoImport },
1986 { ccNone, DoUnexpected }, /* .BANK */
1987 { ccNone, DoUnexpected }, /* .BANKBYTE */
1988 { ccNone, DoBankBytes },
1989 { ccNone, DoUnexpected }, /* .BLANK */
1993 { ccNone, DoCharMap },
1995 { ccNone, DoUnexpected, }, /* .CONCAT */
1996 { ccNone, DoConDes },
1997 { ccNone, DoUnexpected }, /* .CONST */
1998 { ccNone, DoConstructor },
1999 { ccNone, DoUnexpected }, /* .CPU */
2003 { ccNone, DoDebugInfo },
2004 { ccNone, DoDefine },
2005 { ccNone, DoUnexpected }, /* .DEFINED */
2006 { ccNone, DoUnexpected }, /* .DEFINEDMACRO */
2007 { ccNone, DoDelMac },
2008 { ccNone, DoDestructor },
2009 { ccNone, DoDWord },
2010 { ccKeepToken, DoConditionals }, /* .ELSE */
2011 { ccKeepToken, DoConditionals }, /* .ELSEIF */
2012 { ccKeepToken, DoEnd },
2013 { ccNone, DoUnexpected }, /* .ENDENUM */
2014 { ccKeepToken, DoConditionals }, /* .ENDIF */
2015 { ccNone, DoUnexpected }, /* .ENDMACRO */
2016 { ccNone, DoEndProc },
2017 { ccNone, DoUnexpected }, /* .ENDREPEAT */
2018 { ccNone, DoEndScope },
2019 { ccNone, DoUnexpected }, /* .ENDSTRUCT */
2020 { ccNone, DoUnexpected }, /* .ENDUNION */
2022 { ccNone, DoError },
2023 { ccNone, DoExitMacro },
2024 { ccNone, DoExport },
2025 { ccNone, DoExportZP },
2026 { ccNone, DoFarAddr },
2027 { ccNone, DoFatal },
2028 { ccNone, DoFeature },
2029 { ccNone, DoFileOpt },
2030 { ccNone, DoForceImport },
2031 { ccNone, DoUnexpected }, /* .FORCEWORD */
2032 { ccNone, DoGlobal },
2033 { ccNone, DoGlobalZP },
2034 { ccNone, DoUnexpected }, /* .HIBYTE */
2035 { ccNone, DoHiBytes },
2036 { ccNone, DoUnexpected }, /* .HIWORD */
2039 { ccNone, DoUnexpected }, /* .IDENT */
2040 { ccKeepToken, DoConditionals }, /* .IF */
2041 { ccKeepToken, DoConditionals }, /* .IFBLANK */
2042 { ccKeepToken, DoConditionals }, /* .IFCONST */
2043 { ccKeepToken, DoConditionals }, /* .IFDEF */
2044 { ccKeepToken, DoConditionals }, /* .IFNBLANK */
2045 { ccKeepToken, DoConditionals }, /* .IFNCONST */
2046 { ccKeepToken, DoConditionals }, /* .IFNDEF */
2047 { ccKeepToken, DoConditionals }, /* .IFNREF */
2048 { ccKeepToken, DoConditionals }, /* .IFP02 */
2049 { ccKeepToken, DoConditionals }, /* .IFP4510 */
2050 { ccKeepToken, DoConditionals }, /* .IFP816 */
2051 { ccKeepToken, DoConditionals }, /* .IFPC02 */
2052 { ccKeepToken, DoConditionals }, /* .IFPSC02 */
2053 { ccKeepToken, DoConditionals }, /* .IFREF */
2054 { ccNone, DoImport },
2055 { ccNone, DoImportZP },
2056 { ccNone, DoIncBin },
2057 { ccNone, DoInclude },
2058 { ccNone, DoInterruptor },
2059 { ccNone, DoUnexpected }, /* .ISIZE */
2060 { ccNone, DoUnexpected }, /* .ISMNEMONIC */
2061 { ccNone, DoInvalid }, /* .LEFT */
2062 { ccNone, DoLineCont },
2064 { ccNone, DoListBytes },
2065 { ccNone, DoUnexpected }, /* .LOBYTE */
2066 { ccNone, DoLoBytes },
2067 { ccNone, DoUnexpected }, /* .LOCAL */
2068 { ccNone, DoLocalChar },
2069 { ccNone, DoUnexpected }, /* .LOWORD */
2070 { ccNone, DoMacPack },
2071 { ccNone, DoMacro },
2072 { ccNone, DoUnexpected }, /* .MATCH */
2073 { ccNone, DoUnexpected }, /* .MAX */
2074 { ccNone, DoInvalid }, /* .MID */
2075 { ccNone, DoUnexpected }, /* .MIN */
2080 { ccNone, DoP4510 },
2082 { ccNone, DoPageLength },
2083 { ccNone, DoUnexpected }, /* .PARAMCOUNT */
2085 { ccNone, DoPopCPU },
2086 { ccNone, DoPopSeg },
2088 { ccNone, DoPSC02 },
2089 { ccNone, DoPushCPU },
2090 { ccNone, DoPushSeg },
2091 { ccNone, DoUnexpected }, /* .REFERENCED */
2092 { ccNone, DoReloc },
2093 { ccNone, DoRepeat },
2095 { ccNone, DoInvalid }, /* .RIGHT */
2096 { ccNone, DoROData },
2097 { ccNone, DoScope },
2098 { ccNone, DoSegment },
2099 { ccNone, DoUnexpected }, /* .SET */
2100 { ccNone, DoSetCPU },
2101 { ccNone, DoUnexpected }, /* .SIZEOF */
2102 { ccNone, DoSmart },
2103 { ccNone, DoUnexpected }, /* .SPRINTF */
2104 { ccNone, DoUnexpected }, /* .STRAT */
2105 { ccNone, DoUnexpected }, /* .STRING */
2106 { ccNone, DoUnexpected }, /* .STRLEN */
2107 { ccNone, DoStruct },
2109 { ccNone, DoUnexpected }, /* .TCOUNT */
2110 { ccNone, DoUnexpected }, /* .TIME */
2111 { ccKeepToken, DoUnDef },
2112 { ccNone, DoUnion },
2113 { ccNone, DoUnexpected }, /* .VERSION */
2114 { ccNone, DoWarning },
2116 { ccNone, DoUnexpected }, /* .XMATCH */
2117 { ccNone, DoZeropage },
2122 /*****************************************************************************/
2124 /*****************************************************************************/
2128 void HandlePseudo (void)
2129 /* Handle a pseudo instruction */
2133 /* Calculate the index into the table */
2134 unsigned Index = CurTok.Tok - TOK_FIRSTPSEUDO;
2137 if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
2138 Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
2139 (unsigned) PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
2141 CHECK (Index < PSEUDO_COUNT);
2143 /* Get the pseudo intruction descriptor */
2144 D = &CtrlCmdTab [Index];
2146 /* Remember the instruction, then skip it if needed */
2147 if ((D->Flags & ccKeepToken) == 0) {
2148 SB_Copy (&Keyword, &CurTok.SVal);
2152 /* Call the handler */
2158 void CheckPseudo (void)
2159 /* Check if the stacks are empty at end of assembly */
2161 if (CollCount (&SegStack) != 0) {
2162 Warning (1, "Segment stack is not empty");
2164 if (!IS_IsEmpty (&CPUStack)) {
2165 Warning (1, "CPU stack is not empty");