diff --git a/include/asm/section.h b/include/asm/section.h index d92aca67..c2fc531f 100644 --- a/include/asm/section.h +++ b/include/asm/section.h @@ -74,6 +74,7 @@ void sect_PCRelByte(struct Expression *expr, uint32_t pcShift); void sect_BinaryFile(char const *s, int32_t startPos); void sect_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length); +void sect_EndSection(void); void sect_PushSection(void); void sect_PopSection(void); diff --git a/man/rgbasm.5 b/man/rgbasm.5 index c96ace2a..68548c7a 100644 --- a/man/rgbasm.5 +++ b/man/rgbasm.5 @@ -714,6 +714,11 @@ SECTION "OAM Data",WRAM0,ALIGN[8] ;\ align to 256 bytes SECTION "VRAM Data",ROMX,BANK[2],ALIGN[4] ;\ align to 16 bytes .Ed .El +.Pp +The current section can be ended without starting a new section by using +.Ic ENDSECTION . +This directive will clear the section context, so you can no longer write code until you start another section. +It can be useful to avoid accidentally defining code or data in the wrong section. .Ss Section stack .Ic POPS and @@ -785,7 +790,7 @@ The former is situated in ROM, where the code is stored, the latter in RAM, wher .Pp You cannot nest .Ic LOAD -blocks, nor can you change the current section within them. +blocks, nor can you change or stop the current section within them. .Pp .Ic LOAD blocks can use the diff --git a/src/asm/charmap.c b/src/asm/charmap.c index fc8bb2f2..8a989fa3 100644 --- a/src/asm/charmap.c +++ b/src/asm/charmap.c @@ -258,7 +258,7 @@ size_t charmap_ConvertNext(char const **input, uint8_t **output) *input); if (codepointLen == 0) - error("Input string is not valid UTF-8!\n"); + error("Input string is not valid UTF-8\n"); // OK because UTF-8 has no NUL in multi-byte chars *input += codepointLen; diff --git a/src/asm/fstack.c b/src/asm/fstack.c index 4f12fc49..d5147fda 100644 --- a/src/asm/fstack.c +++ b/src/asm/fstack.c @@ -569,7 +569,7 @@ void fstk_Init(char const *mainPath, size_t maxDepth) struct LexerState *state = lexer_OpenFile(mainPath); if (!state) - fatalerror("Failed to open main file!\n"); + fatalerror("Failed to open main file\n"); lexer_SetState(state); char const *fileName = lexer_GetFileName(); size_t len = strlen(fileName); diff --git a/src/asm/lexer.c b/src/asm/lexer.c index d9072fc0..8a16f515 100644 --- a/src/asm/lexer.c +++ b/src/asm/lexer.c @@ -217,6 +217,7 @@ static struct KeywordMapping { {"DW", T_POP_DW}, {"DL", T_POP_DL}, {"SECTION", T_POP_SECTION}, + {"ENDSECTION", T_POP_ENDSECTION}, {"PURGE", T_POP_PURGE}, {"RSRESET", T_POP_RSRESET}, @@ -572,7 +573,7 @@ struct KeywordDictNode { uint16_t children[0x60 - ' ']; struct KeywordMapping const *keyword; // Since the keyword structure is invariant, the min number of nodes is known at compile time -} keywordDict[370] = {0}; // Make sure to keep this correct when adding keywords! +} keywordDict[377] = {0}; // Make sure to keep this correct when adding keywords! // Convert a char into its index into the dict static uint8_t dictIndex(char c) diff --git a/src/asm/parser.y b/src/asm/parser.y index 9c28a052..846a4d39 100644 --- a/src/asm/parser.y +++ b/src/asm/parser.y @@ -632,6 +632,7 @@ enum { %token T_POP_EXPORT "EXPORT" %token T_POP_DB "DB" T_POP_DS "DS" T_POP_DW "DW" T_POP_DL "DL" %token T_POP_SECTION "SECTION" T_POP_FRAGMENT "FRAGMENT" +%token T_POP_ENDSECTION "ENDSECTION" %token T_POP_RB "RB" T_POP_RW "RW" // There is no T_POP_RL, only T_Z80_RL %token T_POP_MACRO "MACRO" %token T_POP_ENDM "ENDM" @@ -936,6 +937,7 @@ directive : endc | purge | pops | pushs + | endsection | popo | pusho | opt @@ -1056,6 +1058,9 @@ pops : T_POP_POPS { sect_PopSection(); } pushs : T_POP_PUSHS { sect_PushSection(); } ; +endsection : T_POP_ENDSECTION { sect_EndSection(); } +; + fail : T_POP_FAIL string { fatalerror("%s\n", $2); } ; diff --git a/src/asm/section.c b/src/asm/section.c index dcdb4d2e..fd700744 100644 --- a/src/asm/section.c +++ b/src/asm/section.c @@ -598,7 +598,7 @@ void sect_EndUnion(void) void sect_CheckUnionClosed(void) { if (unionStack) - error("Unterminated UNION construct!\n"); + error("Unterminated UNION construct\n"); } // Output an absolute byte @@ -965,7 +965,7 @@ void sect_PopSection(void) fatalerror("No entries in the section stack\n"); if (currentLoadSection) - fatalerror("Cannot change the section within a `LOAD` block!\n"); + fatalerror("Cannot change the section within a `LOAD` block\n"); struct SectionStackEntry *entry = sectionStack; @@ -981,6 +981,22 @@ void sect_PopSection(void) free(entry); } +void sect_EndSection(void) +{ + if (!currentSection) + fatalerror("Cannot end the section outside of a SECTION\n"); + + if (currentLoadSection) + fatalerror("Cannot end the section within a `LOAD` block\n"); + + if (unionStack) + fatalerror("Cannot end the section within a UNION\n"); + + // Reset the section scope + currentSection = NULL; + sym_SetCurrentSymbolScope(NULL); +} + bool sect_IsSizeKnown(struct Section const NONNULL(sect)) { // SECTION UNION and SECTION FRAGMENT can still grow diff --git a/src/gfx/main.cpp b/src/gfx/main.cpp index fcbfc85c..2e2aaf79 100644 --- a/src/gfx/main.cpp +++ b/src/gfx/main.cpp @@ -242,7 +242,7 @@ static void registerInput(char const *arg) { printUsage(); exit(1); } else if (arg[0] == '\0') { // Empty input path - fprintf(stderr, "FATAL: input image path cannot be empty!\n"); + fprintf(stderr, "FATAL: input image path cannot be empty\n"); printUsage(); exit(1); } else { diff --git a/src/gfx/reverse.cpp b/src/gfx/reverse.cpp index 969877a0..b85c6f1e 100644 --- a/src/gfx/reverse.cpp +++ b/src/gfx/reverse.cpp @@ -109,8 +109,8 @@ void reverse() { auto const tiles = readInto(options.output); uint8_t tileSize = 8 * options.bitDepth; if (tiles.size() % tileSize != 0) { - fatal("Tile data size must be a multiple of %" PRIu8 " bytes! (Read %zu)", tileSize, - tiles.size()); + fatal("Tile data size (%zu bytes) is not a multiple of %" PRIu8 " bytes", + tiles.size(), tileSize); } // By default, assume tiles are not deduplicated, and add the (allegedly) trimmed tiles diff --git a/test/asm/endsection.asm b/test/asm/endsection.asm new file mode 100644 index 00000000..f2f9b360 --- /dev/null +++ b/test/asm/endsection.asm @@ -0,0 +1,4 @@ +SECTION "test", ROM0 + db 1 +ENDSECTION + db 2 diff --git a/test/asm/endsection.err b/test/asm/endsection.err new file mode 100644 index 00000000..64f1656c --- /dev/null +++ b/test/asm/endsection.err @@ -0,0 +1,3 @@ +error: endsection.asm(4): + Cannot output data outside of a SECTION +error: Assembly aborted (1 error)! diff --git a/test/asm/endsection.out b/test/asm/endsection.out new file mode 100644 index 00000000..e69de29b