1 /*****************************************************************************/
5 /* Pseudo instructions for the ca65 macroassembler */
9 /* (C) 1998-2011, Ullrich von Bassewitz */
10 /* Roemerstrasse 52 */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
15 /* This software is provided 'as-is', without any expressed or implied */
16 /* warranty. In no event will the authors be held liable for any damages */
17 /* arising from the use of this software. */
19 /* Permission is granted to anyone to use this software for any purpose, */
20 /* including commercial applications, and to alter it and redistribute it */
21 /* freely, subject to the following restrictions: */
23 /* 1. The origin of this software must not be misrepresented; you must not */
24 /* claim that you wrote the original software. If you use this software */
25 /* in a product, an acknowledgment in the product documentation would be */
26 /* appreciated but is not required. */
27 /* 2. Altered source versions must be plainly marked as such, and must not */
28 /* be misrepresented as being the original software. */
29 /* 3. This notice may not be removed or altered from any source */
32 /*****************************************************************************/
41 #include <sys/types.h> /* EMX needs this */
45 #include "assertion.h"
50 #include "scopedefs.h"
85 /*****************************************************************************/
87 /*****************************************************************************/
91 /* Keyword we're about to handle */
92 static StrBuf Keyword = STATIC_STRBUF_INITIALIZER;
95 static IntStack CPUStack = STATIC_INTSTACK_INITIALIZER;
98 #define MAX_PUSHED_SEGMENTS 16
99 static Collection SegStack = STATIC_COLLECTION_INITIALIZER;
103 /*****************************************************************************/
105 /*****************************************************************************/
109 static void DoUnexpected (void);
110 /* Got an unexpected keyword */
112 static void DoInvalid (void);
113 /* Handle a token that is invalid here, since it should have been handled on
114 * a much lower level of the expression hierarchy. Getting this sort of token
115 * means that the lower level code has bugs.
116 * This function differs to DoUnexpected in that the latter may be triggered
117 * by the user by using keywords in the wrong location. DoUnexpected is not
118 * an error in the assembler itself, while DoInvalid is.
123 /*****************************************************************************/
124 /* Helper functions */
125 /*****************************************************************************/
129 static unsigned char OptionalAddrSize (void)
130 /* If a colon follows, parse an optional address size spec and return it.
131 * Otherwise return ADDR_SIZE_DEFAULT.
134 unsigned AddrSize = ADDR_SIZE_DEFAULT;
135 if (CurTok.Tok == TOK_COLON) {
137 AddrSize = ParseAddrSize ();
138 if (!ValidAddrSizeForCPU (AddrSize)) {
139 /* Print an error and reset to default */
140 Error ("Invalid address size specification for current CPU");
141 AddrSize = ADDR_SIZE_DEFAULT;
150 static void SetBoolOption (unsigned char* Flag)
151 /* Read a on/off/+/- option and set flag accordingly */
153 static const char* Keys[] = {
158 if (CurTok.Tok == TOK_PLUS) {
161 } else if (CurTok.Tok == TOK_MINUS) {
164 } else if (CurTok.Tok == TOK_IDENT) {
165 /* Map the keyword to a number */
166 switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
167 case 0: *Flag = 0; NextTok (); break;
168 case 1: *Flag = 1; NextTok (); break;
169 default: ErrorSkip ("`on' or `off' expected"); break;
171 } else if (TokIsSep (CurTok.Tok)) {
172 /* Without anything assume switch on */
175 ErrorSkip ("`on' or `off' expected");
181 static void ExportWithAssign (SymEntry* Sym, unsigned char AddrSize, unsigned Flags)
182 /* Allow to assign the value of an export in an .export statement */
184 /* The name and optional address size spec may be followed by an assignment
187 if (CurTok.Tok == TOK_ASSIGN || CurTok.Tok == TOK_EQ) {
189 /* Assignment means the symbol is a label */
190 if (CurTok.Tok == TOK_ASSIGN) {
194 /* Skip the assignment token */
197 /* Define the symbol with the expression following the '=' */
198 SymDef (Sym, Expression(), ADDR_SIZE_DEFAULT, Flags);
202 /* Now export the symbol */
203 SymExport (Sym, AddrSize, Flags);
208 static void ExportImport (void (*Func) (SymEntry*, unsigned char, unsigned),
209 unsigned char DefAddrSize, unsigned Flags)
210 /* Export or import symbols */
213 unsigned char AddrSize;
217 /* We need an identifier here */
218 if (CurTok.Tok != TOK_IDENT) {
219 ErrorSkip ("Identifier expected");
223 /* Find the symbol table entry, allocate a new one if necessary */
224 Sym = SymFind (CurrentScope, &CurTok.SVal, SYM_ALLOC_NEW);
229 /* Get an optional address size */
230 AddrSize = OptionalAddrSize ();
231 if (AddrSize == ADDR_SIZE_DEFAULT) {
232 AddrSize = DefAddrSize;
235 /* Call the actual import/export function */
236 Func (Sym, AddrSize, Flags);
239 if (CurTok.Tok == TOK_COMMA) {
249 static long IntArg (long Min, long Max)
250 /* Read an integer argument and check a range. Accept the token "unlimited"
251 * and return -1 in this case.
254 if (CurTok.Tok == TOK_IDENT && SB_CompareStr (&CurTok.SVal, "unlimited") == 0) {
258 long Val = ConstExpression ();
259 if (Val < Min || Val > Max) {
260 Error ("Range error");
269 static void ConDes (const StrBuf* Name, unsigned Type)
270 /* Parse remaining line for constructor/destructor of the remaining type */
275 /* Find the symbol table entry, allocate a new one if necessary */
276 SymEntry* Sym = SymFind (CurrentScope, Name, SYM_ALLOC_NEW);
278 /* Optional constructor priority */
279 if (CurTok.Tok == TOK_COMMA) {
280 /* Priority value follows */
282 Prio = ConstExpression ();
283 if (Prio < CD_PRIO_MIN || Prio > CD_PRIO_MAX) {
284 /* Value out of range */
285 Error ("Range error");
289 /* Use the default priority value */
293 /* Define the symbol */
294 SymConDes (Sym, ADDR_SIZE_DEFAULT, Type, (unsigned) Prio);
299 /*****************************************************************************/
300 /* Handler functions */
301 /*****************************************************************************/
305 static void DoA16 (void)
306 /* Switch the accu to 16 bit mode (assembler only) */
308 if (GetCPU() != CPU_65816) {
309 Error ("Command is only valid in 65816 mode");
311 /* Immidiate mode has two extension bytes */
312 ExtBytes [AM65I_IMM_ACCU] = 2;
318 static void DoA8 (void)
319 /* Switch the accu to 8 bit mode (assembler only) */
321 if (GetCPU() != CPU_65816) {
322 Error ("Command is only valid in 65816 mode");
324 /* Immidiate mode has one extension byte */
325 ExtBytes [AM65I_IMM_ACCU] = 1;
331 static void DoAddr (void)
332 /* Define addresses */
335 if (GetCPU() == CPU_65816) {
336 EmitWord (GenWordExpr (Expression ()));
338 /* Do a range check */
339 EmitWord (Expression ());
341 if (CurTok.Tok != TOK_COMMA) {
351 static void DoAlign (void)
352 /* Align the PC to some boundary */
358 /* Read the alignment value */
359 Align = ConstExpression ();
360 if (Align <= 0 || Align > 0x10000) {
361 ErrorSkip ("Range error");
365 /* Optional value follows */
366 if (CurTok.Tok == TOK_COMMA) {
368 Val = ConstExpression ();
369 /* We need a byte value here */
370 if (!IsByteRange (Val)) {
371 ErrorSkip ("Range error");
378 /* Check if the alignment is a power of two */
379 Bit = BitFind (Align);
380 if (Align != (0x01L << Bit)) {
381 Error ("Alignment value must be a power of 2");
383 SegAlign (Bit, (int) Val);
389 static void DoASCIIZ (void)
390 /* Define text with a zero terminator */
393 /* Must have a string constant */
394 if (CurTok.Tok != TOK_STRCON) {
395 ErrorSkip ("String constant expected");
399 /* Translate into target charset and emit */
400 TgtTranslateStrBuf (&CurTok.SVal);
401 EmitStrBuf (&CurTok.SVal);
403 if (CurTok.Tok == TOK_COMMA) {
414 static void DoAssert (void)
415 /* Add an assertion */
417 static const char* ActionTab [] = {
420 "LDWARN", "LDWARNING",
427 /* First we have the expression that has to evaluated */
428 ExprNode* Expr = Expression ();
432 if (CurTok.Tok != TOK_IDENT) {
433 ErrorSkip ("Identifier expected");
436 switch (GetSubKey (ActionTab, sizeof (ActionTab) / sizeof (ActionTab[0]))) {
441 Action = ASSERT_ACT_WARN;
446 Action = ASSERT_ACT_ERROR;
452 Action = ASSERT_ACT_LDWARN;
457 Action = ASSERT_ACT_LDERROR;
461 Error ("Illegal assert action specifier");
462 /* Use lderror - there won't be an .o file anyway */
463 Action = ASSERT_ACT_LDERROR;
469 /* We can have an optional message. If no message is present, use
470 * "Assertion failed".
472 if (CurTok.Tok == TOK_COMMA) {
477 /* Read the message */
478 if (CurTok.Tok != TOK_STRCON) {
479 ErrorSkip ("String constant expected");
483 /* Translate the message into a string id. We can then skip the input
486 Msg = GetStrBufId (&CurTok.SVal);
491 /* Use "Assertion failed" */
492 Msg = GetStringId ("Assertion failed");
496 /* Remember the assertion */
497 AddAssertion (Expr, (AssertAction) Action, Msg);
502 static void DoAutoImport (void)
503 /* Mark unresolved symbols as imported */
505 SetBoolOption (&AutoImport);
509 static void DoBankBytes (void)
510 /* Define bytes, extracting the bank byte from each expression in the list */
513 EmitByte (FuncBankByte ());
514 if (CurTok.Tok != TOK_COMMA) {
524 static void DoBss (void)
525 /* Switch to the BSS segment */
532 static void DoByte (void)
536 if (CurTok.Tok == TOK_STRCON) {
537 /* A string, translate into target charset and emit */
538 TgtTranslateStrBuf (&CurTok.SVal);
539 EmitStrBuf (&CurTok.SVal);
542 EmitByte (Expression ());
544 if (CurTok.Tok != TOK_COMMA) {
548 /* Do smart handling of dangling comma */
549 if (CurTok.Tok == TOK_SEP) {
550 Error ("Unexpected end of line");
559 static void DoCase (void)
560 /* Switch the IgnoreCase option */
562 SetBoolOption (&IgnoreCase);
563 IgnoreCase = !IgnoreCase;
568 static void DoCharMap (void)
569 /* Allow custome character mappings */
574 /* Read the index as numerical value */
575 Index = ConstExpression ();
576 if (Index < 0 || Index > 255) {
577 /* Value out of range */
578 ErrorSkip ("Range error");
585 /* Read the character code */
586 Code = ConstExpression ();
587 if (Code < 0 || Code > 255) {
588 /* Value out of range */
589 ErrorSkip ("Range error");
593 /* Set the character translation */
594 TgtTranslateSet ((unsigned) Index, (unsigned char) Code);
599 static void DoCode (void)
600 /* Switch to the code segment */
602 UseSeg (&CodeSegDef);
607 static void DoConDes (void)
608 /* Export a symbol as constructor/destructor */
610 static const char* Keys[] = {
615 StrBuf Name = STATIC_STRBUF_INITIALIZER;
618 /* Symbol name follows */
619 if (CurTok.Tok != TOK_IDENT) {
620 ErrorSkip ("Identifier expected");
623 SB_Copy (&Name, &CurTok.SVal);
626 /* Type follows. May be encoded as identifier or numerical */
628 if (CurTok.Tok == TOK_IDENT) {
630 /* Map the following keyword to a number, then skip it */
631 Type = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
634 /* Check if we got a valid keyword */
636 ErrorSkip ("Syntax error");
642 /* Read the type as numerical value */
643 Type = ConstExpression ();
644 if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) {
645 /* Value out of range */
646 ErrorSkip ("Range error");
652 /* Parse the remainder of the line and export the symbol */
653 ConDes (&Name, (unsigned) Type);
656 /* Free string memory */
662 static void DoConstructor (void)
663 /* Export a symbol as constructor */
665 StrBuf Name = STATIC_STRBUF_INITIALIZER;
667 /* Symbol name follows */
668 if (CurTok.Tok != TOK_IDENT) {
669 ErrorSkip ("Identifier expected");
672 SB_Copy (&Name, &CurTok.SVal);
675 /* Parse the remainder of the line and export the symbol */
676 ConDes (&Name, CD_TYPE_CON);
678 /* Free string memory */
684 static void DoData (void)
685 /* Switch to the data segment */
687 UseSeg (&DataSegDef);
692 static void DoDbg (void)
693 /* Add debug information from high level code */
695 static const char* Keys[] = {
703 /* We expect a subkey */
704 if (CurTok.Tok != TOK_IDENT) {
705 ErrorSkip ("Identifier expected");
709 /* Map the following keyword to a number */
710 Key = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
712 /* Skip the subkey */
715 /* Check the key and dispatch to a handler */
717 case 0: DbgInfoFile (); break;
718 case 1: DbgInfoLine (); break;
719 case 2: DbgInfoSym (); break;
720 default: ErrorSkip ("Syntax error"); break;
726 static void DoDByt (void)
727 /* Output double bytes */
730 EmitWord (GenSwapExpr (Expression ()));
731 if (CurTok.Tok != TOK_COMMA) {
741 static void DoDebugInfo (void)
742 /* Switch debug info on or off */
744 SetBoolOption (&DbgSyms);
749 static void DoDefine (void)
750 /* Define a one line macro */
752 MacDef (MAC_STYLE_DEFINE);
757 static void DoDelMac (void)
758 /* Delete a classic macro */
760 /* We expect an identifier */
761 if (CurTok.Tok != TOK_IDENT) {
762 ErrorSkip ("Identifier expected");
764 MacUndef (&CurTok.SVal, MAC_STYLE_CLASSIC);
771 static void DoDestructor (void)
772 /* Export a symbol as destructor */
774 StrBuf Name = STATIC_STRBUF_INITIALIZER;
776 /* Symbol name follows */
777 if (CurTok.Tok != TOK_IDENT) {
778 ErrorSkip ("Identifier expected");
781 SB_Copy (&Name, &CurTok.SVal);
784 /* Parse the remainder of the line and export the symbol */
785 ConDes (&Name, CD_TYPE_DES);
787 /* Free string memory */
793 static void DoDWord (void)
797 EmitDWord (Expression ());
798 if (CurTok.Tok != TOK_COMMA) {
808 static void DoEnd (void)
809 /* End of assembly */
817 static void DoEndProc (void)
818 /* Leave a lexical level */
820 if (GetCurrentSymTabType () != SCOPETYPE_PROC) {
822 ErrorSkip ("No open .PROC");
830 static void DoEndScope (void)
831 /* Leave a lexical level */
833 if ( GetCurrentSymTabType () != SCOPETYPE_SCOPE) {
835 ErrorSkip ("No open .SCOPE");
843 static void DoError (void)
846 if (CurTok.Tok != TOK_STRCON) {
847 ErrorSkip ("String constant expected");
849 Error ("User error: %m%p", &CurTok.SVal);
856 static void DoExitMacro (void)
857 /* Exit a macro expansion */
859 if (!InMacExpansion ()) {
860 /* We aren't expanding a macro currently */
869 static void DoExport (void)
870 /* Export a symbol */
872 ExportImport (ExportWithAssign, ADDR_SIZE_DEFAULT, SF_NONE);
877 static void DoExportZP (void)
878 /* Export a zeropage symbol */
880 ExportImport (ExportWithAssign, ADDR_SIZE_ZP, SF_NONE);
885 static void DoFarAddr (void)
886 /* Define far addresses (24 bit) */
889 EmitFarAddr (Expression ());
890 if (CurTok.Tok != TOK_COMMA) {
900 static void DoFatal (void)
901 /* Fatal user error */
903 if (CurTok.Tok != TOK_STRCON) {
904 ErrorSkip ("String constant expected");
906 Fatal ("User error: %m%p", &CurTok.SVal);
913 static void DoFeature (void)
914 /* Switch the Feature option */
916 /* Allow a list of comma separated keywords */
919 /* We expect an identifier */
920 if (CurTok.Tok != TOK_IDENT) {
921 ErrorSkip ("Identifier expected");
925 /* Make the string attribute lower case */
928 /* Set the feature and check for errors */
929 if (SetFeature (&CurTok.SVal) == FEAT_UNKNOWN) {
931 ErrorSkip ("Invalid feature: `%m%p'", &CurTok.SVal);
934 /* Skip the keyword */
938 /* Allow more than one keyword */
939 if (CurTok.Tok == TOK_COMMA) {
949 static void DoFileOpt (void)
950 /* Insert a file option */
954 /* The option type may be given as a keyword or as a number. */
955 if (CurTok.Tok == TOK_IDENT) {
957 /* Option given as keyword */
958 static const char* Keys [] = {
959 "AUTHOR", "COMMENT", "COMPILER"
962 /* Map the option to a number */
963 OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
966 ErrorSkip ("File option keyword expected");
970 /* Skip the keyword */
973 /* Must be followed by a comma */
976 /* We accept only string options for now */
977 if (CurTok.Tok != TOK_STRCON) {
978 ErrorSkip ("String constant expected");
982 /* Insert the option */
987 OptAuthor (&CurTok.SVal);
992 OptComment (&CurTok.SVal);
997 OptCompiler (&CurTok.SVal);
1001 Internal ("Invalid OptNum: %ld", OptNum);
1010 /* Option given as number */
1011 OptNum = ConstExpression ();
1012 if (!IsByteRange (OptNum)) {
1013 ErrorSkip ("Range error");
1017 /* Must be followed by a comma */
1020 /* We accept only string options for now */
1021 if (CurTok.Tok != TOK_STRCON) {
1022 ErrorSkip ("String constant expected");
1026 /* Insert the option */
1027 OptStr ((unsigned char) OptNum, &CurTok.SVal);
1036 static void DoForceImport (void)
1037 /* Do a forced import on a symbol */
1039 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_FORCED);
1044 static void DoGlobal (void)
1045 /* Declare a global symbol */
1047 ExportImport (SymGlobal, ADDR_SIZE_DEFAULT, SF_NONE);
1052 static void DoGlobalZP (void)
1053 /* Declare a global zeropage symbol */
1055 ExportImport (SymGlobal, ADDR_SIZE_ZP, SF_NONE);
1059 static void DoHiBytes (void)
1060 /* Define bytes, extracting the hi byte from each expression in the list */
1063 EmitByte (FuncHiByte ());
1064 if (CurTok.Tok != TOK_COMMA) {
1074 static void DoI16 (void)
1075 /* Switch the index registers to 16 bit mode (assembler only) */
1077 if (GetCPU() != CPU_65816) {
1078 Error ("Command is only valid in 65816 mode");
1080 /* Immidiate mode has two extension bytes */
1081 ExtBytes [AM65I_IMM_INDEX] = 2;
1087 static void DoI8 (void)
1088 /* Switch the index registers to 16 bit mode (assembler only) */
1090 if (GetCPU() != CPU_65816) {
1091 Error ("Command is only valid in 65816 mode");
1093 /* Immidiate mode has one extension byte */
1094 ExtBytes [AM65I_IMM_INDEX] = 1;
1100 static void DoImport (void)
1101 /* Import a symbol */
1103 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_NONE);
1108 static void DoImportZP (void)
1109 /* Import a zero page symbol */
1111 ExportImport (SymImport, ADDR_SIZE_ZP, SF_NONE);
1116 static void DoIncBin (void)
1117 /* Include a binary file */
1119 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1120 struct stat StatBuf;
1126 /* Name must follow */
1127 if (CurTok.Tok != TOK_STRCON) {
1128 ErrorSkip ("String constant expected");
1131 SB_Copy (&Name, &CurTok.SVal);
1132 SB_Terminate (&Name);
1135 /* A starting offset may follow */
1136 if (CurTok.Tok == TOK_COMMA) {
1138 Start = ConstExpression ();
1140 /* And a length may follow */
1141 if (CurTok.Tok == TOK_COMMA) {
1143 Count = ConstExpression ();
1148 /* Try to open the file */
1149 F = fopen (SB_GetConstBuf (&Name), "rb");
1152 /* Search for the file in the binary include directory */
1153 char* PathName = SearchFile (BinSearchPath, SB_GetConstBuf (&Name));
1154 if (PathName == 0 || (F = fopen (PathName, "rb")) == 0) {
1155 /* Not found or cannot open, print an error and bail out */
1156 ErrorSkip ("Cannot open include file `%m%p': %s", &Name, strerror (errno));
1161 /* Remember the new file name */
1162 SB_CopyStr (&Name, PathName);
1164 /* Free the allocated memory */
1168 /* Get the size of the file */
1169 fseek (F, 0, SEEK_END);
1172 /* Stat the file and remember the values. There a race condition here,
1173 * since we cannot use fileno() (non standard identifier in standard
1174 * header file), and therefore not fstat. When using stat with the
1175 * file name, there's a risk that the file was deleted and recreated
1176 * while it was open. Since mtime and size are only used to check
1177 * if a file has changed in the debugger, we will ignore this problem
1180 SB_Terminate (&Name);
1181 if (stat (SB_GetConstBuf (&Name), &StatBuf) != 0) {
1182 Fatal ("Cannot stat input file `%m%p': %s", &Name, strerror (errno));
1185 /* Add the file to the input file table */
1186 AddFile (&Name, FT_BINARY, Size, StatBuf.st_mtime);
1188 /* If a count was not given, calculate it now */
1190 Count = Size - Start;
1192 /* Nothing to read - flag this as a range error */
1193 ErrorSkip ("Range error");
1197 /* Count was given, check if it is valid */
1198 if (Start + Count > Size) {
1199 ErrorSkip ("Range error");
1204 /* Seek to the start position */
1205 fseek (F, Start, SEEK_SET);
1207 /* Read chunks and insert them into the output */
1210 unsigned char Buf [1024];
1212 /* Calculate the number of bytes to read */
1213 size_t BytesToRead = (Count > (long)sizeof(Buf))? sizeof(Buf) : (size_t) Count;
1216 size_t BytesRead = fread (Buf, 1, BytesToRead, F);
1217 if (BytesToRead != BytesRead) {
1218 /* Some sort of error */
1219 ErrorSkip ("Cannot read from include file `%m%p': %s",
1220 &Name, strerror (errno));
1224 /* Insert it into the output */
1225 EmitData (Buf, BytesRead);
1227 /* Keep the counters current */
1232 /* Close the file, ignore errors since it's r/o */
1236 /* Free string memory */
1242 static void DoInclude (void)
1243 /* Include another file */
1245 /* Name must follow */
1246 if (CurTok.Tok != TOK_STRCON) {
1247 ErrorSkip ("String constant expected");
1249 SB_Terminate (&CurTok.SVal);
1250 if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) {
1251 /* Error opening the file, skip remainder of line */
1259 static void DoInterruptor (void)
1260 /* Export a symbol as interruptor */
1262 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1264 /* Symbol name follows */
1265 if (CurTok.Tok != TOK_IDENT) {
1266 ErrorSkip ("Identifier expected");
1269 SB_Copy (&Name, &CurTok.SVal);
1272 /* Parse the remainder of the line and export the symbol */
1273 ConDes (&Name, CD_TYPE_INT);
1275 /* Free string memory */
1281 static void DoInvalid (void)
1282 /* Handle a token that is invalid here, since it should have been handled on
1283 * a much lower level of the expression hierarchy. Getting this sort of token
1284 * means that the lower level code has bugs.
1285 * This function differs to DoUnexpected in that the latter may be triggered
1286 * by the user by using keywords in the wrong location. DoUnexpected is not
1287 * an error in the assembler itself, while DoInvalid is.
1290 Internal ("Unexpected token: %m%p", &Keyword);
1295 static void DoLineCont (void)
1296 /* Switch the use of line continuations */
1298 SetBoolOption (&LineCont);
1303 static void DoList (void)
1304 /* Enable/disable the listing */
1306 /* Get the setting */
1308 SetBoolOption (&List);
1310 /* Manage the counter */
1320 static void DoLoBytes (void)
1321 /* Define bytes, extracting the lo byte from each expression in the list */
1324 EmitByte (FuncLoByte ());
1325 if (CurTok.Tok != TOK_COMMA) {
1334 static void DoListBytes (void)
1335 /* Set maximum number of bytes to list for one line */
1337 SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
1342 static void DoLocalChar (void)
1343 /* Define the character that starts local labels */
1345 if (CurTok.Tok != TOK_CHARCON) {
1346 ErrorSkip ("Character constant expected");
1348 if (CurTok.IVal != '@' && CurTok.IVal != '?') {
1349 Error ("Invalid start character for locals");
1351 LocalStart = (char) CurTok.IVal;
1359 static void DoMacPack (void)
1360 /* Insert a macro package */
1364 /* We expect an identifier */
1365 if (CurTok.Tok != TOK_IDENT) {
1366 ErrorSkip ("Identifier expected");
1370 /* Search for the macro package name */
1372 Package = MacPackFind (&CurTok.SVal);
1375 ErrorSkip ("Invalid macro package");
1379 /* Insert the package. If this fails, skip the remainder of the line to
1380 * avoid additional error messages.
1382 if (MacPackInsert (Package) == 0) {
1389 static void DoMacro (void)
1390 /* Start a macro definition */
1392 MacDef (MAC_STYLE_CLASSIC);
1397 static void DoNull (void)
1398 /* Switch to the NULL segment */
1400 UseSeg (&NullSegDef);
1405 static void DoOrg (void)
1406 /* Start absolute code */
1408 long PC = ConstExpression ();
1409 if (PC < 0 || PC > 0xFFFFFF) {
1410 Error ("Range error");
1413 EnterAbsoluteMode (PC);
1418 static void DoOut (void)
1419 /* Output a string */
1421 if (CurTok.Tok != TOK_STRCON) {
1422 ErrorSkip ("String constant expected");
1424 /* Output the string and be sure to flush the output to keep it in
1425 * sync with any error messages if the output is redirected to a file.
1428 (int) SB_GetLen (&CurTok.SVal),
1429 SB_GetConstBuf (&CurTok.SVal));
1437 static void DoP02 (void)
1438 /* Switch to 6502 CPU */
1445 static void DoPC02 (void)
1446 /* Switch to 65C02 CPU */
1453 static void DoP816 (void)
1454 /* Switch to 65816 CPU */
1461 static void DoPageLength (void)
1462 /* Set the page length for the listing */
1464 PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
1469 static void DoPopCPU (void)
1470 /* Pop an old CPU setting from the CPU stack */
1472 /* Must have a CPU on the stack */
1473 if (IS_IsEmpty (&CPUStack)) {
1474 ErrorSkip ("CPU stack is empty");
1478 /* Set the CPU to the value popped from stack */
1479 SetCPU (IS_Pop (&CPUStack));
1484 static void DoPopSeg (void)
1485 /* Pop an old segment from the segment stack */
1489 /* Must have a segment on the stack */
1490 if (CollCount (&SegStack) == 0) {
1491 ErrorSkip ("Segment stack is empty");
1495 /* Pop the last element */
1496 Def = CollPop (&SegStack);
1498 /* Restore this segment */
1501 /* Delete the segment definition */
1507 static void DoProc (void)
1508 /* Start a new lexical scope */
1510 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1511 unsigned char AddrSize;
1515 if (CurTok.Tok == TOK_IDENT) {
1517 /* The new scope has a name. Remember it. */
1518 SB_Copy (&Name, &CurTok.SVal);
1520 /* Search for the symbol, generate a new one if needed */
1521 Sym = SymFind (CurrentScope, &Name, SYM_ALLOC_NEW);
1523 /* Skip the scope name */
1526 /* Read an optional address size specifier */
1527 AddrSize = OptionalAddrSize ();
1529 /* Mark the symbol as defined */
1530 SymDef (Sym, GenCurrentPC (), AddrSize, SF_LABEL);
1534 /* A .PROC statement without a name */
1535 Warning (1, "Unnamed .PROCs are deprecated, please use .SCOPE");
1536 AnonName (&Name, "PROC");
1537 AddrSize = ADDR_SIZE_DEFAULT;
1541 /* Enter a new scope */
1542 SymEnterLevel (&Name, SCOPETYPE_PROC, AddrSize, Sym);
1544 /* Free memory for Name */
1550 static void DoPSC02 (void)
1551 /* Switch to 65SC02 CPU */
1553 SetCPU (CPU_65SC02);
1558 static void DoPushCPU (void)
1559 /* Push the current CPU setting onto the CPU stack */
1561 /* Can only push a limited size of segments */
1562 if (IS_IsFull (&CPUStack)) {
1563 ErrorSkip ("CPU stack overflow");
1567 /* Get the current segment and push it */
1568 IS_Push (&CPUStack, GetCPU ());
1573 static void DoPushSeg (void)
1574 /* Push the current segment onto the segment stack */
1576 /* Can only push a limited size of segments */
1577 if (CollCount (&SegStack) >= MAX_PUSHED_SEGMENTS) {
1578 ErrorSkip ("Segment stack overflow");
1582 /* Get the current segment and push it */
1583 CollAppend (&SegStack, DupSegDef (GetCurrentSegDef ()));
1588 static void DoReloc (void)
1589 /* Enter relocatable mode */
1596 static void DoRepeat (void)
1597 /* Repeat some instruction block */
1604 static void DoRes (void)
1605 /* Reserve some number of storage bytes */
1610 Count = ConstExpression ();
1611 if (Count > 0xFFFF || Count < 0) {
1612 ErrorSkip ("Range error");
1615 if (CurTok.Tok == TOK_COMMA) {
1617 Val = ConstExpression ();
1618 /* We need a byte value here */
1619 if (!IsByteRange (Val)) {
1620 ErrorSkip ("Range error");
1624 /* Emit constant values */
1626 Emit0 ((unsigned char) Val);
1630 /* Emit fill fragments */
1637 static void DoROData (void)
1638 /* Switch to the r/o data segment */
1640 UseSeg (&RODataSegDef);
1645 static void DoScope (void)
1646 /* Start a local scope */
1648 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1649 unsigned char AddrSize;
1652 if (CurTok.Tok == TOK_IDENT) {
1654 /* The new scope has a name. Remember and skip it. */
1655 SB_Copy (&Name, &CurTok.SVal);
1660 /* An unnamed scope */
1661 AnonName (&Name, "SCOPE");
1665 /* Read an optional address size specifier */
1666 AddrSize = OptionalAddrSize ();
1668 /* Enter the new scope */
1669 SymEnterLevel (&Name, SCOPETYPE_SCOPE, AddrSize, 0);
1671 /* Free memory for Name */
1677 static void DoSegment (void)
1678 /* Switch to another segment */
1680 StrBuf Name = STATIC_STRBUF_INITIALIZER;
1683 if (CurTok.Tok != TOK_STRCON) {
1684 ErrorSkip ("String constant expected");
1687 /* Save the name of the segment and skip it */
1688 SB_Copy (&Name, &CurTok.SVal);
1691 /* Use the name for the segment definition */
1692 SB_Terminate (&Name);
1693 Def.Name = SB_GetBuf (&Name);
1695 /* Check for an optional address size modifier */
1696 Def.AddrSize = OptionalAddrSize ();
1698 /* Set the segment */
1702 /* Free memory for Name */
1708 static void DoSetCPU (void)
1709 /* Switch the CPU instruction set */
1711 /* We expect an identifier */
1712 if (CurTok.Tok != TOK_STRCON) {
1713 ErrorSkip ("String constant expected");
1717 /* Try to find the CPU */
1718 SB_Terminate (&CurTok.SVal);
1719 CPU = FindCPU (SB_GetConstBuf (&CurTok.SVal));
1721 /* Switch to the new CPU */
1724 /* Skip the identifier. If the CPU switch was successful, the scanner
1725 * will treat the input now correctly for the new CPU.
1733 static void DoSmart (void)
1734 /* Smart mode on/off */
1736 SetBoolOption (&SmartMode);
1741 static void DoSunPlus (void)
1742 /* Switch to the SUNPLUS CPU */
1744 SetCPU (CPU_SUNPLUS);
1749 static void DoTag (void)
1750 /* Allocate space for a struct */
1755 /* Read the struct name */
1756 SymTable* Struct = ParseScopedSymTable ();
1758 /* Check the supposed struct */
1760 ErrorSkip ("Unknown struct");
1763 if (GetSymTabType (Struct) != SCOPETYPE_STRUCT) {
1764 ErrorSkip ("Not a struct");
1768 /* Get the symbol that defines the size of the struct */
1769 SizeSym = GetSizeOfScope (Struct);
1771 /* Check if it does exist and if its value is known */
1772 if (SizeSym == 0 || !SymIsConst (SizeSym, &Size)) {
1773 ErrorSkip ("Size of struct/union is unknown");
1777 /* Optional multiplicator may follow */
1778 if (CurTok.Tok == TOK_COMMA) {
1781 Multiplicator = ConstExpression ();
1782 /* Multiplicator must make sense */
1783 if (Multiplicator <= 0) {
1784 ErrorSkip ("Range error");
1787 Size *= Multiplicator;
1790 /* Emit fill fragments */
1796 static void DoUnDef (void)
1797 /* Undefine a define style macro */
1799 /* The function is called with the .UNDEF token in place, because we need
1800 * to disable .define macro expansions before reading the next token.
1801 * Otherwise the name of the macro would be expanded, so we would never
1804 DisableDefineStyleMacros ();
1806 EnableDefineStyleMacros ();
1808 /* We expect an identifier */
1809 if (CurTok.Tok != TOK_IDENT) {
1810 ErrorSkip ("Identifier expected");
1812 MacUndef (&CurTok.SVal, MAC_STYLE_DEFINE);
1819 static void DoUnexpected (void)
1820 /* Got an unexpected keyword */
1822 Error ("Unexpected `%m%p'", &Keyword);
1828 static void DoWarning (void)
1831 if (CurTok.Tok != TOK_STRCON) {
1832 ErrorSkip ("String constant expected");
1834 Warning (0, "User warning: %m%p", &CurTok.SVal);
1841 static void DoWord (void)
1845 EmitWord (Expression ());
1846 if (CurTok.Tok != TOK_COMMA) {
1856 static void DoZeropage (void)
1857 /* Switch to the zeropage segment */
1859 UseSeg (&ZeropageSegDef);
1864 /*****************************************************************************/
1866 /*****************************************************************************/
1870 /* Control commands flags */
1872 ccNone = 0x0000, /* No special flags */
1873 ccKeepToken = 0x0001 /* Do not skip the current token */
1876 /* Control command table */
1877 typedef struct CtrlDesc CtrlDesc;
1879 unsigned Flags; /* Flags for this directive */
1880 void (*Handler) (void); /* Command handler */
1883 #define PSEUDO_COUNT (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1884 static CtrlDesc CtrlCmdTab [] = {
1887 { ccNone, DoAddr }, /* .ADDR */
1888 { ccNone, DoAlign },
1889 { ccNone, DoASCIIZ },
1890 { ccNone, DoAssert },
1891 { ccNone, DoAutoImport },
1892 { ccNone, DoUnexpected }, /* .BANKBYTE */
1893 { ccNone, DoBankBytes },
1894 { ccNone, DoUnexpected }, /* .BLANK */
1898 { ccNone, DoCharMap },
1900 { ccNone, DoUnexpected, }, /* .CONCAT */
1901 { ccNone, DoConDes },
1902 { ccNone, DoUnexpected }, /* .CONST */
1903 { ccNone, DoConstructor },
1904 { ccNone, DoUnexpected }, /* .CPU */
1908 { ccNone, DoDebugInfo },
1909 { ccNone, DoDefine },
1910 { ccNone, DoUnexpected }, /* .DEFINED */
1911 { ccNone, DoDelMac },
1912 { ccNone, DoDestructor },
1913 { ccNone, DoDWord },
1914 { ccKeepToken, DoConditionals }, /* .ELSE */
1915 { ccKeepToken, DoConditionals }, /* .ELSEIF */
1916 { ccKeepToken, DoEnd },
1917 { ccNone, DoUnexpected }, /* .ENDENUM */
1918 { ccKeepToken, DoConditionals }, /* .ENDIF */
1919 { ccNone, DoUnexpected }, /* .ENDMACRO */
1920 { ccNone, DoEndProc },
1921 { ccNone, DoUnexpected }, /* .ENDREPEAT */
1922 { ccNone, DoEndScope },
1923 { ccNone, DoUnexpected }, /* .ENDSTRUCT */
1924 { ccNone, DoUnexpected }, /* .ENDUNION */
1926 { ccNone, DoError },
1927 { ccNone, DoExitMacro },
1928 { ccNone, DoExport },
1929 { ccNone, DoExportZP },
1930 { ccNone, DoFarAddr },
1931 { ccNone, DoFatal },
1932 { ccNone, DoFeature },
1933 { ccNone, DoFileOpt },
1934 { ccNone, DoForceImport },
1935 { ccNone, DoUnexpected }, /* .FORCEWORD */
1936 { ccNone, DoGlobal },
1937 { ccNone, DoGlobalZP },
1938 { ccNone, DoUnexpected }, /* .HIBYTE */
1939 { ccNone, DoHiBytes },
1940 { ccNone, DoUnexpected }, /* .HIWORD */
1943 { ccNone, DoUnexpected }, /* .IDENT */
1944 { ccKeepToken, DoConditionals }, /* .IF */
1945 { ccKeepToken, DoConditionals }, /* .IFBLANK */
1946 { ccKeepToken, DoConditionals }, /* .IFCONST */
1947 { ccKeepToken, DoConditionals }, /* .IFDEF */
1948 { ccKeepToken, DoConditionals }, /* .IFNBLANK */
1949 { ccKeepToken, DoConditionals }, /* .IFNCONST */
1950 { ccKeepToken, DoConditionals }, /* .IFNDEF */
1951 { ccKeepToken, DoConditionals }, /* .IFNREF */
1952 { ccKeepToken, DoConditionals }, /* .IFP02 */
1953 { ccKeepToken, DoConditionals }, /* .IFP816 */
1954 { ccKeepToken, DoConditionals }, /* .IFPC02 */
1955 { ccKeepToken, DoConditionals }, /* .IFPSC02 */
1956 { ccKeepToken, DoConditionals }, /* .IFREF */
1957 { ccNone, DoImport },
1958 { ccNone, DoImportZP },
1959 { ccNone, DoIncBin },
1960 { ccNone, DoInclude },
1961 { ccNone, DoInterruptor },
1962 { ccNone, DoInvalid }, /* .LEFT */
1963 { ccNone, DoLineCont },
1965 { ccNone, DoListBytes },
1966 { ccNone, DoUnexpected }, /* .LOBYTE */
1967 { ccNone, DoLoBytes },
1968 { ccNone, DoUnexpected }, /* .LOCAL */
1969 { ccNone, DoLocalChar },
1970 { ccNone, DoUnexpected }, /* .LOWORD */
1971 { ccNone, DoMacPack },
1972 { ccNone, DoMacro },
1973 { ccNone, DoUnexpected }, /* .MATCH */
1974 { ccNone, DoUnexpected }, /* .MAX */
1975 { ccNone, DoInvalid }, /* .MID */
1976 { ccNone, DoUnexpected }, /* .MIN */
1982 { ccNone, DoPageLength },
1983 { ccNone, DoUnexpected }, /* .PARAMCOUNT */
1985 { ccNone, DoPopCPU },
1986 { ccNone, DoPopSeg },
1988 { ccNone, DoPSC02 },
1989 { ccNone, DoPushCPU },
1990 { ccNone, DoPushSeg },
1991 { ccNone, DoUnexpected }, /* .REFERENCED */
1992 { ccNone, DoReloc },
1993 { ccNone, DoRepeat },
1995 { ccNone, DoInvalid }, /* .RIGHT */
1996 { ccNone, DoROData },
1997 { ccNone, DoScope },
1998 { ccNone, DoSegment },
1999 { ccNone, DoUnexpected }, /* .SET */
2000 { ccNone, DoSetCPU },
2001 { ccNone, DoUnexpected }, /* .SIZEOF */
2002 { ccNone, DoSmart },
2003 { ccNone, DoUnexpected }, /* .SPRINTF */
2004 { ccNone, DoUnexpected }, /* .STRAT */
2005 { ccNone, DoUnexpected }, /* .STRING */
2006 { ccNone, DoUnexpected }, /* .STRLEN */
2007 { ccNone, DoStruct },
2008 { ccNone, DoSunPlus },
2010 { ccNone, DoUnexpected }, /* .TCOUNT */
2011 { ccNone, DoUnexpected }, /* .TIME */
2012 { ccKeepToken, DoUnDef },
2013 { ccNone, DoUnion },
2014 { ccNone, DoUnexpected }, /* .VERSION */
2015 { ccNone, DoWarning },
2017 { ccNone, DoUnexpected }, /* .XMATCH */
2018 { ccNone, DoZeropage },
2023 /*****************************************************************************/
2025 /*****************************************************************************/
2029 void HandlePseudo (void)
2030 /* Handle a pseudo instruction */
2034 /* Calculate the index into the table */
2035 unsigned Index = CurTok.Tok - TOK_FIRSTPSEUDO;
2038 if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
2039 Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
2040 (unsigned) PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
2042 CHECK (Index < PSEUDO_COUNT);
2044 /* Get the pseudo intruction descriptor */
2045 D = &CtrlCmdTab [Index];
2047 /* Remember the instruction, then skip it if needed */
2048 if ((D->Flags & ccKeepToken) == 0) {
2049 SB_Copy (&Keyword, &CurTok.SVal);
2053 /* Call the handler */
2059 void CheckPseudo (void)
2060 /* Check if the stacks are empty at end of assembly */
2062 if (CollCount (&SegStack) != 0) {
2063 Warning (1, "Segment stack is not empty");
2065 if (!IS_IsEmpty (&CPUStack)) {
2066 Warning (1, "CPU stack is not empty");