mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-22 19:22:05 +00:00
Add section fragments
Fixes #517, and hopefully enables RGBDS as a SDCC back-end
This commit is contained in:
@@ -496,6 +496,7 @@ static void strsubUTF8(char *dest, const char *src, uint32_t pos, uint32_t len)
|
||||
char tzString[MAXSTRLEN + 1];
|
||||
struct Expression sVal;
|
||||
int32_t nConstValue;
|
||||
enum SectionModifier sectMod;
|
||||
struct SectionSpec sectSpec;
|
||||
struct MacroArgs *macroArg;
|
||||
enum AssertionType assertType;
|
||||
@@ -570,7 +571,7 @@ static void strsubUTF8(char *dest, const char *src, uint32_t pos, uint32_t len)
|
||||
%token T_POP_IF T_POP_ELIF T_POP_ELSE T_POP_ENDC
|
||||
%token T_POP_EXPORT T_POP_GLOBAL T_POP_XDEF
|
||||
%token T_POP_DB T_POP_DS T_POP_DW T_POP_DL
|
||||
%token T_POP_SECTION
|
||||
%token T_POP_SECTION T_POP_FRAGMENT
|
||||
%token T_POP_RB
|
||||
%token T_POP_RW
|
||||
%token T_POP_RL
|
||||
@@ -600,7 +601,7 @@ 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 <nConstValue> sectunion
|
||||
%type <sectMod> sectmod
|
||||
%type <macroArg> macroargs
|
||||
|
||||
%token T_Z80_ADC T_Z80_ADD T_Z80_AND
|
||||
@@ -1438,13 +1439,14 @@ string : T_STRING {
|
||||
}
|
||||
;
|
||||
|
||||
section : T_POP_SECTION sectunion string ',' sectiontype sectorg sectattrs {
|
||||
section : T_POP_SECTION sectmod string ',' sectiontype sectorg sectattrs {
|
||||
out_NewSection($3, $5, $6, &$7, $2);
|
||||
}
|
||||
;
|
||||
|
||||
sectunion : /* empty */ { $$ = false; }
|
||||
| T_POP_UNION { $$ = true; }
|
||||
sectmod : /* empty */ { $$ = SECTION_NORMAL; }
|
||||
| T_POP_UNION { $$ = SECTION_UNION; }
|
||||
| T_POP_FRAGMENT{ $$ = SECTION_FRAGMENT; }
|
||||
;
|
||||
|
||||
sectiontype : T_SECT_WRAM0 { $$ = SECTTYPE_WRAM0; }
|
||||
|
||||
@@ -442,6 +442,7 @@ const struct sLexInitString lexer_strings[] = {
|
||||
|
||||
{"def", T_OP_DEF},
|
||||
|
||||
{"fragment", T_POP_FRAGMENT},
|
||||
{"bank", T_OP_BANK},
|
||||
{"align", T_OP_ALIGN},
|
||||
|
||||
|
||||
@@ -176,7 +176,10 @@ static void writesection(struct Section const *pSect, FILE *f)
|
||||
|
||||
fputlong(pSect->size, f);
|
||||
|
||||
fputc(pSect->nType | pSect->isUnion << 7, f);
|
||||
bool isUnion = pSect->modifier == SECTION_UNION;
|
||||
bool isFragment = pSect->modifier == SECTION_FRAGMENT;
|
||||
|
||||
fputc(pSect->nType | isUnion << 7 | isFragment << 6, f);
|
||||
|
||||
fputlong(pSect->nOrg, f);
|
||||
fputlong(pSect->nBank, f);
|
||||
|
||||
@@ -367,7 +367,6 @@ This tells the assembler what kind of information follows and, if it is code, wh
|
||||
.Pp
|
||||
.Ar name
|
||||
is a string enclosed in double quotes, and can be a new name or the name of an existing section.
|
||||
All sections assembled at the same time that have the same name are considered to be the same section, and their code is put together in the object file generated by the assembler.
|
||||
If the type doesn't match, an error occurs.
|
||||
All other sections must have a unique name, even in different source files, or the linker will treat it as an error.
|
||||
.Pp
|
||||
@@ -655,7 +654,7 @@ The same unionized section (= having the same name) can be declared several time
|
||||
invocation, and across several invocations.
|
||||
Different declarations are treated and merged identically whether within the same invocation, or different ones.
|
||||
.It
|
||||
A section cannot be declared both as unionized or non-unionized.
|
||||
If one section has been declared as unionized, all sections with the same name must be declared unionized as well.
|
||||
.It
|
||||
All declarations must have the same type.
|
||||
For example, even if
|
||||
@@ -679,6 +678,49 @@ or
|
||||
Different declarations of the same unionized section are not appended, but instead overlaid on top of eachother, just like
|
||||
.Sx Unions .
|
||||
Similarly, the size of an unionized section is the largest of all its declarations.
|
||||
.Ss Section Fragments
|
||||
Section fragments are sections with a small twist: when several of the same name are encountered, they are concatenated instead of producing an error.
|
||||
This works within the same file (paralleling the behavior "plain" sections has in previous versions), but also across object files.
|
||||
However, similarly to
|
||||
.Sx Unionized Sections ,
|
||||
some rules must be followed:
|
||||
.Bl -bullet -offset indent
|
||||
.It
|
||||
If one section has been declared as fragment, all sections with the same name must be declared fragments as well.
|
||||
.It
|
||||
All declarations must have the same type.
|
||||
For example, even if
|
||||
.Xr rgblink 1 Ap s
|
||||
.Fl w
|
||||
flag is used,
|
||||
.Ic WRAM0
|
||||
and
|
||||
.Ic WRAMX
|
||||
types are still considered different.
|
||||
.It
|
||||
Different constraints (alignment, bank, etc.) can be specified for each unionized section declaration, but they must all be compatible.
|
||||
For example, alignment must be compatible with any fixed address, all specified banks must be the same, etc.
|
||||
.It
|
||||
A section fragment may not be unionized; after all, that wouldn't make much sense.
|
||||
.El
|
||||
.Pp
|
||||
When RGBASM merges two fragments, the one encountered later is appended to the one encountered earlier.
|
||||
.Pp
|
||||
When RGBLINK merges two fragments, the one whose file was specified last is appended to the one whose file was specified first.
|
||||
For example, assuming
|
||||
.Ql bar.o ,
|
||||
.Ql baz.o ,
|
||||
and
|
||||
.Ql foo.o
|
||||
all contain a fragment with the same name, the command
|
||||
.Dl rgblink -o rom.gb baz.o foo.o bar.o
|
||||
would produce the fragment from
|
||||
.Ql baz.o
|
||||
first, followed by the one from
|
||||
.Ql foo.o ,
|
||||
and the one from
|
||||
.Ql bar.o
|
||||
last.
|
||||
.Sh SYMBOLS
|
||||
.Pp
|
||||
RGBDS supports several types of symbols:
|
||||
|
||||
@@ -88,7 +88,7 @@ struct Section *out_FindSectionByName(const char *pzName)
|
||||
*/
|
||||
static struct Section *getSection(char const *pzName, enum SectionType type,
|
||||
uint32_t org, struct SectionSpec const *attrs,
|
||||
bool isUnion)
|
||||
enum SectionModifier mod)
|
||||
{
|
||||
#define mask(align) ((1 << (align)) - 1)
|
||||
uint32_t bank = attrs->bank;
|
||||
@@ -150,17 +150,17 @@ static struct Section *getSection(char const *pzName, enum SectionType type,
|
||||
fail("Section \"%s\" already exists but with type %s",
|
||||
pSect->pzName, typeNames[pSect->nType]);
|
||||
|
||||
if (pSect->modifier != mod)
|
||||
fail("Section \"%s\" already declared as %s section",
|
||||
pSect->pzName, sectionModNames[pSect->modifier]);
|
||||
/*
|
||||
* 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);
|
||||
if (mod == SECTION_UNION) {
|
||||
/*
|
||||
* WARNING: see comment abount assumption in
|
||||
* WARNING: see comment about assumption in
|
||||
* `EndLoadSection` if modifying the following check!
|
||||
*/
|
||||
if (sect_HasData(type))
|
||||
@@ -210,10 +210,11 @@ static struct Section *getSection(char const *pzName, enum SectionType type,
|
||||
else if (bank != -1 && pSect->nBank != bank)
|
||||
fail("Section \"%s\" already declared with different bank %" PRIu32,
|
||||
pSect->pzName, pSect->nBank);
|
||||
} else {
|
||||
if (pSect->isUnion)
|
||||
fail("Section \"%s\" already declared as union",
|
||||
pSect->pzName);
|
||||
} else { /* Section fragments are handled identically in RGBASM */
|
||||
/* However, concaternating non-fragments will be made an error */
|
||||
if (pSect->modifier != SECTION_FRAGMENT || mod != SECTION_FRAGMENT)
|
||||
warning(WARNING_OBSOLETE, "Concatenation of non-fragment sections is deprecated");
|
||||
|
||||
if (org != pSect->nOrg) {
|
||||
if (pSect->nOrg == -1)
|
||||
fail("Section \"%s\" already declared as floating",
|
||||
@@ -257,7 +258,7 @@ static struct Section *getSection(char const *pzName, enum SectionType type,
|
||||
fatalerror("Not enough memory for sectionname");
|
||||
|
||||
pSect->nType = type;
|
||||
pSect->isUnion = isUnion;
|
||||
pSect->modifier = mod;
|
||||
pSect->size = 0;
|
||||
pSect->nOrg = org;
|
||||
pSect->nBank = bank;
|
||||
@@ -303,15 +304,15 @@ static void changeSection(void)
|
||||
* Set the current section by name and type
|
||||
*/
|
||||
void out_NewSection(char const *pzName, uint32_t type, uint32_t org,
|
||||
struct SectionSpec const *attribs, bool isUnion)
|
||||
struct SectionSpec const *attribs, enum SectionModifier mod)
|
||||
{
|
||||
if (currentLoadSection)
|
||||
fatalerror("Cannot change the section within a `LOAD` block");
|
||||
|
||||
struct Section *pSect = getSection(pzName, type, org, attribs, isUnion);
|
||||
struct Section *pSect = getSection(pzName, type, org, attribs, mod);
|
||||
|
||||
changeSection();
|
||||
curOffset = isUnion ? 0 : pSect->size;
|
||||
curOffset = mod == SECTION_UNION ? 0 : pSect->size;
|
||||
pCurrentSection = pSect;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user