]> Shamusworld >> Repos - rmac/blob - expr.c
Initial commit.
[rmac] / expr.c
1 ////////////////////////////////////////////////////////////////////////////////////////////////////
2 // RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System
3 // EXPR.C - Expression Analyzer
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 #include "expr.h"
9 #include "token.h"
10 #include "listing.h"
11 #include "error.h"
12 #include "procln.h"
13 #include "symbol.h"
14 #include "sect.h"
15 #include "mach.h"
16 #include "risca.h"
17
18 #define DEF_KW                                              // Declare keyword values 
19 #include "kwtab.h"                                          // Incl generated keyword tables & defs
20
21 static char tokcl[128];                                     // Generated table of token classes
22 static VALUE evstk[EVSTACKSIZE];                            // Evaluator value stack
23 static WORD evattr[EVSTACKSIZE];                            // Evaluator attribute stack
24
25 // Token-class initialization list
26 char itokcl[] = {
27    0,                                                       // END
28    CONST, SYMBOL, 0,                                        // ID 
29    '(', '[', '{', 0,                                        // OPAR
30    ')', ']', '}', 0,                                        // CPAR 
31    CR_DEFINED, CR_REFERENCED,                                 // SUNARY (special unary)
32    CR_STREQ, CR_MACDEF,
33    CR_DATE, CR_TIME, 0,
34    '!', '~', UNMINUS, 0,                                    // UNARY
35    '*', '/', '%', 0,                                        // MULT 
36    '+', '-', 0,                                             // ADD 
37    SHL, SHR, 0,                                             // SHIFT 
38    LE, GE, '<', '>', NE, '=', 0,                            // REL 
39    '&', 0,                                                  // AND 
40    '^', 0,                                                  // XOR 
41    '|', 0,                                                  // OR 
42    1                                                        // (the end) 
43 };
44
45 char missym_error[] = "missing symbol";
46 char *str_error = "missing symbol or string";
47
48 // Convert expression to postfix
49 static TOKEN *tk;                                           // Deposit tokens here 
50 SYM *lookup();
51 SYM *newsym();
52
53 //
54 // --- Obtain a String Value -----------------------------------------------------------------------
55 //
56
57 static VALUE str_value(char *p) {
58    VALUE v;
59
60    for(v = 0; *p; ++p)
61       v = (v << 8) | (*p & 0xff);
62    return(v);
63 }
64
65 //
66 // --- Initialize Expression Analyzer --------------------------------------------------------------
67 //
68
69 void init_expr(void) {
70    int i;                                                   // Iterator
71    char *p;                                                 // Token pointer
72
73    // Initialize token-class table
74    for(i = 0; i < 128; ++i)                                 // Mark all entries END
75       tokcl[i] = END;
76
77    for(i = 0, p = itokcl; *p != 1; ++p)
78       if(*p == 0)
79          ++i;
80       else 
81          tokcl[(int)(*p)] = (char)i;
82 }
83
84 //
85 // --- Binary operators (all the same precedence) --------------------------------------------------
86 //
87
88 int expr0(void) {
89    TOKEN t;
90
91    if(expr1() != OK)
92       return(ERROR);
93    while(tokcl[*tok] >= MULT) {
94       t = *tok++;
95       if(expr1() != OK)
96          return(ERROR);
97       *tk++ = t;
98    }
99    return(OK);
100 }
101
102 // 
103 // --- Unary operators (detect unary '-') ----------------------------------------------------------
104 //
105
106 int expr1(void) {
107    int class;
108    TOKEN t;
109    SYM *sy;
110    char *p, *p2;
111    WORD w;
112    int j;
113
114    class = tokcl[*tok];
115
116    if(*tok == '-' || class == UNARY) {
117       t = *tok++;
118       if(expr2() != OK)
119          return(ERROR);
120       if(t == '-')
121          t = UNMINUS;
122       *tk++ = t;
123    } else if(class == SUNARY)
124       switch((int)*tok++) {
125          case CR_TIME:
126             *tk++ = CONST;
127             *tk++ = dos_time();
128             break;
129          case CR_DATE:
130             *tk++ = CONST;
131             *tk++ = dos_date();
132             break;
133          case CR_MACDEF:                                    // ^^macdef <macro-name>
134             if(*tok++ != SYMBOL) return(error(missym_error));
135             p = (char *)*tok++;
136             if(lookup(p, MACRO, 0) == NULL) w = 0;
137             else w = 1;
138
139             *tk++ = CONST;
140             *tk++ = (TOKEN)w;
141             break;
142          case CR_DEFINED:
143             w = DEFINED;
144             goto getsym;
145          case CR_REFERENCED:
146             w = REFERENCED;
147
148             getsym:
149
150             if(*tok++ != SYMBOL) return(error(missym_error));
151             p = (char *)*tok++;
152             j = 0;
153             if(*p == '.') j = curenv;
154             if((sy = lookup(p, LABEL, j)) != NULL && (sy->sattr & w)) w = 1;
155             else w = 0;
156
157             *tk++ = CONST;
158             *tk++ = (TOKEN)w;
159             break;
160          case CR_STREQ:
161             if(*tok != SYMBOL && *tok != STRING) return(error(str_error));
162             p = (char *)tok[1];
163             tok +=2;
164
165             if(*tok++ != ',') return(error(comma_error));
166
167             if(*tok != SYMBOL && *tok != STRING) return(error(str_error));
168             p2 = (char *)tok[1];
169             tok += 2;
170
171             w = (WORD)(!strcmp(p, p2));
172             *tk++ = CONST;
173             *tk++ = (TOKEN)w;
174             break;
175       } 
176         else 
177       return(expr2());
178
179    return(OK);
180 }
181
182 //
183 // --- Terminals (CONSTs) and parenthesis grouping -------------------------------------------------
184 //
185
186 int expr2(void) {
187    char *p;
188    SYM *sy;
189    int j;
190
191    switch((int)*tok++) {
192       case CONST:
193          *tk++ = CONST;
194          *tk++ = *tok++;
195          break;
196       case SYMBOL:
197          p = (char *)*tok++;
198          j = 0;
199          if(*p == '.')
200             j = curenv;
201          sy = lookup(p, LABEL, j);
202          if(sy == NULL)
203             sy = newsym(p, LABEL, j);
204
205          if(sy->sattre & EQUATEDREG) {                      // Check register bank usage
206             if((regbank == BANK_0) && (sy->sattre & BANK_1) && !altbankok)   
207                warns("equated symbol \'%s\' cannot be used in register bank 0", sy->sname);
208             if((regbank == BANK_1) && (sy->sattre & BANK_0) && !altbankok)
209                warns("equated symbol \'%s\' cannot be used in register bank 1", sy->sname);
210          }
211
212          *tk++ = SYMBOL;
213          *tk++ = (TOKEN)sy;
214          break;
215       case STRING:
216          *tk++ = CONST;
217          *tk++ = str_value((char *)*tok++);
218          break;
219       case '(':
220          if(expr0() != OK)
221             return(ERROR);
222          if(*tok++ != ')')
223             return(error("missing close parenthesis ')'"));
224          break;
225       case '[':
226          if(expr0() != OK)
227             return(ERROR);
228          if(*tok++ != ']')
229             return(error("missing close parenthesis ']'"));
230          break;
231       case '$':
232          *tk++ = ACONST;                                    // Attributed const
233          *tk++ = sloc;                                      // Current location
234          *tk++ = cursect | DEFINED;                         // Store attribs
235          break;
236       case '*':
237          *tk++ = ACONST;                                    // Attributed const
238          if(orgactive)
239             *tk++ = orgaddr;
240          else
241             *tk++ = pcloc;                                  // Location at start of line
242          *tk++ = ABS | DEFINED;                             // Store attribs
243          break;
244       default:
245          return(error("bad expression"));
246    }
247    return(OK);
248 }
249
250 //
251 // --- Recursive-descent expression analyzer (with some simple speed hacks) ------------------------
252 //
253
254 int expr(TOKEN *otk, VALUE *a_value, WORD *a_attr, SYM **a_esym) {
255    SYM *sy;
256    char *p;
257    int j;
258
259    tk = otk;
260         
261    // Optimize for single constant or single symbol.
262    if((tok[1] == EOL) ||
263       (((*tok == CONST || *tok == SYMBOL) || (*tok >= KW_R0 && *tok <= KW_R31)) && 
264        (tokcl[tok[2]] < UNARY))) {
265
266       if(*tok >= KW_R0 && *tok <= KW_R31) {
267          *tk++ = CONST;
268          *tk++ = *a_value = (*tok - KW_R0);
269          *a_attr = ABS | DEFINED;
270          if(a_esym != NULL)
271             *a_esym = NULL;
272          tok++;
273          *tk++ = ENDEXPR;
274          return(OK);
275       } else if(*tok == CONST) {
276          *tk++ = CONST;
277          *tk++ = *a_value = tok[1];
278          *a_attr = ABS | DEFINED;
279          if(a_esym != NULL)
280             *a_esym = NULL;
281       } else if(*tok == '*') {
282          *tk++ = CONST;
283          if(orgactive)
284             *tk++ = *a_value = orgaddr;
285          else
286             *tk++ = *a_value = pcloc;
287          *a_attr = ABS | DEFINED;
288          //*tk++ = 
289          if(a_esym != NULL)
290             *a_esym = NULL;
291          tok--;
292       } else {
293          p = (char *)tok[1];
294          j = 0;
295          if(*p == '.')
296             j = curenv;
297          sy = lookup(p, LABEL, j);
298
299          if(sy == NULL)
300             sy = newsym(p, LABEL, j);
301          sy->sattr |= REFERENCED;
302
303          if(sy->sattre & EQUATEDREG) {                      // Check register bank usage
304             if((regbank == BANK_0) && (sy->sattre & BANK_1) && !altbankok)   
305                warns("equated symbol \'%s\' cannot be used in register bank 0", sy->sname);
306             if((regbank == BANK_1) && (sy->sattre & BANK_0) && !altbankok)
307                warns("equated symbol \'%s\' cannot be used in register bank 1", sy->sname);
308          }
309
310          *tk++ = SYMBOL;
311          *tk++ = (TOKEN)sy;
312
313          if(sy->sattr & DEFINED)
314             *a_value = sy->svalue;
315          else *a_value = 0;
316
317          if(sy->sattre & EQUATEDREG) 
318             *a_value &= 0x1F;
319
320          *a_attr = (WORD)(sy->sattr & ~GLOBAL);
321
322          if((sy->sattr & (GLOBAL|DEFINED)) == GLOBAL && a_esym != NULL) {
323             *a_esym = sy;
324          }
325       }
326       tok += 2;
327       *tk++ = ENDEXPR;
328       return(OK);
329    }
330
331    if(expr0() != OK)
332       return(ERROR);
333    *tk++ = ENDEXPR;
334    return(evexpr(otk, a_value, a_attr, a_esym));
335 }
336
337 //
338 // -------------------------------------------------------------------------------------------------
339 // Evaluate expression.
340 // If the expression involves only ONE external symbol, the expression is UNDEFINED, but it's value 
341 // includes everything but the symbol value, and `a_esym' is set to the external symbol.
342 // -------------------------------------------------------------------------------------------------
343 //
344
345 int evexpr(TOKEN *tk, VALUE *a_value, WORD *a_attr, SYM **a_esym) {
346    WORD *sattr;
347    VALUE *sval;
348    WORD attr;
349    SYM *sy;
350    SYM *esym;
351    WORD sym_seg;
352
353    sval = evstk;                                            // (Empty) initial stack
354    sattr = evattr;
355    esym = NULL;                                             // No external symbol involved
356    sym_seg = 0;
357
358    while(*tk != ENDEXPR)
359       switch((int)*tk++) {
360          case SYMBOL:
361             sy = (SYM *)*tk++;
362             sy->sattr |= REFERENCED;                        // Set "referenced" bit 
363
364             if(!(sy->sattr & DEFINED)) {
365                if(!(sy->sattr & GLOBAL)) {                  // Reference to undefined symbol
366                   *a_attr = 0;
367                   *a_value = 0;
368                   return(OK);
369                }
370                if(esym != NULL)                             // Check for multiple externals
371                   return(error(seg_error));
372                esym = sy;
373             }
374
375             if(sy->sattr & DEFINED) {
376                *++sval = sy->svalue;                        // Push symbol's value
377             } else {
378                *++sval = 0;                               // 0 for undefined symbols 
379             }
380
381             *++sattr = (WORD)(sy->sattr & ~GLOBAL);         // Push attribs
382             sym_seg = (WORD)(sy->sattr & (TEXT|DATA|BSS));
383             break;
384          case CONST:
385             *++sval = *tk++;                                // Push value
386             *++sattr = ABS|DEFINED;                         // Push simple attribs
387             break;
388          case ACONST:
389             *++sval = *tk++;                                // Push value
390             *++sattr = (WORD)*tk++;                         // Push attribs
391             break;
392
393             // Binary "+" and "-" matrix:
394             // 
395             //            ABS    Sect     Other
396             //     ----------------------------
397             //   ABS     |      ABS   |  Sect  |  Other |
398             //   Sect    |      Sect  |  [1]   |  Error |
399             //   Other   |      Other |  Error |  [1]   |
400             //      ----------------------------
401             // 
402             //   [1] + : Error
403             //       - : ABS
404          case '+':
405             --sval;                                         // Pop value
406             --sattr;                                        // Pop attrib 
407             *sval += sval[1];                               // Compute value
408
409             if(!(*sattr & (TEXT|DATA|BSS)))
410                *sattr = sattr[1];
411             else if(sattr[1] & (TEXT|DATA|BSS))
412                return(error(seg_error));
413             break;
414          case '-':
415             --sval;                                         // Pop value
416             --sattr;                                        // Pop attrib 
417             *sval -= sval[1];                               // Compute value
418
419             attr = (WORD)(*sattr & (TEXT|DATA|BSS));
420             if(!attr)
421                *sattr = sattr[1];
422             else if(sattr[1] & (TEXT|DATA|BSS)) {
423                if(!(attr & sattr[1])) {
424                   return(error(seg_error));
425                } else {
426                   *sattr &= ~(TEXT|DATA|BSS);
427                }
428             }
429             break;
430             // Unary operators only work on ABS items
431          case UNMINUS:
432             if(*sattr & (TEXT|DATA|BSS))
433                error(seg_error);
434             *sval = -(int)*sval;
435             *sattr = ABS|DEFINED;                           // Expr becomes absolute
436             break;
437          case '!':
438             if(*sattr & (TEXT|DATA|BSS))
439                error(seg_error);
440             *sval = !*sval;
441             *sattr = ABS|DEFINED;                           // Expr becomes absolute
442             break;
443          case '~':
444             if(*sattr & (TEXT|DATA|BSS))
445                error(seg_error);
446             *sval = ~*sval;
447             *sattr = ABS|DEFINED;                           // Expr becomes absolute
448             break;
449             // Comparison operators must have two values that
450             // are in the same segment, but that's the only requirement.
451          case LE:
452             --sattr;
453             --sval;
454             if((*sattr & TDB) != (sattr[1] & TDB)) error(seg_error);
455             *sattr = ABS|DEFINED;
456             *sval = *sval <= sval[1];
457             break;
458          case GE:
459             --sattr;
460             --sval;
461             if((*sattr & TDB) != (sattr[1] & TDB)) error(seg_error);
462             *sattr = ABS|DEFINED;
463             *sval = *sval >= sval[1];
464             break;
465          case '>':
466             --sattr;
467             --sval;
468             if((*sattr & TDB) != (sattr[1] & TDB)) error(seg_error);
469             *sattr = ABS|DEFINED;
470             *sval = *sval > sval[1];
471             break;
472          case '<':
473             --sattr;
474             --sval;
475             if((*sattr & TDB) != (sattr[1] & TDB)) error(seg_error);
476             *sattr = ABS|DEFINED;
477             *sval = *sval < sval[1];
478             break;
479          case NE:
480             --sattr;
481             --sval;
482             if((*sattr & TDB) != (sattr[1] & TDB)) error(seg_error);
483             *sattr = ABS|DEFINED;
484             *sval = *sval != sval[1];
485             break;
486          case '=':
487             --sattr;
488             --sval;
489             if((*sattr & TDB) != (sattr[1] & TDB)) error(seg_error);
490             *sattr = ABS|DEFINED;
491             *sval = *sval == sval[1];
492             break;
493             // All other binary operators must have two ABS items
494             // to work with.  They all produce an ABS value.
495          default:
496             // GH - Removed for v1.0.15 as part of the fix for indexed loads.
497             //if((*sattr & (TEXT|DATA|BSS)) || (*--sattr & (TEXT|DATA|BSS)))
498                //error(seg_error);
499
500             *sattr = ABS|DEFINED;                           // Expr becomes absolute
501             switch((int)tk[-1]) {
502                case '*':
503                   --sval;
504                   --sattr;                                        // Pop attrib 
505                   *sval *= sval[1];
506                   break;
507                case '/':
508                   --sval;
509                   --sattr;                                        // Pop attrib 
510                   if(sval[1] == 0)
511                      return(error("divide by zero"));
512                   *sval /= sval[1];
513                   break;
514                case '%':
515                   --sval;
516                   --sattr;                                        // Pop attrib 
517                   if(sval[1] == 0)
518                      return(error("mod (%) by zero"));
519                   *sval %= sval[1];
520                   break;
521                case SHL:
522                   --sval;
523                   --sattr;                                        // Pop attrib 
524                   *sval <<= sval[1];
525                   break;
526                case SHR:
527                   --sval;
528                   --sattr;                                        // Pop attrib 
529                   *sval >>= sval[1];
530                   break;
531                case '&':
532                   --sval;
533                   --sattr;                                        // Pop attrib 
534                   *sval &= sval[1];
535                   break;
536                case '^':
537                   --sval;
538                   --sattr;                                        // Pop attrib 
539                   *sval ^= sval[1];
540                   break;
541                case '|':
542                   --sval;
543                   --sattr;                                        // Pop attrib 
544                   *sval |= sval[1];
545                   break;
546                default:
547                   interror(5);                              // Bad operator in expression stream
548             }
549       }
550
551    if(esym != NULL) *sattr &= ~DEFINED;
552    if(a_esym != NULL) *a_esym = esym;
553
554    // sym_seg added in 1.0.16 to solve a problem with forward symbols in expressions where absolute
555    // values also existed. The absolutes were overiding the symbol segments and not being included :(
556    //*a_attr = *sattr | sym_seg;                                        // Copy value + attrib
557
558    *a_attr = *sattr;                                        // Copy value + attrib
559    *a_value = *sval;
560
561         return(OK);
562 }
563
564