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
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->last = curmac->lineList;
124 curmac->last->next = malloc(sizeof(LLIST));
125 curmac->last->next->next = NULL;
126 curmac->last->next->line = strdup(ln);
127 curmac->last = curmac->last->next;
130 return 1; // Keep looking
133 return 0; // Stop looking; at the end
140 // macro foo arg1,arg2,...
147 // `defmac1' adds lines of text to the macro definition
148 // `defmac2' processes the formal arguments (and sticks them into the symbol
151 int DefineMacro(void)
153 // Setup entry in symbol table, make sure the macro isn't a duplicate
154 // entry, and that it doesn't override any processor mnemonic or assembler
156 if (*tok++ != SYMBOL)
157 return error("missing symbol");
159 char * name = string[*tok++];
161 if (lookup(name, MACRO, 0) != NULL)
162 return error("duplicate macro definition");
164 curmac = NewSymbol(name, MACRO, 0);
166 curmac->sattr = (WORD)(macnum++);
168 // Parse and define formal arguments in symbol table
176 // Suck in the macro definition; we're looking for an ENDM symbol on a line
177 // by itself to terminate the definition.
179 curmac->lineList = NULL;
180 LNCatch(defmac1, "endm ");
187 // Add lines to a .rept definition
189 int defr1(char * line, int kwno)
193 listeol(); // Flush previous source line
194 lstout('#'); // Mark this a 'rept' block
197 if (kwno == 0) // .endr
202 else if (kwno == 1) // .rept
205 //DEBUG { printf(" defr1: line=\"%s\", kwno=%d, rptlevel=%d\n", line, kwno, rptlevel); }
208 //MORE stupidity here...
209 WARNING("!!! Casting (char *) as LONG !!!")
210 // Allocate length of line + 1('\0') + LONG
211 LONG * p = (LONG *)malloc(strlen(line) + 1 + sizeof(LONG));
213 strcpy((char *)(p + 1), line);
216 firstrpt = p; // First line of rept statement
222 if (firstrpt == NULL)
224 firstrpt = malloc(sizeof(LLIST));
225 firstrpt->next = NULL;
226 firstrpt->line = strdup(line);
231 nextrpt->next = malloc(sizeof(LLIST));
232 nextrpt->next->next = NULL;
233 nextrpt->next->line = strdup(line);
234 nextrpt = nextrpt->next;
243 // Handle a .rept block; this gets hairy because they can be nested
249 // Evaluate repeat expression
250 if (abs_expr(&eval) != OK)
253 // Suck in lines for .rept block
257 LNCatch(defr1, "endr rept ");
259 //DEBUG { printf("HandleRept: firstrpt=$%X\n", firstrpt); }
260 // Alloc and init input object
263 INOBJ * inobj = a_inobj(SRC_IREPT); // Create a new REPT input object
264 IREPT * irept = inobj->inobj.irept;
265 irept->ir_firstln = firstrpt;
266 irept->ir_nextln = NULL;
267 irept->ir_count = eval;
275 // Hand off lines of text to the function 'lnfunc' until a line containing one
276 // of the directives in 'dirlist' is encountered.
278 // 'dirlist' contains space-separated terminated keywords. A final space
279 // terminates the list. Directives are case-insensitively compared to the
282 // If 'lnfunc' is NULL, then lines are simply skipped.
283 // If 'lnfunc' returns an error, processing is stopped.
285 // 'lnfunc' is called with an argument of -1 for every line but the last one,
286 // when it is called with an argument of the keyword number that caused the
289 static int LNCatch(int (* lnfunc)(), char * dirlist)
292 lnsave++; // Tell tokenizer to keep lines
296 if (TokenizeLine() == TKEOF)
298 error("encountered end-of-file looking for '%s'", dirlist);
299 fatal("cannot continue");
302 DEBUG { DumpTokenBuffer(); }
304 // Test for end condition. Two cases to handle:
306 // symbol: <directive>
312 // A string followed by a colon or double colon is a symbol and
313 // *not* a directive, see if we can find the directive after it
314 if ((tok[2] == ':' || tok[2] == DCOLON))
316 if (tok[3] == SYMBOL)
321 // Otherwise, just grab the directive
328 if (*p == '.') // Ignore leading periods
331 k = KWMatch(p, dirlist);
334 // Hand-off line to function
335 // if it returns 0, and we found a keyword, stop looking.
336 // if it returns 1, hand off the line and keep looking.
338 k = (*lnfunc)(lnbuf, k);
345 lnsave--; // Tell tokenizer to stop keeping lines
352 // See if the string `kw' matches one of the keywords in `kwlist'. If so,
353 // return the number of the keyword matched. Return -1 if there was no match.
354 // Strings are compared without regard for case.
356 static int KWMatch(char * kw, char * kwlist)
358 for(int k=0; *kwlist; k++)
365 if (c2 >= 'A' && c2 <= 'Z')
368 if (c1 == ' ' && c2 == EOS)
375 // Skip to beginning of next keyword in `kwlist'
376 while (*kwlist && (*kwlist != ' '))
388 // Invoke a macro by creating a new IMACRO object & chopping up the arguments
390 int InvokeMacro(SYM * mac, WORD siz)
392 DEBUG { printf("InvokeMacro: arguments="); DumpTokens(tok); }
394 INOBJ * inobj = a_inobj(SRC_IMACRO); // Alloc and init IMACRO
395 IMACRO * imacro = inobj->inobj.imacro;
398 // Chop up the arguments, if any (tok comes from token.c, which at this
399 // point points at the macro argument token stream)
402 // Parse out the arguments and set them up correctly
403 TOKEN * p = imacro->argument[nargs].token;
410 for(int i=0; i<3; i++)
413 else if (*tok == CONST)
418 else if ((*tok == STRING) || (*tok == SYMBOL))
421 imacro->argument[nargs].string[stringNum] = strdup(string[*tok++]);
424 else if (*tok == ',')
426 // Comma delimiter was found, so set up for next argument
431 p = imacro->argument[nargs].token;
439 // Make sure to stuff the final EOL (otherwise, it will be skipped)
446 // o -> macro symbol;
447 // o -> macro definition string list;
448 // o save 'curuniq', to be restored when the macro pops;
449 // o bump `macuniq' counter and set 'curuniq' to it;
450 imacro->im_nargs = nargs;
451 imacro->im_macro = mac;
452 imacro->im_siz = siz;
453 imacro->im_nextln = mac->lineList;
454 imacro->im_olduniq = curuniq;
459 printf("# args = %d\n", nargs);
461 for(uint16_t i=0; i<nargs; i++)
464 DumpTokens(imacro->argument[i].token);