diff --git a/include/asm/rpn.h b/include/asm/rpn.h index 61557bd8..41127652 100644 --- a/include/asm/rpn.h +++ b/include/asm/rpn.h @@ -49,6 +49,8 @@ static inline bool rpn_isSymbol(const struct Expression *expr) void rpn_Symbol(struct Expression *expr, char *tzSym); void rpn_Number(struct Expression *expr, uint32_t i); void rpn_LOGNOT(struct Expression *expr, const struct Expression *src); +struct Symbol const *rpn_SymbolOf(struct Expression const *expr); +bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *sym); void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr, const struct Expression *src1, const struct Expression *src2); diff --git a/src/asm/rpn.c b/src/asm/rpn.c index e75958b0..26572717 100644 --- a/src/asm/rpn.c +++ b/src/asm/rpn.c @@ -297,25 +297,28 @@ static int32_t shift(int32_t shiftee, int32_t amount) } } -static struct Symbol const *symbolOf(struct Expression const *expr) +struct Symbol const *rpn_SymbolOf(struct Expression const *expr) { if (!rpn_isSymbol(expr)) return NULL; return sym_FindSymbol((char *)expr->tRPN + 1); } +bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *sym) +{ + /* Check if both expressions only refer to a single symbol */ + struct Symbol const *sym1 = rpn_SymbolOf(src); + + if (!sym1 || !sym || sym1->type != SYM_LABEL || sym->type != SYM_LABEL) + return false; + + return sym_GetSection(sym1) == sym_GetSection(sym); +} + static bool isDiffConstant(struct Expression const *src1, struct Expression const *src2) { - /* Check if both expressions only refer to a single symbol */ - struct Symbol const *symbol1 = symbolOf(src1); - struct Symbol const *symbol2 = symbolOf(src2); - - if (!symbol1 || !symbol2 - || symbol1->type != SYM_LABEL || symbol2->type != SYM_LABEL) - return false; - - return sym_GetSection(symbol1) == sym_GetSection(symbol2); + return rpn_IsDiffConstant(src1, rpn_SymbolOf(src2)); } void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr, @@ -428,8 +431,8 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr, } } else if (op == RPN_SUB && isDiffConstant(src1, src2)) { - struct Symbol const *symbol1 = symbolOf(src1); - struct Symbol const *symbol2 = symbolOf(src2); + struct Symbol const *symbol1 = rpn_SymbolOf(src1); + struct Symbol const *symbol2 = rpn_SymbolOf(src2); expr->nVal = sym_GetValue(symbol1) - sym_GetValue(symbol2); expr->isKnown = true; diff --git a/src/asm/section.c b/src/asm/section.c index 1af1133b..39aede99 100644 --- a/src/asm/section.c +++ b/src/asm/section.c @@ -553,15 +553,21 @@ void out_PCRelByte(struct Expression *expr) { checkcodesection(); reserveSpace(1); + struct Symbol const *pc = sym_FindSymbol("@"); - if (!rpn_isKnown(expr) || pCurrentSection->nOrg == -1) { + if (!rpn_IsDiffConstant(expr, pc)) { createPatch(PATCHTYPE_JR, expr); writebyte(0); } else { - /* Target is relative to the byte *after* the operand */ - uint16_t address = sym_GetPCValue() + 1; - /* The offset wraps (jump from ROM to HRAM, for loopexample) */ - int16_t offset = expr->nVal - address; + struct Symbol const *sym = rpn_SymbolOf(expr); + /* The offset wraps (jump from ROM to HRAM, for example) */ + int16_t offset; + + /* Offset is relative to the byte *after* the operand */ + if (sym == pc) + offset = -2; /* PC as operand to `jr` is lower than reference PC by 2 */ + else + offset = sym_GetValue(sym) - (sym_GetValue(pc) + 1); if (offset < -128 || offset > 127) { yyerror("jr target out of reach (expected -129 < %" PRId16 " < 128)", diff --git a/test/asm/jr-@.asm b/test/asm/jr-@.asm new file mode 100644 index 00000000..2bd55f23 --- /dev/null +++ b/test/asm/jr-@.asm @@ -0,0 +1,7 @@ +SECTION "fixed", ROM0[0] + jr @ +; We need this section to be floating because RGBASM can know the value of PC +; otherwise, leading to different behavior +; FIXME: we rely on this landing at address 2, which isn't *guaranteed*... +SECTION "floating", ROM0 + jr @ diff --git a/test/asm/jr-@.err b/test/asm/jr-@.err new file mode 100644 index 00000000..e69de29b diff --git a/test/asm/jr-@.out b/test/asm/jr-@.out new file mode 100644 index 00000000..e69de29b diff --git a/test/asm/jr-@.out.bin b/test/asm/jr-@.out.bin new file mode 100644 index 00000000..4bc11ec3 --- /dev/null +++ b/test/asm/jr-@.out.bin @@ -0,0 +1 @@ +þþ \ No newline at end of file diff --git a/test/asm/jr-section.asm b/test/asm/jr-section.asm new file mode 100644 index 00000000..fe1bd8fc --- /dev/null +++ b/test/asm/jr-section.asm @@ -0,0 +1,5 @@ +SECTION "Test", ROM0 + +Label: + jr Label + PRINTV Label - @ diff --git a/test/asm/jr-section.err b/test/asm/jr-section.err new file mode 100644 index 00000000..e69de29b diff --git a/test/asm/jr-section.out b/test/asm/jr-section.out new file mode 100644 index 00000000..0ad6e2f9 --- /dev/null +++ b/test/asm/jr-section.out @@ -0,0 +1 @@ +$FFFFFFFE \ No newline at end of file diff --git a/test/asm/jr-section.out.bin b/test/asm/jr-section.out.bin new file mode 100644 index 00000000..cae4e432 --- /dev/null +++ b/test/asm/jr-section.out.bin @@ -0,0 +1 @@ +þ \ No newline at end of file diff --git a/test/link/jr-@.asm b/test/link/jr-@.asm index 2bd55f23..68673a82 100644 --- a/test/link/jr-@.asm +++ b/test/link/jr-@.asm @@ -1,7 +1,3 @@ -SECTION "fixed", ROM0[0] - jr @ -; We need this section to be floating because RGBASM can know the value of PC -; otherwise, leading to different behavior -; FIXME: we rely on this landing at address 2, which isn't *guaranteed*... -SECTION "floating", ROM0 - jr @ +SECTION "Floating", ROM0 + ; RGBASM knows how to compute `jr @` by itself, but this will evade it + jr @ - 1 + 1 diff --git a/test/link/jr-@.out.bin b/test/link/jr-@.out.bin index 4bc11ec3..cae4e432 100644 --- a/test/link/jr-@.out.bin +++ b/test/link/jr-@.out.bin @@ -1 +1 @@ -þþ \ No newline at end of file +þ \ No newline at end of file