Various cleanups to fix compiler warnings.
[rmac] / macro.c
1 //
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
7 //
8
9 #include "macro.h"
10 #include "token.h"
11 #include "error.h"
12 #include "expr.h"
13 #include "listing.h"
14 #include "symbol.h"
15 #include "procln.h"
16 #include "direct.h"
17 #include "debug.h"
18
19 LONG curuniq;                                               // Current macro's unique number
20 TOKEN **argp;                                               // Free spot in argptrs[]
21 int macnum;                                                 // Unique number for macro definition
22
23 static LONG macuniq;                                        // Unique-per-macro number
24 static SYM *curmac;                                         // Macro currently being defined
25 static char **curmln;                                       // Previous macro line (or NULL)
26 static VALUE argno;                                         // Formal argument count 
27
28 static LONG *firstrpt;                                      // First .rept line 
29 static LONG *nextrpt;                                       // Last .rept line 
30 static int rptlevel;                                        // .rept nesting level 
31
32 //
33 // --- Initialize Macro Processor ------------------------------------------------------------------
34 //
35
36 void init_macro(void) {
37    macuniq = 0;
38    macnum = 1;
39    argp = NULL;
40    ib_macro();
41 }
42
43 //
44 // -------------------------------------------------------------------------------------------------
45 // Exit from a Macro;
46 // o  pop any intervening include files and repeat blocks;
47 // o  restore argument stack;
48 // o  pop the macro.
49 // -------------------------------------------------------------------------------------------------
50 //
51
52 int exitmac(void) {
53    IMACRO *imacro;
54    TOKEN **p;
55
56    // Pop intervening include files and .rept blocks
57    while(cur_inobj != NULL && cur_inobj->in_type != SRC_IMACRO)
58       fpop();
59
60    if(cur_inobj == NULL)
61       fatal("too many ENDMs");
62
63    // Restore
64    // o  old arg context
65    // o  old unique number
66    // ...and then pop the macro.
67
68    imacro = cur_inobj->inobj.imacro;
69    curuniq = imacro->im_olduniq;
70
71    p = --argp;
72    argp = (TOKEN **)*argp;
73
74    fpop();
75    
76    mjump_align = 0;
77
78    return(0);
79 }
80
81 //
82 // --- Add a Formal Argument to a Macro Definition -------------------------------------------------
83 //
84
85 int defmac2(char *argname) {
86    SYM *arg;
87
88    if(lookup(argname, MACARG, (int)curmac->sattr) != NULL)
89       return(error("multiple formal argument definition"));
90    arg = newsym(argname, MACARG, (int)curmac->sattr);
91    arg->svalue = argno++;
92
93    return(OK);
94 }
95
96
97 //
98 // -------------------------------------------------------------------------------------------------
99 // Add a line to a macro definition; also print lines to listing file (if enabled).
100 // The last line of the macro (containing .endm) is not included in the macro.  A label on that line
101 // will be lost. `endflg' is misleading here.  It is -1 for all lines but the last one (.endm), 
102 // when it is 0.
103 // -------------------------------------------------------------------------------------------------
104 //
105
106 int defmac1(char *ln, int endflg) {
107    PTR p;
108    LONG len;
109
110    if(list_flag) {
111       listeol();                                            // Flush previous source line
112       lstout('.');                                          // Mark macro definition with period
113    }
114
115    if(endflg) {
116       len = strlen(ln) + 1 + sizeof(LONG);
117       p.cp = amem(len);
118       *p.lp = 0;
119       strcpy(p.cp + sizeof(LONG), ln);
120
121       // Link line of text onto end of list
122       if(curmln == NULL)
123          curmac->svalue = (VALUE)p.cp;
124       else
125          *curmln = p.cp;
126       curmln = (char **)p.cp;
127       return(1);                                            // Keep looking 
128    }
129    else 
130       return(0);                                            // Stop looking at the end
131 }
132
133 //
134 // -------------------------------------------------------------------------------------------------
135 // Define macro
136 //
137 // macro foo arg1,arg2,...
138 //    :
139 //     :
140 //    endm
141 //
142 //  Helper functions:
143 //  -----------------
144 //  `defmac1' adds lines of text to the macro definition
145 //  `defmac2' processes the formal arguments (and sticks them into the symbol table)
146 // -------------------------------------------------------------------------------------------------
147 //
148
149 int defmac(void) {
150    char *p;
151    SYM *mac;
152
153    // Setup entry in symbol table, make sure the macro isn't a duplicate entry, and that
154    // it doesn't override any processor mnemonic or assembler directive.
155    if(*tok++ != SYMBOL) return(error("missing symbol"));
156    p = (char *)*tok++;
157    if(lookup(p, MACRO, 0) != NULL)
158       return(error("multiple macro definition"));
159
160    curmac = mac = newsym(p, MACRO, 0);
161    mac->svalue = 0;
162    mac->sattr = (WORD)(macnum++);
163
164    // Parse and define formal arguments in symbol table
165    if(*tok != EOL) {
166       argno = 0;
167       symlist(defmac2);
168       at_eol();
169    }
170
171    // Suck in the macro definition; we're looking for an ENDM symbol on a line
172    // by itself to terminate the definition.
173    curmln = NULL;
174    lncatch(defmac1, "endm ");
175
176    return(0);
177 }
178
179 //
180 // --- Add lines to a .rept definition -------------------------------------------------------------
181 //
182
183 int defr1(char *ln, int kwno) {
184    LONG len;
185    LONG *p;
186
187    if(list_flag) {
188       listeol();                                            // Flush previous source line
189       lstout('#');                                          // Mark this a 'rept' block
190    }
191
192    switch(kwno) {
193       case 0:                                               // .endr 
194          if(--rptlevel == 0)
195             return(0);
196          goto addln;
197       case 1:                                               // .rept 
198          ++rptlevel;
199       default:
200
201          addln:
202
203          // Allocate length of line + 1('\0') + LONG
204          len = strlen(ln) + 1 + sizeof(LONG);
205          p = (LONG *)amem(len);
206          *p = 0;
207
208          strcpy((char*)(p + 1), ln);
209          
210          if(nextrpt == NULL) {
211             firstrpt = p;           // First line of rept statement
212          } else {
213             *nextrpt = (LONG)p;
214          }
215          nextrpt = p;
216
217          return(rptlevel);
218    }
219 }
220
221 //
222 // --- Define a .rept block, this gets hairy because they can be nested ----------------------------
223 //
224
225 int defrept(void) {
226    INOBJ *inobj;
227    IREPT *irept;
228    VALUE eval;
229
230    // Evaluate repeat expression
231    if(abs_expr(&eval) != OK)
232       return(ERROR);
233
234    // Suck in lines for .rept block
235    firstrpt = NULL;
236    nextrpt = NULL;
237    rptlevel = 1;
238    lncatch(defr1, "endr rept ");
239
240    // Alloc and init input object
241    if(firstrpt) {
242       inobj = a_inobj(SRC_IREPT);                           // Create a new REPT input object
243       irept = inobj->inobj.irept;
244       irept->ir_firstln = firstrpt;
245       irept->ir_nextln = NULL;
246       irept->ir_count = eval;
247    }
248
249    return(0);
250 }
251
252 //
253 // -------------------------------------------------------------------------------------------------
254 // Hand off lines of text to the function `lnfunc' until a line containing one of the directives in 
255 // `dirlist' is encountered. Return the number of the keyword encountered (0..n)
256 // 
257 // `dirlist' contains null-seperated terminated keywords.  A final null terminates the list.  
258 // Directives are compared to the keywords without regard to case.
259 // 
260 // If `lnfunc' is NULL, then lines are simply skipped.
261 // If `lnfunc' returns an error, processing is stopped.
262 // 
263 // `lnfunc' is called with an argument of -1 for every line but the last one, when it is called 
264 // with an argument of the keyword number that caused the match.
265 // -------------------------------------------------------------------------------------------------
266 //
267
268 int lncatch(int (*lnfunc)(), char *dirlist) {
269    char *p;
270    int k;
271
272    if(lnfunc != NULL)
273       ++lnsave;                                             // Tell tokenizer to keep lines 
274
275    for(;;) {
276       if(tokln() == TKEOF) {
277          errors("encountered end-of-file looking for '%s'", dirlist);
278          fatal("cannot continue");
279       }
280
281       // Test for end condition.  Two cases to handle:
282       //            <directive>
283       //    symbol: <directive>
284       p = NULL;
285       k = -1;
286
287       if(*tok == SYMBOL) {
288          if((tok[2] == ':' || tok[2] == DCOLON)) {
289             if(tok[3] == SYMBOL)                            // label: symbol
290                p = (char *)tok[4];
291          } else {
292             p = (char *)tok[1];                             // symbol 
293          }
294       }
295
296       if(p != NULL) {
297          if(*p == '.')                                      // ignore leading '.'s 
298             ++p;
299          k = kwmatch(p, dirlist);
300       }
301
302       // Hand-off line to function
303       // if it returns 0, and we found a keyword, stop looking.
304       // if it returns 1, hand off the line and keep looking.
305       if(lnfunc != NULL)
306          k = (*lnfunc)(lnbuf, k);
307
308       if(!k)
309          break;
310    }
311
312    if(lnfunc != NULL)
313       --lnsave;                                             // Tell tokenizer to stop keeping lines
314
315    return(0);
316 }
317
318 //
319 // -------------------------------------------------------------------------------------------------
320 // See if the string `kw' matches one of the keywords in `kwlist'.  If so, return the number of 
321 // the keyword matched.  Return -1 if there was no match. 
322 // Strings are compared without regard for case.
323 // -------------------------------------------------------------------------------------------------
324 //
325
326 int kwmatch(char *kw, char *kwlist) {
327    char *p;
328    char c1;
329    char c2;
330    int k;
331
332    for(k = 0; *kwlist; ++k) {
333       for(p = kw;;) {
334          c1 = *kwlist++;
335          c2 = *p++;
336
337          if(c2 >= 'A' && c2 <= 'Z')
338             c2 += 32;
339
340          if(c1 == ' ' && c2 == EOS)
341             return(k);
342
343          if(c1 != c2)
344             break;
345       }
346
347       // Skip to beginning of next keyword in `kwlist'
348       while(*kwlist && *kwlist != ' ')
349          ++kwlist;
350       if(*kwlist== ' ')
351          ++kwlist;
352    }
353
354    return(-1);
355 }
356
357 //
358 // -------------------------------------------------------------------------------------------------
359 // Invoke a macro
360 // o  parse, count and copy arguments
361 // o  push macro's string-stream
362 // -------------------------------------------------------------------------------------------------
363 //
364
365 int invokemac(SYM *mac, WORD siz) {
366    TOKEN *p = NULL;
367    IMACRO *imacro;
368    INOBJ *inobj;
369    int dry_run;
370    WORD nargs;
371    WORD arg_siz = 0;
372    TOKEN **argptr = NULL;
373    TOKEN *beg_tok;
374
375    if((!strcmp(mac->sname, "mjump") || !strcmp(mac->sname, "mpad")) && !in_main) {
376       error("macro cannot be used outside of .gpumain");
377       return(ERROR);
378    }
379
380    inobj = a_inobj(SRC_IMACRO);                             // Alloc and init IMACRO 
381    imacro = inobj->inobj.imacro;
382    imacro->im_siz = siz;
383    nargs = 0;
384    beg_tok = tok;
385
386    for(dry_run = 1;; --dry_run) {
387       for(tok = beg_tok; *tok != EOL;) {
388          if(dry_run) ++nargs;
389          else *argptr++ = p;
390
391          while(*tok != ',' && *tok != EOL) {
392             if(*tok == '\\' && tok[1] != EOL) ++tok;
393             switch((int)*tok) {
394                case CONST:
395                case SYMBOL:
396                case ACONST:
397                   if(dry_run) arg_siz += sizeof(TOKEN), ++tok;
398                   else *p++ = *tok++;
399                   // FALLTHROUGH
400                default:
401                   if(dry_run) arg_siz += sizeof(TOKEN), ++tok;
402                   else *p++ = *tok++;
403                   break;
404             }
405          }
406
407          if(dry_run) arg_siz += sizeof(TOKEN);
408          else *p++ = EOL;
409
410          if(*tok == ',') ++tok;
411       }
412
413       // Allocate space for argument ptrs and so on and then go back and construct the arg frame
414       if(dry_run) {
415          if(nargs != 0) p = (TOKEN *)malloc((LONG)(arg_siz + 1));
416          argptr = (TOKEN **)malloc((LONG)((nargs + 1) * sizeof(LONG)));
417          *argptr++ = (TOKEN *)argp;
418          argp = argptr;
419       } else 
420          break;
421    }
422
423
424    // Setup imacro:
425    // o  #arguments;
426    // o  -> macro symbol;
427    // o  -> macro definition string list;
428    // o  save 'curuniq', to be restored when the macro pops;
429    // o  bump `macuniq' counter and set 'curuniq' to it;
430    imacro->im_nargs = nargs;
431    imacro->im_macro = mac;
432    imacro->im_nextln = (LONG *)mac->svalue;
433    imacro->im_olduniq = curuniq;
434    curuniq = ++macuniq;
435
436    DEBUG {
437       printf("nargs=%d\n", nargs);
438       for(nargs = 0; nargs < imacro->im_nargs; ++nargs) {
439          printf("arg%d=", nargs);
440          dumptok(argp[imacro->im_nargs - nargs - 1]);
441       }
442    }
443    
444    return(OK);
445 }
446
447 //
448 // -------------------------------------------------------------------------------------------------
449 // Setup inbuilt macros
450 // -------------------------------------------------------------------------------------------------
451 //
452
453 void ib_macro(void)
454 {
455    SYM * mac;
456
457    curmac = mac = newsym("mjump", MACRO, 0);
458    mac->svalue = 0;
459    mac->sattr = (WORD)(macnum++);
460    argno = 0;
461    defmac2("cc");
462    defmac2("addr");
463    defmac2("jreg");
464    curmln = NULL;
465    defmac1("      nop", -1);
466    defmac1("      movei #\\addr,\\jreg", -1);
467    defmac1("      jump  \\cc,(\\jreg)", -1);
468    defmac1("      nop", -1);
469    defmac1("      nop", -1);
470
471    curmac = mac = newsym("mjr", MACRO, 0);
472    mac->svalue = 0;
473    mac->sattr = (WORD)(macnum++);
474    argno = 0;
475    defmac2("cc");
476    defmac2("addr");
477    curmln = NULL;
478    defmac1("      jr    \\cc,\\addr", -1);
479    defmac1("      nop", -1);
480    defmac1("      nop", -1);
481
482    curmac = mac = newsym("mpad", MACRO, 0);
483    mac->svalue = 0;
484    mac->sattr = (WORD)(macnum++);
485    argno = 0;
486    defmac2("size");
487    curmln = NULL;
488    defmac1("      .rept (\\size/2)", -1);
489    defmac1("         nop", -1);
490    defmac1("      .endr", -1);
491 }