shithub: rgbds

Download patch

ref: 927c65e863a109ec81376abff02db10afbcc9543
parent: 5b6c1569a4e8835f1e3225b90c8c19203a99bc6b
author: ISSOtm <[email protected]>
date: Tue Apr 7 08:23:42 EDT 2020

Fix incorrect PC in LOAD blocks at link time

--- a/include/link/patch.h
+++ b/include/link/patch.h
@@ -20,7 +20,6 @@
 struct Assertion {
 	struct Patch patch;
 	// enum AssertionType type; The `patch`'s field is instead re-used
-	struct Section *section;
 	char *message;
 	/*
 	 * This would be redundant with `.section->fileSymbols`... but
--- a/include/link/section.h
+++ b/include/link/section.h
@@ -19,6 +19,8 @@
 
 #include "linkdefs.h"
 
+struct Section;
+
 struct AttachedSymbol {
 	struct Symbol *symbol;
 	struct AttachedSymbol *next;
@@ -27,9 +29,13 @@
 struct Patch {
 	char *fileName;
 	int32_t offset;
+	uint32_t pcSectionID;
+	uint32_t pcOffset;
 	enum PatchType type;
 	int32_t rpnSize;
 	uint8_t *rpnExpression;
+
+	struct Section const *pcSection;
 };
 
 struct Section {
--- a/include/linkdefs.h
+++ b/include/linkdefs.h
@@ -14,7 +14,7 @@
 
 #define RGBDS_OBJECT_VERSION_STRING "RGB%1hhu"
 #define RGBDS_OBJECT_VERSION_NUMBER (uint8_t)9
-#define RGBDS_OBJECT_REV 3
+#define RGBDS_OBJECT_REV 4
 
 enum AssertionType {
 	ASSERT_WARN,
--- a/src/asm/output.c
+++ b/src/asm/output.c
@@ -34,6 +34,8 @@
 struct Patch {
 	char tzFilename[_MAX_PATH + 1];
 	uint32_t nOffset;
+	struct Section *pcSection;
+	uint32_t pcOffset;
 	uint8_t nType;
 	uint32_t nRPNSize;
 	uint8_t *pRPN;
@@ -79,16 +81,13 @@
 /*
  * Count the number of patches used in this object
  */
-static uint32_t countpatches(struct Section *pSect)
+static uint32_t countpatches(struct Section const *pSect)
 {
-	struct Patch *pPatch;
 	uint32_t r = 0;
 
-	pPatch = pSect->pPatches;
-	while (pPatch) {
+	for (struct Patch const *patch = pSect->pPatches; patch != NULL;
+	     patch = patch->pNext)
 		r++;
-		pPatch = pPatch->pNext;
-	}
 
 	return r;
 }
@@ -132,9 +131,9 @@
 /*
  * Return a section's ID
  */
-static uint32_t getsectid(struct Section *pSect)
+static uint32_t getsectid(struct Section const *pSect)
 {
-	struct Section *sec;
+	struct Section const *sec;
 	uint32_t ID = 0;
 
 	sec = pSectionList;
@@ -149,13 +148,20 @@
 	fatalerror("Unknown section '%s'", pSect->pzName);
 }
 
+static uint32_t getSectIDIfAny(struct Section const *sect)
+{
+	return sect ? getsectid(sect) : -1;
+}
+
 /*
  * Write a patch to a file
  */
-static void writepatch(struct Patch *pPatch, FILE *f)
+static void writepatch(struct Patch const *pPatch, FILE *f)
 {
 	fputstring(pPatch->tzFilename, f);
 	fputlong(pPatch->nOffset, f);
+	fputlong(getSectIDIfAny(pPatch->pcSection), f);
+	fputlong(pPatch->pcOffset, f);
 	fputc(pPatch->nType, f);
 	fputlong(pPatch->nRPNSize, f);
 	fwrite(pPatch->pRPN, 1, pPatch->nRPNSize, f);
@@ -164,7 +170,7 @@
 /*
  * Write a section to a file
  */
-static void writesection(struct Section *pSect, FILE *f)
+static void writesection(struct Section const *pSect, FILE *f)
 {
 	fputstring(pSect->pzName, f);
 
@@ -177,16 +183,12 @@
 	fputlong(pSect->nAlign, f);
 
 	if (sect_HasData(pSect->nType)) {
-		struct Patch *pPatch;
-
 		fwrite(pSect->tData, 1, pSect->size, f);
 		fputlong(countpatches(pSect), f);
 
-		pPatch = pSect->pPatches;
-		while (pPatch) {
-			writepatch(pPatch, f);
-			pPatch = pPatch->pNext;
-		}
+		for (struct Patch const *patch = pSect->pPatches; patch != NULL;
+		     patch = patch->pNext)
+			writepatch(patch, f);
 	}
 }
 
@@ -202,7 +204,7 @@
 		fputc(pSym->isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, f);
 		fputstring(pSym->tzFileName, f);
 		fputlong(pSym->nFileLine, f);
-		fputlong(pSym->pSection ? getsectid(pSym->pSection) : -1, f);
+		fputlong(getSectIDIfAny(pSym->pSection), f);
 		fputlong(pSym->nValue, f);
 	}
 }
@@ -300,10 +302,8 @@
 static struct Patch *allocpatch(uint32_t type, struct Expression const *expr,
 				uint32_t ofs)
 {
-	struct Patch *pPatch;
+	struct Patch *pPatch = malloc(sizeof(struct Patch));
 
-	pPatch = malloc(sizeof(struct Patch));
-
 	if (!pPatch)
 		fatalerror("No memory for patch: %s", strerror(errno));
 	pPatch->pRPN = malloc(sizeof(*pPatch->pRPN) * expr->nRPNPatchSize);
@@ -316,6 +316,8 @@
 	pPatch->nType = type;
 	fstk_DumpToStr(pPatch->tzFilename, sizeof(pPatch->tzFilename));
 	pPatch->nOffset = ofs;
+	pPatch->pcSection = sect_GetSymbolSection();
+	pPatch->pcOffset = curOffset;
 
 	writerpn(pPatch->pRPN, &pPatch->nRPNSize, expr->tRPN, expr->nRPNLength);
 	assert(pPatch->nRPNSize == expr->nRPNPatchSize);
@@ -346,7 +348,6 @@
 		return false;
 
 	assertion->patch = allocpatch(type, expr, ofs);
-	assertion->section = pCurrentSection;
 	assertion->message = strdup(message);
 	if (!assertion->message) {
 		free(assertion);
@@ -362,7 +363,6 @@
 static void writeassert(struct Assertion *assert, FILE *f)
 {
 	writepatch(assert->patch, f);
-	fputlong(assert->section ? getsectid(assert->section) : -1, f);
 	fputstring(assert->message, f);
 }
 
@@ -381,11 +381,8 @@
  */
 void out_WriteObject(void)
 {
-	FILE *f;
-	struct Section *pSect;
-	struct Assertion *assert = assertions;
+	FILE *f = fopen(tzObjectname, "wb");
 
-	f = fopen(tzObjectname, "wb");
 	if (!f)
 		err(1, "Couldn't write file '%s'", tzObjectname);
 
@@ -398,21 +395,16 @@
 	fputlong(nbSymbols, f);
 	fputlong(countsections(), f);
 
-	for (struct sSymbol const *sym = objectSymbols; sym; sym = sym->next) {
+	for (struct sSymbol const *sym = objectSymbols; sym; sym = sym->next)
 		writesymbol(sym, f);
-	}
 
-	pSect = pSectionList;
-	while (pSect) {
-		writesection(pSect, f);
-		pSect = pSect->pNext;
-	}
+	for (struct Section *sect = pSectionList; sect; sect = sect->pNext)
+		writesection(sect, f);
 
 	fputlong(countasserts(), f);
-	while (assert) {
+	for (struct Assertion *assert = assertions; assert;
+	     assert = assert->next)
 		writeassert(assert, f);
-		assert = assert->next;
-	}
 
 	fclose(f);
 }
--- a/src/link/object.c
+++ b/src/link/object.c
@@ -207,8 +207,9 @@
  * @param fileName The filename to report in errors
  * @param i The number of the patch to report in errors
  */
-static void readPatch(FILE *file, struct Patch *patch,
-		      char const *fileName, char const *sectName, uint32_t i)
+static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
+		      char const *sectName, uint32_t i,
+		      struct Section *fileSections[])
 {
 	tryReadstr(patch->fileName, file,
 		   "%s: Unable to read \"%s\"'s patch #%u's name: %s",
@@ -216,6 +217,15 @@
 	tryReadlong(patch->offset, file,
 		    "%s: Unable to read \"%s\"'s patch #%u's offset: %s",
 		    fileName, sectName, i);
+	tryReadlong(patch->pcSectionID, file,
+		    "%s: Unable to read \"%s\"'s patch #%u's PC offset: %s",
+		    fileName, sectName, i);
+	patch->pcSection = patch->pcSectionID == -1
+					? NULL
+					: fileSections[patch->pcSectionID];
+	tryReadlong(patch->pcOffset, file,
+		    "%s: Unable to read \"%s\"'s patch #%u's PC offset: %s",
+		    fileName, sectName, i);
 	tryGetc(patch->type, file,
 		"%s: Unable to read \"%s\"'s patch #%u's type: %s",
 		fileName, sectName, i);
@@ -242,7 +252,7 @@
  * @param fileName The filename to report in errors
  */
 static void readSection(FILE *file, struct Section *section,
-			char const *fileName)
+			char const *fileName, struct Section *fileSections[])
 {
 	int32_t tmp;
 	uint8_t type;
@@ -302,9 +312,10 @@
 		if (!patches)
 			err(1, "%s: Unable to read \"%s\"'s patches", fileName,
 			    section->name);
-		for (uint32_t i = 0; i < section->nbPatches; i++)
+		for (uint32_t i = 0; i < section->nbPatches; i++) {
 			readPatch(file, &patches[i], fileName, section->name,
-				  i);
+				  i, fileSections);
+		}
 		section->patches = patches;
 	}
 }
@@ -345,18 +356,14 @@
  * @param fileName The filename to report in errors
  */
 static void readAssertion(FILE *file, struct Assertion *assert,
-			  char const *fileName, struct Section *fileSections[],
-			  uint32_t i)
+			  char const *fileName, uint32_t i,
+			  struct Section *fileSections[])
 {
 	char assertName[sizeof("Assertion #" EXPAND_AND_STR(UINT32_MAX))];
-	uint32_t sectionID;
 
 	snprintf(assertName, sizeof(assertName), "Assertion #%u", i);
 
-	readPatch(file, &assert->patch, fileName, assertName, 0);
-	tryReadlong(sectionID, file, "%s: Cannot read assertion's section ID: %s",
-		    fileName);
-	assert->section = sectionID == -1 ? NULL : fileSections[sectionID];
+	readPatch(file, &assert->patch, fileName, assertName, 0, fileSections);
 	tryReadstr(assert->message, file, "%s: Cannot read assertion's message: %s",
 		   fileName);
 }
@@ -455,26 +462,25 @@
 	verbosePrint("Reading %u sections...\n", nbSections);
 	for (uint32_t i = 0; i < nbSections; i++) {
 		/* Read section */
-		struct Section *section = malloc(sizeof(*section));
-
-		if (!section)
+		fileSections[i] = malloc(sizeof(*fileSections[i]));
+		if (!fileSections[i])
 			err(1, "%s: Couldn't create new section", fileName);
-		section->nextu = NULL;
-		readSection(file, section, fileName);
-		section->fileSymbols = fileSymbols;
 
-		sect_AddSection(section);
-		fileSections[i] = section;
+		fileSections[i]->nextu = NULL;
+		readSection(file, fileSections[i], fileName, fileSections);
+		fileSections[i]->fileSymbols = fileSymbols;
 		if (nbSymPerSect[i]) {
-			section->symbols = malloc(sizeof(*section->symbols)
-							* nbSymPerSect[i]);
-			if (!section->symbols)
+			fileSections[i]->symbols = malloc(nbSymPerSect[i]
+					* sizeof(*fileSections[i]->symbols));
+			if (!fileSections[i]->symbols)
 				err(1, "%s: Couldn't link to symbols",
 				    fileName);
 		} else {
-			section->symbols = NULL;
+			fileSections[i]->symbols = NULL;
 		}
-		section->nbSymbols = 0;
+		fileSections[i]->nbSymbols = 0;
+
+		sect_AddSection(fileSections[i]);
 	}
 
 	/* Give symbols pointers to their sections */
@@ -501,7 +507,7 @@
 
 		if (!assertion)
 			err(1, "%s: Couldn't create new assertion", fileName);
-		readAssertion(file, assertion, fileName, fileSections, i);
+		readAssertion(file, assertion, fileName, i, fileSections);
 		assertion->fileSymbols = fileSymbols;
 		assertion->next = assertions;
 		assertions = assertion;
--- a/src/link/patch.c
+++ b/src/link/patch.c
@@ -136,7 +136,6 @@
  * @return The patch's value
  */
 static int32_t computeRPNExpr(struct Patch const *patch,
-			      struct Section const *section,
 			      struct Symbol const * const *fileSymbols)
 {
 /* Small shortcut to avoid a lot of repetition */
@@ -283,7 +282,7 @@
 			break;
 
 		case RPN_BANK_SELF:
-			value = section->bank;
+			value = patch->pcSection->bank;
 			break;
 
 		case RPN_HRAM:
@@ -322,13 +321,17 @@
 
 			symbol = getSymbol(fileSymbols, value);
 
-			if (!strcmp(symbol->name, "@")) {
-				value = section->org + patch->offset;
-			} else {
+			if (strcmp(symbol->name, "@")) {
 				value = symbol->value;
 				/* Symbols attached to sections have offsets */
 				if (symbol->section)
 					value += symbol->section->org;
+			} else if (!patch->pcSection) {
+				error("%s: PC has no value outside a section",
+				      patch->fileName);
+				value = 0;
+			} else {
+				value = patch->pcOffset + patch->pcSection->org;
 			}
 			break;
 		}
@@ -351,7 +354,7 @@
 	initRPNStack();
 
 	while (assert) {
-		if (!computeRPNExpr(&assert->patch, assert->section,
+		if (!computeRPNExpr(&assert->patch,
 				    (struct Symbol const * const *)
 				    			assert->fileSymbols)) {
 			switch ((enum AssertionType)assert->patch.type) {
@@ -395,7 +398,7 @@
 	verbosePrint("Patching section \"%s\"...\n", section->name);
 	for (uint32_t patchID = 0; patchID < section->nbPatches; patchID++) {
 		struct Patch *patch = &section->patches[patchID];
-		int32_t value = computeRPNExpr(patch, section,
+		int32_t value = computeRPNExpr(patch,
 					       (struct Symbol const * const *)
 							section->fileSymbols);
 
@@ -402,7 +405,8 @@
 		/* `jr` is quite unlike the others... */
 		if (patch->type == PATCHTYPE_JR) {
 			/* Target is relative to the byte *after* the operand */
-			uint16_t address = section->org + patch->offset + 1;
+			uint16_t address = patch->pcSection->org
+							+ patch->pcOffset + 1;
 			int16_t offset = value - address;
 
 			if (offset < -128 || offset > 127)
--- a/src/rgbds.5
+++ b/src/rgbds.5
@@ -101,8 +101,6 @@
 
         LONG    NumberOfPatches ; Number of patches to apply.
 
-        ; These types of sections may have patches
-
         REPT    NumberOfPatches
 
             STRING  SourceFile   ; Name of the source file (for printing error
@@ -111,6 +109,16 @@
             LONG    Offset       ; Offset into the section where patch should
                                  ; be applied (in bytes).
 
+            LONG    PCSectionID  ; Index within the file of the section in which
+                                 ; PC is located.
+                                 ; This is usually the same section that the
+                                 ; patch should be applied into, except e.g.
+                                 ; with LOAD blocks.
+
+            LONG    PCOffset     ; PC's offset into the above section.
+                                 ; Used because the section may be floating, so
+                                 ; PC's value is not known to RGBASM.
+
             BYTE    Type         ; 0 = BYTE patch.
                                  ; 1 = little endian WORD patch.
                                  ; 2 = little endian LONG patch.
@@ -137,6 +145,13 @@
 
   LONG    Offset       ; Offset into the section where the assertion is located.
 
+  LONG    SectionID    ; Index within the file of the section in which PC is
+                       ; located, or -1 if defined outside a section.
+
+  LONG    PCOffset     ; PC's offset into the above section.
+                       ; Used because the section may be floating, so PC's value
+                       ; is not known to RGBASM.
+
   BYTE    Type         ; 0 = Prints the message but allows linking to continue
                        ; 1 = Prints the message and evaluates other assertions,
                        ;     but linking fails afterwards
@@ -145,10 +160,6 @@
   LONG    RPNSize      ; Size of the RPN expression's buffer.
 
   BYTE    RPN[RPNSize] ; RPN expression, same as patches. Assert fails if == 0.
-
-  LONG    SectionID    ; The section number (of this object file) in which this
-                       ; assert is defined. If it doesn't belong to any specific
-                       ; section (like a constant), this field has the value -1.
 
   STRING  Message      ; A message displayed when the assert fails. If set to
                        ; the empty string, a generic message is printed instead.
--- a/test/asm/ram-code.asm
+++ b/test/asm/ram-code.asm
@@ -1,8 +1,14 @@
 SECTION "test", ROM0[1]
 	call Target
-	LOAD "new", WRAM0[$C001]
+	PRINTT "PC in ROM: {@}\n"
+	LOAD "new", WRAMX[$D001],BANK[1]
+	PRINTT "PC in WRAM: {@}\n"
+	assert @ == $D001
 Target:	dl DEAD << 16 | BEEF
+	db BANK(@)
+	jr .end
 .end
+	jr .end
 	ds 2, $2A
 	ENDL
 After:
@@ -10,16 +16,16 @@
 	ld hl, Word
 	dw Byte, Target.end, After
 
-SECTION "dead", WRAMX[$DEAD]
+SECTION "dead", WRAMX[$DEAD],BANK[2]
 DEAD:
 SECTION "beef", SRAM[$BEEF]
 BEEF:
 
-SECTION "ram test", WRAM0 ; Should end up at $C005
+SECTION "ram test", WRAMX,BANK[1] ; Should end up at $D005
 Word:
 	dw
 
-SECTION "small ram test", WRAM0 ; Should end up at $C000
+SECTION "small ram test", WRAMX,BANK[1] ; Should end up at $D000
 Byte:
 	db
 
--- a/test/asm/ram-code.out
+++ b/test/asm/ram-code.out
@@ -1,3 +1,5 @@
-$C001
-$C005
-$A
+PC in ROM: $4
+PC in WRAM: $D001
+$D001
+$D008
+$F
binary files a/test/asm/ram-code.out.bin b/test/asm/ram-code.out.bin differ