mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
@@ -18,6 +18,7 @@
|
|||||||
#ifdef __GNUC__ // GCC or compatible
|
#ifdef __GNUC__ // GCC or compatible
|
||||||
#define format_(archetype, str_index, first_arg) \
|
#define format_(archetype, str_index, first_arg) \
|
||||||
__attribute__ ((format (archetype, str_index, first_arg)))
|
__attribute__ ((format (archetype, str_index, first_arg)))
|
||||||
|
#define attr_(...) __attribute__ ((__VA_ARGS__))
|
||||||
// In release builds, define "unreachable" as such, but trap in debug builds
|
// In release builds, define "unreachable" as such, but trap in debug builds
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
#define unreachable_ __builtin_unreachable
|
#define unreachable_ __builtin_unreachable
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
#else
|
#else
|
||||||
// Unsupported, but no need to throw a fit
|
// Unsupported, but no need to throw a fit
|
||||||
#define format_(archetype, str_index, first_arg)
|
#define format_(archetype, str_index, first_arg)
|
||||||
|
#define attr_(...)
|
||||||
// This seems to generate similar code to __builtin_unreachable, despite different semantics
|
// This seems to generate similar code to __builtin_unreachable, despite different semantics
|
||||||
// Note that executing this is undefined behavior (declared _Noreturn, but does return)
|
// Note that executing this is undefined behavior (declared _Noreturn, but does return)
|
||||||
static inline _Noreturn unreachable_(void) {}
|
static inline _Noreturn unreachable_(void) {}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ struct UnionStackEntry {
|
|||||||
/*
|
/*
|
||||||
* A quick check to see if we have an initialized section
|
* A quick check to see if we have an initialized section
|
||||||
*/
|
*/
|
||||||
static bool checksection(void)
|
attr_(warn_unused_result) static bool checksection(void)
|
||||||
{
|
{
|
||||||
if (currentSection)
|
if (currentSection)
|
||||||
return true;
|
return true;
|
||||||
@@ -57,7 +57,7 @@ static bool 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 bool checkcodesection(void)
|
attr_(warn_unused_result) static bool checkcodesection(void)
|
||||||
{
|
{
|
||||||
if (!checksection())
|
if (!checksection())
|
||||||
return false;
|
return false;
|
||||||
@@ -70,29 +70,43 @@ static bool checkcodesection(void)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void checkSectionSize(struct Section const *sect, uint32_t size)
|
attr_(warn_unused_result) static bool checkSectionSize(struct Section const *sect, uint32_t size)
|
||||||
{
|
{
|
||||||
uint32_t maxSize = maxsize[sect->type];
|
uint32_t maxSize = maxsize[sect->type];
|
||||||
|
|
||||||
if (size > maxSize)
|
// If the new size is reasonable, keep going
|
||||||
fatalerror("Section '%s' grew too big (max size = 0x%" PRIX32
|
if (size <= maxSize)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
error("Section '%s' grew too big (max size = 0x%" PRIX32
|
||||||
" bytes, reached 0x%" PRIX32 ").\n", sect->name, maxSize, size);
|
" bytes, reached 0x%" PRIX32 ").\n", sect->name, maxSize, size);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if the section has grown too much.
|
* Check if the section has grown too much.
|
||||||
*/
|
*/
|
||||||
static void reserveSpace(uint32_t delta_size)
|
attr_(warn_unused_result) static bool reserveSpace(uint32_t delta_size)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* This check is here to trap broken code that generates sections that
|
* This check is here to trap broken code that generates sections that are too big and to
|
||||||
* are too big and to prevent the assembler from generating huge object
|
* prevent the assembler from 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.
|
* A check at the linking stage is still necessary.
|
||||||
*/
|
*/
|
||||||
checkSectionSize(currentSection, curOffset + loadOffset + delta_size);
|
|
||||||
if (currentLoadSection)
|
// If the section has already overflowed, skip the check to avoid erroring out ad nauseam
|
||||||
checkSectionSize(currentLoadSection, curOffset + delta_size);
|
if (currentSection->size != UINT32_MAX
|
||||||
|
&& !checkSectionSize(currentSection, curOffset + loadOffset + delta_size))
|
||||||
|
// Mark the section as overflowed, to avoid repeating the error
|
||||||
|
currentSection->size = UINT32_MAX;
|
||||||
|
|
||||||
|
if (currentLoadSection && currentLoadSection->size != UINT32_MAX
|
||||||
|
&& !checkSectionSize(currentLoadSection, curOffset + delta_size))
|
||||||
|
currentLoadSection->size = UINT32_MAX;
|
||||||
|
|
||||||
|
return currentSection->size != UINT32_MAX
|
||||||
|
&& (!currentLoadSection || currentLoadSection->size != UINT32_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Section *sect_FindSectionByName(const char *name)
|
struct Section *sect_FindSectionByName(const char *name)
|
||||||
@@ -596,7 +610,8 @@ void sect_AbsByte(uint8_t b)
|
|||||||
{
|
{
|
||||||
if (!checkcodesection())
|
if (!checkcodesection())
|
||||||
return;
|
return;
|
||||||
reserveSpace(1);
|
if (!reserveSpace(1))
|
||||||
|
return;
|
||||||
|
|
||||||
writebyte(b);
|
writebyte(b);
|
||||||
}
|
}
|
||||||
@@ -605,7 +620,8 @@ void sect_AbsByteGroup(uint8_t const *s, int32_t length)
|
|||||||
{
|
{
|
||||||
if (!checkcodesection())
|
if (!checkcodesection())
|
||||||
return;
|
return;
|
||||||
reserveSpace(length);
|
if (!reserveSpace(length))
|
||||||
|
return;
|
||||||
|
|
||||||
while (length--)
|
while (length--)
|
||||||
writebyte(*s++);
|
writebyte(*s++);
|
||||||
@@ -615,7 +631,8 @@ void sect_AbsWordGroup(uint8_t const *s, int32_t length)
|
|||||||
{
|
{
|
||||||
if (!checkcodesection())
|
if (!checkcodesection())
|
||||||
return;
|
return;
|
||||||
reserveSpace(length * 2);
|
if (!reserveSpace(length * 2))
|
||||||
|
return;
|
||||||
|
|
||||||
while (length--)
|
while (length--)
|
||||||
writeword(*s++);
|
writeword(*s++);
|
||||||
@@ -625,7 +642,8 @@ void sect_AbsLongGroup(uint8_t const *s, int32_t length)
|
|||||||
{
|
{
|
||||||
if (!checkcodesection())
|
if (!checkcodesection())
|
||||||
return;
|
return;
|
||||||
reserveSpace(length * 4);
|
if (!reserveSpace(length * 4))
|
||||||
|
return;
|
||||||
|
|
||||||
while (length--)
|
while (length--)
|
||||||
writelong(*s++);
|
writelong(*s++);
|
||||||
@@ -638,17 +656,16 @@ void sect_Skip(int32_t skip, bool ds)
|
|||||||
{
|
{
|
||||||
if (!checksection())
|
if (!checksection())
|
||||||
return;
|
return;
|
||||||
reserveSpace(skip);
|
if (!reserveSpace(skip))
|
||||||
|
return;
|
||||||
if (!ds && sect_HasData(currentSection->type))
|
|
||||||
warning(WARNING_EMPTY_DATA_DIRECTIVE, "%s directive without data in ROM\n",
|
|
||||||
(skip == 4) ? "DL" : (skip == 2) ? "DW" : "DB");
|
|
||||||
|
|
||||||
if (!sect_HasData(currentSection->type)) {
|
if (!sect_HasData(currentSection->type)) {
|
||||||
growSection(skip);
|
growSection(skip);
|
||||||
} else {
|
} else {
|
||||||
if (!checkcodesection())
|
if (!ds)
|
||||||
return;
|
warning(WARNING_EMPTY_DATA_DIRECTIVE, "%s directive without data in ROM\n",
|
||||||
|
(skip == 4) ? "DL" : (skip == 2) ? "DW" : "DB");
|
||||||
|
// We know we're in a code SECTION
|
||||||
while (skip--)
|
while (skip--)
|
||||||
writebyte(fillByte);
|
writebyte(fillByte);
|
||||||
}
|
}
|
||||||
@@ -661,7 +678,8 @@ void sect_String(char const *s)
|
|||||||
{
|
{
|
||||||
if (!checkcodesection())
|
if (!checkcodesection())
|
||||||
return;
|
return;
|
||||||
reserveSpace(strlen(s));
|
if (!reserveSpace(strlen(s)))
|
||||||
|
return;
|
||||||
|
|
||||||
while (*s)
|
while (*s)
|
||||||
writebyte(*s++);
|
writebyte(*s++);
|
||||||
@@ -675,7 +693,8 @@ void sect_RelByte(struct Expression *expr, uint32_t pcShift)
|
|||||||
{
|
{
|
||||||
if (!checkcodesection())
|
if (!checkcodesection())
|
||||||
return;
|
return;
|
||||||
reserveSpace(1);
|
if (!reserveSpace(1))
|
||||||
|
return;
|
||||||
|
|
||||||
if (!rpn_isKnown(expr)) {
|
if (!rpn_isKnown(expr)) {
|
||||||
createPatch(PATCHTYPE_BYTE, expr, pcShift);
|
createPatch(PATCHTYPE_BYTE, expr, pcShift);
|
||||||
@@ -694,7 +713,8 @@ void sect_RelBytes(uint32_t n, struct Expression *exprs, size_t size)
|
|||||||
{
|
{
|
||||||
if (!checkcodesection())
|
if (!checkcodesection())
|
||||||
return;
|
return;
|
||||||
reserveSpace(n);
|
if (!reserveSpace(n))
|
||||||
|
return;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < n; i++) {
|
for (uint32_t i = 0; i < n; i++) {
|
||||||
struct Expression *expr = &exprs[i % size];
|
struct Expression *expr = &exprs[i % size];
|
||||||
@@ -719,7 +739,8 @@ void sect_RelWord(struct Expression *expr, uint32_t pcShift)
|
|||||||
{
|
{
|
||||||
if (!checkcodesection())
|
if (!checkcodesection())
|
||||||
return;
|
return;
|
||||||
reserveSpace(2);
|
if (!reserveSpace(2))
|
||||||
|
return;
|
||||||
|
|
||||||
if (!rpn_isKnown(expr)) {
|
if (!rpn_isKnown(expr)) {
|
||||||
createPatch(PATCHTYPE_WORD, expr, pcShift);
|
createPatch(PATCHTYPE_WORD, expr, pcShift);
|
||||||
@@ -738,7 +759,8 @@ void sect_RelLong(struct Expression *expr, uint32_t pcShift)
|
|||||||
{
|
{
|
||||||
if (!checkcodesection())
|
if (!checkcodesection())
|
||||||
return;
|
return;
|
||||||
reserveSpace(2);
|
if (!reserveSpace(2))
|
||||||
|
return;
|
||||||
|
|
||||||
if (!rpn_isKnown(expr)) {
|
if (!rpn_isKnown(expr)) {
|
||||||
createPatch(PATCHTYPE_LONG, expr, pcShift);
|
createPatch(PATCHTYPE_LONG, expr, pcShift);
|
||||||
@@ -757,7 +779,8 @@ void sect_PCRelByte(struct Expression *expr, uint32_t pcShift)
|
|||||||
{
|
{
|
||||||
if (!checkcodesection())
|
if (!checkcodesection())
|
||||||
return;
|
return;
|
||||||
reserveSpace(1);
|
if (!reserveSpace(1))
|
||||||
|
return;
|
||||||
struct Symbol const *pc = sym_GetPC();
|
struct Symbol const *pc = sym_GetPC();
|
||||||
|
|
||||||
if (!rpn_IsDiffConstant(expr, pc)) {
|
if (!rpn_IsDiffConstant(expr, pc)) {
|
||||||
@@ -828,7 +851,8 @@ void sect_BinaryFile(char const *s, int32_t startPos)
|
|||||||
}
|
}
|
||||||
|
|
||||||
fseek(f, startPos, SEEK_SET);
|
fseek(f, startPos, SEEK_SET);
|
||||||
reserveSpace(fsize - startPos);
|
if (!reserveSpace(fsize - startPos))
|
||||||
|
goto cleanup;
|
||||||
} else {
|
} else {
|
||||||
if (errno != ESPIPE)
|
if (errno != ESPIPE)
|
||||||
error("Error determining size of INCBIN file '%s': %s\n",
|
error("Error determining size of INCBIN file '%s': %s\n",
|
||||||
@@ -867,7 +891,8 @@ void sect_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
|
|||||||
return;
|
return;
|
||||||
if (length == 0) /* Don't even bother with 0-byte slices */
|
if (length == 0) /* Don't even bother with 0-byte slices */
|
||||||
return;
|
return;
|
||||||
reserveSpace(length);
|
if (!reserveSpace(length))
|
||||||
|
return;
|
||||||
|
|
||||||
char *fullPath = NULL;
|
char *fullPath = NULL;
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
|
|||||||
@@ -1,5 +1,25 @@
|
|||||||
SECTION "Overflow", ROM0
|
SECTION "Overflow", ROM0
|
||||||
ds $6000
|
ds $6000
|
||||||
LOAD "oops",WRAM0
|
LOAD "oops",WRAM0
|
||||||
|
; We might get an error for "oops", but it can also make sense to no-op the directive
|
||||||
ds $2001
|
ds $2001
|
||||||
|
; We shouldn't get any more errors for "Overflow" nor "oops"
|
||||||
|
db
|
||||||
ENDL
|
ENDL
|
||||||
|
|
||||||
|
SECTION "Moar overflow", ROM0
|
||||||
|
ds $6001
|
||||||
|
LOAD "hmm", WRAM0
|
||||||
|
ds $2000
|
||||||
|
; Since the `ds` overflows "Moar overflow", it could be no-op'd, making this `db` not error
|
||||||
|
db
|
||||||
|
ENDL
|
||||||
|
|
||||||
|
SECTION "Not overflowing", ROM0
|
||||||
|
ds $5FFF
|
||||||
|
LOAD "lol", WRAM0
|
||||||
|
ds $2000
|
||||||
|
db
|
||||||
|
; Since the LOAD block is overflowed, this may be no-op'd, not affecting the "parent"
|
||||||
|
ENDL
|
||||||
|
dw ; This, however...
|
||||||
|
|||||||
@@ -1,2 +1,11 @@
|
|||||||
FATAL: load-overflow.asm(4):
|
ERROR: load-overflow.asm(5):
|
||||||
Section 'Overflow' grew too big (max size = 0x8000 bytes, reached 0x8001).
|
Section 'Overflow' grew too big (max size = 0x8000 bytes, reached 0x8001).
|
||||||
|
ERROR: load-overflow.asm(5):
|
||||||
|
Section 'oops' grew too big (max size = 0x2000 bytes, reached 0x2001).
|
||||||
|
ERROR: load-overflow.asm(13):
|
||||||
|
Section 'Moar overflow' grew too big (max size = 0x8000 bytes, reached 0x8001).
|
||||||
|
ERROR: load-overflow.asm(22):
|
||||||
|
Section 'lol' grew too big (max size = 0x2000 bytes, reached 0x2001).
|
||||||
|
ERROR: load-overflow.asm(25):
|
||||||
|
Section 'Not overflowing' grew too big (max size = 0x8000 bytes, reached 0x8001).
|
||||||
|
error: Assembly aborted (5 errors)!
|
||||||
|
|||||||
Reference in New Issue
Block a user