mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Allow specifying offset in addition to alignment
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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; */
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
10
test/link/section-union/align-ofs-conflict.asm
Normal file
10
test/link/section-union/align-ofs-conflict.asm
Normal file
@@ -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
|
||||
6
test/link/section-union/align-ofs-conflict.out
Normal file
6
test/link/section-union/align-ofs-conflict.out
Normal file
@@ -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)
|
||||
10
test/link/section-union/different-ofs.asm
Normal file
10
test/link/section-union/different-ofs.asm
Normal file
@@ -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
|
||||
6
test/link/section-union/different-ofs.out
Normal file
6
test/link/section-union/different-ofs.out
Normal file
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user