diff --git a/include/asm/section.h b/include/asm/section.h index a0fd4ff6..3fb4d035 100644 --- a/include/asm/section.h +++ b/include/asm/section.h @@ -22,7 +22,8 @@ struct Section { uint32_t size; uint32_t nOrg; uint32_t nBank; - uint32_t nAlign; + uint8_t nAlign; + uint16_t alignOfs; struct Section *pNext; struct Patch *pPatches; uint8_t *tData; @@ -30,7 +31,8 @@ struct Section { struct SectionSpec { uint32_t bank; - uint32_t alignment; + uint8_t alignment; + uint16_t alignOfs; }; struct Section *out_FindSectionByName(const char *pzName); diff --git a/include/link/section.h b/include/link/section.h index 90525220..af3804dc 100644 --- a/include/link/section.h +++ b/include/link/section.h @@ -50,6 +50,7 @@ struct Section { uint32_t bank; bool isAlignFixed; uint16_t alignMask; + uint16_t alignOfs; uint8_t *data; /* Array of size `size`*/ uint32_t nbPatches; struct Patch *patches; diff --git a/src/asm/asmy.y b/src/asm/asmy.y index 80e84213..b42a17ad 100644 --- a/src/asm/asmy.y +++ b/src/asm/asmy.y @@ -1425,6 +1425,7 @@ section : T_POP_SECTION sectunion string ',' sectiontype sectorg sectattrs { sectunion : /* empty */ { $$ = false; } | T_POP_UNION { $$ = true; } +; sectiontype : T_SECT_WRAM0 { $$ = SECTTYPE_WRAM0; } | T_SECT_VRAM { $$ = SECTTYPE_VRAM; } @@ -1449,15 +1450,29 @@ sectorg : /* empty */ { $$ = -1; } sectattrs : /* empty */ { $$.alignment = 0; + $$.alignOfs = 0; $$.bank = -1; } | sectattrs ',' T_OP_ALIGN '[' uconst ']' { - if ($5 < 0 || $5 > 16) - yyerror("Alignment must be between 0 and 16 bits, not %u", + if ($5 > 16) + yyerror("Alignment must be between 0 and 16, not %u", $5); else $$.alignment = $5; } + | sectattrs ',' T_OP_ALIGN '[' uconst ',' uconst ']' { + if ($5 > 16) { + yyerror("Alignment must be between 0 and 16, not %u", + $5); + } else { + $$.alignment = $5; + if ($7 >= 1 << $$.alignment) + yyerror("Alignment offset must not be greater than alignment (%u < %u)", + $7, 1 << $$.alignment); + else + $$.alignOfs = $7; + } + } | sectattrs ',' T_OP_BANK '[' uconst ']' { /* We cannot check the validity of this now */ $$.bank = $5; diff --git a/src/asm/output.c b/src/asm/output.c index f445a1bd..3f7feb50 100644 --- a/src/asm/output.c +++ b/src/asm/output.c @@ -181,6 +181,7 @@ static void writesection(struct Section const *pSect, FILE *f) fputlong(pSect->nOrg, f); fputlong(pSect->nBank, f); fputc(pSect->nAlign, f); + fputlong(pSect->alignOfs, f); if (sect_HasData(pSect->nType)) { fwrite(pSect->tData, 1, pSect->size, f); diff --git a/src/asm/section.c b/src/asm/section.c index d3ac304f..11653ff0 100644 --- a/src/asm/section.c +++ b/src/asm/section.c @@ -85,9 +85,14 @@ 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, - uint32_t org, uint32_t bank, - uint8_t alignment, bool isUnion) + uint32_t org, struct SectionSpec const *attrs, + bool isUnion) { +#define mask(align) ((1 << (align)) - 1) + uint32_t bank = attrs->bank; + uint8_t alignment = attrs->alignment; + uint16_t alignOffset = attrs->alignOfs; + if (bank != -1) { if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM && type != SECTTYPE_SRAM && type != SECTTYPE_WRAMX) @@ -99,12 +104,18 @@ static struct Section *getSection(char const *pzName, enum SectionType type, bankranges[type][0], bankranges[type][1]); } + if (alignOffset >= 1 << alignment) { + yyerror("Alignment offset must not be greater than alignment (%u < %u)", + alignOffset, 1 << alignment); + alignOffset = 0; + } + if (alignment != 0) { - /* It doesn't make sense to have both set */ - uint32_t mask = (1 << alignment) - 1; + /* It doesn't make sense to have both alignment and org set */ + uint32_t mask = mask(alignment); if (org != -1) { - if (org & mask) + if ((org - alignOffset) & mask) yyerror("Section \"%s\"'s fixed address doesn't match its alignment", pzName); alignment = 0; /* Ignore it if it's satisfied */ @@ -153,32 +164,41 @@ static struct Section *getSection(char const *pzName, enum SectionType type, if (sect_HasData(type)) fail("Cannot declare ROM sections as UNION"); if (org != -1) { - /* If neither is fixed, they must be the same */ + /* If both are 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 - && (((1 << pSect->nAlign) - 1) & org)) - fail("Section \"%s\" already declared as aligned to %u bytes", - pSect->pzName, 1 << pSect->nAlign); + && (mask(pSect->nAlign) + & (org - pSect->alignOfs))) + fail("Section \"%s\" already declared as aligned to %u bytes (offset %u)", + pSect->pzName, 1 << pSect->nAlign, + pSect->alignOfs); 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) + if ((pSect->nOrg - alignOffset) + & mask(alignment)) fail("Section \"%s\" already declared as fixed at incompatible address $%x", pSect->pzName, pSect->nOrg); + /* Check if alignment offsets are compatible */ + } else if ((alignOffset & mask(pSect->nAlign)) + != (pSect->alignOfs + & mask(alignment))) { + fail("Section \"%s\" already declared with incompatible %u-byte alignment (offset %u)", + pSect->pzName, pSect->nAlign, + pSect->alignOfs); } else if (alignment > pSect->nAlign) { /* * If the section is not fixed, * its alignment is the largest of both */ pSect->nAlign = alignment; + pSect->alignOfs = alignOffset; } } /* If the section's bank is unspecified, override it */ @@ -239,6 +259,7 @@ static struct Section *getSection(char const *pzName, enum SectionType type, pSect->nOrg = org; pSect->nBank = bank; pSect->nAlign = alignment; + pSect->alignOfs = alignOffset; pSect->pNext = pSectionList; pSect->pPatches = NULL; @@ -261,6 +282,7 @@ static struct Section *getSection(char const *pzName, enum SectionType type, pSectionList = pSect; return pSect; +#undef mask } /* @@ -280,13 +302,12 @@ static void setSection(struct Section *pSect) * Set the current section by name and type */ void out_NewSection(char const *pzName, uint32_t type, uint32_t org, - struct SectionSpec const *attributes, bool isUnion) + struct SectionSpec const *attribs, bool isUnion) { if (currentLoadSection) fatalerror("Cannot change the section within a `LOAD` block"); - struct Section *pSect = getSection(pzName, type, org, attributes->bank, - attributes->alignment, isUnion); + struct Section *pSect = getSection(pzName, type, org, attribs, isUnion); setSection(pSect); curOffset = isUnion ? 0 : pSect->size; @@ -297,15 +318,14 @@ void out_NewSection(char const *pzName, uint32_t type, uint32_t org, * Set the current section by name and type */ void out_SetLoadSection(char const *name, uint32_t type, uint32_t org, - struct SectionSpec const *attributes) + struct SectionSpec const *attribs) { checkcodesection(); if (currentLoadSection) fatalerror("`LOAD` blocks cannot be nested"); - struct Section *pSect = getSection(name, type, org, attributes->bank, - attributes->alignment, false); + struct Section *pSect = getSection(name, type, org, attribs, false); loadOffset = curOffset; curOffset = 0; /* curOffset -= loadOffset; */ diff --git a/src/link/assign.c b/src/link/assign.c index 10d609ca..4ee4928f 100644 --- a/src/link/assign.c +++ b/src/link/assign.c @@ -132,7 +132,8 @@ static bool isLocationSuitable(struct Section const *section, if (section->isAddressFixed && section->org != location->address) return false; - if (section->isAlignFixed && location->address & section->alignMask) + if (section->isAlignFixed + && ((location->address - section->alignOfs) & section->alignMask)) return false; if (location->address < freeSpace->address) @@ -183,8 +184,13 @@ static struct FreeSpace *getPlacement(struct Section const *section, space = NULL; } else if (section->isAlignFixed) { /* Move to next aligned location */ + /* Move back to alignment boundary */ + location->address -= section->alignOfs; + /* Ensure we're there (e.g. on first check) */ location->address &= ~section->alignMask; - location->address += section->alignMask + 1; + /* Go to next align boundary and add offset */ + location->address += section->alignMask + 1 + + section->alignOfs; } else { /* Any location is fine, so, next free block */ space = space->next; @@ -309,8 +315,8 @@ static void placeSection(struct Section *section) if (section->isAddressFixed) snprintf(where, 64, "at address $%04x", section->org); else if (section->isAlignFixed) - snprintf(where, 64, "with align mask %x", - ~section->alignMask); + snprintf(where, 64, "with align mask %x and offset %u", + ~section->alignMask, section->alignOfs); else strcpy(where, "anywhere"); } diff --git a/src/link/object.c b/src/link/object.c index 69043f47..360a986e 100644 --- a/src/link/object.c +++ b/src/link/object.c @@ -255,7 +255,7 @@ static void readSection(FILE *file, struct Section *section, char const *fileName, struct Section *fileSections[]) { int32_t tmp; - uint8_t type; + uint8_t byte; tryReadstr(section->name, file, "%s: Cannot read section name: %s", fileName); @@ -265,24 +265,34 @@ static void readSection(FILE *file, struct Section *section, errx(1, "\"%s\"'s section size (%d) is invalid", section->name, tmp); section->size = tmp; - tryGetc(type, file, "%s: Cannot read \"%s\"'s type: %s", + tryGetc(byte, file, "%s: Cannot read \"%s\"'s type: %s", fileName, section->name); - section->type = type & 0x7F; - section->isUnion = type >> 7; + section->type = byte & 0x7F; + section->isUnion = byte >> 7; tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s org: %s", fileName, section->name); section->isAddressFixed = tmp >= 0; - if (tmp > UINT16_MAX) - errx(1, "\"%s\"'s org' is too large (%d)", section->name, tmp); + if (tmp > UINT16_MAX) { + error("\"%s\"'s org is too large (%d)", section->name, tmp); + tmp = UINT16_MAX; + } section->org = tmp; tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s bank: %s", fileName, section->name); section->isBankFixed = tmp >= 0; section->bank = tmp; - tryGetc(tmp, file, "%s: Cannot read \"%s\"'s alignment: %s", + tryGetc(byte, file, "%s: Cannot read \"%s\"'s alignment: %s", fileName, section->name); - section->isAlignFixed = tmp != 0; - section->alignMask = (1 << tmp) - 1; + section->isAlignFixed = byte != 0; + section->alignMask = (1 << byte) - 1; + tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s alignment offset: %s", + fileName, section->name); + if (tmp > UINT16_MAX) { + error("\"%s\"'s alignment offset is too large (%d)", + section->name, tmp); + tmp = UINT16_MAX; + } + section->alignOfs = tmp; if (sect_HasData(section->type)) { /* Ensure we never allocate 0 bytes */ diff --git a/src/link/section.c b/src/link/section.c index 095ec21e..5061ed89 100644 --- a/src/link/section.c +++ b/src/link/section.c @@ -48,21 +48,28 @@ static void mergeSections(struct Section *target, struct Section *other) 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", + if ((other->org - target->alignOfs) & target->alignMask) + errx(1, "Section \"%s\" is defined with conflicting %u-byte alignment (offset %u) and address $%x", other->name, target->alignMask + 1, - other->org); + target->alignOfs, 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", + if ((target->org - other->alignOfs) & other->alignMask) + errx(1, "Section \"%s\" is defined with conflicting address $%x and %u-byte alignment (offset %u)", other->name, target->org, - other->alignMask + 1); + other->alignMask + 1, other->alignOfs); + } else if (target->isAlignFixed + && (other->alignMask & target->alignOfs) + != (target->alignMask & other->alignOfs)) { + errx(1, "Section \"%s\" is defined with conflicting %u-byte alignment (offset %u) and %u-byte alignment (offset %u)", + other->name, target->alignMask + 1, + target->alignOfs, other->alignMask + 1, + other->alignOfs); } else if (!target->isAlignFixed - || other->alignMask > target->alignMask) { + || (other->alignMask > target->alignMask)) { target->isAlignFixed = true; target->alignMask = other->alignMask; } diff --git a/src/rgbds.5 b/src/rgbds.5 index c8af1f3f..308eea64 100644 --- a/src/rgbds.5 +++ b/src/rgbds.5 @@ -94,6 +94,9 @@ REPT NumberOfSections BYTE Align ; Alignment of this section, as N bits. 0 when not specified. + LONG Ofs ; Offset relative to the alignment specified above. + ; Must be below 1 << Align. + IF (Type == ROMX) || (Type == ROM0) ; Sections that can contain data. BYTE Data[Size] ; Raw data of the section. diff --git a/test/link/section-union/align-conflict.out b/test/link/section-union/align-conflict.out index 1e4766ef..d29b7b97 100644 --- a/test/link/section-union/align-conflict.out +++ b/test/link/section-union/align-conflict.out @@ -1,6 +1,6 @@ -error: Section "conflicting alignment" is defined with conflicting 4-byte alignment and address $cafe +error: Section "conflicting alignment" is defined with conflicting 4-byte alignment (offset 0) and address $cafe --- ERROR: -(18): - Section "conflicting alignment" already declared as aligned to 4 bytes + Section "conflicting alignment" already declared as aligned to 4 bytes (offset 0) ERROR: -(18): Cannot create section "conflicting alignment" (1 errors) diff --git a/test/link/section-union/align-ofs-conflict.asm b/test/link/section-union/align-ofs-conflict.asm new file mode 100644 index 00000000..144f7e86 --- /dev/null +++ b/test/link/section-union/align-ofs-conflict.asm @@ -0,0 +1,10 @@ +IF !DEF(SECOND) +ATTRS equs ",ALIGN[3,7]" +ELSE +ATTRS equs ",ALIGN[4,14]" +ENDC + +SECTION UNION "conflicting alignment", WRAM0 ATTRS + db + + PURGE ATTRS diff --git a/test/link/section-union/align-ofs-conflict.out b/test/link/section-union/align-ofs-conflict.out new file mode 100644 index 00000000..08cc5929 --- /dev/null +++ b/test/link/section-union/align-ofs-conflict.out @@ -0,0 +1,6 @@ +error: Section "conflicting alignment" is defined with conflicting 8-byte alignment (offset 7) and 16-byte alignment (offset 14) +--- +ERROR: -(18): + Section "conflicting alignment" already declared with incompatible 3-byte alignment (offset 7) +ERROR: -(18): + Cannot create section "conflicting alignment" (1 errors) diff --git a/test/link/section-union/different-ofs.asm b/test/link/section-union/different-ofs.asm new file mode 100644 index 00000000..a288ba68 --- /dev/null +++ b/test/link/section-union/different-ofs.asm @@ -0,0 +1,10 @@ +IF !DEF(SECOND) +ATTRS equs ",ALIGN[3,7]" +ELSE +ATTRS equs ",ALIGN[3,6]" +ENDC + +SECTION UNION "conflicting alignment", WRAM0 ATTRS + db + + PURGE ATTRS diff --git a/test/link/section-union/different-ofs.out b/test/link/section-union/different-ofs.out new file mode 100644 index 00000000..c897549f --- /dev/null +++ b/test/link/section-union/different-ofs.out @@ -0,0 +1,6 @@ +error: Section "conflicting alignment" is defined with conflicting 8-byte alignment (offset 7) and 8-byte alignment (offset 6) +--- +ERROR: -(18): + Section "conflicting alignment" already declared with incompatible 3-byte alignment (offset 7) +ERROR: -(18): + Cannot create section "conflicting alignment" (1 errors) diff --git a/test/link/section-union/good/a.asm b/test/link/section-union/good/a.asm index 55f9c90a..7fe79004 100644 --- a/test/link/section-union/good/a.asm +++ b/test/link/section-union/good/a.asm @@ -11,6 +11,11 @@ SECTION UNION "c", HRAM[$FFC0] ds 5 c1:: +SECTION UNION "d", SRAM,ALIGN[8,$BA] +d1:: + +SECTION UNION "e", SRAM[$BABE] + SECTION "output 1", ROM0 dw a1,a2 ; $C00A, $C02A @@ -19,3 +24,4 @@ SECTION "output 1", ROM0 SECTION "output 3", ROM0 db BANK(banked) + dw d1,d2 ; $ABBA, $BBBA diff --git a/test/link/section-union/good/b.asm b/test/link/section-union/good/b.asm index 21828c1a..5a8031c8 100644 --- a/test/link/section-union/good/b.asm +++ b/test/link/section-union/good/b.asm @@ -12,6 +12,13 @@ b1: ; Same but in different sections now ds 5 c2:: +SECTION UNION "d", SRAM,ALIGN[12,$BBA] + ds $1000 +d2:: + +SECTION UNION "e", SRAM,ALIGN[8,$BE] + + SECTION "output 2", ROM0 dw a1,a2 dw b1,b2