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);
81 /*****************************************************************************/
82 /* Helper functions */
83 /*****************************************************************************/
87 static void SetBoolOption (unsigned char* Flag)
88 /* Read a on/off/+/- option and set flag accordingly */
90 static const char* Keys[] = {
95 if (Tok == TOK_PLUS) {
98 } else if (Tok == TOK_MINUS) {
101 } else if (Tok == TOK_IDENT) {
102 /* Map the keyword to a number */
103 switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
104 case 0: *Flag = 0; NextTok (); break;
105 case 1: *Flag = 1; NextTok (); break;
106 default: ErrorSkip (ERR_ONOFF_EXPECTED); break;
108 } else if (Tok == TOK_SEP || Tok == TOK_EOF) {
109 /* Without anything assume switch on */
112 ErrorSkip (ERR_ONOFF_EXPECTED);
118 static void ExportImport (void (*SymFunc) (const char*, int), int ZP)
119 /* Export or import symbols */
122 if (Tok != TOK_IDENT) {
123 ErrorSkip (ERR_IDENT_EXPECTED);
128 if (Tok == TOK_COMMA) {
138 static long IntArg (long Min, long Max)
139 /* Read an integer argument and check a range. Accept the token "unlimited"
140 * and return -1 in this case.
143 if (Tok == TOK_IDENT && strcmp (SVal, "unlimited") == 0) {
147 long Val = ConstExpression ();
148 if (Val < Min || Val > Max) {
158 /*****************************************************************************/
159 /* Handler functions */
160 /*****************************************************************************/
164 static void DoA16 (void)
165 /* Switch the accu to 16 bit mode (assembler only) */
167 if (GetCPU() != CPU_65816) {
168 Error (ERR_816_MODE_ONLY);
170 /* Immidiate mode has two extension bytes */
171 ExtBytes [AMI_IMM_ACCU] = 2;
177 static void DoA8 (void)
178 /* Switch the accu to 8 bit mode (assembler only) */
180 if (GetCPU() != CPU_65816) {
181 Error (ERR_816_MODE_ONLY);
183 /* Immidiate mode has one extension byte */
184 ExtBytes [AMI_IMM_ACCU] = 1;
190 static void DoAddr (void)
191 /* Define addresses */
194 if (GetCPU() == CPU_65816) {
195 EmitWord (ForceWordExpr (Expression ()));
197 /* Do a range check */
198 EmitWord (Expression ());
200 if (Tok != TOK_COMMA) {
210 static void DoAlign (void)
211 /* Align the PC to some boundary */
217 /* Read the alignment value */
218 Align = ConstExpression ();
219 if (Align <= 0 || Align > 0x10000) {
220 ErrorSkip (ERR_RANGE);
224 /* Optional value follows */
225 if (Tok == TOK_COMMA) {
227 Val = ConstExpression ();
228 /* We need a byte value here */
229 if (!IsByteRange (Val)) {
230 ErrorSkip (ERR_RANGE);
237 /* Check if the alignment is a power of two */
238 Bit = BitFind (Align);
239 if (Align != (0x01L << Bit)) {
242 SegAlign (Bit, (int) Val);
248 static void DoASCIIZ (void)
249 /* Define text with a zero terminator */
252 if (Tok != TOK_STRCON) {
253 ErrorSkip (ERR_STRCON_EXPECTED);
256 EmitData (SVal, strlen (SVal));
258 if (Tok == TOK_COMMA) {
269 static void DoAutoImport (void)
270 /* Mark unresolved symbols as imported */
272 SetBoolOption (&AutoImport);
277 static void DoBss (void)
278 /* Switch to the BSS segment */
285 static void DoByte (void)
289 if (Tok == TOK_STRCON) {
291 EmitData (SVal, strlen (SVal));
294 EmitByte (Expression ());
296 if (Tok != TOK_COMMA) {
300 /* Do smart handling of dangling comma */
301 if (Tok == TOK_SEP) {
302 Error (ERR_UNEXPECTED_EOL);
311 static void DoCase (void)
312 /* Switch the IgnoreCase option */
314 SetBoolOption (&IgnoreCase);
315 IgnoreCase = !IgnoreCase;
320 static void DoCode (void)
321 /* Switch to the code segment */
328 static void DoData (void)
329 /* Switch to the data segment */
336 static void DoDByt (void)
337 /* Output double bytes */
340 EmitWord (SwapExpr (Expression ()));
341 if (Tok != TOK_COMMA) {
351 static void DoDebugInfo (void)
352 /* Switch debug info on or off */
354 SetBoolOption (&DbgSyms);
359 static void DoDefine (void)
360 /* Define a one line macro */
362 MacDef (MAC_STYLE_DEFINE);
367 static void DoDWord (void)
371 EmitDWord (Expression ());
372 if (Tok != TOK_COMMA) {
382 static void DoEnd (void)
383 /* End of assembly */
390 static void DoEndProc (void)
391 /* Leave a lexical level */
398 static void DoError (void)
401 if (Tok == TOK_STRCON) {
402 ErrorSkip (ERR_STRCON_EXPECTED);
404 Error (ERR_USER, SVal);
411 static void DoExitMacro (void)
412 /* Exit a macro expansion */
414 if (!InMacExpansion ()) {
415 /* We aren't expanding a macro currently */
424 static void DoExport (void)
425 /* Export a symbol */
427 ExportImport (SymExport, 0);
432 static void DoExportZP (void)
433 /* Export a zeropage symbol */
435 ExportImport (SymExport, 1);
440 static void DoFarAddr (void)
441 /* Define far addresses (24 bit) */
444 EmitFarAddr (Expression ());
445 if (Tok != TOK_COMMA) {
455 static void DoFeature (void)
456 /* Switch the Feature option */
460 static const char* Keys[] = {
462 "LABELS_WITHOUT_COLONS",
465 "DOLLAR_IN_IDENTIFIERS",
468 /* Allow a list of comma separated keywords */
471 /* We expect an identifier */
472 if (Tok != TOK_IDENT) {
473 ErrorSkip (ERR_IDENT_EXPECTED);
477 /* Map the keyword to a number */
478 Feature = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
481 ErrorSkip (ERR_ILLEGAL_FEATURE);
485 /* Skip the keyword */
488 /* Switch the feature on */
490 case 0: DollarIsPC = 1; break;
491 case 1: NoColonLabels = 1; break;
492 case 2: LooseStringTerm = 1; break;
493 case 3: AtInIdents = 1; break;
494 case 4: DollarInIdents = 1; break;
495 default: Internal ("Invalid feature: %d", Feature);
498 /* Allow more than one keyword */
499 if (Tok == TOK_COMMA) {
509 static void DoFileOpt (void)
510 /* Insert a file option */
514 /* The option type may be given as a keyword or as a number. */
515 if (Tok == TOK_IDENT) {
517 /* Option given as keyword */
518 static const char* Keys [] = {
519 "AUTHOR", "COMMENT", "COMPILER"
522 /* Map the option to a number */
523 OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
526 ErrorSkip (ERR_OPTION_KEY_EXPECTED);
530 /* Skip the keyword */
533 /* Must be followed by a comma */
536 /* We accept only string options for now */
537 if (Tok != TOK_STRCON) {
538 ErrorSkip (ERR_STRCON_EXPECTED);
542 /* Insert the option */
561 Internal ("Invalid OptNum: %l", OptNum);
570 /* Option given as number */
571 OptNum = ConstExpression ();
572 if (!IsByteRange (OptNum)) {
573 ErrorSkip (ERR_RANGE);
577 /* Must be followed by a comma */
580 /* We accept only string options for now */
581 if (Tok != TOK_STRCON) {
582 ErrorSkip (ERR_STRCON_EXPECTED);
586 /* Insert the option */
587 OptStr ((unsigned char) OptNum, SVal);
596 static void DoGlobal (void)
597 /* Declare a global symbol */
599 ExportImport (SymGlobal, 0);
604 static void DoGlobalZP (void)
605 /* Declare a global zeropage symbol */
607 ExportImport (SymGlobal, 1);
612 static void DoI16 (void)
613 /* Switch the index registers to 16 bit mode (assembler only) */
615 if (GetCPU() != CPU_65816) {
616 Error (ERR_816_MODE_ONLY);
618 /* Immidiate mode has two extension bytes */
619 ExtBytes [AMI_IMM_INDEX] = 2;
625 static void DoI8 (void)
626 /* Switch the index registers to 16 bit mode (assembler only) */
628 if (GetCPU() != CPU_65816) {
629 Error (ERR_816_MODE_ONLY);
631 /* Immidiate mode has one extension byte */
632 ExtBytes [AMI_IMM_INDEX] = 1;
638 static void DoImport (void)
639 /* Import a symbol */
641 ExportImport (SymImport, 0);
646 static void DoImportZP (void)
647 /* Import a zero page symbol */
649 ExportImport (SymImport, 1);
654 static void DoIncBin (void)
655 /* Include a binary file */
657 /* Name must follow */
658 if (Tok != TOK_STRCON) {
659 ErrorSkip (ERR_STRCON_EXPECTED);
661 /* Try to open the file */
662 FILE* F = fopen (SVal, "rb");
664 Error (ERR_CANNOT_OPEN_INCLUDE, SVal, strerror (errno));
666 unsigned char Buf [1024];
668 /* Read chunks and insert them into the output */
669 while ((Count = fread (Buf, 1, sizeof (Buf), F)) > 0) {
670 EmitData (Buf, Count);
672 /* Close the file, ignore errors since it's r/o */
682 static void DoInclude (void)
683 /* Include another file */
685 char Name [MAX_STR_LEN+1];
687 /* Name must follow */
688 if (Tok != TOK_STRCON) {
689 ErrorSkip (ERR_STRCON_EXPECTED);
699 static void DoLineCont (void)
700 /* Switch the use of line continuations */
702 SetBoolOption (&LineCont);
707 static void DoList (void)
708 /* Enable/disable the listing */
710 /* Get the setting */
712 SetBoolOption (&List);
714 /* Manage the counter */
724 static void DoListBytes (void)
725 /* Set maximum number of bytes to list for one line */
727 SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES));
732 static void DoLocalChar (void)
733 /* Define the character that starts local labels */
735 if (Tok != TOK_CHARCON) {
736 ErrorSkip (ERR_CHARCON_EXPECTED);
738 if (IVal != '@' && IVal != '?') {
739 Error (ERR_ILLEGAL_LOCALSTART);
741 LocalStart = (char) IVal;
749 static void DoMacPack (void)
750 /* Insert a macro package */
752 /* Macro package names */
753 static const char* Keys [] = {
760 /* We expect an identifier */
761 if (Tok != TOK_IDENT) {
762 ErrorSkip (ERR_IDENT_EXPECTED);
766 /* Map the keyword to a number */
767 Package = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
770 ErrorSkip (ERR_ILLEGAL_MACPACK);
774 /* Skip the package name */
777 /* Insert the package */
778 InsertMacPack (Package);
783 static void DoMacro (void)
784 /* Start a macro definition */
786 MacDef (MAC_STYLE_CLASSIC);
791 static void DoMid (void)
792 /* Handle .MID - this should never happen, since the keyword is actually
793 * handled on a much lower level of the expression hierarchy.
796 Internal ("Unexpected token: .MID");
801 static void DoNull (void)
802 /* Switch to the NULL segment */
809 static void DoOrg (void)
810 /* Start absolute code */
812 long PC = ConstExpression ();
813 if (PC < 0 || PC > 0xFFFF) {
822 static void DoOut (void)
823 /* Output a string */
825 if (Tok != TOK_STRCON) {
826 ErrorSkip (ERR_STRCON_EXPECTED);
828 /* Output the string and be sure to flush the output to keep it in
829 * sync with any error messages if the output is redirected to a file.
831 printf ("%s\n", SVal);
839 static void DoP02 (void)
840 /* Switch to 6502 CPU */
847 static void DoPC02 (void)
848 /* Switch to 65C02 CPU */
855 static void DoP816 (void)
856 /* Switch to 65816 CPU */
863 static void DoPageLength (void)
864 /* Set the page length for the listing */
866 PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN);
871 static void DoProc (void)
872 /* Start a new lexical scope */
874 if (Tok == TOK_IDENT) {
875 /* The new scope has a name */
876 SymDef (SVal, CurrentPC (), IsZPSeg ());
884 static void DoReloc (void)
885 /* Enter relocatable mode */
892 static void DoRepeat (void)
893 /* Repeat some instruction block */
895 ErrorSkip (ERR_NOT_IMPLEMENTED);
900 static void DoRes (void)
901 /* Reserve some number of storage bytes */
906 Count = ConstExpression ();
907 if (Count > 0xFFFF || Count < 0) {
908 ErrorSkip (ERR_RANGE);
911 if (Tok == TOK_COMMA) {
913 Val = ConstExpression ();
914 /* We need a byte value here */
915 if (!IsByteRange (Val)) {
916 ErrorSkip (ERR_RANGE);
920 /* Emit constant values */
922 Emit0 ((unsigned char) Val);
926 /* Emit fill fragments */
933 static void DoROData (void)
934 /* Switch to the r/o data segment */
941 static void DoSegment (void)
942 /* Switch to another segment */
944 static const char* AttrTab [] = {
945 "ZEROPAGE", "DIRECT",
949 char Name [sizeof (SVal)];
952 if (Tok != TOK_STRCON) {
953 ErrorSkip (ERR_STRCON_EXPECTED);
956 /* Save the name of the segment and skip it */
960 /* Check for an optional segment attribute */
961 SegType = SEGTYPE_DEFAULT;
962 if (Tok == TOK_COMMA) {
964 if (Tok != TOK_IDENT) {
965 ErrorSkip (ERR_IDENT_EXPECTED);
967 int Attr = GetSubKey (AttrTab, sizeof (AttrTab) / sizeof (AttrTab [0]));
973 SegType = SEGTYPE_ZP;
978 SegType = SEGTYPE_ABS;
984 SegType = SEGTYPE_FAR;
988 Error (ERR_ILLEGAL_SEG_ATTR);
994 /* Set the segment */
995 UseSeg (Name, SegType);
1001 static void DoSmart (void)
1002 /* Smart mode on/off */
1004 SetBoolOption (&SmartMode);
1009 static void DoSunPlus (void)
1010 /* Switch to the SUNPLUS CPU */
1012 SetCPU (CPU_SUNPLUS);
1017 static void DoUnexpected (void)
1018 /* Got an unexpected keyword */
1020 Error (ERR_UNEXPECTED, Keyword);
1026 static void DoWord (void)
1030 EmitWord (Expression ());
1031 if (Tok != TOK_COMMA) {
1041 static void DoZeropage (void)
1042 /* Switch to the zeropage segment */
1049 /*****************************************************************************/
1051 /*****************************************************************************/
1055 /* Control commands flags */
1057 ccNone = 0x0000, /* No special flags */
1058 ccKeepToken = 0x0001 /* Do not skip the current token */
1061 /* Control command table */
1063 unsigned Flags; /* Flags for this directive */
1064 void (*Handler) (void); /* Command handler */
1066 typedef struct CtrlDesc_ CtrlDesc;
1068 #define PSEUDO_COUNT (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
1069 static CtrlDesc CtrlCmdTab [] = {
1072 { ccNone, DoAddr }, /* .ADDR */
1073 { ccNone, DoAlign },
1074 { ccNone, DoASCIIZ },
1075 { ccNone, DoAutoImport },
1076 { ccNone, DoUnexpected }, /* .BLANK */
1081 { ccNone, DoUnexpected, }, /* .CONCAT */
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 */