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 LLIST * firstrpt; // First .rept line
30 static LLIST * nextrpt; // Last .rept line
31 static int rptlevel; // .rept nesting level
33 // Function prototypes
34 static int KWMatch(char *, char *);
35 static int LNCatch(int (*)(), char *);
39 // Initialize macro processor
51 // -- pop any intervening include files and repeat blocks;
52 // -- restore argument stack;
57 WARNING(!!! Bad macro exiting !!!)
59 This is a problem. Currently, the argument logic just keeps the current
60 arguments and doesn't save anything if a new macro is called in the middle
61 of another (nested macros). Need to fix that somehow.
63 // Pop intervening include files and .rept blocks
64 while (cur_inobj != NULL && cur_inobj->in_type != SRC_IMACRO)
67 if (cur_inobj == NULL)
68 fatal("too many ENDMs");
72 // o old unique number
73 // ...and then pop the macro.
75 IMACRO * imacro = cur_inobj->inobj.imacro;
76 curuniq = imacro->im_olduniq;
78 // /*TOKEN ** p = */argp--;
79 // argp = (TOKEN **)*argp;
80 DEBUG { printf("ExitMacro: argp: %d -> ", argp); }
81 argp -= imacro->im_nargs;
82 DEBUG { printf("%d (nargs = %d)\n", argp, imacro->im_nargs); }
89 // Add a formal argument to a macro definition
91 int defmac2(char * argname)
93 if (lookup(argname, MACARG, (int)curmac->sattr) != NULL)
94 return error("multiple formal argument definition");
96 SYM * arg = NewSymbol(argname, MACARG, (int)curmac->sattr);
97 arg->svalue = argno++;
104 // Add a line to a macro definition; also print lines to listing file (if
105 // enabled). The last line of the macro (containing .endm) is not included in
106 // the macro. A label on that line will be lost.
107 // notEndFlag is -1 for all lines but the last one (.endm), when it is 0.
109 int defmac1(char * ln, int notEndFlag)
113 listeol(); // Flush previous source line
114 lstout('.'); // Mark macro definition with period
117 // This is just wrong, wrong, wrong. It makes up a weird kind of string with
118 // a pointer on front, and then uses a ** to manage them: This is a recipe
120 // How to manage it then?
121 // Could use a linked list, like Landon uses everywhere else.
124 Allocate a space big enough for the string + NULL + pointer.
125 Set the pointer to NULL.
126 Copy the string to the space after the pointer.
127 If this is the 1st time through, set the SYM * "svalue" to the pointer.
128 If this is the 2nd time through, derefence the ** to point to the memory you
129 just allocated. Then, set the ** to the location of the memory you allocated
130 for the next pass through.
132 This is a really low level way to do a linked list, and by bypassing all the
133 safety features of the language. Seems like we can do better here.
138 len = strlen(ln) + 1 + sizeof(LONG);
141 strcpy(p.cp + sizeof(LONG), ln);
143 // Link line of text onto end of list
145 curmac->svalue = p.cp;
149 curmln = (char **)p.cp;
150 return 1; // Keep looking
152 if (curmac->lineList == NULL)
154 curmac->lineList = malloc(sizeof(LLIST));
155 curmac->lineList->next = NULL;
156 curmac->lineList->line = strdup(ln);
157 curmac->last = curmac->lineList;
161 curmac->last->next = malloc(sizeof(LLIST));
162 curmac->last->next->next = NULL;
163 curmac->last->next->line = strdup(ln);
164 curmac->last = curmac->last->next;
167 return 1; // Keep looking
171 return 0; // Stop looking at the end
178 // macro foo arg1,arg2,...
185 // `defmac1' adds lines of text to the macro definition
186 // `defmac2' processes the formal arguments (and sticks them into the symbol
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 * line, int kwno)
231 listeol(); // Flush previous source line
232 lstout('#'); // Mark this a 'rept' block
235 if (kwno == 0) // .endr
240 else if (kwno == 1) // .rept
243 //DEBUG { printf(" defr1: line=\"%s\", kwno=%d, rptlevel=%d\n", line, kwno, rptlevel); }
246 //MORE stupidity here...
247 WARNING("!!! Casting (char *) as LONG !!!")
248 // Allocate length of line + 1('\0') + LONG
249 LONG * p = (LONG *)malloc(strlen(line) + 1 + sizeof(LONG));
251 strcpy((char *)(p + 1), line);
254 firstrpt = p; // First line of rept statement
260 if (firstrpt == NULL)
262 firstrpt = malloc(sizeof(LLIST));
263 firstrpt->next = NULL;
264 firstrpt->line = strdup(line);
269 nextrpt->next = malloc(sizeof(LLIST));
270 nextrpt->next->next = NULL;
271 nextrpt->next->line = strdup(line);
272 nextrpt = nextrpt->next;
281 // Handle a .rept block; this gets hairy because they can be nested
287 // Evaluate repeat expression
288 if (abs_expr(&eval) != OK)
291 // Suck in lines for .rept block
295 LNCatch(defr1, "endr rept ");
297 //DEBUG { printf("HandleRept: firstrpt=$%X\n", firstrpt); }
298 // Alloc and init input object
301 INOBJ * inobj = a_inobj(SRC_IREPT); // Create a new REPT input object
302 IREPT * irept = inobj->inobj.irept;
303 irept->ir_firstln = firstrpt;
304 irept->ir_nextln = NULL;
305 irept->ir_count = eval;
313 // Hand off lines of text to the function 'lnfunc' until a line containing one
314 // of the directives in 'dirlist' is encountered.
316 // 'dirlist' contains space-separated terminated keywords. A final space
317 // terminates the list. Directives are case-insensitively compared to the
320 // If 'lnfunc' is NULL, then lines are simply skipped.
321 // If 'lnfunc' returns an error, processing is stopped.
323 // 'lnfunc' is called with an argument of -1 for every line but the last one,
324 // when it is called with an argument of the keyword number that caused the
327 static int LNCatch(int (* lnfunc)(), char * dirlist)
330 lnsave++; // Tell tokenizer to keep lines
334 if (TokenizeLine() == TKEOF)
336 error("encountered end-of-file looking for '%s'", dirlist);
337 fatal("cannot continue");
340 // Test for end condition. Two cases to handle:
342 // symbol: <directive>
348 // A string followed by a colon or double colon is a symbol and
349 // *not* a directive, see if we can find the directive after it
350 if ((tok[2] == ':' || tok[2] == DCOLON))
352 if (tok[3] == SYMBOL)
357 // Otherwise, just grab the directive
364 if (*p == '.') // Ignore leading periods
367 k = KWMatch(p, dirlist);
370 // Hand-off line to function
371 // if it returns 0, and we found a keyword, stop looking.
372 // if it returns 1, hand off the line and keep looking.
374 k = (*lnfunc)(lnbuf, k);
381 lnsave--; // Tell tokenizer to stop keeping lines
388 // See if the string `kw' matches one of the keywords in `kwlist'. If so,
389 // return the number of the keyword matched. Return -1 if there was no match.
390 // Strings are compared without regard for case.
392 static int KWMatch(char * kw, char * kwlist)
394 for(int k=0; *kwlist; k++)
401 if (c2 >= 'A' && c2 <= 'Z')
404 if (c1 == ' ' && c2 == EOS)
411 // Skip to beginning of next keyword in `kwlist'
412 while (*kwlist && (*kwlist != ' '))
425 // o parse, count and copy arguments
426 // o push macro's string-stream
428 int InvokeMacro(SYM * mac, WORD siz)
433 // TOKEN ** argptr = NULL;
434 //Doesn't need to be global! (or does it???--it does)
436 DEBUG printf("InvokeMacro: argp: %d -> ", argp);
438 INOBJ * inobj = a_inobj(SRC_IMACRO); // Alloc and init IMACRO
439 IMACRO * imacro = inobj->inobj.imacro;
440 imacro->im_siz = siz;
442 TOKEN * beg_tok = tok; // 'tok' comes from token.c
448 for(dry_run=1; ; dry_run--)
450 for(tok=beg_tok; *tok!=EOL;)
464 // Keep going while tok isn't pointing at a comma or EOL
465 while (*tok != ',' && *tok != EOL)
467 // Skip over backslash character, unless it's followed by an EOL
468 if (*tok == '\\' && tok[1] != EOL)
475 //Shamus: Possible bug. ACONST has 2 tokens after it, not just 1
479 arg_siz += sizeof(TOKEN);
486 // FALLTHROUGH (picks up the arg after a CONST, SYMBOL or ACONST)
490 arg_siz += sizeof(TOKEN);
502 // We hit the comma or EOL, so count/stuff it
504 arg_siz += sizeof(TOKEN);
508 // If we hit the comma instead of an EOL, skip over it
512 // Do our QnD token grabbing (this will be redone once we get all
513 // the data structures fixed as this is a really dirty hack)
516 dest = imacro->argument[argumentNum].token;
521 // Remap strings to point the IMACRO internal token storage
522 if (*startOfArg == SYMBOL || *startOfArg == STRING)
524 *dest++ = *startOfArg++;
525 imacro->argument[argumentNum].string[stringNum] = strdup(string[*startOfArg++]);
526 *dest++ = stringNum++;
529 *dest++ = *startOfArg++;
531 while (*startOfArg != EOL);
533 *dest = *startOfArg; // Copy EOL...
538 // Allocate space for argument ptrs and so on and then go back and
539 // construct the arg frame
543 p = (TOKEN *)malloc(arg_siz);
544 // p = (TOKEN *)malloc(arg_siz + sizeof(TOKEN));
548 This construct is meant to deal with nested macros, so the simple minded way
549 we deal with them now won't work. :-/ Have to think about how to fix.
550 What we could do is simply move the argp with each call, and move it back by
551 the number of arguments in the macro that's ending. That would solve the
553 [Which we do now. But that uncovered another problem: the token strings are all
554 stale by the time a nested macro gets to the end. But they're supposed to be
555 symbols, which means if we put symbol references into the argument token
556 streams, we can alleviate this problem.]
559 argptr = (TOKEN **)malloc((nargs + 1) * sizeof(LONG));
560 *argptr++ = (TOKEN *)argp;
563 // We don't need to do anything here since we already advance argp
564 // when parsing the arguments.
572 DEBUG { printf("%d\n", argp); }
576 // o -> macro symbol;
577 // o -> macro definition string list;
578 // o save 'curuniq', to be restored when the macro pops;
579 // o bump `macuniq' counter and set 'curuniq' to it;
580 imacro->im_nargs = nargs;
581 imacro->im_macro = mac;
582 imacro->im_nextln = mac->lineList;
583 imacro->im_olduniq = curuniq;
585 imacro->argBase = argp - nargs; // Shamus: keep track of argument base
589 printf("nargs=%d\n", nargs);
591 for(nargs=0; nargs<imacro->im_nargs; nargs++)
593 printf("arg%d=", nargs);
594 dumptok(argPtrs[(argp - imacro->im_nargs) + nargs]);