2 // RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System
3 // MACRO.C - Macro Definition and Invocation
4 // Copyright (C) 199x Landon Dyer, 2011 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
19 LONG curuniq; // Current macro's unique number
20 TOKEN **argp; // Free spot in argptrs[]
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 char **curmln; // Previous macro line (or NULL)
26 static VALUE argno; // Formal argument count
28 static LONG *firstrpt; // First .rept line
29 static LONG *nextrpt; // Last .rept line
30 static int rptlevel; // .rept nesting level
33 // --- Initialize Macro Processor ------------------------------------------------------------------
36 void init_macro(void) {
44 // -------------------------------------------------------------------------------------------------
46 // o pop any intervening include files and repeat blocks;
47 // o restore argument stack;
49 // -------------------------------------------------------------------------------------------------
56 // Pop intervening include files and .rept blocks
57 while(cur_inobj != NULL && cur_inobj->in_type != SRC_IMACRO)
61 fatal("too many ENDMs");
65 // o old unique number
66 // ...and then pop the macro.
68 imacro = cur_inobj->inobj.imacro;
69 curuniq = imacro->im_olduniq;
72 argp = (TOKEN **)*argp;
82 // --- Add a Formal Argument to a Macro Definition -------------------------------------------------
85 int defmac2(char *argname) {
88 if(lookup(argname, MACARG, (int)curmac->sattr) != NULL)
89 return(error("multiple formal argument definition"));
90 arg = newsym(argname, MACARG, (int)curmac->sattr);
91 arg->svalue = argno++;
98 // -------------------------------------------------------------------------------------------------
99 // Add a line to a macro definition; also print lines to listing file (if enabled).
100 // The last line of the macro (containing .endm) is not included in the macro. A label on that line
101 // will be lost. `endflg' is misleading here. It is -1 for all lines but the last one (.endm),
103 // -------------------------------------------------------------------------------------------------
106 int defmac1(char *ln, int endflg) {
111 listeol(); // Flush previous source line
112 lstout('.'); // Mark macro definition with period
116 len = strlen(ln) + 1 + sizeof(LONG);
119 strcpy(p.cp + sizeof(LONG), ln);
121 // Link line of text onto end of list
123 curmac->svalue = (VALUE)p.cp;
126 curmln = (char **)p.cp;
127 return(1); // Keep looking
130 return(0); // Stop looking at the end
134 // -------------------------------------------------------------------------------------------------
137 // macro foo arg1,arg2,...
144 // `defmac1' adds lines of text to the macro definition
145 // `defmac2' processes the formal arguments (and sticks them into the symbol table)
146 // -------------------------------------------------------------------------------------------------
153 // Setup entry in symbol table, make sure the macro isn't a duplicate entry, and that
154 // it doesn't override any processor mnemonic or assembler directive.
155 if(*tok++ != SYMBOL) return(error("missing symbol"));
157 if(lookup(p, MACRO, 0) != NULL)
158 return(error("multiple macro definition"));
160 curmac = mac = newsym(p, MACRO, 0);
162 mac->sattr = (WORD)(macnum++);
164 // Parse and define formal arguments in symbol table
171 // Suck in the macro definition; we're looking for an ENDM symbol on a line
172 // by itself to terminate the definition.
174 lncatch(defmac1, "endm ");
180 // --- Add lines to a .rept definition -------------------------------------------------------------
183 int defr1(char *ln, int kwno) {
188 listeol(); // Flush previous source line
189 lstout('#'); // Mark this a 'rept' block
203 // Allocate length of line + 1('\0') + LONG
204 len = strlen(ln) + 1 + sizeof(LONG);
205 p = (LONG *)amem(len);
208 strcpy((char*)(p + 1), ln);
210 if(nextrpt == NULL) {
211 firstrpt = p; // First line of rept statement
222 // --- Define a .rept block, this gets hairy because they can be nested ----------------------------
230 // Evaluate repeat expression
231 if(abs_expr(&eval) != OK)
234 // Suck in lines for .rept block
238 lncatch(defr1, "endr rept ");
240 // Alloc and init input object
242 inobj = a_inobj(SRC_IREPT); // Create a new REPT input object
243 irept = inobj->inobj.irept;
244 irept->ir_firstln = firstrpt;
245 irept->ir_nextln = NULL;
246 irept->ir_count = eval;
253 // -------------------------------------------------------------------------------------------------
254 // Hand off lines of text to the function `lnfunc' until a line containing one of the directives in
255 // `dirlist' is encountered. Return the number of the keyword encountered (0..n)
257 // `dirlist' contains null-seperated terminated keywords. A final null terminates the list.
258 // Directives are compared to the keywords without regard to case.
260 // If `lnfunc' is NULL, then lines are simply skipped.
261 // If `lnfunc' returns an error, processing is stopped.
263 // `lnfunc' is called with an argument of -1 for every line but the last one, when it is called
264 // with an argument of the keyword number that caused the match.
265 // -------------------------------------------------------------------------------------------------
268 int lncatch(int (*lnfunc)(), char *dirlist) {
273 ++lnsave; // Tell tokenizer to keep lines
276 if(tokln() == TKEOF) {
277 errors("encountered end-of-file looking for '%s'", dirlist);
278 fatal("cannot continue");
281 // Test for end condition. Two cases to handle:
283 // symbol: <directive>
288 if((tok[2] == ':' || tok[2] == DCOLON)) {
289 if(tok[3] == SYMBOL) // label: symbol
292 p = (char *)tok[1]; // symbol
297 if(*p == '.') // ignore leading '.'s
299 k = kwmatch(p, dirlist);
302 // Hand-off line to function
303 // if it returns 0, and we found a keyword, stop looking.
304 // if it returns 1, hand off the line and keep looking.
306 k = (*lnfunc)(lnbuf, k);
313 --lnsave; // Tell tokenizer to stop keeping lines
319 // -------------------------------------------------------------------------------------------------
320 // See if the string `kw' matches one of the keywords in `kwlist'. If so, return the number of
321 // the keyword matched. Return -1 if there was no match.
322 // Strings are compared without regard for case.
323 // -------------------------------------------------------------------------------------------------
326 int kwmatch(char *kw, char *kwlist) {
332 for(k = 0; *kwlist; ++k) {
337 if(c2 >= 'A' && c2 <= 'Z')
340 if(c1 == ' ' && c2 == EOS)
347 // Skip to beginning of next keyword in `kwlist'
348 while(*kwlist && *kwlist != ' ')
358 // -------------------------------------------------------------------------------------------------
360 // o parse, count and copy arguments
361 // o push macro's string-stream
362 // -------------------------------------------------------------------------------------------------
365 int invokemac(SYM *mac, WORD siz) {
372 TOKEN **argptr = NULL;
375 if((!strcmp(mac->sname, "mjump") || !strcmp(mac->sname, "mpad")) && !in_main) {
376 error("macro cannot be used outside of .gpumain");
380 inobj = a_inobj(SRC_IMACRO); // Alloc and init IMACRO
381 imacro = inobj->inobj.imacro;
382 imacro->im_siz = siz;
386 for(dry_run = 1;; --dry_run) {
387 for(tok = beg_tok; *tok != EOL;) {
391 while(*tok != ',' && *tok != EOL) {
392 if(*tok == '\\' && tok[1] != EOL) ++tok;
397 if(dry_run) arg_siz += sizeof(TOKEN), ++tok;
401 if(dry_run) arg_siz += sizeof(TOKEN), ++tok;
407 if(dry_run) arg_siz += sizeof(TOKEN);
410 if(*tok == ',') ++tok;
413 // Allocate space for argument ptrs and so on and then go back and construct the arg frame
415 if(nargs != 0) p = (TOKEN *)malloc((LONG)(arg_siz + 1));
416 argptr = (TOKEN **)malloc((LONG)((nargs + 1) * sizeof(LONG)));
417 *argptr++ = (TOKEN *)argp;
426 // o -> macro symbol;
427 // o -> macro definition string list;
428 // o save 'curuniq', to be restored when the macro pops;
429 // o bump `macuniq' counter and set 'curuniq' to it;
430 imacro->im_nargs = nargs;
431 imacro->im_macro = mac;
432 imacro->im_nextln = (LONG *)mac->svalue;
433 imacro->im_olduniq = curuniq;
437 printf("nargs=%d\n", nargs);
438 for(nargs = 0; nargs < imacro->im_nargs; ++nargs) {
439 printf("arg%d=", nargs);
440 dumptok(argp[imacro->im_nargs - nargs - 1]);
448 // -------------------------------------------------------------------------------------------------
449 // Setup inbuilt macros
450 // -------------------------------------------------------------------------------------------------
457 curmac = mac = newsym("mjump", MACRO, 0);
459 mac->sattr = (WORD)(macnum++);
466 defmac1(" movei #\\addr,\\jreg", -1);
467 defmac1(" jump \\cc,(\\jreg)", -1);
471 curmac = mac = newsym("mjr", MACRO, 0);
473 mac->sattr = (WORD)(macnum++);
478 defmac1(" jr \\cc,\\addr", -1);
482 curmac = mac = newsym("mpad", MACRO, 0);
484 mac->sattr = (WORD)(macnum++);
488 defmac1(" .rept (\\size/2)", -1);
490 defmac1(" .endr", -1);