diff --git a/include/link/patch.h b/include/link/patch.h index ab1e2f5e..3e1e1012 100644 --- 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 diff --git a/include/link/section.h b/include/link/section.h index f8b28047..90525220 100644 --- 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 AttachedSymbol { 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 { diff --git a/include/linkdefs.h b/include/linkdefs.h index 76d89d57..6aefbbca 100644 --- 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, diff --git a/src/asm/output.c b/src/asm/output.c index a33bdd48..33e944ba 100644 --- 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 @@ static uint32_t countsections(void) /* * 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 @@ static void fputstring(char const *s, FILE *f) /* * 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 @@ static uint32_t getsectid(struct Section *pSect) 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 @@ static void writepatch(struct Patch *pPatch, FILE *f) /* * 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 @@ static void writesection(struct Section *pSect, FILE *f) 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 @@ static void writesymbol(struct sSymbol const *pSym, FILE *f) 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,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, uint32_t ofs) { - struct Patch *pPatch; - - pPatch = malloc(sizeof(struct Patch)); + struct Patch *pPatch = malloc(sizeof(struct Patch)); if (!pPatch) 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; 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 @@ bool out_CreateAssert(enum AssertionType type, struct Expression const *expr, return false; assertion->patch = allocpatch(type, expr, ofs); - assertion->section = pCurrentSection; assertion->message = strdup(message); if (!assertion->message) { free(assertion); @@ -362,7 +363,6 @@ bool out_CreateAssert(enum AssertionType type, struct Expression const *expr, 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 @@ static void registerExportedSymbol(struct sSymbol *symbol, void *arg) */ 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 @@ void out_WriteObject(void) 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); } diff --git a/src/link/object.c b/src/link/object.c index e75b2e37..437aeca2 100644 --- a/src/link/object.c +++ b/src/link/object.c @@ -207,8 +207,9 @@ static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName) * @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 @@ static void readPatch(FILE *file, struct Patch *patch, 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 @@ static void readPatch(FILE *file, struct Patch *patch, * @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 @@ static void readSection(FILE *file, struct Section *section, 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 @@ static void linkSymToSect(struct Symbol const *symbol, struct Section *section) * @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 @@ void obj_ReadFile(char const *fileName) 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 @@ void obj_ReadFile(char const *fileName) 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; diff --git a/src/link/patch.c b/src/link/patch.c index ef610d94..50733952 100644 --- a/src/link/patch.c +++ b/src/link/patch.c @@ -136,7 +136,6 @@ static struct Symbol const *getSymbol(struct Symbol const * const *symbolList, * @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 @@ static int32_t computeRPNExpr(struct Patch const *patch, break; case RPN_BANK_SELF: - value = section->bank; + value = patch->pcSection->bank; break; case RPN_HRAM: @@ -322,13 +321,17 @@ static int32_t computeRPNExpr(struct Patch const *patch, 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 @@ void patch_CheckAssertions(struct Assertion *assert) 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,14 +398,15 @@ static void applyFilePatches(struct Section *section) verbosePrint("Patching section \"%s\"...\n", section->name); for (uint32_t patchID = 0; patchID < section->nbPatches; patchID++) { struct Patch *patch = §ion->patches[patchID]; - int32_t value = computeRPNExpr(patch, section, + int32_t value = computeRPNExpr(patch, (struct Symbol const * const *) section->fileSymbols); /* `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) diff --git a/src/rgbds.5 b/src/rgbds.5 index 3bd2b3c8..a5479c56 100644 --- a/src/rgbds.5 +++ b/src/rgbds.5 @@ -101,8 +101,6 @@ REPT NumberOfSections 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 @@ REPT NumberOfSections 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 @@ REPT NumberOfAssertions 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 @@ -146,10 +161,6 @@ REPT NumberOfAssertions 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. diff --git a/test/asm/ram-code.asm b/test/asm/ram-code.asm index 5d47fb1d..721eb600 100644 --- 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 @@ After: 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 diff --git a/test/asm/ram-code.out b/test/asm/ram-code.out index 22626262..7aad84db 100644 --- 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 diff --git a/test/asm/ram-code.out.bin b/test/asm/ram-code.out.bin index 2494277f..e69de29b 100644 Binary files a/test/asm/ram-code.out.bin and b/test/asm/ram-code.out.bin differ