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
20 static void SetupDefaultMacros(void);
22 LONG curuniq; // Current macro's unique number
23 //TOKEN ** argp; // Free spot in argptrs[]
24 int macnum; // Unique number for macro definition
25 TOKEN * argPtrs[128]; // 128 arguments ought to be enough for anyone
28 static LONG macuniq; // Unique-per-macro number
29 static SYM * curmac; // Macro currently being defined
30 //static char ** curmln; // Previous macro line (or NULL)
31 static VALUE argno; // Formal argument count
33 static LONG * firstrpt; // First .rept line
34 static LONG * nextrpt; // Last .rept line
35 static int rptlevel; // .rept nesting level
39 // Initialize Macro Processor
53 // -- pop any intervening include files and repeat blocks;
54 // -- restore argument stack;
59 #warning !!! Bad macro exiting !!!
61 This is a problem. Currently, the argument logic just keeps the current
62 arguments and doesn't save anything if a new macro is called in the middle
63 of another (nested macros). Need to fix that somehow.
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 // /*TOKEN ** p = */argp--;
81 // argp = (TOKEN **)*argp;
82 DEBUG printf("ExitMacro: argp: %d -> ", argp);
83 argp -= imacro->im_nargs;
84 DEBUG printf("%d (nargs = %d)\n", argp, imacro->im_nargs);
93 // Add a Formal Argument to a Macro Definition
95 int defmac2(char * argname)
99 if (lookup(argname, MACARG, (int)curmac->sattr) != NULL)
100 return error("multiple formal argument definition");
102 arg = NewSymbol(argname, MACARG, (int)curmac->sattr);
103 arg->svalue = argno++;
110 // Add a line to a macro definition; also print lines to listing file (if
111 // enabled). The last line of the macro (containing .endm) is not included in
112 // the macro. A label on that line will be lost.
113 // notEndFlag is -1 for all lines but the last one (.endm), when it is 0.
115 int defmac1(char * ln, int notEndFlag)
122 listeol(); // Flush previous source line
123 lstout('.'); // Mark macro definition with period
126 // This is just wrong, wrong, wrong. It makes up a weird kind of string with
127 // a pointer on front, and then uses a ** to manage them: This is a recipe
129 // How to manage it then?
130 // Could use a linked list, like Landon uses everywhere else.
133 Allocate a space big enough for the string + NULL + pointer.
134 Set the pointer to NULL.
135 Copy the string to the space after the pointer.
136 If this is the 1st time through, set the SYM * "svalue" to the pointer.
137 If this is the 2nd time through, derefence the ** to point to the memory you just allocated.
138 Then, set the ** to the location of the memory you allocated for the next pass through.
140 This is a really low level way to do a linked list, and by bypassing all the safety
141 features of the language. Seems like we can do better here.
146 len = strlen(ln) + 1 + sizeof(LONG);
149 strcpy(p.cp + sizeof(LONG), ln);
151 // Link line of text onto end of list
153 curmac->svalue = p.cp;
157 curmln = (char **)p.cp;
158 return 1; // Keep looking
160 if (curmac->lineList == NULL)
162 curmac->lineList = malloc(sizeof(struct LineList));
163 curmac->lineList->next = NULL;
164 curmac->lineList->line = strdup(ln);
165 curmac->last = curmac->lineList;
169 curmac->last->next = malloc(sizeof(struct LineList));
170 curmac->last->next->next = NULL;
171 curmac->last->next->line = strdup(ln);
172 curmac->last = curmac->last->next;
175 return 1; // Keep looking
179 return 0; // Stop looking at the end
186 // macro foo arg1,arg2,...
193 // `defmac1' adds lines of text to the macro definition
194 // `defmac2' processes the formal arguments (and sticks them into the symbol table)
196 int DefineMacro(void)
198 // Setup entry in symbol table, make sure the macro isn't a duplicate
199 // entry, and that it doesn't override any processor mnemonic or assembler
201 if (*tok++ != SYMBOL)
202 return error("missing symbol");
204 char * name = string[*tok++];
206 if (lookup(name, MACRO, 0) != NULL)
207 return error("duplicate macro definition");
209 curmac = NewSymbol(name, MACRO, 0);
211 curmac->sattr = (WORD)(macnum++);
213 // Parse and define formal arguments in symbol table
221 // Suck in the macro definition; we're looking for an ENDM symbol on a line
222 // by itself to terminate the definition.
224 curmac->lineList = NULL;
225 lncatch(defmac1, "endm ");
232 // Add lines to a .rept definition
234 int defr1(char * ln, int kwno)
241 listeol(); // Flush previous source line
242 lstout('#'); // Mark this a 'rept' block
254 //MORE stupidity here...
255 #warning "!!! Casting (char *) as LONG !!!"
257 // Allocate length of line + 1('\0') + LONG
258 len = strlen(ln) + 1 + sizeof(LONG);
259 // p = (LONG *)amem(len);
260 p = (LONG *)malloc(len);
263 strcpy((char *)(p + 1), ln);
267 firstrpt = p; // First line of rept statement
282 // Define a .rept block, this gets hairy because they can be nested
290 // Evaluate repeat expression
291 if (abs_expr(&eval) != OK)
294 // Suck in lines for .rept block
298 lncatch(defr1, "endr rept ");
300 // Alloc and init input object
303 inobj = a_inobj(SRC_IREPT); // Create a new REPT input object
304 irept = inobj->inobj.irept;
305 irept->ir_firstln = firstrpt;
306 irept->ir_nextln = NULL;
307 irept->ir_count = eval;
315 // Hand off lines of text to the function `lnfunc' until a line containing one
316 // of the directives in `dirlist' is encountered. Return the number of the
317 // keyword encountered (0..n)
319 // `dirlist' contains null-seperated terminated keywords. A final null
320 // terminates the list. Directives are compared to the keywords without regard
323 // If `lnfunc' is NULL, then lines are simply skipped.
324 // If `lnfunc' returns an error, processing is stopped.
326 // `lnfunc' is called with an argument of -1 for every line but the last one,
327 // when it is called with an argument of the keyword number that caused the
330 int lncatch(int (* lnfunc)(), char * dirlist)
336 lnsave++; // Tell tokenizer to keep lines
340 if (tokln() == TKEOF)
342 errors("encountered end-of-file looking for '%s'", dirlist);
343 fatal("cannot continue");
346 // Test for end condition. Two cases to handle:
348 // symbol: <directive>
354 if ((tok[2] == ':' || tok[2] == DCOLON))
356 if (tok[3] == SYMBOL) // label: symbol
366 p = (char *)tok[1]; // symbol
368 p = string[tok[1]]; // Symbol
375 if (*p == '.') // ignore leading '.'s
378 k = kwmatch(p, dirlist);
381 // Hand-off line to function
382 // if it returns 0, and we found a keyword, stop looking.
383 // if it returns 1, hand off the line and keep looking.
385 k = (*lnfunc)(lnbuf, k);
392 lnsave--; // Tell tokenizer to stop keeping lines
399 // See if the string `kw' matches one of the keywords in `kwlist'. If so,
400 // return the number of the keyword matched. Return -1 if there was no match.
401 // Strings are compared without regard for case.
403 int kwmatch(char * kw, char * kwlist)
410 for(k=0; *kwlist; ++k)
417 if (c2 >= 'A' && c2 <= 'Z')
420 if (c1 == ' ' && c2 == EOS)
427 // Skip to beginning of next keyword in `kwlist'
428 while (*kwlist && *kwlist != ' ')
441 // o parse, count and copy arguments
442 // o push macro's string-stream
444 int InvokeMacro(SYM * mac, WORD siz)
449 // TOKEN ** argptr = NULL;
450 //Doesn't need to be global! (or does it???--it does)
452 DEBUG printf("InvokeMacro: argp: %d -> ", argp);
454 if ((!strcmp(mac->sname, "mjump") || !strcmp(mac->sname, "mpad")) && !in_main)
456 error("macro cannot be used outside of .gpumain");
460 INOBJ * inobj = a_inobj(SRC_IMACRO); // Alloc and init IMACRO
461 IMACRO * imacro = inobj->inobj.imacro;
462 imacro->im_siz = siz;
464 TOKEN * beg_tok = tok; // 'tok' comes from token.c
471 for(dry_run=1; ; dry_run--)
473 for(tok=beg_tok; *tok!=EOL;)
487 // Keep going while tok isn't pointing at a comma or EOL
488 while (*tok != ',' && *tok != EOL)
490 // Skip over backslash character, unless it's followed by an EOL
491 if (*tok == '\\' && tok[1] != EOL)
498 //Shamus: Possible bug. ACONST has 2 tokens after it, not just 1
502 arg_siz += sizeof(TOKEN);
509 // FALLTHROUGH (picks up the arg after a CONST, SYMBOL or ACONST)
513 arg_siz += sizeof(TOKEN);
525 // We hit the comma or EOL, so count/stuff it
527 arg_siz += sizeof(TOKEN);
531 // If we hit the comma instead of an EOL, skip over it
535 // Do our QnD token grabbing (this will be redone once we get all
536 // the data structures fixed as this is a really dirty hack)
539 dest = imacro->argument[argumentNum].token;
544 // Remap strings to point the IMACRO internal token storage
545 if (*startOfArg == SYMBOL || *startOfArg == STRING)
547 *dest++ = *startOfArg++;
548 imacro->argument[argumentNum].string[stringNum] = strdup(string[*startOfArg++]);
549 *dest++ = stringNum++;
552 *dest++ = *startOfArg++;
554 while (*startOfArg != EOL);
556 *dest = *startOfArg; // Copy EOL...
561 // Allocate space for argument ptrs and so on and then go back and
562 // construct the arg frame
566 p = (TOKEN *)malloc(arg_siz);
567 // p = (TOKEN *)malloc(arg_siz + sizeof(TOKEN));
571 This construct is meant to deal with nested macros, so the simple minded way
572 we deal with them now won't work. :-/ Have to think about how to fix.
573 What we could do is simply move the argp with each call, and move it back by the
574 number of arguments in the macro that's ending. That would solve the problem nicely.
575 [Which we do now. But that uncovered another problem: the token strings are all
576 stale by the time a nested macro gets to the end. But they're supposed to be symbols,
577 which means if we put symbol references into the argument token streams, we can
578 alleviate this problem.]
581 argptr = (TOKEN **)malloc((nargs + 1) * sizeof(LONG));
582 *argptr++ = (TOKEN *)argp;
585 // We don't need to do anything here since we already advance argp
586 // when parsing the arguments.
594 DEBUG printf("%d\n", argp);
598 // o -> macro symbol;
599 // o -> macro definition string list;
600 // o save 'curuniq', to be restored when the macro pops;
601 // o bump `macuniq' counter and set 'curuniq' to it;
602 imacro->im_nargs = nargs;
603 imacro->im_macro = mac;
604 // imacro->im_nextln = (TOKEN *)mac->svalue;
605 imacro->im_nextln = mac->lineList;
606 imacro->im_olduniq = curuniq;
608 imacro->argBase = argp - nargs; // Shamus: keep track of argument base
612 printf("nargs=%d\n", nargs);
614 for(nargs=0; nargs<imacro->im_nargs; nargs++)
616 printf("arg%d=", nargs);
617 // dumptok(argp[imacro->im_nargs - nargs - 1]);
618 // dumptok(argPtrs[imacro->im_nargs - nargs - 1]);
619 dumptok(argPtrs[(argp - imacro->im_nargs) + nargs]);
628 // Setup inbuilt macros (SubQMod)
630 static void SetupDefaultMacros(void)
632 curmac = NewSymbol("mjump", MACRO, 0);
634 curmac->sattr = (WORD)(macnum++);
640 curmac->lineList = NULL;
642 defmac1(" movei #\\addr,\\jreg", -1);
643 defmac1(" jump \\cc,(\\jreg)", -1);
647 curmac = NewSymbol("mjr", MACRO, 0);
649 curmac->sattr = (WORD)(macnum++);
654 curmac->lineList = NULL;
655 defmac1(" jr \\cc,\\addr", -1);
659 curmac = NewSymbol("mpad", MACRO, 0);
661 curmac->sattr = (WORD)(macnum++);
665 curmac->lineList = NULL;
666 defmac1(" .rept (\\size/2)", -1);
668 defmac1(" .endr", -1);