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"
60 /*****************************************************************************/
62 /*****************************************************************************/
66 /* Keyword we're about to handle */
67 static char Keyword [sizeof (SVal)+1] = ".";
71 /*****************************************************************************/
73 /*****************************************************************************/
77 static void DoUnexpected (void);
78 /* Got an unexpected keyword */
80 static void DoInvalid (void);
81 /* Handle a token that is invalid here, since it should have been handled on
82 * a much lower level of the expression hierarchy. Getting this sort of token
83 * means that the lower level code has bugs.
84 * This function differs to DoUnexpected in that the latter may be triggered
85 * by the user by using keywords in the wrong location. DoUnexpected is not
86 * an error in the assembler itself, while DoInvalid is.
91 /*****************************************************************************/
92 /* Helper functions */
93 /*****************************************************************************/
97 static void SetBoolOption (unsigned char* Flag)
98 /* Read a on/off/+/- option and set flag accordingly */
100 static const char* Keys[] = {
105 if (Tok == TOK_PLUS) {
108 } else if (Tok == TOK_MINUS) {
111 } else if (Tok == TOK_IDENT) {
112 /* Map the keyword to a number */
113 switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
114 case 0: *Flag = 0; NextTok (); break;
115 case 1: *Flag = 1; NextTok (); break;
116 default: ErrorSkip (ERR_ONOFF_EXPECTED); break;
118 } else if (Tok == TOK_SEP || Tok == TOK_EOF) {
119 /* Without anything assume switch on */
122 ErrorSkip (ERR_ONOFF_EXPECTED);
128 static void ExportImport (void (*SymFunc) (const char*, int), int ZP)
129 /* Export or import symbols */
132 if (Tok != TOK_IDENT) {
133 ErrorSkip (ERR_IDENT_EXPECTED);
138 if (Tok == TOK_COMMA) {
148 static long IntArg (long Min, long Max)
149 /* Read an integer argument and check a range. Accept the token "unlimited"
150 * and return -1 in this case.
153 if (Tok == TOK_IDENT && strcmp (SVal, "unlimited") == 0) {
157 long Val = ConstExpression ();
158 if (Val < Min || Val > Max) {
168 /*****************************************************************************/
169 /* Handler functions */
170 /*****************************************************************************/
174 static void DoA16 (void)
175 /* Switch the accu to 16 bit mode (assembler only) */
177 if (GetCPU() != CPU_65816) {
178 Error (ERR_816_MODE_ONLY);
180 /* Immidiate mode has two extension bytes */
181 ExtBytes [AMI_IMM_ACCU] = 2;
187 static void DoA8 (void)
188 /* Switch the accu to 8 bit mode (assembler only) */
190 if (GetCPU() != CPU_65816) {
191 Error (ERR_816_MODE_ONLY);
193 /* Immidiate mode has one extension byte */
194 ExtBytes [AMI_IMM_ACCU] = 1;
200 static void DoAddr (void)
201 /* Define addresses */
204 if (GetCPU() == CPU_65816) {
205 EmitWord (ForceWordExpr (Expression ()));
207 /* Do a range check */
208 EmitWord (Expression ());
210 if (Tok != TOK_COMMA) {
220 static void DoAlign (void)
221 /* Align the PC to some boundary */
227 /* Read the alignment value */
228 Align = ConstExpression ();
229 if (Align <= 0 || Align > 0x10000) {
230 ErrorSkip (ERR_RANGE);
234 /* Optional value follows */
235 if (Tok == TOK_COMMA) {
237 Val = ConstExpression ();
238 /* We need a byte value here */
239 if (!IsByteRange (Val)) {
240 ErrorSkip (ERR_RANGE);
247 /* Check if the alignment is a power of two */
248 Bit = BitFind (Align);
249 if (Align != (0x01L << Bit)) {
252 SegAlign (Bit, (int) Val);
258 static void DoASCIIZ (void)
259 /* Define text with a zero terminator */
262 if (Tok != TOK_STRCON) {
263 ErrorSkip (ERR_STRCON_EXPECTED);
266 EmitData (SVal, strlen (SVal));
268 if (Tok == TOK_COMMA) {
279 static void DoAutoImport (void)
280 /* Mark unresolved symbols as imported */
282 SetBoolOption (&AutoImport);
287 static void DoBss (void)
288 /* Switch to the BSS segment */
295 static void DoByte (void)
299 if (Tok == TOK_STRCON) {
301 EmitData (SVal, strlen (SVal));
304 EmitByte (Expression ());
306 if (Tok != TOK_COMMA) {
310 /* Do smart handling of dangling comma */
311 if (Tok == TOK_SEP) {
312 Error (ERR_UNEXPECTED_EOL);
321 static void DoCase (void)
322 /* Switch the IgnoreCase option */
324 SetBoolOption (&IgnoreCase);
325 IgnoreCase = !IgnoreCase;
330 static void DoCode (void)
331 /* Switch to the code segment */
338 static void DoData (void)
339 /* Switch to the data segment */
346 static void DoDByt (void)
347 /* Output double bytes */
350 EmitWord (SwapExpr (Expression ()));
351 if (Tok != TOK_COMMA) {
361 static void DoDebugInfo (void)
362 /* Switch debug info on or off */
364 SetBoolOption (&DbgSyms);
369 static void DoDefine (void)
370 /* Define a one line macro */
372 MacDef (MAC_STYLE_DEFINE);
377 static void DoDWord (void)
381 EmitDWord (Expression ());
382 if (Tok != TOK_COMMA) {
392 static void DoEnd (void)
393 /* End of assembly */
400 static void DoEndProc (void)
401 /* Leave a lexical level */
408 static void DoError (void)
411 if (Tok == TOK_STRCON) {
412 ErrorSkip (ERR_STRCON_EXPECTED);
414 Error (ERR_USER, SVal);
421 static void DoExitMacro (void)
422 /* Exit a macro expansion */
424 if (!InMacExpansion ()) {
425 /* We aren't expanding a macro currently */
434 static void DoExport (void)
435 /* Export a symbol */
437 ExportImport (SymExport, 0);
442 static void DoExportZP (void)
443 /* Export a zeropage symbol */
445 ExportImport (SymExport, 1);
450 static void DoFarAddr (void)
451 /* Define far addresses (24 bit) */
454 EmitFarAddr (Expression ());
455 if (Tok != TOK_COMMA) {
465 static void DoFeature (void)
466 /* Switch the Feature option */
470 static const char* Keys[] = {
472 "LABELS_WITHOUT_COLONS",
475 "DOLLAR_IN_IDENTIFIERS",
478 /* Allow a list of comma separated keywords */
481 /* We expect an identifier */
482 if (Tok != TOK_IDENT) {
483 ErrorSkip (ERR_IDENT_EXPECTED);
487 /* Map the keyword to a number */
488 Feature = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
491 ErrorSkip (ERR_ILLEGAL_FEATURE);
495 /* Skip the keyword */
498 /* Switch the feature on */
500 case 0: DollarIsPC = 1; break;
501 case 1: NoColonLabels = 1; break;
502 case 2: LooseStringTerm = 1; break;
503 case 3: AtInIdents = 1; break;
504 case 4: DollarInIdents = 1; break;
505 default: Internal ("Invalid feature: %d", Feature);
508 /* Allow more than one keyword */
509 if (Tok == TOK_COMMA) {
519 static void DoFileOpt (void)
520 /* Insert a file option */
524 /* The option type may be given as a keyword or as a number. */
525 if (Tok == TOK_IDENT) {
527 /* Option given as keyword */
528 static const char* Keys [] = {
529 "AUTHOR", "COMMENT", "COMPILER"
532 /* Map the option to a number */
533 OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
536 ErrorSkip (ERR_OPTION_KEY_EXPECTED);
540 /* Skip the keyword */
543 /* Must be followed by a comma */
546 /* We accept only string options for now */
547 if (Tok != TOK_STRCON) {
548 ErrorSkip (ERR_STRCON_EXPECTED);
552 /* Insert the option */
571 Internal ("Invalid OptNum: %l", OptNum);
580 /* Option given as number */
581 OptNum = ConstExpression ();
582 if (!IsByteRange (OptNum)) {
583 ErrorSkip (ERR_RANGE);
587 /* Must be followed by a comma */
590 /* We accept only string options for now */
591 if (Tok != TOK_STRCON) {
592 ErrorSkip (ERR_STRCON_EXPECTED);
596 /* Insert the option */
597 OptStr ((unsigned char) OptNum, SVal);
606 static void DoGlobal (void)
607 /* Declare a global symbol */
609 ExportImport (SymGlobal, 0);
614 static void DoGlobalZP (void)
615 /* Declare a global zeropage symbol */
617 ExportImport (SymGlobal, 1);
622 static void DoI16 (void)
623 /* Switch the index registers to 16 bit mode (assembler only) */
625 if (GetCPU() != CPU_65816) {
626 Error (ERR_816_MODE_ONLY);
628 /* Immidiate mode has two extension bytes */
629 ExtBytes [AMI_IMM_INDEX] = 2;
635 static void DoI8 (void)
636 /* Switch the index registers to 16 bit mode (assembler only) */
638 if (GetCPU() != CPU_65816) {
639 Error (ERR_816_MODE_ONLY);
641 /* Immidiate mode has one extension byte */
642 ExtBytes [AMI_IMM_INDEX] = 1;
648 static void DoImport (void)
649 /* Import a symbol */
651 ExportImport (SymImport, 0);
656 static void DoImportZP (void)
657 /* Import a zero page symbol */
659 ExportImport (SymImport, 1);
664 static void DoIncBin (void)
665 /* Include a binary file */
667 /* Name must follow */
668 if (Tok != TOK_STRCON) {
669 ErrorSkip (ERR_STRCON_EXPECTED);
671 /* Try to open the file */
672 FILE* F = fopen (SVal, "rb");
674 Error (ERR_CANNOT_OPEN_INCLUDE, SVal, strerror (errno));
676 unsigned char Buf [1024];
678 /* Read chunks and insert them into the output */
679 while ((Count = fread (Buf, 1, sizeof (Buf), F)) > 0) {
680 EmitData (Buf, Count);
682 /* Close the file, ignore errors since it's r/o */
692 static void DoInclude (void)
693 /* Include another file */
695 char Name [MAX_STR_LEN+1];
697 /* Name must follow */
698 if (Tok != TOK_STRCON) {
699 ErrorSkip (ERR_STRCON_EXPECTED);
709 static void DoInvalid (void)
710 /* Handle a token that is invalid here, since it should have been handled on
711 * a much lower level of the expression hierarchy. Getting this sort of token
712 * means that the lower level code has bugs.
713 * This function differs to DoUnexpected in that the latter may be triggered
714 * by the user by using keywords in the wrong location. DoUnexpected is not
715 * an error in the assembler itself, while DoInvalid is.
718 Internal ("Unexpected token: %s", Keyword);
723 static void DoLineCont (void)
724 /* Switch the use of line continuations */
726 SetBoolOption (&LineCont);
731 static void DoList (void)
732 /* Enable/disable the listing */
734 /* Get the setting */
736 SetBoolOption (&List);
738 /* Manage the counter */
748 static void DoListBytes (void)
749 /* Set maximum number of bytes to list for one line */
751 SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
756 static void DoLocalChar (void)
757 /* Define the character that starts local labels */
759 if (Tok != TOK_CHARCON) {
760 ErrorSkip (ERR_CHARCON_EXPECTED);
762 if (IVal != '@' && IVal != '?') {
763 Error (ERR_ILLEGAL_LOCALSTART);
765 LocalStart = (char) IVal;
773 static void DoMacPack (void)
774 /* Insert a macro package */
776 /* Macro package names */
777 static const char* Keys [] = {
784 /* We expect an identifier */
785 if (Tok != TOK_IDENT) {
786 ErrorSkip (ERR_IDENT_EXPECTED);
790 /* Map the keyword to a number */
791 Package = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
794 ErrorSkip (ERR_ILLEGAL_MACPACK);
798 /* Skip the package name */
801 /* Insert the package */
802 InsertMacPack (Package);
807 static void DoMacro (void)
808 /* Start a macro definition */
810 MacDef (MAC_STYLE_CLASSIC);
815 static void DoNull (void)
816 /* Switch to the NULL segment */
823 static void DoOrg (void)
824 /* Start absolute code */
826 long PC = ConstExpression ();
827 if (PC < 0 || PC > 0xFFFF) {
836 static void DoOut (void)
837 /* Output a string */
839 if (Tok != TOK_STRCON) {
840 ErrorSkip (ERR_STRCON_EXPECTED);
842 /* Output the string and be sure to flush the output to keep it in
843 * sync with any error messages if the output is redirected to a file.
845 printf ("%s\n", SVal);
853 static void DoP02 (void)
854 /* Switch to 6502 CPU */
861 static void DoPC02 (void)
862 /* Switch to 65C02 CPU */
869 static void DoP816 (void)
870 /* Switch to 65816 CPU */
877 static void DoPageLength (void)
878 /* Set the page length for the listing */
880 PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
885 static void DoProc (void)
886 /* Start a new lexical scope */
888 if (Tok == TOK_IDENT) {
889 /* The new scope has a name */
890 SymDef (SVal, CurrentPC (), IsZPSeg ());
898 static void DoReloc (void)
899 /* Enter relocatable mode */
906 static void DoRepeat (void)
907 /* Repeat some instruction block */
909 ErrorSkip (ERR_NOT_IMPLEMENTED);
914 static void DoRes (void)
915 /* Reserve some number of storage bytes */
920 Count = ConstExpression ();
921 if (Count > 0xFFFF || Count < 0) {
922 ErrorSkip (ERR_RANGE);
925 if (Tok == TOK_COMMA) {
927 Val = ConstExpression ();
928 /* We need a byte value here */
929 if (!IsByteRange (Val)) {
930 ErrorSkip (ERR_RANGE);
934 /* Emit constant values */
936 Emit0 ((unsigned char) Val);
940 /* Emit fill fragments */
947 static void DoROData (void)
948 /* Switch to the r/o data segment */
955 static void DoSegment (void)
956 /* Switch to another segment */
958 static const char* AttrTab [] = {
959 "ZEROPAGE", "DIRECT",
963 char Name [sizeof (SVal)];
966 if (Tok != TOK_STRCON) {
967 ErrorSkip (ERR_STRCON_EXPECTED);
970 /* Save the name of the segment and skip it */
974 /* Check for an optional segment attribute */
975 SegType = SEGTYPE_DEFAULT;
976 if (Tok == TOK_COMMA) {
978 if (Tok != TOK_IDENT) {
979 ErrorSkip (ERR_IDENT_EXPECTED);
981 int Attr = GetSubKey (AttrTab, sizeof (AttrTab) / sizeof (AttrTab [0]));
987 SegType = SEGTYPE_ZP;
992 SegType = SEGTYPE_ABS;
998 SegType = SEGTYPE_FAR;
1002 Error (ERR_ILLEGAL_SEG_ATTR);
1008 /* Set the segment */
1009 UseSeg (Name, SegType);
1015 static void DoSmart (void)
1016 /* Smart mode on/off */
1018 SetBoolOption (&SmartMode);
1023 static void DoSunPlus (void)
1024 /* Switch to the SUNPLUS CPU */
1026 SetCPU (CPU_SUNPLUS);
1031 static void DoUnexpected (void)
1032 /* Got an unexpected keyword */
1034 Error (ERR_UNEXPECTED, Keyword);
1040 static void DoWord (void)
1044 EmitWord (Expression ());
1045 if (Tok != TOK_COMMA) {
1055 static void DoZeropage (void)
1056 /* Switch to the zeropage segment */
1063 /*****************************************************************************/
1065 /*****************************************************************************/
1069 /* Control commands flags */
1071 ccNone = 0x0000, /* No special flags */
1072 ccKeepToken = 0x0001 /* Do not skip the current token */
1075 /* Control command table */
1077 unsigned Flags; /* Flags for this directive */
1078 void (*Handler) (void); /* Command handler */
1080 typedef struct CtrlDesc_ CtrlDesc;
1082 #define PSEUDO_COUNT (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1083 static CtrlDesc CtrlCmdTab [] = {
1086 { ccNone, DoAddr }, /* .ADDR */
1087 { ccNone, DoAlign },
1088 { ccNone, DoASCIIZ },
1089 { ccNone, DoAutoImport },
1090 { ccNone, DoUnexpected }, /* .BLANK */
1095 { ccNone, DoUnexpected, }, /* .CONCAT */
1096 { ccNone, DoUnexpected }, /* .CONST */
1097 { ccNone, DoUnexpected }, /* .CPU */
1100 { ccNone, DoDebugInfo },
1101 { ccNone, DoDefine },
1102 { ccNone, DoUnexpected }, /* .DEFINED */
1103 { ccNone, DoDWord },
1104 { ccKeepToken, DoConditionals }, /* .ELSE */
1105 { ccKeepToken, DoConditionals }, /* .ELSEIF */
1107 { ccKeepToken, DoConditionals }, /* .ENDIF */
1108 { ccNone, DoUnexpected }, /* .ENDMACRO */
1109 { ccNone, DoEndProc },
1110 { ccNone, DoUnexpected }, /* .ENDREPEAT */
1111 { ccNone, DoError },
1112 { ccNone, DoExitMacro },
1113 { ccNone, DoExport },
1114 { ccNone, DoExportZP },
1115 { ccNone, DoFarAddr },
1116 { ccNone, DoFeature },
1117 { ccNone, DoFileOpt },
1118 { ccNone, DoGlobal },
1119 { ccNone, DoGlobalZP },
1122 { ccKeepToken, DoConditionals }, /* .IF */
1123 { ccKeepToken, DoConditionals }, /* .IFBLANK */
1124 { ccKeepToken, DoConditionals }, /* .IFCONST */
1125 { ccKeepToken, DoConditionals }, /* .IFDEF */
1126 { ccKeepToken, DoConditionals }, /* .IFNBLANK */
1127 { ccKeepToken, DoConditionals }, /* .IFNCONST */
1128 { ccKeepToken, DoConditionals }, /* .IFNDEF */
1129 { ccKeepToken, DoConditionals }, /* .IFNREF */
1130 { ccKeepToken, DoConditionals }, /* .IFP02 */
1131 { ccKeepToken, DoConditionals }, /* .IFP816 */
1132 { ccKeepToken, DoConditionals }, /* .IFPC02 */
1133 { ccKeepToken, DoConditionals }, /* .IFREF */
1134 { ccNone, DoImport },
1135 { ccNone, DoImportZP },
1136 { ccNone, DoIncBin },
1137 { ccNone, DoInclude },
1138 { ccNone, DoInvalid }, /* .LEFT */
1139 { ccNone, DoLineCont },
1141 { ccNone, DoListBytes },
1142 { ccNone, DoUnexpected }, /* .LOCAL */
1143 { ccNone, DoLocalChar },
1144 { ccNone, DoMacPack },
1145 { ccNone, DoMacro },
1146 { ccNone, DoUnexpected }, /* .MATCH */
1147 { ccNone, DoInvalid }, /* .MID */
1153 { ccNone, DoPageLength },
1154 { ccNone, DoUnexpected }, /* .PARAMCOUNT */
1157 { ccNone, DoUnexpected }, /* .REFERENCED */
1158 { ccNone, DoReloc },
1159 { ccNone, DoRepeat },
1161 { ccNone, DoInvalid }, /* .RIGHT */
1162 { ccNone, DoROData },
1163 { ccNone, DoSegment },
1164 { ccNone, DoSmart },
1165 { ccNone, DoUnexpected }, /* .STRING */
1166 { ccNone, DoSunPlus },
1168 { ccNone, DoUnexpected }, /* .XMATCH */
1169 { ccNone, DoZeropage },
1174 /*****************************************************************************/
1176 /*****************************************************************************/
1180 int TokIsPseudo (unsigned Tok)
1181 /* Return true if the given token is a pseudo instruction token */
1183 return (Tok >= TOK_FIRSTPSEUDO && Tok <= TOK_LASTPSEUDO);
1188 void HandlePseudo (void)
1189 /* Handle a pseudo instruction */
1193 /* Calculate the index into the table */
1194 unsigned Index = Tok - TOK_FIRSTPSEUDO;
1197 if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
1198 Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
1199 PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
1201 CHECK (Index < PSEUDO_COUNT);
1203 /* Get the pseudo intruction descriptor */
1204 D = &CtrlCmdTab [Index];
1206 /* Remember the instruction, then skip it if needed */
1207 if ((D->Flags & ccKeepToken) == 0) {
1208 strcpy (Keyword+1, SVal);
1212 /* Call the handler */