From c592d665a803e3dfc7681aa13c7c719135d2dcd1 Mon Sep 17 00:00:00 2001 From: Rangi <35663410+Rangi42@users.noreply.github.com> Date: Mon, 28 Sep 2026 20:43:14 -0400 Subject: [PATCH] Allow negative from-the-end starting indexes in `INCBIN` (#2181) --- include/asm/section.hpp | 2 +- man/rgbasm.5 | 3 ++- src/asm/parser.y | 4 ++-- src/asm/section.cpp | 31 ++++++++++++++++++++++++++----- test/asm/incbin-negative-bad.asm | 2 +- test/asm/incbin-negative-bad.err | 3 ++- test/asm/incbin-negative.asm | 3 +++ test/asm/incbin-negative.out.bin | 1 + 8 files changed, 38 insertions(+), 11 deletions(-) create mode 100644 test/asm/incbin-negative.asm create mode 100644 test/asm/incbin-negative.out.bin diff --git a/include/asm/section.hpp b/include/asm/section.hpp index fbd5688f..3236ccc6 100644 --- a/include/asm/section.hpp +++ b/include/asm/section.hpp @@ -103,7 +103,7 @@ void sect_RelBytes(uint32_t n, std::vector const &exprs); void sect_RelWord(Expression const &expr, uint32_t pcShift); void sect_RelLong(Expression const &expr, uint32_t pcShift); void sect_PCRelByte(Expression const &expr, uint32_t pcShift); -bool sect_BinaryFile(std::string const &name, uint32_t startPos, std::optional length); +bool sect_BinaryFile(std::string const &name, int32_t startPos, std::optional length); void sect_EndSection(); void sect_PushSection(); diff --git a/man/rgbasm.5 b/man/rgbasm.5 index e2aef57d..6e5e9a68 100644 --- a/man/rgbasm.5 +++ b/man/rgbasm.5 @@ -1906,13 +1906,14 @@ INCBIN "sprites/hero.bin" .Pp You can also include only part of a file with .Ic INCBIN . -The example below includes 256 bytes from data.bin, starting from byte 78. +The example below includes 256 bytes from data.bin, starting from byte 78 (byte positions start from 0). .Bd -literal -offset indent INCBIN "data.bin", 78, 256 .Ed .Pp The length argument is optional. If only the start position is specified, the bytes from the start position until the end of the file will be included. +Negative start positions count from the end of the file, so the last byte is at position -1. .Ss Statically allocating space in RAM .Ic DS statically allocates a number of empty bytes. diff --git a/src/asm/parser.y b/src/asm/parser.y index 7188a1bb..ff534be2 100644 --- a/src/asm/parser.y +++ b/src/asm/parser.y @@ -1052,12 +1052,12 @@ incbin: YYACCEPT; } } - | POP_INCBIN string COMMA uconst { + | POP_INCBIN string COMMA iconst { if (sect_BinaryFile($2, $4, std::nullopt)) { YYACCEPT; } } - | POP_INCBIN string COMMA uconst COMMA uconst { + | POP_INCBIN string COMMA iconst COMMA uconst { if (sect_BinaryFile($2, $4, $6)) { YYACCEPT; } diff --git a/src/asm/section.cpp b/src/asm/section.cpp index 1ee37a2e..eddba606 100644 --- a/src/asm/section.cpp +++ b/src/asm/section.cpp @@ -998,7 +998,7 @@ void sect_PCRelByte(Expression const &expr, uint32_t pcShift) { } } -bool sect_BinaryFile(std::string const &name, uint32_t startPos, std::optional length) { +bool sect_BinaryFile(std::string const &name, int32_t startPos, std::optional length) { if (!requireCodeSection()) { return false; } @@ -1016,18 +1016,31 @@ bool sect_BinaryFile(std::string const &name, uint32_t startPos, std::optional fileSize = seekSize(file); fileSize.has_value()) { - if (startPos > *fileSize) { + // Adjust negative start positions such that -1 is the last byte of a file + if (startPos < 0) { + startPos += *fileSize; + } + if (startPos < 0) { error( - "Specified start position (%" PRIu32 ") is greater than length of \"%s\" (%" PRIu64 + "Specified start position (%" PRId32 ") is before the start of \"%s\" (%" PRIu64 ")", startPos, name.c_str(), *fileSize ); return false; - } else if (length.has_value() && *length > *fileSize - startPos) { + } else if (uint32_t pos = static_cast(startPos); pos > *fileSize) { error( - "Specified range in `INCBIN` file \"%s\" is out of bounds (%" PRIu32 " + %" PRIu32 + "Specified start position (%" PRId32 ") is greater than length of \"%s\" (%" PRIu64 + ")", + startPos, + name.c_str(), + *fileSize + ); + return false; + } else if (length.has_value() && *length > *fileSize - pos) { + error( + "Specified range in `INCBIN` file \"%s\" is out of bounds (%" PRId32 " + %" PRIu32 " > %" PRIu64 ")", name.c_str(), startPos, @@ -1045,6 +1058,14 @@ bool sect_BinaryFile(std::string const &name, uint32_t startPos, std::optional