mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Allow binary AND to be sometimes constant (#976)
This commit is contained in:
@@ -28,7 +28,7 @@ struct Section {
|
|||||||
uint32_t size;
|
uint32_t size;
|
||||||
uint32_t org;
|
uint32_t org;
|
||||||
uint32_t bank;
|
uint32_t bank;
|
||||||
uint8_t align;
|
uint8_t align; // Exactly as specified in `ALIGN[]`
|
||||||
uint16_t alignOfs;
|
uint16_t alignOfs;
|
||||||
struct Section *next;
|
struct Section *next;
|
||||||
struct Patch *patches;
|
struct Patch *patches;
|
||||||
|
|||||||
@@ -317,7 +317,7 @@ struct Symbol const *rpn_SymbolOf(struct Expression const *expr)
|
|||||||
{
|
{
|
||||||
if (!rpn_isSymbol(expr))
|
if (!rpn_isSymbol(expr))
|
||||||
return NULL;
|
return NULL;
|
||||||
return sym_FindScopedSymbol((char *)expr->rpn + 1);
|
return sym_FindScopedSymbol((char const *)expr->rpn + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *sym)
|
bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *sym)
|
||||||
@@ -339,10 +339,51 @@ static bool isDiffConstant(struct Expression const *src1,
|
|||||||
return rpn_IsDiffConstant(src1, rpn_SymbolOf(src2));
|
return rpn_IsDiffConstant(src1, rpn_SymbolOf(src2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to compute a constant binary AND from non-constant operands
|
||||||
|
* This is possible if one operand is a symbol belonging to an `ALIGN[N]` section, and the other is
|
||||||
|
* a constant that only keeps (some of) the lower N bits.
|
||||||
|
*
|
||||||
|
* @return The constant result if it can be computed, or -1 otherwise.
|
||||||
|
*/
|
||||||
|
static int32_t tryConstMask(struct Expression const *lhs, struct Expression const *rhs)
|
||||||
|
{
|
||||||
|
struct Symbol const *sym = rpn_SymbolOf(lhs);
|
||||||
|
struct Expression const *expr = rhs;
|
||||||
|
|
||||||
|
if (!sym || !sym_GetSection(sym)) {
|
||||||
|
// If the lhs isn't a symbol, try again the other way around
|
||||||
|
sym = rpn_SymbolOf(rhs);
|
||||||
|
expr = lhs;
|
||||||
|
|
||||||
|
if (!sym || !sym_GetSection(sym))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
assert(sym_IsNumeric(sym));
|
||||||
|
|
||||||
|
if (!rpn_isKnown(expr))
|
||||||
|
return -1;
|
||||||
|
// We can now safely use `expr->val`
|
||||||
|
struct Section const *sect = sym_GetSection(sym);
|
||||||
|
int32_t unknownBits = (1 << 16) - (1 << sect->align); // The max alignment is 16
|
||||||
|
|
||||||
|
// The mask must ignore all unknown bits
|
||||||
|
if ((expr->val & unknownBits) != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// `sym_GetValue()` attempts to add the section's address,
|
||||||
|
// but that's "-1" because the section is floating (otherwise we wouldn't be here)
|
||||||
|
assert(sect->org == (uint32_t)-1);
|
||||||
|
int32_t symbolOfs = sym_GetValue(sym) + 1;
|
||||||
|
|
||||||
|
return (symbolOfs + sect->alignOfs) & ~unknownBits;
|
||||||
|
}
|
||||||
|
|
||||||
void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
||||||
const struct Expression *src1, const struct Expression *src2)
|
const struct Expression *src1, const struct Expression *src2)
|
||||||
{
|
{
|
||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
|
int32_t constMaskVal;
|
||||||
|
|
||||||
/* First, check if the expression is known */
|
/* First, check if the expression is known */
|
||||||
expr->isKnown = src1->isKnown && src2->isKnown;
|
expr->isKnown = src1->isKnown && src2->isKnown;
|
||||||
@@ -490,6 +531,9 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
|||||||
|
|
||||||
expr->val = sym_GetValue(symbol1) - sym_GetValue(symbol2);
|
expr->val = sym_GetValue(symbol1) - sym_GetValue(symbol2);
|
||||||
expr->isKnown = true;
|
expr->isKnown = true;
|
||||||
|
} else if (op == RPN_AND && (constMaskVal = tryConstMask(src1, src2)) != -1) {
|
||||||
|
expr->val = constMaskVal;
|
||||||
|
expr->isKnown = true;
|
||||||
} else {
|
} else {
|
||||||
/* If it's not known, start computing the RPN expression */
|
/* If it's not known, start computing the RPN expression */
|
||||||
|
|
||||||
|
|||||||
17
test/asm/const-and.asm
Normal file
17
test/asm/const-and.asm
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
println @ & 0 ; This should produce an error, but not crash
|
||||||
|
|
||||||
|
SECTION "Test", ROM0,ALIGN[4,2]
|
||||||
|
|
||||||
|
ds 40
|
||||||
|
Aligned:
|
||||||
|
|
||||||
|
println Aligned & $0f
|
||||||
|
println ~$fffc & Aligned
|
||||||
|
println Aligned & $1f ; Not constant
|
||||||
|
println @ & $0f
|
||||||
|
|
||||||
|
SECTION "Unaligned", ROM0
|
||||||
|
|
||||||
|
println @ & 0
|
||||||
|
println @ & 1 ; Nope
|
||||||
7
test/asm/const-and.err
Normal file
7
test/asm/const-and.err
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
error: const-and.asm(2):
|
||||||
|
PC has no value outside a section
|
||||||
|
error: const-and.asm(11):
|
||||||
|
Expected constant expression: 'Aligned' is not constant at assembly time
|
||||||
|
error: const-and.asm(17):
|
||||||
|
Expected constant expression: PC is not constant at assembly time
|
||||||
|
error: Assembly aborted (3 errors)!
|
||||||
7
test/asm/const-and.out
Normal file
7
test/asm/const-and.out
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
$0
|
||||||
|
$A
|
||||||
|
$A
|
||||||
|
$0
|
||||||
|
$A
|
||||||
|
$0
|
||||||
|
$0
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
|
|
||||||
SECTION "test", ROM0
|
SECTION "test", ROM0
|
||||||
|
|
||||||
ds 123
|
|
||||||
|
|
||||||
FloatingBase:
|
FloatingBase:
|
||||||
assert WARN, FloatingBase & 0, "Worry about me, but not too much."
|
assert WARN, FloatingBase, "Worry about me, but not too much."
|
||||||
assert FAIL, FloatingBase & 0, "Okay, this is getting serious!"
|
assert FAIL, FloatingBase, "Okay, this is getting serious!"
|
||||||
assert FATAL, FloatingBase & 0, "It all ends now."
|
assert FATAL, FloatingBase, "It all ends now."
|
||||||
assert FAIL, FloatingBase & 0, "Not even time to roll credits!"
|
assert FAIL, FloatingBase, "Not even time to roll credits!"
|
||||||
assert WARN, 0, "Still can finish the film, though!"
|
assert WARN, 0, "Still can finish the film, though!"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
warning: assert.asm(7): Worry about me, but not too much.
|
warning: assert.asm(5): Worry about me, but not too much.
|
||||||
error: assert.asm(8): Okay, this is getting serious!
|
error: assert.asm(6): Okay, this is getting serious!
|
||||||
FATAL: assert.asm(9): It all ends now.
|
FATAL: assert.asm(7): It all ends now.
|
||||||
Linking aborted after 2 errors
|
Linking aborted after 2 errors
|
||||||
|
|||||||
Reference in New Issue
Block a user