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