//
-// RMAC - Reboot's Macro Assembler for all Atari computers
+// RMAC - Renamed Macro Assembler for all Atari computers
// EXPR.C - Expression Analyzer
-// Copyright (C) 199x Landon Dyer, 2011-2019 Reboot and Friends
+// Copyright (C) 199x Landon Dyer, 2011-2021 Reboot and Friends
// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986
// Source utilised with the kind permission of Landon Dyer
//
CR_DEFINED, CR_REFERENCED, // SUNARY (special unary)
CR_STREQ, CR_MACDEF,
CR_DATE, CR_TIME,
- CR_ABSCOUNT, 0,
+ CR_ABSCOUNT, CR_FILESIZE, 0,
'!', '~', UNMINUS, UNLT, UNGT, 0, // UNARY
'*', '/', '%', 0, // MULT
'+', '-', 0, // ADD
// riscasm.c)
static int symbolNum; // Pointer to the entry in symbolPtr[]
-
//
// Obtain a string value
//
return v;
}
-
//
// Initialize expression analyzer
//
symbolNum = 0;
}
-
//
// Binary operators (all the same precedence)
//
return OK;
}
-
//
// Unary operators (detect unary '-')
// ggn: If expression starts with a plus then also eat it up. For some reason
*evalTokenBuffer.u64++ = sloc;
}
break;
+ case CR_FILESIZE:
+ if (*tok++ != STRING)
+ return error("^^FILESIZE expects filename inside string");
+ *evalTokenBuffer.u32++ = CONST;
+ // @@copypasted from d_incbin, maybe factor this out somehow?
+ // Attempt to open the include file in the current directory, then (if that
+ // failed) try list of include files passed in the enviroment string or by
+ // the "-d" option.
+ int fd, i;
+ char buf1[256];
+
+ if ((fd = open(string[*tok], _OPEN_INC)) < 0)
+ {
+ for(i=0; nthpath("RMACPATH", i, buf1)!=0; i++)
+ {
+ fd = strlen(buf1);
+
+ // Append path char if necessary
+ if ((fd > 0) && (buf1[fd - 1] != SLASHCHAR))
+ strcat(buf1, SLASHSTRING);
+
+ strcat(buf1, string[*tok]);
+
+ if ((fd = open(buf1, _OPEN_INC)) >= 0)
+ goto allright;
+ }
+
+ return error("cannot open: \"%s\"", string[tok[1]]);
+ }
+
+allright:
+ *evalTokenBuffer.u64++ = (uint64_t)lseek(fd, 0L, SEEK_END);
+ close(fd);
+
+ // Advance tok because of consumed string token
+ tok++;
+ break;
case CR_TIME:
*evalTokenBuffer.u32++ = CONST;
*evalTokenBuffer.u64++ = dos_time();
return OK;
}
-
//
// Terminals (CONSTs) and parenthesis grouping
//
case '$':
*evalTokenBuffer.u32++ = ACONST; // Attributed const
*evalTokenBuffer.u32++ = sloc; // Current location
- *evalTokenBuffer.u32++ = cursect | DEFINED; // Store attribs
+ *evalTokenBuffer.u32++ = DEFINED | ((orgactive | org68k_active) ? 0 : cursect); // Store attribs
break;
case '*':
*evalTokenBuffer.u32++ = ACONST; // Attributed const
// pcloc == location at start of line
*evalTokenBuffer.u32++ = (orgactive ? orgaddr : pcloc);
// '*' takes attributes of current section, not ABS!
- *evalTokenBuffer.u32++ = cursect | DEFINED;
+ // Also, if we're ORG'd, the symbol is absolute
+ *evalTokenBuffer.u32++ = DEFINED | ((orgactive | org68k_active) ? 0 : cursect);
break;
default:
return error("bad expression");
return OK;
}
-
//
// Recursive-descent expression analyzer (with some simple speed hacks)
//
PTR ptk;
evalTokenBuffer.u32 = otk; // Set token pointer to 'exprbuf' (direct.c)
- // Also set in various other places too (riscasm.c,
- // e.g.)
+ // 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.
- // 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 0
-// if ((tok[1] == EOL)
- if ((tok[1] == EOL && ((tok[0] != CONST || tok[0] != FCONST) && tokenClass[tok[0]] != SUNARY))
-// || (((*tok == CONST || *tok == FCONST || *tok == SYMBOL) || (*tok >= KW_R0 && *tok <= KW_R31))
-// && (tokenClass[tok[2]] < UNARY)))
- || (((tok[0] == SYMBOL) || (tok[0] >= KW_R0 && tok[0] <= KW_R31))
- && (tokenClass[tok[2]] < UNARY))
- || ((tok[0] == CONST || tok[0] == FCONST) && (tokenClass[tok[3]] < UNARY))
- )
-#else
-// Shamus: Seems to me that this could be greatly simplified by 1st checking if the first token is a multibyte token, *then* checking if there's an EOL after it depending on the actual length of the token (multiple vs. single). Otherwise, we have the horror show that is the following:
+ // Shamus: Seems to me that this could be greatly simplified by 1st
+ // checking if the first token is a multibyte token, *then*
+ // checking if there's an EOL after it depending on the actual
+ // length of the token (multiple vs. single). Otherwise, we have
+ // the horror show that is the following:
if ((tok[1] == EOL
&& (tok[0] != CONST && tokenClass[tok[0]] != SUNARY))
- || (((tok[0] == SYMBOL)
- || (tok[0] >= KW_R0 && tok[0] <= KW_R31))
+ || ((tok[0] == SYMBOL)
&& (tokenClass[tok[2]] < UNARY))
|| ((tok[0] == CONST) && (tokenClass[tok[3]] < UNARY))
)
-// Shamus: Yes, you can parse that out and make some kind of sense of it, but damn, it takes a while to get it and understand the subtle bugs that result from not being careful about what you're checking; especially vis-a-vis niavely checking tok[1] for an EOL. O_o
-#endif
+// Shamus: Yes, you can parse that out and make some kind of sense of it, but damn, it takes a while to get it and understand the subtle bugs that result from not being careful about what you're checking; especially vis-a-vis naively checking tok[1] for an EOL. O_o
{
- if (*tok >= KW_R0 && *tok <= KW_R31)
- {
- *evalTokenBuffer.u32++ = CONST;
- *evalTokenBuffer.u64++ = *a_value = (*tok - KW_R0);
- *a_attr = ABS | DEFINED;
-
- if (a_esym != NULL)
- *a_esym = NULL;
-
- tok++;
- }
- else if (*tok == CONST)
+ if (*tok == CONST)
{
ptk.u32 = tok;
*evalTokenBuffer.u32++ = *ptk.u32++;
{
*evalTokenBuffer.u32++ = CONST;
- if (orgactive)
+ if (orgactive | org68k_active)
+ {
*evalTokenBuffer.u64++ = *a_value = orgaddr;
+ *a_attr = DEFINED; // We have ORG active, it doesn't belong in a section!
+ }
else
+ {
*evalTokenBuffer.u64++ = *a_value = pcloc;
+ // '*' takes attributes of current section, not ABS!
+ *a_attr = cursect | DEFINED;
+ }
- // '*' takes attributes of current section, not ABS!
- *a_attr = cursect | DEFINED;
if (a_esym != NULL)
*a_esym = NULL;
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);
symbolNum++;
#endif
- if (symbol->sattr & DEFINED)
- *a_value = symbol->svalue;
- else
- *a_value = 0;
+ *a_value = (symbol->sattr & DEFINED ? symbol->svalue : 0);
+ *a_attr = (WORD)(symbol->sattr & ~GLOBAL);
/*
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)
+ {
*a_value &= 0x1F;
-
- *a_attr = (WORD)(symbol->sattr & ~GLOBAL);
+ *a_attr |= RISCREG; // Mark it as a register, 'cause it is
+ *a_esym = symbol;
+ }
if ((symbol->sattr & (GLOBAL | DEFINED)) == GLOBAL
&& a_esym != NULL)
return evexpr(otk, a_value, a_attr, a_esym);
}
-
//
// Evaluate expression.
// If the expression involves only ONE external symbol, the expression is
switch ((int)*tk.u32++)
{
case SYMBOL:
-//printf("evexpr(): SYMBOL\n");
sy = symbolPtr[*tk.u32++];
sy->sattr |= REFERENCED; // Set "referenced" bit
case CONST:
*++sval = *tk.u64++;
-//printf("evexpr(): CONST = %lX\n", *sval);
*++sattr = ABS | DEFINED; // Push simple attribs
break;
case FCONST:
-//printf("evexpr(): FCONST = %lf\n", *tk.dp);
// Even though it's a double, we can treat it like a uint64_t since
// we're just moving the bits around.
*++sval = *tk.u64++;
break;
case ACONST:
-//printf("evexpr(): ACONST = %i\n", *tk.u32);
*++sval = *tk.u32++; // Push value
*++sattr = (WORD)*tk.u32++; // Push attribs
break;
// - : ABS
case '+':
-//printf("evexpr(): +\n");
--sval; // Pop value
--sattr; // Pop attrib
-//printf("--> N+N: %i + %i = ", *sval, sval[1]);
// Get FLOAT attribute, if any
attr = (sattr[0] | sattr[1]) & FLOAT;
{
*sval += sval[1]; // Compute value
}
-//printf("%i\n", *sval);
if (!(*sattr & TDB))
*sattr = sattr[1] | attr;
break;
case '-':
-//printf("evexpr(): -\n");
--sval; // Pop value
--sattr; // Pop attrib
-//printf("--> N-N: %i - %i = ", *sval, sval[1]);
// Get FLOAT attribute, if any
attr = (sattr[0] | sattr[1]) & FLOAT;
{
*sval -= sval[1];
}
-//printf("%i\n", *sval);
*sattr |= attr; // Inherit FLOAT attribute
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];
// Unary operators only work on ABS items
case UNMINUS:
-//printf("evexpr(): UNMINUS\n");
if (*sattr & TDB)
return error(seg_error);
break;
case UNLT: // Unary < (get the low byte of a word)
-//printf("evexpr(): UNLT\n");
if (*sattr & TDB)
return error(seg_error);
break;
case UNGT: // Unary > (get the high byte of a word)
-//printf("evexpr(): UNGT\n");
if (*sattr & TDB)
return error(seg_error);
break;
case '!':
-//printf("evexpr(): !\n");
if (*sattr & TDB)
return error(seg_error);
break;
case '~':
-//printf("evexpr(): ~\n");
if (*sattr & TDB)
return error(seg_error);
// Comparison operators must have two values that
// are in the same segment, but that's the only requirement.
case LE:
-//printf("evexpr(): LE\n");
sattr--;
sval--;
break;
case GE:
-//printf("evexpr(): GE\n");
sattr--;
sval--;
break;
case '>':
-//printf("evexpr(): >\n");
sattr--;
sval--;
break;
case '<':
-//printf("evexpr(): <\n");
sattr--;
sval--;
break;
case NE:
-//printf("evexpr(): NE\n");
sattr--;
sval--;
break;
case '=':
-//printf("evexpr(): =\n");
sattr--;
sval--;
// Shamus: Is this true? There's at least one counterexample of legit
// code where this assumption fails to produce correct code.
default:
-//printf("evexpr(): default\n");
switch ((int)tk.u32[-1])
{
case '*':
sval--;
sattr--;
-//printf("--> NxN: %i x %i = ", *sval, sval[1]);
// Get FLOAT attribute, if any
attr = (sattr[0] | sattr[1]) & FLOAT;
{
*sval *= sval[1];
}
-//printf("%i\n", *sval);
-//no *sattr = ABS | DEFINED | attr; // Expr becomes absolute
break;
case '/':
sval--;
sattr--;
-//printf("--> N/N: %i / %i = ", sval[0], sval[1]);
// Get FLOAT attribute, if any
attr = (sattr[0] | sattr[1]) & FLOAT;
{
if (sval[1] == 0)
return error("divide 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
// ints.
*sval = (int32_t)sval[0] / (int32_t)sval[1];
}
-//printf("%i\n", *sval);
-//no *sattr = ABS | DEFINED | attr; // Expr becomes absolute
break;
case '%':
return error("mod (%) by zero");
*sval %= sval[1];
-//no *sattr = ABS | DEFINED; // Expr becomes absolute
break;
case SHL:
return error("floating point numbers not allowed with operator '<<'.");
*sval <<= sval[1];
-//no *sattr = ABS | DEFINED; // Expr becomes absolute
break;
case SHR:
return error("floating point numbers not allowed with operator '>>'.");
*sval >>= sval[1];
-//no *sattr = ABS | DEFINED; // Expr becomes absolute
break;
case '&':
return error("floating point numbers not allowed with operator '&'.");
*sval &= sval[1];
-//no *sattr = ABS | DEFINED; // Expr becomes absolute
break;
case '^':
return error("floating point numbers not allowed with operator '^'.");
*sval ^= sval[1];
-//no *sattr = ABS | DEFINED; // Expr becomes absolute
break;
case '|':
return error("floating point numbers not allowed with operator '|'.");
*sval |= sval[1];
-//no *sattr = ABS | DEFINED; // Expr becomes absolute
break;
default:
return OK;
}
-
//
// Count the # of tokens in the passed in expression
// N.B.: 64-bit constants count as two tokens each
return length;
}
-