1 /*****************************************************************************/
5 /* Pseudo instructions for the ca65 macroassembler */
9 /* (C) 1998-2003 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"
79 /*****************************************************************************/
81 /*****************************************************************************/
85 /* Keyword we're about to handle */
86 static char Keyword [sizeof (SVal)+1];
89 #define MAX_PUSHED_SEGMENTS 16
90 static Collection SegStack = STATIC_COLLECTION_INITIALIZER;
94 /*****************************************************************************/
96 /*****************************************************************************/
100 static void DoUnexpected (void);
101 /* Got an unexpected keyword */
103 static void DoInvalid (void);
104 /* Handle a token that is invalid here, since it should have been handled on
105 * a much lower level of the expression hierarchy. Getting this sort of token
106 * means that the lower level code has bugs.
107 * This function differs to DoUnexpected in that the latter may be triggered
108 * by the user by using keywords in the wrong location. DoUnexpected is not
109 * an error in the assembler itself, while DoInvalid is.
114 /*****************************************************************************/
115 /* Helper functions */
116 /*****************************************************************************/
120 static unsigned char OptionalAddrSize (void)
121 /* If a colon follows, parse an optional address size spec and return it.
122 * Otherwise return ADDR_SIZE_DEFAULT.
125 unsigned AddrSize = ADDR_SIZE_DEFAULT;
126 if (Tok == TOK_COLON) {
128 AddrSize = ParseAddrSize ();
136 static void SetBoolOption (unsigned char* Flag)
137 /* Read a on/off/+/- option and set flag accordingly */
139 static const char* Keys[] = {
144 if (Tok == TOK_PLUS) {
147 } else if (Tok == TOK_MINUS) {
150 } else if (Tok == TOK_IDENT) {
151 /* Map the keyword to a number */
152 switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
153 case 0: *Flag = 0; NextTok (); break;
154 case 1: *Flag = 1; NextTok (); break;
155 default: ErrorSkip ("`on' or `off' expected"); break;
157 } else if (TokIsSep (Tok)) {
158 /* Without anything assume switch on */
161 ErrorSkip ("`on' or `off' expected");
167 static void ExportImport (void (*Func) (SymEntry*, unsigned char, unsigned),
168 unsigned char DefAddrSize, unsigned Flags)
169 /* Export or import symbols */
172 unsigned char AddrSize;
176 /* We need an identifier here */
177 if (Tok != TOK_IDENT) {
178 ErrorSkip ("Identifier expected");
182 /* Find the symbol table entry, allocate a new one if necessary */
183 Sym = SymFind (CurrentScope, SVal, SYM_ALLOC_NEW);
188 /* Get an optional address size */
189 AddrSize = OptionalAddrSize ();
190 if (AddrSize == ADDR_SIZE_DEFAULT) {
191 AddrSize = DefAddrSize;
194 /* Call the actual import/export function */
195 Func (Sym, AddrSize, Flags);
198 if (Tok == TOK_COMMA) {
208 static long IntArg (long Min, long Max)
209 /* Read an integer argument and check a range. Accept the token "unlimited"
210 * and return -1 in this case.
213 if (Tok == TOK_IDENT && strcmp (SVal, "unlimited") == 0) {
217 long Val = ConstExpression ();
218 if (Val < Min || Val > Max) {
219 Error ("Range error");
228 static void ConDes (const char* Name, unsigned Type)
229 /* Parse remaining line for constructor/destructor of the remaining type */
234 /* Find the symbol table entry, allocate a new one if necessary */
235 SymEntry* Sym = SymFind (CurrentScope, Name, SYM_ALLOC_NEW);
237 /* Optional constructor priority */
238 if (Tok == TOK_COMMA) {
239 /* Priority value follows */
241 Prio = ConstExpression ();
242 if (Prio < CD_PRIO_MIN || Prio > CD_PRIO_MAX) {
243 /* Value out of range */
244 Error ("Range error");
248 /* Use the default priority value */
252 /* Define the symbol */
253 SymConDes (Sym, ADDR_SIZE_DEFAULT, Type, (unsigned) Prio);
258 /*****************************************************************************/
259 /* Handler functions */
260 /*****************************************************************************/
264 static void DoA16 (void)
265 /* Switch the accu to 16 bit mode (assembler only) */
267 if (GetCPU() != CPU_65816) {
268 Error ("Command is only valid in 65816 mode");
270 /* Immidiate mode has two extension bytes */
271 ExtBytes [AMI_IMM_ACCU] = 2;
277 static void DoA8 (void)
278 /* Switch the accu to 8 bit mode (assembler only) */
280 if (GetCPU() != CPU_65816) {
281 Error ("Command is only valid in 65816 mode");
283 /* Immidiate mode has one extension byte */
284 ExtBytes [AMI_IMM_ACCU] = 1;
290 static void DoAddr (void)
291 /* Define addresses */
294 if (GetCPU() == CPU_65816) {
295 EmitWord (GenWordExpr (Expression ()));
297 /* Do a range check */
298 EmitWord (Expression ());
300 if (Tok != TOK_COMMA) {
310 static void DoAlign (void)
311 /* Align the PC to some boundary */
317 /* Read the alignment value */
318 Align = ConstExpression ();
319 if (Align <= 0 || Align > 0x10000) {
320 ErrorSkip ("Range error");
324 /* Optional value follows */
325 if (Tok == TOK_COMMA) {
327 Val = ConstExpression ();
328 /* We need a byte value here */
329 if (!IsByteRange (Val)) {
330 ErrorSkip ("Range error");
337 /* Check if the alignment is a power of two */
338 Bit = BitFind (Align);
339 if (Align != (0x01L << Bit)) {
340 Error ("Alignment value must be a power of 2");
342 SegAlign (Bit, (int) Val);
348 static void DoASCIIZ (void)
349 /* Define text with a zero terminator */
354 /* Must have a string constant */
355 if (Tok != TOK_STRCON) {
356 ErrorSkip ("String constant expected");
360 /* Get the length of the string constant */
363 /* Translate into target charset and emit */
364 TgtTranslateBuf (SVal, Len);
365 EmitData ((unsigned char*) SVal, Len);
367 if (Tok == TOK_COMMA) {
378 static void DoAssert (void)
379 /* Add an assertion */
381 static const char* ActionTab [] = {
389 /* First we have the expression that has to evaluated */
390 ExprNode* Expr = Expression ();
394 if (Tok != TOK_IDENT) {
395 ErrorSkip ("Identifier expected");
398 Action = GetSubKey (ActionTab, sizeof (ActionTab) / sizeof (ActionTab[0]));
404 Action = ASSERT_ACT_WARN;
409 Action = ASSERT_ACT_ERROR;
413 Error ("Illegal assert action specifier");
418 /* Read the message */
419 if (Tok != TOK_STRCON) {
420 ErrorSkip ("String constant expected");
422 AddAssertion (Expr, Action, GetStringId (SVal));
429 static void DoAutoImport (void)
430 /* Mark unresolved symbols as imported */
432 SetBoolOption (&AutoImport);
437 static void DoBss (void)
438 /* Switch to the BSS segment */
445 static void DoByte (void)
449 if (Tok == TOK_STRCON) {
450 /* A string, translate into target charset and emit */
451 unsigned Len = strlen (SVal);
452 TgtTranslateBuf (SVal, Len);
453 EmitData ((unsigned char*) SVal, Len);
456 EmitByte (Expression ());
458 if (Tok != TOK_COMMA) {
462 /* Do smart handling of dangling comma */
463 if (Tok == TOK_SEP) {
464 Error ("Unexpected end of line");
473 static void DoCase (void)
474 /* Switch the IgnoreCase option */
476 SetBoolOption (&IgnoreCase);
477 IgnoreCase = !IgnoreCase;
482 static void DoCharMap (void)
483 /* Allow custome character mappings */
488 /* Read the index as numerical value */
489 Index = ConstExpression ();
490 if (Index < 1 || Index > 255) {
491 /* Value out of range */
492 ErrorSkip ("Range error");
499 /* Read the character code */
500 Code = ConstExpression ();
501 if (Code < 1 || Code > 255) {
502 /* Value out of range */
503 ErrorSkip ("Range error");
507 /* Set the character translation */
508 TgtTranslateSet ((unsigned) Index, (unsigned char) Code);
513 static void DoCode (void)
514 /* Switch to the code segment */
516 UseSeg (&CodeSegDef);
521 static void DoConDes (void)
522 /* Export a symbol as constructor/destructor */
524 static const char* Keys[] = {
528 char Name [sizeof (SVal)];
531 /* Symbol name follows */
532 if (Tok != TOK_IDENT) {
533 ErrorSkip ("Identifier expected");
539 /* Type follows. May be encoded as identifier or numerical */
541 if (Tok == TOK_IDENT) {
543 /* Map the following keyword to a number, then skip it */
544 Type = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
547 /* Check if we got a valid keyword */
549 Error ("Syntax error");
556 /* Read the type as numerical value */
557 Type = ConstExpression ();
558 if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) {
559 /* Value out of range */
560 Error ("Range error");
566 /* Parse the remainder of the line and export the symbol */
567 ConDes (Name, (unsigned) Type);
572 static void DoConstructor (void)
573 /* Export a symbol as constructor */
575 char Name [sizeof (SVal)];
577 /* Symbol name follows */
578 if (Tok != TOK_IDENT) {
579 ErrorSkip ("Identifier expected");
585 /* Parse the remainder of the line and export the symbol */
586 ConDes (Name, CD_TYPE_CON);
591 static void DoData (void)
592 /* Switch to the data segment */
594 UseSeg (&DataSegDef);
599 static void DoDbg (void)
600 /* Add debug information from high level code */
602 static const char* Keys[] = {
610 /* We expect a subkey */
611 if (Tok != TOK_IDENT) {
612 ErrorSkip ("Identifier expected");
616 /* Map the following keyword to a number */
617 Key = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
619 /* Skip the subkey */
622 /* Check the key and dispatch to a handler */
624 case 0: DbgInfoFile (); break;
625 case 1: DbgInfoLine (); break;
626 case 2: DbgInfoSym (); break;
627 default: ErrorSkip ("Syntax error"); break;
633 static void DoDByt (void)
634 /* Output double bytes */
637 EmitWord (GenSwapExpr (Expression ()));
638 if (Tok != TOK_COMMA) {
648 static void DoDebugInfo (void)
649 /* Switch debug info on or off */
651 SetBoolOption (&DbgSyms);
656 static void DoDefine (void)
657 /* Define a one line macro */
659 MacDef (MAC_STYLE_DEFINE);
664 static void DoDestructor (void)
665 /* Export a symbol as destructor */
667 char Name [sizeof (SVal)];
669 /* Symbol name follows */
670 if (Tok != TOK_IDENT) {
671 ErrorSkip ("Identifier expected");
677 /* Parse the remainder of the line and export the symbol */
678 ConDes (Name, CD_TYPE_DES);
683 static void DoDWord (void)
687 EmitDWord (Expression ());
688 if (Tok != TOK_COMMA) {
698 static void DoEnd (void)
699 /* End of assembly */
707 static void DoEndProc (void)
708 /* Leave a lexical level */
710 if (CurrentScope == RootScope || GetCurrentSymTabType () != ST_PROC) {
712 ErrorSkip ("No open .PROC");
720 static void DoEndScope (void)
721 /* Leave a lexical level */
723 if (CurrentScope == RootScope || GetCurrentSymTabType () != ST_SCOPE) {
725 ErrorSkip ("No open .SCOPE");
733 static void DoError (void)
736 if (Tok != TOK_STRCON) {
737 ErrorSkip ("String constant expected");
739 Error ("User error: %s", SVal);
746 static void DoExitMacro (void)
747 /* Exit a macro expansion */
749 if (!InMacExpansion ()) {
750 /* We aren't expanding a macro currently */
759 static void DoExport (void)
760 /* Export a symbol */
762 ExportImport (SymExport, ADDR_SIZE_DEFAULT, SF_NONE);
767 static void DoExportZP (void)
768 /* Export a zeropage symbol */
770 ExportImport (SymExport, ADDR_SIZE_ZP, SF_NONE);
775 static void DoFarAddr (void)
776 /* Define far addresses (24 bit) */
779 EmitFarAddr (Expression ());
780 if (Tok != TOK_COMMA) {
790 static void DoFeature (void)
791 /* Switch the Feature option */
793 /* Allow a list of comma separated keywords */
796 /* We expect an identifier */
797 if (Tok != TOK_IDENT) {
798 ErrorSkip ("Identifier expected");
802 /* Make the string attribute lower case */
805 /* Set the feature and check for errors */
806 if (SetFeature (SVal) == FEAT_UNKNOWN) {
808 ErrorSkip ("Invalid feature: `%s'", SVal);
811 /* Skip the keyword */
815 /* Allow more than one keyword */
816 if (Tok == TOK_COMMA) {
826 static void DoFileOpt (void)
827 /* Insert a file option */
831 /* The option type may be given as a keyword or as a number. */
832 if (Tok == TOK_IDENT) {
834 /* Option given as keyword */
835 static const char* Keys [] = {
836 "AUTHOR", "COMMENT", "COMPILER"
839 /* Map the option to a number */
840 OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
843 ErrorSkip ("File option keyword expected");
847 /* Skip the keyword */
850 /* Must be followed by a comma */
853 /* We accept only string options for now */
854 if (Tok != TOK_STRCON) {
855 ErrorSkip ("String constant expected");
859 /* Insert the option */
878 Internal ("Invalid OptNum: %ld", OptNum);
887 /* Option given as number */
888 OptNum = ConstExpression ();
889 if (!IsByteRange (OptNum)) {
890 ErrorSkip ("Range error");
894 /* Must be followed by a comma */
897 /* We accept only string options for now */
898 if (Tok != TOK_STRCON) {
899 ErrorSkip ("String constant expected");
903 /* Insert the option */
904 OptStr ((unsigned char) OptNum, SVal);
913 static void DoForceImport (void)
914 /* Do a forced import on a symbol */
916 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_FORCED);
921 static void DoGlobal (void)
922 /* Declare a global symbol */
924 ExportImport (SymGlobal, ADDR_SIZE_DEFAULT, SF_NONE);
929 static void DoGlobalZP (void)
930 /* Declare a global zeropage symbol */
932 ExportImport (SymGlobal, ADDR_SIZE_ZP, SF_NONE);
937 static void DoI16 (void)
938 /* Switch the index registers to 16 bit mode (assembler only) */
940 if (GetCPU() != CPU_65816) {
941 Error ("Command is only valid in 65816 mode");
943 /* Immidiate mode has two extension bytes */
944 ExtBytes [AMI_IMM_INDEX] = 2;
950 static void DoI8 (void)
951 /* Switch the index registers to 16 bit mode (assembler only) */
953 if (GetCPU() != CPU_65816) {
954 Error ("Command is only valid in 65816 mode");
956 /* Immidiate mode has one extension byte */
957 ExtBytes [AMI_IMM_INDEX] = 1;
963 static void DoImport (void)
964 /* Import a symbol */
966 ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_NONE);
971 static void DoImportZP (void)
972 /* Import a zero page symbol */
974 ExportImport (SymImport, ADDR_SIZE_ZP, SF_NONE);
979 static void DoIncBin (void)
980 /* Include a binary file */
982 char Name [sizeof (SVal)];
988 /* Name must follow */
989 if (Tok != TOK_STRCON) {
990 ErrorSkip ("String constant expected");
996 /* A starting offset may follow */
997 if (Tok == TOK_COMMA) {
999 Start = ConstExpression ();
1001 /* And a length may follow */
1002 if (Tok == TOK_COMMA) {
1004 Count = ConstExpression ();
1009 /* Try to open the file */
1010 F = fopen (Name, "rb");
1013 /* Search for the file in the include directories. */
1014 char* PathName = FindInclude (Name);
1015 if (PathName == 0 || (F = fopen (PathName, "r")) == 0) {
1016 /* Not found or cannot open, print an error and bail out */
1017 ErrorSkip ("Cannot open include file `%s': %s", Name, strerror (errno));
1020 /* Free the allocated memory */
1023 /* If we had an error before, bail out now */
1029 /* Get the size of the file */
1030 fseek (F, 0, SEEK_END);
1033 /* If a count was not given, calculate it now */
1035 Count = Size - Start;
1037 /* Nothing to read - flag this as a range error */
1038 ErrorSkip ("Range error");
1042 /* Count was given, check if it is valid */
1043 if (Start + Count > Size) {
1044 ErrorSkip ("Range error");
1049 /* Seek to the start position */
1050 fseek (F, Start, SEEK_SET);
1052 /* Read chunks and insert them into the output */
1055 unsigned char Buf [1024];
1057 /* Calculate the number of bytes to read */
1058 size_t BytesToRead = (Count > (long)sizeof(Buf))? sizeof(Buf) : (size_t) Count;
1061 size_t BytesRead = fread (Buf, 1, BytesToRead, F);
1062 if (BytesToRead != BytesRead) {
1063 /* Some sort of error */
1064 ErrorSkip ("Cannot read from include file `%s': %s",
1065 Name, strerror (errno));
1069 /* Insert it into the output */
1070 EmitData (Buf, BytesRead);
1072 /* Keep the counters current */
1077 /* Close the file, ignore errors since it's r/o */
1083 static void DoInclude (void)
1084 /* Include another file */
1086 char Name [MAX_STR_LEN+1];
1088 /* Name must follow */
1089 if (Tok != TOK_STRCON) {
1090 ErrorSkip ("String constant expected");
1092 strcpy (Name, SVal);
1094 NewInputFile (Name);
1100 static void DoInvalid (void)
1101 /* Handle a token that is invalid here, since it should have been handled on
1102 * a much lower level of the expression hierarchy. Getting this sort of token
1103 * means that the lower level code has bugs.
1104 * This function differs to DoUnexpected in that the latter may be triggered
1105 * by the user by using keywords in the wrong location. DoUnexpected is not
1106 * an error in the assembler itself, while DoInvalid is.
1109 Internal ("Unexpected token: %s", Keyword);
1114 static void DoLineCont (void)
1115 /* Switch the use of line continuations */
1117 SetBoolOption (&LineCont);
1122 static void DoList (void)
1123 /* Enable/disable the listing */
1125 /* Get the setting */
1127 SetBoolOption (&List);
1129 /* Manage the counter */
1139 static void DoListBytes (void)
1140 /* Set maximum number of bytes to list for one line */
1142 SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
1147 static void DoLocalChar (void)
1148 /* Define the character that starts local labels */
1150 if (Tok != TOK_CHARCON) {
1151 ErrorSkip ("Character constant expected");
1153 if (IVal != '@' && IVal != '?') {
1154 Error ("Invalid start character for locals");
1156 LocalStart = (char) IVal;
1164 static void DoMacPack (void)
1165 /* Insert a macro package */
1167 /* Macro package names */
1168 static const char* Keys [] = {
1177 /* We expect an identifier */
1178 if (Tok != TOK_IDENT) {
1179 ErrorSkip ("Identifier expected");
1183 /* Map the keyword to a number */
1184 Package = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
1187 ErrorSkip ("Invalid macro package");
1191 /* Skip the package name */
1194 /* Insert the package */
1195 InsertMacPack (Package);
1200 static void DoMacro (void)
1201 /* Start a macro definition */
1203 MacDef (MAC_STYLE_CLASSIC);
1208 static void DoNull (void)
1209 /* Switch to the NULL segment */
1211 UseSeg (&NullSegDef);
1216 static void DoOrg (void)
1217 /* Start absolute code */
1219 long PC = ConstExpression ();
1220 if (PC < 0 || PC > 0xFFFFFF) {
1221 Error ("Range error");
1229 static void DoOut (void)
1230 /* Output a string */
1232 if (Tok != TOK_STRCON) {
1233 ErrorSkip ("String constant expected");
1235 /* Output the string and be sure to flush the output to keep it in
1236 * sync with any error messages if the output is redirected to a file.
1238 printf ("%s\n", SVal);
1246 static void DoP02 (void)
1247 /* Switch to 6502 CPU */
1254 static void DoPC02 (void)
1255 /* Switch to 65C02 CPU */
1262 static void DoP816 (void)
1263 /* Switch to 65816 CPU */
1270 static void DoPageLength (void)
1271 /* Set the page length for the listing */
1273 PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
1278 static void DoPopSeg (void)
1279 /* Pop an old segment from the segment stack */
1283 /* Must have a segment on the stack */
1284 if (CollCount (&SegStack) == 0) {
1285 ErrorSkip ("Segment stack is empty");
1289 /* Pop the last element */
1290 Def = CollPop (&SegStack);
1292 /* Restore this segment */
1295 /* Delete the segment definition */
1301 static void DoProc (void)
1302 /* Start a new lexical scope */
1304 char Name[sizeof(SVal)];
1305 unsigned char AddrSize;
1307 if (Tok == TOK_IDENT) {
1309 /* The new scope has a name. Remember it. */
1310 strcpy (Name, SVal);
1312 /* Search for the symbol, generate a new one if needed */
1313 SymEntry* Sym = SymFind (CurrentScope, Name, SYM_ALLOC_NEW);
1315 /* Skip the scope name */
1318 /* Read an optional address size specifier */
1319 AddrSize = OptionalAddrSize ();
1321 /* Mark the symbol as defined */
1322 SymDef (Sym, GenCurrentPC (), AddrSize, SF_LABEL);
1326 /* A .PROC statement without a name */
1327 Warning (1, "Unnamed .PROCs are deprecated, please use .SCOPE");
1328 AnonName (Name, sizeof (Name), "PROC");
1329 AddrSize = ADDR_SIZE_DEFAULT;
1333 /* Enter a new scope */
1334 SymEnterLevel (Name, ST_PROC, AddrSize);
1339 static void DoPSC02 (void)
1340 /* Switch to 65SC02 CPU */
1342 SetCPU (CPU_65SC02);
1347 static void DoPushSeg (void)
1348 /* Push the current segment onto the segment stack */
1350 /* Can only push a limited size of segments */
1351 if (CollCount (&SegStack) >= MAX_PUSHED_SEGMENTS) {
1352 ErrorSkip ("Segment stack overflow");
1356 /* Get the current segment and push it */
1357 CollAppend (&SegStack, DupSegDef (GetCurrentSegDef ()));
1362 static void DoReloc (void)
1363 /* Enter relocatable mode */
1370 static void DoRepeat (void)
1371 /* Repeat some instruction block */
1378 static void DoRes (void)
1379 /* Reserve some number of storage bytes */
1384 Count = ConstExpression ();
1385 if (Count > 0xFFFF || Count < 0) {
1386 ErrorSkip ("Range error");
1389 if (Tok == TOK_COMMA) {
1391 Val = ConstExpression ();
1392 /* We need a byte value here */
1393 if (!IsByteRange (Val)) {
1394 ErrorSkip ("Range error");
1398 /* Emit constant values */
1400 Emit0 ((unsigned char) Val);
1404 /* Emit fill fragments */
1411 static void DoROData (void)
1412 /* Switch to the r/o data segment */
1414 UseSeg (&RODataSegDef);
1419 static void DoScope (void)
1420 /* Start a local scope */
1422 char Name[sizeof (SVal)];
1423 unsigned char AddrSize;
1426 if (Tok == TOK_IDENT) {
1428 /* The new scope has a name. Remember and skip it. */
1429 strcpy (Name, SVal);
1434 /* An unnamed scope */
1435 AnonName (Name, sizeof (Name), "SCOPE");
1439 /* Read an optional address size specifier */
1440 AddrSize = OptionalAddrSize ();
1442 /* Enter the new scope */
1443 SymEnterLevel (Name, ST_SCOPE, AddrSize);
1449 static void DoSegment (void)
1450 /* Switch to another segment */
1452 char Name [sizeof (SVal)];
1456 if (Tok != TOK_STRCON) {
1457 ErrorSkip ("String constant expected");
1460 /* Save the name of the segment and skip it */
1461 strcpy (Name, SVal);
1464 /* Check for an optional address size modifier */
1465 Def.AddrSize = OptionalAddrSize ();
1467 /* Set the segment */
1474 static void DoSetCPU (void)
1475 /* Switch the CPU instruction set */
1477 /* We expect an identifier */
1478 if (Tok != TOK_STRCON) {
1479 ErrorSkip ("String constant expected");
1481 /* Try to find the CPU, then skip the identifier */
1482 cpu_t CPU = FindCPU (SVal);
1485 /* Switch to the new CPU */
1492 static void DoSmart (void)
1493 /* Smart mode on/off */
1495 SetBoolOption (&SmartMode);
1500 static void DoSunPlus (void)
1501 /* Switch to the SUNPLUS CPU */
1503 SetCPU (CPU_SUNPLUS);
1508 static void DoTag (void)
1509 /* Allocate space for a struct */
1513 /* Read the struct name */
1514 SymTable* Struct = ParseScopedSymTable (SYM_FIND_EXISTING);
1516 /* Check the supposed struct */
1518 ErrorSkip ("Unknown struct");
1521 if (GetSymTabType (Struct) != ST_STRUCT) {
1522 ErrorSkip ("Not a struct");
1526 /* Get the size of the struct */
1527 Size = GetSymVal (SymFind (Struct, ".size", SYM_FIND_EXISTING));
1529 /* Optional multiplicator may follow */
1530 if (Tok == TOK_COMMA) {
1533 Multiplicator = ConstExpression ();
1534 /* Multiplicator must make sense */
1535 if (Multiplicator <= 0) {
1536 ErrorSkip ("Range error");
1539 Size *= Multiplicator;
1542 /* Emit fill fragments */
1548 static void DoUnexpected (void)
1549 /* Got an unexpected keyword */
1551 Error ("Unexpected `%s'", Keyword);
1557 static void DoWarning (void)
1560 if (Tok != TOK_STRCON) {
1561 ErrorSkip ("String constant expected");
1563 Warning (0, "User warning: %s", SVal);
1570 static void DoWord (void)
1574 EmitWord (Expression ());
1575 if (Tok != TOK_COMMA) {
1585 static void DoZeropage (void)
1586 /* Switch to the zeropage segment */
1588 UseSeg (&ZeropageSegDef);
1593 /*****************************************************************************/
1595 /*****************************************************************************/
1599 /* Control commands flags */
1601 ccNone = 0x0000, /* No special flags */
1602 ccKeepToken = 0x0001 /* Do not skip the current token */
1605 /* Control command table */
1606 typedef struct CtrlDesc CtrlDesc;
1608 unsigned Flags; /* Flags for this directive */
1609 void (*Handler) (void); /* Command handler */
1612 #define PSEUDO_COUNT (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1613 static CtrlDesc CtrlCmdTab [] = {
1616 { ccNone, DoAddr }, /* .ADDR */
1617 { ccNone, DoAlign },
1618 { ccNone, DoASCIIZ },
1619 { ccNone, DoAssert },
1620 { ccNone, DoAutoImport },
1621 { ccNone, DoUnexpected }, /* .BLANK */
1625 { ccNone, DoCharMap },
1627 { ccNone, DoUnexpected, }, /* .CONCAT */
1628 { ccNone, DoConDes },
1629 { ccNone, DoUnexpected }, /* .CONST */
1630 { ccNone, DoConstructor },
1631 { ccNone, DoUnexpected }, /* .CPU */
1635 { ccNone, DoDebugInfo },
1636 { ccNone, DoDefine },
1637 { ccNone, DoUnexpected }, /* .DEFINED */
1638 { ccNone, DoDestructor },
1639 { ccNone, DoDWord },
1640 { ccKeepToken, DoConditionals }, /* .ELSE */
1641 { ccKeepToken, DoConditionals }, /* .ELSEIF */
1642 { ccKeepToken, DoEnd },
1643 { ccNone, DoUnexpected }, /* .ENDENUM */
1644 { ccKeepToken, DoConditionals }, /* .ENDIF */
1645 { ccNone, DoUnexpected }, /* .ENDMACRO */
1646 { ccNone, DoEndProc },
1647 { ccNone, DoUnexpected }, /* .ENDREPEAT */
1648 { ccNone, DoEndScope },
1649 { ccNone, DoUnexpected }, /* .ENDSTRUCT */
1650 { ccNone, DoUnexpected }, /* .ENDUNION */
1652 { ccNone, DoError },
1653 { ccNone, DoExitMacro },
1654 { ccNone, DoExport },
1655 { ccNone, DoExportZP },
1656 { ccNone, DoFarAddr },
1657 { ccNone, DoFeature },
1658 { ccNone, DoFileOpt },
1659 { ccNone, DoForceImport },
1660 { ccNone, DoUnexpected }, /* .FORCEWORD */
1661 { ccNone, DoGlobal },
1662 { ccNone, DoGlobalZP },
1665 { ccKeepToken, DoConditionals }, /* .IF */
1666 { ccKeepToken, DoConditionals }, /* .IFBLANK */
1667 { ccKeepToken, DoConditionals }, /* .IFCONST */
1668 { ccKeepToken, DoConditionals }, /* .IFDEF */
1669 { ccKeepToken, DoConditionals }, /* .IFNBLANK */
1670 { ccKeepToken, DoConditionals }, /* .IFNCONST */
1671 { ccKeepToken, DoConditionals }, /* .IFNDEF */
1672 { ccKeepToken, DoConditionals }, /* .IFNREF */
1673 { ccKeepToken, DoConditionals }, /* .IFP02 */
1674 { ccKeepToken, DoConditionals }, /* .IFP816 */
1675 { ccKeepToken, DoConditionals }, /* .IFPC02 */
1676 { ccKeepToken, DoConditionals }, /* .IFPSC02 */
1677 { ccKeepToken, DoConditionals }, /* .IFREF */
1678 { ccNone, DoImport },
1679 { ccNone, DoImportZP },
1680 { ccNone, DoIncBin },
1681 { ccNone, DoInclude },
1682 { ccNone, DoInvalid }, /* .LEFT */
1683 { ccNone, DoLineCont },
1685 { ccNone, DoListBytes },
1686 { ccNone, DoUnexpected }, /* .LOCAL */
1687 { ccNone, DoLocalChar },
1688 { ccNone, DoMacPack },
1689 { ccNone, DoMacro },
1690 { ccNone, DoUnexpected }, /* .MATCH */
1691 { ccNone, DoInvalid }, /* .MID */
1697 { ccNone, DoPageLength },
1698 { ccNone, DoUnexpected }, /* .PARAMCOUNT */
1700 { ccNone, DoPopSeg },
1702 { ccNone, DoPSC02 },
1703 { ccNone, DoPushSeg },
1704 { ccNone, DoUnexpected }, /* .REFERENCED */
1705 { ccNone, DoReloc },
1706 { ccNone, DoRepeat },
1708 { ccNone, DoInvalid }, /* .RIGHT */
1709 { ccNone, DoROData },
1710 { ccNone, DoScope },
1711 { ccNone, DoSegment },
1712 { ccNone, DoSetCPU },
1713 { ccNone, DoUnexpected }, /* .SIZEOF */
1714 { ccNone, DoSmart },
1715 { ccNone, DoUnexpected }, /* .STRAT */
1716 { ccNone, DoUnexpected }, /* .STRING */
1717 { ccNone, DoUnexpected }, /* .STRLEN */
1718 { ccNone, DoStruct },
1719 { ccNone, DoSunPlus },
1721 { ccNone, DoUnexpected }, /* .TCOUNT */
1722 { ccNone, DoUnexpected }, /* .TIME */
1723 { ccNone, DoUnion },
1724 { ccNone, DoUnexpected }, /* .VERSION */
1725 { ccNone, DoWarning },
1727 { ccNone, DoUnexpected }, /* .XMATCH */
1728 { ccNone, DoZeropage },
1733 /*****************************************************************************/
1735 /*****************************************************************************/
1739 void HandlePseudo (void)
1740 /* Handle a pseudo instruction */
1744 /* Calculate the index into the table */
1745 unsigned Index = Tok - TOK_FIRSTPSEUDO;
1748 if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
1749 Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
1750 PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
1752 CHECK (Index < PSEUDO_COUNT);
1754 /* Get the pseudo intruction descriptor */
1755 D = &CtrlCmdTab [Index];
1757 /* Remember the instruction, then skip it if needed */
1758 if ((D->Flags & ccKeepToken) == 0) {
1759 strcpy (Keyword, SVal);
1763 /* Call the handler */
1769 void SegStackCheck (void)
1770 /* Check if the segment stack is empty at end of assembly */
1772 if (CollCount (&SegStack) != 0) {
1773 Error ("Segment stack is not empty");