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