2 // RMAC - Renamed Macro Assembler for all Atari computers
3 // MACRO.C - Macro Definition and Invocation
4 // Copyright (C) 199x Landon Dyer, 2011-2021 Reboot and Friends
5 // RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986
6 // Source utilised with the kind permission of Landon Dyer
20 LONG curuniq; // Current macro's unique number
21 int macnum; // Unique number for macro definition
23 static LONG macuniq; // Unique-per-macro number
24 static SYM * curmac; // Macro currently being defined
25 static uint32_t argno; // Formal argument count
26 LONG reptuniq; // Unique-per-rept number
28 static LLIST * firstrpt; // First .rept line
29 static LLIST * nextrpt; // Last .rept line
30 int rptlevel; // .rept nesting level
32 // Function prototypes
33 static int KWMatch(char *, char *);
34 static int LNCatch(int (*)(), char *);
38 // Initialize macro processor
50 // -- pop any intervening include files and repeat blocks;
51 // -- restore argument stack;
56 WARNING(!!! Bad macro exiting !!!)
58 This is a problem. Currently, the argument logic just keeps the current
59 arguments and doesn't save anything if a new macro is called in the middle
60 of another (nested macros). Need to fix that somehow.
62 Is this still true, now that we have IMACROs with TOKENSTREAMs in them? Need to
63 check it out for sure...!
65 // Pop intervening include files and .rept blocks
66 while (cur_inobj != NULL && cur_inobj->in_type != SRC_IMACRO)
69 if (cur_inobj == NULL)
70 fatal("too many ENDMs");
74 // o old unique number
75 // ...and then pop the macro.
77 IMACRO * imacro = cur_inobj->inobj.imacro;
78 curuniq = imacro->im_olduniq;
80 DEBUG { printf("ExitMacro: nargs = %d\n", imacro->im_nargs); }
87 // Add a formal argument to a macro definition
89 int defmac2(char * argname)
91 if (lookup(argname, MACARG, (int)curmac->sattr) != NULL)
92 return error("multiple formal argument definition");
94 SYM * arg = NewSymbol(argname, MACARG, (int)curmac->sattr);
95 arg->svalue = argno++;
102 // Add a line to a macro definition; also print lines to listing file (if
103 // enabled). The last line of the macro (containing .endm) is not included in
104 // the macro. A label on that line will be lost.
105 // notEndFlag is -1 for all lines but the last one (.endm), when it is 0.
107 int defmac1(char * ln, int notEndFlag)
111 listeol(); // Flush previous source line
112 lstout('.'); // Mark macro definition with period
117 if (curmac->lineList == NULL)
119 curmac->lineList = malloc(sizeof(LLIST));
120 curmac->lineList->next = NULL;
121 curmac->lineList->line = strdup(ln);
122 curmac->lineList->lineno = curlineno;
123 curmac->last = curmac->lineList;
127 curmac->last->next = malloc(sizeof(LLIST));
128 curmac->last->next->next = NULL;
129 curmac->last->next->line = strdup(ln);
130 curmac->lineList->lineno = curlineno;
131 curmac->last = curmac->last->next;
134 return 1; // Keep looking
137 return 0; // Stop looking; at the end
144 // macro foo arg1,arg2,...
151 // `defmac1' adds lines of text to the macro definition
152 // `defmac2' processes the formal arguments (and sticks them into the symbol
155 int DefineMacro(void)
157 // Setup entry in symbol table, make sure the macro isn't a duplicate
158 // entry, and that it doesn't override any processor mnemonic or assembler
160 if (*tok++ != SYMBOL)
161 return error("missing symbol");
163 char * name = string[*tok++];
165 if (lookup(name, MACRO, 0) != NULL)
166 return error("duplicate macro definition");
168 curmac = NewSymbol(name, MACRO, 0);
170 curmac->sattr = (WORD)(macnum++);
172 // Parse and define formal arguments in symbol table
180 // Suck in the macro definition; we're looking for an ENDM symbol on a line
181 // by itself to terminate the definition.
183 curmac->lineList = NULL;
184 LNCatch(defmac1, "endm ");
191 // Add lines to a .rept definition
193 int defr1(char * line, int kwno)
197 listeol(); // Flush previous source line
198 lstout('#'); // Mark this a 'rept' block
201 if (kwno == 0) // .endr
206 else if (kwno == 1) // .rept
209 //DEBUG { printf(" defr1: line=\"%s\", kwno=%d, rptlevel=%d\n", line, kwno, rptlevel); }
212 //MORE stupidity here...
213 WARNING("!!! Casting (char *) as LONG !!!")
214 // Allocate length of line + 1('\0') + LONG
215 LONG * p = (LONG *)malloc(strlen(line) + 1 + sizeof(LONG));
217 strcpy((char *)(p + 1), line);
220 firstrpt = p; // First line of rept statement
226 if (firstrpt == NULL)
228 firstrpt = malloc(sizeof(LLIST));
229 firstrpt->next = NULL;
230 firstrpt->line = strdup(line);
231 firstrpt->lineno = curlineno;
236 nextrpt->next = malloc(sizeof(LLIST));
237 nextrpt->next->next = NULL;
238 nextrpt->next->line = strdup(line);
239 nextrpt->next->lineno = curlineno;
240 nextrpt = nextrpt->next;
249 // Handle a .rept block; this gets hairy because they can be nested
255 // Evaluate repeat expression
256 if (abs_expr(&eval) != OK)
259 // Suck in lines for .rept block
263 LNCatch(defr1, "endr rept ");
265 //DEBUG { printf("HandleRept: firstrpt=$%X\n", firstrpt); }
266 // Alloc and init input object
269 INOBJ * inobj = a_inobj(SRC_IREPT); // Create a new REPT input object
270 IREPT * irept = inobj->inobj.irept;
271 irept->ir_firstln = firstrpt;
272 irept->ir_nextln = NULL;
273 irept->ir_count = (uint32_t)eval;
281 // Hand off lines of text to the function 'lnfunc' until a line containing one
282 // of the directives in 'dirlist' is encountered.
284 // 'dirlist' contains space-separated terminated keywords. A final space
285 // terminates the list. Directives are case-insensitively compared to the
288 // If 'lnfunc' is NULL, then lines are simply skipped.
289 // If 'lnfunc' returns an error, processing is stopped.
291 // 'lnfunc' is called with an argument of -1 for every line but the last one,
292 // when it is called with an argument of the keyword number that caused the
295 static int LNCatch(int (* lnfunc)(), char * dirlist)
298 lnsave++; // Tell tokenizer to keep lines
302 if (TokenizeLine() == TKEOF)
304 error("encountered end-of-file looking for '%s'", dirlist);
305 fatal("cannot continue");
308 DEBUG { DumpTokenBuffer(); }
310 // Test for end condition. Two cases to handle:
312 // symbol: <directive>
318 // A string followed by a colon or double colon is a symbol and
319 // *not* a directive, see if we can find the directive after it
320 if ((tok[2] == ':' || tok[2] == DCOLON))
322 if (tok[3] == SYMBOL)
327 // Otherwise, just grab the directive
334 if (*p == '.') // Ignore leading periods
337 k = KWMatch(p, dirlist);
340 // Hand-off line to function
341 // if it returns 0, and we found a keyword, stop looking.
342 // if it returns 1, hand off the line and keep looking.
344 k = (*lnfunc)(lnbuf, k);
351 lnsave--; // Tell tokenizer to stop keeping lines
358 // See if the string `kw' matches one of the keywords in `kwlist'. If so,
359 // return the number of the keyword matched. Return -1 if there was no match.
360 // Strings are compared without regard for case.
362 static int KWMatch(char * kw, char * kwlist)
364 for(int k=0; *kwlist; k++)
371 if (c2 >= 'A' && c2 <= 'Z')
374 if (c1 == ' ' && c2 == EOS)
381 // Skip to beginning of next keyword in `kwlist'
382 while (*kwlist && (*kwlist != ' '))
394 // Invoke a macro by creating a new IMACRO object & chopping up the arguments
396 int InvokeMacro(SYM * mac, WORD siz)
398 DEBUG { printf("InvokeMacro: arguments="); DumpTokens(tok); }
400 INOBJ * inobj = a_inobj(SRC_IMACRO); // Alloc and init IMACRO
401 IMACRO * imacro = inobj->inobj.imacro;
404 // Chop up the arguments, if any (tok comes from token.c, which at this
405 // point points at the macro argument token stream)
408 // Parse out the arguments and set them up correctly
409 TOKEN * p = imacro->argument[nargs].token;
417 // Sanity checking (it's numTokens + 1 because we need an EOL
418 // if we successfully parse this argument)
419 if ((numTokens + 3) >= TS_MAXTOKENS)
420 return error("Too many tokens in argument #%d in MACRO invocation", nargs + 1);
422 for(int i=0; i<3; i++)
427 else if (*tok == CONST) // Constants are 64-bits
429 // Sanity checking (it's numTokens + 1 because we need an EOL
430 // if we successfully parse this argument)
431 if ((numTokens + 3) >= TS_MAXTOKENS)
432 return error("Too many tokens in argument #%d in MACRO invocation", nargs + 1);
434 *p++ = *tok++; // Token
435 uint64_t *p64 = (uint64_t *)p;
436 uint64_t *tok64 = (uint64_t *)tok;
438 tok = (TOKEN *)tok64;
442 else if ((*tok == STRING) || (*tok == SYMBOL))
444 // Sanity checking (it's numTokens + 1 because we need an EOL
445 // if we successfully parse this argument)
446 if (stringNum >= TS_MAXSTRINGS)
447 return error("Too many strings in argument #%d in MACRO invocation", nargs + 1);
449 if ((numTokens + 2) >= TS_MAXTOKENS)
450 return error("Too many tokens in argument #%d in MACRO invocation", nargs + 1);
453 imacro->argument[nargs].string[stringNum] = strdup(string[*tok++]);
457 else if (*tok == ',')
460 if ((nargs + 1) >= TS_MAXARGS)
461 return error("Too many arguments in MACRO invocation");
463 // Comma delimiter was found, so set up for next argument
469 p = imacro->argument[nargs].token;
473 // Sanity checking (it's numTokens + 1 because we need an EOL
474 // if we successfully parse this argument)
475 if ((numTokens + 1) >= TS_MAXTOKENS)
476 return error("Too many tokens in argument #%d in MACRO invocation", nargs + 1);
483 // Make sure to stuff the final EOL (otherwise, it will be skipped)
490 // o -> macro symbol;
491 // o -> macro definition string list;
492 // o save 'curuniq', to be restored when the macro pops;
493 // o bump `macuniq' counter and set 'curuniq' to it;
494 imacro->im_nargs = nargs;
495 imacro->im_macro = mac;
496 imacro->im_siz = siz;
497 imacro->im_nextln = mac->lineList;
498 imacro->im_olduniq = curuniq;
503 printf("# args = %d\n", nargs);
505 for(uint16_t i=0; i<nargs; i++)
508 DumpTokens(imacro->argument[i].token);