From ee20d9010e49bd243e57aa41ccc32bf5da99e0bf Mon Sep 17 00:00:00 2001 From: Rangi Date: Sun, 14 Feb 2021 20:53:44 -0500 Subject: [PATCH] Make `@` relative to the start of a `ds` even at link time Fix #737 --- include/asm/output.h | 2 +- include/asm/section.h | 8 ++--- include/link/section.h | 1 - include/linkdefs.h | 2 -- src/asm/output.c | 17 +++++------ src/asm/parser.y | 65 ++++++++++++++++++++--------------------- src/asm/section.c | 25 ++++++++-------- src/link/object.c | 3 +- src/link/patch.c | 9 ++---- test/asm/ds-@.asm | 14 +++++++++ test/asm/ds-@.err | 0 test/asm/ds-@.out | 0 test/asm/ds-@.out.bin | Bin 0 -> 32 bytes 13 files changed, 74 insertions(+), 72 deletions(-) create mode 100644 test/asm/ds-@.asm create mode 100644 test/asm/ds-@.err create mode 100644 test/asm/ds-@.out create mode 100644 test/asm/ds-@.out.bin diff --git a/include/asm/output.h b/include/asm/output.h index dbd363f3..f5696a6d 100644 --- a/include/asm/output.h +++ b/include/asm/output.h @@ -22,7 +22,7 @@ extern struct Section *pSectionList, *pCurrentSection; 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); diff --git a/include/asm/section.h b/include/asm/section.h index 9f0efd92..96ee481e 100644 --- a/include/asm/section.h +++ b/include/asm/section.h @@ -62,11 +62,11 @@ void out_AbsWordGroup(uint8_t const *s, int32_t length); 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); diff --git a/include/link/section.h b/include/link/section.h index b7cb6eb7..d9da1a5b 100644 --- a/include/link/section.h +++ b/include/link/section.h @@ -34,7 +34,6 @@ struct Patch { uint32_t pcSectionID; uint32_t pcOffset; enum PatchType type; - bool isOperand; int32_t rpnSize; uint8_t *rpnExpression; diff --git a/include/linkdefs.h b/include/linkdefs.h index 2cdb76e0..d21f61f8 100644 --- a/include/linkdefs.h +++ b/include/linkdefs.h @@ -99,8 +99,6 @@ enum ExportLevel { SYMTYPE_EXPORT }; -// Bit 7 is special, not part of the actual patch type -#define PATCH_ISOPERAND 0x80 enum PatchType { PATCHTYPE_BYTE, PATCHTYPE_WORD, diff --git a/src/asm/output.c b/src/asm/output.c index 14c466f3..89cef353 100644 --- a/src/asm/output.c +++ b/src/asm/output.c @@ -39,7 +39,6 @@ struct Patch { 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 uint32_t getSectIDIfAny(struct Section const *sect) 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 @@ static struct Patch *allocpatch(uint32_t type, struct Expression const *expr, ui 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 @@ static struct Patch *allocpatch(uint32_t type, struct Expression const *expr, ui /* * 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; } diff --git a/src/asm/parser.y b/src/asm/parser.y index 4f9dc6b0..c95f6feb 100644 --- a/src/asm/parser.y +++ b/src/asm/parser.y @@ -1122,7 +1122,7 @@ constlist_8bit_entry : %empty { $$ = true; } | reloc_8bit_no_str { - out_RelByte(&$1, false); + out_RelByte(&$1, 0); $$ = false; } | string { @@ -1146,7 +1146,7 @@ constlist_16bit_entry : %empty { $$ = true; } | reloc_16bit_no_str { - out_RelWord(&$1, false); + out_RelWord(&$1, 0); $$ = false; } | string { @@ -1170,7 +1170,7 @@ constlist_32bit_entry : %empty { $$ = true; } | relocexpr_no_str { - out_RelLong(&$1, false); + out_RelLong(&$1, 0); $$ = false; } | string { @@ -1570,27 +1570,27 @@ cpu_command : z80_adc 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); } ; 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); } ; 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_bit : T_Z80_BIT const_3bit T_COMMA reg_r { 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_ccf : T_Z80_CCF { out_AbsByte(0x3F); } 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_inc : T_Z80_INC reg_r { out_AbsByte(0x04 | ($2 << 3)); } 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_jp : T_Z80_JP reloc_16bit { 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 @@ z80_ldio : T_Z80_LDH T_MODE_A T_COMMA op_mem_ind { 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,24 +1723,24 @@ z80_ld : z80_ld_mem 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); } ; 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 @@ z80_ld_mem : T_Z80_LD op_mem_ind T_COMMA T_MODE_SP { rpn_Free(&$2); } else { out_AbsByte(0xEA); - out_RelWord(&$2, true); + out_RelWord(&$2, 1); } } ; @@ -1767,7 +1767,7 @@ z80_ld_rr : T_Z80_LD reg_rr T_COMMA T_MODE_A { 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 @@ z80_ld_a : T_Z80_LD reg_r T_COMMA c_ind { 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_a : T_Z80_LD reg_r T_COMMA c_ind { 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_nop : T_Z80_NOP { out_AbsByte(0x00); } 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_rrca : T_Z80_RRCA { out_AbsByte(0x0F); } 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_rst : T_Z80_RST reloc_8bit { 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 @@ z80_stop : T_Z80_STOP { } | 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_swap : T_Z80_SWAP reg_r { 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); } ; diff --git a/src/asm/section.c b/src/asm/section.c index 44a816f2..da46f7d2 100644 --- a/src/asm/section.c +++ b/src/asm/section.c @@ -488,9 +488,10 @@ static inline void writelong(uint32_t b) 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 @@ void out_String(char const *s) * 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 @@ void out_RelBytes(struct Expression *expr, uint32_t n) 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 @@ void out_RelBytes(struct Expression *expr, uint32_t n) * 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 @@ void out_RelWord(struct Expression *expr, bool isOperand) * 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,14 +692,14 @@ void out_RelLong(struct Expression *expr, bool isOperand) * 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); 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); diff --git a/src/link/object.c b/src/link/object.c index fa5ffe6f..66dca803 100644 --- a/src/link/object.c +++ b/src/link/object.c @@ -286,8 +286,7 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName, cha 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); diff --git a/src/link/patch.c b/src/link/patch.c index 1ded0d8f..05cdf96a 100644 --- a/src/link/patch.c +++ b/src/link/patch.c @@ -421,10 +421,6 @@ static int32_t computeRPNExpr(struct Patch const *patch, 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 @@ static void applyFilePatches(struct Section *section, struct Section *dataSectio /* `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)) diff --git a/test/asm/ds-@.asm b/test/asm/ds-@.asm new file mode 100644 index 00000000..6bbfe365 --- /dev/null +++ b/test/asm/ds-@.asm @@ -0,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: diff --git a/test/asm/ds-@.err b/test/asm/ds-@.err new file mode 100644 index 00000000..e69de29b diff --git a/test/asm/ds-@.out b/test/asm/ds-@.out new file mode 100644 index 00000000..e69de29b diff --git a/test/asm/ds-@.out.bin b/test/asm/ds-@.out.bin new file mode 100644 index 0000000000000000000000000000000000000000..5a0eb06c9925754209db719a5e7ca413ad38b383 GIT binary patch literal 32 RcmZQzfB*p~fN~jNd;kof0RaF2 literal 0 HcmV?d00001