mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Implement unionized sections in RGBLINK
This commit is contained in:
@@ -37,6 +37,7 @@ struct Section {
|
|||||||
char *name;
|
char *name;
|
||||||
uint16_t size;
|
uint16_t size;
|
||||||
enum SectionType type;
|
enum SectionType type;
|
||||||
|
bool isUnion;
|
||||||
bool isAddressFixed;
|
bool isAddressFixed;
|
||||||
uint16_t org;
|
uint16_t org;
|
||||||
bool isBankFixed;
|
bool isBankFixed;
|
||||||
@@ -50,6 +51,7 @@ struct Section {
|
|||||||
struct Symbol **fileSymbols;
|
struct Symbol **fileSymbols;
|
||||||
uint32_t nbSymbols;
|
uint32_t nbSymbols;
|
||||||
struct Symbol const **symbols;
|
struct Symbol const **symbols;
|
||||||
|
struct Section *nextu; /* The next "component" of this unionized sect */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
#define RGBDS_OBJECT_VERSION_STRING "RGB%1hhu"
|
#define RGBDS_OBJECT_VERSION_STRING "RGB%1hhu"
|
||||||
#define RGBDS_OBJECT_VERSION_NUMBER (uint8_t)9
|
#define RGBDS_OBJECT_VERSION_NUMBER (uint8_t)9
|
||||||
#define RGBDS_OBJECT_REV 2
|
#define RGBDS_OBJECT_REV 3
|
||||||
|
|
||||||
enum AssertionType {
|
enum AssertionType {
|
||||||
ASSERT_WARN,
|
ASSERT_WARN,
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ static void writesection(struct Section *pSect, FILE *f)
|
|||||||
|
|
||||||
fputlong(pSect->size, f);
|
fputlong(pSect->size, f);
|
||||||
|
|
||||||
fputc(pSect->nType, f);
|
fputc(pSect->nType | pSect->isUnion << 7, f);
|
||||||
|
|
||||||
fputlong(pSect->nOrg, f);
|
fputlong(pSect->nOrg, f);
|
||||||
fputlong(pSect->nBank, f);
|
fputlong(pSect->nBank, f);
|
||||||
|
|||||||
@@ -245,6 +245,7 @@ static void readSection(FILE *file, struct Section *section,
|
|||||||
char const *fileName)
|
char const *fileName)
|
||||||
{
|
{
|
||||||
int32_t tmp;
|
int32_t tmp;
|
||||||
|
uint8_t type;
|
||||||
|
|
||||||
tryReadstr(section->name, file, "%s: Cannot read section name: %s",
|
tryReadstr(section->name, file, "%s: Cannot read section name: %s",
|
||||||
fileName);
|
fileName);
|
||||||
@@ -254,8 +255,10 @@ static void readSection(FILE *file, struct Section *section,
|
|||||||
errx(1, "\"%s\"'s section size (%d) is invalid", section->name,
|
errx(1, "\"%s\"'s section size (%d) is invalid", section->name,
|
||||||
tmp);
|
tmp);
|
||||||
section->size = 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);
|
fileName, section->name);
|
||||||
|
section->type = type & 0x7F;
|
||||||
|
section->isUnion = type >> 7;
|
||||||
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s org: %s",
|
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s org: %s",
|
||||||
fileName, section->name);
|
fileName, section->name);
|
||||||
section->isAddressFixed = tmp >= 0;
|
section->isAddressFixed = tmp >= 0;
|
||||||
@@ -358,6 +361,14 @@ static void readAssertion(FILE *file, struct Assertion *assert,
|
|||||||
fileName);
|
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
|
* Reads an object file of any supported format
|
||||||
* @param fileName The filename to report for errors
|
* @param fileName The filename to report for errors
|
||||||
@@ -448,6 +459,7 @@ void obj_ReadFile(char const *fileName)
|
|||||||
|
|
||||||
if (!section)
|
if (!section)
|
||||||
err(1, "%s: Couldn't create new section", fileName);
|
err(1, "%s: Couldn't create new section", fileName);
|
||||||
|
section->nextu = NULL;
|
||||||
readSection(file, section, fileName);
|
readSection(file, section, fileName);
|
||||||
section->fileSymbols = fileSymbols;
|
section->fileSymbols = fileSymbols;
|
||||||
|
|
||||||
@@ -472,9 +484,10 @@ void obj_ReadFile(char const *fileName)
|
|||||||
if (sectionID == -1) {
|
if (sectionID == -1) {
|
||||||
fileSymbols[i]->section = NULL;
|
fileSymbols[i]->section = NULL;
|
||||||
} else {
|
} else {
|
||||||
fileSymbols[i]->section = fileSections[sectionID];
|
|
||||||
/* Give the section a pointer to the symbol as well */
|
/* Give the section a pointer to the symbol as well */
|
||||||
linkSymToSect(fileSymbols[i], fileSections[sectionID]);
|
linkSymToSect(fileSymbols[i], fileSections[sectionID]);
|
||||||
|
fileSymbols[i]->section =
|
||||||
|
getMainSection(fileSections[sectionID]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -117,6 +117,24 @@ static uint32_t getRPNByte(uint8_t const **expression, int32_t *size,
|
|||||||
return *(*expression)++;
|
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.
|
* Compute a patch's value from its RPN string.
|
||||||
* @param patch The patch to compute the value of
|
* @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,
|
value |= getRPNByte(&expression, &size,
|
||||||
patch->fileName) << shift;
|
patch->fileName) << shift;
|
||||||
|
|
||||||
symbol = section->fileSymbols[value];
|
value = getSymbol(section->fileSymbols, value,
|
||||||
|
patch->fileName)->section->bank;
|
||||||
/* 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;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_BANK_SECT:
|
case RPN_BANK_SECT:
|
||||||
@@ -305,17 +312,8 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
|||||||
value |= getRPNByte(&expression, &size,
|
value |= getRPNByte(&expression, &size,
|
||||||
patch->fileName) << shift;
|
patch->fileName) << shift;
|
||||||
|
|
||||||
symbol = section->fileSymbols[value];
|
symbol = getSymbol(section->fileSymbols, value,
|
||||||
|
patch->fileName);
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strcmp(symbol->name, "@")) {
|
if (!strcmp(symbol->name, "@")) {
|
||||||
value = section->org + patch->offset;
|
value = section->org + patch->offset;
|
||||||
@@ -387,10 +385,8 @@ void patch_CheckAssertions(struct Assertion *assert)
|
|||||||
* @param section The section to patch
|
* @param section The section to patch
|
||||||
* @param arg Ignored callback arg
|
* @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))
|
if (!sect_HasData(section->type))
|
||||||
return;
|
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)
|
void patch_ApplyPatches(void)
|
||||||
{
|
{
|
||||||
initRPNStack();
|
initRPNStack();
|
||||||
|
|||||||
@@ -36,17 +36,76 @@ void sect_ForEach(void (*callback)(struct Section *, void *), void *arg)
|
|||||||
hash_ForEach(sections, forEach, &callbackArg);
|
hash_ForEach(sections, forEach, &callbackArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mergeSections(struct Section *target, struct Section *other)
|
||||||
|
{
|
||||||
|
if (other->isAddressFixed) {
|
||||||
|
if (target->isAddressFixed) {
|
||||||
|
if (target->org != other->org)
|
||||||
|
errx(1, "Section \"%s\" is defined with conflicting addresses $%x and $%x",
|
||||||
|
other->name, target->org, other->org);
|
||||||
|
} else if (target->isAlignFixed) {
|
||||||
|
if (other->org & target->alignMask)
|
||||||
|
errx(1, "Section \"%s\" is defined with conflicting %u-byte alignment and address $%x",
|
||||||
|
other->name, target->alignMask + 1,
|
||||||
|
other->org);
|
||||||
|
}
|
||||||
|
target->isAddressFixed = true;
|
||||||
|
target->org = other->org;
|
||||||
|
} else if (other->isAlignFixed) {
|
||||||
|
if (target->isAddressFixed) {
|
||||||
|
if (target->org & other->alignMask)
|
||||||
|
errx(1, "Section \"%s\" is defined with conflicting address $%x and %u-byte alignment",
|
||||||
|
other->name, target->org,
|
||||||
|
other->alignMask + 1);
|
||||||
|
} else if (!target->isAlignFixed
|
||||||
|
|| other->alignMask > target->alignMask) {
|
||||||
|
target->isAlignFixed = true;
|
||||||
|
target->alignMask = other->alignMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (other->isBankFixed) {
|
||||||
|
if (!target->isBankFixed) {
|
||||||
|
target->isBankFixed = true;
|
||||||
|
target->bank = other->bank;
|
||||||
|
} else if (target->bank != other->bank) {
|
||||||
|
errx(1, "Section \"%s\" is defined with conflicting banks %u and %u",
|
||||||
|
other->name, target->bank, other->bank);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (other->size > target->size)
|
||||||
|
target->size = other->size;
|
||||||
|
|
||||||
|
target->nextu = other;
|
||||||
|
}
|
||||||
|
|
||||||
void sect_AddSection(struct Section *section)
|
void sect_AddSection(struct Section *section)
|
||||||
{
|
{
|
||||||
/* Check if the section already exists */
|
/* Check if the section already exists */
|
||||||
if (hash_GetElement(sections, section->name))
|
struct Section *other = hash_GetElement(sections, section->name);
|
||||||
errx(1, "Section name \"%s\" is already in use", section->name);
|
|
||||||
|
|
||||||
/* If not, add it */
|
if (other) {
|
||||||
bool collided = hash_AddElement(sections, section->name, section);
|
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)
|
if (beVerbose && collided)
|
||||||
warnx("Section hashmap collision occurred!");
|
warnx("Section hashmap collision occurred!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Section *sect_GetSection(char const *name)
|
struct Section *sect_GetSection(char const *name)
|
||||||
|
|||||||
21
test/link/section-union/a.asm
Normal file
21
test/link/section-union/a.asm
Normal file
@@ -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)
|
||||||
18
test/link/section-union/b.asm
Normal file
18
test/link/section-union/b.asm
Normal file
@@ -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
|
||||||
BIN
test/link/section-union/ref.out.bin
Normal file
BIN
test/link/section-union/ref.out.bin
Normal file
Binary file not shown.
9
test/link/section-union/script.link
Normal file
9
test/link/section-union/script.link
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
ROM0
|
||||||
|
"output 1"
|
||||||
|
"output 2"
|
||||||
|
"output 3"
|
||||||
|
WRAM0
|
||||||
|
"a" ; $C000
|
||||||
|
WRAMX 1
|
||||||
|
ORG $DAB0
|
||||||
|
"b"
|
||||||
@@ -71,12 +71,20 @@ for i in *.asm; do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# This test does its own thing
|
# These tests do their own thing
|
||||||
|
|
||||||
$RGBASM -o $otemp high-low/a.asm
|
$RGBASM -o $otemp high-low/a.asm
|
||||||
$RGBLINK -o $gbtemp $otemp
|
$RGBLINK -o $gbtemp $otemp
|
||||||
$RGBASM -o $otemp high-low/b.asm
|
$RGBASM -o $otemp high-low/b.asm
|
||||||
$RGBLINK -o $gbtemp2 $otemp
|
$RGBLINK -o $gbtemp2 $otemp
|
||||||
i="high-low.asm" tryDiff $gbtemp $gbtemp2
|
i="high-low.asm" tryCmp $gbtemp $gbtemp2
|
||||||
|
rc=$(($? || $rc))
|
||||||
|
|
||||||
|
$RGBASM -o $otemp section-union/a.asm
|
||||||
|
$RGBASM -o $gbtemp2 section-union/b.asm
|
||||||
|
$RGBLINK -o $gbtemp -l section-union/script.link $otemp $gbtemp2
|
||||||
|
dd if=$gbtemp count=1 bs=$(printf %s $(wc -c < section-union/ref.out.bin)) > $otemp 2>/dev/null
|
||||||
|
i="section-union.asm" tryCmp section-union/ref.out.bin $otemp
|
||||||
rc=$(($? || $rc))
|
rc=$(($? || $rc))
|
||||||
|
|
||||||
rm -f $otemp $gbtemp $gbtemp2 $outtemp
|
rm -f $otemp $gbtemp $gbtemp2 $outtemp
|
||||||
|
|||||||
Reference in New Issue
Block a user