From cb52ae0f26462f3069f0fec0643dd98fd0929154 Mon Sep 17 00:00:00 2001 From: ISSOtm Date: Fri, 20 Mar 2020 21:02:32 +0100 Subject: [PATCH 1/5] Implement unionized sections in RGBASM This touched a lot more code than initially expected, for two reasons. First, this broke a big RGBASM assumption: that sections are always being written to at their end. This plus other problems required touching basically the entirety of `section.c`. Second, I tried different solutions to solve the above problem, and along the way I cleaned up many things around. (I believe that keeping this to "cleanup" commits yields subpar results, and since it's boring they get postponed anyways.) RGBLINK support still needs to be added, but this will come next. --- include/asm/asm.h | 1 - include/asm/main.h | 2 + include/asm/output.h | 5 +- include/asm/section.h | 16 +- include/asm/symbol.h | 7 +- src/asm/asmy.y | 26 +-- src/asm/main.c | 7 +- src/asm/output.c | 17 +- src/asm/section.c | 339 ++++++++++++++++++++++++------------- src/asm/symbol.c | 8 +- test/asm/section-union.asm | 37 ++++ test/asm/section-union.err | 8 + test/asm/section-union.out | 3 + 13 files changed, 314 insertions(+), 162 deletions(-) create mode 100644 test/asm/section-union.asm create mode 100644 test/asm/section-union.err create mode 100644 test/asm/section-union.out diff --git a/include/asm/asm.h b/include/asm/asm.h index 5571a7a6..5f1bc4ef 100644 --- a/include/asm/asm.h +++ b/include/asm/asm.h @@ -27,7 +27,6 @@ extern int32_t nLineNo; extern uint32_t nTotalLines; -extern uint32_t nPC; extern uint32_t nIFDepth; extern bool skipElif; extern uint32_t nUnionDepth; diff --git a/include/asm/main.h b/include/asm/main.h index bc057906..bf396ddd 100644 --- a/include/asm/main.h +++ b/include/asm/main.h @@ -31,6 +31,8 @@ extern uint32_t ulNewMacroSize; extern int32_t nGBGfxID; extern int32_t nBinaryID; +extern uint32_t curOffset; /* Offset into the current section */ + extern struct sOptions DefaultOptions; extern struct sOptions CurrentOptions; diff --git a/include/asm/output.h b/include/asm/output.h index 7be1f43e..e6a45df7 100644 --- a/include/asm/output.h +++ b/include/asm/output.h @@ -19,9 +19,10 @@ extern char *tzObjectname; extern struct Section *pSectionList, *pCurrentSection; void out_SetFileName(char *s); -void out_CreatePatch(uint32_t type, struct Expression const *expr); +void out_CreatePatch(uint32_t type, struct Expression const *expr, + uint32_t ofs); bool out_CreateAssert(enum AssertionType type, struct Expression const *expr, - char const *message); + char const *message, uint32_t ofs); void out_WriteObject(void); #endif /* RGBDS_ASM_OUTPUT_H */ diff --git a/include/asm/section.h b/include/asm/section.h index 8fdefaa8..a0fd4ff6 100644 --- a/include/asm/section.h +++ b/include/asm/section.h @@ -18,7 +18,8 @@ struct Expression; struct Section { char *pzName; enum SectionType nType; - uint32_t nPC; + bool isUnion; + uint32_t size; uint32_t nOrg; uint32_t nBank; uint32_t nAlign; @@ -28,25 +29,26 @@ struct Section { }; struct SectionSpec { - int32_t bank; - int32_t alignment; + uint32_t bank; + uint32_t alignment; }; struct Section *out_FindSectionByName(const char *pzName); -void out_NewSection(char const *pzName, uint32_t secttype, int32_t org, - struct SectionSpec const *attributes); -void out_SetLoadSection(char const *name, uint32_t secttype, int32_t org, +void out_NewSection(char const *pzName, uint32_t secttype, uint32_t org, + struct SectionSpec const *attributes, bool isUnion); +void out_SetLoadSection(char const *name, uint32_t secttype, uint32_t org, struct SectionSpec const *attributes); void out_EndLoadSection(void); struct Section *sect_GetSymbolSection(void); +uint32_t sect_GetOutputOffset(void); void out_AbsByte(uint8_t b); void out_AbsByteGroup(uint8_t const *s, int32_t length); void out_Skip(int32_t skip); void out_String(char const *s); void out_RelByte(struct Expression *expr); -void out_RelBytes(struct Expression *expr, int32_t n); +void out_RelBytes(struct Expression *expr, uint32_t n); void out_RelWord(struct Expression *expr); void out_RelLong(struct Expression *expr); void out_PCRelByte(struct Expression *expr); diff --git a/include/asm/symbol.h b/include/asm/symbol.h index 16a37ecb..490786ce 100644 --- a/include/asm/symbol.h +++ b/include/asm/symbol.h @@ -13,6 +13,8 @@ #include #include +#include "asm/section.h" + #include "types.h" #define HASHSIZE (1 << 16) @@ -30,7 +32,6 @@ enum SymbolType { struct sSymbol { char tzName[MAXSYMLEN + 1]; enum SymbolType type; - bool isConstant; /* Whether the symbol's value is currently known */ bool isExported; /* Whether the symbol is to be exported */ bool isBuiltin; /* Whether the symbol is a built-in */ bool isReferenced; /* Whether the symbol is referenced in a RPN expr */ @@ -52,7 +53,9 @@ static inline bool sym_IsDefined(struct sSymbol const *sym) static inline bool sym_IsConstant(struct sSymbol const *sym) { - return sym->isConstant; + return sym->type == SYM_EQU || sym->type == SYM_SET + || (sym->type == SYM_LABEL && sym->pSection + && sym->pSection->nOrg != -1); } static inline bool sym_IsNumeric(struct sSymbol const *sym) diff --git a/src/asm/asmy.y b/src/asm/asmy.y index 3200ffd0..e0eaeae6 100644 --- a/src/asm/asmy.y +++ b/src/asm/asmy.y @@ -393,20 +393,19 @@ static void startUnion(void) if (nUnionDepth > MAXUNIONS) fatalerror("Too many nested UNIONs"); - unionStart[unionIndex] = nPC; + unionStart[unionIndex] = curOffset; unionSize[unionIndex] = 0; } static void updateUnion(void) { uint32_t unionIndex = nUnionDepth - 1; - uint32_t size = nPC - unionStart[unionIndex]; + uint32_t size = curOffset - unionStart[unionIndex]; if (size > unionSize[unionIndex]) unionSize[unionIndex] = size; - nPC = unionStart[unionIndex]; - pCurrentSection->nPC = unionStart[unionIndex]; + curOffset = unionStart[unionIndex]; } static size_t strlenUTF8(const char *s) @@ -601,7 +600,8 @@ static void strsubUTF8(char *dest, const char *src, uint32_t pos, uint32_t len) %token T_SECT_WRAM0 T_SECT_VRAM T_SECT_ROMX T_SECT_ROM0 T_SECT_HRAM %token T_SECT_WRAMX T_SECT_SRAM T_SECT_OAM -%type macroargs; +%type sectunion +%type macroargs %token T_Z80_ADC T_Z80_ADD T_Z80_AND %token T_Z80_BIT @@ -815,7 +815,8 @@ assert_type : /* empty */ { $$ = ASSERT_ERROR; } assert : T_POP_ASSERT assert_type relocexpr { if (!rpn_isKnown(&$3)) { - if (!out_CreateAssert($2, &$3, "")) + if (!out_CreateAssert($2, &$3, "", + sect_GetOutputOffset())) yyerror("Assertion creation failed: %s", strerror(errno)); } else if ($3.nVal == 0) { @@ -836,7 +837,8 @@ assert : T_POP_ASSERT assert_type relocexpr | T_POP_ASSERT assert_type relocexpr ',' string { if (!rpn_isKnown(&$3)) { - if (!out_CreateAssert($2, &$3, $5)) + if (!out_CreateAssert($2, &$3, $5, + sect_GetOutputOffset())) yyerror("Assertion creation failed: %s", strerror(errno)); } else if ($3.nVal == 0) { @@ -968,8 +970,7 @@ endu : T_POP_ENDU { updateUnion(); nUnionDepth--; - nPC = unionStart[nUnionDepth] + unionSize[nUnionDepth]; - pCurrentSection->nPC = nPC; + curOffset = unionStart[nUnionDepth] + unionSize[nUnionDepth]; } ; @@ -1418,11 +1419,14 @@ string : T_STRING { } ; -section : T_POP_SECTION string ',' sectiontype sectorg sectattrs { - out_NewSection($2, $4, $5, &$6); +section : T_POP_SECTION sectunion string ',' sectiontype sectorg sectattrs { + out_NewSection($3, $5, $6, &$7, $2); } ; +sectunion : /* empty */ { $$ = false; } + | T_POP_UNION { $$ = true; } + sectiontype : T_SECT_WRAM0 { $$ = SECTTYPE_WRAM0; } | T_SECT_VRAM { $$ = SECTTYPE_VRAM; } | T_SECT_ROMX { $$ = SECTTYPE_ROMX; } diff --git a/src/asm/main.c b/src/asm/main.c index 5426a1f4..4b94e5f2 100644 --- a/src/asm/main.c +++ b/src/asm/main.c @@ -40,11 +40,13 @@ const size_t cldefine_entrysize = 2 * sizeof(void *); char **cldefines; clock_t nStartClock, nEndClock; -int32_t nLineNo; -uint32_t nTotalLines, nPC, nIFDepth, nUnionDepth; +uint32_t nTotalLines, nIFDepth, nUnionDepth; bool skipElif; uint32_t unionStart[128], unionSize[128]; +int32_t nLineNo; +uint32_t curOffset; + #if defined(YYDEBUG) && YYDEBUG extern int yydebug; #endif @@ -525,7 +527,6 @@ int main(int argc, char *argv[]) nIFDepth = 0; skipElif = true; nUnionDepth = 0; - nPC = 0; sym_Init(); sym_SetExportAll(CurrentOptions.exportall); fstk_Init(tzMainfile); diff --git a/src/asm/output.c b/src/asm/output.c index 5faa849a..3c742aa0 100644 --- a/src/asm/output.c +++ b/src/asm/output.c @@ -187,7 +187,7 @@ static void writesection(struct Section *pSect, FILE *f) { fputstring(pSect->pzName, f); - fputlong(pSect->nPC, f); + fputlong(pSect->size, f); fputc(pSect->nType, f); @@ -198,7 +198,7 @@ static void writesection(struct Section *pSect, FILE *f) if (sect_HasData(pSect->nType)) { struct Patch *pPatch; - fwrite(pSect->tData, 1, pSect->nPC, f); + fwrite(pSect->tData, 1, pSect->size, f); fputlong(countpatches(pSect), f); pPatch = pSect->pPatches; @@ -402,7 +402,8 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn, /* * Allocate a new patch structure and link it into the list */ -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) { struct Patch *pPatch; @@ -418,8 +419,8 @@ static struct Patch *allocpatch(uint32_t type, struct Expression const *expr) pPatch->nRPNSize = 0; pPatch->nType = type; - pPatch->nOffset = pCurrentSection->nPC; fstk_DumpToStr(pPatch->tzFilename, sizeof(pPatch->tzFilename)); + pPatch->nOffset = ofs; writerpn(pPatch->pRPN, &pPatch->nRPNSize, expr->tRPN, expr->nRPNLength); assert(pPatch->nRPNSize == expr->nRPNPatchSize); @@ -430,9 +431,9 @@ static struct Patch *allocpatch(uint32_t type, struct Expression const *expr) /* * Create a new patch (includes the rpn expr) */ -void out_CreatePatch(uint32_t type, struct Expression const *expr) +void out_CreatePatch(uint32_t type, struct Expression const *expr, uint32_t ofs) { - struct Patch *pPatch = allocpatch(type, expr); + struct Patch *pPatch = allocpatch(type, expr, ofs); pPatch->pNext = pCurrentSection->pPatches; pCurrentSection->pPatches = pPatch; @@ -442,14 +443,14 @@ void out_CreatePatch(uint32_t type, struct Expression const *expr) * Creates an assert that will be written to the object file */ bool out_CreateAssert(enum AssertionType type, struct Expression const *expr, - char const *message) + char const *message, uint32_t ofs) { struct Assertion *assertion = malloc(sizeof(*assertion)); if (!assertion) return false; - assertion->patch = allocpatch(type, expr); + assertion->patch = allocpatch(type, expr, ofs); assertion->section = pCurrentSection; assertion->message = strdup(message); if (!assertion->message) { diff --git a/src/asm/section.c b/src/asm/section.c index 46536519..f2ac719d 100644 --- a/src/asm/section.c +++ b/src/asm/section.c @@ -16,16 +16,18 @@ struct SectionStackEntry { struct Section *pSection; struct sSymbol *pScope; /* Section's symbol scope */ + uint32_t offset; struct SectionStackEntry *pNext; }; struct SectionStackEntry *pSectionStack; static struct Section *currentLoadSection = NULL; +uint32_t loadOffset = 0; /* The offset of the LOAD section within its parent */ /* * A quick check to see if we have an initialized section */ -static void checksection(void) +static inline void checksection(void) { if (pCurrentSection == NULL) fatalerror("Code generation before SECTION directive"); @@ -35,7 +37,7 @@ static void checksection(void) * A quick check to see if we have an initialized section that can contain * this much initialized data */ -static void checkcodesection(void) +static inline void checkcodesection(void) { checksection(); @@ -49,22 +51,20 @@ static void checkcodesection(void) /* * Check if the section has grown too much. */ -static void checksectionoverflow(uint32_t delta_size) +static void reserveSpace(uint32_t delta_size) { uint32_t maxSize = maxsize[pCurrentSection->nType]; - uint32_t newSize = pCurrentSection->nPC + delta_size; + uint32_t newSize = curOffset + delta_size; - if (newSize > maxSize) { - /* - * This check is here to trap broken code that generates - * sections that are too big and to prevent the assembler from - * generating huge object files or trying to allocate too much - * memory. - * The real check must be done at the linking stage. - */ + /* + * This check is here to trap broken code that generates sections that + * are too big and to prevent the assembler from generating huge object + * files or trying to allocate too much memory. + * A check at the linking stage is still necessary. + */ + if (newSize > maxSize) fatalerror("Section '%s' is too big (max size = 0x%X bytes, reached 0x%X).", pCurrentSection->pzName, maxSize, newSize); - } } struct Section *out_FindSectionByName(const char *pzName) @@ -85,7 +85,8 @@ struct Section *out_FindSectionByName(const char *pzName) * Find a section by name and type. If it doesn't exist, create it */ static struct Section *getSection(char const *pzName, enum SectionType type, - int32_t org, int32_t bank, int32_t alignment) + uint32_t org, uint32_t bank, + uint32_t alignment, bool isUnion) { if (bank != -1) { if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM @@ -123,13 +124,99 @@ static struct Section *getSection(char const *pzName, enum SectionType type, struct Section *pSect = out_FindSectionByName(pzName); if (pSect) { - if (type == pSect->nType - && ((uint32_t)org) == pSect->nOrg - && ((uint32_t)bank) == pSect->nBank - && ((uint32_t)alignment == pSect->nAlign)) { - return pSect; + unsigned int nbSectErrors = 0; +#define fail(...) \ + do { \ + yyerror(__VA_ARGS__); \ + nbSectErrors++; \ + } while (0) + + if (type != pSect->nType) + fail("Section \"%s\" already exists but with type %s", + pSect->pzName, typeNames[pSect->nType]); + + /* + * Normal sections need to have exactly identical constraints; + * but unionized sections only need "compatible" constraints, + * and they end up with the strictest combination of both + */ + if (isUnion) { + if (!pSect->isUnion) + fail("Section \"%s\" already declared as non-union", + pSect->pzName); + /* + * WARNING: see comment abount assumption in + * `EndLoadSection` if modifying the following check! + */ + if (sect_HasData(type)) + fail("Cannot declare ROM sections as UNION"); + if (org != -1) { + /* If neither is fixed, they must be the same */ + if (pSect->nOrg != -1 && pSect->nOrg != org) + fail("Section \"%s\" already declared as fixed at different address $%x", + pSect->pzName, pSect->nOrg); + else + /* Otherwise, just override */ + pSect->nOrg = org; + } else if (alignment != 0) { + /* Make sure any fixed address is compatible */ + if (pSect->nOrg != -1) { + uint32_t mask = alignment - 1; + + if (pSect->nOrg & mask) + fail("Section \"%s\" already declared as fixed at incompatible address $%x", + pSect->pzName, + pSect->nOrg); + } else if (alignment > pSect->nAlign) { + /* + * If the section is not fixed, + * its alignment is the largest of both + */ + pSect->nAlign = alignment; + } + } + /* If the section's bank is unspecified, override it */ + if (pSect->nBank == -1) + pSect->nBank = bank; + /* If both specify a bank, it must be the same one */ + else if (bank != -1 && pSect->nBank != bank) + fail("Section \"%s\" already declared with different bank %u", + pSect->pzName, pSect->nBank); + } else { + if (pSect->isUnion) + fail("Section \"%s\" already declared as union", + pSect->pzName); + if (org != pSect->nOrg) { + if (pSect->nOrg == -1) + fail("Section \"%s\" already declared as floating", + pSect->pzName); + else + fail("Section \"%s\" already declared as fixed at $%x", + pSect->pzName, pSect->nOrg); + } + if (bank != pSect->nBank) { + if (pSect->nBank == -1) + fail("Section \"%s\" already declared as floating bank", + pSect->pzName); + else + fail("Section \"%s\" already declared as fixed at bank %u", + pSect->pzName, pSect->nBank); + } + if (alignment != pSect->nAlign) { + if (pSect->nAlign == 0) + fail("Section \"%s\" already declared as unaligned", + pSect->pzName); + else + fail("Section \"%s\" already declared as aligned to %u bits", + pSect->pzName, pSect->nAlign); + } } - fatalerror("Section already exists but with a different type"); + + if (nbSectErrors) + fatalerror("Cannot create section \"%s\" (%u errors)", + pSect->pzName, nbSectErrors); +#undef fail + return pSect; } pSect = malloc(sizeof(*pSect)); @@ -141,7 +228,8 @@ static struct Section *getSection(char const *pzName, enum SectionType type, fatalerror("Not enough memory for sectionname"); pSect->nType = type; - pSect->nPC = 0; + pSect->isUnion = isUnion; + pSect->size = 0; pSect->nOrg = org; pSect->nBank = bank; pSect->nAlign = alignment; @@ -177,10 +265,7 @@ static void setSection(struct Section *pSect) if (nUnionDepth > 0) fatalerror("Cannot change the section within a UNION"); - nPC = (pSect != NULL) ? pSect->nPC : 0; - pPCSymbol->pSection = pSect; - pPCSymbol->isConstant = pSect && pSect->nOrg != -1; sym_SetCurrentSymbolScope(NULL); } @@ -188,24 +273,24 @@ static void setSection(struct Section *pSect) /* * Set the current section by name and type */ -void out_NewSection(char const *pzName, uint32_t type, int32_t org, - struct SectionSpec const *attributes) +void out_NewSection(char const *pzName, uint32_t type, uint32_t org, + struct SectionSpec const *attributes, bool isUnion) { if (currentLoadSection) fatalerror("Cannot change the section within a `LOAD` block"); struct Section *pSect = getSection(pzName, type, org, attributes->bank, - 1 << attributes->alignment); + 1 << attributes->alignment, isUnion); - nPC = pSect->nPC; setSection(pSect); + curOffset = isUnion ? 0 : pSect->size; pCurrentSection = pSect; } /* * Set the current section by name and type */ -void out_SetLoadSection(char const *name, uint32_t type, int32_t org, +void out_SetLoadSection(char const *name, uint32_t type, uint32_t org, struct SectionSpec const *attributes) { checkcodesection(); @@ -214,9 +299,10 @@ void out_SetLoadSection(char const *name, uint32_t type, int32_t org, fatalerror("`LOAD` blocks cannot be nested"); struct Section *pSect = getSection(name, type, org, attributes->bank, - 1 << attributes->alignment); + 1 << attributes->alignment, false); - nPC = pSect->nPC; + loadOffset = curOffset; + curOffset = 0; /* curOffset -= loadOffset; */ setSection(pSect); currentLoadSection = pSect; } @@ -227,8 +313,9 @@ void out_EndLoadSection(void) yyerror("Found `ENDL` outside of a `LOAD` block"); currentLoadSection = NULL; - nPC = pCurrentSection->nPC; setSection(pCurrentSection); + curOffset += loadOffset; + loadOffset = 0; } struct Section *sect_GetSymbolSection(void) @@ -236,15 +323,44 @@ struct Section *sect_GetSymbolSection(void) return currentLoadSection ? currentLoadSection : pCurrentSection; } -/* - * Output an absolute byte (bypassing ROM/union checks) - */ -static void absByteBypassCheck(uint8_t b) +uint32_t sect_GetOutputOffset(void) { - pCurrentSection->tData[pCurrentSection->nPC++] = b; - if (currentLoadSection) - currentLoadSection->nPC++; - nPC++; + return curOffset + loadOffset; +} + +static inline void growSection(uint32_t growth) +{ + curOffset += growth; + if (curOffset > pCurrentSection->size) + pCurrentSection->size = curOffset; + if (currentLoadSection && curOffset > currentLoadSection->size) + currentLoadSection->size = curOffset; +} + +static inline void writebyte(uint8_t byte) +{ + pCurrentSection->tData[sect_GetOutputOffset()] = byte; + growSection(1); +} + +static inline void writeword(uint16_t b) +{ + writebyte(b & 0xFF); + writebyte(b >> 8); +} + +static inline void writelong(uint32_t b) +{ + writebyte(b & 0xFF); + writebyte(b >> 8); + writebyte(b >> 16); + writebyte(b >> 24); +} + +static inline void createPatch(enum PatchType type, + struct Expression const *expr) +{ + out_CreatePatch(type, expr, sect_GetOutputOffset()); } /* @@ -253,16 +369,18 @@ static void absByteBypassCheck(uint8_t b) void out_AbsByte(uint8_t b) { checkcodesection(); - checksectionoverflow(1); - absByteBypassCheck(b); + reserveSpace(1); + + writebyte(b); } void out_AbsByteGroup(uint8_t const *s, int32_t length) { checkcodesection(); - checksectionoverflow(length); + reserveSpace(length); + while (length--) - absByteBypassCheck(*s++); + writebyte(*s++); } /* @@ -271,19 +389,17 @@ void out_AbsByteGroup(uint8_t const *s, int32_t length) void out_Skip(int32_t skip) { checksection(); - checksectionoverflow(skip); + reserveSpace(skip); + if (!sect_HasData(pCurrentSection->nType)) { - pCurrentSection->nPC += skip; - if (currentLoadSection) - currentLoadSection->nPC += skip; - nPC += skip; + growSection(skip); } else if (nUnionDepth > 0) { while (skip--) - absByteBypassCheck(CurrentOptions.fillchar); + writebyte(CurrentOptions.fillchar); } else { checkcodesection(); while (skip--) - absByteBypassCheck(CurrentOptions.fillchar); + writebyte(CurrentOptions.fillchar); } } @@ -293,19 +409,10 @@ void out_Skip(int32_t skip) void out_String(char const *s) { checkcodesection(); - checksectionoverflow(strlen(s)); - while (*s) - absByteBypassCheck(*s++); -} + reserveSpace(strlen(s)); -static void outputExpression(struct Expression const *expr) -{ - if (!rpn_isKnown(expr)) { - out_CreatePatch(PATCHTYPE_BYTE, expr); - out_AbsByte(0); - } else { - out_AbsByte(expr->nVal); - } + while (*s) + writebyte(*s++); } /* @@ -314,7 +421,15 @@ static void outputExpression(struct Expression const *expr) */ void out_RelByte(struct Expression *expr) { - outputExpression(expr); + checkcodesection(); + reserveSpace(1); + + if (!rpn_isKnown(expr)) { + createPatch(PATCHTYPE_BYTE, expr); + writebyte(0); + } else { + writebyte(expr->nVal); + } rpn_Free(expr); } @@ -322,25 +437,20 @@ void out_RelByte(struct Expression *expr) * Output several copies of a relocatable byte. Checking will be done to see if * it is an absolute value in disguise. */ -void out_RelBytes(struct Expression *expr, int32_t n) -{ - while (n--) - outputExpression(expr); - rpn_Free(expr); -} - -/* - * Output an absolute word - */ -static void absWord(uint16_t b) +void out_RelBytes(struct Expression *expr, uint32_t n) { checkcodesection(); - checksectionoverflow(2); - pCurrentSection->tData[pCurrentSection->nPC++] = b & 0xFF; - pCurrentSection->tData[pCurrentSection->nPC++] = b >> 8; - if (currentLoadSection) - currentLoadSection->nPC += 2; - nPC += 2; + reserveSpace(n); + + while (n--) { + if (!rpn_isKnown(expr)) { + createPatch(PATCHTYPE_BYTE, expr); + writebyte(0); + } else { + writebyte(expr->nVal); + } + } + rpn_Free(expr); } /* @@ -349,42 +459,32 @@ static void absWord(uint16_t b) */ void out_RelWord(struct Expression *expr) { + checkcodesection(); + reserveSpace(2); + if (!rpn_isKnown(expr)) { - out_CreatePatch(PATCHTYPE_WORD, expr); - absWord(0); + createPatch(PATCHTYPE_WORD, expr); + writeword(0); } else { - absWord(expr->nVal); + writeword(expr->nVal); } rpn_Free(expr); } -/* - * Output an absolute longword - */ -static void absLong(uint32_t b) -{ - checkcodesection(); - checksectionoverflow(4); - pCurrentSection->tData[pCurrentSection->nPC++] = b & 0xFF; - pCurrentSection->tData[pCurrentSection->nPC++] = b >> 8; - pCurrentSection->tData[pCurrentSection->nPC++] = b >> 16; - pCurrentSection->tData[pCurrentSection->nPC++] = b >> 24; - if (currentLoadSection) - currentLoadSection->nPC += 4; - nPC += 4; -} - /* * Output a relocatable longword. Checking will be done to see if * is an absolute value in disguise. */ void out_RelLong(struct Expression *expr) { + checkcodesection(); + reserveSpace(2); + if (!rpn_isKnown(expr)) { - out_CreatePatch(PATCHTYPE_LONG, expr); - absLong(0); + createPatch(PATCHTYPE_LONG, expr); + writelong(0); } else { - absLong(expr->nVal); + writelong(expr->nVal); } rpn_Free(expr); } @@ -396,13 +496,11 @@ void out_RelLong(struct Expression *expr) void out_PCRelByte(struct Expression *expr) { checkcodesection(); - checksectionoverflow(1); + reserveSpace(1); + if (!rpn_isKnown(expr) || pCurrentSection->nOrg == -1) { - out_CreatePatch(PATCHTYPE_JR, expr); - pCurrentSection->tData[pCurrentSection->nPC++] = 0; - if (currentLoadSection) - currentLoadSection->nPC++; - nPC++; + createPatch(PATCHTYPE_JR, expr); + writebyte(0); } else { /* Target is relative to the byte *after* the operand */ uint16_t address = sym_GetValue(pPCSymbol) + 1; @@ -412,9 +510,9 @@ void out_PCRelByte(struct Expression *expr) if (offset < -128 || offset > 127) { yyerror("jr target out of reach (expected -129 < %d < 128)", offset); - out_AbsByte(0); + writebyte(0); } else { - out_AbsByte(offset); + writebyte(offset); } } rpn_Free(expr); @@ -444,7 +542,7 @@ void out_BinaryFile(char const *s) fsize = ftell(f); rewind(f); - checksectionoverflow(fsize); + reserveSpace(fsize); } else if (errno != ESPIPE) { yyerror("Error determining size of INCBIN file '%s': %s", s, strerror(errno)); @@ -452,11 +550,8 @@ void out_BinaryFile(char const *s) while ((byte = fgetc(f)) != EOF) { if (fsize == -1) - checksectionoverflow(1); - pCurrentSection->tData[pCurrentSection->nPC++] = byte; - if (currentLoadSection) - currentLoadSection->nPC++; - nPC++; + growSection(1); + writebyte(byte); } if (ferror(f)) @@ -493,7 +588,7 @@ void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length) } checkcodesection(); - checksectionoverflow(length); + reserveSpace(length); int32_t fsize; @@ -524,10 +619,7 @@ void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length) int byte = fgetc(f); if (byte != EOF) { - pCurrentSection->tData[pCurrentSection->nPC++] = byte; - if (currentLoadSection) - currentLoadSection->nPC++; - nPC++; + writebyte(byte); } else if (ferror(f)) { yyerror("Error reading INCBIN file '%s': %s", s, strerror(errno)); @@ -553,6 +645,7 @@ void out_PushSection(void) pSect->pSection = pCurrentSection; pSect->pScope = sym_GetCurrentSymbolScope(); + pSect->offset = curOffset; pSect->pNext = pSectionStack; pSectionStack = pSect; } @@ -571,6 +664,8 @@ void out_PopSection(void) setSection(pSect->pSection); pCurrentSection = pSect->pSection; sym_SetCurrentSymbolScope(pSect->pScope); + curOffset = pSect->offset; + pSectionStack = pSect->pNext; free(pSect); } diff --git a/src/asm/symbol.c b/src/asm/symbol.c index 79acb21c..9e204aad 100644 --- a/src/asm/symbol.c +++ b/src/asm/symbol.c @@ -62,7 +62,7 @@ static int32_t Callback__LINE__(struct sSymbol const *self) static int32_t CallbackPC(struct sSymbol const *self) { - return self->pSection ? self->pSection->nOrg + self->pSection->nPC : 0; + return self->pSection ? self->pSection->nOrg + curOffset : 0; } /* @@ -124,7 +124,6 @@ static struct sSymbol *createsymbol(char const *s) if (snprintf((*ppsym)->tzName, MAXSYMLEN + 1, "%s", s) > MAXSYMLEN) warning(WARNING_LONG_STR, "Symbol name is too long: '%s'", s); - (*ppsym)->isConstant = false; (*ppsym)->isExported = false; (*ppsym)->isBuiltin = false; (*ppsym)->isReferenced = false; @@ -349,7 +348,6 @@ struct sSymbol *sym_AddEqu(char const *tzSym, int32_t value) nsym->nValue = value; nsym->type = SYM_EQU; - nsym->isConstant = true; nsym->pScope = NULL; updateSymbolFilename(nsym); @@ -417,7 +415,6 @@ struct sSymbol *sym_AddSet(char const *tzSym, int32_t value) nsym->nValue = value; nsym->type = SYM_SET; - nsym->isConstant = true; nsym->pScope = NULL; updateSymbolFilename(nsym); @@ -479,9 +476,8 @@ struct sSymbol *sym_AddReloc(char const *tzSym) nsym->tzFileName, nsym->nFileLine); /* If the symbol already exists as a ref, just "take over" it */ - nsym->nValue = nPC; + nsym->nValue = curOffset; nsym->type = SYM_LABEL; - nsym->isConstant = pCurrentSection && pCurrentSection->nOrg != -1; if (exportall) nsym->isExported = true; diff --git a/test/asm/section-union.asm b/test/asm/section-union.asm new file mode 100644 index 00000000..cf7af0e8 --- /dev/null +++ b/test/asm/section-union.asm @@ -0,0 +1,37 @@ +SECTION UNION "test", WRAM0 +Base: + ds $1000 + +SECTION UNION "test", WRAM0,ALIGN[8] + ds 42 +Plus42: + +SECTION UNION "test", WRAM0,ALIGN[4] + +SECTION UNION "test", WRAM0[$C000] +; Since the section's base address is known, the labels are constant now + ds $1000 ; This shouldn't overflow +End: + +SECTION UNION "test", WRAM0,ALIGN[9] + + +check_label: MACRO +EXPECTED equ \2 + IF \1 == EXPECTED +RESULT equs "OK!" + ELSE +RESULT equs "expected {EXPECTED}" + ENDC + PURGE EXPECTED + + PRINTT "\1 is at {\1} ({RESULT})\n" + PURGE RESULT +ENDM + + check_label Base, $C000 + check_label Plus42, $C000 + 42 + check_label End, $D000 + + +SECTION "test", WRAM0 ; Don't allow creating a section that's not a union! diff --git a/test/asm/section-union.err b/test/asm/section-union.err new file mode 100644 index 00000000..533d56e5 --- /dev/null +++ b/test/asm/section-union.err @@ -0,0 +1,8 @@ +ERROR: section-union.asm(37): + Section "test" already declared as union +ERROR: section-union.asm(37): + Section "test" already declared as fixed at $c000 +ERROR: section-union.asm(37): + Section "test" already declared as aligned to 256 bits +ERROR: section-union.asm(37): + Cannot create section "test" (3 errors) diff --git a/test/asm/section-union.out b/test/asm/section-union.out new file mode 100644 index 00000000..52dd429a --- /dev/null +++ b/test/asm/section-union.out @@ -0,0 +1,3 @@ +Base is at $C000 (OK!) +Plus42 is at $C02A (OK!) +End is at $D000 (OK!) From e123b6dec79f25a765ae0d0f960db10e14b4644d Mon Sep 17 00:00:00 2001 From: ISSOtm Date: Fri, 20 Mar 2020 23:44:02 +0100 Subject: [PATCH 2/5] Implement unionized sections in RGBLINK --- include/link/section.h | 2 + include/linkdefs.h | 2 +- src/asm/output.c | 2 +- src/link/object.c | 17 ++++++- src/link/patch.c | 66 +++++++++++++++----------- src/link/section.c | 71 +++++++++++++++++++++++++--- test/link/section-union/a.asm | 21 ++++++++ test/link/section-union/b.asm | 18 +++++++ test/link/section-union/ref.out.bin | Bin 0 -> 25 bytes test/link/section-union/script.link | 9 ++++ test/link/test.sh | 12 ++++- 11 files changed, 181 insertions(+), 39 deletions(-) create mode 100644 test/link/section-union/a.asm create mode 100644 test/link/section-union/b.asm create mode 100644 test/link/section-union/ref.out.bin create mode 100644 test/link/section-union/script.link diff --git a/include/link/section.h b/include/link/section.h index fe13867d..f8b28047 100644 --- a/include/link/section.h +++ b/include/link/section.h @@ -37,6 +37,7 @@ struct Section { char *name; uint16_t size; enum SectionType type; + bool isUnion; bool isAddressFixed; uint16_t org; bool isBankFixed; @@ -50,6 +51,7 @@ struct Section { struct Symbol **fileSymbols; uint32_t nbSymbols; struct Symbol const **symbols; + struct Section *nextu; /* The next "component" of this unionized sect */ }; /* diff --git a/include/linkdefs.h b/include/linkdefs.h index 75fb2b45..76d89d57 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 2 +#define RGBDS_OBJECT_REV 3 enum AssertionType { ASSERT_WARN, diff --git a/src/asm/output.c b/src/asm/output.c index 3c742aa0..449d95f4 100644 --- a/src/asm/output.c +++ b/src/asm/output.c @@ -189,7 +189,7 @@ static void writesection(struct Section *pSect, FILE *f) fputlong(pSect->size, f); - fputc(pSect->nType, f); + fputc(pSect->nType | pSect->isUnion << 7, f); fputlong(pSect->nOrg, f); fputlong(pSect->nBank, f); diff --git a/src/link/object.c b/src/link/object.c index d42565d9..5685983c 100644 --- a/src/link/object.c +++ b/src/link/object.c @@ -245,6 +245,7 @@ static void readSection(FILE *file, struct Section *section, char const *fileName) { int32_t tmp; + uint8_t type; tryReadstr(section->name, file, "%s: Cannot read section name: %s", fileName); @@ -254,8 +255,10 @@ static void readSection(FILE *file, struct Section *section, errx(1, "\"%s\"'s section size (%d) is invalid", section->name, tmp); section->size = tmp; - tryGetc(section->type, file, "%s: Cannot read \"%s\"'s type: %s", + tryGetc(type, file, "%s: Cannot read \"%s\"'s type: %s", fileName, section->name); + section->type = type & 0x7F; + section->isUnion = type >> 7; tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s org: %s", fileName, section->name); section->isAddressFixed = tmp >= 0; @@ -358,6 +361,14 @@ static void readAssertion(FILE *file, struct Assertion *assert, fileName); } +static inline struct Section *getMainSection(struct Section *section) +{ + if (section->isUnion) + section = sect_GetSection(section->name); + + return section; +} + /** * Reads an object file of any supported format * @param fileName The filename to report for errors @@ -448,6 +459,7 @@ void obj_ReadFile(char const *fileName) if (!section) err(1, "%s: Couldn't create new section", fileName); + section->nextu = NULL; readSection(file, section, fileName); section->fileSymbols = fileSymbols; @@ -472,9 +484,10 @@ void obj_ReadFile(char const *fileName) if (sectionID == -1) { fileSymbols[i]->section = NULL; } else { - fileSymbols[i]->section = fileSections[sectionID]; /* Give the section a pointer to the symbol as well */ linkSymToSect(fileSymbols[i], fileSections[sectionID]); + fileSymbols[i]->section = + getMainSection(fileSections[sectionID]); } } diff --git a/src/link/patch.c b/src/link/patch.c index 0998dda0..38ae524e 100644 --- a/src/link/patch.c +++ b/src/link/patch.c @@ -117,6 +117,24 @@ static uint32_t getRPNByte(uint8_t const **expression, int32_t *size, return *(*expression)++; } +static struct Symbol const *getSymbol(struct Symbol ** const symbolList, + uint32_t index, char const *fileName) +{ + struct Symbol const *symbol = symbolList[index]; + + /* If the symbol is defined elsewhere... */ + if (symbol->type == SYMTYPE_IMPORT) { + struct Symbol const *symbolDefinition = + sym_GetSymbol(symbol->name); + if (!symbolDefinition) + errx(1, "%s: Unknown symbol \"%s\"", fileName, + symbol->name); + symbol = symbolDefinition; + } + + return symbol; +} + /** * Compute a patch's value from its RPN string. * @param patch The patch to compute the value of @@ -238,19 +256,8 @@ static int32_t computeRPNExpr(struct Patch const *patch, value |= getRPNByte(&expression, &size, patch->fileName) << shift; - symbol = section->fileSymbols[value]; - - /* If the symbol is defined elsewhere... */ - if (symbol->type == SYMTYPE_IMPORT) { - struct Symbol const *symbolDefinition = - sym_GetSymbol(symbol->name); - if (!symbolDefinition) - errx(1, "%s: Unknown symbol \"%s\"", - patch->fileName, symbol->name); - symbol = symbolDefinition; - } - - value = symbol->section->bank; + value = getSymbol(section->fileSymbols, value, + patch->fileName)->section->bank; break; case RPN_BANK_SECT: @@ -305,17 +312,8 @@ static int32_t computeRPNExpr(struct Patch const *patch, value |= getRPNByte(&expression, &size, patch->fileName) << shift; - symbol = section->fileSymbols[value]; - - /* If the symbol is defined elsewhere... */ - if (symbol->type == SYMTYPE_IMPORT) { - struct Symbol const *symbolDefinition = - sym_GetSymbol(symbol->name); - if (!symbolDefinition) - errx(1, "%s: Unknown symbol \"%s\"", - patch->fileName, symbol->name); - symbol = symbolDefinition; - } + symbol = getSymbol(section->fileSymbols, value, + patch->fileName); if (!strcmp(symbol->name, "@")) { value = section->org + patch->offset; @@ -387,10 +385,8 @@ void patch_CheckAssertions(struct Assertion *assert) * @param section The section to patch * @param arg Ignored callback arg */ -static void applyPatches(struct Section *section, void *arg) +static void applyFilePatches(struct Section *section) { - (void)arg; - if (!sect_HasData(section->type)) return; @@ -435,6 +431,22 @@ static void applyPatches(struct Section *section, void *arg) } } +/** + * Applies all of a section's patches, iterating over "components" of + * unionized sections + * @param section The section to patch + * @param arg Ignored callback arg + */ +static void applyPatches(struct Section *section, void *arg) +{ + (void)arg; + + do { + applyFilePatches(section); + section = section->nextu; + } while (section); +} + void patch_ApplyPatches(void) { initRPNStack(); diff --git a/src/link/section.c b/src/link/section.c index d1c35640..d52afee1 100644 --- a/src/link/section.c +++ b/src/link/section.c @@ -36,17 +36,76 @@ void sect_ForEach(void (*callback)(struct Section *, void *), void *arg) hash_ForEach(sections, forEach, &callbackArg); } +static void mergeSections(struct Section *target, struct Section *other) +{ + if (other->isAddressFixed) { + if (target->isAddressFixed) { + if (target->org != other->org) + errx(1, "Section \"%s\" is defined with conflicting addresses $%x and $%x", + other->name, target->org, other->org); + } else if (target->isAlignFixed) { + if (other->org & target->alignMask) + errx(1, "Section \"%s\" is defined with conflicting %u-byte alignment and address $%x", + other->name, target->alignMask + 1, + other->org); + } + target->isAddressFixed = true; + target->org = other->org; + } else if (other->isAlignFixed) { + if (target->isAddressFixed) { + if (target->org & other->alignMask) + errx(1, "Section \"%s\" is defined with conflicting address $%x and %u-byte alignment", + other->name, target->org, + other->alignMask + 1); + } else if (!target->isAlignFixed + || other->alignMask > target->alignMask) { + target->isAlignFixed = true; + target->alignMask = other->alignMask; + } + } + + if (other->isBankFixed) { + if (!target->isBankFixed) { + target->isBankFixed = true; + target->bank = other->bank; + } else if (target->bank != other->bank) { + errx(1, "Section \"%s\" is defined with conflicting banks %u and %u", + other->name, target->bank, other->bank); + } + } + + if (other->size > target->size) + target->size = other->size; + + target->nextu = other; +} + void sect_AddSection(struct Section *section) { /* Check if the section already exists */ - if (hash_GetElement(sections, section->name)) - errx(1, "Section name \"%s\" is already in use", section->name); + struct Section *other = hash_GetElement(sections, section->name); - /* If not, add it */ - bool collided = hash_AddElement(sections, section->name, section); + if (other) { + if (other->isUnion && section->isUnion) { + mergeSections(other, section); + } else if (section->isUnion || other->isUnion) { + errx(1, "Section \"%s\" defined as both unionized and not", + section->name); + } else { + errx(1, "Section name \"%s\" is already in use", + section->name); + } + } else if (section->isUnion && sect_HasData(section->type)) { + errx(1, "Section \"%s\" is of type %s, which cannot be unionized", + section->name, typeNames[section->type]); + } else { + /* If not, add it */ + bool collided = hash_AddElement(sections, section->name, + section); - if (beVerbose && collided) - warnx("Section hashmap collision occurred!"); + if (beVerbose && collided) + warnx("Section hashmap collision occurred!"); + } } struct Section *sect_GetSection(char const *name) diff --git a/test/link/section-union/a.asm b/test/link/section-union/a.asm new file mode 100644 index 00000000..55f9c90a --- /dev/null +++ b/test/link/section-union/a.asm @@ -0,0 +1,21 @@ +SECTION UNION "a", WRAM0 + ds 10 +a1: + +SECTION UNION "b", WRAMX,ALIGN[4] +banked:: + ds 10 +b1: + +SECTION UNION "c", HRAM[$FFC0] + ds 5 +c1:: + + +SECTION "output 1", ROM0 + dw a1,a2 ; $C00A, $C02A + dw b1,b2 ; $DABA, $DAB2 + dw c1,c2 ; $FFC5, $FFC5 + +SECTION "output 3", ROM0 + db BANK(banked) diff --git a/test/link/section-union/b.asm b/test/link/section-union/b.asm new file mode 100644 index 00000000..21828c1a --- /dev/null +++ b/test/link/section-union/b.asm @@ -0,0 +1,18 @@ +SECTION UNION "a", WRAM0 +a1: ; This is here to check that the two `a1` don't conflict + ds 42 +a2:: + +SECTION UNION "b", WRAMX[$DAB0] + ds 2 +b2:: + +SECTION UNION "c", HRAM[$FFC0] +b1: ; Same but in different sections now + ds 5 +c2:: + +SECTION "output 2", ROM0 + dw a1,a2 + dw b1,b2 + dw c1,c2 diff --git a/test/link/section-union/ref.out.bin b/test/link/section-union/ref.out.bin new file mode 100644 index 0000000000000000000000000000000000000000..4ec95224b205e478e9f1acd9b49399855e30763c GIT binary patch literal 25 ccmd-&pmku^txdO%{y+Mk0mwe^AHrq?0MXJA+W-In literal 0 HcmV?d00001 diff --git a/test/link/section-union/script.link b/test/link/section-union/script.link new file mode 100644 index 00000000..1bb79ddc --- /dev/null +++ b/test/link/section-union/script.link @@ -0,0 +1,9 @@ +ROM0 + "output 1" + "output 2" + "output 3" +WRAM0 + "a" ; $C000 +WRAMX 1 + ORG $DAB0 + "b" diff --git a/test/link/test.sh b/test/link/test.sh index 8ec56be5..68d33600 100755 --- a/test/link/test.sh +++ b/test/link/test.sh @@ -71,12 +71,20 @@ for i in *.asm; do fi done -# This test does its own thing +# These tests do their own thing + $RGBASM -o $otemp high-low/a.asm $RGBLINK -o $gbtemp $otemp $RGBASM -o $otemp high-low/b.asm $RGBLINK -o $gbtemp2 $otemp -i="high-low.asm" tryDiff $gbtemp $gbtemp2 +i="high-low.asm" tryCmp $gbtemp $gbtemp2 +rc=$(($? || $rc)) + +$RGBASM -o $otemp section-union/a.asm +$RGBASM -o $gbtemp2 section-union/b.asm +$RGBLINK -o $gbtemp -l section-union/script.link $otemp $gbtemp2 +dd if=$gbtemp count=1 bs=$(printf %s $(wc -c < section-union/ref.out.bin)) > $otemp 2>/dev/null +i="section-union.asm" tryCmp section-union/ref.out.bin $otemp rc=$(($? || $rc)) rm -f $otemp $gbtemp $gbtemp2 $outtemp From 275b8e15ff2eb13eeeef9e8d60783ffa839007e6 Mon Sep 17 00:00:00 2001 From: ISSOtm Date: Sat, 21 Mar 2020 00:29:58 +0100 Subject: [PATCH 3/5] Document modification to object file format --- src/rgbds.5 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rgbds.5 b/src/rgbds.5 index 3f2eb743..3bd2b3c8 100644 --- a/src/rgbds.5 +++ b/src/rgbds.5 @@ -48,8 +48,10 @@ REPT NumberOfSymbols ; Number of symbols defined in this object file. BYTE Type ; 0 = LOCAL symbol only used in this file. ; 1 = IMPORT this symbol from elsewhere ; 2 = EXPORT this symbol to other objects. + ; Bit 7 is independent from the above value, and + ; encodes whether the section is unionized - IF Type != 1 ; If symbol is defined in this object file. + IF (Type & 0x7F) != 1 ; If symbol is defined in this object file. STRING FileName ; File where the symbol is defined. From 4877bb783cfd7ef9ec8190c71663c68530024026 Mon Sep 17 00:00:00 2001 From: ISSOtm Date: Sun, 22 Mar 2020 01:46:10 +0100 Subject: [PATCH 4/5] Add more tests for unionized sections + fix bugs Implementing those tests found a few bugs... oops --- src/asm/section.c | 6 ++++- src/link/section.c | 4 +++ test/asm/section-union.err | 2 +- test/link/section-union/align-conflict.asm | 10 ++++++++ test/link/section-union/align-conflict.out | 6 +++++ test/link/section-union/bad-types.asm | 10 ++++++++ test/link/section-union/bad-types.out | 6 +++++ test/link/section-union/bank-conflict.asm | 8 ++++++ test/link/section-union/bank-conflict.out | 6 +++++ test/link/section-union/data-overlay.asm | 10 ++++++++ test/link/section-union/data-overlay.out | 6 +++++ test/link/section-union/different-data.asm | 8 ++++++ test/link/section-union/different-data.out | 6 +++++ test/link/section-union/different-size.asm | 8 ++++++ test/link/section-union/different-size.out | 6 +++++ .../link/section-union/different-syntaxes.asm | 10 ++++++++ .../link/section-union/different-syntaxes.out | 6 +++++ test/link/section-union/{ => good}/a.asm | 0 test/link/section-union/{ => good}/b.asm | 0 .../link/section-union/{ => good}/ref.out.bin | Bin .../link/section-union/{ => good}/script.link | 0 test/link/section-union/no-room.asm | 12 +++++++++ test/link/section-union/no-room.out | 2 ++ test/link/section-union/org-conflict.asm | 8 ++++++ test/link/section-union/org-conflict.out | 6 +++++ test/link/section-union/split-data.asm | 10 ++++++++ test/link/section-union/split-data.out | 6 +++++ test/link/test.sh | 23 ++++++++++++++---- 28 files changed, 178 insertions(+), 7 deletions(-) create mode 100644 test/link/section-union/align-conflict.asm create mode 100644 test/link/section-union/align-conflict.out create mode 100644 test/link/section-union/bad-types.asm create mode 100644 test/link/section-union/bad-types.out create mode 100644 test/link/section-union/bank-conflict.asm create mode 100644 test/link/section-union/bank-conflict.out create mode 100644 test/link/section-union/data-overlay.asm create mode 100644 test/link/section-union/data-overlay.out create mode 100644 test/link/section-union/different-data.asm create mode 100644 test/link/section-union/different-data.out create mode 100644 test/link/section-union/different-size.asm create mode 100644 test/link/section-union/different-size.out create mode 100644 test/link/section-union/different-syntaxes.asm create mode 100644 test/link/section-union/different-syntaxes.out rename test/link/section-union/{ => good}/a.asm (100%) rename test/link/section-union/{ => good}/b.asm (100%) rename test/link/section-union/{ => good}/ref.out.bin (100%) rename test/link/section-union/{ => good}/script.link (100%) create mode 100644 test/link/section-union/no-room.asm create mode 100644 test/link/section-union/no-room.out create mode 100644 test/link/section-union/org-conflict.asm create mode 100644 test/link/section-union/org-conflict.out create mode 100644 test/link/section-union/split-data.asm create mode 100644 test/link/section-union/split-data.out diff --git a/src/asm/section.c b/src/asm/section.c index f2ac719d..812d5da5 100644 --- a/src/asm/section.c +++ b/src/asm/section.c @@ -155,6 +155,10 @@ static struct Section *getSection(char const *pzName, enum SectionType type, if (pSect->nOrg != -1 && pSect->nOrg != org) fail("Section \"%s\" already declared as fixed at different address $%x", pSect->pzName, pSect->nOrg); + else if (pSect->nAlign != 0 + && ((pSect->nAlign - 1) & org)) + fail("Section \"%s\" already declared as aligned to %u bytes", + pSect->pzName, pSect->nAlign); else /* Otherwise, just override */ pSect->nOrg = org; @@ -207,7 +211,7 @@ static struct Section *getSection(char const *pzName, enum SectionType type, fail("Section \"%s\" already declared as unaligned", pSect->pzName); else - fail("Section \"%s\" already declared as aligned to %u bits", + fail("Section \"%s\" already declared as aligned to %u bytes", pSect->pzName, pSect->nAlign); } } diff --git a/src/link/section.c b/src/link/section.c index d52afee1..3cdaee06 100644 --- a/src/link/section.c +++ b/src/link/section.c @@ -38,6 +38,10 @@ void sect_ForEach(void (*callback)(struct Section *, void *), void *arg) static void mergeSections(struct Section *target, struct Section *other) { + if (target->type != other->type) + errx(1, "Section \"%s\" is defined with conflicting types %s and %s", + other->name, + typeNames[target->type], typeNames[other->type]); if (other->isAddressFixed) { if (target->isAddressFixed) { if (target->org != other->org) diff --git a/test/asm/section-union.err b/test/asm/section-union.err index 533d56e5..871b97db 100644 --- a/test/asm/section-union.err +++ b/test/asm/section-union.err @@ -3,6 +3,6 @@ ERROR: section-union.asm(37): ERROR: section-union.asm(37): Section "test" already declared as fixed at $c000 ERROR: section-union.asm(37): - Section "test" already declared as aligned to 256 bits + Section "test" already declared as aligned to 256 bytes ERROR: section-union.asm(37): Cannot create section "test" (3 errors) diff --git a/test/link/section-union/align-conflict.asm b/test/link/section-union/align-conflict.asm new file mode 100644 index 00000000..7a92349b --- /dev/null +++ b/test/link/section-union/align-conflict.asm @@ -0,0 +1,10 @@ +IF !DEF(SECOND) +ATTRS equs ",ALIGN[2]" +ELSE +ATTRS equs "[$CAFE]" +ENDC + +SECTION UNION "conflicting alignment", WRAM0 ATTRS + db + + PURGE ATTRS diff --git a/test/link/section-union/align-conflict.out b/test/link/section-union/align-conflict.out new file mode 100644 index 00000000..1e4766ef --- /dev/null +++ b/test/link/section-union/align-conflict.out @@ -0,0 +1,6 @@ +error: Section "conflicting alignment" is defined with conflicting 4-byte alignment and address $cafe +--- +ERROR: -(18): + Section "conflicting alignment" already declared as aligned to 4 bytes +ERROR: -(18): + Cannot create section "conflicting alignment" (1 errors) diff --git a/test/link/section-union/bad-types.asm b/test/link/section-union/bad-types.asm new file mode 100644 index 00000000..e8c2e5f9 --- /dev/null +++ b/test/link/section-union/bad-types.asm @@ -0,0 +1,10 @@ +IF !DEF(SECOND) +TYPE equs "HRAM" +ELSE +TYPE equs "WRAM0" +ENDC + +SECTION UNION "conflicting types", TYPE + db + + PURGE TYPE diff --git a/test/link/section-union/bad-types.out b/test/link/section-union/bad-types.out new file mode 100644 index 00000000..bd5fdfcf --- /dev/null +++ b/test/link/section-union/bad-types.out @@ -0,0 +1,6 @@ +error: Section "conflicting types" is defined with conflicting types HRAM and WRAM0 +--- +ERROR: -(18): + Section "conflicting types" already exists but with type HRAM +ERROR: -(18): + Cannot create section "conflicting types" (1 errors) diff --git a/test/link/section-union/bank-conflict.asm b/test/link/section-union/bank-conflict.asm new file mode 100644 index 00000000..cc5c6aac --- /dev/null +++ b/test/link/section-union/bank-conflict.asm @@ -0,0 +1,8 @@ +IF !DEF(SECOND) +SECOND equs "4" +ENDC + +SECTION UNION "conflicting banks", WRAMX, BANK[SECOND] + db + + PURGE SECOND diff --git a/test/link/section-union/bank-conflict.out b/test/link/section-union/bank-conflict.out new file mode 100644 index 00000000..c806274f --- /dev/null +++ b/test/link/section-union/bank-conflict.out @@ -0,0 +1,6 @@ +error: Section "conflicting banks" is defined with conflicting banks 4 and 1 +--- +ERROR: -(14): + Section "conflicting banks" already declared with different bank 4 +ERROR: -(14): + Cannot create section "conflicting banks" (1 errors) diff --git a/test/link/section-union/data-overlay.asm b/test/link/section-union/data-overlay.asm new file mode 100644 index 00000000..be65b67e --- /dev/null +++ b/test/link/section-union/data-overlay.asm @@ -0,0 +1,10 @@ +IF !DEF(SECOND) +DATA equs "ds 4" +ELSE +DATA equs "db $aa, $bb, $cc, $dd" +ENDC + +SECTION UNION "overlaid data", ROM0 + DATA + + PURGE DATA diff --git a/test/link/section-union/data-overlay.out b/test/link/section-union/data-overlay.out new file mode 100644 index 00000000..b2cfb039 --- /dev/null +++ b/test/link/section-union/data-overlay.out @@ -0,0 +1,6 @@ +error: Section "overlaid data" is of type ROM0, which cannot be unionized +--- +ERROR: -(18): + Cannot declare ROM sections as UNION +ERROR: -(18): + Cannot create section "overlaid data" (1 errors) diff --git a/test/link/section-union/different-data.asm b/test/link/section-union/different-data.asm new file mode 100644 index 00000000..ca6b1537 --- /dev/null +++ b/test/link/section-union/different-data.asm @@ -0,0 +1,8 @@ +IF !DEF(SECOND) +DATA = 1 +ELSE +DATA = 2 +ENDC + +SECTION UNION "different data", ROM0 + db DATA diff --git a/test/link/section-union/different-data.out b/test/link/section-union/different-data.out new file mode 100644 index 00000000..46619e1c --- /dev/null +++ b/test/link/section-union/different-data.out @@ -0,0 +1,6 @@ +error: Section "different data" is of type ROM0, which cannot be unionized +--- +ERROR: -(16): + Cannot declare ROM sections as UNION +ERROR: -(16): + Cannot create section "different data" (1 errors) diff --git a/test/link/section-union/different-size.asm b/test/link/section-union/different-size.asm new file mode 100644 index 00000000..6514bf93 --- /dev/null +++ b/test/link/section-union/different-size.asm @@ -0,0 +1,8 @@ +IF !DEF(SECOND) +SIZE = 69 +ELSE +SIZE = 420 +ENDC + +SECTION UNION "different section sizes", ROM0 + ds SIZE diff --git a/test/link/section-union/different-size.out b/test/link/section-union/different-size.out new file mode 100644 index 00000000..bc6131f6 --- /dev/null +++ b/test/link/section-union/different-size.out @@ -0,0 +1,6 @@ +error: Section "different section sizes" is of type ROM0, which cannot be unionized +--- +ERROR: -(16): + Cannot declare ROM sections as UNION +ERROR: -(16): + Cannot create section "different section sizes" (1 errors) diff --git a/test/link/section-union/different-syntaxes.asm b/test/link/section-union/different-syntaxes.asm new file mode 100644 index 00000000..d261d21f --- /dev/null +++ b/test/link/section-union/different-syntaxes.asm @@ -0,0 +1,10 @@ +IF !DEF(SECOND) +INSTR equs "sbc a" +ELSE +INSTR equs "db $9f" +ENDC + +SECTION UNION "different syntaxes", ROM0 + INSTR + + PURGE INSTR diff --git a/test/link/section-union/different-syntaxes.out b/test/link/section-union/different-syntaxes.out new file mode 100644 index 00000000..52dd7709 --- /dev/null +++ b/test/link/section-union/different-syntaxes.out @@ -0,0 +1,6 @@ +error: Section "different syntaxes" is of type ROM0, which cannot be unionized +--- +ERROR: -(18): + Cannot declare ROM sections as UNION +ERROR: -(18): + Cannot create section "different syntaxes" (1 errors) diff --git a/test/link/section-union/a.asm b/test/link/section-union/good/a.asm similarity index 100% rename from test/link/section-union/a.asm rename to test/link/section-union/good/a.asm diff --git a/test/link/section-union/b.asm b/test/link/section-union/good/b.asm similarity index 100% rename from test/link/section-union/b.asm rename to test/link/section-union/good/b.asm diff --git a/test/link/section-union/ref.out.bin b/test/link/section-union/good/ref.out.bin similarity index 100% rename from test/link/section-union/ref.out.bin rename to test/link/section-union/good/ref.out.bin diff --git a/test/link/section-union/script.link b/test/link/section-union/good/script.link similarity index 100% rename from test/link/section-union/script.link rename to test/link/section-union/good/script.link diff --git a/test/link/section-union/no-room.asm b/test/link/section-union/no-room.asm new file mode 100644 index 00000000..a6202ab3 --- /dev/null +++ b/test/link/section-union/no-room.asm @@ -0,0 +1,12 @@ +; NB: the error is that there is no room to place the unionized section in, +; so RGBASM can't possibly error out. +IF !DEF(SECOND) + SECTION "bloat", WRAMX[$d000], BANK[2] + ds $fe0 + + SECTION UNION "test", WRAMX, BANK[2] + db +ELSE + SECTION UNION "test", WRAMX, ALIGN[6] + db +ENDC diff --git a/test/link/section-union/no-room.out b/test/link/section-union/no-room.out new file mode 100644 index 00000000..4a1104da --- /dev/null +++ b/test/link/section-union/no-room.out @@ -0,0 +1,2 @@ +error: Unable to place "test" (WRAMX section) in bank $02 with align mask ffffffc0 +--- diff --git a/test/link/section-union/org-conflict.asm b/test/link/section-union/org-conflict.asm new file mode 100644 index 00000000..b51635b3 --- /dev/null +++ b/test/link/section-union/org-conflict.asm @@ -0,0 +1,8 @@ +IF !DEF(SECOND) +ADDR = $BEEF +ELSE +ADDR = $BABE +ENDC + +SECTION UNION "conflicting address", SRAM[ADDR] + db diff --git a/test/link/section-union/org-conflict.out b/test/link/section-union/org-conflict.out new file mode 100644 index 00000000..b5a332ec --- /dev/null +++ b/test/link/section-union/org-conflict.out @@ -0,0 +1,6 @@ +error: Section "conflicting address" is defined with conflicting addresses $beef and $babe +--- +ERROR: -(16): + Section "conflicting address" already declared as fixed at different address $beef +ERROR: -(16): + Cannot create section "conflicting address" (1 errors) diff --git a/test/link/section-union/split-data.asm b/test/link/section-union/split-data.asm new file mode 100644 index 00000000..2ddff420 --- /dev/null +++ b/test/link/section-union/split-data.asm @@ -0,0 +1,10 @@ +IF !DEF(SECOND) +DATA equs "ds 1\ndb $aa" +ELSE +DATA equs "db $bb\nds 1" +ENDC + +SECTION UNION "mutually-overlaid data", ROM0 + DATA + + PURGE DATA diff --git a/test/link/section-union/split-data.out b/test/link/section-union/split-data.out new file mode 100644 index 00000000..36db397f --- /dev/null +++ b/test/link/section-union/split-data.out @@ -0,0 +1,6 @@ +error: Section "mutually-overlaid data" is of type ROM0, which cannot be unionized +--- +ERROR: -(18): + Cannot declare ROM sections as UNION +ERROR: -(18): + Cannot create section "mutually-overlaid data" (1 errors) diff --git a/test/link/test.sh b/test/link/test.sh index 68d33600..d987090c 100755 --- a/test/link/test.sh +++ b/test/link/test.sh @@ -80,12 +80,25 @@ $RGBLINK -o $gbtemp2 $otemp i="high-low.asm" tryCmp $gbtemp $gbtemp2 rc=$(($? || $rc)) -$RGBASM -o $otemp section-union/a.asm -$RGBASM -o $gbtemp2 section-union/b.asm -$RGBLINK -o $gbtemp -l section-union/script.link $otemp $gbtemp2 -dd if=$gbtemp count=1 bs=$(printf %s $(wc -c < section-union/ref.out.bin)) > $otemp 2>/dev/null -i="section-union.asm" tryCmp section-union/ref.out.bin $otemp +$RGBASM -o $otemp section-union/good/a.asm +$RGBASM -o $gbtemp2 section-union/good/b.asm +$RGBLINK -o $gbtemp -l section-union/good/script.link $otemp $gbtemp2 +dd if=$gbtemp count=1 bs=$(printf %s $(wc -c < section-union/good/ref.out.bin)) > $otemp 2>/dev/null +i="section-union.asm" tryCmp section-union/good/ref.out.bin $otemp rc=$(($? || $rc)) +for i in section-union/*.asm; do + $RGBASM -o $otemp $i + $RGBASM -o $gbtemp2 $i -DSECOND + if $RGBLINK $otemp $gbtemp2 > $outtemp 2>&1; then + echo -e "${bold}${red}$i didn't fail to link!${rescolors}${resbold}" + rc=1 + fi + echo --- >> $outtemp + # Ensure RGBASM also errors out + echo 'SECOND equs "1"' | cat $i - $i | $RGBASM - 2>> $outtemp + tryDiff ${i%.asm}.out $outtemp + rc=$(($? || $rc)) +done rm -f $otemp $gbtemp $gbtemp2 $outtemp exit $rc From 92134d7684dd4cf4753eac184ad940f80acee943 Mon Sep 17 00:00:00 2001 From: ISSOtm Date: Sun, 22 Mar 2020 10:59:12 +0100 Subject: [PATCH 5/5] Add testing for assertions inside unionized sections --- test/link/section-union/assert.asm | 18 ++++++++++++++++++ test/link/section-union/assert.out | 6 ++++++ test/link/test.sh | 2 +- 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 test/link/section-union/assert.asm create mode 100644 test/link/section-union/assert.out diff --git a/test/link/section-union/assert.asm b/test/link/section-union/assert.asm new file mode 100644 index 00000000..91b745d7 --- /dev/null +++ b/test/link/section-union/assert.asm @@ -0,0 +1,18 @@ +IF !DEF(SECOND) +OFS = 42 +ELSE +OFS = 69 +ENDC + +BASE = $C0DE + +SECTION UNION "assertions in unions", WRAM0 +IF DEF(SECOND) + assert @ != BASE, "Force failing the build" ; Force failure in RGBLINK, though +ENDC + ds OFS + assert @ == BASE + OFS, "This assertion should not trigger" + +; Only make RGBASM aware of the section's location *after* it sees the assertion +; This forces it to pass it to RGBLINK +SECTION UNION "assertions in unions", WRAM0[BASE] diff --git a/test/link/section-union/assert.out b/test/link/section-union/assert.out new file mode 100644 index 00000000..d0bce86e --- /dev/null +++ b/test/link/section-union/assert.out @@ -0,0 +1,6 @@ +section-union/assert.asm(11): Force failing the build +error: 1 assertions failed! +--- +ERROR: -(30): + Assertion failed: Force failing the build +error: Assembly aborted (1 errors)! diff --git a/test/link/test.sh b/test/link/test.sh index d987090c..8295b643 100755 --- a/test/link/test.sh +++ b/test/link/test.sh @@ -84,7 +84,7 @@ $RGBASM -o $otemp section-union/good/a.asm $RGBASM -o $gbtemp2 section-union/good/b.asm $RGBLINK -o $gbtemp -l section-union/good/script.link $otemp $gbtemp2 dd if=$gbtemp count=1 bs=$(printf %s $(wc -c < section-union/good/ref.out.bin)) > $otemp 2>/dev/null -i="section-union.asm" tryCmp section-union/good/ref.out.bin $otemp +i="section-union/good.asm" tryCmp section-union/good/ref.out.bin $otemp rc=$(($? || $rc)) for i in section-union/*.asm; do $RGBASM -o $otemp $i