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 LONG curuniq; // Current macro's unique number
21 //TOKEN ** argp; // Free spot in argptrs[]
22 int macnum; // Unique number for macro definition
23 TOKEN * argPtrs[128]; // 128 arguments ought to be enough for anyone
26 static LONG macuniq; // Unique-per-macro number
27 static SYM * curmac; // Macro currently being defined
28 //static char ** curmln; // Previous macro line (or NULL)
29 static VALUE argno; // Formal argument count
31 static LONG * firstrpt; // First .rept line
32 static LONG * nextrpt; // Last .rept line
33 static int rptlevel; // .rept nesting level
37 // Initialize macro processor
50 // -- pop any intervening include files and repeat blocks;
51 // -- restore argument stack;
57 #pragma message !!! Bad macro exiting !!!
59 #pragma WARNING(!!! Bad macro exiting !!!)
63 This is a problem. Currently, the argument logic just keeps the current
64 arguments and doesn't save anything if a new macro is called in the middle
65 of another (nested macros). Need to fix that somehow.
67 // Pop intervening include files and .rept blocks
68 while (cur_inobj != NULL && cur_inobj->in_type != SRC_IMACRO)
71 if (cur_inobj == NULL)
72 fatal("too many ENDMs");
76 // o old unique number
77 // ...and then pop the macro.
79 IMACRO * imacro = cur_inobj->inobj.imacro;
80 curuniq = imacro->im_olduniq;
82 // /*TOKEN ** p = */argp--;
83 // argp = (TOKEN **)*argp;
84 DEBUG printf("ExitMacro: argp: %d -> ", argp);
85 argp -= imacro->im_nargs;
86 DEBUG printf("%d (nargs = %d)\n", argp, imacro->im_nargs);
94 // Add a formal argument to a macro definition
96 int defmac2(char * argname)
100 if (lookup(argname, MACARG, (int)curmac->sattr) != NULL)
101 return error("multiple formal argument definition");
103 arg = NewSymbol(argname, MACARG, (int)curmac->sattr);
104 arg->svalue = argno++;
111 // Add a line to a macro definition; also print lines to listing file (if
112 // enabled). The last line of the macro (containing .endm) is not included in
113 // the macro. A label on that line will be lost.
114 // notEndFlag is -1 for all lines but the last one (.endm), when it is 0.
116 int defmac1(char * ln, int notEndFlag)
123 listeol(); // Flush previous source line
124 lstout('.'); // Mark macro definition with period
127 // This is just wrong, wrong, wrong. It makes up a weird kind of string with
128 // a pointer on front, and then uses a ** to manage them: This is a recipe
130 // How to manage it then?
131 // Could use a linked list, like Landon uses everywhere else.
134 Allocate a space big enough for the string + NULL + pointer.
135 Set the pointer to NULL.
136 Copy the string to the space after the pointer.
137 If this is the 1st time through, set the SYM * "svalue" to the pointer.
138 If this is the 2nd time through, derefence the ** to point to the memory you just allocated.
139 Then, set the ** to the location of the memory you allocated for the next pass through.
141 This is a really low level way to do a linked list, and by bypassing all the safety
142 features of the language. Seems like we can do better here.
147 len = strlen(ln) + 1 + sizeof(LONG);
150 strcpy(p.cp + sizeof(LONG), ln);
152 // Link line of text onto end of list
154 curmac->svalue = p.cp;
158 curmln = (char **)p.cp;
159 return 1; // Keep looking
161 if (curmac->lineList == NULL)
163 curmac->lineList = malloc(sizeof(struct LineList));
164 curmac->lineList->next = NULL;
165 curmac->lineList->line = strdup(ln);
166 curmac->last = curmac->lineList;
170 curmac->last->next = malloc(sizeof(struct LineList));
171 curmac->last->next->next = NULL;
172 curmac->last->next->line = strdup(ln);
173 curmac->last = curmac->last->next;
176 return 1; // Keep looking
180 return 0; // Stop looking at the end
187 // macro foo arg1,arg2,...
194 // `defmac1' adds lines of text to the macro definition
195 // `defmac2' processes the formal arguments (and sticks them into the symbol table)
197 int DefineMacro(void)
199 // Setup entry in symbol table, make sure the macro isn't a duplicate
200 // entry, and that it doesn't override any processor mnemonic or assembler
202 if (*tok++ != SYMBOL)
203 return error("missing symbol");
205 char * name = string[*tok++];
207 if (lookup(name, MACRO, 0) != NULL)
208 return error("duplicate macro definition");
210 curmac = NewSymbol(name, MACRO, 0);
212 curmac->sattr = (WORD)(macnum++);
214 // Parse and define formal arguments in symbol table
222 // Suck in the macro definition; we're looking for an ENDM symbol on a line
223 // by itself to terminate the definition.
225 curmac->lineList = NULL;
226 lncatch(defmac1, "endm ");
233 // Add lines to a .rept definition
235 int defr1(char * ln, int kwno)
242 listeol(); // Flush previous source line
243 lstout('#'); // Mark this a 'rept' block
255 //MORE stupidity here...
257 #pragma warning "!!! Casting (char *) as LONG !!!"
259 #pragma WARNING(!!! Casting (char *) as LONG !!!)
262 // Allocate length of line + 1('\0') + LONG
263 len = strlen(ln) + 1 + sizeof(LONG);
264 // p = (LONG *)amem(len);
265 p = (LONG *)malloc(len);
268 strcpy((char *)(p + 1), ln);
272 firstrpt = p; // First line of rept statement
287 // Define a .rept block, this gets hairy because they can be nested
295 // Evaluate repeat expression
296 if (abs_expr(&eval) != OK)
299 // Suck in lines for .rept block
303 lncatch(defr1, "endr rept ");
305 // Alloc and init input object
308 inobj = a_inobj(SRC_IREPT); // Create a new REPT input object
309 irept = inobj->inobj.irept;
310 irept->ir_firstln = firstrpt;
311 irept->ir_nextln = NULL;
312 irept->ir_count = eval;
320 // Hand off lines of text to the function `lnfunc' until a line containing one
321 // of the directives in `dirlist' is encountered. Return the number of the
322 // keyword encountered (0..n)
324 // `dirlist' contains null-seperated terminated keywords. A final null
325 // terminates the list. Directives are compared to the keywords without regard
328 // If `lnfunc' is NULL, then lines are simply skipped.
329 // If `lnfunc' returns an error, processing is stopped.
331 // `lnfunc' is called with an argument of -1 for every line but the last one,
332 // when it is called with an argument of the keyword number that caused the
335 int lncatch(int (* lnfunc)(), char * dirlist)
341 lnsave++; // Tell tokenizer to keep lines
345 if (TokenizeLine() == TKEOF)
347 errors("encountered end-of-file looking for '%s'", dirlist);
348 fatal("cannot continue");
351 // Test for end condition. Two cases to handle:
353 // symbol: <directive>
359 if ((tok[2] == ':' || tok[2] == DCOLON))
361 if (tok[3] == SYMBOL) // label: symbol
366 p = string[tok[1]]; // Symbol
372 if (*p == '.') // ignore leading '.'s
375 k = kwmatch(p, dirlist);
378 // Hand-off line to function
379 // if it returns 0, and we found a keyword, stop looking.
380 // if it returns 1, hand off the line and keep looking.
382 k = (*lnfunc)(lnbuf, k);
389 lnsave--; // Tell tokenizer to stop keeping lines
396 // See if the string `kw' matches one of the keywords in `kwlist'. If so,
397 // return the number of the keyword matched. Return -1 if there was no match.
398 // Strings are compared without regard for case.
400 int kwmatch(char * kw, char * kwlist)
407 for(k=0; *kwlist; ++k)
414 if (c2 >= 'A' && c2 <= 'Z')
417 if (c1 == ' ' && c2 == EOS)
424 // Skip to beginning of next keyword in `kwlist'
425 while (*kwlist && *kwlist != ' ')
438 // o parse, count and copy arguments
439 // o push macro's string-stream
441 int InvokeMacro(SYM * mac, WORD siz)
446 // TOKEN ** argptr = NULL;
447 //Doesn't need to be global! (or does it???--it does)
449 DEBUG printf("InvokeMacro: argp: %d -> ", argp);
451 INOBJ * inobj = a_inobj(SRC_IMACRO); // Alloc and init IMACRO
452 IMACRO * imacro = inobj->inobj.imacro;
453 imacro->im_siz = siz;
455 TOKEN * beg_tok = tok; // 'tok' comes from token.c
462 for(dry_run=1; ; dry_run--)
464 for(tok=beg_tok; *tok!=EOL;)
478 // Keep going while tok isn't pointing at a comma or EOL
479 while (*tok != ',' && *tok != EOL)
481 // Skip over backslash character, unless it's followed by an EOL
482 if (*tok == '\\' && tok[1] != EOL)
489 //Shamus: Possible bug. ACONST has 2 tokens after it, not just 1
493 arg_siz += sizeof(TOKEN);
500 // FALLTHROUGH (picks up the arg after a CONST, SYMBOL or ACONST)
504 arg_siz += sizeof(TOKEN);
516 // We hit the comma or EOL, so count/stuff it
518 arg_siz += sizeof(TOKEN);
522 // If we hit the comma instead of an EOL, skip over it
526 // Do our QnD token grabbing (this will be redone once we get all
527 // the data structures fixed as this is a really dirty hack)
530 dest = imacro->argument[argumentNum].token;
535 // Remap strings to point the IMACRO internal token storage
536 if (*startOfArg == SYMBOL || *startOfArg == STRING)
538 *dest++ = *startOfArg++;
539 imacro->argument[argumentNum].string[stringNum] = strdup(string[*startOfArg++]);
540 *dest++ = stringNum++;
543 *dest++ = *startOfArg++;
545 while (*startOfArg != EOL);
547 *dest = *startOfArg; // Copy EOL...
552 // Allocate space for argument ptrs and so on and then go back and
553 // construct the arg frame
557 p = (TOKEN *)malloc(arg_siz);
558 // p = (TOKEN *)malloc(arg_siz + sizeof(TOKEN));
562 This construct is meant to deal with nested macros, so the simple minded way
563 we deal with them now won't work. :-/ Have to think about how to fix.
564 What we could do is simply move the argp with each call, and move it back by
565 the number of arguments in the macro that's ending. That would solve the
567 [Which we do now. But that uncovered another problem: the token strings are all
568 stale by the time a nested macro gets to the end. But they're supposed to be
569 symbols, which means if we put symbol references into the argument token
570 streams, we can alleviate this problem.]
573 argptr = (TOKEN **)malloc((nargs + 1) * sizeof(LONG));
574 *argptr++ = (TOKEN *)argp;
577 // We don't need to do anything here since we already advance argp
578 // when parsing the arguments.
586 DEBUG printf("%d\n", argp);
590 // o -> macro symbol;
591 // o -> macro definition string list;
592 // o save 'curuniq', to be restored when the macro pops;
593 // o bump `macuniq' counter and set 'curuniq' to it;
594 imacro->im_nargs = nargs;
595 imacro->im_macro = mac;
596 // imacro->im_nextln = (TOKEN *)mac->svalue;
597 imacro->im_nextln = mac->lineList;
598 imacro->im_olduniq = curuniq;
600 imacro->argBase = argp - nargs; // Shamus: keep track of argument base
604 printf("nargs=%d\n", nargs);
606 for(nargs=0; nargs<imacro->im_nargs; nargs++)
608 printf("arg%d=", nargs);
609 // dumptok(argp[imacro->im_nargs - nargs - 1]);
610 // dumptok(argPtrs[imacro->im_nargs - nargs - 1]);
611 dumptok(argPtrs[(argp - imacro->im_nargs) + nargs]);