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 /*****************************************************************************/
67 /*****************************************************************************/
69 /*****************************************************************************/
73 /* Keyword we're about to handle */
74 static char Keyword [sizeof (SVal)+1] = ".";
78 /*****************************************************************************/
80 /*****************************************************************************/
84 static void DoUnexpected (void);
85 /* Got an unexpected keyword */
87 static void DoInvalid (void);
88 /* Handle a token that is invalid here, since it should have been handled on
89 * a much lower level of the expression hierarchy. Getting this sort of token
90 * means that the lower level code has bugs.
91 * This function differs to DoUnexpected in that the latter may be triggered
92 * by the user by using keywords in the wrong location. DoUnexpected is not
93 * an error in the assembler itself, while DoInvalid is.
98 /*****************************************************************************/
99 /* Helper functions */
100 /*****************************************************************************/
104 static void SetBoolOption (unsigned char* Flag)
105 /* Read a on/off/+/- option and set flag accordingly */
107 static const char* Keys[] = {
112 if (Tok == TOK_PLUS) {
115 } else if (Tok == TOK_MINUS) {
118 } else if (Tok == TOK_IDENT) {
119 /* Map the keyword to a number */
120 switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
121 case 0: *Flag = 0; NextTok (); break;
122 case 1: *Flag = 1; NextTok (); break;
123 default: ErrorSkip (ERR_ONOFF_EXPECTED); break;
125 } else if (Tok == TOK_SEP || Tok == TOK_EOF) {
126 /* Without anything assume switch on */
129 ErrorSkip (ERR_ONOFF_EXPECTED);
135 static void ExportImport (void (*SymFunc) (const char*, int), int ZP)
136 /* Export or import symbols */
139 if (Tok != TOK_IDENT) {
140 ErrorSkip (ERR_IDENT_EXPECTED);
145 if (Tok == TOK_COMMA) {
155 static long IntArg (long Min, long Max)
156 /* Read an integer argument and check a range. Accept the token "unlimited"
157 * and return -1 in this case.
160 if (Tok == TOK_IDENT && strcmp (SVal, "unlimited") == 0) {
164 long Val = ConstExpression ();
165 if (Val < Min || Val > Max) {
175 /*****************************************************************************/
176 /* Handler functions */
177 /*****************************************************************************/
181 static void DoA16 (void)
182 /* Switch the accu to 16 bit mode (assembler only) */
184 if (GetCPU() != CPU_65816) {
185 Error (ERR_816_MODE_ONLY);
187 /* Immidiate mode has two extension bytes */
188 ExtBytes [AMI_IMM_ACCU] = 2;
194 static void DoA8 (void)
195 /* Switch the accu to 8 bit mode (assembler only) */
197 if (GetCPU() != CPU_65816) {
198 Error (ERR_816_MODE_ONLY);
200 /* Immidiate mode has one extension byte */
201 ExtBytes [AMI_IMM_ACCU] = 1;
207 static void DoAddr (void)
208 /* Define addresses */
211 if (GetCPU() == CPU_65816) {
212 EmitWord (ForceWordExpr (Expression ()));
214 /* Do a range check */
215 EmitWord (Expression ());
217 if (Tok != TOK_COMMA) {
227 static void DoAlign (void)
228 /* Align the PC to some boundary */
234 /* Read the alignment value */
235 Align = ConstExpression ();
236 if (Align <= 0 || Align > 0x10000) {
237 ErrorSkip (ERR_RANGE);
241 /* Optional value follows */
242 if (Tok == TOK_COMMA) {
244 Val = ConstExpression ();
245 /* We need a byte value here */
246 if (!IsByteRange (Val)) {
247 ErrorSkip (ERR_RANGE);
254 /* Check if the alignment is a power of two */
255 Bit = BitFind (Align);
256 if (Align != (0x01L << Bit)) {
259 SegAlign (Bit, (int) Val);
265 static void DoASCIIZ (void)
266 /* Define text with a zero terminator */
269 if (Tok != TOK_STRCON) {
270 ErrorSkip (ERR_STRCON_EXPECTED);
273 /* Translate into target charset and emit */
274 TgtTranslateStr (SVal);
275 EmitData ((unsigned char*) SVal, strlen (SVal));
277 if (Tok == TOK_COMMA) {
288 static void DoAutoImport (void)
289 /* Mark unresolved symbols as imported */
291 SetBoolOption (&AutoImport);
296 static void DoBss (void)
297 /* Switch to the BSS segment */
304 static void DoByte (void)
308 if (Tok == TOK_STRCON) {
309 /* A string, translate into target charset and emit */
310 TgtTranslateStr (SVal);
311 EmitData ((unsigned char*) SVal, strlen (SVal));
314 EmitByte (Expression ());
316 if (Tok != TOK_COMMA) {
320 /* Do smart handling of dangling comma */
321 if (Tok == TOK_SEP) {
322 Error (ERR_UNEXPECTED_EOL);
331 static void DoCase (void)
332 /* Switch the IgnoreCase option */
334 SetBoolOption (&IgnoreCase);
335 IgnoreCase = !IgnoreCase;
340 static void DoCode (void)
341 /* Switch to the code segment */
348 static void DoData (void)
349 /* Switch to the data segment */
356 static void DoDbg (void)
357 /* Add debug information from high level code */
359 static const char* Keys[] = {
367 /* We expect a subkey */
368 if (Tok != TOK_IDENT) {
369 ErrorSkip (ERR_IDENT_EXPECTED);
373 /* Map the following keyword to a number */
374 Key = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
376 /* Skip the subkey */
379 /* Parameters are separated by a comma */
382 /* Check the key and dispatch to a handler */
384 case 0: DbgInfoFile (); break;
385 case 1: DbgInfoLine (); break;
386 case 2: DbgInfoSym (); break;
387 default: ErrorSkip (ERR_SYNTAX); break;
393 static void DoDByt (void)
394 /* Output double bytes */
397 EmitWord (SwapExpr (Expression ()));
398 if (Tok != TOK_COMMA) {
408 static void DoDebugInfo (void)
409 /* Switch debug info on or off */
411 SetBoolOption (&DbgSyms);
416 static void DoDefine (void)
417 /* Define a one line macro */
419 MacDef (MAC_STYLE_DEFINE);
424 static void DoDWord (void)
428 EmitDWord (Expression ());
429 if (Tok != TOK_COMMA) {
439 static void DoEnd (void)
440 /* End of assembly */
447 static void DoEndProc (void)
448 /* Leave a lexical level */
455 static void DoError (void)
458 if (Tok != TOK_STRCON) {
459 ErrorSkip (ERR_STRCON_EXPECTED);
461 Error (ERR_USER, SVal);
468 static void DoExitMacro (void)
469 /* Exit a macro expansion */
471 if (!InMacExpansion ()) {
472 /* We aren't expanding a macro currently */
481 static void DoExport (void)
482 /* Export a symbol */
484 ExportImport (SymExport, 0);
489 static void DoExportZP (void)
490 /* Export a zeropage symbol */
492 ExportImport (SymExport, 1);
497 static void DoFarAddr (void)
498 /* Define far addresses (24 bit) */
501 EmitFarAddr (Expression ());
502 if (Tok != TOK_COMMA) {
512 static void DoFeature (void)
513 /* Switch the Feature option */
515 /* Allow a list of comma separated keywords */
518 /* We expect an identifier */
519 if (Tok != TOK_IDENT) {
520 ErrorSkip (ERR_IDENT_EXPECTED);
524 /* Make the string attribute lower case */
527 /* Set the feature and check for errors */
528 if (SetFeature (SVal) == FEAT_UNKNOWN) {
530 ErrorSkip (ERR_ILLEGAL_FEATURE);
533 /* Skip the keyword */
537 /* Allow more than one keyword */
538 if (Tok == TOK_COMMA) {
548 static void DoFileOpt (void)
549 /* Insert a file option */
553 /* The option type may be given as a keyword or as a number. */
554 if (Tok == TOK_IDENT) {
556 /* Option given as keyword */
557 static const char* Keys [] = {
558 "AUTHOR", "COMMENT", "COMPILER"
561 /* Map the option to a number */
562 OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
565 ErrorSkip (ERR_OPTION_KEY_EXPECTED);
569 /* Skip the keyword */
572 /* Must be followed by a comma */
575 /* We accept only string options for now */
576 if (Tok != TOK_STRCON) {
577 ErrorSkip (ERR_STRCON_EXPECTED);
581 /* Insert the option */
600 Internal ("Invalid OptNum: %l", OptNum);
609 /* Option given as number */
610 OptNum = ConstExpression ();
611 if (!IsByteRange (OptNum)) {
612 ErrorSkip (ERR_RANGE);
616 /* Must be followed by a comma */
619 /* We accept only string options for now */
620 if (Tok != TOK_STRCON) {
621 ErrorSkip (ERR_STRCON_EXPECTED);
625 /* Insert the option */
626 OptStr ((unsigned char) OptNum, SVal);
635 static void DoGlobal (void)
636 /* Declare a global symbol */
638 ExportImport (SymGlobal, 0);
643 static void DoGlobalZP (void)
644 /* Declare a global zeropage symbol */
646 ExportImport (SymGlobal, 1);
651 static void DoI16 (void)
652 /* Switch the index registers to 16 bit mode (assembler only) */
654 if (GetCPU() != CPU_65816) {
655 Error (ERR_816_MODE_ONLY);
657 /* Immidiate mode has two extension bytes */
658 ExtBytes [AMI_IMM_INDEX] = 2;
664 static void DoI8 (void)
665 /* Switch the index registers to 16 bit mode (assembler only) */
667 if (GetCPU() != CPU_65816) {
668 Error (ERR_816_MODE_ONLY);
670 /* Immidiate mode has one extension byte */
671 ExtBytes [AMI_IMM_INDEX] = 1;
677 static void DoImport (void)
678 /* Import a symbol */
680 ExportImport (SymImport, 0);
685 static void DoImportZP (void)
686 /* Import a zero page symbol */
688 ExportImport (SymImport, 1);
693 static void DoIncBin (void)
694 /* Include a binary file */
696 /* Name must follow */
697 if (Tok != TOK_STRCON) {
698 ErrorSkip (ERR_STRCON_EXPECTED);
700 /* Try to open the file */
701 FILE* F = fopen (SVal, "rb");
703 Error (ERR_CANNOT_OPEN_INCLUDE, SVal, strerror (errno));
705 unsigned char Buf [1024];
707 /* Read chunks and insert them into the output */
708 while ((Count = fread (Buf, 1, sizeof (Buf), F)) > 0) {
709 EmitData (Buf, Count);
711 /* Close the file, ignore errors since it's r/o */
721 static void DoInclude (void)
722 /* Include another file */
724 char Name [MAX_STR_LEN+1];
726 /* Name must follow */
727 if (Tok != TOK_STRCON) {
728 ErrorSkip (ERR_STRCON_EXPECTED);
738 static void DoInitializer (void)
739 /* Export a symbol as initializer */
741 ExportImport (SymInitializer, 0);
746 static void DoInvalid (void)
747 /* Handle a token that is invalid here, since it should have been handled on
748 * a much lower level of the expression hierarchy. Getting this sort of token
749 * means that the lower level code has bugs.
750 * This function differs to DoUnexpected in that the latter may be triggered
751 * by the user by using keywords in the wrong location. DoUnexpected is not
752 * an error in the assembler itself, while DoInvalid is.
755 Internal ("Unexpected token: %s", Keyword);
760 static void DoLineCont (void)
761 /* Switch the use of line continuations */
763 SetBoolOption (&LineCont);
768 static void DoList (void)
769 /* Enable/disable the listing */
771 /* Get the setting */
773 SetBoolOption (&List);
775 /* Manage the counter */
785 static void DoListBytes (void)
786 /* Set maximum number of bytes to list for one line */
788 SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
793 static void DoLocalChar (void)
794 /* Define the character that starts local labels */
796 if (Tok != TOK_CHARCON) {
797 ErrorSkip (ERR_CHARCON_EXPECTED);
799 if (IVal != '@' && IVal != '?') {
800 Error (ERR_ILLEGAL_LOCALSTART);
802 LocalStart = (char) IVal;
810 static void DoMacPack (void)
811 /* Insert a macro package */
813 /* Macro package names */
814 static const char* Keys [] = {
821 /* We expect an identifier */
822 if (Tok != TOK_IDENT) {
823 ErrorSkip (ERR_IDENT_EXPECTED);
827 /* Map the keyword to a number */
828 Package = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
831 ErrorSkip (ERR_ILLEGAL_MACPACK);
835 /* Skip the package name */
838 /* Insert the package */
839 InsertMacPack (Package);
844 static void DoMacro (void)
845 /* Start a macro definition */
847 MacDef (MAC_STYLE_CLASSIC);
852 static void DoNull (void)
853 /* Switch to the NULL segment */
860 static void DoOrg (void)
861 /* Start absolute code */
863 long PC = ConstExpression ();
864 if (PC < 0 || PC > 0xFFFFFF) {
873 static void DoOut (void)
874 /* Output a string */
876 if (Tok != TOK_STRCON) {
877 ErrorSkip (ERR_STRCON_EXPECTED);
879 /* Output the string and be sure to flush the output to keep it in
880 * sync with any error messages if the output is redirected to a file.
882 printf ("%s\n", SVal);
890 static void DoP02 (void)
891 /* Switch to 6502 CPU */
898 static void DoPC02 (void)
899 /* Switch to 65C02 CPU */
906 static void DoP816 (void)
907 /* Switch to 65816 CPU */
914 static void DoPageLength (void)
915 /* Set the page length for the listing */
917 PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
922 static void DoProc (void)
923 /* Start a new lexical scope */
925 if (Tok == TOK_IDENT) {
926 /* The new scope has a name */
927 SymDef (SVal, CurrentPC (), IsZPSeg ());
935 static void DoReloc (void)
936 /* Enter relocatable mode */
943 static void DoRepeat (void)
944 /* Repeat some instruction block */
951 static void DoRes (void)
952 /* Reserve some number of storage bytes */
957 Count = ConstExpression ();
958 if (Count > 0xFFFF || Count < 0) {
959 ErrorSkip (ERR_RANGE);
962 if (Tok == TOK_COMMA) {
964 Val = ConstExpression ();
965 /* We need a byte value here */
966 if (!IsByteRange (Val)) {
967 ErrorSkip (ERR_RANGE);
971 /* Emit constant values */
973 Emit0 ((unsigned char) Val);
977 /* Emit fill fragments */
984 static void DoROData (void)
985 /* Switch to the r/o data segment */
992 static void DoSegment (void)
993 /* Switch to another segment */
995 static const char* AttrTab [] = {
996 "ZEROPAGE", "DIRECT",
1000 char Name [sizeof (SVal)];
1003 if (Tok != TOK_STRCON) {
1004 ErrorSkip (ERR_STRCON_EXPECTED);
1007 /* Save the name of the segment and skip it */
1008 strcpy (Name, SVal);
1011 /* Check for an optional segment attribute */
1012 SegType = SEGTYPE_DEFAULT;
1013 if (Tok == TOK_COMMA) {
1015 if (Tok != TOK_IDENT) {
1016 ErrorSkip (ERR_IDENT_EXPECTED);
1018 int Attr = GetSubKey (AttrTab, sizeof (AttrTab) / sizeof (AttrTab [0]));
1024 SegType = SEGTYPE_ZP;
1029 SegType = SEGTYPE_ABS;
1035 SegType = SEGTYPE_FAR;
1039 Error (ERR_ILLEGAL_SEG_ATTR);
1045 /* Set the segment */
1046 UseSeg (Name, SegType);
1052 static void DoSmart (void)
1053 /* Smart mode on/off */
1055 SetBoolOption (&SmartMode);
1060 static void DoSunPlus (void)
1061 /* Switch to the SUNPLUS CPU */
1063 SetCPU (CPU_SUNPLUS);
1068 static void DoUnexpected (void)
1069 /* Got an unexpected keyword */
1071 Error (ERR_UNEXPECTED, Keyword);
1077 static void DoWarning (void)
1080 if (Tok != TOK_STRCON) {
1081 ErrorSkip (ERR_STRCON_EXPECTED);
1083 Warning (WARN_USER, SVal);
1090 static void DoWord (void)
1094 EmitWord (Expression ());
1095 if (Tok != TOK_COMMA) {
1105 static void DoZeropage (void)
1106 /* Switch to the zeropage segment */
1113 /*****************************************************************************/
1115 /*****************************************************************************/
1119 /* Control commands flags */
1121 ccNone = 0x0000, /* No special flags */
1122 ccKeepToken = 0x0001 /* Do not skip the current token */
1125 /* Control command table */
1127 unsigned Flags; /* Flags for this directive */
1128 void (*Handler) (void); /* Command handler */
1130 typedef struct CtrlDesc_ CtrlDesc;
1132 #define PSEUDO_COUNT (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1133 static CtrlDesc CtrlCmdTab [] = {
1136 { ccNone, DoAddr }, /* .ADDR */
1137 { ccNone, DoAlign },
1138 { ccNone, DoASCIIZ },
1139 { ccNone, DoAutoImport },
1140 { ccNone, DoUnexpected }, /* .BLANK */
1145 { ccNone, DoUnexpected, }, /* .CONCAT */
1146 { ccNone, DoUnexpected }, /* .CONST */
1147 { ccNone, DoUnexpected }, /* .CPU */
1151 { ccNone, DoDebugInfo },
1152 { ccNone, DoDefine },
1153 { ccNone, DoUnexpected }, /* .DEFINED */
1154 { ccNone, DoDWord },
1155 { ccKeepToken, DoConditionals }, /* .ELSE */
1156 { ccKeepToken, DoConditionals }, /* .ELSEIF */
1158 { ccKeepToken, DoConditionals }, /* .ENDIF */
1159 { ccNone, DoUnexpected }, /* .ENDMACRO */
1160 { ccNone, DoEndProc },
1161 { ccNone, DoUnexpected }, /* .ENDREPEAT */
1162 { ccNone, DoError },
1163 { ccNone, DoExitMacro },
1164 { ccNone, DoExport },
1165 { ccNone, DoExportZP },
1166 { ccNone, DoFarAddr },
1167 { ccNone, DoFeature },
1168 { ccNone, DoFileOpt },
1169 { ccNone, DoUnexpected }, /* .FORCEWORD */
1170 { ccNone, DoGlobal },
1171 { ccNone, DoGlobalZP },
1174 { ccKeepToken, DoConditionals }, /* .IF */
1175 { ccKeepToken, DoConditionals }, /* .IFBLANK */
1176 { ccKeepToken, DoConditionals }, /* .IFCONST */
1177 { ccKeepToken, DoConditionals }, /* .IFDEF */
1178 { ccKeepToken, DoConditionals }, /* .IFNBLANK */
1179 { ccKeepToken, DoConditionals }, /* .IFNCONST */
1180 { ccKeepToken, DoConditionals }, /* .IFNDEF */
1181 { ccKeepToken, DoConditionals }, /* .IFNREF */
1182 { ccKeepToken, DoConditionals }, /* .IFP02 */
1183 { ccKeepToken, DoConditionals }, /* .IFP816 */
1184 { ccKeepToken, DoConditionals }, /* .IFPC02 */
1185 { ccKeepToken, DoConditionals }, /* .IFREF */
1186 { ccNone, DoImport },
1187 { ccNone, DoImportZP },
1188 { ccNone, DoIncBin },
1189 { ccNone, DoInclude },
1190 { ccNone, DoInitializer },
1191 { ccNone, DoInvalid }, /* .LEFT */
1192 { ccNone, DoLineCont },
1194 { ccNone, DoListBytes },
1195 { ccNone, DoUnexpected }, /* .LOCAL */
1196 { ccNone, DoLocalChar },
1197 { ccNone, DoMacPack },
1198 { ccNone, DoMacro },
1199 { ccNone, DoUnexpected }, /* .MATCH */
1200 { ccNone, DoInvalid }, /* .MID */
1206 { ccNone, DoPageLength },
1207 { ccNone, DoUnexpected }, /* .PARAMCOUNT */
1210 { ccNone, DoUnexpected }, /* .REFERENCED */
1211 { ccNone, DoReloc },
1212 { ccNone, DoRepeat },
1214 { ccNone, DoInvalid }, /* .RIGHT */
1215 { ccNone, DoROData },
1216 { ccNone, DoSegment },
1217 { ccNone, DoSmart },
1218 { ccNone, DoUnexpected }, /* .STRAT */
1219 { ccNone, DoUnexpected }, /* .STRING */
1220 { ccNone, DoUnexpected }, /* .STRLEN */
1221 { ccNone, DoSunPlus },
1222 { ccNone, DoUnexpected }, /* .TCOUNT */
1223 { ccNone, DoWarning },
1225 { ccNone, DoUnexpected }, /* .XMATCH */
1226 { ccNone, DoZeropage },
1231 /*****************************************************************************/
1233 /*****************************************************************************/
1237 int TokIsPseudo (unsigned Tok)
1238 /* Return true if the given token is a pseudo instruction token */
1240 return (Tok >= TOK_FIRSTPSEUDO && Tok <= TOK_LASTPSEUDO);
1245 void HandlePseudo (void)
1246 /* Handle a pseudo instruction */
1250 /* Calculate the index into the table */
1251 unsigned Index = Tok - TOK_FIRSTPSEUDO;
1254 if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
1255 Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
1256 PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
1258 CHECK (Index < PSEUDO_COUNT);
1260 /* Get the pseudo intruction descriptor */
1261 D = &CtrlCmdTab [Index];
1263 /* Remember the instruction, then skip it if needed */
1264 if ((D->Flags & ccKeepToken) == 0) {
1265 strcpy (Keyword+1, SVal);
1269 /* Call the handler */