ref: ee20d9010e49bd243e57aa41ccc32bf5da99e0bf
parent: 2bc12447e2888ef60593242ec044ef60bc865d9d
author: Rangi <remy.oukaour+rangi42@gmail.com>
date: Sun Feb 14 15:53:44 EST 2021
Make `@` relative to the start of a `ds` even at link time Fix #737
--- a/include/asm/output.h
+++ b/include/asm/output.h
@@ -22,7 +22,7 @@
void out_RegisterNode(struct FileStackNode *node);
void out_ReplaceNode(struct FileStackNode *node);
void out_SetFileName(char *s);
-void out_CreatePatch(uint32_t type, struct Expression const *expr, uint32_t ofs, bool isOperand);
+void out_CreatePatch(uint32_t type, struct Expression const *expr, uint32_t ofs, uint32_t pcShift);
bool out_CreateAssert(enum AssertionType type, struct Expression const *expr,
char const *message, uint32_t ofs);
void out_WriteObject(void);
--- a/include/asm/section.h
+++ b/include/asm/section.h
@@ -62,11 +62,11 @@
void out_AbsLongGroup(uint8_t const *s, int32_t length);
void out_Skip(int32_t skip, bool ds);
void out_String(char const *s);
-void out_RelByte(struct Expression *expr, bool isOperand);
+void out_RelByte(struct Expression *expr, uint32_t pcShift);
void out_RelBytes(struct Expression *expr, uint32_t n);
-void out_RelWord(struct Expression *expr, bool isOperand);
-void out_RelLong(struct Expression *expr, bool isOperand);
-void out_PCRelByte(struct Expression *expr, bool isOperand);
+void out_RelWord(struct Expression *expr, uint32_t pcShift);
+void out_RelLong(struct Expression *expr, uint32_t pcShift);
+void out_PCRelByte(struct Expression *expr, uint32_t pcShift);
void out_BinaryFile(char const *s, int32_t startPos);
void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length);
--- a/include/link/section.h
+++ b/include/link/section.h
@@ -34,7 +34,6 @@
uint32_t pcSectionID;
uint32_t pcOffset;
enum PatchType type;
- bool isOperand;
int32_t rpnSize;
uint8_t *rpnExpression;
--- a/include/linkdefs.h
+++ b/include/linkdefs.h
@@ -99,8 +99,6 @@
SYMTYPE_EXPORT
};
-// Bit 7 is special, not part of the actual patch type
-#define PATCH_ISOPERAND 0x80
enum PatchType {
PATCHTYPE_BYTE,
PATCHTYPE_WORD,
--- a/src/asm/output.c
+++ b/src/asm/output.c
@@ -39,7 +39,6 @@
struct Section *pcSection;
uint32_t pcOffset;
uint8_t type;
- bool isOperand; // If set, PC is not at the patch's address, but at the byte before
uint32_t nRPNSize;
uint8_t *pRPN;
struct Patch *next;
@@ -204,17 +203,12 @@
static void writepatch(struct Patch const *patch, FILE *f)
{
assert(patch->src->ID != -1);
- uint8_t type = patch->type;
-
- if (patch->isOperand)
- type |= PATCH_ISOPERAND;
-
putlong(patch->src->ID, f);
putlong(patch->lineNo, f);
putlong(patch->nOffset, f);
putlong(getSectIDIfAny(patch->pcSection), f);
putlong(patch->pcOffset, f);
- putc(type, f);
+ putc(patch->type, f);
putlong(patch->nRPNSize, f);
fwrite(patch->pRPN, 1, patch->nRPNSize, f);
}
@@ -387,7 +381,6 @@
fatalerror("No memory for patch's RPN expression: %s\n", strerror(errno));
patch->type = type;
- patch->isOperand = false;
patch->src = node;
out_RegisterNode(node);
patch->lineNo = lexer_GetLineNo();
@@ -416,11 +409,15 @@
/*
* Create a new patch (includes the rpn expr)
*/
-void out_CreatePatch(uint32_t type, struct Expression const *expr, uint32_t ofs, bool isOperand)
+void out_CreatePatch(uint32_t type, struct Expression const *expr, uint32_t ofs, uint32_t pcShift)
{
struct Patch *patch = allocpatch(type, expr, ofs);
- patch->isOperand = isOperand;
+ // If the patch had a quantity of bytes output before it,
+ // PC is not at the patch's location, but at the location
+ // before those bytes.
+ patch->pcOffset -= pcShift;
+
patch->next = pCurrentSection->patches;
pCurrentSection->patches = patch;
}
--- a/src/asm/parser.y
+++ b/src/asm/parser.y
@@ -1122,7 +1122,7 @@
$$ = true;
}
| reloc_8bit_no_str {
- out_RelByte(&$1, false);
+ out_RelByte(&$1, 0);
$$ = false;
}
| string {
@@ -1146,7 +1146,7 @@
$$ = true;
}
| reloc_16bit_no_str {
- out_RelWord(&$1, false);
+ out_RelWord(&$1, 0);
$$ = false;
}
| string {
@@ -1170,7 +1170,7 @@
$$ = true;
}
| relocexpr_no_str {
- out_RelLong(&$1, false);
+ out_RelLong(&$1, 0);
$$ = false;
}
| string {
@@ -1570,7 +1570,7 @@
z80_adc : T_Z80_ADC op_a_n {
out_AbsByte(0xCE);
- out_RelByte(&$2, true);
+ out_RelByte(&$2, 1);
}
| T_Z80_ADC op_a_r { out_AbsByte(0x88 | $2); }
;
@@ -1577,13 +1577,13 @@
z80_add : T_Z80_ADD op_a_n {
out_AbsByte(0xC6);
- out_RelByte(&$2, true);
+ out_RelByte(&$2, 1);
}
| T_Z80_ADD op_a_r { out_AbsByte(0x80 | $2); }
| T_Z80_ADD op_hl_ss { out_AbsByte(0x09 | ($2 << 4)); }
| T_Z80_ADD T_MODE_SP T_COMMA reloc_8bit {
out_AbsByte(0xE8);
- out_RelByte(&$4, true);
+ out_RelByte(&$4, 1);
}
;
@@ -1590,7 +1590,7 @@
z80_and : T_Z80_AND op_a_n {
out_AbsByte(0xE6);
- out_RelByte(&$2, true);
+ out_RelByte(&$2, 1);
}
| T_Z80_AND op_a_r { out_AbsByte(0xA0 | $2); }
;
@@ -1603,11 +1603,11 @@
z80_call : T_Z80_CALL reloc_16bit {
out_AbsByte(0xCD);
- out_RelWord(&$2, true);
+ out_RelWord(&$2, 1);
}
| T_Z80_CALL ccode T_COMMA reloc_16bit {
out_AbsByte(0xC4 | ($2 << 3));
- out_RelWord(&$4, true);
+ out_RelWord(&$4, 1);
}
;
@@ -1616,7 +1616,7 @@
z80_cp : T_Z80_CP op_a_n {
out_AbsByte(0xFE);
- out_RelByte(&$2, true);
+ out_RelByte(&$2, 1);
}
| T_Z80_CP op_a_r { out_AbsByte(0xB8 | $2); }
;
@@ -1650,11 +1650,11 @@
z80_jp : T_Z80_JP reloc_16bit {
out_AbsByte(0xC3);
- out_RelWord(&$2, true);
+ out_RelWord(&$2, 1);
}
| T_Z80_JP ccode T_COMMA reloc_16bit {
out_AbsByte(0xC2 | ($2 << 3));
- out_RelWord(&$4, true);
+ out_RelWord(&$4, 1);
}
| T_Z80_JP T_MODE_HL {
out_AbsByte(0xE9);
@@ -1663,11 +1663,11 @@
z80_jr : T_Z80_JR reloc_16bit {
out_AbsByte(0x18);
- out_PCRelByte(&$2, true);
+ out_PCRelByte(&$2, 1);
}
| T_Z80_JR ccode T_COMMA reloc_16bit {
out_AbsByte(0x20 | ($2 << 3));
- out_PCRelByte(&$4, true);
+ out_PCRelByte(&$4, 1);
}
;
@@ -1691,13 +1691,13 @@
rpn_CheckHRAM(&$4, &$4);
out_AbsByte(0xF0);
- out_RelByte(&$4, true);
+ out_RelByte(&$4, 1);
}
| T_Z80_LDH op_mem_ind T_COMMA T_MODE_A {
rpn_CheckHRAM(&$2, &$2);
out_AbsByte(0xE0);
- out_RelByte(&$2, true);
+ out_RelByte(&$2, 1);
}
| T_Z80_LDH T_MODE_A T_COMMA c_ind {
out_AbsByte(0xF2);
@@ -1723,11 +1723,11 @@
z80_ld_hl : T_Z80_LD T_MODE_HL T_COMMA T_MODE_SP reloc_8bit {
out_AbsByte(0xF8);
- out_RelByte(&$5, true);
+ out_RelByte(&$5, 1);
}
| T_Z80_LD T_MODE_HL T_COMMA reloc_16bit {
out_AbsByte(0x01 | (REG_HL << 4));
- out_RelWord(&$4, true);
+ out_RelWord(&$4, 1);
}
;
@@ -1734,13 +1734,13 @@
z80_ld_sp : T_Z80_LD T_MODE_SP T_COMMA T_MODE_HL { out_AbsByte(0xF9); }
| T_Z80_LD T_MODE_SP T_COMMA reloc_16bit {
out_AbsByte(0x01 | (REG_SP << 4));
- out_RelWord(&$4, true);
+ out_RelWord(&$4, 1);
}
;
z80_ld_mem : T_Z80_LD op_mem_ind T_COMMA T_MODE_SP {
out_AbsByte(0x08);
- out_RelWord(&$2, true);
+ out_RelWord(&$2, 1);
}
| T_Z80_LD op_mem_ind T_COMMA T_MODE_A {
if (optimizeloads && rpn_isKnown(&$2)
@@ -1750,7 +1750,7 @@
rpn_Free(&$2);
} else {
out_AbsByte(0xEA);
- out_RelWord(&$2, true);
+ out_RelWord(&$2, 1);
}
}
;
@@ -1767,7 +1767,7 @@
z80_ld_r : T_Z80_LD reg_r T_COMMA reloc_8bit {
out_AbsByte(0x06 | ($2 << 3));
- out_RelByte(&$4, true);
+ out_RelByte(&$4, 1);
}
| T_Z80_LD reg_r T_COMMA reg_r {
if (($2 == REG_HL_IND) && ($4 == REG_HL_IND))
@@ -1798,7 +1798,7 @@
rpn_Free(&$4);
} else {
out_AbsByte(0xFA);
- out_RelWord(&$4, true);
+ out_RelWord(&$4, 1);
}
} else {
error("Destination operand must be A\n");
@@ -1809,11 +1809,11 @@
z80_ld_ss : T_Z80_LD T_MODE_BC T_COMMA reloc_16bit {
out_AbsByte(0x01 | (REG_BC << 4));
- out_RelWord(&$4, true);
+ out_RelWord(&$4, 1);
}
| T_Z80_LD T_MODE_DE T_COMMA reloc_16bit {
out_AbsByte(0x01 | (REG_DE << 4));
- out_RelWord(&$4, true);
+ out_RelWord(&$4, 1);
}
/*
* HL is taken care of in z80_ld_hl
@@ -1826,7 +1826,7 @@
z80_or : T_Z80_OR op_a_n {
out_AbsByte(0xF6);
- out_RelByte(&$2, true);
+ out_RelByte(&$2, 1);
}
| T_Z80_OR op_a_r { out_AbsByte(0xB0 | $2); }
;
@@ -1890,10 +1890,7 @@
z80_rst : T_Z80_RST reloc_8bit {
rpn_CheckRST(&$2, &$2);
if (!rpn_isKnown(&$2))
- // This could be considered as an "operand", but the purpose of the
- // "operand" flag is to signal to RGBLINK to correct PC,
- // which we don't want here.
- out_RelByte(&$2, false);
+ out_RelByte(&$2, 0);
else
out_AbsByte(0xC7 | $2.nVal);
rpn_Free(&$2);
@@ -1902,7 +1899,7 @@
z80_sbc : T_Z80_SBC op_a_n {
out_AbsByte(0xDE);
- out_RelByte(&$2, true);
+ out_RelByte(&$2, 1);
}
| T_Z80_SBC op_a_r { out_AbsByte(0x98 | $2); }
;
@@ -1940,13 +1937,13 @@
}
| T_Z80_STOP reloc_8bit {
out_AbsByte(0x10);
- out_RelByte(&$2, true);
+ out_RelByte(&$2, 1);
}
;
z80_sub : T_Z80_SUB op_a_n {
out_AbsByte(0xD6);
- out_RelByte(&$2, true);
+ out_RelByte(&$2, 1);
}
| T_Z80_SUB op_a_r { out_AbsByte(0x90 | $2);
}
@@ -1960,7 +1957,7 @@
z80_xor : T_Z80_XOR op_a_n {
out_AbsByte(0xEE);
- out_RelByte(&$2, true);
+ out_RelByte(&$2, 1);
}
| T_Z80_XOR op_a_r { out_AbsByte(0xA8 | $2); }
;
--- a/src/asm/section.c
+++ b/src/asm/section.c
@@ -488,9 +488,10 @@
writebyte(b >> 24);
}
-static inline void createPatch(enum PatchType type, struct Expression const *expr, bool isOperand)
+static inline void createPatch(enum PatchType type, struct Expression const *expr,
+ uint32_t pcShift)
{
- out_CreatePatch(type, expr, sect_GetOutputOffset(), isOperand);
+ out_CreatePatch(type, expr, sect_GetOutputOffset(), pcShift);
}
void sect_StartUnion(void)
@@ -617,13 +618,13 @@
* Output a relocatable byte. Checking will be done to see if it
* is an absolute value in disguise.
*/
-void out_RelByte(struct Expression *expr, bool isOperand)
+void out_RelByte(struct Expression *expr, uint32_t pcShift)
{
checkcodesection();
reserveSpace(1);
if (!rpn_isKnown(expr)) {
- createPatch(PATCHTYPE_BYTE, expr, isOperand);
+ createPatch(PATCHTYPE_BYTE, expr, pcShift);
writebyte(0);
} else {
writebyte(expr->nVal);
@@ -640,9 +641,9 @@
checkcodesection();
reserveSpace(n);
- while (n--) {
+ for (uint32_t i = 0; i < n; i++) {
if (!rpn_isKnown(expr)) {
- createPatch(PATCHTYPE_BYTE, expr, false);
+ createPatch(PATCHTYPE_BYTE, expr, i);
writebyte(0);
} else {
writebyte(expr->nVal);
@@ -655,13 +656,13 @@
* Output a relocatable word. Checking will be done to see if
* it's an absolute value in disguise.
*/
-void out_RelWord(struct Expression *expr, bool isOperand)
+void out_RelWord(struct Expression *expr, uint32_t pcShift)
{
checkcodesection();
reserveSpace(2);
if (!rpn_isKnown(expr)) {
- createPatch(PATCHTYPE_WORD, expr, isOperand);
+ createPatch(PATCHTYPE_WORD, expr, pcShift);
writeword(0);
} else {
writeword(expr->nVal);
@@ -673,13 +674,13 @@
* Output a relocatable longword. Checking will be done to see if
* is an absolute value in disguise.
*/
-void out_RelLong(struct Expression *expr, bool isOperand)
+void out_RelLong(struct Expression *expr, uint32_t pcShift)
{
checkcodesection();
reserveSpace(2);
if (!rpn_isKnown(expr)) {
- createPatch(PATCHTYPE_LONG, expr, isOperand);
+ createPatch(PATCHTYPE_LONG, expr, pcShift);
writelong(0);
} else {
writelong(expr->nVal);
@@ -691,7 +692,7 @@
* Output a PC-relative relocatable byte. Checking will be done to see if it
* is an absolute value in disguise.
*/
-void out_PCRelByte(struct Expression *expr, bool isOperand)
+void out_PCRelByte(struct Expression *expr, uint32_t pcShift)
{
checkcodesection();
reserveSpace(1);
@@ -698,7 +699,7 @@
struct Symbol const *pc = sym_GetPC();
if (!rpn_IsDiffConstant(expr, pc)) {
- createPatch(PATCHTYPE_JR, expr, isOperand);
+ createPatch(PATCHTYPE_JR, expr, pcShift);
writebyte(0);
} else {
struct Symbol const *sym = rpn_SymbolOf(expr);
--- a/src/link/object.c
+++ b/src/link/object.c
@@ -286,8 +286,7 @@
tryGetc(type, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s type: %s",
fileName, sectName, i);
- patch->type = type & 0x7F;
- patch->isOperand = type & PATCH_ISOPERAND;
+ patch->type = type;
tryReadlong(patch->rpnSize, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s RPN size: %s",
fileName, sectName, i);
--- a/src/link/patch.c
+++ b/src/link/patch.c
@@ -421,10 +421,6 @@
isError = true;
} else {
value = patch->pcOffset + patch->pcSection->org;
- // If the patch is an operand, PC is not at the patch's
- // location, but at the (opcode) byte right before it
- if (patch->isOperand)
- value--;
}
} else {
symbol = getSymbol(fileSymbols, value);
@@ -520,9 +516,10 @@
/* `jr` is quite unlike the others... */
if (patch->type == PATCHTYPE_JR) {
- /* Target is relative to the byte *after* the operand */
+ // Offset is relative to the byte *after* the operand
+ // PC as operand to `jr` is lower than reference PC by 2
uint16_t address = patch->pcSection->org
- + patch->pcOffset + 1;
+ + patch->pcOffset + 2;
int16_t jumpOffset = value - address;
if (!isError && (jumpOffset < -128 || jumpOffset > 127))
--- /dev/null
+++ b/test/asm/ds-@.asm
@@ -1,0 +1,14 @@
+SECTION "test fixed", ROM0[0]
+
+FixedStart:
+ ds 8, (@ - FixedStart) * 2 + zero
+ ds 8, (@ - FixedStart) * 2 + zero
+
+SECTION "test floating", ROM0
+
+FloatingStart:
+ ds 8, (@ - FloatingStart) * 2 + zero
+ ds 8, (@ - FloatingStart) * 2 + zero
+
+SECTION "zero", ROM0[0]
+zero:
binary files /dev/null b/test/asm/ds-@.out.bin differ