shithub: rgbds

Download patch

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