ref: baeec2315fd4acab9f3f1021ba98a731ba0de2b9
parent: 46a402f7d76dd90b668bc79c9d11b0f9f0c98e44
parent: 92134d7684dd4cf4753eac184ad940f80acee943
author: Eldred Habert <[email protected]>
date: Sun Mar 22 07:21:24 EDT 2020
Merge pull request #495 from ISSOtm/sectunion Implement unionized sections
--- a/include/asm/asm.h
+++ b/include/asm/asm.h
@@ -27,7 +27,6 @@
extern int32_t nLineNo;
extern uint32_t nTotalLines;
-extern uint32_t nPC;
extern uint32_t nIFDepth;
extern bool skipElif;
extern uint32_t nUnionDepth;
--- a/include/asm/main.h
+++ b/include/asm/main.h
@@ -31,6 +31,8 @@
extern int32_t nGBGfxID;
extern int32_t nBinaryID;
+extern uint32_t curOffset; /* Offset into the current section */
+
extern struct sOptions DefaultOptions;
extern struct sOptions CurrentOptions;
--- a/include/asm/output.h
+++ b/include/asm/output.h
@@ -19,9 +19,10 @@
extern struct Section *pSectionList, *pCurrentSection;
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,
- char const *message);
+ char const *message, uint32_t ofs);
void out_WriteObject(void);
#endif /* RGBDS_ASM_OUTPUT_H */
--- a/include/asm/section.h
+++ b/include/asm/section.h
@@ -18,7 +18,8 @@
struct Section {
char *pzName;
enum SectionType nType;
- uint32_t nPC;
+ bool isUnion;
+ uint32_t size;
uint32_t nOrg;
uint32_t nBank;
uint32_t nAlign;
@@ -28,18 +29,19 @@
};
struct SectionSpec {
- int32_t bank;
- int32_t alignment;
+ uint32_t bank;
+ uint32_t alignment;
};
struct Section *out_FindSectionByName(const char *pzName);
-void out_NewSection(char const *pzName, uint32_t secttype, int32_t org,
- struct SectionSpec const *attributes);
-void out_SetLoadSection(char const *name, uint32_t secttype, int32_t org,
+void out_NewSection(char const *pzName, uint32_t secttype, uint32_t org,
+ struct SectionSpec const *attributes, bool isUnion);
+void out_SetLoadSection(char const *name, uint32_t secttype, uint32_t org,
struct SectionSpec const *attributes);
void out_EndLoadSection(void);
struct Section *sect_GetSymbolSection(void);
+uint32_t sect_GetOutputOffset(void);
void out_AbsByte(uint8_t b);
void out_AbsByteGroup(uint8_t const *s, int32_t length);
@@ -46,7 +48,7 @@
void out_Skip(int32_t skip);
void out_String(char const *s);
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_RelLong(struct Expression *expr);
void out_PCRelByte(struct Expression *expr);
--- a/include/asm/symbol.h
+++ b/include/asm/symbol.h
@@ -13,6 +13,8 @@
#include <stdint.h>
#include <string.h>
+#include "asm/section.h"
+
#include "types.h"
#define HASHSIZE (1 << 16)
@@ -30,7 +32,6 @@
struct sSymbol {
char tzName[MAXSYMLEN + 1];
enum SymbolType type;
- bool isConstant; /* Whether the symbol's value is currently known */
bool isExported; /* Whether the symbol is to be exported */
bool isBuiltin; /* Whether the symbol is a built-in */
bool isReferenced; /* Whether the symbol is referenced in a RPN expr */
@@ -52,7 +53,9 @@
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)
--- a/include/link/section.h
+++ b/include/link/section.h
@@ -37,6 +37,7 @@
char *name;
uint16_t size;
enum SectionType type;
+ bool isUnion;
bool isAddressFixed;
uint16_t org;
bool isBankFixed;
@@ -50,6 +51,7 @@
struct Symbol **fileSymbols;
uint32_t nbSymbols;
struct Symbol const **symbols;
+ struct Section *nextu; /* The next "component" of this unionized sect */
};
/*
--- a/include/linkdefs.h
+++ b/include/linkdefs.h
@@ -14,7 +14,7 @@
#define RGBDS_OBJECT_VERSION_STRING "RGB%1hhu"
#define RGBDS_OBJECT_VERSION_NUMBER (uint8_t)9
-#define RGBDS_OBJECT_REV 2
+#define RGBDS_OBJECT_REV 3
enum AssertionType {
ASSERT_WARN,
--- a/src/asm/asmy.y
+++ b/src/asm/asmy.y
@@ -393,7 +393,7 @@
if (nUnionDepth > MAXUNIONS)
fatalerror("Too many nested UNIONs");
- unionStart[unionIndex] = nPC;
+ unionStart[unionIndex] = curOffset;
unionSize[unionIndex] = 0;
}
@@ -400,13 +400,12 @@
static void updateUnion(void)
{
uint32_t unionIndex = nUnionDepth - 1;
- uint32_t size = nPC - unionStart[unionIndex];
+ uint32_t size = curOffset - unionStart[unionIndex];
if (size > unionSize[unionIndex])
unionSize[unionIndex] = size;
- nPC = unionStart[unionIndex];
- pCurrentSection->nPC = unionStart[unionIndex];
+ curOffset = unionStart[unionIndex];
}
static size_t strlenUTF8(const char *s)
@@ -601,7 +600,8 @@
%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 <macroArg> macroargs;
+%type <nConstValue> sectunion
+%type <macroArg> macroargs
%token T_Z80_ADC T_Z80_ADD T_Z80_AND
%token T_Z80_BIT
@@ -815,7 +815,8 @@
assert : T_POP_ASSERT assert_type relocexpr
{
if (!rpn_isKnown(&$3)) {
- if (!out_CreateAssert($2, &$3, ""))
+ if (!out_CreateAssert($2, &$3, "",
+ sect_GetOutputOffset()))
yyerror("Assertion creation failed: %s",
strerror(errno));
} else if ($3.nVal == 0) {
@@ -836,7 +837,8 @@
| T_POP_ASSERT assert_type relocexpr ',' string
{
if (!rpn_isKnown(&$3)) {
- if (!out_CreateAssert($2, &$3, $5))
+ if (!out_CreateAssert($2, &$3, $5,
+ sect_GetOutputOffset()))
yyerror("Assertion creation failed: %s",
strerror(errno));
} else if ($3.nVal == 0) {
@@ -968,8 +970,7 @@
updateUnion();
nUnionDepth--;
- nPC = unionStart[nUnionDepth] + unionSize[nUnionDepth];
- pCurrentSection->nPC = nPC;
+ curOffset = unionStart[nUnionDepth] + unionSize[nUnionDepth];
}
;
@@ -1418,10 +1419,13 @@
}
;
-section : T_POP_SECTION string ',' sectiontype sectorg sectattrs {
- out_NewSection($2, $4, $5, &$6);
+section : T_POP_SECTION sectunion string ',' sectiontype sectorg sectattrs {
+ out_NewSection($3, $5, $6, &$7, $2);
}
;
+
+sectunion : /* empty */ { $$ = false; }
+ | T_POP_UNION { $$ = true; }
sectiontype : T_SECT_WRAM0 { $$ = SECTTYPE_WRAM0; }
| T_SECT_VRAM { $$ = SECTTYPE_VRAM; }
--- a/src/asm/main.c
+++ b/src/asm/main.c
@@ -40,11 +40,13 @@
char **cldefines;
clock_t nStartClock, nEndClock;
-int32_t nLineNo;
-uint32_t nTotalLines, nPC, nIFDepth, nUnionDepth;
+uint32_t nTotalLines, nIFDepth, nUnionDepth;
bool skipElif;
uint32_t unionStart[128], unionSize[128];
+int32_t nLineNo;
+uint32_t curOffset;
+
#if defined(YYDEBUG) && YYDEBUG
extern int yydebug;
#endif
@@ -525,7 +527,6 @@
nIFDepth = 0;
skipElif = true;
nUnionDepth = 0;
- nPC = 0;
sym_Init();
sym_SetExportAll(CurrentOptions.exportall);
fstk_Init(tzMainfile);
--- a/src/asm/output.c
+++ b/src/asm/output.c
@@ -187,9 +187,9 @@
{
fputstring(pSect->pzName, f);
- fputlong(pSect->nPC, f);
+ fputlong(pSect->size, f);
- fputc(pSect->nType, f);
+ fputc(pSect->nType | pSect->isUnion << 7, f);
fputlong(pSect->nOrg, f);
fputlong(pSect->nBank, f);
@@ -198,7 +198,7 @@
if (sect_HasData(pSect->nType)) {
struct Patch *pPatch;
- fwrite(pSect->tData, 1, pSect->nPC, f);
+ fwrite(pSect->tData, 1, pSect->size, f);
fputlong(countpatches(pSect), f);
pPatch = pSect->pPatches;
@@ -402,7 +402,8 @@
/*
* 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;
@@ -418,8 +419,8 @@
pPatch->nRPNSize = 0;
pPatch->nType = type;
- pPatch->nOffset = pCurrentSection->nPC;
fstk_DumpToStr(pPatch->tzFilename, sizeof(pPatch->tzFilename));
+ pPatch->nOffset = ofs;
writerpn(pPatch->pRPN, &pPatch->nRPNSize, expr->tRPN, expr->nRPNLength);
assert(pPatch->nRPNSize == expr->nRPNPatchSize);
@@ -430,9 +431,9 @@
/*
* 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;
pCurrentSection->pPatches = pPatch;
@@ -442,7 +443,7 @@
* Creates an assert that will be written to the object file
*/
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));
@@ -449,7 +450,7 @@
if (!assertion)
return false;
- assertion->patch = allocpatch(type, expr);
+ assertion->patch = allocpatch(type, expr, ofs);
assertion->section = pCurrentSection;
assertion->message = strdup(message);
if (!assertion->message) {
--- a/src/asm/section.c
+++ b/src/asm/section.c
@@ -16,16 +16,18 @@
struct SectionStackEntry {
struct Section *pSection;
struct sSymbol *pScope; /* Section's symbol scope */
+ uint32_t offset;
struct SectionStackEntry *pNext;
};
struct SectionStackEntry *pSectionStack;
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
*/
-static void checksection(void)
+static inline void checksection(void)
{
if (pCurrentSection == NULL)
fatalerror("Code generation before SECTION directive");
@@ -35,7 +37,7 @@
* A quick check to see if we have an initialized section that can contain
* this much initialized data
*/
-static void checkcodesection(void)
+static inline void checkcodesection(void)
{
checksection();
@@ -49,22 +51,20 @@
/*
* 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 newSize = pCurrentSection->nPC + delta_size;
+ uint32_t newSize = curOffset + delta_size;
- if (newSize > maxSize) {
- /*
- * This check is here to trap broken code that generates
- * sections that are too big and to prevent the assembler from
- * generating huge object files or trying to allocate too much
- * memory.
- * The real check must be done at the linking stage.
- */
+ /*
+ * This check is here to trap broken code that generates sections that
+ * are too big and to prevent the assembler from generating huge object
+ * files or trying to allocate too much memory.
+ * A check at the linking stage is still necessary.
+ */
+ if (newSize > maxSize)
fatalerror("Section '%s' is too big (max size = 0x%X bytes, reached 0x%X).",
pCurrentSection->pzName, maxSize, newSize);
- }
}
struct Section *out_FindSectionByName(const char *pzName)
@@ -85,7 +85,8 @@
* Find a section by name and type. If it doesn't exist, create it
*/
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 (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM
@@ -123,13 +124,103 @@
struct Section *pSect = out_FindSectionByName(pzName);
if (pSect) {
- if (type == pSect->nType
- && ((uint32_t)org) == pSect->nOrg
- && ((uint32_t)bank) == pSect->nBank
- && ((uint32_t)alignment == pSect->nAlign)) {
- return pSect;
+ unsigned int nbSectErrors = 0;
+#define fail(...) \
+ do { \
+ yyerror(__VA_ARGS__); \
+ 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 if (pSect->nAlign != 0
+ && ((pSect->nAlign - 1) & org))
+ fail("Section \"%s\" already declared as aligned to %u bytes",
+ pSect->pzName, pSect->nAlign);
+ 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;
+ }
+ }
+ /* 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 bytes",
+ pSect->pzName, pSect->nAlign);
+ }
}
- fatalerror("Section already exists but with a different type");
+
+ if (nbSectErrors)
+ fatalerror("Cannot create section \"%s\" (%u errors)",
+ pSect->pzName, nbSectErrors);
+#undef fail
+ return pSect;
}
pSect = malloc(sizeof(*pSect));
@@ -141,7 +232,8 @@
fatalerror("Not enough memory for sectionname");
pSect->nType = type;
- pSect->nPC = 0;
+ pSect->isUnion = isUnion;
+ pSect->size = 0;
pSect->nOrg = org;
pSect->nBank = bank;
pSect->nAlign = alignment;
@@ -177,10 +269,7 @@
if (nUnionDepth > 0)
fatalerror("Cannot change the section within a UNION");
- nPC = (pSect != NULL) ? pSect->nPC : 0;
-
pPCSymbol->pSection = pSect;
- pPCSymbol->isConstant = pSect && pSect->nOrg != -1;
sym_SetCurrentSymbolScope(NULL);
}
@@ -188,17 +277,17 @@
/*
* Set the current section by name and type
*/
-void out_NewSection(char const *pzName, uint32_t type, int32_t org,
- struct SectionSpec const *attributes)
+void out_NewSection(char const *pzName, uint32_t type, uint32_t org,
+ struct SectionSpec const *attributes, bool isUnion)
{
if (currentLoadSection)
fatalerror("Cannot change the section within a `LOAD` block");
struct Section *pSect = getSection(pzName, type, org, attributes->bank,
- 1 << attributes->alignment);
+ 1 << attributes->alignment, isUnion);
- nPC = pSect->nPC;
setSection(pSect);
+ curOffset = isUnion ? 0 : pSect->size;
pCurrentSection = pSect;
}
@@ -205,7 +294,7 @@
/*
* 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)
{
checkcodesection();
@@ -214,9 +303,10 @@
fatalerror("`LOAD` blocks cannot be nested");
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);
currentLoadSection = pSect;
}
@@ -227,8 +317,9 @@
yyerror("Found `ENDL` outside of a `LOAD` block");
currentLoadSection = NULL;
- nPC = pCurrentSection->nPC;
setSection(pCurrentSection);
+ curOffset += loadOffset;
+ loadOffset = 0;
}
struct Section *sect_GetSymbolSection(void)
@@ -236,17 +327,46 @@
return currentLoadSection ? currentLoadSection : pCurrentSection;
}
-/*
- * Output an absolute byte (bypassing ROM/union checks)
- */
-static void absByteBypassCheck(uint8_t b)
+uint32_t sect_GetOutputOffset(void)
{
- pCurrentSection->tData[pCurrentSection->nPC++] = b;
- if (currentLoadSection)
- currentLoadSection->nPC++;
- nPC++;
+ return curOffset + loadOffset;
}
+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());
+}
+
/*
* Output an absolute byte
*/
@@ -253,16 +373,18 @@
void out_AbsByte(uint8_t b)
{
checkcodesection();
- checksectionoverflow(1);
- absByteBypassCheck(b);
+ reserveSpace(1);
+
+ writebyte(b);
}
void out_AbsByteGroup(uint8_t const *s, int32_t length)
{
checkcodesection();
- checksectionoverflow(length);
+ reserveSpace(length);
+
while (length--)
- absByteBypassCheck(*s++);
+ writebyte(*s++);
}
/*
@@ -271,19 +393,17 @@
void out_Skip(int32_t skip)
{
checksection();
- checksectionoverflow(skip);
+ reserveSpace(skip);
+
if (!sect_HasData(pCurrentSection->nType)) {
- pCurrentSection->nPC += skip;
- if (currentLoadSection)
- currentLoadSection->nPC += skip;
- nPC += skip;
+ growSection(skip);
} else if (nUnionDepth > 0) {
while (skip--)
- absByteBypassCheck(CurrentOptions.fillchar);
+ writebyte(CurrentOptions.fillchar);
} else {
checkcodesection();
while (skip--)
- absByteBypassCheck(CurrentOptions.fillchar);
+ writebyte(CurrentOptions.fillchar);
}
}
@@ -293,21 +413,12 @@
void out_String(char const *s)
{
checkcodesection();
- checksectionoverflow(strlen(s));
+ reserveSpace(strlen(s));
+
while (*s)
- absByteBypassCheck(*s++);
+ writebyte(*s++);
}
-static void outputExpression(struct Expression const *expr)
-{
- if (!rpn_isKnown(expr)) {
- out_CreatePatch(PATCHTYPE_BYTE, expr);
- out_AbsByte(0);
- } else {
- out_AbsByte(expr->nVal);
- }
-}
-
/*
* Output a relocatable byte. Checking will be done to see if it
* is an absolute value in disguise.
@@ -314,7 +425,15 @@
*/
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);
}
@@ -322,69 +441,54 @@
* Output several copies of a relocatable byte. Checking will be done to see if
* 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);
+ checkcodesection();
+ reserveSpace(n);
+
+ while (n--) {
+ if (!rpn_isKnown(expr)) {
+ createPatch(PATCHTYPE_BYTE, expr);
+ writebyte(0);
+ } else {
+ writebyte(expr->nVal);
+ }
+ }
rpn_Free(expr);
}
/*
- * Output an absolute word
- */
-static void absWord(uint16_t b)
-{
- checkcodesection();
- checksectionoverflow(2);
- pCurrentSection->tData[pCurrentSection->nPC++] = b & 0xFF;
- pCurrentSection->tData[pCurrentSection->nPC++] = b >> 8;
- if (currentLoadSection)
- currentLoadSection->nPC += 2;
- nPC += 2;
-}
-
-/*
* Output a relocatable word. Checking will be done to see if
* it's an absolute value in disguise.
*/
void out_RelWord(struct Expression *expr)
{
+ checkcodesection();
+ reserveSpace(2);
+
if (!rpn_isKnown(expr)) {
- out_CreatePatch(PATCHTYPE_WORD, expr);
- absWord(0);
+ createPatch(PATCHTYPE_WORD, expr);
+ writeword(0);
} else {
- absWord(expr->nVal);
+ writeword(expr->nVal);
}
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
* is an absolute value in disguise.
*/
void out_RelLong(struct Expression *expr)
{
+ checkcodesection();
+ reserveSpace(2);
+
if (!rpn_isKnown(expr)) {
- out_CreatePatch(PATCHTYPE_LONG, expr);
- absLong(0);
+ createPatch(PATCHTYPE_LONG, expr);
+ writelong(0);
} else {
- absLong(expr->nVal);
+ writelong(expr->nVal);
}
rpn_Free(expr);
}
@@ -396,13 +500,11 @@
void out_PCRelByte(struct Expression *expr)
{
checkcodesection();
- checksectionoverflow(1);
+ reserveSpace(1);
+
if (!rpn_isKnown(expr) || pCurrentSection->nOrg == -1) {
- out_CreatePatch(PATCHTYPE_JR, expr);
- pCurrentSection->tData[pCurrentSection->nPC++] = 0;
- if (currentLoadSection)
- currentLoadSection->nPC++;
- nPC++;
+ createPatch(PATCHTYPE_JR, expr);
+ writebyte(0);
} else {
/* Target is relative to the byte *after* the operand */
uint16_t address = sym_GetValue(pPCSymbol) + 1;
@@ -412,9 +514,9 @@
if (offset < -128 || offset > 127) {
yyerror("jr target out of reach (expected -129 < %d < 128)",
offset);
- out_AbsByte(0);
+ writebyte(0);
} else {
- out_AbsByte(offset);
+ writebyte(offset);
}
}
rpn_Free(expr);
@@ -444,7 +546,7 @@
fsize = ftell(f);
rewind(f);
- checksectionoverflow(fsize);
+ reserveSpace(fsize);
} else if (errno != ESPIPE) {
yyerror("Error determining size of INCBIN file '%s': %s", s,
strerror(errno));
@@ -452,11 +554,8 @@
while ((byte = fgetc(f)) != EOF) {
if (fsize == -1)
- checksectionoverflow(1);
- pCurrentSection->tData[pCurrentSection->nPC++] = byte;
- if (currentLoadSection)
- currentLoadSection->nPC++;
- nPC++;
+ growSection(1);
+ writebyte(byte);
}
if (ferror(f))
@@ -493,7 +592,7 @@
}
checkcodesection();
- checksectionoverflow(length);
+ reserveSpace(length);
int32_t fsize;
@@ -524,10 +623,7 @@
int byte = fgetc(f);
if (byte != EOF) {
- pCurrentSection->tData[pCurrentSection->nPC++] = byte;
- if (currentLoadSection)
- currentLoadSection->nPC++;
- nPC++;
+ writebyte(byte);
} else if (ferror(f)) {
yyerror("Error reading INCBIN file '%s': %s", s,
strerror(errno));
@@ -553,6 +649,7 @@
pSect->pSection = pCurrentSection;
pSect->pScope = sym_GetCurrentSymbolScope();
+ pSect->offset = curOffset;
pSect->pNext = pSectionStack;
pSectionStack = pSect;
}
@@ -571,6 +668,8 @@
setSection(pSect->pSection);
pCurrentSection = pSect->pSection;
sym_SetCurrentSymbolScope(pSect->pScope);
+ curOffset = pSect->offset;
+
pSectionStack = pSect->pNext;
free(pSect);
}
--- a/src/asm/symbol.c
+++ b/src/asm/symbol.c
@@ -62,7 +62,7 @@
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 @@
if (snprintf((*ppsym)->tzName, MAXSYMLEN + 1, "%s", s) > MAXSYMLEN)
warning(WARNING_LONG_STR, "Symbol name is too long: '%s'", s);
- (*ppsym)->isConstant = false;
(*ppsym)->isExported = false;
(*ppsym)->isBuiltin = false;
(*ppsym)->isReferenced = false;
@@ -349,7 +348,6 @@
nsym->nValue = value;
nsym->type = SYM_EQU;
- nsym->isConstant = true;
nsym->pScope = NULL;
updateSymbolFilename(nsym);
@@ -417,7 +415,6 @@
nsym->nValue = value;
nsym->type = SYM_SET;
- nsym->isConstant = true;
nsym->pScope = NULL;
updateSymbolFilename(nsym);
@@ -479,9 +476,8 @@
nsym->tzFileName, nsym->nFileLine);
/* If the symbol already exists as a ref, just "take over" it */
- nsym->nValue = nPC;
+ nsym->nValue = curOffset;
nsym->type = SYM_LABEL;
- nsym->isConstant = pCurrentSection && pCurrentSection->nOrg != -1;
if (exportall)
nsym->isExported = true;
--- a/src/link/object.c
+++ b/src/link/object.c
@@ -245,6 +245,7 @@
char const *fileName)
{
int32_t tmp;
+ uint8_t type;
tryReadstr(section->name, file, "%s: Cannot read section name: %s",
fileName);
@@ -254,8 +255,10 @@
errx(1, "\"%s\"'s section size (%d) is invalid", section->name,
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);
+ section->type = type & 0x7F;
+ section->isUnion = type >> 7;
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s org: %s",
fileName, section->name);
section->isAddressFixed = tmp >= 0;
@@ -358,6 +361,14 @@
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
* @param fileName The filename to report for errors
@@ -448,6 +459,7 @@
if (!section)
err(1, "%s: Couldn't create new section", fileName);
+ section->nextu = NULL;
readSection(file, section, fileName);
section->fileSymbols = fileSymbols;
@@ -472,9 +484,10 @@
if (sectionID == -1) {
fileSymbols[i]->section = NULL;
} else {
- fileSymbols[i]->section = fileSections[sectionID];
/* Give the section a pointer to the symbol as well */
linkSymToSect(fileSymbols[i], fileSections[sectionID]);
+ fileSymbols[i]->section =
+ getMainSection(fileSections[sectionID]);
}
}
--- a/src/link/patch.c
+++ b/src/link/patch.c
@@ -117,6 +117,24 @@
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.
* @param patch The patch to compute the value of
@@ -238,19 +256,8 @@
value |= getRPNByte(&expression, &size,
patch->fileName) << shift;
- symbol = section->fileSymbols[value];
-
- /* 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;
+ value = getSymbol(section->fileSymbols, value,
+ patch->fileName)->section->bank;
break;
case RPN_BANK_SECT:
@@ -305,18 +312,9 @@
value |= getRPNByte(&expression, &size,
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, "@")) {
value = section->org + patch->offset;
} else {
@@ -387,10 +385,8 @@
* @param section The section to patch
* @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))
return;
@@ -433,6 +429,22 @@
}
}
}
+}
+
+/**
+ * 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)
--- a/src/link/section.c
+++ b/src/link/section.c
@@ -36,17 +36,80 @@
hash_ForEach(sections, forEach, &callbackArg);
}
+static void mergeSections(struct Section *target, struct Section *other)
+{
+ if (target->type != other->type)
+ errx(1, "Section \"%s\" is defined with conflicting types %s and %s",
+ other->name,
+ typeNames[target->type], typeNames[other->type]);
+ 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)
{
/* Check if the section already exists */
- if (hash_GetElement(sections, section->name))
- errx(1, "Section name \"%s\" is already in use", section->name);
+ struct Section *other = hash_GetElement(sections, section->name);
- /* If not, add it */
- bool collided = hash_AddElement(sections, section->name, section);
+ if (other) {
+ 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)
- warnx("Section hashmap collision occurred!");
+ if (beVerbose && collided)
+ warnx("Section hashmap collision occurred!");
+ }
}
struct Section *sect_GetSection(char const *name)
--- a/src/rgbds.5
+++ b/src/rgbds.5
@@ -48,8 +48,10 @@
BYTE Type ; 0 = LOCAL symbol only used in this file.
; 1 = IMPORT this symbol from elsewhere
; 2 = EXPORT this symbol to other objects.
+ ; Bit 7 is independent from the above value, and
+ ; encodes whether the section is unionized
- IF Type != 1 ; If symbol is defined in this object file.
+ IF (Type & 0x7F) != 1 ; If symbol is defined in this object file.
STRING FileName ; File where the symbol is defined.
--- /dev/null
+++ b/test/asm/section-union.asm
@@ -1,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!
--- /dev/null
+++ b/test/asm/section-union.err
@@ -1,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 bytes
+ERROR: section-union.asm(37):
+ Cannot create section "test" (3 errors)
--- /dev/null
+++ b/test/asm/section-union.out
@@ -1,0 +1,3 @@
+Base is at $C000 (OK!)
+Plus42 is at $C02A (OK!)
+End is at $D000 (OK!)
--- /dev/null
+++ b/test/link/section-union/align-conflict.asm
@@ -1,0 +1,10 @@
+IF !DEF(SECOND)
+ATTRS equs ",ALIGN[2]"
+ELSE
+ATTRS equs "[$CAFE]"
+ENDC
+
+SECTION UNION "conflicting alignment", WRAM0 ATTRS
+ db
+
+ PURGE ATTRS
--- /dev/null
+++ b/test/link/section-union/align-conflict.out
@@ -1,0 +1,6 @@
+error: Section "conflicting alignment" is defined with conflicting 4-byte alignment and address $cafe
+---
+ERROR: -(18):
+ Section "conflicting alignment" already declared as aligned to 4 bytes
+ERROR: -(18):
+ Cannot create section "conflicting alignment" (1 errors)
--- /dev/null
+++ b/test/link/section-union/assert.asm
@@ -1,0 +1,18 @@
+IF !DEF(SECOND)
+OFS = 42
+ELSE
+OFS = 69
+ENDC
+
+BASE = $C0DE
+
+SECTION UNION "assertions in unions", WRAM0
+IF DEF(SECOND)
+ assert @ != BASE, "Force failing the build" ; Force failure in RGBLINK, though
+ENDC
+ ds OFS
+ assert @ == BASE + OFS, "This assertion should not trigger"
+
+; Only make RGBASM aware of the section's location *after* it sees the assertion
+; This forces it to pass it to RGBLINK
+SECTION UNION "assertions in unions", WRAM0[BASE]
--- /dev/null
+++ b/test/link/section-union/assert.out
@@ -1,0 +1,6 @@
+section-union/assert.asm(11): Force failing the build
+error: 1 assertions failed!
+---
+ERROR: -(30):
+ Assertion failed: Force failing the build
+error: Assembly aborted (1 errors)!
--- /dev/null
+++ b/test/link/section-union/bad-types.asm
@@ -1,0 +1,10 @@
+IF !DEF(SECOND)
+TYPE equs "HRAM"
+ELSE
+TYPE equs "WRAM0"
+ENDC
+
+SECTION UNION "conflicting types", TYPE
+ db
+
+ PURGE TYPE
--- /dev/null
+++ b/test/link/section-union/bad-types.out
@@ -1,0 +1,6 @@
+error: Section "conflicting types" is defined with conflicting types HRAM and WRAM0
+---
+ERROR: -(18):
+ Section "conflicting types" already exists but with type HRAM
+ERROR: -(18):
+ Cannot create section "conflicting types" (1 errors)
--- /dev/null
+++ b/test/link/section-union/bank-conflict.asm
@@ -1,0 +1,8 @@
+IF !DEF(SECOND)
+SECOND equs "4"
+ENDC
+
+SECTION UNION "conflicting banks", WRAMX, BANK[SECOND]
+ db
+
+ PURGE SECOND
--- /dev/null
+++ b/test/link/section-union/bank-conflict.out
@@ -1,0 +1,6 @@
+error: Section "conflicting banks" is defined with conflicting banks 4 and 1
+---
+ERROR: -(14):
+ Section "conflicting banks" already declared with different bank 4
+ERROR: -(14):
+ Cannot create section "conflicting banks" (1 errors)
--- /dev/null
+++ b/test/link/section-union/data-overlay.asm
@@ -1,0 +1,10 @@
+IF !DEF(SECOND)
+DATA equs "ds 4"
+ELSE
+DATA equs "db $aa, $bb, $cc, $dd"
+ENDC
+
+SECTION UNION "overlaid data", ROM0
+ DATA
+
+ PURGE DATA
--- /dev/null
+++ b/test/link/section-union/data-overlay.out
@@ -1,0 +1,6 @@
+error: Section "overlaid data" is of type ROM0, which cannot be unionized
+---
+ERROR: -(18):
+ Cannot declare ROM sections as UNION
+ERROR: -(18):
+ Cannot create section "overlaid data" (1 errors)
--- /dev/null
+++ b/test/link/section-union/different-data.asm
@@ -1,0 +1,8 @@
+IF !DEF(SECOND)
+DATA = 1
+ELSE
+DATA = 2
+ENDC
+
+SECTION UNION "different data", ROM0
+ db DATA
--- /dev/null
+++ b/test/link/section-union/different-data.out
@@ -1,0 +1,6 @@
+error: Section "different data" is of type ROM0, which cannot be unionized
+---
+ERROR: -(16):
+ Cannot declare ROM sections as UNION
+ERROR: -(16):
+ Cannot create section "different data" (1 errors)
--- /dev/null
+++ b/test/link/section-union/different-size.asm
@@ -1,0 +1,8 @@
+IF !DEF(SECOND)
+SIZE = 69
+ELSE
+SIZE = 420
+ENDC
+
+SECTION UNION "different section sizes", ROM0
+ ds SIZE
--- /dev/null
+++ b/test/link/section-union/different-size.out
@@ -1,0 +1,6 @@
+error: Section "different section sizes" is of type ROM0, which cannot be unionized
+---
+ERROR: -(16):
+ Cannot declare ROM sections as UNION
+ERROR: -(16):
+ Cannot create section "different section sizes" (1 errors)
--- /dev/null
+++ b/test/link/section-union/different-syntaxes.asm
@@ -1,0 +1,10 @@
+IF !DEF(SECOND)
+INSTR equs "sbc a"
+ELSE
+INSTR equs "db $9f"
+ENDC
+
+SECTION UNION "different syntaxes", ROM0
+ INSTR
+
+ PURGE INSTR
--- /dev/null
+++ b/test/link/section-union/different-syntaxes.out
@@ -1,0 +1,6 @@
+error: Section "different syntaxes" is of type ROM0, which cannot be unionized
+---
+ERROR: -(18):
+ Cannot declare ROM sections as UNION
+ERROR: -(18):
+ Cannot create section "different syntaxes" (1 errors)
--- /dev/null
+++ b/test/link/section-union/good/a.asm
@@ -1,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)
--- /dev/null
+++ b/test/link/section-union/good/b.asm
@@ -1,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
binary files /dev/null b/test/link/section-union/good/ref.out.bin differ
--- /dev/null
+++ b/test/link/section-union/good/script.link
@@ -1,0 +1,9 @@
+ROM0
+ "output 1"
+ "output 2"
+ "output 3"
+WRAM0
+ "a" ; $C000
+WRAMX 1
+ ORG $DAB0
+ "b"
--- /dev/null
+++ b/test/link/section-union/no-room.asm
@@ -1,0 +1,12 @@
+; NB: the error is that there is no room to place the unionized section in,
+; so RGBASM can't possibly error out.
+IF !DEF(SECOND)
+ SECTION "bloat", WRAMX[$d000], BANK[2]
+ ds $fe0
+
+ SECTION UNION "test", WRAMX, BANK[2]
+ db
+ELSE
+ SECTION UNION "test", WRAMX, ALIGN[6]
+ db
+ENDC
--- /dev/null
+++ b/test/link/section-union/no-room.out
@@ -1,0 +1,2 @@
+error: Unable to place "test" (WRAMX section) in bank $02 with align mask ffffffc0
+---
--- /dev/null
+++ b/test/link/section-union/org-conflict.asm
@@ -1,0 +1,8 @@
+IF !DEF(SECOND)
+ADDR = $BEEF
+ELSE
+ADDR = $BABE
+ENDC
+
+SECTION UNION "conflicting address", SRAM[ADDR]
+ db
--- /dev/null
+++ b/test/link/section-union/org-conflict.out
@@ -1,0 +1,6 @@
+error: Section "conflicting address" is defined with conflicting addresses $beef and $babe
+---
+ERROR: -(16):
+ Section "conflicting address" already declared as fixed at different address $beef
+ERROR: -(16):
+ Cannot create section "conflicting address" (1 errors)
--- /dev/null
+++ b/test/link/section-union/split-data.asm
@@ -1,0 +1,10 @@
+IF !DEF(SECOND)
+DATA equs "ds 1\ndb $aa"
+ELSE
+DATA equs "db $bb\nds 1"
+ENDC
+
+SECTION UNION "mutually-overlaid data", ROM0
+ DATA
+
+ PURGE DATA
--- /dev/null
+++ b/test/link/section-union/split-data.out
@@ -1,0 +1,6 @@
+error: Section "mutually-overlaid data" is of type ROM0, which cannot be unionized
+---
+ERROR: -(18):
+ Cannot declare ROM sections as UNION
+ERROR: -(18):
+ Cannot create section "mutually-overlaid data" (1 errors)
--- a/test/link/test.sh
+++ b/test/link/test.sh
@@ -71,13 +71,34 @@
fi
done
-# This test does its own thing
+# These tests do their own thing
+
$RGBASM -o $otemp high-low/a.asm
$RGBLINK -o $gbtemp $otemp
$RGBASM -o $otemp high-low/b.asm
$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/good/a.asm
+$RGBASM -o $gbtemp2 section-union/good/b.asm
+$RGBLINK -o $gbtemp -l section-union/good/script.link $otemp $gbtemp2
+dd if=$gbtemp count=1 bs=$(printf %s $(wc -c < section-union/good/ref.out.bin)) > $otemp 2>/dev/null
+i="section-union/good.asm" tryCmp section-union/good/ref.out.bin $otemp
+rc=$(($? || $rc))
+for i in section-union/*.asm; do
+ $RGBASM -o $otemp $i
+ $RGBASM -o $gbtemp2 $i -DSECOND
+ if $RGBLINK $otemp $gbtemp2 > $outtemp 2>&1; then
+ echo -e "${bold}${red}$i didn't fail to link!${rescolors}${resbold}"
+ rc=1
+ fi
+ echo --- >> $outtemp
+ # Ensure RGBASM also errors out
+ echo 'SECOND equs "1"' | cat $i - $i | $RGBASM - 2>> $outtemp
+ tryDiff ${i%.asm}.out $outtemp
+ rc=$(($? || $rc))
+done
rm -f $otemp $gbtemp $gbtemp2 $outtemp
exit $rc