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
19 LONG curuniq; // Current macro's unique number
20 //TOKEN ** argp; // Free spot in argptrs[]
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 char ** curmln; // Previous macro line (or NULL)
28 static VALUE argno; // Formal argument count
30 static LONG * firstrpt; // First .rept line
31 static LONG * nextrpt; // Last .rept line
32 static int rptlevel; // .rept nesting level
36 // Initialize Macro Processor
50 // -- pop any intervening include files and repeat blocks;
51 // -- restore argument stack;
56 // Pop intervening include files and .rept blocks
57 while (cur_inobj != NULL && cur_inobj->in_type != SRC_IMACRO)
60 if (cur_inobj == NULL)
61 fatal("too many ENDMs");
65 // o old unique number
66 // ...and then pop the macro.
68 IMACRO * imacro = cur_inobj->inobj.imacro;
69 curuniq = imacro->im_olduniq;
71 /*TOKEN ** p = */argp--;
72 // argp = (TOKEN **)*argp;
81 // Add a Formal Argument to a Macro Definition
83 int defmac2(char * argname)
87 if (lookup(argname, MACARG, (int)curmac->sattr) != NULL)
88 return error("multiple formal argument definition");
90 arg = NewSymbol(argname, MACARG, (int)curmac->sattr);
91 arg->svalue = argno++;
98 // Add a line to a macro definition; also print lines to listing file (if
99 // enabled). The last line of the macro (containing .endm) is not included in
100 // the macro. A label on that line will be lost.
101 // notEndFlag is -1 for all lines but the last one (.endm), when it is 0.
103 int defmac1(char * ln, int notEndFlag)
110 listeol(); // Flush previous source line
111 lstout('.'); // Mark macro definition with period
114 // This is just wrong, wrong, wrong. It makes up a weird kind of string with
115 // a pointer on front, and then uses a ** to manage them: This is a recipe
117 // How to manage it then?
118 // Could use a linked list, like Landon uses everywhere else.
121 Allocate a space big enough for the string + NULL + pointer.
122 Set the pointer to NULL.
123 Copy the string to the space after the pointer.
124 If this is the 1st time through, set the SYM * "svalue" to the pointer.
125 If this is the 2nd time through, derefence the ** to point to the memory you just allocated.
126 Then, set the ** to the location of the memory you allocated for the next pass through.
128 This is a really low level way to do a linked list, and by bypassing all the safety
129 features of the language. Seems like we can do better here.
134 len = strlen(ln) + 1 + sizeof(LONG);
137 strcpy(p.cp + sizeof(LONG), ln);
139 // Link line of text onto end of list
141 curmac->svalue = p.cp;
145 curmln = (char **)p.cp;
146 return 1; // Keep looking
148 if (curmac->lineList == NULL)
150 curmac->lineList = malloc(sizeof(struct LineList));
151 curmac->lineList->next = NULL;
152 curmac->lineList->line = strdup(ln);
153 curmac->last = curmac->lineList;
157 curmac->last->next = malloc(sizeof(struct LineList));
158 curmac->last->next->next = NULL;
159 curmac->last->next->line = strdup(ln);
160 curmac->last = curmac->last->next;
163 return 1; // Keep looking
167 return 0; // Stop looking at the end
174 // macro foo arg1,arg2,...
181 // `defmac1' adds lines of text to the macro definition
182 // `defmac2' processes the formal arguments (and sticks them into the symbol table)
184 int DefineMacro(void)
186 // Setup entry in symbol table, make sure the macro isn't a duplicate
187 // entry, and that it doesn't override any processor mnemonic or assembler
189 if (*tok++ != SYMBOL)
190 return error("missing symbol");
192 char * name = string[*tok++];
194 if (lookup(name, MACRO, 0) != NULL)
195 return error("duplicate macro definition");
197 curmac = NewSymbol(name, MACRO, 0);
199 curmac->sattr = (WORD)(macnum++);
201 // Parse and define formal arguments in symbol table
209 // Suck in the macro definition; we're looking for an ENDM symbol on a line
210 // by itself to terminate the definition.
212 curmac->lineList = NULL;
213 lncatch(defmac1, "endm ");
220 // Add lines to a .rept definition
222 int defr1(char * ln, int kwno)
229 listeol(); // Flush previous source line
230 lstout('#'); // Mark this a 'rept' block
242 //MORE stupidity here...
243 #warning "!!! Casting (char *) as LONG !!!"
245 // Allocate length of line + 1('\0') + LONG
246 len = strlen(ln) + 1 + sizeof(LONG);
247 // p = (LONG *)amem(len);
248 p = (LONG *)malloc(len);
251 strcpy((char *)(p + 1), ln);
255 firstrpt = p; // First line of rept statement
270 // Define a .rept block, this gets hairy because they can be nested
278 // Evaluate repeat expression
279 if (abs_expr(&eval) != OK)
282 // Suck in lines for .rept block
286 lncatch(defr1, "endr rept ");
288 // Alloc and init input object
291 inobj = a_inobj(SRC_IREPT); // Create a new REPT input object
292 irept = inobj->inobj.irept;
293 irept->ir_firstln = firstrpt;
294 irept->ir_nextln = NULL;
295 irept->ir_count = eval;
303 // Hand off lines of text to the function `lnfunc' until a line containing one
304 // of the directives in `dirlist' is encountered. Return the number of the
305 // keyword encountered (0..n)
307 // `dirlist' contains null-seperated terminated keywords. A final null
308 // terminates the list. Directives are compared to the keywords without regard
311 // If `lnfunc' is NULL, then lines are simply skipped.
312 // If `lnfunc' returns an error, processing is stopped.
314 // `lnfunc' is called with an argument of -1 for every line but the last one,
315 // when it is called with an argument of the keyword number that caused the
318 int lncatch(int (* lnfunc)(), char * dirlist)
324 lnsave++; // Tell tokenizer to keep lines
328 if (tokln() == TKEOF)
330 errors("encountered end-of-file looking for '%s'", dirlist);
331 fatal("cannot continue");
334 // Test for end condition. Two cases to handle:
336 // symbol: <directive>
342 if ((tok[2] == ':' || tok[2] == DCOLON))
344 if (tok[3] == SYMBOL) // label: symbol
354 p = (char *)tok[1]; // symbol
356 p = string[tok[1]]; // Symbol
363 if (*p == '.') // ignore leading '.'s
366 k = kwmatch(p, dirlist);
369 // Hand-off line to function
370 // if it returns 0, and we found a keyword, stop looking.
371 // if it returns 1, hand off the line and keep looking.
373 k = (*lnfunc)(lnbuf, k);
380 lnsave--; // Tell tokenizer to stop keeping lines
387 // See if the string `kw' matches one of the keywords in `kwlist'. If so,
388 // return the number of the keyword matched. Return -1 if there was no match.
389 // Strings are compared without regard for case.
391 int kwmatch(char * kw, char * kwlist)
398 for(k=0; *kwlist; ++k)
405 if (c2 >= 'A' && c2 <= 'Z')
408 if (c1 == ' ' && c2 == EOS)
415 // Skip to beginning of next keyword in `kwlist'
416 while (*kwlist && *kwlist != ' ')
429 // o parse, count and copy arguments
430 // o push macro's string-stream
432 int InvokeMacro(SYM * mac, WORD siz)
437 // TOKEN ** argptr = NULL;
438 //Doesn't need to be global! (or does it???)
441 if ((!strcmp(mac->sname, "mjump") || !strcmp(mac->sname, "mpad")) && !in_main)
443 error("macro cannot be used outside of .gpumain");
447 INOBJ * inobj = a_inobj(SRC_IMACRO); // Alloc and init IMACRO
448 IMACRO * imacro = inobj->inobj.imacro;
449 imacro->im_siz = siz;
451 TOKEN * beg_tok = tok; // 'tok' comes from token.c
453 for(dry_run=1; ; dry_run--)
455 for(tok=beg_tok; *tok!=EOL;)
466 // Keep going while tok isn't pointing at a comma or EOL
467 while (*tok != ',' && *tok != EOL)
469 // Skip over backslash character, unless it's followed by an EOL
470 if (*tok == '\\' && tok[1] != EOL)
480 arg_siz += sizeof(TOKEN);
485 // FALLTHROUGH (picks up the arg after a CONST, SYMBOL or ACONST)
489 arg_siz += sizeof(TOKEN);
499 // We hit the comma or EOL, so count/stuff it
501 arg_siz += sizeof(TOKEN);
505 // If we hit the comma instead of an EOL, skip over it
510 // Allocate space for argument ptrs and so on and then go back and
511 // construct the arg frame
515 //Barfing here with memory corruption in glibc. TOKEN is defined as LONG, which is uint32_t
516 p = (TOKEN *)malloc(arg_siz);
517 // p = (TOKEN *)malloc(arg_siz + sizeof(TOKEN));
519 // This construct is meant to deal with nested macros, so the simple minded way
520 // we deal with them now won't work. :-/ Have to think about how to fix.
522 argptr = (TOKEN **)malloc((nargs + 1) * sizeof(LONG));
523 *argptr++ = (TOKEN *)argp;
534 // o -> macro symbol;
535 // o -> macro definition string list;
536 // o save 'curuniq', to be restored when the macro pops;
537 // o bump `macuniq' counter and set 'curuniq' to it;
538 imacro->im_nargs = nargs;
539 imacro->im_macro = mac;
540 // imacro->im_nextln = (TOKEN *)mac->svalue;
541 imacro->im_nextln = mac->lineList;
542 imacro->im_olduniq = curuniq;
547 printf("nargs=%d\n", nargs);
549 for(nargs=0; nargs<imacro->im_nargs; ++nargs)
551 printf("arg%d=", nargs);
552 // dumptok(argp[imacro->im_nargs - nargs - 1]);
553 dumptok(argPtrs[imacro->im_nargs - nargs - 1]);
562 // Setup inbuilt macros
566 curmac = NewSymbol("mjump", MACRO, 0);
568 curmac->sattr = (WORD)(macnum++);
574 curmac->lineList = NULL;
576 defmac1(" movei #\\addr,\\jreg", -1);
577 defmac1(" jump \\cc,(\\jreg)", -1);
581 curmac = NewSymbol("mjr", MACRO, 0);
583 curmac->sattr = (WORD)(macnum++);
588 curmac->lineList = NULL;
589 defmac1(" jr \\cc,\\addr", -1);
593 curmac = NewSymbol("mpad", MACRO, 0);
595 curmac->sattr = (WORD)(macnum++);
599 curmac->lineList = NULL;
600 defmac1(" .rept (\\size/2)", -1);
602 defmac1(" .endr", -1);