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;
56 #warning !!! Bad macro exiting !!!
58 This is a problem. Currently, the argument logic just keeps the current
59 arguments and doesn't save anything if a new macro is called in the middle
60 of another (nested macros). Need to fix that somehow.
62 // Pop intervening include files and .rept blocks
63 while (cur_inobj != NULL && cur_inobj->in_type != SRC_IMACRO)
66 if (cur_inobj == NULL)
67 fatal("too many ENDMs");
71 // o old unique number
72 // ...and then pop the macro.
74 IMACRO * imacro = cur_inobj->inobj.imacro;
75 curuniq = imacro->im_olduniq;
77 // /*TOKEN ** p = */argp--;
78 // argp = (TOKEN **)*argp;
79 DEBUG printf("ExitMacro: argp: %d -> ", argp);
80 argp -= imacro->im_nargs;
81 DEBUG printf("%d (nargs = %d)\n", argp, imacro->im_nargs);
89 // Add a Formal Argument to a Macro Definition
91 int defmac2(char * argname)
95 if (lookup(argname, MACARG, (int)curmac->sattr) != NULL)
96 return error("multiple formal argument definition");
98 arg = NewSymbol(argname, MACARG, (int)curmac->sattr);
99 arg->svalue = argno++;
106 // Add a line to a macro definition; also print lines to listing file (if
107 // enabled). The last line of the macro (containing .endm) is not included in
108 // the macro. A label on that line will be lost.
109 // notEndFlag is -1 for all lines but the last one (.endm), when it is 0.
111 int defmac1(char * ln, int notEndFlag)
118 listeol(); // Flush previous source line
119 lstout('.'); // Mark macro definition with period
122 // This is just wrong, wrong, wrong. It makes up a weird kind of string with
123 // a pointer on front, and then uses a ** to manage them: This is a recipe
125 // How to manage it then?
126 // Could use a linked list, like Landon uses everywhere else.
129 Allocate a space big enough for the string + NULL + pointer.
130 Set the pointer to NULL.
131 Copy the string to the space after the pointer.
132 If this is the 1st time through, set the SYM * "svalue" to the pointer.
133 If this is the 2nd time through, derefence the ** to point to the memory you just allocated.
134 Then, set the ** to the location of the memory you allocated for the next pass through.
136 This is a really low level way to do a linked list, and by bypassing all the safety
137 features of the language. Seems like we can do better here.
142 len = strlen(ln) + 1 + sizeof(LONG);
145 strcpy(p.cp + sizeof(LONG), ln);
147 // Link line of text onto end of list
149 curmac->svalue = p.cp;
153 curmln = (char **)p.cp;
154 return 1; // Keep looking
156 if (curmac->lineList == NULL)
158 curmac->lineList = malloc(sizeof(struct LineList));
159 curmac->lineList->next = NULL;
160 curmac->lineList->line = strdup(ln);
161 curmac->last = curmac->lineList;
165 curmac->last->next = malloc(sizeof(struct LineList));
166 curmac->last->next->next = NULL;
167 curmac->last->next->line = strdup(ln);
168 curmac->last = curmac->last->next;
171 return 1; // Keep looking
175 return 0; // Stop looking at the end
182 // macro foo arg1,arg2,...
189 // `defmac1' adds lines of text to the macro definition
190 // `defmac2' processes the formal arguments (and sticks them into the symbol table)
192 int DefineMacro(void)
194 // Setup entry in symbol table, make sure the macro isn't a duplicate
195 // entry, and that it doesn't override any processor mnemonic or assembler
197 if (*tok++ != SYMBOL)
198 return error("missing symbol");
200 char * name = string[*tok++];
202 if (lookup(name, MACRO, 0) != NULL)
203 return error("duplicate macro definition");
205 curmac = NewSymbol(name, MACRO, 0);
207 curmac->sattr = (WORD)(macnum++);
209 // Parse and define formal arguments in symbol table
217 // Suck in the macro definition; we're looking for an ENDM symbol on a line
218 // by itself to terminate the definition.
220 curmac->lineList = NULL;
221 lncatch(defmac1, "endm ");
228 // Add lines to a .rept definition
230 int defr1(char * ln, int kwno)
237 listeol(); // Flush previous source line
238 lstout('#'); // Mark this a 'rept' block
250 //MORE stupidity here...
251 #warning "!!! Casting (char *) as LONG !!!"
253 // Allocate length of line + 1('\0') + LONG
254 len = strlen(ln) + 1 + sizeof(LONG);
255 // p = (LONG *)amem(len);
256 p = (LONG *)malloc(len);
259 strcpy((char *)(p + 1), ln);
263 firstrpt = p; // First line of rept statement
278 // Define a .rept block, this gets hairy because they can be nested
286 // Evaluate repeat expression
287 if (abs_expr(&eval) != OK)
290 // Suck in lines for .rept block
294 lncatch(defr1, "endr rept ");
296 // Alloc and init input object
299 inobj = a_inobj(SRC_IREPT); // Create a new REPT input object
300 irept = inobj->inobj.irept;
301 irept->ir_firstln = firstrpt;
302 irept->ir_nextln = NULL;
303 irept->ir_count = eval;
311 // Hand off lines of text to the function `lnfunc' until a line containing one
312 // of the directives in `dirlist' is encountered. Return the number of the
313 // keyword encountered (0..n)
315 // `dirlist' contains null-seperated terminated keywords. A final null
316 // terminates the list. Directives are compared to the keywords without regard
319 // If `lnfunc' is NULL, then lines are simply skipped.
320 // If `lnfunc' returns an error, processing is stopped.
322 // `lnfunc' is called with an argument of -1 for every line but the last one,
323 // when it is called with an argument of the keyword number that caused the
326 int lncatch(int (* lnfunc)(), char * dirlist)
332 lnsave++; // Tell tokenizer to keep lines
336 if (tokln() == TKEOF)
338 errors("encountered end-of-file looking for '%s'", dirlist);
339 fatal("cannot continue");
342 // Test for end condition. Two cases to handle:
344 // symbol: <directive>
350 if ((tok[2] == ':' || tok[2] == DCOLON))
352 if (tok[3] == SYMBOL) // label: symbol
362 p = (char *)tok[1]; // symbol
364 p = string[tok[1]]; // Symbol
371 if (*p == '.') // ignore leading '.'s
374 k = kwmatch(p, dirlist);
377 // Hand-off line to function
378 // if it returns 0, and we found a keyword, stop looking.
379 // if it returns 1, hand off the line and keep looking.
381 k = (*lnfunc)(lnbuf, k);
388 lnsave--; // Tell tokenizer to stop keeping lines
395 // See if the string `kw' matches one of the keywords in `kwlist'. If so,
396 // return the number of the keyword matched. Return -1 if there was no match.
397 // Strings are compared without regard for case.
399 int kwmatch(char * kw, char * kwlist)
406 for(k=0; *kwlist; ++k)
413 if (c2 >= 'A' && c2 <= 'Z')
416 if (c1 == ' ' && c2 == EOS)
423 // Skip to beginning of next keyword in `kwlist'
424 while (*kwlist && *kwlist != ' ')
437 // o parse, count and copy arguments
438 // o push macro's string-stream
440 int InvokeMacro(SYM * mac, WORD siz)
445 // TOKEN ** argptr = NULL;
446 //Doesn't need to be global! (or does it???--it does)
448 DEBUG printf("InvokeMacro: argp: %d -> ", argp);
450 INOBJ * inobj = a_inobj(SRC_IMACRO); // Alloc and init IMACRO
451 IMACRO * imacro = inobj->inobj.imacro;
452 imacro->im_siz = siz;
454 TOKEN * beg_tok = tok; // 'tok' comes from token.c
461 for(dry_run=1; ; dry_run--)
463 for(tok=beg_tok; *tok!=EOL;)
477 // Keep going while tok isn't pointing at a comma or EOL
478 while (*tok != ',' && *tok != EOL)
480 // Skip over backslash character, unless it's followed by an EOL
481 if (*tok == '\\' && tok[1] != EOL)
488 //Shamus: Possible bug. ACONST has 2 tokens after it, not just 1
492 arg_siz += sizeof(TOKEN);
499 // FALLTHROUGH (picks up the arg after a CONST, SYMBOL or ACONST)
503 arg_siz += sizeof(TOKEN);
515 // We hit the comma or EOL, so count/stuff it
517 arg_siz += sizeof(TOKEN);
521 // If we hit the comma instead of an EOL, skip over it
525 // Do our QnD token grabbing (this will be redone once we get all
526 // the data structures fixed as this is a really dirty hack)
529 dest = imacro->argument[argumentNum].token;
534 // Remap strings to point the IMACRO internal token storage
535 if (*startOfArg == SYMBOL || *startOfArg == STRING)
537 *dest++ = *startOfArg++;
538 imacro->argument[argumentNum].string[stringNum] = strdup(string[*startOfArg++]);
539 *dest++ = stringNum++;
542 *dest++ = *startOfArg++;
544 while (*startOfArg != EOL);
546 *dest = *startOfArg; // Copy EOL...
551 // Allocate space for argument ptrs and so on and then go back and
552 // construct the arg frame
556 p = (TOKEN *)malloc(arg_siz);
557 // p = (TOKEN *)malloc(arg_siz + sizeof(TOKEN));
561 This construct is meant to deal with nested macros, so the simple minded way
562 we deal with them now won't work. :-/ Have to think about how to fix.
563 What we could do is simply move the argp with each call, and move it back by the
564 number of arguments in the macro that's ending. That would solve the problem nicely.
565 [Which we do now. But that uncovered another problem: the token strings are all
566 stale by the time a nested macro gets to the end. But they're supposed to be symbols,
567 which means if we put symbol references into the argument token streams, we can
568 alleviate this problem.]
571 argptr = (TOKEN **)malloc((nargs + 1) * sizeof(LONG));
572 *argptr++ = (TOKEN *)argp;
575 // We don't need to do anything here since we already advance argp
576 // when parsing the arguments.
584 DEBUG printf("%d\n", argp);
588 // o -> macro symbol;
589 // o -> macro definition string list;
590 // o save 'curuniq', to be restored when the macro pops;
591 // o bump `macuniq' counter and set 'curuniq' to it;
592 imacro->im_nargs = nargs;
593 imacro->im_macro = mac;
594 // imacro->im_nextln = (TOKEN *)mac->svalue;
595 imacro->im_nextln = mac->lineList;
596 imacro->im_olduniq = curuniq;
598 imacro->argBase = argp - nargs; // Shamus: keep track of argument base
602 printf("nargs=%d\n", nargs);
604 for(nargs=0; nargs<imacro->im_nargs; nargs++)
606 printf("arg%d=", nargs);
607 // dumptok(argp[imacro->im_nargs - nargs - 1]);
608 // dumptok(argPtrs[imacro->im_nargs - nargs - 1]);
609 dumptok(argPtrs[(argp - imacro->im_nargs) + nargs]);