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/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/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..449d95f4 100644 --- a/src/asm/output.c +++ b/src/asm/output.c @@ -187,9 +187,9 @@ static void writesection(struct Section *pSect, FILE *f) { fputstring(pSect->pzName, f); - fputlong(pSect->nPC, f); + fputlong(pSect->size, f); - fputc(pSect->nType, f); + fputc(pSect->nType | pSect->isUnion << 7, f); fputlong(pSect->nOrg, f); fputlong(pSect->nBank, 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..812d5da5 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,103 @@ 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 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; + } 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 bytes", + 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 +232,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 +269,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 +277,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 +303,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 +317,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 +327,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 +373,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 +393,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 +413,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 +425,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 +441,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 +463,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 +500,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 +514,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 +546,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 +554,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 +592,7 @@ void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length) } checkcodesection(); - checksectionoverflow(length); + reserveSpace(length); int32_t fsize; @@ -524,10 +623,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 +649,7 @@ void out_PushSection(void) pSect->pSection = pCurrentSection; pSect->pScope = sym_GetCurrentSymbolScope(); + pSect->offset = curOffset; pSect->pNext = pSectionStack; pSectionStack = pSect; } @@ -571,6 +668,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/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..3cdaee06 100644 --- a/src/link/section.c +++ b/src/link/section.c @@ -36,17 +36,80 @@ 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 (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) + 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/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. 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..871b97db --- /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 bytes +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!) 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/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/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/good/a.asm b/test/link/section-union/good/a.asm new file mode 100644 index 00000000..55f9c90a --- /dev/null +++ b/test/link/section-union/good/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/good/b.asm b/test/link/section-union/good/b.asm new file mode 100644 index 00000000..21828c1a --- /dev/null +++ b/test/link/section-union/good/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/good/ref.out.bin b/test/link/section-union/good/ref.out.bin new file mode 100644 index 00000000..4ec95224 Binary files /dev/null and b/test/link/section-union/good/ref.out.bin differ diff --git a/test/link/section-union/good/script.link b/test/link/section-union/good/script.link new file mode 100644 index 00000000..1bb79ddc --- /dev/null +++ b/test/link/section-union/good/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/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 8ec56be5..8295b643 100755 --- a/test/link/test.sh +++ b/test/link/test.sh @@ -71,13 +71,34 @@ 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/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/good.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