1 /*****************************************************************************/
5 /* Pseudo instructions for the ca65 macroassembler */
9 /* (C) 1998-2000 Ullrich von Bassewitz */
11 /* D-70597 Stuttgart */
12 /* EMail: uz@musoftware.de */
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 /*****************************************************************************/
42 #include "../common/bitops.h"
61 /*****************************************************************************/
63 /*****************************************************************************/
67 /* Keyword we're about to handle */
68 static char Keyword [sizeof (SVal)+1] = ".";
72 /*****************************************************************************/
74 /*****************************************************************************/
78 static void DoUnexpected (void);
82 /*****************************************************************************/
83 /* Helper functions */
84 /*****************************************************************************/
88 static void SetBoolOption (unsigned char* Flag)
89 /* Read a on/off/+/- option and set flag accordingly */
91 static const char* Keys[] = {
96 if (Tok == TOK_PLUS) {
99 } else if (Tok == TOK_MINUS) {
102 } else if (Tok == TOK_IDENT) {
103 /* Map the keyword to a number */
104 switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
105 case 0: *Flag = 0; NextTok (); break;
106 case 1: *Flag = 1; NextTok (); break;
107 default: ErrorSkip (ERR_ONOFF_EXPECTED); break;
109 } else if (Tok == TOK_SEP || Tok == TOK_EOF) {
110 /* Without anything assume switch on */
113 ErrorSkip (ERR_ONOFF_EXPECTED);
119 static void ExportImport (void (*SymFunc) (const char*, int), int ZP)
120 /* Export or import symbols */
123 if (Tok != TOK_IDENT) {
124 ErrorSkip (ERR_IDENT_EXPECTED);
129 if (Tok == TOK_COMMA) {
139 static long IntArg (long Min, long Max)
140 /* Read an integer argument and check a range. Accept the token "unlimited"
141 * and return -1 in this case.
144 if (Tok == TOK_IDENT && strcmp (SVal, "unlimited") == 0) {
148 long Val = ConstExpression ();
149 if (Val < Min || Val > Max) {
159 /*****************************************************************************/
160 /* Handler functions */
161 /*****************************************************************************/
165 static void DoA16 (void)
166 /* Switch the accu to 16 bit mode (assembler only) */
168 if (GetCPU() != CPU_65816) {
169 Error (ERR_816_MODE_ONLY);
171 /* Immidiate mode has two extension bytes */
172 ExtBytes [AMI_IMM_ACCU] = 2;
178 static void DoA8 (void)
179 /* Switch the accu to 8 bit mode (assembler only) */
181 if (GetCPU() != CPU_65816) {
182 Error (ERR_816_MODE_ONLY);
184 /* Immidiate mode has one extension byte */
185 ExtBytes [AMI_IMM_ACCU] = 1;
191 static void DoAddr (void)
192 /* Define addresses */
195 if (GetCPU() == CPU_65816) {
196 EmitWord (ForceWordExpr (Expression ()));
198 /* Do a range check */
199 EmitWord (Expression ());
201 if (Tok != TOK_COMMA) {
211 static void DoAlign (void)
212 /* Align the PC to some boundary */
218 /* Read the alignment value */
219 Align = ConstExpression ();
220 if (Align <= 0 || Align > 0x10000) {
221 ErrorSkip (ERR_RANGE);
225 /* Optional value follows */
226 if (Tok == TOK_COMMA) {
228 Val = ConstExpression ();
229 /* We need a byte value here */
230 if (!IsByteRange (Val)) {
231 ErrorSkip (ERR_RANGE);
238 /* Check if the alignment is a power of two */
239 Bit = BitFind (Align);
240 if (Align != (0x01L << Bit)) {
243 SegAlign (Bit, (int) Val);
249 static void DoASCIIZ (void)
250 /* Define text with a zero terminator */
253 if (StringExpression () == 0) {
254 ErrorSkip (ERR_STRCON_EXPECTED);
257 EmitData (SVal, strlen (SVal));
259 if (Tok == TOK_COMMA) {
270 static void DoAutoImport (void)
271 /* Mark unresolved symbols as imported */
273 SetBoolOption (&AutoImport);
278 static void DoBss (void)
279 /* Switch to the BSS segment */
286 static void DoByte (void)
290 if (StringExpression () != 0) {
292 EmitData (SVal, strlen (SVal));
295 EmitByte (Expression ());
297 if (Tok != TOK_COMMA) {
301 /* Do smart handling of dangling comma */
302 if (Tok == TOK_SEP) {
303 Error (ERR_UNEXPECTED_EOL);
312 static void DoCase (void)
313 /* Switch the IgnoreCase option */
315 SetBoolOption (&IgnoreCase);
316 IgnoreCase = !IgnoreCase;
321 static void DoCode (void)
322 /* Switch to the code segment */
329 static void DoData (void)
330 /* Switch to the data segment */
337 static void DoDByt (void)
338 /* Output double bytes */
341 EmitWord (SwapExpr (Expression ()));
342 if (Tok != TOK_COMMA) {
352 static void DoDebugInfo (void)
353 /* Switch debug info on or off */
355 SetBoolOption (&DbgSyms);
360 static void DoDefine (void)
361 /* Define a one line macro */
363 MacDef (MAC_STYLE_DEFINE);
368 static void DoDWord (void)
372 EmitDWord (Expression ());
373 if (Tok != TOK_COMMA) {
383 static void DoEnd (void)
384 /* End of assembly */
391 static void DoEndProc (void)
392 /* Leave a lexical level */
399 static void DoError (void)
402 if (StringExpression () == 0) {
403 ErrorSkip (ERR_STRCON_EXPECTED);
405 Error (ERR_USER, SVal);
412 static void DoExitMacro (void)
413 /* Exit a macro expansion */
415 if (!InMacExpansion ()) {
416 /* We aren't expanding a macro currently */
425 static void DoExport (void)
426 /* Export a symbol */
428 ExportImport (SymExport, 0);
433 static void DoExportZP (void)
434 /* Export a zeropage symbol */
436 ExportImport (SymExport, 1);
441 static void DoFarAddr (void)
442 /* Define far addresses (24 bit) */
445 EmitFarAddr (Expression ());
446 if (Tok != TOK_COMMA) {
456 static void DoFeature (void)
457 /* Switch the Feature option */
461 static const char* Keys[] = {
463 "LABELS_WITHOUT_COLONS",
466 "DOLLAR_IN_IDENTIFIERS",
469 /* Allow a list of comma separated keywords */
472 /* We expect an identifier */
473 if (Tok != TOK_IDENT) {
474 ErrorSkip (ERR_IDENT_EXPECTED);
478 /* Map the keyword to a number */
479 Feature = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
482 ErrorSkip (ERR_ILLEGAL_FEATURE);
486 /* Skip the keyword */
489 /* Switch the feature on */
491 case 0: DollarIsPC = 1; break;
492 case 1: NoColonLabels = 1; break;
493 case 2: LooseStringTerm = 1; break;
494 case 3: AtInIdents = 1; break;
495 case 4: DollarInIdents = 1; break;
496 default: Internal ("Invalid feature: %d", Feature);
499 /* Allow more than one keyword */
500 if (Tok == TOK_COMMA) {
510 static void DoFileOpt (void)
511 /* Insert a file option */
515 /* The option type may be given as a keyword or as a number. */
516 if (Tok == TOK_IDENT) {
518 /* Option given as keyword */
519 static const char* Keys [] = {
520 "AUTHOR", "COMMENT", "COMPILER"
523 /* Map the option to a number */
524 OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
527 ErrorSkip (ERR_OPTION_KEY_EXPECTED);
531 /* Skip the keyword */
534 /* Must be followed by a comma */
537 /* We accept only string options for now */
538 if (StringExpression () == 0) {
539 ErrorSkip (ERR_STRCON_EXPECTED);
543 /* Insert the option */
562 Internal ("Invalid OptNum: %l", OptNum);
571 /* Option given as number */
572 OptNum = ConstExpression ();
573 if (!IsByteRange (OptNum)) {
574 ErrorSkip (ERR_RANGE);
578 /* Must be followed by a comma */
581 /* We accept only string options for now */
582 if (StringExpression () == 0) {
583 ErrorSkip (ERR_STRCON_EXPECTED);
587 /* Insert the option */
588 OptStr ((unsigned char) OptNum, SVal);
597 static void DoGlobal (void)
598 /* Declare a global symbol */
600 ExportImport (SymGlobal, 0);
605 static void DoGlobalZP (void)
606 /* Declare a global zeropage symbol */
608 ExportImport (SymGlobal, 1);
613 static void DoI16 (void)
614 /* Switch the index registers to 16 bit mode (assembler only) */
616 if (GetCPU() != CPU_65816) {
617 Error (ERR_816_MODE_ONLY);
619 /* Immidiate mode has two extension bytes */
620 ExtBytes [AMI_IMM_INDEX] = 2;
626 static void DoI8 (void)
627 /* Switch the index registers to 16 bit mode (assembler only) */
629 if (GetCPU() != CPU_65816) {
630 Error (ERR_816_MODE_ONLY);
632 /* Immidiate mode has one extension byte */
633 ExtBytes [AMI_IMM_INDEX] = 1;
639 static void DoImport (void)
640 /* Import a symbol */
642 ExportImport (SymImport, 0);
647 static void DoImportZP (void)
648 /* Import a zero page symbol */
650 ExportImport (SymImport, 1);
655 static void DoIncBin (void)
656 /* Include a binary file */
658 /* Name must follow */
659 if (StringExpression () == 0) {
660 ErrorSkip (ERR_STRCON_EXPECTED);
662 /* Try to open the file */
663 FILE* F = fopen (SVal, "rb");
665 Error (ERR_CANNOT_OPEN_INCLUDE, SVal, strerror (errno));
667 unsigned char Buf [1024];
669 /* Read chunks and insert them into the output */
670 while ((Count = fread (Buf, 1, sizeof (Buf), F)) > 0) {
671 EmitData (Buf, Count);
673 /* Close the file, ignore errors since it's r/o */
683 static void DoInclude (void)
684 /* Include another file */
686 char Name [MAX_STR_LEN+1];
688 /* Name must follow */
689 if (StringExpression () == 0) {
690 ErrorSkip (ERR_STRCON_EXPECTED);
700 static void DoLineCont (void)
701 /* Switch the use of line continuations */
703 SetBoolOption (&LineCont);
708 static void DoList (void)
709 /* Enable/disable the listing */
711 /* Get the setting */
713 SetBoolOption (&List);
715 /* Manage the counter */
725 static void DoListBytes (void)
726 /* Set maximum number of bytes to list for one line */
728 SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
733 static void DoLocalChar (void)
734 /* Define the character that starts local labels */
736 if (Tok != TOK_CHARCON) {
737 ErrorSkip (ERR_CHARCON_EXPECTED);
739 if (IVal != '@' && IVal != '?') {
740 Error (ERR_ILLEGAL_LOCALSTART);
742 LocalStart = (char) IVal;
750 static void DoMacPack (void)
751 /* Insert a macro package */
753 /* Macro package names */
754 static const char* Keys [] = {
761 /* We expect an identifier */
762 if (Tok != TOK_IDENT) {
763 ErrorSkip (ERR_IDENT_EXPECTED);
767 /* Map the keyword to a number */
768 Package = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
771 ErrorSkip (ERR_ILLEGAL_MACPACK);
775 /* Skip the package name */
778 /* Insert the package */
779 InsertMacPack (Package);
784 static void DoMacro (void)
785 /* Start a macro definition */
787 MacDef (MAC_STYLE_CLASSIC);
792 static void DoMid (void)
793 /* Handle .MID - this should never happen, since the keyword is actually
794 * handled on a much lower level of the expression hierarchy.
797 Internal ("Unexpected token: .MID");
802 static void DoNull (void)
803 /* Switch to the NULL segment */
810 static void DoOrg (void)
811 /* Start absolute code */
813 long PC = ConstExpression ();
814 if (PC < 0 || PC > 0xFFFF) {
823 static void DoOut (void)
824 /* Output a string */
826 if (StringExpression () == 0) {
827 ErrorSkip (ERR_STRCON_EXPECTED);
829 /* Output the string and be sure to flush the output to keep it in
830 * sync with any error messages if the output is redirected to a file.
832 printf ("%s\n", SVal);
840 static void DoP02 (void)
841 /* Switch to 6502 CPU */
848 static void DoPC02 (void)
849 /* Switch to 65C02 CPU */
856 static void DoP816 (void)
857 /* Switch to 65816 CPU */
864 static void DoPageLength (void)
865 /* Set the page length for the listing */
867 PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
872 static void DoProc (void)
873 /* Start a new lexical scope */
875 if (Tok == TOK_IDENT) {
876 /* The new scope has a name */
877 SymDef (SVal, CurrentPC (), IsZPSeg ());
885 static void DoReloc (void)
886 /* Enter relocatable mode */
893 static void DoRepeat (void)
894 /* Repeat some instruction block */
896 ErrorSkip (ERR_NOT_IMPLEMENTED);
901 static void DoRes (void)
902 /* Reserve some number of storage bytes */
907 Count = ConstExpression ();
908 if (Count > 0xFFFF || Count < 0) {
909 ErrorSkip (ERR_RANGE);
912 if (Tok == TOK_COMMA) {
914 Val = ConstExpression ();
915 /* We need a byte value here */
916 if (!IsByteRange (Val)) {
917 ErrorSkip (ERR_RANGE);
921 /* Emit constant values */
923 Emit0 ((unsigned char) Val);
927 /* Emit fill fragments */
934 static void DoROData (void)
935 /* Switch to the r/o data segment */
942 static void DoSegment (void)
943 /* Switch to another segment */
945 static const char* AttrTab [] = {
946 "ZEROPAGE", "DIRECT",
950 char Name [sizeof (SVal)];
953 if (StringExpression () == 0) {
954 ErrorSkip (ERR_STRCON_EXPECTED);
957 /* Save the name of the segment and skip it */
961 /* Check for an optional segment attribute */
962 SegType = SEGTYPE_DEFAULT;
963 if (Tok == TOK_COMMA) {
965 if (Tok != TOK_IDENT) {
966 ErrorSkip (ERR_IDENT_EXPECTED);
968 int Attr = GetSubKey (AttrTab, sizeof (AttrTab) / sizeof (AttrTab [0]));
974 SegType = SEGTYPE_ZP;
979 SegType = SEGTYPE_ABS;
985 SegType = SEGTYPE_FAR;
989 Error (ERR_ILLEGAL_SEG_ATTR);
995 /* Set the segment */
996 UseSeg (Name, SegType);
1002 static void DoSmart (void)
1003 /* Smart mode on/off */
1005 SetBoolOption (&SmartMode);
1010 static void DoSunPlus (void)
1011 /* Switch to the SUNPLUS CPU */
1013 SetCPU (CPU_SUNPLUS);
1018 static void DoUnexpected (void)
1019 /* Got an unexpected keyword */
1021 Error (ERR_UNEXPECTED, Keyword);
1027 static void DoWord (void)
1031 EmitWord (Expression ());
1032 if (Tok != TOK_COMMA) {
1042 static void DoZeropage (void)
1043 /* Switch to the zeropage segment */
1050 /*****************************************************************************/
1052 /*****************************************************************************/
1056 /* Control commands flags */
1058 ccNone = 0x0000, /* No special flags */
1059 ccKeepToken = 0x0001 /* Do not skip the current token */
1062 /* Control command table */
1064 unsigned Flags; /* Flags for this directive */
1065 void (*Handler) (void); /* Command handler */
1067 typedef struct CtrlDesc_ CtrlDesc;
1069 #define PSEUDO_COUNT (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1070 static CtrlDesc CtrlCmdTab [] = {
1073 { ccNone, DoAddr }, /* .ADDR */
1074 { ccNone, DoAlign },
1075 { ccNone, DoASCIIZ },
1076 { ccNone, DoAutoImport },
1077 { ccNone, DoUnexpected }, /* .BLANK */
1082 { ccNone, DoUnexpected }, /* .CONST */
1083 { ccNone, DoUnexpected }, /* .CPU */
1086 { ccNone, DoDebugInfo },
1087 { ccNone, DoDefine },
1088 { ccNone, DoUnexpected }, /* .DEFINED */
1089 { ccNone, DoDWord },
1090 { ccKeepToken, DoConditionals }, /* .ELSE */
1091 { ccKeepToken, DoConditionals }, /* .ELSEIF */
1093 { ccKeepToken, DoConditionals }, /* .ENDIF */
1094 { ccNone, DoUnexpected }, /* .ENDMACRO */
1095 { ccNone, DoEndProc },
1096 { ccNone, DoUnexpected }, /* .ENDREPEAT */
1097 { ccNone, DoError },
1098 { ccNone, DoExitMacro },
1099 { ccNone, DoExport },
1100 { ccNone, DoExportZP },
1101 { ccNone, DoFarAddr },
1102 { ccNone, DoFeature },
1103 { ccNone, DoFileOpt },
1104 { ccNone, DoGlobal },
1105 { ccNone, DoGlobalZP },
1108 { ccKeepToken, DoConditionals }, /* .IF */
1109 { ccKeepToken, DoConditionals }, /* .IFBLANK */
1110 { ccKeepToken, DoConditionals }, /* .IFCONST */
1111 { ccKeepToken, DoConditionals }, /* .IFDEF */
1112 { ccKeepToken, DoConditionals }, /* .IFNBLANK */
1113 { ccKeepToken, DoConditionals }, /* .IFNCONST */
1114 { ccKeepToken, DoConditionals }, /* .IFNDEF */
1115 { ccKeepToken, DoConditionals }, /* .IFNREF */
1116 { ccKeepToken, DoConditionals }, /* .IFP02 */
1117 { ccKeepToken, DoConditionals }, /* .IFP816 */
1118 { ccKeepToken, DoConditionals }, /* .IFPC02 */
1119 { ccKeepToken, DoConditionals }, /* .IFREF */
1120 { ccNone, DoImport },
1121 { ccNone, DoImportZP },
1122 { ccNone, DoIncBin },
1123 { ccNone, DoInclude },
1124 { ccNone, DoLineCont },
1126 { ccNone, DoListBytes },
1127 { ccNone, DoUnexpected }, /* .LOCAL */
1128 { ccNone, DoLocalChar },
1129 { ccNone, DoMacPack },
1130 { ccNone, DoMacro },
1131 { ccNone, DoUnexpected }, /* .MATCH */
1138 { ccNone, DoPageLength },
1139 { ccNone, DoUnexpected }, /* .PARAMCOUNT */
1142 { ccNone, DoUnexpected }, /* .REFERENCED */
1143 { ccNone, DoReloc },
1144 { ccNone, DoRepeat },
1146 { ccNone, DoROData },
1147 { ccNone, DoSegment },
1148 { ccNone, DoSmart },
1149 { ccNone, DoUnexpected }, /* .STRING */
1150 { ccNone, DoSunPlus },
1152 { ccNone, DoUnexpected }, /* .XMATCH */
1153 { ccNone, DoZeropage },
1158 /*****************************************************************************/
1160 /*****************************************************************************/
1164 int TokIsPseudo (unsigned Tok)
1165 /* Return true if the given token is a pseudo instruction token */
1167 return (Tok >= TOK_FIRSTPSEUDO && Tok <= TOK_LASTPSEUDO);
1172 void HandlePseudo (void)
1173 /* Handle a pseudo instruction */
1177 /* Calculate the index into the table */
1178 unsigned Index = Tok - TOK_FIRSTPSEUDO;
1181 if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
1182 Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
1183 PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
1185 CHECK (Index < PSEUDO_COUNT);
1187 /* Get the pseudo intruction descriptor */
1188 D = &CtrlCmdTab [Index];
1190 /* Remember the instruction, then skip it if needed */
1191 if ((D->Flags & ccKeepToken) == 0) {
1192 strcpy (Keyword+1, SVal);
1196 /* Call the handler */