ref: b0ec8468e65c1fdc17102fcfd29747ef29535f4f
parent: e82ad21704bf4df1806195d30d8fa2bf65f0d812
author: ISSOtm <[email protected]>
date: Tue Apr 7 20:40:41 EDT 2020
Allow specifying offset in addition to alignment
--- a/include/asm/section.h
+++ b/include/asm/section.h
@@ -22,7 +22,8 @@
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 SectionSpec {
uint32_t bank;
- uint32_t alignment;
+ uint8_t alignment;
+ uint16_t alignOfs;
};
struct Section *out_FindSectionByName(const char *pzName);
--- a/include/link/section.h
+++ b/include/link/section.h
@@ -50,6 +50,7 @@
uint32_t bank;
bool isAlignFixed;
uint16_t alignMask;
+ uint16_t alignOfs;
uint8_t *data; /* Array of size `size`*/
uint32_t nbPatches;
struct Patch *patches;
--- a/src/asm/asmy.y
+++ b/src/asm/asmy.y
@@ -1425,6 +1425,7 @@
sectunion : /* empty */ { $$ = false; }
| T_POP_UNION { $$ = true; }
+;
sectiontype : T_SECT_WRAM0 { $$ = SECTTYPE_WRAM0; }
| T_SECT_VRAM { $$ = SECTTYPE_VRAM; }
@@ -1449,14 +1450,28 @@
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 */
--- a/src/asm/output.c
+++ b/src/asm/output.c
@@ -181,6 +181,7 @@
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);
--- a/src/asm/section.c
+++ b/src/asm/section.c
@@ -85,9 +85,14 @@
* 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 @@
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,14 +164,16 @@
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;
@@ -167,12 +180,18 @@
} 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,
@@ -179,6 +198,7 @@
* 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 @@
pSect->nOrg = org;
pSect->nBank = bank;
pSect->nAlign = alignment;
+ pSect->alignOfs = alignOffset;
pSect->pNext = pSectionList;
pSect->pPatches = NULL;
@@ -261,6 +282,7 @@
pSectionList = pSect;
return pSect;
+#undef mask
}
/*
@@ -280,13 +302,12 @@
* 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,7 +318,7 @@
* 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();
@@ -304,8 +325,7 @@
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; */
--- a/src/link/assign.c
+++ b/src/link/assign.c
@@ -132,7 +132,8 @@
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 @@
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 @@
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");
}
--- a/src/link/object.c
+++ b/src/link/object.c
@@ -255,7 +255,7 @@
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 @@
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 */
--- a/src/link/section.c
+++ b/src/link/section.c
@@ -48,21 +48,28 @@
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;
}
--- a/src/rgbds.5
+++ b/src/rgbds.5
@@ -94,6 +94,9 @@
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.
--- 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)
--- /dev/null
+++ b/test/link/section-union/align-ofs-conflict.asm
@@ -1,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
--- /dev/null
+++ b/test/link/section-union/align-ofs-conflict.out
@@ -1,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)
--- /dev/null
+++ b/test/link/section-union/different-ofs.asm
@@ -1,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
--- /dev/null
+++ b/test/link/section-union/different-ofs.out
@@ -1,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)
--- a/test/link/section-union/good/a.asm
+++ b/test/link/section-union/good/a.asm
@@ -11,7 +11,12 @@
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
dw b1,b2 ; $DABA, $DAB2
@@ -19,3 +24,4 @@
SECTION "output 3", ROM0
db BANK(banked)
+ dw d1,d2 ; $ABBA, $BBBA
--- a/test/link/section-union/good/b.asm
+++ b/test/link/section-union/good/b.asm
@@ -12,6 +12,13 @@
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