1 ////////////////////////////////////////////////////////////////////////////////////////////////////
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
18 LONG curuniq; // Current macro's unique number
19 TOKEN **argp; // Free spot in argptrs[]
20 int macnum; // Unique number for macro definition
22 static LONG macuniq; // Unique-per-macro number
23 static SYM *curmac; // Macro currently being defined
24 static char **curmln; // Previous macro line (or NULL)
25 static VALUE argno; // Formal argument count
27 static LONG *firstrpt; // First .rept line
28 static LONG *nextrpt; // Last .rept line
29 static int rptlevel; // .rept nesting level
32 // --- Initialize Macro Processor ------------------------------------------------------------------
35 void init_macro(void) {
43 // -------------------------------------------------------------------------------------------------
45 // o pop any intervening include files and repeat blocks;
46 // o restore argument stack;
48 // -------------------------------------------------------------------------------------------------
55 // Pop intervening include files and .rept blocks
56 while(cur_inobj != NULL && cur_inobj->in_type != SRC_IMACRO)
60 fatal("too many ENDMs");
64 // o old unique number
65 // ...and then pop the macro.
67 imacro = cur_inobj->inobj.imacro;
68 curuniq = imacro->im_olduniq;
71 argp = (TOKEN **)*argp;
81 // --- Add a Formal Argument to a Macro Definition -------------------------------------------------
84 int defmac2(char *argname) {
87 if(lookup(argname, MACARG, (int)curmac->sattr) != NULL)
88 return(error("multiple formal argument definition"));
89 arg = newsym(argname, MACARG, (int)curmac->sattr);
90 arg->svalue = argno++;
97 // -------------------------------------------------------------------------------------------------
98 // Add a line to a macro definition; also print lines to listing file (if enabled).
99 // The last line of the macro (containing .endm) is not included in the macro. A label on that line
100 // will be lost. `endflg' is misleading here. It is -1 for all lines but the last one (.endm),
102 // -------------------------------------------------------------------------------------------------
105 int defmac1(char *ln, int endflg) {
110 listeol(); // Flush previous source line
111 lstout('.'); // Mark macro definition with period
115 len = strlen(ln) + 1 + sizeof(LONG);
118 strcpy(p.cp + sizeof(LONG), ln);
120 // Link line of text onto end of list
122 curmac->svalue = (VALUE)p.cp;
125 curmln = (char **)p.cp;
126 return(1); // Keep looking
129 return(0); // Stop looking at the end
133 // -------------------------------------------------------------------------------------------------
136 // macro foo arg1,arg2,...
143 // `defmac1' adds lines of text to the macro definition
144 // `defmac2' processes the formal arguments (and sticks them into the symbol table)
145 // -------------------------------------------------------------------------------------------------
152 // Setup entry in symbol table, make sure the macro isn't a duplicate entry, and that
153 // it doesn't override any processor mnemonic or assembler directive.
154 if(*tok++ != SYMBOL) return(error("missing symbol"));
156 if(lookup(p, MACRO, 0) != NULL)
157 return(error("multiple macro definition"));
159 curmac = mac = newsym(p, MACRO, 0);
161 mac->sattr = (WORD)(macnum++);
163 // Parse and define formal arguments in symbol table
170 // Suck in the macro definition; we're looking for an ENDM symbol on a line
171 // by itself to terminate the definition.
173 lncatch(defmac1, "endm ");
179 // --- Add lines to a .rept definition -------------------------------------------------------------
182 int defr1(char *ln, int kwno) {
187 listeol(); // Flush previous source line
188 lstout('#'); // Mark this a 'rept' block
202 // Allocate length of line + 1('\0') + LONG
203 len = strlen(ln) + 1 + sizeof(LONG);
204 p = (LONG *)amem(len);
207 strcpy((char*)(p + 1), ln);
209 if(nextrpt == NULL) {
210 firstrpt = p; // First line of rept statement
221 // --- Define a .rept block, this gets hairy because they can be nested ----------------------------
229 // Evaluate repeat expression
230 if(abs_expr(&eval) != OK)
233 // Suck in lines for .rept block
237 lncatch(defr1, "endr rept ");
239 // Alloc and init input object
241 inobj = a_inobj(SRC_IREPT); // Create a new REPT input object
242 irept = inobj->inobj.irept;
243 irept->ir_firstln = firstrpt;
244 irept->ir_nextln = NULL;
245 irept->ir_count = eval;
252 // -------------------------------------------------------------------------------------------------
253 // Hand off lines of text to the function `lnfunc' until a line containing one of the directives in
254 // `dirlist' is encountered. Return the number of the keyword encountered (0..n)
256 // `dirlist' contains null-seperated terminated keywords. A final null terminates the list.
257 // Directives are compared to the keywords without regard to case.
259 // If `lnfunc' is NULL, then lines are simply skipped.
260 // If `lnfunc' returns an error, processing is stopped.
262 // `lnfunc' is called with an argument of -1 for every line but the last one, when it is called
263 // with an argument of the keyword number that caused the match.
264 // -------------------------------------------------------------------------------------------------
267 int lncatch(int (*lnfunc)(), char *dirlist) {
272 ++lnsave; // Tell tokenizer to keep lines
275 if(tokln() == TKEOF) {
276 errors("encountered end-of-file looking for '%s'", dirlist);
277 fatal("cannot continue");
280 // Test for end condition. Two cases to handle:
282 // symbol: <directive>
287 if((tok[2] == ':' || tok[2] == DCOLON)) {
288 if(tok[3] == SYMBOL) // label: symbol
291 p = (char *)tok[1]; // symbol
296 if(*p == '.') // ignore leading '.'s
298 k = kwmatch(p, dirlist);
301 // Hand-off line to function
302 // if it returns 0, and we found a keyword, stop looking.
303 // if it returns 1, hand off the line and keep looking.
305 k = (*lnfunc)(lnbuf, k);
312 --lnsave; // Tell tokenizer to stop keeping lines
318 // -------------------------------------------------------------------------------------------------
319 // See if the string `kw' matches one of the keywords in `kwlist'. If so, return the number of
320 // the keyword matched. Return -1 if there was no match.
321 // Strings are compared without regard for case.
322 // -------------------------------------------------------------------------------------------------
325 int kwmatch(char *kw, char *kwlist) {
331 for(k = 0; *kwlist; ++k) {
336 if(c2 >= 'A' && c2 <= 'Z')
339 if(c1 == ' ' && c2 == EOS)
346 // Skip to beginning of next keyword in `kwlist'
347 while(*kwlist && *kwlist != ' ')
357 // -------------------------------------------------------------------------------------------------
359 // o parse, count and copy arguments
360 // o push macro's string-stream
361 // -------------------------------------------------------------------------------------------------
364 int invokemac(SYM *mac, WORD siz) {
371 TOKEN **argptr = NULL;
374 if((!strcmp(mac->sname, "mjump") || !strcmp(mac->sname, "mpad")) && !in_main) {
375 error("macro cannot be used outside of .gpumain");
379 inobj = a_inobj(SRC_IMACRO); // Alloc and init IMACRO
380 imacro = inobj->inobj.imacro;
381 imacro->im_siz = siz;
385 for(dry_run = 1;; --dry_run) {
386 for(tok = beg_tok; *tok != EOL;) {
390 while(*tok != ',' && *tok != EOL) {
391 if(*tok == '\\' && tok[1] != EOL) ++tok;
396 if(dry_run) arg_siz += sizeof(TOKEN), ++tok;
400 if(dry_run) arg_siz += sizeof(TOKEN), ++tok;
406 if(dry_run) arg_siz += sizeof(TOKEN);
409 if(*tok == ',') ++tok;
412 // Allocate space for argument ptrs and so on and then go back and construct the arg frame
414 if(nargs != 0) p = (TOKEN *)malloc((LONG)(arg_siz + 1));
415 argptr = (TOKEN **)malloc((LONG)((nargs + 1) * sizeof(LONG)));
416 *argptr++ = (TOKEN *)argp;
425 // o -> macro symbol;
426 // o -> macro definition string list;
427 // o save 'curuniq', to be restored when the macro pops;
428 // o bump `macuniq' counter and set 'curuniq' to it;
429 imacro->im_nargs = nargs;
430 imacro->im_macro = mac;
431 imacro->im_nextln = (LONG *)mac->svalue;
432 imacro->im_olduniq = curuniq;
436 printf("nargs=%d\n", nargs);
437 for(nargs = 0; nargs < imacro->im_nargs; ++nargs) {
438 printf("arg%d=", nargs);
439 dumptok(argp[imacro->im_nargs - nargs - 1]);
447 // -------------------------------------------------------------------------------------------------
448 // Setup inbuilt macros
449 // -------------------------------------------------------------------------------------------------
452 void ib_macro(void) {
455 curmac = mac = newsym("mjump", MACRO, 0);
457 mac->sattr = (WORD)(macnum++);
464 defmac1(" movei #\\addr,\\jreg", -1);
465 defmac1(" jump \\cc,(\\jreg)", -1);
469 curmac = mac = newsym("mjr", MACRO, 0);
471 mac->sattr = (WORD)(macnum++);
476 defmac1(" jr \\cc,\\addr", -1);
480 curmac = mac = newsym("mpad", MACRO, 0);
482 mac->sattr = (WORD)(macnum++);
486 defmac1(" .rept (\\size/2)", -1);
488 defmac1(" .endr", -1);