1 /*****************************************************************************/
5 /* Pseudo instructions for the ca65 macroassembler */
9 /* (C) 1998-2005, Ullrich von Bassewitz */
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 "assertdefs.h"
80 /*****************************************************************************/
82 /*****************************************************************************/
86 /* Keyword we're about to handle */
87 static char Keyword [sizeof (SVal)+1];
90 #define MAX_PUSHED_SEGMENTS 16
91 static Collection SegStack = STATIC_COLLECTION_INITIALIZER;
95 /*****************************************************************************/
97 /*****************************************************************************/
101 static void DoUnexpected (void);
102 /* Got an unexpected keyword */
104 static void DoInvalid (void);
105 /* Handle a token that is invalid here, since it should have been handled on
106 * a much lower level of the expression hierarchy. Getting this sort of token
107 * means that the lower level code has bugs.
108 * This function differs to DoUnexpected in that the latter may be triggered
109 * by the user by using keywords in the wrong location. DoUnexpected is not
110 * an error in the assembler itself, while DoInvalid is.
115 /*****************************************************************************/
116 /* Helper functions */
117 /*****************************************************************************/
121 static unsigned char OptionalAddrSize (void)
122 /* If a colon follows, parse an optional address size spec and return it.
123 * Otherwise return ADDR_SIZE_DEFAULT.
126 unsigned AddrSize = ADDR_SIZE_DEFAULT;
127 if (Tok == TOK_COLON) {
129 AddrSize = ParseAddrSize ();
137 static void SetBoolOption (unsigned char* Flag)
138 /* Read a on/off/+/- option and set flag accordingly */
140 static const char* Keys[] = {
145 if (Tok == TOK_PLUS) {
148 } else if (Tok == TOK_MINUS) {
151 } else if (Tok == TOK_IDENT) {
152 /* Map the keyword to a number */
153 switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
154 case 0: *Flag = 0; NextTok (); break;
155 case 1: *Flag = 1; NextTok (); break;
156 default: ErrorSkip ("`on' or `off' expected"); break;
158 } else if (TokIsSep (Tok)) {
159 /* Without anything assume switch on */
162 ErrorSkip ("`on' or `off' expected");
168 static void ExportImport (void (*Func) (SymEntry*, unsigned char, unsigned),
169 unsigned char DefAddrSize, unsigned Flags)
170 /* Export or import symbols */
173 unsigned char AddrSize;
177 /* We need an identifier here */
178 if (Tok != TOK_IDENT) {
179 ErrorSkip ("Identifier expected");
183 /* Find the symbol table entry, allocate a new one if necessary */
184 Sym = SymFind (CurrentScope, SVal, SYM_ALLOC_NEW);
189 /* Get an optional address size */
190 AddrSize = OptionalAddrSize ();
191 if (AddrSize == ADDR_SIZE_DEFAULT) {
192 AddrSize = DefAddrSize;
195 /* Call the actual import/export function */
196 Func (Sym, AddrSize, Flags);
199 if (Tok == TOK_COMMA) {
209 static long IntArg (long Min, long Max)
210 /* Read an integer argument and check a range. Accept the token "unlimited"
211 * and return -1 in this case.
214 if (Tok == TOK_IDENT && strcmp (SVal, "unlimited") == 0) {
218 long Val = ConstExpression ();
219 if (Val < Min || Val > Max) {
220 Error ("Range error");
229 static void ConDes (const char* Name, unsigned Type)
230 /* Parse remaining line for constructor/destructor of the remaining type */
235 /* Find the symbol table entry, allocate a new one if necessary */
236 SymEntry* Sym = SymFind (CurrentScope, Name, SYM_ALLOC_NEW);
238 /* Optional constructor priority */
239 if (Tok == TOK_COMMA) {
240 /* Priority value follows */
242 Prio = ConstExpression ();
243 if (Prio < CD_PRIO_MIN || Prio > CD_PRIO_MAX) {
244 /* Value out of range */
245 Error ("Range error");
249 /* Use the default priority value */
253 /* Define the symbol */
254 SymConDes (Sym, ADDR_SIZE_DEFAULT, Type, (unsigned) Prio);
259 /*****************************************************************************/
260 /* Handler functions */
261 /*****************************************************************************/
265 static void DoA16 (void)
266 /* Switch the accu to 16 bit mode (assembler only) */
268 if (GetCPU() != CPU_65816) {
269 Error ("Command is only valid in 65816 mode");
271 /* Immidiate mode has two extension bytes */
272 ExtBytes [AM65I_IMM_ACCU] = 2;
278 static void DoA8 (void)
279 /* Switch the accu to 8 bit mode (assembler only) */
281 if (GetCPU() != CPU_65816) {
282 Error ("Command is only valid in 65816 mode");
284 /* Immidiate mode has one extension byte */
285 ExtBytes [AM65I_IMM_ACCU] = 1;
291 static void DoAddr (void)
292 /* Define addresses */
295 if (GetCPU() == CPU_65816) {
296 EmitWord (GenWordExpr (Expression ()));
298 /* Do a range check */
299 EmitWord (Expression ());
301 if (Tok != TOK_COMMA) {
311 static void DoAlign (void)
312 /* Align the PC to some boundary */
318 /* Read the alignment value */
319 Align = ConstExpression ();
320 if (Align <= 0 || Align > 0x10000) {
321 ErrorSkip ("Range error");
325 /* Optional value follows */
326 if (Tok == TOK_COMMA) {
328 Val = ConstExpression ();
329 /* We need a byte value here */
330 if (!IsByteRange (Val)) {
331 ErrorSkip ("Range error");
338 /* Check if the alignment is a power of two */
339 Bit = BitFind (Align);
340 if (Align != (0x01L << Bit)) {
341 Error ("Alignment value must be a power of 2");
343 SegAlign (Bit, (int) Val);
349 static void DoASCIIZ (void)
350 /* Define text with a zero terminator */
355 /* Must have a string constant */
356 if (Tok != TOK_STRCON) {
357 ErrorSkip ("String constant expected");
361 /* Get the length of the string constant */
364 /* Translate into target charset and emit */
365 TgtTranslateBuf (SVal, Len);
366 EmitData ((unsigned char*) SVal, Len);
368 if (Tok == TOK_COMMA) {
379 static void DoAssert (void)
380 /* Add an assertion */
382 static const char* ActionTab [] = {
390 /* First we have the expression that has to evaluated */
391 ExprNode* Expr = Expression ();
395 if (Tok != TOK_IDENT) {
396 ErrorSkip ("Identifier expected");
399 Action = GetSubKey (ActionTab, sizeof (ActionTab) / sizeof (ActionTab[0]));
405 Action = ASSERT_ACT_WARN;
410 Action = ASSERT_ACT_ERROR;
414 Error ("Illegal assert action specifier");
418 /* We can have an optional message. If no message is present, use
419 * "Assertion failed".
421 if (Tok == TOK_COMMA) {
426 /* Read the message */
427 if (Tok != TOK_STRCON) {
428 ErrorSkip ("String constant expected");
432 /* Translate the message into a string id. We can then skip the input
435 Msg = GetStringId (SVal);
440 /* Use "Assertion failed" */
441 Msg = GetStringId ("Assertion failed");
445 /* Remember the assertion */
446 AddAssertion (Expr, Action, Msg);
451 static void DoAutoImport (void)
452 /* Mark unresolved symbols as imported */
454 SetBoolOption (&AutoImport);
459 static void DoBss (void)
460 /* Switch to the BSS segment */
467 static void DoByte (void)
471 if (Tok == TOK_STRCON) {
472 /* A string, translate into target charset and emit */
473 unsigned Len = strlen (SVal);
474 TgtTranslateBuf (SVal, Len);
475 EmitData ((unsigned char*) SVal, Len);
478 EmitByte (Expression ());
480 if (Tok != TOK_COMMA) {
484 /* Do smart handling of dangling comma */
485 if (Tok == TOK_SEP) {
486 Error ("Unexpected end of line");
495 static void DoCase (void)
496 /* Switch the IgnoreCase option */
498 SetBoolOption (&IgnoreCase);
499 IgnoreCase = !IgnoreCase;
504 static void DoCharMap (void)
505 /* Allow custome character mappings */
510 /* Read the index as numerical value */
511 Index = ConstExpression ();
512 if (Index < 0 || Index > 255) {
513 /* Value out of range */
514 ErrorSkip ("Range error");
521 /* Read the character code */
522 Code = ConstExpression ();
523 if (Code < 0 || Code > 255) {
524 /* Value out of range */
525 ErrorSkip ("Range error");
529 /* Set the character translation */
530 TgtTranslateSet ((unsigned) Index, (unsigned char) Code);
535 static void DoCode (void)
536 /* Switch to the code segment */
538 UseSeg (&CodeSegDef);
543 static void DoConDes (void)
544 /* Export a symbol as constructor/destructor */
546 static const char* Keys[] = {
551 char Name [sizeof (SVal)];
554 /* Symbol name follows */
555 if (Tok != TOK_IDENT) {
556 ErrorSkip ("Identifier expected");
562 /* Type follows. May be encoded as identifier or numerical */
564 if (Tok == TOK_IDENT) {
566 /* Map the following keyword to a number, then skip it */
567 Type = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
570 /* Check if we got a valid keyword */
572 Error ("Syntax error");
579 /* Read the type as numerical value */
580 Type = ConstExpression ();
581 if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) {
582 /* Value out of range */
583 Error ("Range error");
589 /* Parse the remainder of the line and export the symbol */
590 ConDes (Name, (unsigned) Type);
595 static void DoConstructor (void)
596 /* Export a symbol as constructor */
598 char Name [sizeof (SVal)];
600 /* Symbol name follows */
601 if (Tok != TOK_IDENT) {
602 ErrorSkip ("Identifier expected");
608 /* Parse the remainder of the line and export the symbol */
609 ConDes (Name, CD_TYPE_CON);
614 static void DoData (void)
615 /* Switch to the data segment */
617 UseSeg (&DataSegDef);
622 static void DoDbg (void)
623 /* Add debug information from high level code */
625 static const char* Keys[] = {
633 /* We expect a subkey */
634 if (Tok != TOK_IDENT) {
635 ErrorSkip ("Identifier expected");
639 /* Map the following keyword to a number */
640 Key = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
642 /* Skip the subkey */
645 /* Check the key and dispatch to a handler */
647 case 0: DbgInfoFile (); break;
648 case 1: DbgInfoLine (); break;
649 case 2: DbgInfoSym (); break;
650 default: ErrorSkip ("Syntax error"); break;
656 static void DoDByt (void)
657 /* Output double bytes */
660 EmitWord (GenSwapExpr (Expression ()));
661 if (Tok != TOK_COMMA) {
671 static void DoDebugInfo (void)
672 /* Switch debug info on or off */
674 SetBoolOption (&DbgSyms);
679 static void DoDefine (void)
680 /* Define a one line macro */
682 MacDef (MAC_STYLE_DEFINE);
687 static void DoDestructor (void)
688 /* Export a symbol as destructor */
690 char Name [sizeof (SVal)];
692 /* Symbol name follows */
693 if (Tok != TOK_IDENT) {
694 ErrorSkip ("Identifier expected");
700 /* Parse the remainder of the line and export the symbol */
701 ConDes (Name, CD_TYPE_DES);
706 static void DoDWord (void)
710 EmitDWord (Expression ());
711 if (Tok != TOK_COMMA) {
721 static void DoEnd (void)
722 /* End of assembly */
730 static void DoEndProc (void)
731 /* Leave a lexical level */
733 if (GetCurrentSymTabType () != ST_PROC) {
735 ErrorSkip ("No open .PROC");
743 static void DoEndScope (void)
744 /* Leave a lexical level */
746 if ( GetCurrentSymTabType () != ST_SCOPE) {
748 ErrorSkip ("No open .SCOPE");
756 static void DoError (void)
759 if (Tok != TOK_STRCON) {
760 ErrorSkip ("String constant expected");
762 Error ("User error: %s", SVal);
769 static void DoExitMacro (void)
770 /* Exit a macro expansion */
772 if (!InMacExpansion ()) {
773 /* We aren't expanding a macro currently */
782 static void DoExport (void)
783 /* Export a symbol */
785 ExportImport (SymExport, ADDR_SIZE_DEFAULT, SF_NONE);
790 static void DoExportZP (void)
791 /* Export a zeropage symbol */
793 ExportImport (SymExport, ADDR_SIZE_ZP, SF_NONE);
798 static void DoFarAddr (void)
799 /* Define far addresses (24 bit) */
802 EmitFarAddr (Expression ());
803 if (Tok != TOK_COMMA) {
813 static void DoFeature (void)
814 /* Switch the Feature option */
816 /* Allow a list of comma separated keywords */
819 /* We expect an identifier */
820 if (Tok != TOK_IDENT) {
821 ErrorSkip ("Identifier expected");
825 /* Make the string attribute lower case */
828 /* Set the feature and check for errors */
829 if (SetFeature (SVal) == FEAT_UNKNOWN) {
831 ErrorSkip ("Invalid feature: `%s'", SVal);
834 /* Skip the keyword */
838 /* Allow more than one keyword */
839 if (Tok == TOK_COMMA) {
849 static void DoFileOpt (void)
850 /* Insert a file option */
854 /* The option type may be given as a keyword or as a number. */
855 if (Tok == TOK_IDENT) {
857 /* Option given as keyword */
858 static const char* Keys [] = {
859 "AUTHOR", "COMMENT", "COMPILER"
862 /* Map the option to a number */
863 OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
866 ErrorSkip ("File option keyword expected");
870 /* Skip the keyword */
873 /* Must be followed by a comma */
876 /* We accept only string options for now */
877 if (Tok != TOK_STRCON) {
878 ErrorSkip ("String constant expected");
882 /* Insert the option */
901 Internal ("Invalid OptNum: %ld", OptNum);
910 /* Option given as number */
911 OptNum = ConstExpression ();
912 if (!IsByteRange (OptNum)) {
913 ErrorSkip ("Range error");
917 /* Must be followed by a comma */
920 /* We accept only string options for now */
921 if (Tok != TOK_STRCON) {
922 ErrorSkip ("String constant expected");
926 /* Insert the option */
927 OptStr ((unsigned char) OptNum, SVal);
936 static void DoForceImport (void)
937 /* Do a forced import on a symbol */
939 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_FORCED);
944 static void DoGlobal (void)
945 /* Declare a global symbol */
947 ExportImport (SymGlobal, ADDR_SIZE_DEFAULT, SF_NONE);
952 static void DoGlobalZP (void)
953 /* Declare a global zeropage symbol */
955 ExportImport (SymGlobal, ADDR_SIZE_ZP, SF_NONE);
960 static void DoI16 (void)
961 /* Switch the index registers to 16 bit mode (assembler only) */
963 if (GetCPU() != CPU_65816) {
964 Error ("Command is only valid in 65816 mode");
966 /* Immidiate mode has two extension bytes */
967 ExtBytes [AM65I_IMM_INDEX] = 2;
973 static void DoI8 (void)
974 /* Switch the index registers to 16 bit mode (assembler only) */
976 if (GetCPU() != CPU_65816) {
977 Error ("Command is only valid in 65816 mode");
979 /* Immidiate mode has one extension byte */
980 ExtBytes [AM65I_IMM_INDEX] = 1;
986 static void DoImport (void)
987 /* Import a symbol */
989 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_NONE);
994 static void DoImportZP (void)
995 /* Import a zero page symbol */
997 ExportImport (SymImport, ADDR_SIZE_ZP, SF_NONE);
1002 static void DoIncBin (void)
1003 /* Include a binary file */
1005 char Name [sizeof (SVal)];
1011 /* Name must follow */
1012 if (Tok != TOK_STRCON) {
1013 ErrorSkip ("String constant expected");
1016 strcpy (Name, SVal);
1019 /* A starting offset may follow */
1020 if (Tok == TOK_COMMA) {
1022 Start = ConstExpression ();
1024 /* And a length may follow */
1025 if (Tok == TOK_COMMA) {
1027 Count = ConstExpression ();
1032 /* Try to open the file */
1033 F = fopen (Name, "rb");
1036 /* Search for the file in the include directories. */
1037 char* PathName = FindInclude (Name);
1038 if (PathName == 0 || (F = fopen (PathName, "r")) == 0) {
1039 /* Not found or cannot open, print an error and bail out */
1040 ErrorSkip ("Cannot open include file `%s': %s", Name, strerror (errno));
1043 /* Free the allocated memory */
1046 /* If we had an error before, bail out now */
1052 /* Get the size of the file */
1053 fseek (F, 0, SEEK_END);
1056 /* If a count was not given, calculate it now */
1058 Count = Size - Start;
1060 /* Nothing to read - flag this as a range error */
1061 ErrorSkip ("Range error");
1065 /* Count was given, check if it is valid */
1066 if (Start + Count > Size) {
1067 ErrorSkip ("Range error");
1072 /* Seek to the start position */
1073 fseek (F, Start, SEEK_SET);
1075 /* Read chunks and insert them into the output */
1078 unsigned char Buf [1024];
1080 /* Calculate the number of bytes to read */
1081 size_t BytesToRead = (Count > (long)sizeof(Buf))? sizeof(Buf) : (size_t) Count;
1084 size_t BytesRead = fread (Buf, 1, BytesToRead, F);
1085 if (BytesToRead != BytesRead) {
1086 /* Some sort of error */
1087 ErrorSkip ("Cannot read from include file `%s': %s",
1088 Name, strerror (errno));
1092 /* Insert it into the output */
1093 EmitData (Buf, BytesRead);
1095 /* Keep the counters current */
1100 /* Close the file, ignore errors since it's r/o */
1106 static void DoInclude (void)
1107 /* Include another file */
1109 char Name [MAX_STR_LEN+1];
1111 /* Name must follow */
1112 if (Tok != TOK_STRCON) {
1113 ErrorSkip ("String constant expected");
1115 strcpy (Name, SVal);
1117 NewInputFile (Name);
1123 static void DoInterruptor (void)
1124 /* Export a symbol as interruptor */
1126 char Name [sizeof (SVal)];
1128 /* Symbol name follows */
1129 if (Tok != TOK_IDENT) {
1130 ErrorSkip ("Identifier expected");
1133 strcpy (Name, SVal);
1136 /* Parse the remainder of the line and export the symbol */
1137 ConDes (Name, CD_TYPE_INT);
1142 static void DoInvalid (void)
1143 /* Handle a token that is invalid here, since it should have been handled on
1144 * a much lower level of the expression hierarchy. Getting this sort of token
1145 * means that the lower level code has bugs.
1146 * This function differs to DoUnexpected in that the latter may be triggered
1147 * by the user by using keywords in the wrong location. DoUnexpected is not
1148 * an error in the assembler itself, while DoInvalid is.
1151 Internal ("Unexpected token: %s", Keyword);
1156 static void DoLineCont (void)
1157 /* Switch the use of line continuations */
1159 SetBoolOption (&LineCont);
1164 static void DoList (void)
1165 /* Enable/disable the listing */
1167 /* Get the setting */
1169 SetBoolOption (&List);
1171 /* Manage the counter */
1181 static void DoListBytes (void)
1182 /* Set maximum number of bytes to list for one line */
1184 SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
1189 static void DoLocalChar (void)
1190 /* Define the character that starts local labels */
1192 if (Tok != TOK_CHARCON) {
1193 ErrorSkip ("Character constant expected");
1195 if (IVal != '@' && IVal != '?') {
1196 Error ("Invalid start character for locals");
1198 LocalStart = (char) IVal;
1206 static void DoMacPack (void)
1207 /* Insert a macro package */
1211 /* We expect an identifier */
1212 if (Tok != TOK_IDENT) {
1213 ErrorSkip ("Identifier expected");
1217 /* Search for the macro package name */
1218 Package = MacPackFind (SVal);
1221 ErrorSkip ("Invalid macro package");
1225 /* Skip the package name */
1228 /* Insert the package */
1229 MacPackInsert (Package);
1234 static void DoMacro (void)
1235 /* Start a macro definition */
1237 MacDef (MAC_STYLE_CLASSIC);
1242 static void DoNull (void)
1243 /* Switch to the NULL segment */
1245 UseSeg (&NullSegDef);
1250 static void DoOrg (void)
1251 /* Start absolute code */
1253 long PC = ConstExpression ();
1254 if (PC < 0 || PC > 0xFFFFFF) {
1255 Error ("Range error");
1263 static void DoOut (void)
1264 /* Output a string */
1266 if (Tok != TOK_STRCON) {
1267 ErrorSkip ("String constant expected");
1269 /* Output the string and be sure to flush the output to keep it in
1270 * sync with any error messages if the output is redirected to a file.
1272 printf ("%s\n", SVal);
1280 static void DoP02 (void)
1281 /* Switch to 6502 CPU */
1288 static void DoPC02 (void)
1289 /* Switch to 65C02 CPU */
1296 static void DoP816 (void)
1297 /* Switch to 65816 CPU */
1304 static void DoPageLength (void)
1305 /* Set the page length for the listing */
1307 PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
1312 static void DoPopSeg (void)
1313 /* Pop an old segment from the segment stack */
1317 /* Must have a segment on the stack */
1318 if (CollCount (&SegStack) == 0) {
1319 ErrorSkip ("Segment stack is empty");
1323 /* Pop the last element */
1324 Def = CollPop (&SegStack);
1326 /* Restore this segment */
1329 /* Delete the segment definition */
1335 static void DoProc (void)
1336 /* Start a new lexical scope */
1338 char Name[sizeof(SVal)];
1339 unsigned char AddrSize;
1341 if (Tok == TOK_IDENT) {
1345 /* The new scope has a name. Remember it. */
1346 strcpy (Name, SVal);
1348 /* Search for the symbol, generate a new one if needed */
1349 Sym = SymFind (CurrentScope, Name, SYM_ALLOC_NEW);
1351 /* Skip the scope name */
1354 /* Read an optional address size specifier */
1355 AddrSize = OptionalAddrSize ();
1357 /* Mark the symbol as defined */
1358 SymDef (Sym, GenCurrentPC (), AddrSize, SF_LABEL);
1362 /* A .PROC statement without a name */
1363 Warning (1, "Unnamed .PROCs are deprecated, please use .SCOPE");
1364 AnonName (Name, sizeof (Name), "PROC");
1365 AddrSize = ADDR_SIZE_DEFAULT;
1369 /* Enter a new scope */
1370 SymEnterLevel (Name, ST_PROC, AddrSize);
1375 static void DoPSC02 (void)
1376 /* Switch to 65SC02 CPU */
1378 SetCPU (CPU_65SC02);
1383 static void DoPushSeg (void)
1384 /* Push the current segment onto the segment stack */
1386 /* Can only push a limited size of segments */
1387 if (CollCount (&SegStack) >= MAX_PUSHED_SEGMENTS) {
1388 ErrorSkip ("Segment stack overflow");
1392 /* Get the current segment and push it */
1393 CollAppend (&SegStack, DupSegDef (GetCurrentSegDef ()));
1398 static void DoReloc (void)
1399 /* Enter relocatable mode */
1406 static void DoRepeat (void)
1407 /* Repeat some instruction block */
1414 static void DoRes (void)
1415 /* Reserve some number of storage bytes */
1420 Count = ConstExpression ();
1421 if (Count > 0xFFFF || Count < 0) {
1422 ErrorSkip ("Range error");
1425 if (Tok == TOK_COMMA) {
1427 Val = ConstExpression ();
1428 /* We need a byte value here */
1429 if (!IsByteRange (Val)) {
1430 ErrorSkip ("Range error");
1434 /* Emit constant values */
1436 Emit0 ((unsigned char) Val);
1440 /* Emit fill fragments */
1447 static void DoROData (void)
1448 /* Switch to the r/o data segment */
1450 UseSeg (&RODataSegDef);
1455 static void DoScope (void)
1456 /* Start a local scope */
1458 char Name[sizeof (SVal)];
1459 unsigned char AddrSize;
1462 if (Tok == TOK_IDENT) {
1464 /* The new scope has a name. Remember and skip it. */
1465 strcpy (Name, SVal);
1470 /* An unnamed scope */
1471 AnonName (Name, sizeof (Name), "SCOPE");
1475 /* Read an optional address size specifier */
1476 AddrSize = OptionalAddrSize ();
1478 /* Enter the new scope */
1479 SymEnterLevel (Name, ST_SCOPE, AddrSize);
1485 static void DoSegment (void)
1486 /* Switch to another segment */
1488 char Name [sizeof (SVal)];
1492 if (Tok != TOK_STRCON) {
1493 ErrorSkip ("String constant expected");
1496 /* Save the name of the segment and skip it */
1497 strcpy (Name, SVal);
1500 /* Check for an optional address size modifier */
1501 Def.AddrSize = OptionalAddrSize ();
1503 /* Set the segment */
1510 static void DoSetCPU (void)
1511 /* Switch the CPU instruction set */
1513 /* We expect an identifier */
1514 if (Tok != TOK_STRCON) {
1515 ErrorSkip ("String constant expected");
1517 /* Try to find the CPU */
1518 cpu_t CPU = FindCPU (SVal);
1520 /* Switch to the new CPU */
1523 /* Skip the identifier. If the CPU switch was successful, the scanner
1524 * will treat the input now correctly for the new CPU.
1532 static void DoSmart (void)
1533 /* Smart mode on/off */
1535 SetBoolOption (&SmartMode);
1540 static void DoSunPlus (void)
1541 /* Switch to the SUNPLUS CPU */
1543 SetCPU (CPU_SUNPLUS);
1548 static void DoTag (void)
1549 /* Allocate space for a struct */
1554 /* Read the struct name */
1555 SymTable* Struct = ParseScopedSymTable ();
1557 /* Check the supposed struct */
1559 ErrorSkip ("Unknown struct");
1562 if (GetSymTabType (Struct) != ST_STRUCT) {
1563 ErrorSkip ("Not a struct");
1567 /* Get the symbol that defines the size of the struct */
1568 SizeSym = GetSizeOfScope (Struct);
1570 /* Check if it does exist and if its value is known */
1571 if (SizeSym == 0 || !SymIsConst (SizeSym, &Size)) {
1572 ErrorSkip ("Size of struct/union is unknown");
1576 /* Optional multiplicator may follow */
1577 if (Tok == TOK_COMMA) {
1580 Multiplicator = ConstExpression ();
1581 /* Multiplicator must make sense */
1582 if (Multiplicator <= 0) {
1583 ErrorSkip ("Range error");
1586 Size *= Multiplicator;
1589 /* Emit fill fragments */
1595 static void DoUnexpected (void)
1596 /* Got an unexpected keyword */
1598 Error ("Unexpected `%s'", Keyword);
1604 static void DoWarning (void)
1607 if (Tok != TOK_STRCON) {
1608 ErrorSkip ("String constant expected");
1610 Warning (0, "User warning: %s", SVal);
1617 static void DoWord (void)
1621 EmitWord (Expression ());
1622 if (Tok != TOK_COMMA) {
1632 static void DoZeropage (void)
1633 /* Switch to the zeropage segment */
1635 UseSeg (&ZeropageSegDef);
1640 /*****************************************************************************/
1642 /*****************************************************************************/
1646 /* Control commands flags */
1648 ccNone = 0x0000, /* No special flags */
1649 ccKeepToken = 0x0001 /* Do not skip the current token */
1652 /* Control command table */
1653 typedef struct CtrlDesc CtrlDesc;
1655 unsigned Flags; /* Flags for this directive */
1656 void (*Handler) (void); /* Command handler */
1659 #define PSEUDO_COUNT (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1660 static CtrlDesc CtrlCmdTab [] = {
1663 { ccNone, DoAddr }, /* .ADDR */
1664 { ccNone, DoAlign },
1665 { ccNone, DoASCIIZ },
1666 { ccNone, DoAssert },
1667 { ccNone, DoAutoImport },
1668 { ccNone, DoUnexpected }, /* .BANKBYTE */
1669 { ccNone, DoUnexpected }, /* .BLANK */
1673 { ccNone, DoCharMap },
1675 { ccNone, DoUnexpected, }, /* .CONCAT */
1676 { ccNone, DoConDes },
1677 { ccNone, DoUnexpected }, /* .CONST */
1678 { ccNone, DoConstructor },
1679 { ccNone, DoUnexpected }, /* .CPU */
1683 { ccNone, DoDebugInfo },
1684 { ccNone, DoDefine },
1685 { ccNone, DoUnexpected }, /* .DEFINED */
1686 { ccNone, DoDestructor },
1687 { ccNone, DoDWord },
1688 { ccKeepToken, DoConditionals }, /* .ELSE */
1689 { ccKeepToken, DoConditionals }, /* .ELSEIF */
1690 { ccKeepToken, DoEnd },
1691 { ccNone, DoUnexpected }, /* .ENDENUM */
1692 { ccKeepToken, DoConditionals }, /* .ENDIF */
1693 { ccNone, DoUnexpected }, /* .ENDMACRO */
1694 { ccNone, DoEndProc },
1695 { ccNone, DoUnexpected }, /* .ENDREPEAT */
1696 { ccNone, DoEndScope },
1697 { ccNone, DoUnexpected }, /* .ENDSTRUCT */
1698 { ccNone, DoUnexpected }, /* .ENDUNION */
1700 { ccNone, DoError },
1701 { ccNone, DoExitMacro },
1702 { ccNone, DoExport },
1703 { ccNone, DoExportZP },
1704 { ccNone, DoFarAddr },
1705 { ccNone, DoFeature },
1706 { ccNone, DoFileOpt },
1707 { ccNone, DoForceImport },
1708 { ccNone, DoUnexpected }, /* .FORCEWORD */
1709 { ccNone, DoGlobal },
1710 { ccNone, DoGlobalZP },
1711 { ccNone, DoUnexpected }, /* .HIBYTE */
1712 { ccNone, DoUnexpected }, /* .HIWORD */
1715 { ccNone, DoUnexpected }, /* .IDENT */
1716 { ccKeepToken, DoConditionals }, /* .IF */
1717 { ccKeepToken, DoConditionals }, /* .IFBLANK */
1718 { ccKeepToken, DoConditionals }, /* .IFCONST */
1719 { ccKeepToken, DoConditionals }, /* .IFDEF */
1720 { ccKeepToken, DoConditionals }, /* .IFNBLANK */
1721 { ccKeepToken, DoConditionals }, /* .IFNCONST */
1722 { ccKeepToken, DoConditionals }, /* .IFNDEF */
1723 { ccKeepToken, DoConditionals }, /* .IFNREF */
1724 { ccKeepToken, DoConditionals }, /* .IFP02 */
1725 { ccKeepToken, DoConditionals }, /* .IFP816 */
1726 { ccKeepToken, DoConditionals }, /* .IFPC02 */
1727 { ccKeepToken, DoConditionals }, /* .IFPSC02 */
1728 { ccKeepToken, DoConditionals }, /* .IFREF */
1729 { ccNone, DoImport },
1730 { ccNone, DoImportZP },
1731 { ccNone, DoIncBin },
1732 { ccNone, DoInclude },
1733 { ccNone, DoInterruptor },
1734 { ccNone, DoInvalid }, /* .LEFT */
1735 { ccNone, DoLineCont },
1737 { ccNone, DoListBytes },
1738 { ccNone, DoUnexpected }, /* .LOBYTE */
1739 { ccNone, DoUnexpected }, /* .LOCAL */
1740 { ccNone, DoLocalChar },
1741 { ccNone, DoUnexpected }, /* .LOWORD */
1742 { ccNone, DoMacPack },
1743 { ccNone, DoMacro },
1744 { ccNone, DoUnexpected }, /* .MATCH */
1745 { ccNone, DoInvalid }, /* .MID */
1751 { ccNone, DoPageLength },
1752 { ccNone, DoUnexpected }, /* .PARAMCOUNT */
1754 { ccNone, DoPopSeg },
1756 { ccNone, DoPSC02 },
1757 { ccNone, DoPushSeg },
1758 { ccNone, DoUnexpected }, /* .REFERENCED */
1759 { ccNone, DoReloc },
1760 { ccNone, DoRepeat },
1762 { ccNone, DoInvalid }, /* .RIGHT */
1763 { ccNone, DoROData },
1764 { ccNone, DoScope },
1765 { ccNone, DoSegment },
1766 { ccNone, DoUnexpected }, /* .SET */
1767 { ccNone, DoSetCPU },
1768 { ccNone, DoUnexpected }, /* .SIZEOF */
1769 { ccNone, DoSmart },
1770 { ccNone, DoUnexpected }, /* .SPRINTF */
1771 { ccNone, DoUnexpected }, /* .STRAT */
1772 { ccNone, DoUnexpected }, /* .STRING */
1773 { ccNone, DoUnexpected }, /* .STRLEN */
1774 { ccNone, DoStruct },
1775 { ccNone, DoSunPlus },
1777 { ccNone, DoUnexpected }, /* .TCOUNT */
1778 { ccNone, DoUnexpected }, /* .TIME */
1779 { ccNone, DoUnion },
1780 { ccNone, DoUnexpected }, /* .VERSION */
1781 { ccNone, DoWarning },
1783 { ccNone, DoUnexpected }, /* .XMATCH */
1784 { ccNone, DoZeropage },
1789 /*****************************************************************************/
1791 /*****************************************************************************/
1795 void HandlePseudo (void)
1796 /* Handle a pseudo instruction */
1800 /* Calculate the index into the table */
1801 unsigned Index = Tok - TOK_FIRSTPSEUDO;
1804 if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
1805 Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
1806 (unsigned) PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
1808 CHECK (Index < PSEUDO_COUNT);
1810 /* Get the pseudo intruction descriptor */
1811 D = &CtrlCmdTab [Index];
1813 /* Remember the instruction, then skip it if needed */
1814 if ((D->Flags & ccKeepToken) == 0) {
1815 strcpy (Keyword, SVal);
1819 /* Call the handler */
1825 void SegStackCheck (void)
1826 /* Check if the segment stack is empty at end of assembly */
1828 if (CollCount (&SegStack) != 0) {
1829 Error ("Segment stack is not empty");