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
49 // -- pop any intervening include files and repeat blocks;
50 // -- restore argument stack;
55 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);
88 // Add a formal argument to a macro definition
90 int defmac2(char * argname)
94 if (lookup(argname, MACARG, (int)curmac->sattr) != NULL)
95 return error("multiple formal argument definition");
97 arg = NewSymbol(argname, MACARG, (int)curmac->sattr);
98 arg->svalue = argno++;
105 // Add a line to a macro definition; also print lines to listing file (if
106 // enabled). The last line of the macro (containing .endm) is not included in
107 // the macro. A label on that line will be lost.
108 // notEndFlag is -1 for all lines but the last one (.endm), when it is 0.
110 int defmac1(char * ln, int notEndFlag)
117 listeol(); // Flush previous source line
118 lstout('.'); // Mark macro definition with period
121 // This is just wrong, wrong, wrong. It makes up a weird kind of string with
122 // a pointer on front, and then uses a ** to manage them: This is a recipe
124 // How to manage it then?
125 // Could use a linked list, like Landon uses everywhere else.
128 Allocate a space big enough for the string + NULL + pointer.
129 Set the pointer to NULL.
130 Copy the string to the space after the pointer.
131 If this is the 1st time through, set the SYM * "svalue" to the pointer.
132 If this is the 2nd time through, derefence the ** to point to the memory you just allocated.
133 Then, set the ** to the location of the memory you allocated for the next pass through.
135 This is a really low level way to do a linked list, and by bypassing all the safety
136 features of the language. Seems like we can do better here.
141 len = strlen(ln) + 1 + sizeof(LONG);
144 strcpy(p.cp + sizeof(LONG), ln);
146 // Link line of text onto end of list
148 curmac->svalue = p.cp;
152 curmln = (char **)p.cp;
153 return 1; // Keep looking
155 if (curmac->lineList == NULL)
157 curmac->lineList = malloc(sizeof(struct LineList));
158 curmac->lineList->next = NULL;
159 curmac->lineList->line = strdup(ln);
160 curmac->last = curmac->lineList;
164 curmac->last->next = malloc(sizeof(struct LineList));
165 curmac->last->next->next = NULL;
166 curmac->last->next->line = strdup(ln);
167 curmac->last = curmac->last->next;
170 return 1; // Keep looking
174 return 0; // Stop looking at the end
181 // macro foo arg1,arg2,...
188 // `defmac1' adds lines of text to the macro definition
189 // `defmac2' processes the formal arguments (and sticks them into the symbol table)
191 int DefineMacro(void)
193 // Setup entry in symbol table, make sure the macro isn't a duplicate
194 // entry, and that it doesn't override any processor mnemonic or assembler
196 if (*tok++ != SYMBOL)
197 return error("missing symbol");
199 char * name = string[*tok++];
201 if (lookup(name, MACRO, 0) != NULL)
202 return error("duplicate macro definition");
204 curmac = NewSymbol(name, MACRO, 0);
206 curmac->sattr = (WORD)(macnum++);
208 // Parse and define formal arguments in symbol table
216 // Suck in the macro definition; we're looking for an ENDM symbol on a line
217 // by itself to terminate the definition.
219 curmac->lineList = NULL;
220 lncatch(defmac1, "endm ");
227 // Add lines to a .rept definition
229 int defr1(char * ln, int kwno)
236 listeol(); // Flush previous source line
237 lstout('#'); // Mark this a 'rept' block
249 //MORE stupidity here...
250 WARNING(!!! Casting (char *) as LONG !!!)
252 // Allocate length of line + 1('\0') + LONG
253 len = strlen(ln) + 1 + sizeof(LONG);
254 // p = (LONG *)amem(len);
255 p = (LONG *)malloc(len);
258 strcpy((char *)(p + 1), ln);
262 firstrpt = p; // First line of rept statement
277 // Define a .rept block, this gets hairy because they can be nested
285 // Evaluate repeat expression
286 if (abs_expr(&eval) != OK)
289 // Suck in lines for .rept block
293 lncatch(defr1, "endr rept ");
295 // Alloc and init input object
298 inobj = a_inobj(SRC_IREPT); // Create a new REPT input object
299 irept = inobj->inobj.irept;
300 irept->ir_firstln = firstrpt;
301 irept->ir_nextln = NULL;
302 irept->ir_count = eval;
310 // Hand off lines of text to the function `lnfunc' until a line containing one
311 // of the directives in `dirlist' is encountered. Return the number of the
312 // keyword encountered (0..n)
314 // `dirlist' contains null-seperated terminated keywords. A final null
315 // terminates the list. Directives are compared to the keywords without regard
318 // If `lnfunc' is NULL, then lines are simply skipped.
319 // If `lnfunc' returns an error, processing is stopped.
321 // `lnfunc' is called with an argument of -1 for every line but the last one,
322 // when it is called with an argument of the keyword number that caused the
325 int lncatch(int (* lnfunc)(), char * dirlist)
331 lnsave++; // Tell tokenizer to keep lines
335 if (TokenizeLine() == TKEOF)
337 errors("encountered end-of-file looking for '%s'", dirlist);
338 fatal("cannot continue");
341 // Test for end condition. Two cases to handle:
343 // symbol: <directive>
349 if ((tok[2] == ':' || tok[2] == DCOLON))
351 if (tok[3] == SYMBOL) // label: symbol
356 p = string[tok[1]]; // Symbol
362 if (*p == '.') // ignore leading '.'s
365 k = kwmatch(p, dirlist);
368 // Hand-off line to function
369 // if it returns 0, and we found a keyword, stop looking.
370 // if it returns 1, hand off the line and keep looking.
372 k = (*lnfunc)(lnbuf, k);
379 lnsave--; // Tell tokenizer to stop keeping lines
386 // See if the string `kw' matches one of the keywords in `kwlist'. If so,
387 // return the number of the keyword matched. Return -1 if there was no match.
388 // Strings are compared without regard for case.
390 int kwmatch(char * kw, char * kwlist)
397 for(k=0; *kwlist; ++k)
404 if (c2 >= 'A' && c2 <= 'Z')
407 if (c1 == ' ' && c2 == EOS)
414 // Skip to beginning of next keyword in `kwlist'
415 while (*kwlist && *kwlist != ' ')
428 // o parse, count and copy arguments
429 // o push macro's string-stream
431 int InvokeMacro(SYM * mac, WORD siz)
436 // TOKEN ** argptr = NULL;
437 //Doesn't need to be global! (or does it???--it does)
439 DEBUG printf("InvokeMacro: argp: %d -> ", argp);
441 INOBJ * inobj = a_inobj(SRC_IMACRO); // Alloc and init IMACRO
442 IMACRO * imacro = inobj->inobj.imacro;
443 imacro->im_siz = siz;
445 TOKEN * beg_tok = tok; // 'tok' comes from token.c
452 for(dry_run=1; ; dry_run--)
454 for(tok=beg_tok; *tok!=EOL;)
468 // Keep going while tok isn't pointing at a comma or EOL
469 while (*tok != ',' && *tok != EOL)
471 // Skip over backslash character, unless it's followed by an EOL
472 if (*tok == '\\' && tok[1] != EOL)
479 //Shamus: Possible bug. ACONST has 2 tokens after it, not just 1
483 arg_siz += sizeof(TOKEN);
490 // FALLTHROUGH (picks up the arg after a CONST, SYMBOL or ACONST)
494 arg_siz += sizeof(TOKEN);
506 // We hit the comma or EOL, so count/stuff it
508 arg_siz += sizeof(TOKEN);
512 // If we hit the comma instead of an EOL, skip over it
516 // Do our QnD token grabbing (this will be redone once we get all
517 // the data structures fixed as this is a really dirty hack)
520 dest = imacro->argument[argumentNum].token;
525 // Remap strings to point the IMACRO internal token storage
526 if (*startOfArg == SYMBOL || *startOfArg == STRING)
528 *dest++ = *startOfArg++;
529 imacro->argument[argumentNum].string[stringNum] = strdup(string[*startOfArg++]);
530 *dest++ = stringNum++;
533 *dest++ = *startOfArg++;
535 while (*startOfArg != EOL);
537 *dest = *startOfArg; // Copy EOL...
542 // Allocate space for argument ptrs and so on and then go back and
543 // construct the arg frame
547 p = (TOKEN *)malloc(arg_siz);
548 // p = (TOKEN *)malloc(arg_siz + sizeof(TOKEN));
552 This construct is meant to deal with nested macros, so the simple minded way
553 we deal with them now won't work. :-/ Have to think about how to fix.
554 What we could do is simply move the argp with each call, and move it back by
555 the number of arguments in the macro that's ending. That would solve the
557 [Which we do now. But that uncovered another problem: the token strings are all
558 stale by the time a nested macro gets to the end. But they're supposed to be
559 symbols, which means if we put symbol references into the argument token
560 streams, we can alleviate this problem.]
563 argptr = (TOKEN **)malloc((nargs + 1) * sizeof(LONG));
564 *argptr++ = (TOKEN *)argp;
567 // We don't need to do anything here since we already advance argp
568 // when parsing the arguments.
576 DEBUG printf("%d\n", argp);
580 // o -> macro symbol;
581 // o -> macro definition string list;
582 // o save 'curuniq', to be restored when the macro pops;
583 // o bump `macuniq' counter and set 'curuniq' to it;
584 imacro->im_nargs = nargs;
585 imacro->im_macro = mac;
586 // imacro->im_nextln = (TOKEN *)mac->svalue;
587 imacro->im_nextln = mac->lineList;
588 imacro->im_olduniq = curuniq;
590 imacro->argBase = argp - nargs; // Shamus: keep track of argument base
594 printf("nargs=%d\n", nargs);
596 for(nargs=0; nargs<imacro->im_nargs; nargs++)
598 printf("arg%d=", nargs);
599 // dumptok(argp[imacro->im_nargs - nargs - 1]);
600 // dumptok(argPtrs[imacro->im_nargs - nargs - 1]);
601 dumptok(argPtrs[(argp - imacro->im_nargs) + nargs]);