Fix incorrect PC in LOAD blocks at link time

This commit is contained in:
ISSOtm
2020-04-07 12:23:42 +02:00
parent 5b6c1569a4
commit 927c65e863
10 changed files with 112 additions and 86 deletions

View File

@@ -20,7 +20,6 @@
struct Assertion { struct Assertion {
struct Patch patch; struct Patch patch;
// enum AssertionType type; The `patch`'s field is instead re-used // enum AssertionType type; The `patch`'s field is instead re-used
struct Section *section;
char *message; char *message;
/* /*
* This would be redundant with `.section->fileSymbols`... but * This would be redundant with `.section->fileSymbols`... but

View File

@@ -19,6 +19,8 @@
#include "linkdefs.h" #include "linkdefs.h"
struct Section;
struct AttachedSymbol { struct AttachedSymbol {
struct Symbol *symbol; struct Symbol *symbol;
struct AttachedSymbol *next; struct AttachedSymbol *next;
@@ -27,9 +29,13 @@ struct AttachedSymbol {
struct Patch { struct Patch {
char *fileName; char *fileName;
int32_t offset; int32_t offset;
uint32_t pcSectionID;
uint32_t pcOffset;
enum PatchType type; enum PatchType type;
int32_t rpnSize; int32_t rpnSize;
uint8_t *rpnExpression; uint8_t *rpnExpression;
struct Section const *pcSection;
}; };
struct Section { struct Section {

View File

@@ -14,7 +14,7 @@
#define RGBDS_OBJECT_VERSION_STRING "RGB%1hhu" #define RGBDS_OBJECT_VERSION_STRING "RGB%1hhu"
#define RGBDS_OBJECT_VERSION_NUMBER (uint8_t)9 #define RGBDS_OBJECT_VERSION_NUMBER (uint8_t)9
#define RGBDS_OBJECT_REV 3 #define RGBDS_OBJECT_REV 4
enum AssertionType { enum AssertionType {
ASSERT_WARN, ASSERT_WARN,

View File

@@ -34,6 +34,8 @@
struct Patch { struct Patch {
char tzFilename[_MAX_PATH + 1]; char tzFilename[_MAX_PATH + 1];
uint32_t nOffset; uint32_t nOffset;
struct Section *pcSection;
uint32_t pcOffset;
uint8_t nType; uint8_t nType;
uint32_t nRPNSize; uint32_t nRPNSize;
uint8_t *pRPN; uint8_t *pRPN;
@@ -79,16 +81,13 @@ static uint32_t countsections(void)
/* /*
* Count the number of patches used in this object * 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; uint32_t r = 0;
pPatch = pSect->pPatches; for (struct Patch const *patch = pSect->pPatches; patch != NULL;
while (pPatch) { patch = patch->pNext)
r++; r++;
pPatch = pPatch->pNext;
}
return r; return r;
} }
@@ -132,9 +131,9 @@ static void fputstring(char const *s, FILE *f)
/* /*
* Return a section's ID * 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; uint32_t ID = 0;
sec = pSectionList; sec = pSectionList;
@@ -149,13 +148,20 @@ static uint32_t getsectid(struct Section *pSect)
fatalerror("Unknown section '%s'", pSect->pzName); 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 * 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); fputstring(pPatch->tzFilename, f);
fputlong(pPatch->nOffset, f); fputlong(pPatch->nOffset, f);
fputlong(getSectIDIfAny(pPatch->pcSection), f);
fputlong(pPatch->pcOffset, f);
fputc(pPatch->nType, f); fputc(pPatch->nType, f);
fputlong(pPatch->nRPNSize, f); fputlong(pPatch->nRPNSize, f);
fwrite(pPatch->pRPN, 1, pPatch->nRPNSize, f); fwrite(pPatch->pRPN, 1, pPatch->nRPNSize, f);
@@ -164,7 +170,7 @@ static void writepatch(struct Patch *pPatch, FILE *f)
/* /*
* Write a section to a file * 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); fputstring(pSect->pzName, f);
@@ -177,16 +183,12 @@ static void writesection(struct Section *pSect, FILE *f)
fputlong(pSect->nAlign, f); fputlong(pSect->nAlign, f);
if (sect_HasData(pSect->nType)) { if (sect_HasData(pSect->nType)) {
struct Patch *pPatch;
fwrite(pSect->tData, 1, pSect->size, f); fwrite(pSect->tData, 1, pSect->size, f);
fputlong(countpatches(pSect), f); fputlong(countpatches(pSect), f);
pPatch = pSect->pPatches; for (struct Patch const *patch = pSect->pPatches; patch != NULL;
while (pPatch) { patch = patch->pNext)
writepatch(pPatch, f); writepatch(patch, f);
pPatch = pPatch->pNext;
}
} }
} }
@@ -202,7 +204,7 @@ static void writesymbol(struct sSymbol const *pSym, FILE *f)
fputc(pSym->isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, f); fputc(pSym->isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, f);
fputstring(pSym->tzFileName, f); fputstring(pSym->tzFileName, f);
fputlong(pSym->nFileLine, f); fputlong(pSym->nFileLine, f);
fputlong(pSym->pSection ? getsectid(pSym->pSection) : -1, f); fputlong(getSectIDIfAny(pSym->pSection), f);
fputlong(pSym->nValue, f); fputlong(pSym->nValue, f);
} }
} }
@@ -300,9 +302,7 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
static struct Patch *allocpatch(uint32_t type, struct Expression const *expr, static struct Patch *allocpatch(uint32_t type, struct Expression const *expr,
uint32_t ofs) uint32_t ofs)
{ {
struct Patch *pPatch; struct Patch *pPatch = malloc(sizeof(struct Patch));
pPatch = malloc(sizeof(struct Patch));
if (!pPatch) if (!pPatch)
fatalerror("No memory for patch: %s", strerror(errno)); fatalerror("No memory for patch: %s", strerror(errno));
@@ -316,6 +316,8 @@ static struct Patch *allocpatch(uint32_t type, struct Expression const *expr,
pPatch->nType = type; pPatch->nType = type;
fstk_DumpToStr(pPatch->tzFilename, sizeof(pPatch->tzFilename)); fstk_DumpToStr(pPatch->tzFilename, sizeof(pPatch->tzFilename));
pPatch->nOffset = ofs; pPatch->nOffset = ofs;
pPatch->pcSection = sect_GetSymbolSection();
pPatch->pcOffset = curOffset;
writerpn(pPatch->pRPN, &pPatch->nRPNSize, expr->tRPN, expr->nRPNLength); writerpn(pPatch->pRPN, &pPatch->nRPNSize, expr->tRPN, expr->nRPNLength);
assert(pPatch->nRPNSize == expr->nRPNPatchSize); assert(pPatch->nRPNSize == expr->nRPNPatchSize);
@@ -346,7 +348,6 @@ bool out_CreateAssert(enum AssertionType type, struct Expression const *expr,
return false; return false;
assertion->patch = allocpatch(type, expr, ofs); assertion->patch = allocpatch(type, expr, ofs);
assertion->section = pCurrentSection;
assertion->message = strdup(message); assertion->message = strdup(message);
if (!assertion->message) { if (!assertion->message) {
free(assertion); free(assertion);
@@ -362,7 +363,6 @@ bool out_CreateAssert(enum AssertionType type, struct Expression const *expr,
static void writeassert(struct Assertion *assert, FILE *f) static void writeassert(struct Assertion *assert, FILE *f)
{ {
writepatch(assert->patch, f); writepatch(assert->patch, f);
fputlong(assert->section ? getsectid(assert->section) : -1, f);
fputstring(assert->message, f); fputstring(assert->message, f);
} }
@@ -381,11 +381,8 @@ static void registerExportedSymbol(struct sSymbol *symbol, void *arg)
*/ */
void out_WriteObject(void) void out_WriteObject(void)
{ {
FILE *f; FILE *f = fopen(tzObjectname, "wb");
struct Section *pSect;
struct Assertion *assert = assertions;
f = fopen(tzObjectname, "wb");
if (!f) if (!f)
err(1, "Couldn't write file '%s'", tzObjectname); err(1, "Couldn't write file '%s'", tzObjectname);
@@ -398,21 +395,16 @@ void out_WriteObject(void)
fputlong(nbSymbols, f); fputlong(nbSymbols, f);
fputlong(countsections(), 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); writesymbol(sym, f);
}
pSect = pSectionList; for (struct Section *sect = pSectionList; sect; sect = sect->pNext)
while (pSect) { writesection(sect, f);
writesection(pSect, f);
pSect = pSect->pNext;
}
fputlong(countasserts(), f); fputlong(countasserts(), f);
while (assert) { for (struct Assertion *assert = assertions; assert;
assert = assert->next)
writeassert(assert, f); writeassert(assert, f);
assert = assert->next;
}
fclose(f); fclose(f);
} }

View File

@@ -207,8 +207,9 @@ static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName)
* @param fileName The filename to report in errors * @param fileName The filename to report in errors
* @param i The number of the patch to report in errors * @param i The number of the patch to report in errors
*/ */
static void readPatch(FILE *file, struct Patch *patch, static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
char const *fileName, char const *sectName, uint32_t i) char const *sectName, uint32_t i,
struct Section *fileSections[])
{ {
tryReadstr(patch->fileName, file, tryReadstr(patch->fileName, file,
"%s: Unable to read \"%s\"'s patch #%u's name: %s", "%s: Unable to read \"%s\"'s patch #%u's name: %s",
@@ -216,6 +217,15 @@ static void readPatch(FILE *file, struct Patch *patch,
tryReadlong(patch->offset, file, tryReadlong(patch->offset, file,
"%s: Unable to read \"%s\"'s patch #%u's offset: %s", "%s: Unable to read \"%s\"'s patch #%u's offset: %s",
fileName, sectName, i); 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, tryGetc(patch->type, file,
"%s: Unable to read \"%s\"'s patch #%u's type: %s", "%s: Unable to read \"%s\"'s patch #%u's type: %s",
fileName, sectName, i); fileName, sectName, i);
@@ -242,7 +252,7 @@ static void readPatch(FILE *file, struct Patch *patch,
* @param fileName The filename to report in errors * @param fileName The filename to report in errors
*/ */
static void readSection(FILE *file, struct Section *section, static void readSection(FILE *file, struct Section *section,
char const *fileName) char const *fileName, struct Section *fileSections[])
{ {
int32_t tmp; int32_t tmp;
uint8_t type; uint8_t type;
@@ -302,9 +312,10 @@ static void readSection(FILE *file, struct Section *section,
if (!patches) if (!patches)
err(1, "%s: Unable to read \"%s\"'s patches", fileName, err(1, "%s: Unable to read \"%s\"'s patches", fileName,
section->name); 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, readPatch(file, &patches[i], fileName, section->name,
i); i, fileSections);
}
section->patches = patches; section->patches = patches;
} }
} }
@@ -345,18 +356,14 @@ static void linkSymToSect(struct Symbol const *symbol, struct Section *section)
* @param fileName The filename to report in errors * @param fileName The filename to report in errors
*/ */
static void readAssertion(FILE *file, struct Assertion *assert, static void readAssertion(FILE *file, struct Assertion *assert,
char const *fileName, struct Section *fileSections[], char const *fileName, uint32_t i,
uint32_t i) struct Section *fileSections[])
{ {
char assertName[sizeof("Assertion #" EXPAND_AND_STR(UINT32_MAX))]; char assertName[sizeof("Assertion #" EXPAND_AND_STR(UINT32_MAX))];
uint32_t sectionID;
snprintf(assertName, sizeof(assertName), "Assertion #%u", i); snprintf(assertName, sizeof(assertName), "Assertion #%u", i);
readPatch(file, &assert->patch, fileName, assertName, 0); readPatch(file, &assert->patch, fileName, assertName, 0, fileSections);
tryReadlong(sectionID, file, "%s: Cannot read assertion's section ID: %s",
fileName);
assert->section = sectionID == -1 ? NULL : fileSections[sectionID];
tryReadstr(assert->message, file, "%s: Cannot read assertion's message: %s", tryReadstr(assert->message, file, "%s: Cannot read assertion's message: %s",
fileName); fileName);
} }
@@ -455,26 +462,25 @@ void obj_ReadFile(char const *fileName)
verbosePrint("Reading %u sections...\n", nbSections); verbosePrint("Reading %u sections...\n", nbSections);
for (uint32_t i = 0; i < nbSections; i++) { for (uint32_t i = 0; i < nbSections; i++) {
/* Read section */ /* Read section */
struct Section *section = malloc(sizeof(*section)); fileSections[i] = malloc(sizeof(*fileSections[i]));
if (!fileSections[i])
if (!section)
err(1, "%s: Couldn't create new section", fileName); err(1, "%s: Couldn't create new section", fileName);
section->nextu = NULL;
readSection(file, section, fileName);
section->fileSymbols = fileSymbols;
sect_AddSection(section); fileSections[i]->nextu = NULL;
fileSections[i] = section; readSection(file, fileSections[i], fileName, fileSections);
fileSections[i]->fileSymbols = fileSymbols;
if (nbSymPerSect[i]) { if (nbSymPerSect[i]) {
section->symbols = malloc(sizeof(*section->symbols) fileSections[i]->symbols = malloc(nbSymPerSect[i]
* nbSymPerSect[i]); * sizeof(*fileSections[i]->symbols));
if (!section->symbols) if (!fileSections[i]->symbols)
err(1, "%s: Couldn't link to symbols", err(1, "%s: Couldn't link to symbols",
fileName); fileName);
} else { } 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 */ /* Give symbols pointers to their sections */
@@ -501,7 +507,7 @@ void obj_ReadFile(char const *fileName)
if (!assertion) if (!assertion)
err(1, "%s: Couldn't create new assertion", fileName); 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->fileSymbols = fileSymbols;
assertion->next = assertions; assertion->next = assertions;
assertions = assertion; assertions = assertion;

View File

@@ -136,7 +136,6 @@ static struct Symbol const *getSymbol(struct Symbol const * const *symbolList,
* @return The patch's value * @return The patch's value
*/ */
static int32_t computeRPNExpr(struct Patch const *patch, static int32_t computeRPNExpr(struct Patch const *patch,
struct Section const *section,
struct Symbol const * const *fileSymbols) struct Symbol const * const *fileSymbols)
{ {
/* Small shortcut to avoid a lot of repetition */ /* Small shortcut to avoid a lot of repetition */
@@ -283,7 +282,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
break; break;
case RPN_BANK_SELF: case RPN_BANK_SELF:
value = section->bank; value = patch->pcSection->bank;
break; break;
case RPN_HRAM: case RPN_HRAM:
@@ -322,13 +321,17 @@ static int32_t computeRPNExpr(struct Patch const *patch,
symbol = getSymbol(fileSymbols, value); symbol = getSymbol(fileSymbols, value);
if (!strcmp(symbol->name, "@")) { if (strcmp(symbol->name, "@")) {
value = section->org + patch->offset;
} else {
value = symbol->value; value = symbol->value;
/* Symbols attached to sections have offsets */ /* Symbols attached to sections have offsets */
if (symbol->section) if (symbol->section)
value += symbol->section->org; 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; break;
} }
@@ -351,7 +354,7 @@ void patch_CheckAssertions(struct Assertion *assert)
initRPNStack(); initRPNStack();
while (assert) { while (assert) {
if (!computeRPNExpr(&assert->patch, assert->section, if (!computeRPNExpr(&assert->patch,
(struct Symbol const * const *) (struct Symbol const * const *)
assert->fileSymbols)) { assert->fileSymbols)) {
switch ((enum AssertionType)assert->patch.type) { switch ((enum AssertionType)assert->patch.type) {
@@ -395,14 +398,15 @@ static void applyFilePatches(struct Section *section)
verbosePrint("Patching section \"%s\"...\n", section->name); verbosePrint("Patching section \"%s\"...\n", section->name);
for (uint32_t patchID = 0; patchID < section->nbPatches; patchID++) { for (uint32_t patchID = 0; patchID < section->nbPatches; patchID++) {
struct Patch *patch = &section->patches[patchID]; struct Patch *patch = &section->patches[patchID];
int32_t value = computeRPNExpr(patch, section, int32_t value = computeRPNExpr(patch,
(struct Symbol const * const *) (struct Symbol const * const *)
section->fileSymbols); section->fileSymbols);
/* `jr` is quite unlike the others... */ /* `jr` is quite unlike the others... */
if (patch->type == PATCHTYPE_JR) { if (patch->type == PATCHTYPE_JR) {
/* Target is relative to the byte *after* the operand */ /* 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; int16_t offset = value - address;
if (offset < -128 || offset > 127) if (offset < -128 || offset > 127)

View File

@@ -101,8 +101,6 @@ REPT NumberOfSections
LONG NumberOfPatches ; Number of patches to apply. LONG NumberOfPatches ; Number of patches to apply.
; These types of sections may have patches
REPT NumberOfPatches REPT NumberOfPatches
STRING SourceFile ; Name of the source file (for printing error STRING SourceFile ; Name of the source file (for printing error
@@ -111,6 +109,16 @@ REPT NumberOfSections
LONG Offset ; Offset into the section where patch should LONG Offset ; Offset into the section where patch should
; be applied (in bytes). ; 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. BYTE Type ; 0 = BYTE patch.
; 1 = little endian WORD patch. ; 1 = little endian WORD patch.
; 2 = little endian LONG patch. ; 2 = little endian LONG patch.
@@ -137,6 +145,13 @@ REPT NumberOfAssertions
LONG Offset ; Offset into the section where the assertion is located. 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 BYTE Type ; 0 = Prints the message but allows linking to continue
; 1 = Prints the message and evaluates other assertions, ; 1 = Prints the message and evaluates other assertions,
; but linking fails afterwards ; but linking fails afterwards
@@ -146,10 +161,6 @@ REPT NumberOfAssertions
BYTE RPN[RPNSize] ; RPN expression, same as patches. Assert fails if == 0. 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 STRING Message ; A message displayed when the assert fails. If set to
; the empty string, a generic message is printed instead. ; the empty string, a generic message is printed instead.

View File

@@ -1,8 +1,14 @@
SECTION "test", ROM0[1] SECTION "test", ROM0[1]
call Target 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 Target: dl DEAD << 16 | BEEF
db BANK(@)
jr .end
.end .end
jr .end
ds 2, $2A ds 2, $2A
ENDL ENDL
After: After:
@@ -10,16 +16,16 @@ After:
ld hl, Word ld hl, Word
dw Byte, Target.end, After dw Byte, Target.end, After
SECTION "dead", WRAMX[$DEAD] SECTION "dead", WRAMX[$DEAD],BANK[2]
DEAD: DEAD:
SECTION "beef", SRAM[$BEEF] SECTION "beef", SRAM[$BEEF]
BEEF: BEEF:
SECTION "ram test", WRAM0 ; Should end up at $C005 SECTION "ram test", WRAMX,BANK[1] ; Should end up at $D005
Word: Word:
dw dw
SECTION "small ram test", WRAM0 ; Should end up at $C000 SECTION "small ram test", WRAMX,BANK[1] ; Should end up at $D000
Byte: Byte:
db db

View File

@@ -1,3 +1,5 @@
$C001 PC in ROM: $4
$C005 PC in WRAM: $D001
$A $D001
$D008
$F

Binary file not shown.