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 /*****************************************************************************/
65 /*****************************************************************************/
67 /*****************************************************************************/
71 /* Keyword we're about to handle */
72 static char Keyword [sizeof (SVal)+1] = ".";
76 /*****************************************************************************/
78 /*****************************************************************************/
82 static void DoUnexpected (void);
83 /* Got an unexpected keyword */
85 static void DoInvalid (void);
86 /* Handle a token that is invalid here, since it should have been handled on
87 * a much lower level of the expression hierarchy. Getting this sort of token
88 * means that the lower level code has bugs.
89 * This function differs to DoUnexpected in that the latter may be triggered
90 * by the user by using keywords in the wrong location. DoUnexpected is not
91 * an error in the assembler itself, while DoInvalid is.
96 /*****************************************************************************/
97 /* Helper functions */
98 /*****************************************************************************/
102 static void SetBoolOption (unsigned char* Flag)
103 /* Read a on/off/+/- option and set flag accordingly */
105 static const char* Keys[] = {
110 if (Tok == TOK_PLUS) {
113 } else if (Tok == TOK_MINUS) {
116 } else if (Tok == TOK_IDENT) {
117 /* Map the keyword to a number */
118 switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
119 case 0: *Flag = 0; NextTok (); break;
120 case 1: *Flag = 1; NextTok (); break;
121 default: ErrorSkip (ERR_ONOFF_EXPECTED); break;
123 } else if (Tok == TOK_SEP || Tok == TOK_EOF) {
124 /* Without anything assume switch on */
127 ErrorSkip (ERR_ONOFF_EXPECTED);
133 static void ExportImport (void (*SymFunc) (const char*, int), int ZP)
134 /* Export or import symbols */
137 if (Tok != TOK_IDENT) {
138 ErrorSkip (ERR_IDENT_EXPECTED);
143 if (Tok == TOK_COMMA) {
153 static long IntArg (long Min, long Max)
154 /* Read an integer argument and check a range. Accept the token "unlimited"
155 * and return -1 in this case.
158 if (Tok == TOK_IDENT && strcmp (SVal, "unlimited") == 0) {
162 long Val = ConstExpression ();
163 if (Val < Min || Val > Max) {
173 /*****************************************************************************/
174 /* Handler functions */
175 /*****************************************************************************/
179 static void DoA16 (void)
180 /* Switch the accu to 16 bit mode (assembler only) */
182 if (GetCPU() != CPU_65816) {
183 Error (ERR_816_MODE_ONLY);
185 /* Immidiate mode has two extension bytes */
186 ExtBytes [AMI_IMM_ACCU] = 2;
192 static void DoA8 (void)
193 /* Switch the accu to 8 bit mode (assembler only) */
195 if (GetCPU() != CPU_65816) {
196 Error (ERR_816_MODE_ONLY);
198 /* Immidiate mode has one extension byte */
199 ExtBytes [AMI_IMM_ACCU] = 1;
205 static void DoAddr (void)
206 /* Define addresses */
209 if (GetCPU() == CPU_65816) {
210 EmitWord (ForceWordExpr (Expression ()));
212 /* Do a range check */
213 EmitWord (Expression ());
215 if (Tok != TOK_COMMA) {
225 static void DoAlign (void)
226 /* Align the PC to some boundary */
232 /* Read the alignment value */
233 Align = ConstExpression ();
234 if (Align <= 0 || Align > 0x10000) {
235 ErrorSkip (ERR_RANGE);
239 /* Optional value follows */
240 if (Tok == TOK_COMMA) {
242 Val = ConstExpression ();
243 /* We need a byte value here */
244 if (!IsByteRange (Val)) {
245 ErrorSkip (ERR_RANGE);
252 /* Check if the alignment is a power of two */
253 Bit = BitFind (Align);
254 if (Align != (0x01L << Bit)) {
257 SegAlign (Bit, (int) Val);
263 static void DoASCIIZ (void)
264 /* Define text with a zero terminator */
267 if (Tok != TOK_STRCON) {
268 ErrorSkip (ERR_STRCON_EXPECTED);
271 EmitData ((unsigned char*) SVal, strlen (SVal));
273 if (Tok == TOK_COMMA) {
284 static void DoAutoImport (void)
285 /* Mark unresolved symbols as imported */
287 SetBoolOption (&AutoImport);
292 static void DoBss (void)
293 /* Switch to the BSS segment */
300 static void DoByte (void)
304 if (Tok == TOK_STRCON) {
306 EmitData ((unsigned char*) SVal, strlen (SVal));
309 EmitByte (Expression ());
311 if (Tok != TOK_COMMA) {
315 /* Do smart handling of dangling comma */
316 if (Tok == TOK_SEP) {
317 Error (ERR_UNEXPECTED_EOL);
326 static void DoCase (void)
327 /* Switch the IgnoreCase option */
329 SetBoolOption (&IgnoreCase);
330 IgnoreCase = !IgnoreCase;
335 static void DoCode (void)
336 /* Switch to the code segment */
343 static void DoData (void)
344 /* Switch to the data segment */
351 static void DoDbg (void)
352 /* Add debug information from high level code */
354 static const char* Keys[] = {
362 /* We expect a subkey */
363 if (Tok != TOK_IDENT) {
364 ErrorSkip (ERR_IDENT_EXPECTED);
368 /* Map the following keyword to a number */
369 Key = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
371 /* Skip the subkey */
374 /* Parameters are separated by a comma */
377 /* Check the key and dispatch to a handler */
379 case 0: DbgInfoFile (); break;
380 case 1: DbgInfoLine (); break;
381 case 2: DbgInfoSym (); break;
382 default: ErrorSkip (ERR_SYNTAX); break;
388 static void DoDByt (void)
389 /* Output double bytes */
392 EmitWord (SwapExpr (Expression ()));
393 if (Tok != TOK_COMMA) {
403 static void DoDebugInfo (void)
404 /* Switch debug info on or off */
406 SetBoolOption (&DbgSyms);
411 static void DoDefine (void)
412 /* Define a one line macro */
414 MacDef (MAC_STYLE_DEFINE);
419 static void DoDWord (void)
423 EmitDWord (Expression ());
424 if (Tok != TOK_COMMA) {
434 static void DoEnd (void)
435 /* End of assembly */
442 static void DoEndProc (void)
443 /* Leave a lexical level */
450 static void DoError (void)
453 if (Tok != TOK_STRCON) {
454 ErrorSkip (ERR_STRCON_EXPECTED);
456 Error (ERR_USER, SVal);
463 static void DoExitMacro (void)
464 /* Exit a macro expansion */
466 if (!InMacExpansion ()) {
467 /* We aren't expanding a macro currently */
476 static void DoExport (void)
477 /* Export a symbol */
479 ExportImport (SymExport, 0);
484 static void DoExportZP (void)
485 /* Export a zeropage symbol */
487 ExportImport (SymExport, 1);
492 static void DoFarAddr (void)
493 /* Define far addresses (24 bit) */
496 EmitFarAddr (Expression ());
497 if (Tok != TOK_COMMA) {
507 static void DoFeature (void)
508 /* Switch the Feature option */
512 static const char* Keys[] = {
514 "LABELS_WITHOUT_COLONS",
517 "DOLLAR_IN_IDENTIFIERS",
520 /* Allow a list of comma separated keywords */
523 /* We expect an identifier */
524 if (Tok != TOK_IDENT) {
525 ErrorSkip (ERR_IDENT_EXPECTED);
529 /* Map the keyword to a number */
530 Feature = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
533 ErrorSkip (ERR_ILLEGAL_FEATURE);
537 /* Skip the keyword */
540 /* Switch the feature on */
542 case 0: DollarIsPC = 1; break;
543 case 1: NoColonLabels = 1; break;
544 case 2: LooseStringTerm = 1; break;
545 case 3: AtInIdents = 1; break;
546 case 4: DollarInIdents = 1; break;
547 default: Internal ("Invalid feature: %d", Feature);
550 /* Allow more than one keyword */
551 if (Tok == TOK_COMMA) {
561 static void DoFileOpt (void)
562 /* Insert a file option */
566 /* The option type may be given as a keyword or as a number. */
567 if (Tok == TOK_IDENT) {
569 /* Option given as keyword */
570 static const char* Keys [] = {
571 "AUTHOR", "COMMENT", "COMPILER"
574 /* Map the option to a number */
575 OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
578 ErrorSkip (ERR_OPTION_KEY_EXPECTED);
582 /* Skip the keyword */
585 /* Must be followed by a comma */
588 /* We accept only string options for now */
589 if (Tok != TOK_STRCON) {
590 ErrorSkip (ERR_STRCON_EXPECTED);
594 /* Insert the option */
613 Internal ("Invalid OptNum: %l", OptNum);
622 /* Option given as number */
623 OptNum = ConstExpression ();
624 if (!IsByteRange (OptNum)) {
625 ErrorSkip (ERR_RANGE);
629 /* Must be followed by a comma */
632 /* We accept only string options for now */
633 if (Tok != TOK_STRCON) {
634 ErrorSkip (ERR_STRCON_EXPECTED);
638 /* Insert the option */
639 OptStr ((unsigned char) OptNum, SVal);
648 static void DoGlobal (void)
649 /* Declare a global symbol */
651 ExportImport (SymGlobal, 0);
656 static void DoGlobalZP (void)
657 /* Declare a global zeropage symbol */
659 ExportImport (SymGlobal, 1);
664 static void DoI16 (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 two extension bytes */
671 ExtBytes [AMI_IMM_INDEX] = 2;
677 static void DoI8 (void)
678 /* Switch the index registers to 16 bit mode (assembler only) */
680 if (GetCPU() != CPU_65816) {
681 Error (ERR_816_MODE_ONLY);
683 /* Immidiate mode has one extension byte */
684 ExtBytes [AMI_IMM_INDEX] = 1;
690 static void DoImport (void)
691 /* Import a symbol */
693 ExportImport (SymImport, 0);
698 static void DoImportZP (void)
699 /* Import a zero page symbol */
701 ExportImport (SymImport, 1);
706 static void DoIncBin (void)
707 /* Include a binary file */
709 /* Name must follow */
710 if (Tok != TOK_STRCON) {
711 ErrorSkip (ERR_STRCON_EXPECTED);
713 /* Try to open the file */
714 FILE* F = fopen (SVal, "rb");
716 Error (ERR_CANNOT_OPEN_INCLUDE, SVal, strerror (errno));
718 unsigned char Buf [1024];
720 /* Read chunks and insert them into the output */
721 while ((Count = fread (Buf, 1, sizeof (Buf), F)) > 0) {
722 EmitData (Buf, Count);
724 /* Close the file, ignore errors since it's r/o */
734 static void DoInclude (void)
735 /* Include another file */
737 char Name [MAX_STR_LEN+1];
739 /* Name must follow */
740 if (Tok != TOK_STRCON) {
741 ErrorSkip (ERR_STRCON_EXPECTED);
751 static void DoInvalid (void)
752 /* Handle a token that is invalid here, since it should have been handled on
753 * a much lower level of the expression hierarchy. Getting this sort of token
754 * means that the lower level code has bugs.
755 * This function differs to DoUnexpected in that the latter may be triggered
756 * by the user by using keywords in the wrong location. DoUnexpected is not
757 * an error in the assembler itself, while DoInvalid is.
760 Internal ("Unexpected token: %s", Keyword);
765 static void DoLineCont (void)
766 /* Switch the use of line continuations */
768 SetBoolOption (&LineCont);
773 static void DoList (void)
774 /* Enable/disable the listing */
776 /* Get the setting */
778 SetBoolOption (&List);
780 /* Manage the counter */
790 static void DoListBytes (void)
791 /* Set maximum number of bytes to list for one line */
793 SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
798 static void DoLocalChar (void)
799 /* Define the character that starts local labels */
801 if (Tok != TOK_CHARCON) {
802 ErrorSkip (ERR_CHARCON_EXPECTED);
804 if (IVal != '@' && IVal != '?') {
805 Error (ERR_ILLEGAL_LOCALSTART);
807 LocalStart = (char) IVal;
815 static void DoMacPack (void)
816 /* Insert a macro package */
818 /* Macro package names */
819 static const char* Keys [] = {
826 /* We expect an identifier */
827 if (Tok != TOK_IDENT) {
828 ErrorSkip (ERR_IDENT_EXPECTED);
832 /* Map the keyword to a number */
833 Package = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
836 ErrorSkip (ERR_ILLEGAL_MACPACK);
840 /* Skip the package name */
843 /* Insert the package */
844 InsertMacPack (Package);
849 static void DoMacro (void)
850 /* Start a macro definition */
852 MacDef (MAC_STYLE_CLASSIC);
857 static void DoNull (void)
858 /* Switch to the NULL segment */
865 static void DoOrg (void)
866 /* Start absolute code */
868 long PC = ConstExpression ();
869 if (PC < 0 || PC > 0xFFFF) {
878 static void DoOut (void)
879 /* Output a string */
881 if (Tok != TOK_STRCON) {
882 ErrorSkip (ERR_STRCON_EXPECTED);
884 /* Output the string and be sure to flush the output to keep it in
885 * sync with any error messages if the output is redirected to a file.
887 printf ("%s\n", SVal);
895 static void DoP02 (void)
896 /* Switch to 6502 CPU */
903 static void DoPC02 (void)
904 /* Switch to 65C02 CPU */
911 static void DoP816 (void)
912 /* Switch to 65816 CPU */
919 static void DoPageLength (void)
920 /* Set the page length for the listing */
922 PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
927 static void DoProc (void)
928 /* Start a new lexical scope */
930 if (Tok == TOK_IDENT) {
931 /* The new scope has a name */
932 SymDef (SVal, CurrentPC (), IsZPSeg ());
940 static void DoReloc (void)
941 /* Enter relocatable mode */
948 static void DoRepeat (void)
949 /* Repeat some instruction block */
956 static void DoRes (void)
957 /* Reserve some number of storage bytes */
962 Count = ConstExpression ();
963 if (Count > 0xFFFF || Count < 0) {
964 ErrorSkip (ERR_RANGE);
967 if (Tok == TOK_COMMA) {
969 Val = ConstExpression ();
970 /* We need a byte value here */
971 if (!IsByteRange (Val)) {
972 ErrorSkip (ERR_RANGE);
976 /* Emit constant values */
978 Emit0 ((unsigned char) Val);
982 /* Emit fill fragments */
989 static void DoROData (void)
990 /* Switch to the r/o data segment */
997 static void DoSegment (void)
998 /* Switch to another segment */
1000 static const char* AttrTab [] = {
1001 "ZEROPAGE", "DIRECT",
1005 char Name [sizeof (SVal)];
1008 if (Tok != TOK_STRCON) {
1009 ErrorSkip (ERR_STRCON_EXPECTED);
1012 /* Save the name of the segment and skip it */
1013 strcpy (Name, SVal);
1016 /* Check for an optional segment attribute */
1017 SegType = SEGTYPE_DEFAULT;
1018 if (Tok == TOK_COMMA) {
1020 if (Tok != TOK_IDENT) {
1021 ErrorSkip (ERR_IDENT_EXPECTED);
1023 int Attr = GetSubKey (AttrTab, sizeof (AttrTab) / sizeof (AttrTab [0]));
1029 SegType = SEGTYPE_ZP;
1034 SegType = SEGTYPE_ABS;
1040 SegType = SEGTYPE_FAR;
1044 Error (ERR_ILLEGAL_SEG_ATTR);
1050 /* Set the segment */
1051 UseSeg (Name, SegType);
1057 static void DoSmart (void)
1058 /* Smart mode on/off */
1060 SetBoolOption (&SmartMode);
1065 static void DoSunPlus (void)
1066 /* Switch to the SUNPLUS CPU */
1068 SetCPU (CPU_SUNPLUS);
1073 static void DoUnexpected (void)
1074 /* Got an unexpected keyword */
1076 Error (ERR_UNEXPECTED, Keyword);
1082 static void DoWarning (void)
1085 if (Tok != TOK_STRCON) {
1086 ErrorSkip (ERR_STRCON_EXPECTED);
1088 Warning (WARN_USER, SVal);
1095 static void DoWord (void)
1099 EmitWord (Expression ());
1100 if (Tok != TOK_COMMA) {
1110 static void DoZeropage (void)
1111 /* Switch to the zeropage segment */
1118 /*****************************************************************************/
1120 /*****************************************************************************/
1124 /* Control commands flags */
1126 ccNone = 0x0000, /* No special flags */
1127 ccKeepToken = 0x0001 /* Do not skip the current token */
1130 /* Control command table */
1132 unsigned Flags; /* Flags for this directive */
1133 void (*Handler) (void); /* Command handler */
1135 typedef struct CtrlDesc_ CtrlDesc;
1137 #define PSEUDO_COUNT (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1138 static CtrlDesc CtrlCmdTab [] = {
1141 { ccNone, DoAddr }, /* .ADDR */
1142 { ccNone, DoAlign },
1143 { ccNone, DoASCIIZ },
1144 { ccNone, DoAutoImport },
1145 { ccNone, DoUnexpected }, /* .BLANK */
1150 { ccNone, DoUnexpected, }, /* .CONCAT */
1151 { ccNone, DoUnexpected }, /* .CONST */
1152 { ccNone, DoUnexpected }, /* .CPU */
1156 { ccNone, DoDebugInfo },
1157 { ccNone, DoDefine },
1158 { ccNone, DoUnexpected }, /* .DEFINED */
1159 { ccNone, DoDWord },
1160 { ccKeepToken, DoConditionals }, /* .ELSE */
1161 { ccKeepToken, DoConditionals }, /* .ELSEIF */
1163 { ccKeepToken, DoConditionals }, /* .ENDIF */
1164 { ccNone, DoUnexpected }, /* .ENDMACRO */
1165 { ccNone, DoEndProc },
1166 { ccNone, DoUnexpected }, /* .ENDREPEAT */
1167 { ccNone, DoError },
1168 { ccNone, DoExitMacro },
1169 { ccNone, DoExport },
1170 { ccNone, DoExportZP },
1171 { ccNone, DoFarAddr },
1172 { ccNone, DoFeature },
1173 { ccNone, DoFileOpt },
1174 { ccNone, DoUnexpected }, /* .FORCEWORD */
1175 { ccNone, DoGlobal },
1176 { ccNone, DoGlobalZP },
1179 { ccKeepToken, DoConditionals }, /* .IF */
1180 { ccKeepToken, DoConditionals }, /* .IFBLANK */
1181 { ccKeepToken, DoConditionals }, /* .IFCONST */
1182 { ccKeepToken, DoConditionals }, /* .IFDEF */
1183 { ccKeepToken, DoConditionals }, /* .IFNBLANK */
1184 { ccKeepToken, DoConditionals }, /* .IFNCONST */
1185 { ccKeepToken, DoConditionals }, /* .IFNDEF */
1186 { ccKeepToken, DoConditionals }, /* .IFNREF */
1187 { ccKeepToken, DoConditionals }, /* .IFP02 */
1188 { ccKeepToken, DoConditionals }, /* .IFP816 */
1189 { ccKeepToken, DoConditionals }, /* .IFPC02 */
1190 { ccKeepToken, DoConditionals }, /* .IFREF */
1191 { ccNone, DoImport },
1192 { ccNone, DoImportZP },
1193 { ccNone, DoIncBin },
1194 { ccNone, DoInclude },
1195 { ccNone, DoInvalid }, /* .LEFT */
1196 { ccNone, DoLineCont },
1198 { ccNone, DoListBytes },
1199 { ccNone, DoUnexpected }, /* .LOCAL */
1200 { ccNone, DoLocalChar },
1201 { ccNone, DoMacPack },
1202 { ccNone, DoMacro },
1203 { ccNone, DoUnexpected }, /* .MATCH */
1204 { ccNone, DoInvalid }, /* .MID */
1210 { ccNone, DoPageLength },
1211 { ccNone, DoUnexpected }, /* .PARAMCOUNT */
1214 { ccNone, DoUnexpected }, /* .REFERENCED */
1215 { ccNone, DoReloc },
1216 { ccNone, DoRepeat },
1218 { ccNone, DoInvalid }, /* .RIGHT */
1219 { ccNone, DoROData },
1220 { ccNone, DoSegment },
1221 { ccNone, DoSmart },
1222 { ccNone, DoUnexpected }, /* .STRAT */
1223 { ccNone, DoUnexpected }, /* .STRING */
1224 { ccNone, DoUnexpected }, /* .STRLEN */
1225 { ccNone, DoSunPlus },
1226 { ccNone, DoUnexpected }, /* .TCOUNT */
1227 { ccNone, DoWarning },
1229 { ccNone, DoUnexpected }, /* .XMATCH */
1230 { ccNone, DoZeropage },
1235 /*****************************************************************************/
1237 /*****************************************************************************/
1241 int TokIsPseudo (unsigned Tok)
1242 /* Return true if the given token is a pseudo instruction token */
1244 return (Tok >= TOK_FIRSTPSEUDO && Tok <= TOK_LASTPSEUDO);
1249 void HandlePseudo (void)
1250 /* Handle a pseudo instruction */
1254 /* Calculate the index into the table */
1255 unsigned Index = Tok - TOK_FIRSTPSEUDO;
1258 if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
1259 Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
1260 PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
1262 CHECK (Index < PSEUDO_COUNT);
1264 /* Get the pseudo intruction descriptor */
1265 D = &CtrlCmdTab [Index];
1267 /* Remember the instruction, then skip it if needed */
1268 if ((D->Flags & ccKeepToken) == 0) {
1269 strcpy (Keyword+1, SVal);
1273 /* Call the handler */