mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Make ENDL optional like ENDSECTION (#1538)
Add warning for `LOAD` without `ENDL`
This commit is contained in:
@@ -192,6 +192,7 @@ _rgbasm_completions() {
|
|||||||
shift-amount
|
shift-amount
|
||||||
truncation
|
truncation
|
||||||
unmapped-char
|
unmapped-char
|
||||||
|
unterminated-load
|
||||||
user
|
user
|
||||||
all
|
all
|
||||||
extra
|
extra
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ _rgbasm_warnings() {
|
|||||||
'shift-amount:Warn when a shift'\''s operand it negative or \> 32'
|
'shift-amount:Warn when a shift'\''s operand it negative or \> 32'
|
||||||
'truncation:Warn when implicit truncation loses bits'
|
'truncation:Warn when implicit truncation loses bits'
|
||||||
'unmapped-char:Warn on unmapped character'
|
'unmapped-char:Warn on unmapped character'
|
||||||
|
'unterminated-load:Warn on LOAD without ENDL'
|
||||||
'user:Warn when executing the WARN built-in'
|
'user:Warn when executing the WARN built-in'
|
||||||
)
|
)
|
||||||
# TODO: handle `no-` and `error=` somehow?
|
# TODO: handle `no-` and `error=` somehow?
|
||||||
|
|||||||
@@ -70,7 +70,8 @@ void sect_SetLoadSection(
|
|||||||
SectionSpec const &attrs,
|
SectionSpec const &attrs,
|
||||||
SectionModifier mod
|
SectionModifier mod
|
||||||
);
|
);
|
||||||
void sect_EndLoadSection();
|
void sect_EndLoadSection(char const *cause);
|
||||||
|
void sect_CheckLoadClosed();
|
||||||
|
|
||||||
Section *sect_GetSymbolSection();
|
Section *sect_GetSymbolSection();
|
||||||
uint32_t sect_GetSymbolOffset();
|
uint32_t sect_GetSymbolOffset();
|
||||||
|
|||||||
@@ -7,20 +7,21 @@ extern unsigned int nbErrors, maxErrors;
|
|||||||
|
|
||||||
enum WarningID {
|
enum WarningID {
|
||||||
WARNING_ASSERT, // Assertions
|
WARNING_ASSERT, // Assertions
|
||||||
WARNING_BACKWARDS_FOR, // `for` loop with backwards range
|
WARNING_BACKWARDS_FOR, // `FOR` loop with backwards range
|
||||||
WARNING_BUILTIN_ARG, // Invalid args to builtins
|
WARNING_BUILTIN_ARG, // Invalid args to builtins
|
||||||
WARNING_CHARMAP_REDEF, // Charmap entry re-definition
|
WARNING_CHARMAP_REDEF, // Charmap entry re-definition
|
||||||
WARNING_DIV, // Division undefined behavior
|
WARNING_DIV, // Undefined division behavior
|
||||||
WARNING_EMPTY_DATA_DIRECTIVE, // `db`, `dw` or `dl` directive without data in ROM
|
WARNING_EMPTY_DATA_DIRECTIVE, // `db`, `dw` or `dl` directive without data in ROM
|
||||||
WARNING_EMPTY_MACRO_ARG, // Empty macro argument
|
WARNING_EMPTY_MACRO_ARG, // Empty macro argument
|
||||||
WARNING_EMPTY_STRRPL, // Empty second argument in `STRRPL`
|
WARNING_EMPTY_STRRPL, // Empty second argument in `STRRPL`
|
||||||
WARNING_LARGE_CONSTANT, // Constants too large
|
WARNING_LARGE_CONSTANT, // Constants too large
|
||||||
WARNING_MACRO_SHIFT, // Shift past available arguments in macro
|
WARNING_MACRO_SHIFT, // `SHIFT` past available arguments in macro
|
||||||
WARNING_NESTED_COMMENT, // Comment-start delimiter in a block comment
|
WARNING_NESTED_COMMENT, // Comment-start delimiter in a block comment
|
||||||
WARNING_OBSOLETE, // Obsolete things
|
WARNING_OBSOLETE, // Obsolete/deprecated things
|
||||||
WARNING_SHIFT, // Shifting undefined behavior
|
WARNING_SHIFT, // Undefined `SHIFT` behavior
|
||||||
WARNING_SHIFT_AMOUNT, // Strange shift amount
|
WARNING_SHIFT_AMOUNT, // Strange `SHIFT` amount
|
||||||
WARNING_USER, // User warnings
|
WARNING_UNTERMINATED_LOAD, // `LOAD` without `ENDL`
|
||||||
|
WARNING_USER, // User-defined `WARN`ings
|
||||||
|
|
||||||
NB_PLAIN_WARNINGS,
|
NB_PLAIN_WARNINGS,
|
||||||
|
|
||||||
|
|||||||
@@ -370,6 +370,13 @@ only warns if the active charmap is not empty.
|
|||||||
.Fl Wunmapped-char=2
|
.Fl Wunmapped-char=2
|
||||||
warns if the active charmap is empty, and/or is not the default charmap
|
warns if the active charmap is empty, and/or is not the default charmap
|
||||||
.Sq main .
|
.Sq main .
|
||||||
|
.It Fl Wunterminated-load
|
||||||
|
Warn when a
|
||||||
|
.Ic LOAD
|
||||||
|
block is not terminated by an
|
||||||
|
.Ic ENDL .
|
||||||
|
This warning is enabled by
|
||||||
|
.Fl Wextra .
|
||||||
.It Fl Wno-user
|
.It Fl Wno-user
|
||||||
Warn when the
|
Warn when the
|
||||||
.Ic WARN
|
.Ic WARN
|
||||||
|
|||||||
21
man/rgbasm.5
21
man/rgbasm.5
@@ -903,7 +903,7 @@ SECTION "LOAD example", ROMX
|
|||||||
CopyCode:
|
CopyCode:
|
||||||
ld de, RAMCode
|
ld de, RAMCode
|
||||||
ld hl, RAMLocation
|
ld hl, RAMLocation
|
||||||
ld c, RAMLocation.end - RAMLocation
|
ld c, RAMCode.end - RAMCode
|
||||||
\&.loop
|
\&.loop
|
||||||
ld a, [de]
|
ld a, [de]
|
||||||
inc de
|
inc de
|
||||||
@@ -927,8 +927,8 @@ RAMLocation:
|
|||||||
|
|
||||||
\&.string
|
\&.string
|
||||||
db "Hello World!\e0"
|
db "Hello World!\e0"
|
||||||
\&.end
|
|
||||||
ENDL
|
ENDL
|
||||||
|
\&.end
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
A
|
A
|
||||||
@@ -938,7 +938,9 @@ block feels similar to a
|
|||||||
declaration because it creates a new one.
|
declaration because it creates a new one.
|
||||||
All data and code generated within such a block is placed in the current section like usual, but all labels are created as if they were placed in this newly-created section.
|
All data and code generated within such a block is placed in the current section like usual, but all labels are created as if they were placed in this newly-created section.
|
||||||
.Pp
|
.Pp
|
||||||
In the example above, all of the code and data will end up in the "LOAD example" section.
|
In the example above, all of the code and data will end up in the
|
||||||
|
.Dq LOAD example
|
||||||
|
section.
|
||||||
You will notice the
|
You will notice the
|
||||||
.Sq RAMCode
|
.Sq RAMCode
|
||||||
and
|
and
|
||||||
@@ -950,6 +952,19 @@ You cannot nest
|
|||||||
.Ic LOAD
|
.Ic LOAD
|
||||||
blocks, nor can you change or stop the current section within them.
|
blocks, nor can you change or stop the current section within them.
|
||||||
.Pp
|
.Pp
|
||||||
|
The current
|
||||||
|
.Ic LOAD
|
||||||
|
block can be ended by using
|
||||||
|
.Ic ENDL .
|
||||||
|
This directive is only necessary if you want to resume writing code in its containing ROM section.
|
||||||
|
Any of
|
||||||
|
.Ic LOAD , SECTION , ENDSECTION ,
|
||||||
|
or
|
||||||
|
.Ic POPS
|
||||||
|
will end the current
|
||||||
|
.Ic LOAD
|
||||||
|
block before performing its own function.
|
||||||
|
.Pp
|
||||||
.Ic LOAD
|
.Ic LOAD
|
||||||
blocks can use the
|
blocks can use the
|
||||||
.Ic UNION
|
.Ic UNION
|
||||||
|
|||||||
@@ -381,6 +381,7 @@ int main(int argc, char *argv[]) {
|
|||||||
nbErrors = 1;
|
nbErrors = 1;
|
||||||
|
|
||||||
sect_CheckUnionClosed();
|
sect_CheckUnionClosed();
|
||||||
|
sect_CheckLoadClosed();
|
||||||
sect_CheckSizes();
|
sect_CheckSizes();
|
||||||
|
|
||||||
if (nbErrors != 0)
|
if (nbErrors != 0)
|
||||||
|
|||||||
@@ -849,7 +849,7 @@ load:
|
|||||||
sect_SetLoadSection($3, $5, $6, $7, $2);
|
sect_SetLoadSection($3, $5, $6, $7, $2);
|
||||||
}
|
}
|
||||||
| POP_ENDL {
|
| POP_ENDL {
|
||||||
sect_EndLoadSection();
|
sect_EndLoadSection(nullptr);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|||||||
@@ -425,14 +425,14 @@ void sect_NewSection(
|
|||||||
SectionSpec const &attrs,
|
SectionSpec const &attrs,
|
||||||
SectionModifier mod
|
SectionModifier mod
|
||||||
) {
|
) {
|
||||||
if (currentLoadSection)
|
|
||||||
fatalerror("Cannot change the section within a `LOAD` block\n");
|
|
||||||
|
|
||||||
for (SectionStackEntry &entry : sectionStack) {
|
for (SectionStackEntry &entry : sectionStack) {
|
||||||
if (entry.section && entry.section->name == name)
|
if (entry.section && entry.section->name == name)
|
||||||
fatalerror("Section '%s' is already on the stack\n", name.c_str());
|
fatalerror("Section '%s' is already on the stack\n", name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (currentLoadSection)
|
||||||
|
sect_EndLoadSection("SECTION");
|
||||||
|
|
||||||
Section *sect = getSection(name, type, org, attrs, mod);
|
Section *sect = getSection(name, type, org, attrs, mod);
|
||||||
|
|
||||||
changeSection();
|
changeSection();
|
||||||
@@ -457,11 +457,6 @@ void sect_SetLoadSection(
|
|||||||
if (!requireCodeSection())
|
if (!requireCodeSection())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (currentLoadSection) {
|
|
||||||
error("`LOAD` blocks cannot be nested\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sect_HasData(type)) {
|
if (sect_HasData(type)) {
|
||||||
error("`LOAD` blocks cannot create a ROM section\n");
|
error("`LOAD` blocks cannot create a ROM section\n");
|
||||||
return;
|
return;
|
||||||
@@ -472,6 +467,9 @@ void sect_SetLoadSection(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (currentLoadSection)
|
||||||
|
sect_EndLoadSection("LOAD");
|
||||||
|
|
||||||
Section *sect = getSection(name, type, org, attrs, mod);
|
Section *sect = getSection(name, type, org, attrs, mod);
|
||||||
|
|
||||||
currentLoadLabelScopes = sym_GetCurrentLabelScopes();
|
currentLoadLabelScopes = sym_GetCurrentLabelScopes();
|
||||||
@@ -481,7 +479,14 @@ void sect_SetLoadSection(
|
|||||||
currentLoadSection = sect;
|
currentLoadSection = sect;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sect_EndLoadSection() {
|
void sect_EndLoadSection(char const *cause) {
|
||||||
|
if (cause)
|
||||||
|
warning(
|
||||||
|
WARNING_UNTERMINATED_LOAD,
|
||||||
|
"`LOAD` block without `ENDL` terminated by `%s`\n",
|
||||||
|
cause
|
||||||
|
);
|
||||||
|
|
||||||
if (!currentLoadSection) {
|
if (!currentLoadSection) {
|
||||||
error("Found `ENDL` outside of a `LOAD` block\n");
|
error("Found `ENDL` outside of a `LOAD` block\n");
|
||||||
return;
|
return;
|
||||||
@@ -494,6 +499,11 @@ void sect_EndLoadSection() {
|
|||||||
sym_SetCurrentLabelScopes(currentLoadLabelScopes);
|
sym_SetCurrentLabelScopes(currentLoadLabelScopes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sect_CheckLoadClosed() {
|
||||||
|
if (currentLoadSection)
|
||||||
|
warning(WARNING_UNTERMINATED_LOAD, "`LOAD` block without `ENDL` terminated by EOF\n");
|
||||||
|
}
|
||||||
|
|
||||||
Section *sect_GetSymbolSection() {
|
Section *sect_GetSymbolSection() {
|
||||||
return currentLoadSection ? currentLoadSection : currentSection;
|
return currentLoadSection ? currentLoadSection : currentSection;
|
||||||
}
|
}
|
||||||
@@ -954,7 +964,7 @@ void sect_PopSection() {
|
|||||||
fatalerror("No entries in the section stack\n");
|
fatalerror("No entries in the section stack\n");
|
||||||
|
|
||||||
if (currentLoadSection)
|
if (currentLoadSection)
|
||||||
fatalerror("Cannot change the section within a `LOAD` block\n");
|
sect_EndLoadSection("POPS");
|
||||||
|
|
||||||
SectionStackEntry entry = sectionStack.front();
|
SectionStackEntry entry = sectionStack.front();
|
||||||
sectionStack.pop_front();
|
sectionStack.pop_front();
|
||||||
@@ -972,12 +982,12 @@ void sect_EndSection() {
|
|||||||
if (!currentSection)
|
if (!currentSection)
|
||||||
fatalerror("Cannot end the section outside of a SECTION\n");
|
fatalerror("Cannot end the section outside of a SECTION\n");
|
||||||
|
|
||||||
if (currentLoadSection)
|
|
||||||
fatalerror("Cannot end the section within a `LOAD` block\n");
|
|
||||||
|
|
||||||
if (!currentUnionStack.empty())
|
if (!currentUnionStack.empty())
|
||||||
fatalerror("Cannot end the section within a UNION\n");
|
fatalerror("Cannot end the section within a UNION\n");
|
||||||
|
|
||||||
|
if (currentLoadSection)
|
||||||
|
sect_EndLoadSection("ENDSECTION");
|
||||||
|
|
||||||
// Reset the section scope
|
// Reset the section scope
|
||||||
currentSection = nullptr;
|
currentSection = nullptr;
|
||||||
sym_ResetCurrentLabelScopes();
|
sym_ResetCurrentLabelScopes();
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ static const WarningFlag warningFlags[NB_WARNINGS] = {
|
|||||||
{"obsolete", LEVEL_DEFAULT },
|
{"obsolete", LEVEL_DEFAULT },
|
||||||
{"shift", LEVEL_EVERYTHING},
|
{"shift", LEVEL_EVERYTHING},
|
||||||
{"shift-amount", LEVEL_EVERYTHING},
|
{"shift-amount", LEVEL_EVERYTHING},
|
||||||
|
{"unterminated-load", LEVEL_EXTRA },
|
||||||
{"user", LEVEL_DEFAULT },
|
{"user", LEVEL_DEFAULT },
|
||||||
// Parametric warnings
|
// Parametric warnings
|
||||||
{"numeric-string", LEVEL_EVERYTHING},
|
{"numeric-string", LEVEL_EVERYTHING},
|
||||||
|
|||||||
@@ -1,2 +1,5 @@
|
|||||||
FATAL: endsection-in-load.asm(3):
|
warning: endsection-in-load.asm(3): [-Wunterminated-load]
|
||||||
Cannot end the section within a `LOAD` block
|
`LOAD` block without `ENDL` terminated by `ENDSECTION`
|
||||||
|
error: endsection-in-load.asm(4):
|
||||||
|
Found `ENDL` outside of a `LOAD` block
|
||||||
|
error: Assembly aborted (1 error)!
|
||||||
|
|||||||
51
test/asm/load-endings.asm
Normal file
51
test/asm/load-endings.asm
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
MACRO data
|
||||||
|
db SECTION(@), \#
|
||||||
|
ENDM
|
||||||
|
|
||||||
|
MACRO now_in
|
||||||
|
if strcmp("\1", "nothing")
|
||||||
|
assert !strcmp(SECTION(@), \1)
|
||||||
|
else
|
||||||
|
assert !def(@)
|
||||||
|
endc
|
||||||
|
ENDM
|
||||||
|
|
||||||
|
now_in nothing
|
||||||
|
|
||||||
|
SECTION "A", ROM0
|
||||||
|
now_in "A"
|
||||||
|
data 1
|
||||||
|
LOAD "P", WRAM0
|
||||||
|
now_in "P"
|
||||||
|
data 2
|
||||||
|
|
||||||
|
; LOAD after LOAD
|
||||||
|
LOAD "Q", WRAM0
|
||||||
|
now_in "Q"
|
||||||
|
data 3
|
||||||
|
|
||||||
|
; SECTION after LOAD
|
||||||
|
SECTION "B", ROM0
|
||||||
|
now_in "B"
|
||||||
|
data 4
|
||||||
|
LOAD "R", WRAM0
|
||||||
|
now_in "R"
|
||||||
|
data 5
|
||||||
|
|
||||||
|
; PUSHS after LOAD
|
||||||
|
PUSHS
|
||||||
|
SECTION "C", ROM0
|
||||||
|
now_in "C"
|
||||||
|
data 6
|
||||||
|
LOAD "S", WRAM0
|
||||||
|
now_in "S"
|
||||||
|
data 7
|
||||||
|
|
||||||
|
; POPS after LOAD
|
||||||
|
POPS
|
||||||
|
now_in "R"
|
||||||
|
data 8
|
||||||
|
|
||||||
|
; ENDSECTION after LOAD
|
||||||
|
ENDSECTION
|
||||||
|
now_in nothing
|
||||||
8
test/asm/load-endings.err
Normal file
8
test/asm/load-endings.err
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
warning: load-endings.asm(23): [-Wunterminated-load]
|
||||||
|
`LOAD` block without `ENDL` terminated by `LOAD`
|
||||||
|
warning: load-endings.asm(28): [-Wunterminated-load]
|
||||||
|
`LOAD` block without `ENDL` terminated by `SECTION`
|
||||||
|
warning: load-endings.asm(45): [-Wunterminated-load]
|
||||||
|
`LOAD` block without `ENDL` terminated by `POPS`
|
||||||
|
warning: load-endings.asm(50): [-Wunterminated-load]
|
||||||
|
`LOAD` block without `ENDL` terminated by `ENDSECTION`
|
||||||
1
test/asm/load-endings.out.bin
Normal file
1
test/asm/load-endings.out.bin
Normal file
@@ -0,0 +1 @@
|
|||||||
|
BRRAPQCS
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
SECTION "outer", ROM0
|
SECTION "outer", ROM0
|
||||||
LOAD "inner", WRAM0
|
LOAD "inner1", WRAM0 ; starts "inner1"
|
||||||
LOAD "matryoshka", HRAM
|
LOAD "inner2", HRAM ; ends "inner1", starts "inner2"
|
||||||
ENDL
|
ENDL ; ends "inner2"
|
||||||
ENDL
|
ENDL ; error
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
error: load-in-load.asm(3):
|
warning: load-in-load.asm(3): [-Wunterminated-load]
|
||||||
`LOAD` blocks cannot be nested
|
`LOAD` block without `ENDL` terminated by `LOAD`
|
||||||
error: load-in-load.asm(5):
|
error: load-in-load.asm(5):
|
||||||
Found `ENDL` outside of a `LOAD` block
|
Found `ENDL` outside of a `LOAD` block
|
||||||
error: Assembly aborted (2 errors)!
|
error: Assembly aborted (1 error)!
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
SECTION "outer", ROM0
|
SECTION "outer1", ROM0
|
||||||
LOAD "ram", WRAM0
|
LOAD "ram", WRAM0
|
||||||
SECTION "inner", ROM0
|
SECTION "outer2", ROM0
|
||||||
ENDL
|
ENDL
|
||||||
|
|||||||
@@ -1,2 +1,5 @@
|
|||||||
FATAL: section-in-load.asm(3):
|
warning: section-in-load.asm(3): [-Wunterminated-load]
|
||||||
Cannot change the section within a `LOAD` block
|
`LOAD` block without `ENDL` terminated by `SECTION`
|
||||||
|
error: section-in-load.asm(4):
|
||||||
|
Found `ENDL` outside of a `LOAD` block
|
||||||
|
error: Assembly aborted (1 error)!
|
||||||
|
|||||||
2
test/asm/unterminated-load.asm
Normal file
2
test/asm/unterminated-load.asm
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
SECTION "rom", ROM0
|
||||||
|
LOAD "ram", WRAM0
|
||||||
2
test/asm/unterminated-load.err
Normal file
2
test/asm/unterminated-load.err
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
warning: unterminated-load.asm(3): [-Wunterminated-load]
|
||||||
|
`LOAD` block without `ENDL` terminated by EOF
|
||||||
Reference in New Issue
Block a user