Implement unionized sections in RGBASM

This touched a lot more code than initially expected, for two reasons.

First, this broke a big RGBASM assumption: that sections are always being
written to at their end. This plus other problems required touching
basically the entirety of `section.c`.

Second, I tried different solutions to solve the above problem, and along
the way I cleaned up many things around. (I believe that keeping this to
"cleanup" commits yields subpar results, and since it's boring they get
postponed anyways.)

RGBLINK support still needs to be added, but this will come next.
This commit is contained in:
ISSOtm
2020-03-20 21:02:32 +01:00
parent 46a402f7d7
commit cb52ae0f26
13 changed files with 314 additions and 162 deletions

View File

@@ -27,7 +27,6 @@
extern int32_t nLineNo; extern int32_t nLineNo;
extern uint32_t nTotalLines; extern uint32_t nTotalLines;
extern uint32_t nPC;
extern uint32_t nIFDepth; extern uint32_t nIFDepth;
extern bool skipElif; extern bool skipElif;
extern uint32_t nUnionDepth; extern uint32_t nUnionDepth;

View File

@@ -31,6 +31,8 @@ extern uint32_t ulNewMacroSize;
extern int32_t nGBGfxID; extern int32_t nGBGfxID;
extern int32_t nBinaryID; extern int32_t nBinaryID;
extern uint32_t curOffset; /* Offset into the current section */
extern struct sOptions DefaultOptions; extern struct sOptions DefaultOptions;
extern struct sOptions CurrentOptions; extern struct sOptions CurrentOptions;

View File

@@ -19,9 +19,10 @@ extern char *tzObjectname;
extern struct Section *pSectionList, *pCurrentSection; extern struct Section *pSectionList, *pCurrentSection;
void out_SetFileName(char *s); void out_SetFileName(char *s);
void out_CreatePatch(uint32_t type, struct Expression const *expr); void out_CreatePatch(uint32_t type, struct Expression const *expr,
uint32_t ofs);
bool out_CreateAssert(enum AssertionType type, struct Expression const *expr, bool out_CreateAssert(enum AssertionType type, struct Expression const *expr,
char const *message); char const *message, uint32_t ofs);
void out_WriteObject(void); void out_WriteObject(void);
#endif /* RGBDS_ASM_OUTPUT_H */ #endif /* RGBDS_ASM_OUTPUT_H */

View File

@@ -18,7 +18,8 @@ struct Expression;
struct Section { struct Section {
char *pzName; char *pzName;
enum SectionType nType; enum SectionType nType;
uint32_t nPC; bool isUnion;
uint32_t size;
uint32_t nOrg; uint32_t nOrg;
uint32_t nBank; uint32_t nBank;
uint32_t nAlign; uint32_t nAlign;
@@ -28,25 +29,26 @@ struct Section {
}; };
struct SectionSpec { struct SectionSpec {
int32_t bank; uint32_t bank;
int32_t alignment; uint32_t alignment;
}; };
struct Section *out_FindSectionByName(const char *pzName); struct Section *out_FindSectionByName(const char *pzName);
void out_NewSection(char const *pzName, uint32_t secttype, int32_t org, void out_NewSection(char const *pzName, uint32_t secttype, uint32_t org,
struct SectionSpec const *attributes); struct SectionSpec const *attributes, bool isUnion);
void out_SetLoadSection(char const *name, uint32_t secttype, int32_t org, void out_SetLoadSection(char const *name, uint32_t secttype, uint32_t org,
struct SectionSpec const *attributes); struct SectionSpec const *attributes);
void out_EndLoadSection(void); void out_EndLoadSection(void);
struct Section *sect_GetSymbolSection(void); struct Section *sect_GetSymbolSection(void);
uint32_t sect_GetOutputOffset(void);
void out_AbsByte(uint8_t b); void out_AbsByte(uint8_t b);
void out_AbsByteGroup(uint8_t const *s, int32_t length); void out_AbsByteGroup(uint8_t const *s, int32_t length);
void out_Skip(int32_t skip); void out_Skip(int32_t skip);
void out_String(char const *s); void out_String(char const *s);
void out_RelByte(struct Expression *expr); void out_RelByte(struct Expression *expr);
void out_RelBytes(struct Expression *expr, int32_t n); void out_RelBytes(struct Expression *expr, uint32_t n);
void out_RelWord(struct Expression *expr); void out_RelWord(struct Expression *expr);
void out_RelLong(struct Expression *expr); void out_RelLong(struct Expression *expr);
void out_PCRelByte(struct Expression *expr); void out_PCRelByte(struct Expression *expr);

View File

@@ -13,6 +13,8 @@
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include "asm/section.h"
#include "types.h" #include "types.h"
#define HASHSIZE (1 << 16) #define HASHSIZE (1 << 16)
@@ -30,7 +32,6 @@ enum SymbolType {
struct sSymbol { struct sSymbol {
char tzName[MAXSYMLEN + 1]; char tzName[MAXSYMLEN + 1];
enum SymbolType type; enum SymbolType type;
bool isConstant; /* Whether the symbol's value is currently known */
bool isExported; /* Whether the symbol is to be exported */ bool isExported; /* Whether the symbol is to be exported */
bool isBuiltin; /* Whether the symbol is a built-in */ bool isBuiltin; /* Whether the symbol is a built-in */
bool isReferenced; /* Whether the symbol is referenced in a RPN expr */ bool isReferenced; /* Whether the symbol is referenced in a RPN expr */
@@ -52,7 +53,9 @@ static inline bool sym_IsDefined(struct sSymbol const *sym)
static inline bool sym_IsConstant(struct sSymbol const *sym) static inline bool sym_IsConstant(struct sSymbol const *sym)
{ {
return sym->isConstant; return sym->type == SYM_EQU || sym->type == SYM_SET
|| (sym->type == SYM_LABEL && sym->pSection
&& sym->pSection->nOrg != -1);
} }
static inline bool sym_IsNumeric(struct sSymbol const *sym) static inline bool sym_IsNumeric(struct sSymbol const *sym)

View File

@@ -393,20 +393,19 @@ static void startUnion(void)
if (nUnionDepth > MAXUNIONS) if (nUnionDepth > MAXUNIONS)
fatalerror("Too many nested UNIONs"); fatalerror("Too many nested UNIONs");
unionStart[unionIndex] = nPC; unionStart[unionIndex] = curOffset;
unionSize[unionIndex] = 0; unionSize[unionIndex] = 0;
} }
static void updateUnion(void) static void updateUnion(void)
{ {
uint32_t unionIndex = nUnionDepth - 1; uint32_t unionIndex = nUnionDepth - 1;
uint32_t size = nPC - unionStart[unionIndex]; uint32_t size = curOffset - unionStart[unionIndex];
if (size > unionSize[unionIndex]) if (size > unionSize[unionIndex])
unionSize[unionIndex] = size; unionSize[unionIndex] = size;
nPC = unionStart[unionIndex]; curOffset = unionStart[unionIndex];
pCurrentSection->nPC = unionStart[unionIndex];
} }
static size_t strlenUTF8(const char *s) static size_t strlenUTF8(const char *s)
@@ -601,7 +600,8 @@ 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_WRAM0 T_SECT_VRAM T_SECT_ROMX T_SECT_ROM0 T_SECT_HRAM
%token T_SECT_WRAMX T_SECT_SRAM T_SECT_OAM %token T_SECT_WRAMX T_SECT_SRAM T_SECT_OAM
%type <macroArg> macroargs; %type <nConstValue> sectunion
%type <macroArg> macroargs
%token T_Z80_ADC T_Z80_ADD T_Z80_AND %token T_Z80_ADC T_Z80_ADD T_Z80_AND
%token T_Z80_BIT %token T_Z80_BIT
@@ -815,7 +815,8 @@ assert_type : /* empty */ { $$ = ASSERT_ERROR; }
assert : T_POP_ASSERT assert_type relocexpr assert : T_POP_ASSERT assert_type relocexpr
{ {
if (!rpn_isKnown(&$3)) { if (!rpn_isKnown(&$3)) {
if (!out_CreateAssert($2, &$3, "")) if (!out_CreateAssert($2, &$3, "",
sect_GetOutputOffset()))
yyerror("Assertion creation failed: %s", yyerror("Assertion creation failed: %s",
strerror(errno)); strerror(errno));
} else if ($3.nVal == 0) { } else if ($3.nVal == 0) {
@@ -836,7 +837,8 @@ assert : T_POP_ASSERT assert_type relocexpr
| T_POP_ASSERT assert_type relocexpr ',' string | T_POP_ASSERT assert_type relocexpr ',' string
{ {
if (!rpn_isKnown(&$3)) { if (!rpn_isKnown(&$3)) {
if (!out_CreateAssert($2, &$3, $5)) if (!out_CreateAssert($2, &$3, $5,
sect_GetOutputOffset()))
yyerror("Assertion creation failed: %s", yyerror("Assertion creation failed: %s",
strerror(errno)); strerror(errno));
} else if ($3.nVal == 0) { } else if ($3.nVal == 0) {
@@ -968,8 +970,7 @@ endu : T_POP_ENDU {
updateUnion(); updateUnion();
nUnionDepth--; nUnionDepth--;
nPC = unionStart[nUnionDepth] + unionSize[nUnionDepth]; curOffset = unionStart[nUnionDepth] + unionSize[nUnionDepth];
pCurrentSection->nPC = nPC;
} }
; ;
@@ -1418,11 +1419,14 @@ string : T_STRING {
} }
; ;
section : T_POP_SECTION string ',' sectiontype sectorg sectattrs { section : T_POP_SECTION sectunion string ',' sectiontype sectorg sectattrs {
out_NewSection($2, $4, $5, &$6); out_NewSection($3, $5, $6, &$7, $2);
} }
; ;
sectunion : /* empty */ { $$ = false; }
| T_POP_UNION { $$ = true; }
sectiontype : T_SECT_WRAM0 { $$ = SECTTYPE_WRAM0; } sectiontype : T_SECT_WRAM0 { $$ = SECTTYPE_WRAM0; }
| T_SECT_VRAM { $$ = SECTTYPE_VRAM; } | T_SECT_VRAM { $$ = SECTTYPE_VRAM; }
| T_SECT_ROMX { $$ = SECTTYPE_ROMX; } | T_SECT_ROMX { $$ = SECTTYPE_ROMX; }

View File

@@ -40,11 +40,13 @@ const size_t cldefine_entrysize = 2 * sizeof(void *);
char **cldefines; char **cldefines;
clock_t nStartClock, nEndClock; clock_t nStartClock, nEndClock;
int32_t nLineNo; uint32_t nTotalLines, nIFDepth, nUnionDepth;
uint32_t nTotalLines, nPC, nIFDepth, nUnionDepth;
bool skipElif; bool skipElif;
uint32_t unionStart[128], unionSize[128]; uint32_t unionStart[128], unionSize[128];
int32_t nLineNo;
uint32_t curOffset;
#if defined(YYDEBUG) && YYDEBUG #if defined(YYDEBUG) && YYDEBUG
extern int yydebug; extern int yydebug;
#endif #endif
@@ -525,7 +527,6 @@ int main(int argc, char *argv[])
nIFDepth = 0; nIFDepth = 0;
skipElif = true; skipElif = true;
nUnionDepth = 0; nUnionDepth = 0;
nPC = 0;
sym_Init(); sym_Init();
sym_SetExportAll(CurrentOptions.exportall); sym_SetExportAll(CurrentOptions.exportall);
fstk_Init(tzMainfile); fstk_Init(tzMainfile);

View File

@@ -187,7 +187,7 @@ static void writesection(struct Section *pSect, FILE *f)
{ {
fputstring(pSect->pzName, f); fputstring(pSect->pzName, f);
fputlong(pSect->nPC, f); fputlong(pSect->size, f);
fputc(pSect->nType, f); fputc(pSect->nType, f);
@@ -198,7 +198,7 @@ static void writesection(struct Section *pSect, FILE *f)
if (sect_HasData(pSect->nType)) { if (sect_HasData(pSect->nType)) {
struct Patch *pPatch; struct Patch *pPatch;
fwrite(pSect->tData, 1, pSect->nPC, f); fwrite(pSect->tData, 1, pSect->size, f);
fputlong(countpatches(pSect), f); fputlong(countpatches(pSect), f);
pPatch = pSect->pPatches; pPatch = pSect->pPatches;
@@ -402,7 +402,8 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
/* /*
* Allocate a new patch structure and link it into the list * Allocate a new patch structure and link it into the list
*/ */
static struct Patch *allocpatch(uint32_t type, struct Expression const *expr) static struct Patch *allocpatch(uint32_t type, struct Expression const *expr,
uint32_t ofs)
{ {
struct Patch *pPatch; struct Patch *pPatch;
@@ -418,8 +419,8 @@ static struct Patch *allocpatch(uint32_t type, struct Expression const *expr)
pPatch->nRPNSize = 0; pPatch->nRPNSize = 0;
pPatch->nType = type; pPatch->nType = type;
pPatch->nOffset = pCurrentSection->nPC;
fstk_DumpToStr(pPatch->tzFilename, sizeof(pPatch->tzFilename)); fstk_DumpToStr(pPatch->tzFilename, sizeof(pPatch->tzFilename));
pPatch->nOffset = ofs;
writerpn(pPatch->pRPN, &pPatch->nRPNSize, expr->tRPN, expr->nRPNLength); writerpn(pPatch->pRPN, &pPatch->nRPNSize, expr->tRPN, expr->nRPNLength);
assert(pPatch->nRPNSize == expr->nRPNPatchSize); assert(pPatch->nRPNSize == expr->nRPNPatchSize);
@@ -430,9 +431,9 @@ static struct Patch *allocpatch(uint32_t type, struct Expression const *expr)
/* /*
* Create a new patch (includes the rpn expr) * Create a new patch (includes the rpn expr)
*/ */
void out_CreatePatch(uint32_t type, struct Expression const *expr) void out_CreatePatch(uint32_t type, struct Expression const *expr, uint32_t ofs)
{ {
struct Patch *pPatch = allocpatch(type, expr); struct Patch *pPatch = allocpatch(type, expr, ofs);
pPatch->pNext = pCurrentSection->pPatches; pPatch->pNext = pCurrentSection->pPatches;
pCurrentSection->pPatches = pPatch; pCurrentSection->pPatches = pPatch;
@@ -442,14 +443,14 @@ void out_CreatePatch(uint32_t type, struct Expression const *expr)
* Creates an assert that will be written to the object file * Creates an assert that will be written to the object file
*/ */
bool out_CreateAssert(enum AssertionType type, struct Expression const *expr, bool out_CreateAssert(enum AssertionType type, struct Expression const *expr,
char const *message) char const *message, uint32_t ofs)
{ {
struct Assertion *assertion = malloc(sizeof(*assertion)); struct Assertion *assertion = malloc(sizeof(*assertion));
if (!assertion) if (!assertion)
return false; return false;
assertion->patch = allocpatch(type, expr); assertion->patch = allocpatch(type, expr, ofs);
assertion->section = pCurrentSection; assertion->section = pCurrentSection;
assertion->message = strdup(message); assertion->message = strdup(message);
if (!assertion->message) { if (!assertion->message) {

View File

@@ -16,16 +16,18 @@
struct SectionStackEntry { struct SectionStackEntry {
struct Section *pSection; struct Section *pSection;
struct sSymbol *pScope; /* Section's symbol scope */ struct sSymbol *pScope; /* Section's symbol scope */
uint32_t offset;
struct SectionStackEntry *pNext; struct SectionStackEntry *pNext;
}; };
struct SectionStackEntry *pSectionStack; struct SectionStackEntry *pSectionStack;
static struct Section *currentLoadSection = NULL; static struct Section *currentLoadSection = NULL;
uint32_t loadOffset = 0; /* The offset of the LOAD section within its parent */
/* /*
* A quick check to see if we have an initialized section * A quick check to see if we have an initialized section
*/ */
static void checksection(void) static inline void checksection(void)
{ {
if (pCurrentSection == NULL) if (pCurrentSection == NULL)
fatalerror("Code generation before SECTION directive"); fatalerror("Code generation before SECTION directive");
@@ -35,7 +37,7 @@ static void checksection(void)
* A quick check to see if we have an initialized section that can contain * A quick check to see if we have an initialized section that can contain
* this much initialized data * this much initialized data
*/ */
static void checkcodesection(void) static inline void checkcodesection(void)
{ {
checksection(); checksection();
@@ -49,23 +51,21 @@ static void checkcodesection(void)
/* /*
* Check if the section has grown too much. * Check if the section has grown too much.
*/ */
static void checksectionoverflow(uint32_t delta_size) static void reserveSpace(uint32_t delta_size)
{ {
uint32_t maxSize = maxsize[pCurrentSection->nType]; uint32_t maxSize = maxsize[pCurrentSection->nType];
uint32_t newSize = pCurrentSection->nPC + delta_size; uint32_t newSize = curOffset + delta_size;
if (newSize > maxSize) {
/* /*
* This check is here to trap broken code that generates * This check is here to trap broken code that generates sections that
* sections that are too big and to prevent the assembler from * are too big and to prevent the assembler from generating huge object
* generating huge object files or trying to allocate too much * files or trying to allocate too much memory.
* memory. * A check at the linking stage is still necessary.
* The real check must be done at the linking stage.
*/ */
if (newSize > maxSize)
fatalerror("Section '%s' is too big (max size = 0x%X bytes, reached 0x%X).", fatalerror("Section '%s' is too big (max size = 0x%X bytes, reached 0x%X).",
pCurrentSection->pzName, maxSize, newSize); pCurrentSection->pzName, maxSize, newSize);
} }
}
struct Section *out_FindSectionByName(const char *pzName) struct Section *out_FindSectionByName(const char *pzName)
{ {
@@ -85,7 +85,8 @@ struct Section *out_FindSectionByName(const char *pzName)
* Find a section by name and type. If it doesn't exist, create it * Find a section by name and type. If it doesn't exist, create it
*/ */
static struct Section *getSection(char const *pzName, enum SectionType type, static struct Section *getSection(char const *pzName, enum SectionType type,
int32_t org, int32_t bank, int32_t alignment) uint32_t org, uint32_t bank,
uint32_t alignment, bool isUnion)
{ {
if (bank != -1) { if (bank != -1) {
if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM
@@ -123,13 +124,99 @@ static struct Section *getSection(char const *pzName, enum SectionType type,
struct Section *pSect = out_FindSectionByName(pzName); struct Section *pSect = out_FindSectionByName(pzName);
if (pSect) { if (pSect) {
if (type == pSect->nType unsigned int nbSectErrors = 0;
&& ((uint32_t)org) == pSect->nOrg #define fail(...) \
&& ((uint32_t)bank) == pSect->nBank do { \
&& ((uint32_t)alignment == pSect->nAlign)) { yyerror(__VA_ARGS__); \
return pSect; nbSectErrors++; \
} while (0)
if (type != pSect->nType)
fail("Section \"%s\" already exists but with type %s",
pSect->pzName, typeNames[pSect->nType]);
/*
* 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);
/*
* WARNING: see comment abount assumption in
* `EndLoadSection` if modifying the following check!
*/
if (sect_HasData(type))
fail("Cannot declare ROM sections as UNION");
if (org != -1) {
/* If neither is 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
/* 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)
fail("Section \"%s\" already declared as fixed at incompatible address $%x",
pSect->pzName,
pSect->nOrg);
} else if (alignment > pSect->nAlign) {
/*
* If the section is not fixed,
* its alignment is the largest of both
*/
pSect->nAlign = alignment;
} }
fatalerror("Section already exists but with a different type"); }
/* If the section's bank is unspecified, override it */
if (pSect->nBank == -1)
pSect->nBank = bank;
/* If both specify a bank, it must be the same one */
else if (bank != -1 && pSect->nBank != bank)
fail("Section \"%s\" already declared with different bank %u",
pSect->pzName, pSect->nBank);
} else {
if (pSect->isUnion)
fail("Section \"%s\" already declared as union",
pSect->pzName);
if (org != pSect->nOrg) {
if (pSect->nOrg == -1)
fail("Section \"%s\" already declared as floating",
pSect->pzName);
else
fail("Section \"%s\" already declared as fixed at $%x",
pSect->pzName, pSect->nOrg);
}
if (bank != pSect->nBank) {
if (pSect->nBank == -1)
fail("Section \"%s\" already declared as floating bank",
pSect->pzName);
else
fail("Section \"%s\" already declared as fixed at bank %u",
pSect->pzName, pSect->nBank);
}
if (alignment != pSect->nAlign) {
if (pSect->nAlign == 0)
fail("Section \"%s\" already declared as unaligned",
pSect->pzName);
else
fail("Section \"%s\" already declared as aligned to %u bits",
pSect->pzName, pSect->nAlign);
}
}
if (nbSectErrors)
fatalerror("Cannot create section \"%s\" (%u errors)",
pSect->pzName, nbSectErrors);
#undef fail
return pSect;
} }
pSect = malloc(sizeof(*pSect)); pSect = malloc(sizeof(*pSect));
@@ -141,7 +228,8 @@ static struct Section *getSection(char const *pzName, enum SectionType type,
fatalerror("Not enough memory for sectionname"); fatalerror("Not enough memory for sectionname");
pSect->nType = type; pSect->nType = type;
pSect->nPC = 0; pSect->isUnion = isUnion;
pSect->size = 0;
pSect->nOrg = org; pSect->nOrg = org;
pSect->nBank = bank; pSect->nBank = bank;
pSect->nAlign = alignment; pSect->nAlign = alignment;
@@ -177,10 +265,7 @@ static void setSection(struct Section *pSect)
if (nUnionDepth > 0) if (nUnionDepth > 0)
fatalerror("Cannot change the section within a UNION"); fatalerror("Cannot change the section within a UNION");
nPC = (pSect != NULL) ? pSect->nPC : 0;
pPCSymbol->pSection = pSect; pPCSymbol->pSection = pSect;
pPCSymbol->isConstant = pSect && pSect->nOrg != -1;
sym_SetCurrentSymbolScope(NULL); sym_SetCurrentSymbolScope(NULL);
} }
@@ -188,24 +273,24 @@ static void setSection(struct Section *pSect)
/* /*
* Set the current section by name and type * Set the current section by name and type
*/ */
void out_NewSection(char const *pzName, uint32_t type, int32_t org, void out_NewSection(char const *pzName, uint32_t type, uint32_t org,
struct SectionSpec const *attributes) struct SectionSpec const *attributes, bool isUnion)
{ {
if (currentLoadSection) if (currentLoadSection)
fatalerror("Cannot change the section within a `LOAD` block"); fatalerror("Cannot change the section within a `LOAD` block");
struct Section *pSect = getSection(pzName, type, org, attributes->bank, struct Section *pSect = getSection(pzName, type, org, attributes->bank,
1 << attributes->alignment); 1 << attributes->alignment, isUnion);
nPC = pSect->nPC;
setSection(pSect); setSection(pSect);
curOffset = isUnion ? 0 : pSect->size;
pCurrentSection = pSect; pCurrentSection = pSect;
} }
/* /*
* Set the current section by name and type * Set the current section by name and type
*/ */
void out_SetLoadSection(char const *name, uint32_t type, int32_t org, void out_SetLoadSection(char const *name, uint32_t type, uint32_t org,
struct SectionSpec const *attributes) struct SectionSpec const *attributes)
{ {
checkcodesection(); checkcodesection();
@@ -214,9 +299,10 @@ void out_SetLoadSection(char const *name, uint32_t type, int32_t org,
fatalerror("`LOAD` blocks cannot be nested"); fatalerror("`LOAD` blocks cannot be nested");
struct Section *pSect = getSection(name, type, org, attributes->bank, struct Section *pSect = getSection(name, type, org, attributes->bank,
1 << attributes->alignment); 1 << attributes->alignment, false);
nPC = pSect->nPC; loadOffset = curOffset;
curOffset = 0; /* curOffset -= loadOffset; */
setSection(pSect); setSection(pSect);
currentLoadSection = pSect; currentLoadSection = pSect;
} }
@@ -227,8 +313,9 @@ void out_EndLoadSection(void)
yyerror("Found `ENDL` outside of a `LOAD` block"); yyerror("Found `ENDL` outside of a `LOAD` block");
currentLoadSection = NULL; currentLoadSection = NULL;
nPC = pCurrentSection->nPC;
setSection(pCurrentSection); setSection(pCurrentSection);
curOffset += loadOffset;
loadOffset = 0;
} }
struct Section *sect_GetSymbolSection(void) struct Section *sect_GetSymbolSection(void)
@@ -236,15 +323,44 @@ struct Section *sect_GetSymbolSection(void)
return currentLoadSection ? currentLoadSection : pCurrentSection; return currentLoadSection ? currentLoadSection : pCurrentSection;
} }
/* uint32_t sect_GetOutputOffset(void)
* Output an absolute byte (bypassing ROM/union checks)
*/
static void absByteBypassCheck(uint8_t b)
{ {
pCurrentSection->tData[pCurrentSection->nPC++] = b; return curOffset + loadOffset;
if (currentLoadSection) }
currentLoadSection->nPC++;
nPC++; static inline void growSection(uint32_t growth)
{
curOffset += growth;
if (curOffset > pCurrentSection->size)
pCurrentSection->size = curOffset;
if (currentLoadSection && curOffset > currentLoadSection->size)
currentLoadSection->size = curOffset;
}
static inline void writebyte(uint8_t byte)
{
pCurrentSection->tData[sect_GetOutputOffset()] = byte;
growSection(1);
}
static inline void writeword(uint16_t b)
{
writebyte(b & 0xFF);
writebyte(b >> 8);
}
static inline void writelong(uint32_t b)
{
writebyte(b & 0xFF);
writebyte(b >> 8);
writebyte(b >> 16);
writebyte(b >> 24);
}
static inline void createPatch(enum PatchType type,
struct Expression const *expr)
{
out_CreatePatch(type, expr, sect_GetOutputOffset());
} }
/* /*
@@ -253,16 +369,18 @@ static void absByteBypassCheck(uint8_t b)
void out_AbsByte(uint8_t b) void out_AbsByte(uint8_t b)
{ {
checkcodesection(); checkcodesection();
checksectionoverflow(1); reserveSpace(1);
absByteBypassCheck(b);
writebyte(b);
} }
void out_AbsByteGroup(uint8_t const *s, int32_t length) void out_AbsByteGroup(uint8_t const *s, int32_t length)
{ {
checkcodesection(); checkcodesection();
checksectionoverflow(length); reserveSpace(length);
while (length--) while (length--)
absByteBypassCheck(*s++); writebyte(*s++);
} }
/* /*
@@ -271,19 +389,17 @@ void out_AbsByteGroup(uint8_t const *s, int32_t length)
void out_Skip(int32_t skip) void out_Skip(int32_t skip)
{ {
checksection(); checksection();
checksectionoverflow(skip); reserveSpace(skip);
if (!sect_HasData(pCurrentSection->nType)) { if (!sect_HasData(pCurrentSection->nType)) {
pCurrentSection->nPC += skip; growSection(skip);
if (currentLoadSection)
currentLoadSection->nPC += skip;
nPC += skip;
} else if (nUnionDepth > 0) { } else if (nUnionDepth > 0) {
while (skip--) while (skip--)
absByteBypassCheck(CurrentOptions.fillchar); writebyte(CurrentOptions.fillchar);
} else { } else {
checkcodesection(); checkcodesection();
while (skip--) while (skip--)
absByteBypassCheck(CurrentOptions.fillchar); writebyte(CurrentOptions.fillchar);
} }
} }
@@ -293,19 +409,10 @@ void out_Skip(int32_t skip)
void out_String(char const *s) void out_String(char const *s)
{ {
checkcodesection(); checkcodesection();
checksectionoverflow(strlen(s)); reserveSpace(strlen(s));
while (*s)
absByteBypassCheck(*s++);
}
static void outputExpression(struct Expression const *expr) while (*s)
{ writebyte(*s++);
if (!rpn_isKnown(expr)) {
out_CreatePatch(PATCHTYPE_BYTE, expr);
out_AbsByte(0);
} else {
out_AbsByte(expr->nVal);
}
} }
/* /*
@@ -314,7 +421,15 @@ static void outputExpression(struct Expression const *expr)
*/ */
void out_RelByte(struct Expression *expr) void out_RelByte(struct Expression *expr)
{ {
outputExpression(expr); checkcodesection();
reserveSpace(1);
if (!rpn_isKnown(expr)) {
createPatch(PATCHTYPE_BYTE, expr);
writebyte(0);
} else {
writebyte(expr->nVal);
}
rpn_Free(expr); rpn_Free(expr);
} }
@@ -322,25 +437,20 @@ void out_RelByte(struct Expression *expr)
* Output several copies of a relocatable byte. Checking will be done to see if * Output several copies of a relocatable byte. Checking will be done to see if
* it is an absolute value in disguise. * it is an absolute value in disguise.
*/ */
void out_RelBytes(struct Expression *expr, int32_t n) void out_RelBytes(struct Expression *expr, uint32_t n)
{
while (n--)
outputExpression(expr);
rpn_Free(expr);
}
/*
* Output an absolute word
*/
static void absWord(uint16_t b)
{ {
checkcodesection(); checkcodesection();
checksectionoverflow(2); reserveSpace(n);
pCurrentSection->tData[pCurrentSection->nPC++] = b & 0xFF;
pCurrentSection->tData[pCurrentSection->nPC++] = b >> 8; while (n--) {
if (currentLoadSection) if (!rpn_isKnown(expr)) {
currentLoadSection->nPC += 2; createPatch(PATCHTYPE_BYTE, expr);
nPC += 2; writebyte(0);
} else {
writebyte(expr->nVal);
}
}
rpn_Free(expr);
} }
/* /*
@@ -349,42 +459,32 @@ static void absWord(uint16_t b)
*/ */
void out_RelWord(struct Expression *expr) void out_RelWord(struct Expression *expr)
{ {
checkcodesection();
reserveSpace(2);
if (!rpn_isKnown(expr)) { if (!rpn_isKnown(expr)) {
out_CreatePatch(PATCHTYPE_WORD, expr); createPatch(PATCHTYPE_WORD, expr);
absWord(0); writeword(0);
} else { } else {
absWord(expr->nVal); writeword(expr->nVal);
} }
rpn_Free(expr); rpn_Free(expr);
} }
/*
* Output an absolute longword
*/
static void absLong(uint32_t b)
{
checkcodesection();
checksectionoverflow(4);
pCurrentSection->tData[pCurrentSection->nPC++] = b & 0xFF;
pCurrentSection->tData[pCurrentSection->nPC++] = b >> 8;
pCurrentSection->tData[pCurrentSection->nPC++] = b >> 16;
pCurrentSection->tData[pCurrentSection->nPC++] = b >> 24;
if (currentLoadSection)
currentLoadSection->nPC += 4;
nPC += 4;
}
/* /*
* Output a relocatable longword. Checking will be done to see if * Output a relocatable longword. Checking will be done to see if
* is an absolute value in disguise. * is an absolute value in disguise.
*/ */
void out_RelLong(struct Expression *expr) void out_RelLong(struct Expression *expr)
{ {
checkcodesection();
reserveSpace(2);
if (!rpn_isKnown(expr)) { if (!rpn_isKnown(expr)) {
out_CreatePatch(PATCHTYPE_LONG, expr); createPatch(PATCHTYPE_LONG, expr);
absLong(0); writelong(0);
} else { } else {
absLong(expr->nVal); writelong(expr->nVal);
} }
rpn_Free(expr); rpn_Free(expr);
} }
@@ -396,13 +496,11 @@ void out_RelLong(struct Expression *expr)
void out_PCRelByte(struct Expression *expr) void out_PCRelByte(struct Expression *expr)
{ {
checkcodesection(); checkcodesection();
checksectionoverflow(1); reserveSpace(1);
if (!rpn_isKnown(expr) || pCurrentSection->nOrg == -1) { if (!rpn_isKnown(expr) || pCurrentSection->nOrg == -1) {
out_CreatePatch(PATCHTYPE_JR, expr); createPatch(PATCHTYPE_JR, expr);
pCurrentSection->tData[pCurrentSection->nPC++] = 0; writebyte(0);
if (currentLoadSection)
currentLoadSection->nPC++;
nPC++;
} else { } else {
/* Target is relative to the byte *after* the operand */ /* Target is relative to the byte *after* the operand */
uint16_t address = sym_GetValue(pPCSymbol) + 1; uint16_t address = sym_GetValue(pPCSymbol) + 1;
@@ -412,9 +510,9 @@ void out_PCRelByte(struct Expression *expr)
if (offset < -128 || offset > 127) { if (offset < -128 || offset > 127) {
yyerror("jr target out of reach (expected -129 < %d < 128)", yyerror("jr target out of reach (expected -129 < %d < 128)",
offset); offset);
out_AbsByte(0); writebyte(0);
} else { } else {
out_AbsByte(offset); writebyte(offset);
} }
} }
rpn_Free(expr); rpn_Free(expr);
@@ -444,7 +542,7 @@ void out_BinaryFile(char const *s)
fsize = ftell(f); fsize = ftell(f);
rewind(f); rewind(f);
checksectionoverflow(fsize); reserveSpace(fsize);
} else if (errno != ESPIPE) { } else if (errno != ESPIPE) {
yyerror("Error determining size of INCBIN file '%s': %s", s, yyerror("Error determining size of INCBIN file '%s': %s", s,
strerror(errno)); strerror(errno));
@@ -452,11 +550,8 @@ void out_BinaryFile(char const *s)
while ((byte = fgetc(f)) != EOF) { while ((byte = fgetc(f)) != EOF) {
if (fsize == -1) if (fsize == -1)
checksectionoverflow(1); growSection(1);
pCurrentSection->tData[pCurrentSection->nPC++] = byte; writebyte(byte);
if (currentLoadSection)
currentLoadSection->nPC++;
nPC++;
} }
if (ferror(f)) if (ferror(f))
@@ -493,7 +588,7 @@ void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
} }
checkcodesection(); checkcodesection();
checksectionoverflow(length); reserveSpace(length);
int32_t fsize; int32_t fsize;
@@ -524,10 +619,7 @@ void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
int byte = fgetc(f); int byte = fgetc(f);
if (byte != EOF) { if (byte != EOF) {
pCurrentSection->tData[pCurrentSection->nPC++] = byte; writebyte(byte);
if (currentLoadSection)
currentLoadSection->nPC++;
nPC++;
} else if (ferror(f)) { } else if (ferror(f)) {
yyerror("Error reading INCBIN file '%s': %s", s, yyerror("Error reading INCBIN file '%s': %s", s,
strerror(errno)); strerror(errno));
@@ -553,6 +645,7 @@ void out_PushSection(void)
pSect->pSection = pCurrentSection; pSect->pSection = pCurrentSection;
pSect->pScope = sym_GetCurrentSymbolScope(); pSect->pScope = sym_GetCurrentSymbolScope();
pSect->offset = curOffset;
pSect->pNext = pSectionStack; pSect->pNext = pSectionStack;
pSectionStack = pSect; pSectionStack = pSect;
} }
@@ -571,6 +664,8 @@ void out_PopSection(void)
setSection(pSect->pSection); setSection(pSect->pSection);
pCurrentSection = pSect->pSection; pCurrentSection = pSect->pSection;
sym_SetCurrentSymbolScope(pSect->pScope); sym_SetCurrentSymbolScope(pSect->pScope);
curOffset = pSect->offset;
pSectionStack = pSect->pNext; pSectionStack = pSect->pNext;
free(pSect); free(pSect);
} }

View File

@@ -62,7 +62,7 @@ static int32_t Callback__LINE__(struct sSymbol const *self)
static int32_t CallbackPC(struct sSymbol const *self) static int32_t CallbackPC(struct sSymbol const *self)
{ {
return self->pSection ? self->pSection->nOrg + self->pSection->nPC : 0; return self->pSection ? self->pSection->nOrg + curOffset : 0;
} }
/* /*
@@ -124,7 +124,6 @@ static struct sSymbol *createsymbol(char const *s)
if (snprintf((*ppsym)->tzName, MAXSYMLEN + 1, "%s", s) > MAXSYMLEN) if (snprintf((*ppsym)->tzName, MAXSYMLEN + 1, "%s", s) > MAXSYMLEN)
warning(WARNING_LONG_STR, "Symbol name is too long: '%s'", s); warning(WARNING_LONG_STR, "Symbol name is too long: '%s'", s);
(*ppsym)->isConstant = false;
(*ppsym)->isExported = false; (*ppsym)->isExported = false;
(*ppsym)->isBuiltin = false; (*ppsym)->isBuiltin = false;
(*ppsym)->isReferenced = false; (*ppsym)->isReferenced = false;
@@ -349,7 +348,6 @@ struct sSymbol *sym_AddEqu(char const *tzSym, int32_t value)
nsym->nValue = value; nsym->nValue = value;
nsym->type = SYM_EQU; nsym->type = SYM_EQU;
nsym->isConstant = true;
nsym->pScope = NULL; nsym->pScope = NULL;
updateSymbolFilename(nsym); updateSymbolFilename(nsym);
@@ -417,7 +415,6 @@ struct sSymbol *sym_AddSet(char const *tzSym, int32_t value)
nsym->nValue = value; nsym->nValue = value;
nsym->type = SYM_SET; nsym->type = SYM_SET;
nsym->isConstant = true;
nsym->pScope = NULL; nsym->pScope = NULL;
updateSymbolFilename(nsym); updateSymbolFilename(nsym);
@@ -479,9 +476,8 @@ struct sSymbol *sym_AddReloc(char const *tzSym)
nsym->tzFileName, nsym->nFileLine); nsym->tzFileName, nsym->nFileLine);
/* If the symbol already exists as a ref, just "take over" it */ /* If the symbol already exists as a ref, just "take over" it */
nsym->nValue = nPC; nsym->nValue = curOffset;
nsym->type = SYM_LABEL; nsym->type = SYM_LABEL;
nsym->isConstant = pCurrentSection && pCurrentSection->nOrg != -1;
if (exportall) if (exportall)
nsym->isExported = true; nsym->isExported = true;

View File

@@ -0,0 +1,37 @@
SECTION UNION "test", WRAM0
Base:
ds $1000
SECTION UNION "test", WRAM0,ALIGN[8]
ds 42
Plus42:
SECTION UNION "test", WRAM0,ALIGN[4]
SECTION UNION "test", WRAM0[$C000]
; Since the section's base address is known, the labels are constant now
ds $1000 ; This shouldn't overflow
End:
SECTION UNION "test", WRAM0,ALIGN[9]
check_label: MACRO
EXPECTED equ \2
IF \1 == EXPECTED
RESULT equs "OK!"
ELSE
RESULT equs "expected {EXPECTED}"
ENDC
PURGE EXPECTED
PRINTT "\1 is at {\1} ({RESULT})\n"
PURGE RESULT
ENDM
check_label Base, $C000
check_label Plus42, $C000 + 42
check_label End, $D000
SECTION "test", WRAM0 ; Don't allow creating a section that's not a union!

View File

@@ -0,0 +1,8 @@
ERROR: section-union.asm(37):
Section "test" already declared as union
ERROR: section-union.asm(37):
Section "test" already declared as fixed at $c000
ERROR: section-union.asm(37):
Section "test" already declared as aligned to 256 bits
ERROR: section-union.asm(37):
Cannot create section "test" (3 errors)

View File

@@ -0,0 +1,3 @@
Base is at $C000 (OK!)
Plus42 is at $C02A (OK!)
End is at $D000 (OK!)