mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Merge pull request #495 from ISSOtm/sectunion
Implement unionized sections
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -187,9 +187,9 @@ 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 | pSect->isUnion << 7, f);
|
||||||
|
|
||||||
fputlong(pSect->nOrg, f);
|
fputlong(pSect->nOrg, f);
|
||||||
fputlong(pSect->nBank, f);
|
fputlong(pSect->nBank, 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) {
|
||||||
|
|||||||
@@ -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,103 @@ 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 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;
|
||||||
}
|
}
|
||||||
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 bytes",
|
||||||
|
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 +232,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 +269,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 +277,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 +303,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 +317,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 +327,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 +373,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 +393,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 +413,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 +425,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 +441,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 +463,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 +500,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 +514,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 +546,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 +554,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 +592,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 +623,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 +649,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 +668,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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,18 +36,81 @@ 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 (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)
|
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 (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 */
|
/* If not, add it */
|
||||||
bool collided = hash_AddElement(sections, section->name, section);
|
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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -48,8 +48,10 @@ REPT NumberOfSymbols ; Number of symbols defined in this object file.
|
|||||||
BYTE Type ; 0 = LOCAL symbol only used in this file.
|
BYTE Type ; 0 = LOCAL symbol only used in this file.
|
||||||
; 1 = IMPORT this symbol from elsewhere
|
; 1 = IMPORT this symbol from elsewhere
|
||||||
; 2 = EXPORT this symbol to other objects.
|
; 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.
|
STRING FileName ; File where the symbol is defined.
|
||||||
|
|
||||||
|
|||||||
37
test/asm/section-union.asm
Normal file
37
test/asm/section-union.asm
Normal 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!
|
||||||
8
test/asm/section-union.err
Normal file
8
test/asm/section-union.err
Normal 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 bytes
|
||||||
|
ERROR: section-union.asm(37):
|
||||||
|
Cannot create section "test" (3 errors)
|
||||||
3
test/asm/section-union.out
Normal file
3
test/asm/section-union.out
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
Base is at $C000 (OK!)
|
||||||
|
Plus42 is at $C02A (OK!)
|
||||||
|
End is at $D000 (OK!)
|
||||||
10
test/link/section-union/align-conflict.asm
Normal file
10
test/link/section-union/align-conflict.asm
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
IF !DEF(SECOND)
|
||||||
|
ATTRS equs ",ALIGN[2]"
|
||||||
|
ELSE
|
||||||
|
ATTRS equs "[$CAFE]"
|
||||||
|
ENDC
|
||||||
|
|
||||||
|
SECTION UNION "conflicting alignment", WRAM0 ATTRS
|
||||||
|
db
|
||||||
|
|
||||||
|
PURGE ATTRS
|
||||||
6
test/link/section-union/align-conflict.out
Normal file
6
test/link/section-union/align-conflict.out
Normal file
@@ -0,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)
|
||||||
18
test/link/section-union/assert.asm
Normal file
18
test/link/section-union/assert.asm
Normal file
@@ -0,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]
|
||||||
6
test/link/section-union/assert.out
Normal file
6
test/link/section-union/assert.out
Normal file
@@ -0,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)!
|
||||||
10
test/link/section-union/bad-types.asm
Normal file
10
test/link/section-union/bad-types.asm
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
IF !DEF(SECOND)
|
||||||
|
TYPE equs "HRAM"
|
||||||
|
ELSE
|
||||||
|
TYPE equs "WRAM0"
|
||||||
|
ENDC
|
||||||
|
|
||||||
|
SECTION UNION "conflicting types", TYPE
|
||||||
|
db
|
||||||
|
|
||||||
|
PURGE TYPE
|
||||||
6
test/link/section-union/bad-types.out
Normal file
6
test/link/section-union/bad-types.out
Normal file
@@ -0,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)
|
||||||
8
test/link/section-union/bank-conflict.asm
Normal file
8
test/link/section-union/bank-conflict.asm
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
IF !DEF(SECOND)
|
||||||
|
SECOND equs "4"
|
||||||
|
ENDC
|
||||||
|
|
||||||
|
SECTION UNION "conflicting banks", WRAMX, BANK[SECOND]
|
||||||
|
db
|
||||||
|
|
||||||
|
PURGE SECOND
|
||||||
6
test/link/section-union/bank-conflict.out
Normal file
6
test/link/section-union/bank-conflict.out
Normal file
@@ -0,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)
|
||||||
10
test/link/section-union/data-overlay.asm
Normal file
10
test/link/section-union/data-overlay.asm
Normal file
@@ -0,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
|
||||||
6
test/link/section-union/data-overlay.out
Normal file
6
test/link/section-union/data-overlay.out
Normal file
@@ -0,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)
|
||||||
8
test/link/section-union/different-data.asm
Normal file
8
test/link/section-union/different-data.asm
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
IF !DEF(SECOND)
|
||||||
|
DATA = 1
|
||||||
|
ELSE
|
||||||
|
DATA = 2
|
||||||
|
ENDC
|
||||||
|
|
||||||
|
SECTION UNION "different data", ROM0
|
||||||
|
db DATA
|
||||||
6
test/link/section-union/different-data.out
Normal file
6
test/link/section-union/different-data.out
Normal file
@@ -0,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)
|
||||||
8
test/link/section-union/different-size.asm
Normal file
8
test/link/section-union/different-size.asm
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
IF !DEF(SECOND)
|
||||||
|
SIZE = 69
|
||||||
|
ELSE
|
||||||
|
SIZE = 420
|
||||||
|
ENDC
|
||||||
|
|
||||||
|
SECTION UNION "different section sizes", ROM0
|
||||||
|
ds SIZE
|
||||||
6
test/link/section-union/different-size.out
Normal file
6
test/link/section-union/different-size.out
Normal file
@@ -0,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)
|
||||||
10
test/link/section-union/different-syntaxes.asm
Normal file
10
test/link/section-union/different-syntaxes.asm
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
IF !DEF(SECOND)
|
||||||
|
INSTR equs "sbc a"
|
||||||
|
ELSE
|
||||||
|
INSTR equs "db $9f"
|
||||||
|
ENDC
|
||||||
|
|
||||||
|
SECTION UNION "different syntaxes", ROM0
|
||||||
|
INSTR
|
||||||
|
|
||||||
|
PURGE INSTR
|
||||||
6
test/link/section-union/different-syntaxes.out
Normal file
6
test/link/section-union/different-syntaxes.out
Normal file
@@ -0,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)
|
||||||
21
test/link/section-union/good/a.asm
Normal file
21
test/link/section-union/good/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/good/b.asm
Normal file
18
test/link/section-union/good/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/good/ref.out.bin
Normal file
BIN
test/link/section-union/good/ref.out.bin
Normal file
Binary file not shown.
9
test/link/section-union/good/script.link
Normal file
9
test/link/section-union/good/script.link
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
ROM0
|
||||||
|
"output 1"
|
||||||
|
"output 2"
|
||||||
|
"output 3"
|
||||||
|
WRAM0
|
||||||
|
"a" ; $C000
|
||||||
|
WRAMX 1
|
||||||
|
ORG $DAB0
|
||||||
|
"b"
|
||||||
12
test/link/section-union/no-room.asm
Normal file
12
test/link/section-union/no-room.asm
Normal file
@@ -0,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
|
||||||
2
test/link/section-union/no-room.out
Normal file
2
test/link/section-union/no-room.out
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
error: Unable to place "test" (WRAMX section) in bank $02 with align mask ffffffc0
|
||||||
|
---
|
||||||
8
test/link/section-union/org-conflict.asm
Normal file
8
test/link/section-union/org-conflict.asm
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
IF !DEF(SECOND)
|
||||||
|
ADDR = $BEEF
|
||||||
|
ELSE
|
||||||
|
ADDR = $BABE
|
||||||
|
ENDC
|
||||||
|
|
||||||
|
SECTION UNION "conflicting address", SRAM[ADDR]
|
||||||
|
db
|
||||||
6
test/link/section-union/org-conflict.out
Normal file
6
test/link/section-union/org-conflict.out
Normal file
@@ -0,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)
|
||||||
10
test/link/section-union/split-data.asm
Normal file
10
test/link/section-union/split-data.asm
Normal file
@@ -0,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
|
||||||
6
test/link/section-union/split-data.out
Normal file
6
test/link/section-union/split-data.out
Normal file
@@ -0,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)
|
||||||
@@ -71,13 +71,34 @@ 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))
|
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
|
rm -f $otemp $gbtemp $gbtemp2 $outtemp
|
||||||
exit $rc
|
exit $rc
|
||||||
|
|||||||
Reference in New Issue
Block a user