2 // RMAC - Reboot's Macro Assembler for all Atari computers
3 // MACRO.C - Macro Definition and Invocation
4 // Copyright (C) 199x Landon Dyer, 2011-2020 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
23 static LONG macuniq; // Unique-per-macro number
24 static SYM * curmac; // Macro currently being defined
25 static uint32_t argno; // Formal argument count
27 static LLIST * firstrpt; // First .rept line
28 static LLIST * nextrpt; // Last .rept line
29 static int rptlevel; // .rept nesting level
31 // Function prototypes
32 static int KWMatch(char *, char *);
33 static int LNCatch(int (*)(), char *);
37 // Initialize macro processor
48 // -- pop any intervening include files and repeat blocks;
49 // -- restore argument stack;
54 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 Is this still true, now that we have IMACROs with TOKENSTREAMs in them? Need to
61 check it out for sure...!
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 DEBUG { printf("ExitMacro: nargs = %d\n", imacro->im_nargs); }
85 // Add a formal argument to a macro definition
87 int defmac2(char * argname)
89 if (lookup(argname, MACARG, (int)curmac->sattr) != NULL)
90 return error("multiple formal argument definition");
92 SYM * arg = NewSymbol(argname, MACARG, (int)curmac->sattr);
93 arg->svalue = argno++;
100 // Add a line to a macro definition; also print lines to listing file (if
101 // enabled). The last line of the macro (containing .endm) is not included in
102 // the macro. A label on that line will be lost.
103 // notEndFlag is -1 for all lines but the last one (.endm), when it is 0.
105 int defmac1(char * ln, int notEndFlag)
109 listeol(); // Flush previous source line
110 lstout('.'); // Mark macro definition with period
115 if (curmac->lineList == NULL)
117 curmac->lineList = malloc(sizeof(LLIST));
118 curmac->lineList->next = NULL;
119 curmac->lineList->line = strdup(ln);
120 curmac->lineList->lineno = curlineno;
121 curmac->last = curmac->lineList;
125 curmac->last->next = malloc(sizeof(LLIST));
126 curmac->last->next->next = NULL;
127 curmac->last->next->line = strdup(ln);
128 curmac->lineList->lineno = curlineno;
129 curmac->last = curmac->last->next;
132 return 1; // Keep looking
135 return 0; // Stop looking; at the end
142 // macro foo arg1,arg2,...
149 // `defmac1' adds lines of text to the macro definition
150 // `defmac2' processes the formal arguments (and sticks them into the symbol
153 int DefineMacro(void)
155 // Setup entry in symbol table, make sure the macro isn't a duplicate
156 // entry, and that it doesn't override any processor mnemonic or assembler
158 if (*tok++ != SYMBOL)
159 return error("missing symbol");
161 char * name = string[*tok++];
163 if (lookup(name, MACRO, 0) != NULL)
164 return error("duplicate macro definition");
166 curmac = NewSymbol(name, MACRO, 0);
168 curmac->sattr = (WORD)(macnum++);
170 // Parse and define formal arguments in symbol table
178 // Suck in the macro definition; we're looking for an ENDM symbol on a line
179 // by itself to terminate the definition.
181 curmac->lineList = NULL;
182 LNCatch(defmac1, "endm ");
189 // Add lines to a .rept definition
191 int defr1(char * line, int kwno)
195 listeol(); // Flush previous source line
196 lstout('#'); // Mark this a 'rept' block
199 if (kwno == 0) // .endr
204 else if (kwno == 1) // .rept
207 //DEBUG { printf(" defr1: line=\"%s\", kwno=%d, rptlevel=%d\n", line, kwno, rptlevel); }
210 //MORE stupidity here...
211 WARNING("!!! Casting (char *) as LONG !!!")
212 // Allocate length of line + 1('\0') + LONG
213 LONG * p = (LONG *)malloc(strlen(line) + 1 + sizeof(LONG));
215 strcpy((char *)(p + 1), line);
218 firstrpt = p; // First line of rept statement
224 if (firstrpt == NULL)
226 firstrpt = malloc(sizeof(LLIST));
227 firstrpt->next = NULL;
228 firstrpt->line = strdup(line);
229 firstrpt->lineno = curlineno;
234 nextrpt->next = malloc(sizeof(LLIST));
235 nextrpt->next->next = NULL;
236 nextrpt->next->line = strdup(line);
237 nextrpt->next->lineno = curlineno;
238 nextrpt = nextrpt->next;
247 // Handle a .rept block; this gets hairy because they can be nested
253 // Evaluate repeat expression
254 if (abs_expr(&eval) != OK)
257 // Suck in lines for .rept block
261 LNCatch(defr1, "endr rept ");
263 //DEBUG { printf("HandleRept: firstrpt=$%X\n", firstrpt); }
264 // Alloc and init input object
267 INOBJ * inobj = a_inobj(SRC_IREPT); // Create a new REPT input object
268 IREPT * irept = inobj->inobj.irept;
269 irept->ir_firstln = firstrpt;
270 irept->ir_nextln = NULL;
271 irept->ir_count = (uint32_t)eval;
279 // Hand off lines of text to the function 'lnfunc' until a line containing one
280 // of the directives in 'dirlist' is encountered.
282 // 'dirlist' contains space-separated terminated keywords. A final space
283 // terminates the list. Directives are case-insensitively compared to the
286 // If 'lnfunc' is NULL, then lines are simply skipped.
287 // If 'lnfunc' returns an error, processing is stopped.
289 // 'lnfunc' is called with an argument of -1 for every line but the last one,
290 // when it is called with an argument of the keyword number that caused the
293 static int LNCatch(int (* lnfunc)(), char * dirlist)
296 lnsave++; // Tell tokenizer to keep lines
300 if (TokenizeLine() == TKEOF)
302 error("encountered end-of-file looking for '%s'", dirlist);
303 fatal("cannot continue");
306 DEBUG { DumpTokenBuffer(); }
308 // Test for end condition. Two cases to handle:
310 // symbol: <directive>
316 // A string followed by a colon or double colon is a symbol and
317 // *not* a directive, see if we can find the directive after it
318 if ((tok[2] == ':' || tok[2] == DCOLON))
320 if (tok[3] == SYMBOL)
325 // Otherwise, just grab the directive
332 if (*p == '.') // Ignore leading periods
335 k = KWMatch(p, dirlist);
338 // Hand-off line to function
339 // if it returns 0, and we found a keyword, stop looking.
340 // if it returns 1, hand off the line and keep looking.
342 k = (*lnfunc)(lnbuf, k);
349 lnsave--; // Tell tokenizer to stop keeping lines
356 // See if the string `kw' matches one of the keywords in `kwlist'. If so,
357 // return the number of the keyword matched. Return -1 if there was no match.
358 // Strings are compared without regard for case.
360 static int KWMatch(char * kw, char * kwlist)
362 for(int k=0; *kwlist; k++)
369 if (c2 >= 'A' && c2 <= 'Z')
372 if (c1 == ' ' && c2 == EOS)
379 // Skip to beginning of next keyword in `kwlist'
380 while (*kwlist && (*kwlist != ' '))
392 // Invoke a macro by creating a new IMACRO object & chopping up the arguments
394 int InvokeMacro(SYM * mac, WORD siz)
396 DEBUG { printf("InvokeMacro: arguments="); DumpTokens(tok); }
398 INOBJ * inobj = a_inobj(SRC_IMACRO); // Alloc and init IMACRO
399 IMACRO * imacro = inobj->inobj.imacro;
402 // Chop up the arguments, if any (tok comes from token.c, which at this
403 // point points at the macro argument token stream)
406 // Parse out the arguments and set them up correctly
407 TOKEN * p = imacro->argument[nargs].token;
414 for(int i=0; i<3; i++)
417 else if (*tok == CONST) // Constants are 64-bits
419 *p++ = *tok++; // Token
420 uint64_t *p64 = (uint64_t *)p;
421 uint64_t *tok64 = (uint64_t *)tok;
423 tok = (TOKEN *)tok64;
426 else if ((*tok == STRING) || (*tok == SYMBOL))
429 imacro->argument[nargs].string[stringNum] = strdup(string[*tok++]);
432 else if (*tok == ',')
434 // Comma delimiter was found, so set up for next argument
439 p = imacro->argument[nargs].token;
447 // Make sure to stuff the final EOL (otherwise, it will be skipped)
454 // o -> macro symbol;
455 // o -> macro definition string list;
456 // o save 'curuniq', to be restored when the macro pops;
457 // o bump `macuniq' counter and set 'curuniq' to it;
458 imacro->im_nargs = nargs;
459 imacro->im_macro = mac;
460 imacro->im_siz = siz;
461 imacro->im_nextln = mac->lineList;
462 imacro->im_olduniq = curuniq;
467 printf("# args = %d\n", nargs);
469 for(uint16_t i=0; i<nargs; i++)
472 DumpTokens(imacro->argument[i].token);