]> Shamusworld >> Repos - rmac/blob - macro.c
First working 64-bit version of RMAC
[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 "debug.h"
11 #include "direct.h"
12 #include "error.h"
13 #include "expr.h"
14 #include "listing.h"
15 #include "procln.h"
16 #include "symbol.h"
17 #include "token.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 TOKEN * argPtrs[128];                                           // 128 arguments ought to be enough for anyone
23 static int argp;
24
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 
29
30 static LONG * firstrpt;                                         // First .rept line 
31 static LONG * nextrpt;                                          // Last .rept line 
32 static int rptlevel;                                            // .rept nesting level 
33
34
35 //
36 // Initialize Macro Processor
37 //
38 void init_macro(void)
39 {
40         macuniq = 0;
41         macnum = 1;
42 //      argp = NULL;
43         argp = 0;
44         ib_macro();
45 }
46
47
48 //
49 // Exit from a Macro;
50 // -- pop any intervening include files and repeat blocks;
51 // -- restore argument stack;
52 // -- pop the macro.
53 //
54 int exitmac(void)
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 * imacro = cur_inobj->inobj.imacro;
69         curuniq = imacro->im_olduniq;
70
71         /*TOKEN ** p = */argp--;
72 //      argp = (TOKEN **)*argp;
73
74         fpop();
75         mjump_align = 0;
76         return 0;
77 }
78
79
80 //
81 // Add a Formal Argument to a Macro Definition
82 //
83 int defmac2(char * argname)
84 {
85         SYM * arg;
86
87         if (lookup(argname, MACARG, (int)curmac->sattr) != NULL)
88                 return error("multiple formal argument definition");
89
90         arg = NewSymbol(argname, MACARG, (int)curmac->sattr);
91         arg->svalue = argno++;
92
93         return OK;
94 }
95
96
97 //
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.
102 //
103 int defmac1(char * ln, int notEndFlag)
104 {
105         PTR p;
106         LONG len;
107
108         if (list_flag)
109         {
110                 listeol();                                                      // Flush previous source line
111                 lstout('.');                                            // Mark macro definition with period
112         }
113
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
116         // for disaster.
117         // How to manage it then?
118         // Could use a linked list, like Landon uses everywhere else.
119 /*
120 How it works:
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.
127
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.
130 */
131         if (notEndFlag)
132         {
133 #if 0
134                 len = strlen(ln) + 1 + sizeof(LONG);
135                 p.cp = malloc(len);
136                 *p.lp = 0;
137                 strcpy(p.cp + sizeof(LONG), ln);
138
139                 // Link line of text onto end of list
140                 if (curmln == NULL)
141                         curmac->svalue = p.cp;
142                 else
143                         *curmln = p.cp;
144
145                 curmln = (char **)p.cp;
146                 return 1;                                                       // Keep looking 
147 #else
148                 if (curmac->lineList == NULL)
149                 {
150                         curmac->lineList = malloc(sizeof(struct LineList));
151                         curmac->lineList->next = NULL;
152                         curmac->lineList->line = strdup(ln);
153                         curmac->last = curmac->lineList;
154                 }
155                 else
156                 {
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;
161                 }
162
163                 return 1;                                                       // Keep looking
164 #endif
165         }
166
167         return 0;                                                               // Stop looking at the end
168 }
169
170
171 //
172 // Define macro
173 //
174 // macro foo arg1,arg2,...
175 //    :
176 //     :
177 //    endm
178 //
179 //  Helper functions:
180 //  -----------------
181 //  `defmac1' adds lines of text to the macro definition
182 //  `defmac2' processes the formal arguments (and sticks them into the symbol table)
183 //
184 int DefineMacro(void)
185 {
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
188         // directive.
189         if (*tok++ != SYMBOL)
190                 return error("missing symbol");
191
192         char * name = string[*tok++];
193
194         if (lookup(name, MACRO, 0) != NULL)
195                 return error("duplicate macro definition");
196
197         curmac = NewSymbol(name, MACRO, 0);
198         curmac->svalue = 0;
199         curmac->sattr = (WORD)(macnum++);
200
201         // Parse and define formal arguments in symbol table
202         if (*tok != EOL)
203         {
204                 argno = 0;
205                 symlist(defmac2);
206                 at_eol();
207         }
208
209         // Suck in the macro definition; we're looking for an ENDM symbol on a line
210         // by itself to terminate the definition.
211 //      curmln = NULL;
212         curmac->lineList = NULL;
213         lncatch(defmac1, "endm ");
214
215         return 0;
216 }
217
218
219 //
220 // Add lines to a .rept definition
221 //
222 int defr1(char * ln, int kwno)
223 {
224         LONG len;
225         LONG * p;
226
227         if (list_flag)
228         {
229                 listeol();                                                              // Flush previous source line
230                 lstout('#');                                                    // Mark this a 'rept' block
231         }
232
233         switch (kwno)
234         {
235         case 0:                                                                         // .endr 
236                 if (--rptlevel == 0)
237                 return(0);
238                 goto addln;
239         case 1:                                                                         // .rept 
240                 rptlevel++;
241         default:
242 //MORE stupidity here...
243 #warning "!!! Casting (char *) as LONG !!!"
244         addln:
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);
249                 *p = 0;
250
251                 strcpy((char *)(p + 1), ln);
252                 
253                 if (nextrpt == NULL)
254                 {
255                         firstrpt = p;           // First line of rept statement
256                 }
257                 else
258                 {
259                         *nextrpt = (LONG)p;
260                 }
261
262                 nextrpt = p;
263
264                 return rptlevel;
265         }
266 }
267
268
269 //
270 // Define a .rept block, this gets hairy because they can be nested
271 //
272 int defrept(void)
273 {
274         INOBJ * inobj;
275         IREPT * irept;
276         VALUE eval;
277
278         // Evaluate repeat expression
279         if (abs_expr(&eval) != OK)
280                 return ERROR;
281
282         // Suck in lines for .rept block
283         firstrpt = NULL;
284         nextrpt = NULL;
285         rptlevel = 1;
286         lncatch(defr1, "endr rept ");
287
288         // Alloc and init input object
289         if (firstrpt)
290         {
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;
296         }
297
298         return 0;
299 }
300
301
302 //
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)
306 // 
307 // `dirlist' contains null-seperated terminated keywords.  A final null
308 // terminates the list. Directives are compared to the keywords without regard
309 // to case.
310 // 
311 // If `lnfunc' is NULL, then lines are simply skipped.
312 // If `lnfunc' returns an error, processing is stopped.
313 // 
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
316 // match.
317 //
318 int lncatch(int (* lnfunc)(), char * dirlist)
319 {
320         char * p;
321         int k;
322
323         if (lnfunc != NULL)
324                 lnsave++;                                                               // Tell tokenizer to keep lines 
325
326         for(;;)
327         {
328                 if (tokln() == TKEOF)
329                 {
330                         errors("encountered end-of-file looking for '%s'", dirlist);
331                         fatal("cannot continue");
332                 }
333
334                 // Test for end condition.  Two cases to handle:
335                 //            <directive>
336                 //    symbol: <directive>
337                 p = NULL;
338                 k = -1;
339
340                 if (*tok == SYMBOL)
341                 {
342                         if ((tok[2] == ':' || tok[2] == DCOLON))
343                         {
344                                 if (tok[3] == SYMBOL)                   // label: symbol
345 #if 0
346                                         p = (char *)tok[4];
347 #else
348                                         p = string[tok[4]];
349 #endif
350                         }
351                         else
352                         {
353 #if 0
354                                 p = (char *)tok[1];                             // symbol 
355 #else
356                                 p = string[tok[1]];                             // Symbol
357 #endif
358                         }
359                 }
360
361                 if (p != NULL)
362                 {
363                         if (*p == '.')                                          // ignore leading '.'s 
364                                 p++;
365
366                         k = kwmatch(p, dirlist);
367                 }
368
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.
372                 if (lnfunc != NULL)
373                         k = (*lnfunc)(lnbuf, k);
374
375                 if (!k)
376                         break;
377         }
378
379         if (lnfunc != NULL)
380                 lnsave--;                                                               // Tell tokenizer to stop keeping lines
381
382         return 0;
383 }
384
385
386 //
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.
390 //
391 int kwmatch(char * kw, char * kwlist)
392 {
393         char * p;
394         char c1;
395         char c2;
396         int k;
397
398         for(k=0; *kwlist; ++k)
399         {
400                 for(p=kw;;)
401                 {
402                         c1 = *kwlist++;
403                         c2 = *p++;
404
405                         if (c2 >= 'A' && c2 <= 'Z')
406                                 c2 += 32;
407
408                         if (c1 == ' ' && c2 == EOS)
409                                 return k;
410
411                         if (c1 != c2)
412                                 break;
413                 }
414
415                 // Skip to beginning of next keyword in `kwlist'
416                 while (*kwlist && *kwlist != ' ')
417                         ++kwlist;
418
419                 if (*kwlist== ' ')
420                         kwlist++;
421         }
422
423         return -1;
424 }
425
426
427 //
428 // Invoke a macro
429 // o  parse, count and copy arguments
430 // o  push macro's string-stream
431 //
432 int InvokeMacro(SYM * mac, WORD siz)
433 {
434         TOKEN * p = NULL;
435         int dry_run;
436         WORD arg_siz = 0;
437 //      TOKEN ** argptr = NULL;
438 //Doesn't need to be global! (or does it???)
439         argp = 0;
440
441         if ((!strcmp(mac->sname, "mjump") || !strcmp(mac->sname, "mpad")) && !in_main)
442         {
443                 error("macro cannot be used outside of .gpumain");
444                 return ERROR;
445         }
446
447         INOBJ * inobj = a_inobj(SRC_IMACRO);            // Alloc and init IMACRO 
448         IMACRO * imacro = inobj->inobj.imacro;
449         imacro->im_siz = siz;
450         WORD nargs = 0;
451         TOKEN * beg_tok = tok;                                          // 'tok' comes from token.c
452
453         for(dry_run=1; ; dry_run--)
454         {
455                 for(tok=beg_tok; *tok!=EOL;)
456                 {
457                         if (dry_run)
458                                 nargs++;
459                         else
460 #if 0                           
461                                 *argptr++ = p;
462 #else
463                                 argPtrs[argp++] = p;
464 #endif
465
466                         // Keep going while tok isn't pointing at a comma or EOL
467                         while (*tok != ',' && *tok != EOL)
468                         {
469                                 // Skip over backslash character, unless it's followed by an EOL
470                                 if (*tok == '\\' && tok[1] != EOL)
471                                         tok++;
472
473                                 switch (*tok)
474                                 {
475                                 case CONST:
476                                 case SYMBOL:
477                                 case ACONST:
478                                         if (dry_run)
479                                         {
480                                                 arg_siz += sizeof(TOKEN);
481                                                 tok++;
482                                         }
483                                         else
484                                                 *p++ = *tok++;
485                                 // FALLTHROUGH (picks up the arg after a CONST, SYMBOL or ACONST)
486                                 default:
487                                         if (dry_run)
488                                         {
489                                                 arg_siz += sizeof(TOKEN);
490                                                 tok++;
491                                         }
492                                         else
493                                                 *p++ = *tok++;
494
495                                         break;
496                                 }
497                         }
498
499                         // We hit the comma or EOL, so count/stuff it
500                         if (dry_run)
501                                 arg_siz += sizeof(TOKEN);
502                         else
503                                 *p++ = EOL;
504
505                         // If we hit the comma instead of an EOL, skip over it
506                         if (*tok == ',')
507                                 tok++;
508                 }
509
510                 // Allocate space for argument ptrs and so on and then go back and
511                 // construct the arg frame
512                 if (dry_run)
513                 {
514                         if (nargs != 0)
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));
518
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.
521 #if 0
522                         argptr = (TOKEN **)malloc((nargs + 1) * sizeof(LONG));
523                         *argptr++ = (TOKEN *)argp;
524                         argp = argptr;
525 #else
526 #endif
527                 }
528                 else 
529                         break;
530         }
531
532         // Setup imacro:
533         // o  #arguments;
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;
543         curuniq = macuniq++;
544
545         DEBUG
546         {
547                 printf("nargs=%d\n", nargs);
548
549                 for(nargs=0; nargs<imacro->im_nargs; ++nargs)
550                 {
551                         printf("arg%d=", nargs);
552 //                      dumptok(argp[imacro->im_nargs - nargs - 1]);
553                         dumptok(argPtrs[imacro->im_nargs - nargs - 1]);
554                 }
555         }
556
557         return OK;
558 }
559
560
561 //
562 // Setup inbuilt macros
563 //
564 void ib_macro(void)
565 {
566         curmac = NewSymbol("mjump", MACRO, 0);
567         curmac->svalue = 0;
568         curmac->sattr = (WORD)(macnum++);
569         argno = 0;
570         defmac2("cc");
571         defmac2("addr");
572         defmac2("jreg");
573 //      curmln = NULL;
574         curmac->lineList = NULL;
575         defmac1("  nop", -1);
576         defmac1("  movei #\\addr,\\jreg", -1);
577         defmac1("  jump  \\cc,(\\jreg)", -1);
578         defmac1("  nop", -1);
579         defmac1("  nop", -1);
580
581         curmac = NewSymbol("mjr", MACRO, 0);
582         curmac->svalue = 0;
583         curmac->sattr = (WORD)(macnum++);
584         argno = 0;
585         defmac2("cc");
586         defmac2("addr");
587 //      curmln = NULL;
588         curmac->lineList = NULL;
589         defmac1("  jr    \\cc,\\addr", -1);
590         defmac1("  nop", -1);
591         defmac1("  nop", -1);
592
593         curmac = NewSymbol("mpad", MACRO, 0);
594         curmac->svalue = 0;
595         curmac->sattr = (WORD)(macnum++);
596         argno = 0;
597         defmac2("size");
598 //      curmln = NULL;
599         curmac->lineList = NULL;
600         defmac1("  .rept (\\size/2)", -1);
601         defmac1("     nop", -1);
602         defmac1("  .endr", -1);
603 }