//
-// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System
+// RMAC - Reboot's Macro Assembler for all Atari computers
// EXPR.C - Expression Analyzer
-// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends
+// Copyright (C) 199x Landon Dyer, 2011-2017 Reboot and Friends
// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986
-// Source Utilised with the Kind Permission of Landon Dyer
+// Source utilised with the kind permission of Landon Dyer
//
#include "expr.h"
#include "symbol.h"
#include "token.h"
-#define DEF_KW // Declare keyword values
-#include "kwtab.h" // Incl generated keyword tables & defs
+#define DEF_KW // Declare keyword values
+#include "kwtab.h" // Incl generated keyword tables & defs
-static char tokenClass[128]; // Generated table of token classes
-static VALUE evstk[EVSTACKSIZE]; // Evaluator value stack
-static WORD evattr[EVSTACKSIZE]; // Evaluator attribute stack
+// N.B.: The size of tokenClass should be identical to the largest value of
+// a token; we're assuming 256 but not 100% sure!
+static char tokenClass[256]; // Generated table of token classes
+static uint32_t evstk[EVSTACKSIZE]; // Evaluator value stack
+static WORD evattr[EVSTACKSIZE]; // Evaluator attribute stack
// Token-class initialization list
char itokcl[] = {
- 0, // END
- CONST, SYMBOL, 0, // ID
- '(', '[', '{', 0, // OPAR
- ')', ']', '}', 0, // CPAR
- CR_DEFINED, CR_REFERENCED, // SUNARY (special unary)
+ 0, // END
+ CONST, SYMBOL, 0, // ID
+ '(', '[', '{', 0, // OPAR
+ ')', ']', '}', 0, // CPAR
+ CR_DEFINED, CR_REFERENCED, // SUNARY (special unary)
CR_STREQ, CR_MACDEF,
- CR_DATE, CR_TIME, 0,
- '!', '~', UNMINUS, 0, // UNARY
- '*', '/', '%', 0, // MULT
- '+', '-', 0, // ADD
- SHL, SHR, 0, // SHIFT
- LE, GE, '<', '>', NE, '=', 0, // REL
- '&', 0, // AND
- '^', 0, // XOR
- '|', 0, // OR
- 1 // (the end)
+ CR_DATE, CR_TIME,
+ CR_ABSCOUNT, 0,
+ '!', '~', UNMINUS, 0, // UNARY
+ '*', '/', '%', 0, // MULT
+ '+', '-', 0, // ADD
+ SHL, SHR, 0, // SHIFT
+ LE, GE, '<', '>', NE, '=', 0, // REL
+ '&', 0, // AND
+ '^', 0, // XOR
+ '|', 0, // OR
+ 1 // (the end)
};
const char missym_error[] = "missing symbol";
const char str_error[] = "missing symbol or string";
// Convert expression to postfix
-static TOKEN * evalTokenBuffer; // Deposit tokens here (this is really a
- // pointer to exprbuf from direct.c)
- // (Can also be from others, like riscasm.c)
-static symbolNum; // Pointer to the entry in symbolPtr[]
+static TOKEN * evalTokenBuffer; // Deposit tokens here (this is really a
+ // pointer to exprbuf from direct.c)
+ // (Can also be from others, like
+ // riscasm.c)
+static int symbolNum; // Pointer to the entry in symbolPtr[]
//
-// Obtain a String Value
+// Obtain a string value
//
-static VALUE str_value(char * p)
+static uint32_t str_value(char * p)
{
- VALUE v;
+ uint32_t v;
for(v=0; *p; p++)
v = (v << 8) | (*p & 0xFF);
//
-// Initialize Expression Analyzer
+// Initialize expression analyzer
//
void InitExpression(void)
{
- int i; // Iterator
- char * p; // Token pointer
-
// Initialize token-class table (all set to END)
- for(i=0; i<128; i++)
+ for(int i=0; i<256; i++)
tokenClass[i] = END;
- for(i=0, p=itokcl; *p!=1; p++)
+ int i = 0;
+
+ for(char * p=itokcl; *p!=1; p++)
{
if (*p == 0)
i++;
- else
+ else
tokenClass[(int)(*p)] = (char)i;
}
if (expr1() != OK)
return ERROR;
-
+
while (tokenClass[*tok] >= MULT)
{
t = *tok++;
//
// Unary operators (detect unary '-')
+// ggn: If expression starts with a plus then also eat it up.
+// For some reason the parser gets confused when this happens and
+// emits a "bad expression".
//
int expr1(void)
{
class = tokenClass[*tok];
- if (*tok == '-' || class == UNARY)
+ if (*tok == '-' || *tok == '+' || class == UNARY)
{
t = *tok++;
{
switch ((int)*tok++)
{
+ case CR_ABSCOUNT:
+ *evalTokenBuffer++ = CONST;
+ *evalTokenBuffer++ = (LONG)sect[ABS].sloc;
+ break;
case CR_TIME:
*evalTokenBuffer++ = CONST;
*evalTokenBuffer++ = dos_time();
if (*tok != SYMBOL && *tok != STRING)
return error(str_error);
- p = string[tok[1]];
+ p2 = string[tok[1]];
tok += 2;
w = (WORD)(!strcmp(p, p2));
break;
}
}
- else
+ else
return expr2();
return OK;
// Check register bank usage
if (sy->sattre & EQUATEDREG)
{
- if ((regbank == BANK_0) && (sy->sattre & BANK_1) && !altbankok)
- warns("equated symbol \'%s\' cannot be used in register bank 0", sy->sname);
+ if ((regbank == BANK_0) && (sy->sattre & BANK_1) && !altbankok)
+ warn("equated symbol \'%s\' cannot be used in register bank 0", sy->sname);
if ((regbank == BANK_1) && (sy->sattre & BANK_0) && !altbankok)
- warns("equated symbol \'%s\' cannot be used in register bank 1", sy->sname);
+ warn("equated symbol \'%s\' cannot be used in register bank 1", sy->sname);
}
*evalTokenBuffer++ = SYMBOL;
-#if 0
- *evalTokenBuffer++ = (TOKEN)sy;
-#else
*evalTokenBuffer++ = symbolNum;
symbolPtr[symbolNum] = sy;
symbolNum++;
-#endif
break;
case STRING:
*evalTokenBuffer++ = CONST;
return ERROR;
if (*tok++ != ')')
- return error("missing close parenthesis ')'");
+ return error("missing closing parenthesis ')'");
break;
case '[':
return ERROR;
if (*tok++ != ']')
- return error("missing close parenthesis ']'");
+ return error("missing closing bracket ']'");
break;
case '$':
case '*':
*evalTokenBuffer++ = ACONST; // Attributed const
- if (orgactive)
- *evalTokenBuffer++ = orgaddr;
- else
- *evalTokenBuffer++ = pcloc; // Location at start of line
+ // pcloc == location at start of line
+ *evalTokenBuffer++ = (orgactive ? orgaddr : pcloc);
+ // '*' takes attributes of current section, not ABS!
+ *evalTokenBuffer++ = cursect | DEFINED;
+ break;
+ case '{':
+ if (expr0() != OK) // Eat up first parameter (register or immediate)
+ return ERROR;
+
+ if (*tok++ != ':') // Demand a ':' there
+ return error("missing colon ':'");
+
+ if (expr0() != OK) // Eat up second parameter (register or immediate)
+ return ERROR;
+
+ if (*tok++ != '}')
+ return error("missing closing brace '}'");
- *evalTokenBuffer++ = ABS | DEFINED; // Store attribs
break;
default:
return error("bad expression");
//
// Recursive-descent expression analyzer (with some simple speed hacks)
//
-int expr(TOKEN * otk, VALUE * a_value, WORD * a_attr, SYM ** a_esym)
+int expr(TOKEN * otk, uint32_t * a_value, WORD * a_attr, SYM ** a_esym)
{
// Passed in values (once derefenced, that is) can all be zero. They are
// there so that the expression analyzer can fill them in as needed. The
evalTokenBuffer = otk; // Set token pointer to 'exprbuf' (direct.c)
// Also set in various other places too (riscasm.c, e.g.)
+//printf("expr(): tokens 0-2: %i %i %i (%c %c %c); tc[2] = %i\n", tok[0], tok[1], tok[2], tok[0], tok[1], tok[2], tokenClass[tok[2]]);
// Optimize for single constant or single symbol.
- if ((tok[1] == EOL)
+ // Shamus: Subtle bug here. EOL token is 101; if you have a constant token
+ // followed by the value 101, it will trigger a bad evaluation here.
+ // This is probably a really bad assumption to be making here...!
+ // (assuming tok[1] == EOL is a single token that is)
+ // Seems that even other tokens (SUNARY type) can fuck this up too.
+// if ((tok[1] == EOL)
+ if ((tok[1] == EOL && (tok[0] != CONST && tokenClass[tok[0]] != SUNARY))
|| (((*tok == CONST || *tok == SYMBOL) || (*tok >= KW_R0 && *tok <= KW_R31))
&& (tokenClass[tok[2]] < UNARY)))
{
*a_esym = NULL;
tok++;
-// *evalTokenBuffer++ = ENDEXPR;
-// return OK;
}
else if (*tok == CONST)
{
*a_esym = NULL;
tok += 2;
+//printf("Quick eval in expr(): CONST = %i, tokenClass[tok[2]] = %i\n", *a_value, tokenClass[*tok]);
}
else if (*tok == '*')
{
else
*evalTokenBuffer++ = *a_value = pcloc;
- *a_attr = ABS | DEFINED;
+ // '*' takes attributes of current section, not ABS!
+ *a_attr = cursect | DEFINED;
if (a_esym != NULL)
*a_esym = NULL;
-// tok--;
tok++;
}
else if (*tok == STRING || *tok == SYMBOL)
p = string[tok[1]];
j = (*p == '.' ? curenv : 0);
symbol = lookup(p, LABEL, j);
+#if 0
+printf("eval: Looking up symbol (%s) [=%08X]\n", p, symbol);
+if (symbol)
+ printf(" attr=%04X, attre=%08X, val=%i, name=%s\n", symbol->sattr, symbol->sattre, symbol->svalue, symbol->sname);
+#endif
if (symbol == NULL)
symbol = NewSymbol(p, LABEL, j);
symbol->sattr |= REFERENCED;
- // Check for undefined register equates
- if (symbol->sattre & UNDEF_EQUR)
+ // Check for undefined register equates, but only if it's not part
+ // of a #<SYMBOL> construct, as it could be that the label that's
+ // been undefined may later be used as an address label--which
+ // means it will be fixed up later, and thus, not an error.
+ if ((symbol->sattre & UNDEF_EQUR) && !riscImmTokenSeen)
{
- errors("undefined register equate '%s'", symbol->sname);
+ error("undefined register equate '%s'", symbol->sname);
//if we return right away, it returns some spurious errors...
// return ERROR;
}
// Check register bank usage
if (symbol->sattre & EQUATEDREG)
{
- if ((regbank == BANK_0) && (symbol->sattre & BANK_1) && !altbankok)
- warns("equated symbol '%s' cannot be used in register bank 0", symbol->sname);
+ if ((regbank == BANK_0) && (symbol->sattre & BANK_1) && !altbankok)
+ warn("equated symbol '%s' cannot be used in register bank 0", symbol->sname);
if ((regbank == BANK_1) && (symbol->sattre & BANK_0) && !altbankok)
- warns("equated symbol '%s' cannot be used in register bank 1", symbol->sname);
+ warn("equated symbol '%s' cannot be used in register bank 1", symbol->sname);
}
*evalTokenBuffer++ = SYMBOL;
All that extra crap that was put into the svalue when doing the equr stuff is
thrown away right here. What the hell is it for?
*/
- if (symbol->sattre & EQUATEDREG)
+ if (symbol->sattre & EQUATEDREG)
*a_value &= 0x1F;
*a_attr = (WORD)(symbol->sattr & ~GLOBAL);
- if ((symbol->sattr & (GLOBAL | DEFINED)) == GLOBAL && a_esym != NULL)
+ if ((symbol->sattr & (GLOBAL | DEFINED)) == GLOBAL
+ && a_esym != NULL)
*a_esym = symbol;
tok += 2;
}
else
{
- // Unknown type here... Alert the user!
+ // Unknown type here... Alert the user!,
error("undefined RISC register in expression");
// Prevent spurious error reporting...
tok++;
return ERROR;
}
-// tok += 2;
*evalTokenBuffer++ = ENDEXPR;
return OK;
}
// UNDEFINED, but it's value includes everything but the symbol value, and
// `a_esym' is set to the external symbol.
//
-int evexpr(TOKEN * tk, VALUE * a_value, WORD * a_attr, SYM ** a_esym)
+int evexpr(TOKEN * tk, uint32_t * a_value, WORD * a_attr, SYM ** a_esym)
{
- WORD * sattr;
- VALUE * sval;
WORD attr;
SYM * sy;
- SYM * esym;
- WORD sym_seg;
-
- sval = evstk; // (Empty) initial stack
- sattr = evattr;
- esym = NULL; // No external symbol involved
- sym_seg = 0;
+ uint32_t * sval = evstk; // (Empty) initial stack
+ WORD * sattr = evattr;
+ SYM * esym = NULL; // No external symbol involved
+ WORD sym_seg = 0;
while (*tk != ENDEXPR)
{
switch ((int)*tk++)
{
case SYMBOL:
-// sy = (SYM *)*tk++;
+//printf("evexpr(): SYMBOL\n");
sy = symbolPtr[*tk++];
- sy->sattr |= REFERENCED; // Set "referenced" bit
+ sy->sattr |= REFERENCED; // Set "referenced" bit
if (!(sy->sattr & DEFINED))
{
}
else
{
- *++sval = 0; // 0 for undefined symbols
+ *++sval = 0; // 0 for undefined symbols
}
*++sattr = (WORD)(sy->sattr & ~GLOBAL); // Push attribs
- sym_seg = (WORD)(sy->sattr & (TEXT | DATA | BSS));
+ sym_seg = (WORD)(sy->sattr & TDB);
break;
case CONST:
+//printf("evexpr(): CONST = %i\n", *tk);
*++sval = *tk++; // Push value
*++sattr = ABS | DEFINED; // Push simple attribs
break;
case ACONST:
+//printf("evexpr(): ACONST = %i\n", *tk);
*++sval = *tk++; // Push value
*++sattr = (WORD)*tk++; // Push attribs
break;
// Binary "+" and "-" matrix:
- //
+ //
// ABS Sect Other
// ----------------------------
// ABS | ABS | Sect | Other |
// Sect | Sect | [1] | Error |
// Other | Other | Error | [1] |
// ----------------------------
- //
+ //
// [1] + : Error
// - : ABS
case '+':
+//printf("evexpr(): +\n");
--sval; // Pop value
- --sattr; // Pop attrib
+ --sattr; // Pop attrib
+//printf("--> N+N: %i + %i = ", *sval, sval[1]);
*sval += sval[1]; // Compute value
+//printf("%i\n", *sval);
- if (!(*sattr & (TEXT | DATA | BSS)))
+ if (!(*sattr & TDB))
*sattr = sattr[1];
- else if (sattr[1] & (TEXT | DATA | BSS))
+ else if (sattr[1] & TDB)
return error(seg_error);
break;
case '-':
+//printf("evexpr(): -\n");
--sval; // Pop value
- --sattr; // Pop attrib
+ --sattr; // Pop attrib
+//printf("--> N-N: %i - %i = ", *sval, sval[1]);
*sval -= sval[1]; // Compute value
+//printf("%i\n", *sval);
- attr = (WORD)(*sattr & (TEXT | DATA | BSS));
-
+ attr = (WORD)(*sattr & TDB);
+#if 0
+printf("EVEXPR (-): sym1 = %X, sym2 = %X\n", attr, sattr[1]);
+#endif
+ // If symbol1 is ABS, take attributes from symbol2
if (!attr)
*sattr = sattr[1];
- else if (sattr[1] & (TEXT | DATA | BSS))
- {
- if (!(attr & sattr[1]))
- return error(seg_error);
- else
- *sattr &= ~(TEXT | DATA | BSS);
- }
+ // Otherwise, they're both TDB and so attributes cancel out
+ else if (sattr[1] & TDB)
+ *sattr &= ~TDB;
break;
// Unary operators only work on ABS items
case UNMINUS:
- if (*sattr & (TEXT | DATA | BSS))
+//printf("evexpr(): UNMINUS\n");
+ if (*sattr & TDB)
error(seg_error);
*sval = -(int)*sval;
*sattr = ABS | DEFINED; // Expr becomes absolute
break;
case '!':
- if (*sattr & (TEXT | DATA | BSS))
+//printf("evexpr(): !\n");
+ if (*sattr & TDB)
error(seg_error);
*sval = !*sval;
*sattr = ABS | DEFINED; // Expr becomes absolute
break;
case '~':
- if (*sattr & (TEXT | DATA | BSS))
+//printf("evexpr(): ~\n");
+ if (*sattr & TDB)
error(seg_error);
*sval = ~*sval;
// Comparison operators must have two values that
// are in the same segment, but that's the only requirement.
case LE:
- --sattr;
- --sval;
+//printf("evexpr(): LE\n");
+ sattr--;
+ sval--;
if ((*sattr & TDB) != (sattr[1] & TDB))
error(seg_error);
*sval = *sval <= sval[1];
break;
case GE:
- --sattr;
- --sval;
+//printf("evexpr(): GE\n");
+ sattr--;
+ sval--;
if ((*sattr & TDB) != (sattr[1] & TDB))
error(seg_error);
*sval = *sval >= sval[1];
break;
case '>':
- --sattr;
- --sval;
+//printf("evexpr(): >\n");
+ sattr--;
+ sval--;
if ((*sattr & TDB) != (sattr[1] & TDB))
error(seg_error);
*sval = *sval > sval[1];
break;
case '<':
- --sattr;
- --sval;
+//printf("evexpr(): <\n");
+ sattr--;
+ sval--;
if ((*sattr & TDB) != (sattr[1] & TDB))
error(seg_error);
*sval = *sval < sval[1];
break;
case NE:
- --sattr;
- --sval;
+//printf("evexpr(): NE\n");
+ sattr--;
+ sval--;
if ((*sattr & TDB) != (sattr[1] & TDB))
error(seg_error);
*sval = *sval != sval[1];
break;
case '=':
- --sattr;
- --sval;
+//printf("evexpr(): =\n");
+ sattr--;
+ sval--;
if ((*sattr & TDB) != (sattr[1] & TDB))
error(seg_error);
// All other binary operators must have two ABS items
// to work with. They all produce an ABS value.
default:
+//printf("evexpr(): default\n");
// GH - Removed for v1.0.15 as part of the fix for indexed loads.
//if ((*sattr & (TEXT|DATA|BSS)) || (*--sattr & (TEXT|DATA|BSS)))
//error(seg_error);
switch ((int)tk[-1])
{
case '*':
- --sval;
- --sattr; // Pop attrib
+ sval--;
+ sattr--; // Pop attrib
+//printf("--> NxN: %i x %i = ", *sval, sval[1]);
*sval *= sval[1];
+//printf("%i\n", *sval);
break;
case '/':
- --sval;
- --sattr; // Pop attrib
+ sval--;
+ sattr--; // Pop attrib
if (sval[1] == 0)
- return error("divide by zero");
-
- *sval /= sval[1];
+ return error("division by zero");
+
+//printf("--> N/N: %i / %i = ", sval[0], sval[1]);
+ // Compiler is picky here: Without casting these, it discards
+ // the sign if dividing a negative # by a positive one,
+ // creating a bad result. :-/
+ // Definitely a side effect of using uint32_ts intead of ints.
+ *sval = (int32_t)sval[0] / (int32_t)sval[1];
+//printf("%i\n", *sval);
break;
case '%':
- --sval;
- --sattr; // Pop attrib
+ sval--;
+ sattr--; // Pop attrib
if (sval[1] == 0)
return error("mod (%) by zero");
*sval %= sval[1];
break;
case SHL:
- --sval;
- --sattr; // Pop attrib
+ sval--;
+ sattr--; // Pop attrib
*sval <<= sval[1];
break;
case SHR:
- --sval;
- --sattr; // Pop attrib
+ sval--;
+ sattr--; // Pop attrib
*sval >>= sval[1];
break;
case '&':
- --sval;
- --sattr; // Pop attrib
+ sval--;
+ sattr--; // Pop attrib
*sval &= sval[1];
break;
case '^':
- --sval;
- --sattr; // Pop attrib
+ sval--;
+ sattr--; // Pop attrib
*sval ^= sval[1];
break;
case '|':
- --sval;
- --sattr; // Pop attrib
+ sval--;
+ sattr--; // Pop attrib
*sval |= sval[1];
break;
default:
// sym_seg added in 1.0.16 to solve a problem with forward symbols in
// expressions where absolute values also existed. The absolutes were
// overiding the symbol segments and not being included :(
- //*a_attr = *sattr | sym_seg; // Copy value + attrib
+ //*a_attr = *sattr | sym_seg; // Copy value + attrib
*a_attr = *sattr; // Copy value + attrib
*a_value = *sval;
return OK;
}
+