]> Shamusworld >> Repos - rmac/blob - macro.c
First stab at removing cruft.
[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
20 //static void SetupDefaultMacros(void);
21
22 LONG curuniq;                                                           // Current macro's unique number
23 //TOKEN ** argp;                                                                // Free spot in argptrs[]
24 int macnum;                                                                     // Unique number for macro definition
25 TOKEN * argPtrs[128];                                           // 128 arguments ought to be enough for anyone
26 static int argp;
27
28 static LONG macuniq;                                            // Unique-per-macro number
29 static SYM * curmac;                                            // Macro currently being defined
30 //static char ** curmln;                                                // Previous macro line (or NULL)
31 static VALUE argno;                                                     // Formal argument count 
32
33 static LONG * firstrpt;                                         // First .rept line 
34 static LONG * nextrpt;                                          // Last .rept line 
35 static int rptlevel;                                            // .rept nesting level 
36
37
38 //
39 // Initialize Macro Processor
40 //
41 void InitMacro(void)
42 {
43         macuniq = 0;
44         macnum = 1;
45 //      argp = NULL;
46         argp = 0;
47 //      SetupDefaultMacros();
48 }
49
50
51 //
52 // Exit from a Macro;
53 // -- pop any intervening include files and repeat blocks;
54 // -- restore argument stack;
55 // -- pop the macro.
56 //
57 int ExitMacro(void)
58 {
59 #warning !!! Bad macro exiting !!!
60 /*
61 This is a problem. Currently, the argument logic just keeps the current
62 arguments and doesn't save anything if a new macro is called in the middle
63 of another (nested macros). Need to fix that somehow.
64 */
65         // Pop intervening include files and .rept blocks
66         while (cur_inobj != NULL && cur_inobj->in_type != SRC_IMACRO)
67                 fpop();
68
69         if (cur_inobj == NULL)
70                 fatal("too many ENDMs");
71
72         // Restore
73         // o  old arg context
74         // o  old unique number
75         // ...and then pop the macro.
76
77         IMACRO * imacro = cur_inobj->inobj.imacro;
78         curuniq = imacro->im_olduniq;
79
80 //      /*TOKEN ** p = */argp--;
81 //      argp = (TOKEN **)*argp;
82         DEBUG printf("ExitMacro: argp: %d -> ", argp);
83         argp -= imacro->im_nargs;
84         DEBUG printf("%d (nargs = %d)\n", argp, imacro->im_nargs);
85
86         fpop();
87 //      mjump_align = 0;
88         return 0;
89 }
90
91
92 //
93 // Add a Formal Argument to a Macro Definition
94 //
95 int defmac2(char * argname)
96 {
97         SYM * arg;
98
99         if (lookup(argname, MACARG, (int)curmac->sattr) != NULL)
100                 return error("multiple formal argument definition");
101
102         arg = NewSymbol(argname, MACARG, (int)curmac->sattr);
103         arg->svalue = argno++;
104
105         return OK;
106 }
107
108
109 //
110 // Add a line to a macro definition; also print lines to listing file (if
111 // enabled). The last line of the macro (containing .endm) is not included in
112 // the macro. A label on that line will be lost.
113 // notEndFlag is -1 for all lines but the last one (.endm), when it is 0.
114 //
115 int defmac1(char * ln, int notEndFlag)
116 {
117         PTR p;
118         LONG len;
119
120         if (list_flag)
121         {
122                 listeol();                                                      // Flush previous source line
123                 lstout('.');                                            // Mark macro definition with period
124         }
125
126         // This is just wrong, wrong, wrong. It makes up a weird kind of string with
127         // a pointer on front, and then uses a ** to manage them: This is a recipe
128         // for disaster.
129         // How to manage it then?
130         // Could use a linked list, like Landon uses everywhere else.
131 /*
132 How it works:
133 Allocate a space big enough for the string + NULL + pointer.
134 Set the pointer to NULL.
135 Copy the string to the space after the pointer.
136 If this is the 1st time through, set the SYM * "svalue" to the pointer.
137 If this is the 2nd time through, derefence the ** to point to the memory you just allocated.
138 Then, set the ** to the location of the memory you allocated for the next pass through.
139
140 This is a really low level way to do a linked list, and by bypassing all the safety
141 features of the language. Seems like we can do better here.
142 */
143         if (notEndFlag)
144         {
145 #if 0
146                 len = strlen(ln) + 1 + sizeof(LONG);
147                 p.cp = malloc(len);
148                 *p.lp = 0;
149                 strcpy(p.cp + sizeof(LONG), ln);
150
151                 // Link line of text onto end of list
152                 if (curmln == NULL)
153                         curmac->svalue = p.cp;
154                 else
155                         *curmln = p.cp;
156
157                 curmln = (char **)p.cp;
158                 return 1;                                                       // Keep looking 
159 #else
160                 if (curmac->lineList == NULL)
161                 {
162                         curmac->lineList = malloc(sizeof(struct LineList));
163                         curmac->lineList->next = NULL;
164                         curmac->lineList->line = strdup(ln);
165                         curmac->last = curmac->lineList;
166                 }
167                 else
168                 {
169                         curmac->last->next = malloc(sizeof(struct LineList));
170                         curmac->last->next->next = NULL;
171                         curmac->last->next->line = strdup(ln);
172                         curmac->last = curmac->last->next;
173                 }
174
175                 return 1;                                                       // Keep looking
176 #endif
177         }
178
179         return 0;                                                               // Stop looking at the end
180 }
181
182
183 //
184 // Define macro
185 //
186 // macro foo arg1,arg2,...
187 //    :
188 //     :
189 //    endm
190 //
191 //  Helper functions:
192 //  -----------------
193 //  `defmac1' adds lines of text to the macro definition
194 //  `defmac2' processes the formal arguments (and sticks them into the symbol table)
195 //
196 int DefineMacro(void)
197 {
198         // Setup entry in symbol table, make sure the macro isn't a duplicate
199         // entry, and that it doesn't override any processor mnemonic or assembler
200         // directive.
201         if (*tok++ != SYMBOL)
202                 return error("missing symbol");
203
204         char * name = string[*tok++];
205
206         if (lookup(name, MACRO, 0) != NULL)
207                 return error("duplicate macro definition");
208
209         curmac = NewSymbol(name, MACRO, 0);
210         curmac->svalue = 0;
211         curmac->sattr = (WORD)(macnum++);
212
213         // Parse and define formal arguments in symbol table
214         if (*tok != EOL)
215         {
216                 argno = 0;
217                 symlist(defmac2);
218                 at_eol();
219         }
220
221         // Suck in the macro definition; we're looking for an ENDM symbol on a line
222         // by itself to terminate the definition.
223 //      curmln = NULL;
224         curmac->lineList = NULL;
225         lncatch(defmac1, "endm ");
226
227         return 0;
228 }
229
230
231 //
232 // Add lines to a .rept definition
233 //
234 int defr1(char * ln, int kwno)
235 {
236         LONG len;
237         LONG * p;
238
239         if (list_flag)
240         {
241                 listeol();                                                              // Flush previous source line
242                 lstout('#');                                                    // Mark this a 'rept' block
243         }
244
245         switch (kwno)
246         {
247         case 0:                                                                         // .endr 
248                 if (--rptlevel == 0)
249                 return(0);
250                 goto addln;
251         case 1:                                                                         // .rept 
252                 rptlevel++;
253         default:
254 //MORE stupidity here...
255 #warning "!!! Casting (char *) as LONG !!!"
256         addln:
257                 // Allocate length of line + 1('\0') + LONG
258                 len = strlen(ln) + 1 + sizeof(LONG);
259 //              p = (LONG *)amem(len);
260                 p = (LONG *)malloc(len);
261                 *p = 0;
262
263                 strcpy((char *)(p + 1), ln);
264                 
265                 if (nextrpt == NULL)
266                 {
267                         firstrpt = p;           // First line of rept statement
268                 }
269                 else
270                 {
271                         *nextrpt = (LONG)p;
272                 }
273
274                 nextrpt = p;
275
276                 return rptlevel;
277         }
278 }
279
280
281 //
282 // Define a .rept block, this gets hairy because they can be nested
283 //
284 int defrept(void)
285 {
286         INOBJ * inobj;
287         IREPT * irept;
288         VALUE eval;
289
290         // Evaluate repeat expression
291         if (abs_expr(&eval) != OK)
292                 return ERROR;
293
294         // Suck in lines for .rept block
295         firstrpt = NULL;
296         nextrpt = NULL;
297         rptlevel = 1;
298         lncatch(defr1, "endr rept ");
299
300         // Alloc and init input object
301         if (firstrpt)
302         {
303                 inobj = a_inobj(SRC_IREPT);                             // Create a new REPT input object
304                 irept = inobj->inobj.irept;
305                 irept->ir_firstln = firstrpt;
306                 irept->ir_nextln = NULL;
307                 irept->ir_count = eval;
308         }
309
310         return 0;
311 }
312
313
314 //
315 // Hand off lines of text to the function `lnfunc' until a line containing one
316 // of the directives in `dirlist' is encountered. Return the number of the
317 // keyword encountered (0..n)
318 // 
319 // `dirlist' contains null-seperated terminated keywords.  A final null
320 // terminates the list. Directives are compared to the keywords without regard
321 // to case.
322 // 
323 // If `lnfunc' is NULL, then lines are simply skipped.
324 // If `lnfunc' returns an error, processing is stopped.
325 // 
326 // `lnfunc' is called with an argument of -1 for every line but the last one,
327 // when it is called with an argument of the keyword number that caused the
328 // match.
329 //
330 int lncatch(int (* lnfunc)(), char * dirlist)
331 {
332         char * p;
333         int k;
334
335         if (lnfunc != NULL)
336                 lnsave++;                                                               // Tell tokenizer to keep lines 
337
338         for(;;)
339         {
340                 if (tokln() == TKEOF)
341                 {
342                         errors("encountered end-of-file looking for '%s'", dirlist);
343                         fatal("cannot continue");
344                 }
345
346                 // Test for end condition.  Two cases to handle:
347                 //            <directive>
348                 //    symbol: <directive>
349                 p = NULL;
350                 k = -1;
351
352                 if (*tok == SYMBOL)
353                 {
354                         if ((tok[2] == ':' || tok[2] == DCOLON))
355                         {
356                                 if (tok[3] == SYMBOL)                   // label: symbol
357 #if 0
358                                         p = (char *)tok[4];
359 #else
360                                         p = string[tok[4]];
361 #endif
362                         }
363                         else
364                         {
365 #if 0
366                                 p = (char *)tok[1];                             // symbol 
367 #else
368                                 p = string[tok[1]];                             // Symbol
369 #endif
370                         }
371                 }
372
373                 if (p != NULL)
374                 {
375                         if (*p == '.')                                          // ignore leading '.'s 
376                                 p++;
377
378                         k = kwmatch(p, dirlist);
379                 }
380
381                 // Hand-off line to function
382                 // if it returns 0, and we found a keyword, stop looking.
383                 // if it returns 1, hand off the line and keep looking.
384                 if (lnfunc != NULL)
385                         k = (*lnfunc)(lnbuf, k);
386
387                 if (!k)
388                         break;
389         }
390
391         if (lnfunc != NULL)
392                 lnsave--;                                                               // Tell tokenizer to stop keeping lines
393
394         return 0;
395 }
396
397
398 //
399 // See if the string `kw' matches one of the keywords in `kwlist'.  If so,
400 // return the number of the keyword matched.  Return -1 if there was no match.
401 // Strings are compared without regard for case.
402 //
403 int kwmatch(char * kw, char * kwlist)
404 {
405         char * p;
406         char c1;
407         char c2;
408         int k;
409
410         for(k=0; *kwlist; ++k)
411         {
412                 for(p=kw;;)
413                 {
414                         c1 = *kwlist++;
415                         c2 = *p++;
416
417                         if (c2 >= 'A' && c2 <= 'Z')
418                                 c2 += 32;
419
420                         if (c1 == ' ' && c2 == EOS)
421                                 return k;
422
423                         if (c1 != c2)
424                                 break;
425                 }
426
427                 // Skip to beginning of next keyword in `kwlist'
428                 while (*kwlist && *kwlist != ' ')
429                         ++kwlist;
430
431                 if (*kwlist== ' ')
432                         kwlist++;
433         }
434
435         return -1;
436 }
437
438
439 //
440 // Invoke a macro
441 // o  parse, count and copy arguments
442 // o  push macro's string-stream
443 //
444 int InvokeMacro(SYM * mac, WORD siz)
445 {
446         TOKEN * p = NULL;
447         int dry_run;
448         WORD arg_siz = 0;
449 //      TOKEN ** argptr = NULL;
450 //Doesn't need to be global! (or does it???--it does)
451 //      argp = 0;
452         DEBUG printf("InvokeMacro: argp: %d -> ", argp);
453
454 #if 0
455         if ((!strcmp(mac->sname, "mjump") || !strcmp(mac->sname, "mpad")) && !in_main)
456         {
457                 error("macro cannot be used outside of .gpumain");
458                 return ERROR;
459         }
460 #endif
461
462         INOBJ * inobj = a_inobj(SRC_IMACRO);            // Alloc and init IMACRO 
463         IMACRO * imacro = inobj->inobj.imacro;
464         imacro->im_siz = siz;
465         WORD nargs = 0;
466         TOKEN * beg_tok = tok;                                          // 'tok' comes from token.c
467         TOKEN * startOfArg;
468         TOKEN * dest;
469         int stringNum = 0;
470         int argumentNum = 0;
471         int i;
472
473         for(dry_run=1; ; dry_run--)
474         {
475                 for(tok=beg_tok; *tok!=EOL;)
476                 {
477                         if (dry_run)
478                                 nargs++;
479                         else
480                         {
481 #if 0                           
482                                 *argptr++ = p;
483 #else
484                                 argPtrs[argp++] = p;
485                                 startOfArg = p;
486 #endif
487                         }
488
489                         // Keep going while tok isn't pointing at a comma or EOL
490                         while (*tok != ',' && *tok != EOL)
491                         {
492                                 // Skip over backslash character, unless it's followed by an EOL
493                                 if (*tok == '\\' && tok[1] != EOL)
494                                         tok++;
495
496                                 switch (*tok)
497                                 {
498                                 case CONST:
499                                 case SYMBOL:
500 //Shamus: Possible bug. ACONST has 2 tokens after it, not just 1
501                                 case ACONST:
502                                         if (dry_run)
503                                         {
504                                                 arg_siz += sizeof(TOKEN);
505                                                 tok++;
506                                         }
507                                         else
508                                         {
509                                                 *p++ = *tok++;
510                                         }
511                                 // FALLTHROUGH (picks up the arg after a CONST, SYMBOL or ACONST)
512                                 default:
513                                         if (dry_run)
514                                         {
515                                                 arg_siz += sizeof(TOKEN);
516                                                 tok++;
517                                         }
518                                         else
519                                         {
520                                                 *p++ = *tok++;
521                                         }
522
523                                         break;
524                                 }
525                         }
526
527                         // We hit the comma or EOL, so count/stuff it
528                         if (dry_run)
529                                 arg_siz += sizeof(TOKEN);
530                         else
531                                 *p++ = EOL;
532
533                         // If we hit the comma instead of an EOL, skip over it
534                         if (*tok == ',')
535                                 tok++;
536
537                         // Do our QnD token grabbing (this will be redone once we get all
538                         // the data structures fixed as this is a really dirty hack)
539                         if (!dry_run)
540                         {
541                                 dest = imacro->argument[argumentNum].token;
542                                 stringNum = 0;
543
544                                 do
545                                 {
546                                         // Remap strings to point the IMACRO internal token storage
547                                         if (*startOfArg == SYMBOL || *startOfArg == STRING)
548                                         {
549                                                 *dest++ = *startOfArg++;
550                                                 imacro->argument[argumentNum].string[stringNum] = strdup(string[*startOfArg++]);
551                                                 *dest++ = stringNum++;
552                                         }
553                                         else
554                                                 *dest++ = *startOfArg++;
555                                 }
556                                 while (*startOfArg != EOL);
557
558                                 *dest = *startOfArg;            // Copy EOL...
559                                 argumentNum++;
560                         }
561                 }
562
563                 // Allocate space for argument ptrs and so on and then go back and
564                 // construct the arg frame
565                 if (dry_run)
566                 {
567                         if (nargs != 0)
568                                 p = (TOKEN *)malloc(arg_siz);
569 //                              p = (TOKEN *)malloc(arg_siz + sizeof(TOKEN));
570
571 /*
572 Shamus:
573 This construct is meant to deal with nested macros, so the simple minded way
574 we deal with them now won't work. :-/ Have to think about how to fix.
575 What we could do is simply move the argp with each call, and move it back by the
576 number of arguments in the macro that's ending. That would solve the problem nicely.
577 [Which we do now. But that uncovered another problem: the token strings are all
578 stale by the time a nested macro gets to the end. But they're supposed to be symbols,
579 which means if we put symbol references into the argument token streams, we can
580 alleviate this problem.]
581 */
582 #if 0
583                         argptr = (TOKEN **)malloc((nargs + 1) * sizeof(LONG));
584                         *argptr++ = (TOKEN *)argp;
585                         argp = argptr;
586 #else
587                         // We don't need to do anything here since we already advance argp
588                         // when parsing the arguments.
589 //                      argp += nargs;
590 #endif
591                 }
592                 else 
593                         break;
594         }
595
596         DEBUG printf("%d\n", argp);
597
598         // Setup imacro:
599         // o  # arguments;
600         // o  -> macro symbol;
601         // o  -> macro definition string list;
602         // o  save 'curuniq', to be restored when the macro pops;
603         // o  bump `macuniq' counter and set 'curuniq' to it;
604         imacro->im_nargs = nargs;
605         imacro->im_macro = mac;
606 //      imacro->im_nextln = (TOKEN *)mac->svalue;
607         imacro->im_nextln = mac->lineList;
608         imacro->im_olduniq = curuniq;
609         curuniq = macuniq++;
610         imacro->argBase = argp - nargs;                 // Shamus: keep track of argument base
611
612         DEBUG
613         {
614                 printf("nargs=%d\n", nargs);
615
616                 for(nargs=0; nargs<imacro->im_nargs; nargs++)
617                 {
618                         printf("arg%d=", nargs);
619 //                      dumptok(argp[imacro->im_nargs - nargs - 1]);
620 //                      dumptok(argPtrs[imacro->im_nargs - nargs - 1]);
621                         dumptok(argPtrs[(argp - imacro->im_nargs) + nargs]);
622                 }
623         }
624
625         return OK;
626 }
627
628
629 #if 0
630 //
631 // Setup inbuilt macros (SubQMod)
632 //
633 static void SetupDefaultMacros(void)
634 {
635         curmac = NewSymbol("mjump", MACRO, 0);
636         curmac->svalue = 0;
637         curmac->sattr = (WORD)(macnum++);
638         argno = 0;
639         defmac2("cc");
640         defmac2("addr");
641         defmac2("jreg");
642 //      curmln = NULL;
643         curmac->lineList = NULL;
644         defmac1("  nop", -1);
645         defmac1("  movei #\\addr,\\jreg", -1);
646         defmac1("  jump  \\cc,(\\jreg)", -1);
647         defmac1("  nop", -1);
648         defmac1("  nop", -1);
649
650         curmac = NewSymbol("mjr", MACRO, 0);
651         curmac->svalue = 0;
652         curmac->sattr = (WORD)(macnum++);
653         argno = 0;
654         defmac2("cc");
655         defmac2("addr");
656 //      curmln = NULL;
657         curmac->lineList = NULL;
658         defmac1("  jr    \\cc,\\addr", -1);
659         defmac1("  nop", -1);
660         defmac1("  nop", -1);
661
662         curmac = NewSymbol("mpad", MACRO, 0);
663         curmac->svalue = 0;
664         curmac->sattr = (WORD)(macnum++);
665         argno = 0;
666         defmac2("size");
667 //      curmln = NULL;
668         curmac->lineList = NULL;
669         defmac1("  .rept (\\size/2)", -1);
670         defmac1("     nop", -1);
671         defmac1("  .endr", -1);
672 }
673 #endif