2 // RMAC - Reboot's Macro Assembler for all Atari computers
3 // MACRO.C - Macro Definition and Invocation
4 // Copyright (C) 199x Landon Dyer, 2011-2017 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
22 TOKEN * argPtrs[128]; // 128 arguments ought to be enough for anyone
25 static LONG macuniq; // Unique-per-macro number
26 static SYM * curmac; // Macro currently being defined
27 static VALUE argno; // Formal argument count
29 static LONG * firstrpt; // First .rept line
30 static LONG * nextrpt; // Last .rept line
31 static int rptlevel; // .rept nesting level
35 // Initialize macro processor
47 // -- pop any intervening include files and repeat blocks;
48 // -- restore argument stack;
53 WARNING(!!! Bad macro exiting !!!)
56 This is a problem. Currently, the argument logic just keeps the current
57 arguments and doesn't save anything if a new macro is called in the middle
58 of another (nested macros). Need to fix that somehow.
60 // Pop intervening include files and .rept blocks
61 while (cur_inobj != NULL && cur_inobj->in_type != SRC_IMACRO)
64 if (cur_inobj == NULL)
65 fatal("too many ENDMs");
69 // o old unique number
70 // ...and then pop the macro.
72 IMACRO * imacro = cur_inobj->inobj.imacro;
73 curuniq = imacro->im_olduniq;
75 // /*TOKEN ** p = */argp--;
76 // argp = (TOKEN **)*argp;
77 DEBUG printf("ExitMacro: argp: %d -> ", argp);
78 argp -= imacro->im_nargs;
79 DEBUG printf("%d (nargs = %d)\n", argp, imacro->im_nargs);
86 // Add a formal argument to a macro definition
88 int defmac2(char * argname)
92 if (lookup(argname, MACARG, (int)curmac->sattr) != NULL)
93 return error("multiple formal argument definition");
95 arg = NewSymbol(argname, MACARG, (int)curmac->sattr);
96 arg->svalue = argno++;
103 // Add a line to a macro definition; also print lines to listing file (if
104 // enabled). The last line of the macro (containing .endm) is not included in
105 // the macro. A label on that line will be lost.
106 // notEndFlag is -1 for all lines but the last one (.endm), when it is 0.
108 int defmac1(char * ln, int notEndFlag)
115 listeol(); // Flush previous source line
116 lstout('.'); // Mark macro definition with period
119 // This is just wrong, wrong, wrong. It makes up a weird kind of string with
120 // a pointer on front, and then uses a ** to manage them: This is a recipe
122 // How to manage it then?
123 // Could use a linked list, like Landon uses everywhere else.
126 Allocate a space big enough for the string + NULL + pointer.
127 Set the pointer to NULL.
128 Copy the string to the space after the pointer.
129 If this is the 1st time through, set the SYM * "svalue" to the pointer.
130 If this is the 2nd time through, derefence the ** to point to the memory you just allocated.
131 Then, set the ** to the location of the memory you allocated for the next pass through.
133 This is a really low level way to do a linked list, and by bypassing all the safety
134 features of the language. Seems like we can do better here.
139 len = strlen(ln) + 1 + sizeof(LONG);
142 strcpy(p.cp + sizeof(LONG), ln);
144 // Link line of text onto end of list
146 curmac->svalue = p.cp;
150 curmln = (char **)p.cp;
151 return 1; // Keep looking
153 if (curmac->lineList == NULL)
155 curmac->lineList = malloc(sizeof(struct LineList));
156 curmac->lineList->next = NULL;
157 curmac->lineList->line = strdup(ln);
158 curmac->last = curmac->lineList;
162 curmac->last->next = malloc(sizeof(struct LineList));
163 curmac->last->next->next = NULL;
164 curmac->last->next->line = strdup(ln);
165 curmac->last = curmac->last->next;
168 return 1; // Keep looking
172 return 0; // Stop looking at the end
179 // macro foo arg1,arg2,...
186 // `defmac1' adds lines of text to the macro definition
187 // `defmac2' processes the formal arguments (and sticks them into the symbol table)
189 int DefineMacro(void)
191 // Setup entry in symbol table, make sure the macro isn't a duplicate
192 // entry, and that it doesn't override any processor mnemonic or assembler
194 if (*tok++ != SYMBOL)
195 return error("missing symbol");
197 char * name = string[*tok++];
199 if (lookup(name, MACRO, 0) != NULL)
200 return error("duplicate macro definition");
202 curmac = NewSymbol(name, MACRO, 0);
204 curmac->sattr = (WORD)(macnum++);
206 // Parse and define formal arguments in symbol table
214 // Suck in the macro definition; we're looking for an ENDM symbol on a line
215 // by itself to terminate the definition.
217 curmac->lineList = NULL;
218 lncatch(defmac1, "endm ");
225 // Add lines to a .rept definition
227 int defr1(char * ln, int kwno)
234 listeol(); // Flush previous source line
235 lstout('#'); // Mark this a 'rept' block
247 //MORE stupidity here...
248 WARNING(!!! Casting (char *) as LONG !!!)
250 // Allocate length of line + 1('\0') + LONG
251 len = strlen(ln) + 1 + sizeof(LONG);
252 // p = (LONG *)amem(len);
253 p = (LONG *)malloc(len);
256 strcpy((char *)(p + 1), ln);
260 firstrpt = p; // First line of rept statement
275 // Define a .rept block, this gets hairy because they can be nested
283 // Evaluate repeat expression
284 if (abs_expr(&eval) != OK)
287 // Suck in lines for .rept block
291 lncatch(defr1, "endr rept ");
293 // Alloc and init input object
296 inobj = a_inobj(SRC_IREPT); // Create a new REPT input object
297 irept = inobj->inobj.irept;
298 irept->ir_firstln = firstrpt;
299 irept->ir_nextln = NULL;
300 irept->ir_count = eval;
308 // Hand off lines of text to the function `lnfunc' until a line containing one
309 // of the directives in `dirlist' is encountered. Return the number of the
310 // keyword encountered (0..n)
312 // `dirlist' contains null-seperated terminated keywords. A final null
313 // terminates the list. Directives are compared to the keywords without regard
316 // If `lnfunc' is NULL, then lines are simply skipped.
317 // If `lnfunc' returns an error, processing is stopped.
319 // `lnfunc' is called with an argument of -1 for every line but the last one,
320 // when it is called with an argument of the keyword number that caused the
323 int lncatch(int (* lnfunc)(), char * dirlist)
329 lnsave++; // Tell tokenizer to keep lines
333 if (TokenizeLine() == TKEOF)
335 error("encountered end-of-file looking for '%s'", dirlist);
336 fatal("cannot continue");
339 // Test for end condition. Two cases to handle:
341 // symbol: <directive>
347 if ((tok[2] == ':' || tok[2] == DCOLON))
349 if (tok[3] == SYMBOL) // label: symbol
354 p = string[tok[1]]; // Symbol
360 if (*p == '.') // ignore leading '.'s
363 k = kwmatch(p, dirlist);
366 // Hand-off line to function
367 // if it returns 0, and we found a keyword, stop looking.
368 // if it returns 1, hand off the line and keep looking.
370 k = (*lnfunc)(lnbuf, k);
377 lnsave--; // Tell tokenizer to stop keeping lines
384 // See if the string `kw' matches one of the keywords in `kwlist'. If so,
385 // return the number of the keyword matched. Return -1 if there was no match.
386 // Strings are compared without regard for case.
388 int kwmatch(char * kw, char * kwlist)
395 for(k=0; *kwlist; ++k)
402 if (c2 >= 'A' && c2 <= 'Z')
405 if (c1 == ' ' && c2 == EOS)
412 // Skip to beginning of next keyword in `kwlist'
413 while (*kwlist && *kwlist != ' ')
426 // o parse, count and copy arguments
427 // o push macro's string-stream
429 int InvokeMacro(SYM * mac, WORD siz)
434 // TOKEN ** argptr = NULL;
435 //Doesn't need to be global! (or does it???--it does)
437 DEBUG printf("InvokeMacro: argp: %d -> ", argp);
439 INOBJ * inobj = a_inobj(SRC_IMACRO); // Alloc and init IMACRO
440 IMACRO * imacro = inobj->inobj.imacro;
441 imacro->im_siz = siz;
443 TOKEN * beg_tok = tok; // 'tok' comes from token.c
449 for(dry_run=1; ; dry_run--)
451 for(tok=beg_tok; *tok!=EOL;)
465 // Keep going while tok isn't pointing at a comma or EOL
466 while (*tok != ',' && *tok != EOL)
468 // Skip over backslash character, unless it's followed by an EOL
469 if (*tok == '\\' && tok[1] != EOL)
476 //Shamus: Possible bug. ACONST has 2 tokens after it, not just 1
480 arg_siz += sizeof(TOKEN);
487 // FALLTHROUGH (picks up the arg after a CONST, SYMBOL or ACONST)
491 arg_siz += sizeof(TOKEN);
503 // We hit the comma or EOL, so count/stuff it
505 arg_siz += sizeof(TOKEN);
509 // If we hit the comma instead of an EOL, skip over it
513 // Do our QnD token grabbing (this will be redone once we get all
514 // the data structures fixed as this is a really dirty hack)
517 dest = imacro->argument[argumentNum].token;
522 // Remap strings to point the IMACRO internal token storage
523 if (*startOfArg == SYMBOL || *startOfArg == STRING)
525 *dest++ = *startOfArg++;
526 imacro->argument[argumentNum].string[stringNum] = strdup(string[*startOfArg++]);
527 *dest++ = stringNum++;
530 *dest++ = *startOfArg++;
532 while (*startOfArg != EOL);
534 *dest = *startOfArg; // Copy EOL...
539 // Allocate space for argument ptrs and so on and then go back and
540 // construct the arg frame
544 p = (TOKEN *)malloc(arg_siz);
545 // p = (TOKEN *)malloc(arg_siz + sizeof(TOKEN));
549 This construct is meant to deal with nested macros, so the simple minded way
550 we deal with them now won't work. :-/ Have to think about how to fix.
551 What we could do is simply move the argp with each call, and move it back by
552 the number of arguments in the macro that's ending. That would solve the
554 [Which we do now. But that uncovered another problem: the token strings are all
555 stale by the time a nested macro gets to the end. But they're supposed to be
556 symbols, which means if we put symbol references into the argument token
557 streams, we can alleviate this problem.]
560 argptr = (TOKEN **)malloc((nargs + 1) * sizeof(LONG));
561 *argptr++ = (TOKEN *)argp;
564 // We don't need to do anything here since we already advance argp
565 // when parsing the arguments.
573 DEBUG printf("%d\n", argp);
577 // o -> macro symbol;
578 // o -> macro definition string list;
579 // o save 'curuniq', to be restored when the macro pops;
580 // o bump `macuniq' counter and set 'curuniq' to it;
581 imacro->im_nargs = nargs;
582 imacro->im_macro = mac;
583 // imacro->im_nextln = (TOKEN *)mac->svalue;
584 imacro->im_nextln = mac->lineList;
585 imacro->im_olduniq = curuniq;
587 imacro->argBase = argp - nargs; // Shamus: keep track of argument base
591 printf("nargs=%d\n", nargs);
593 for(nargs=0; nargs<imacro->im_nargs; nargs++)
595 printf("arg%d=", nargs);
596 // dumptok(argp[imacro->im_nargs - nargs - 1]);
597 // dumptok(argPtrs[imacro->im_nargs - nargs - 1]);
598 dumptok(argPtrs[(argp - imacro->im_nargs) + nargs]);