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