Compare commits

...
33 Commits
Author SHA1 Message Date
Rangi a2c52caca2 Disallow NUL characters in section names and assertion messages
Allowing these in object files would lose anything after the '\0'
when RGBLINK reads the object.
2026-08-22 19:51:03 -04:00
Rangi 21a4b85a4f Fix STRFMT stopping at NUL characters in format spec strings 2026-08-22 19:23:49 -04:00
Rangi 9fa5058add Correct error message for macro arg \<-INT_MIN> 2026-08-22 19:02:23 -04:00
Rangi e287ee2724 Fix C++ UB from negating INT_MIN with macro shift INT_MIN 2026-08-22 18:48:36 -04:00
Rangi 0888600cb7 Avoid OOM allocation error from invalid too-high bank numbers 2026-08-22 18:31:26 -04:00
Rangi 8bc7de35f9 Prevent SECTION FRAGMENT combined sizes from overflowing their uint16_t size 2026-08-22 18:24:17 -04:00
Rangi ed0a2d1075 Fix infinite loop when a symbol name in an invalid object file starts with an invalid UTF-8 byte like 0xC0 2026-08-22 18:06:32 -04:00
Rangi 71d9e236bc Document multiple -v options in the rgbasm and rgblink man pages, same as rgbgfx 2026-08-19 09:21:05 -04:00
ISSOtm d9003f633a Allow the CMake user to provide their own version string
This will be useful for downstream packagers, as well as rgbds-live
2026-08-15 20:25:35 +02:00
ISSOtm cda77721d4 Avoid assuming that RGBDS is the root CMakeLists
This breaks rgbds-live building us as a subproject, for example
2026-08-15 20:05:17 +02:00
Rangi 307846b03e Release v1.0.3 2026-08-01 11:27:43 -04:00
Rangi e7e671b249 Update external test commits 2026-08-01 11:24:42 -04:00
Rangi 0c772ea049 Update external testing CI with libbet's new way of running Python
libbet now uses `py -3` if the `py` binary exists, instead of if
`COMSPEC` is defined. So we don't need to `unset COMSPEC`, we just
need to avoid Windows' `py` launcher from being in Cygwin's `PATH`.

It turns out that `C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 ...`
was not effectively preventing Windows' `PATH` from being inherited
by Cygwin's `PATH`, because to take effect it needs to run Cygwin's
`/etc/profile` script. The simple way to do that is using a login
shell. So we don't need the `PREFIX`/`--prefix` from the previous
commit after all.
2026-08-01 04:49:43 +02:00
Rangi 6f64fd00f6 Fix CI testing of installing RGBDS on Cygwin (#2038)
We need to pass `-e` to Cygwin `bash` explicitly, since specifying
it as the non-default `shell:` no longer automatically adds `-e`
behavior to `run` blocks.

We also need to install to `/usr/bin` (which `cygwin-install-action`
automatically adds to the `PATH`), not `/usr/local/bin` (which is not
in the default `PATH`). This tests our `PREFIX`/`--prefix` handling.
2026-07-31 10:25:59 -04:00
Rangi 4384223951 CI testing in Cygwin 64-bit now uses CMake and runs external tests (#2037) 2026-07-30 12:59:53 -04:00
Rangi 4e87f61656 Edit some comments and formatting in testing.yml for consistency 2026-07-29 22:08:21 -04:00
Rangi 9d854114ea Use Ninja as the generator for CMake on FreeBSD 2026-07-30 02:33:47 +02:00
Rangi 70d8d67ffc Run external tests for FreeBSD (#2035)
This requires aliasing `make` to `gmake` and `python` to `python3`
2026-07-29 19:45:07 -04:00
Rangi ca2b8e4cde Support the COLUMNS environment variable for help/usage output 2026-07-24 14:46:05 +02:00
Rangi 80c8e77b19 Fixed-point FMOD and LOG explicitly return 0 for some invalid inputs
This is consistent with `fix_Div`'s explicit error handling,
and does not rely on subtle C/C++ `fmod` or `log` IEEE 754 behavior.
2026-07-23 00:38:44 -04:00
Rangi 604c69e048 Fix palette-packing decant logic to avoid UB from invalid iterator (#2031)
The "Iterate through the component's color sets, and transfer them"
logic shifted `from.begin()`, which would make subsequent `std::advance`
calls go past `_assigned.end()` and cause UB. This rewrite uses absolute
numeric indexes into `_assigned` to avoid that potential problem.

I haven't been able to craft a test case that actually *reaches*
that piece of logic, and I suspect that it's unreachable given how
we sort and process color sets before decanting, but cannot prove
that; so it stays in. (Maybe if we added 4bpp 16-colors-per-palette
support, it would become reachable?)
2026-07-22 23:48:02 -04:00
Rangi ec5e6cbabb Handle -Wtruncation for link-time jr values
Fixes #2028
2026-07-21 14:51:58 -04:00
Rangi 50678d0942 Use _fseeki64/_ftelli64 on Windows since fseek/fell are limited by 32-bit long 2026-07-21 14:38:14 -04:00
Rangi c691ec6607 Use std::optional<uint64_t> instead of long with a special -1 sentinel for seekSize 2026-07-21 14:31:05 -04:00
Rangi c3073f4318 Assume that InsertionOrderedMap never adds duplicate keys 2026-07-21 14:07:45 -04:00
Rangi e5b03e3cb1 Zero-initialize Png struct width and height just in case 2026-07-21 13:58:31 -04:00
Rangi 0d379e9016 Assume clz/ctz argument is nonzero in our fallback implementations 2026-07-21 13:56:57 -04:00
Rangi 9cbfec30c7 Avoid redundant "Unexpected text" error message after "Unknown" error 2026-07-21 13:54:11 -04:00
Rangi 9def9c9aa8 Fix an SDAS object error message to use the valid section name variable 2026-07-21 13:41:53 -04:00
Rangi b5681c0d34 Set binary or text mode on stdout as well as stdin 2026-07-21 13:39:28 -04:00
Rangi ec43f06bd9 Fix handling of at-files containing -- followed by positional arguments 2026-07-21 13:35:39 -04:00
Rangi 106ec399d5 Fix reading value of relocations symbol/section index from SDAS objects 2026-07-21 13:14:35 -04:00
Rangi d9c0d37fd5 Explicitly allow JR offsets to wrap around in RGBLINK
This reverts commit f27a8132d9.
2026-07-20 19:23:11 -04:00
70 changed files with 397 additions and 184 deletions
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env sh
local_bin_dir="$PWD/.local/bin"
mkdir -p "$local_bin_dir"
# Alias `make` to `gmake` and `python` to `python3` for use by external tests.
ln -s "$(which gmake)" "$local_bin_dir/make"
ln -s "$(which python3)" "$local_bin_dir/python"
export PATH="$local_bin_dir:$PATH"
+2 -1
View File
@@ -58,7 +58,8 @@ case "${OS%%-*}" in
printf 'PATH=%s\n' "$PATH" >>"$GITHUB_ENV" # Make it available to later CI steps too.
;;
freebsd)
pkg install -y bash bison cmake git png
# GNU `gmake` and `gcc` are dependencies for most repos built by our external tests.
pkg install -y bash bison cmake ninja git png gmake lang/gcc python3 py312-pillow
;;
windows)
# GitHub Actions' hosted runners ship CMake 3.x, but versions prior to 4.0.0 ignore `CPACK_PACKAGE_FILE_NAME`.
+65 -20
View File
@@ -196,8 +196,8 @@ jobs:
shell: cmd
run: | # ASan seems to be broken on Windows, so we disable it.
call .github\scripts\msvc-env.bat ${{ matrix.bits }}
cmake -B build -G Ninja --preset develop -DSANITIZERS=OFF ^
-DFETCHCONTENT_BASE_DIR="${{ env.DEPS_ROOT_DIR }}" -DCMAKE_C_FLAGS="/nologo"
cmake -B build -G Ninja --preset develop -DFETCHCONTENT_BASE_DIR="${{ env.DEPS_ROOT_DIR }}" ^
-DSANITIZERS=OFF -DCMAKE_C_FLAGS="/nologo"
cmake --build build -- -k 0
- name: Install
shell: cmd
@@ -255,10 +255,11 @@ jobs:
key: dep-srcs-${{ hashFiles('cmake/deps.cmake') }}
enableCrossOsArchive: true
- name: Cross-build Windows binaries
run: | # MinGW does not support `--preset develop` sanitizers ASan or UBSan.
run: | # MinGW doesn't support `--preset develop` sanitizers ASan or UBSan, so we disable them.
dll_search_dir=$(printf '%s\n' /usr/lib/gcc/*-w64-mingw32/*-win32 | tee -a /dev/stderr)
cmake -B build -G Ninja --preset develop -DSANITIZERS=OFF --toolchain cmake/toolchain-mingw${{ matrix.bits }}.cmake \
-DFETCHCONTENT_BASE_DIR="${{ env.DEPS_ROOT_DIR }}" -DDLL_SEARCH_DIRS="$dll_search_dir"
cmake -B build -G Ninja --preset develop -DFETCHCONTENT_BASE_DIR="${{ env.DEPS_ROOT_DIR }}" \
-DSANITIZERS=OFF -DDLL_SEARCH_DIRS="$dll_search_dir" \
--toolchain cmake/toolchain-mingw${{ matrix.bits }}.cmake
cmake --build build -- -k 0
- name: Package Windows binaries
run: |
@@ -332,18 +333,16 @@ jobs:
cygwin:
strategy:
matrix:
bits: [32, 64]
include:
- bits: 32
arch: x86
- bits: 64
arch: x86_64
arch: [x86, x86_64]
fail-fast: false
runs-on: windows-2022
timeout-minutes: 30
env:
CYGWIN_NOWINPATH: 1
CHERE_INVOKING: 1
defaults:
run:
shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -o igncr '{0}'
shell: C:\cygwin\bin\bash.exe -leo igncr '{0}'
steps:
- name: Save Windows git location for the PATH
shell: pwsh
@@ -357,22 +356,62 @@ jobs:
platform: ${{ matrix.arch }}
packages: >-
bison
cmake
gcc-g++
git
libpng-devel
make
ninja
pkg-config
- name: Build
run: | # Cygwin does not support `make develop` sanitizers ASan or UBSan
python3
python-imaging
- name: Build using Make
if: matrix.arch == 'x86'
run: | # Cygwin doesn't support `make develop` sanitizers ASan or UBSan, so we don't use it.
make -k -j "$(getconf _NPROCESSORS_ONLN)" Q=
- name: Install
- name: Install using Make
if: matrix.arch == 'x86'
run: |
make install Q=
type rgbasm rgblink rgbfix rgbgfx
man -w 1 rgbasm rgblink rgbfix rgbgfx
- name: Run tests
- name: Build using CMake
if: matrix.arch == 'x86_64'
run: | # Cygwin doesn't support `--preset develop` sanitizers ASan or UBSan, so we disable them.
cmake -B build -G Ninja --preset develop -DSANITIZERS=OFF
cmake --build build -- -k 0
- name: Install using CMake
if: matrix.arch == 'x86_64'
run: |
cmake --install build --verbose
type rgbasm rgblink rgbfix rgbgfx
man -w 1 rgbasm rgblink rgbfix rgbgfx
- name: Compute test dependency cache params
id: test-deps-cache-params
run: |
paths=$(test/external/fetch-repos.sh --get-paths)
hash=$(test/external/fetch-repos.sh --get-hash)
tee -a <<<"paths=\"${paths//,/\\n}\"" $GITHUB_OUTPUT
tee -a <<<"hash=${hash%-}" $GITHUB_OUTPUT
- name: Check test dependency repositories cache
id: test-deps-cache
uses: actions/cache@v5
with:
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
key: cygwin-${{ matrix.bits }}-${{ steps.test-deps-cache-params.outputs.hash }}
- name: Fetch test dependency repositories
if: steps.test-deps-cache.outputs.cache-hit != 'true'
continue-on-error: true
run: |
test/external/fetch-repos.sh
- name: Run tests using our script
if: matrix.arch == 'x86'
run: | # 32-bit Cygwin's `git` 2.38 doesn't support `git clone --revision`, so we don't fetch/run external tests.
test/run-tests.sh --os cygwin --only-internal --jobs "$(getconf _NPROCESSORS_ONLN)"
- name: Run tests using CTest
if: matrix.arch == 'x86_64'
run: |
ctest --test-dir build --schedule-random
- name: Use Windows git location in the PATH
shell: pwsh
run: | # Prevents the `actions/checkout` post-job cleanup from using Cygwin's git binary
@@ -395,12 +434,18 @@ jobs:
GIT_CONFIG_COUNT GIT_CONFIG_KEY_0 GIT_CONFIG_VALUE_0
release: "15.1"
usesh: true
copyback: false
prepare: |
.github/scripts/install-deps.sh freebsd
run: | # Leak detection is not supported on FreeBSD, so disable it.
cmake -B build --preset develop
cmake --build build --verbose -- -k -j "$(getconf _NPROCESSORS_ONLN)"
ASAN_OPTIONS=detect_leaks=0 ctest --test-dir build --schedule-random --label-exclude external
run: |
: # Build
cmake -B build -G Ninja --preset develop
cmake --build build -- -k 0
: # Install
cmake --install build --verbose
type rgbasm rgblink rgbfix rgbgfx
man -w 1 rgbasm rgblink rgbfix rgbgfx
: # Run tests
: # Leak detection is not supported on FreeBSD, so we disable it.
. .github/scripts/freebsd-env.sh
ASAN_OPTIONS=detect_leaks=0 ctest --test-dir build --schedule-random
+45 -35
View File
@@ -11,21 +11,21 @@ file(STRINGS "include/version.hpp" version_defines REGEX "^[ \t]*#define[ \t]+PA
foreach(line IN LISTS version_defines)
# We want the `CMAKE_MATCH_n` variables, so we just need to run *some* regex op.
string(REGEX MATCH "PACKAGE_(VERSION_[^ \t]+)[ \t]+([0-9]+)" dummy "${line}")
set("${CMAKE_MATCH_1}" "${CMAKE_MATCH_2}")
set("RGBDS_${CMAKE_MATCH_1}" "${CMAKE_MATCH_2}")
endforeach()
project(rgbds
VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}"
VERSION "${RGBDS_VERSION_MAJOR}.${RGBDS_VERSION_MINOR}.${RGBDS_VERSION_PATCH}"
LANGUAGES CXX
DESCRIPTION "Game Boy assembly toolchain"
HOMEPAGE_URL "https://rgbds.gbdev.io")
if(DEFINED VERSION_RC)
string(APPEND CMAKE_PROJECT_VERSION "-rc${VERSION_RC}")
string(APPEND PROJECT_VERSION "-rc${RGBDS_VERSION_RC}")
endif()
# Reject in-source builds, as they may conflict with the Makefile.
get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH)
get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH)
get_filename_component(srcdir "${CMAKE_CURRENT_SOURCE_DIR}" REALPATH)
get_filename_component(bindir "${CMAKE_CURRENT_BINARY_DIR}" REALPATH)
if(srcdir STREQUAL bindir)
message(FATAL_ERROR "RGBDS should not be built in the source directory.
Instead, create a separate build directory and specify to CMake the path to the source directory.")
@@ -82,35 +82,45 @@ endif()
# Use versioning consistent with Makefile:
# the git revision is used but uses the fallback in an archive.
message(CHECK_START "Determining RGBDS version from Git history")
list(APPEND CMAKE_MESSAGE_INDENT " ")
set(GIT_REV "") # This fallback is important!
find_package(Git)
list(POP_BACK CMAKE_MESSAGE_INDENT)
if(NOT Git_FOUND)
message(CHECK_FAIL "Git not found")
else()
execute_process(COMMAND "${GIT_EXECUTABLE}" --git-dir=.git -c safe.directory='*'
describe --tags --dirty --always --match "v[0-9]*"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE GIT_REV OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_VARIABLE git_err ERROR_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE result)
if(NOT result EQUAL 0)
# Note that this happens e.g. when building from a tarball, so it shouldn't fail the build!
message(CHECK_FAIL "error ${result} from Git:")
list(APPEND CMAKE_MESSAGE_INDENT " ")
message("${git_err}")
list(POP_BACK CMAKE_MESSAGE_INDENT)
if(DEFINED RGBDS_VERSION_STRING) # Possibly stored in the cache, e.g. if specified on the CLI.
if(RGBDS_VERSION_STRING STREQUAL "")
message(STATUS "Will generate version string from `version.hpp`")
else()
message(CHECK_PASS "${GIT_REV}")
if(NOT "${GIT_REV}" MATCHES "^v[0-9]+\\.[0-9]+\\.[0-9]+(-rc[0-9]+)?")
# Can't find an ancestor tag! (That passes `--match`, anyway.)
message(WARNING "No `v*` Git tag reachable; falling back")
elseif(NOT CMAKE_MATCH_0 STREQUAL "v${CMAKE_PROJECT_VERSION}")
message(SEND_ERROR "\
message(STATUS "Using provided version string - ${RGBDS_VERSION_STRING}")
endif()
else()
message(CHECK_START "Determining RGBDS version from Git history")
# Note that we do NOT store this in the cache, since the Git revision is fairly volatile.
set(RGBDS_VERSION_STRING "") # An empty value causes `version.cpp` to generate a version string.
list(APPEND CMAKE_MESSAGE_INDENT " ")
find_package(Git)
list(POP_BACK CMAKE_MESSAGE_INDENT)
if(NOT Git_FOUND)
message(CHECK_FAIL "Git not found")
else()
execute_process(COMMAND "${GIT_EXECUTABLE}" --git-dir=.git -c safe.directory='*'
describe --tags --dirty --always --match "v[0-9]*"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
OUTPUT_VARIABLE RGBDS_VERSION_STRING OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_VARIABLE git_err ERROR_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE result)
if(NOT result EQUAL 0)
# Note that this happens e.g. when building from a tarball, so it shouldn't fail the build!
message(CHECK_FAIL "error ${result} from Git:")
list(APPEND CMAKE_MESSAGE_INDENT " ")
message("${git_err}")
list(POP_BACK CMAKE_MESSAGE_INDENT)
else()
message(CHECK_PASS "${RGBDS_VERSION_STRING}")
if(NOT "${RGBDS_VERSION_STRING}" MATCHES "^v[0-9]+\\.[0-9]+\\.[0-9]+(-rc[0-9]+)?")
# Can't find an ancestor tag! (That passes `--match`, anyway.)
message(WARNING "No `v*` Git tag reachable; falling back")
elseif(NOT CMAKE_MATCH_0 STREQUAL "v${PROJECT_VERSION}")
message(SEND_ERROR "\
Version mismatch! Git says ${CMAKE_MATCH_0},
version.hpp says v${CMAKE_PROJECT_VERSION}!")
version.hpp says v${PROJECT_VERSION}")
endif()
endif()
endif()
endif()
@@ -215,14 +225,14 @@ endforeach()
# the rest is rather convention from our side, and thus more appropriate for presets or CLI flags.
## CPACK_PACKAGE_NAME: copied from `project()`
set(CPACK_PACKAGE_VENDOR "GBDev")
set(CPACK_PACKAGE_VERSION "${CMAKE_PROJECT_VERSION}") # The individual components are defined implicitly.
set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}") # The individual components are defined implicitly.
set(CPACK_PACKAGE_DESCRIPTION "An assembly toolchain for the Nintendo Game Boy and Game Boy Color") # Same as our repo's description.
## CPACK_PACKAGE_DESCRIPTION_SUMMARY: copied from `project()`
set(CPACK_PACKAGE_HOMEPAGE_URL "https://rgbds.gbdev.io")
## CPACK_PACKAGE_FILE_NAME: should be provided at runtime (`cpack -P`)
set(CPACK_PACKAGE_CHECKSUM SHA256)
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/README.md")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
set(CPACK_STRIP_FILES ON) # Only applies to binary packages, not sources.
set(CPACK_VERBATIM_VARIABLES ON)
set(CPACK_THREADS 0) # Use all available CPU cores.
+1 -1
View File
@@ -1,6 +1,6 @@
FROM debian:13-slim
LABEL org.opencontainers.image.source=https://github.com/gbdev/rgbds
ARG version=1.0.2
ARG version=1.0.3
WORKDIR /rgbds
COPY . .
+3
View File
@@ -34,6 +34,9 @@ public:
} else {
assume(mode & std::ios_base::out);
_file.emplace<std::streambuf *>(std::cout.rdbuf());
if (setmode(STDOUT_FILENO, (mode & std::ios_base::binary) ? O_BINARY : O_TEXT) == -1) {
return nullptr;
}
}
return this;
}
+2 -1
View File
@@ -10,7 +10,8 @@
#include "gfx/rgba.hpp"
struct Png {
uint32_t width, height;
uint32_t width = 0;
uint32_t height = 0;
std::vector<Rgba> pixels{};
std::vector<Rgba> palette{};
+14 -16
View File
@@ -50,42 +50,40 @@ static inline void unreachable_() {
#pragma intrinsic(_BitScanReverse, _BitScanForward)
static inline int ctz(unsigned int x) {
unsigned long cnt;
assume(x != 0);
_BitScanForward(&cnt, x);
return cnt;
unsigned long count;
_BitScanForward(&count, x);
return count;
}
static inline int clz(unsigned int x) {
unsigned long cnt;
assume(x != 0);
_BitScanReverse(&cnt, x);
return 31 - cnt;
unsigned long count;
_BitScanReverse(&count, x);
return 31 - count;
}
#else
#include <limits.h>
static inline int ctz(unsigned int x) {
int cnt = 0;
assume(x != 0);
int count = 0;
while (!(x & 1)) {
x >>= 1;
++cnt;
++count;
}
return cnt;
return count;
}
static inline int clz(unsigned int x) {
int cnt = 0;
assume(x != 0);
int count = 0;
while (x <= UINT_MAX / 2) {
x <<= 1;
++cnt;
++count;
}
return cnt;
return count;
}
#endif
+7 -1
View File
@@ -12,7 +12,7 @@
#include <unordered_map>
#include <utility>
#include "helpers.hpp" // Enum
#include "helpers.hpp" // Enum, assume
// A wrapper around iterables to reverse their iteration order; used in `for`-each loops.
template<typename IterableT>
@@ -55,12 +55,18 @@ public:
typename decltype(list)::const_iterator begin() const { return list.begin(); }
typename decltype(list)::const_iterator end() const { return list.end(); }
// Adding a key that already exists would make the previous value unreachable.
// `InsertionOrderedMap`s are only used for charmaps and sections, which each
// avoid `add`ing if already present, so we can `assume` this is not a concern.
ItemT &add(KeyT const &key) {
assume(!contains(key));
map[key] = list.size();
return list.emplace_back();
}
ItemT &add(KeyT const &key, ItemT &&value) {
assume(!contains(key));
map[key] = list.size();
list.emplace_back(std::move(value));
return list.back();
+8 -2
View File
@@ -55,8 +55,14 @@
#define setmode(fd, mode) (0)
#endif
// MingGW and Cygwin need POSIX functions which are not standard C explicitly enabled,
#if defined(__MINGW32__) || defined(__CYGWIN__)
// Windows has 32-bit `long`, which limits `fseek` and `ftell` to 2 GiB
#if defined(_MSC_VER) || defined(__MINGW32__)
#define fseek _fseeki64
#define ftell _ftelli64
#endif
// MingGW and Cygwin may need POSIX functions which are not standard C explicitly enabled
#if (defined(__MINGW32__) || defined(__CYGWIN__)) && !defined(_POSIX_C_SOURCE)
#define _POSIX_C_SOURCE 200809L
#endif
+1 -1
View File
@@ -27,7 +27,7 @@ int xfclose(FILE *file);
int xclose(int fd);
// Measure file size with `fseek` and `ftell` idiom
long seekSize(FILE *file);
std::optional<uint64_t> seekSize(FILE *file);
// Locale-independent character class functions
bool isNewline(int c);
+1 -1
View File
@@ -5,7 +5,7 @@
#define PACKAGE_VERSION_MAJOR 1
#define PACKAGE_VERSION_MINOR 0
#define PACKAGE_VERSION_PATCH 2
#define PACKAGE_VERSION_PATCH 3
// #define PACKAGE_VERSION_RC 1
char const *get_package_version_string();
+1 -1
View File
@@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: MIT
.\"
.Dd July 20, 2026
.Dd August 1, 2026
.Dt GBZ80 7
.Os
.Sh NAME
+1 -1
View File
@@ -2,7 +2,7 @@
.\"
.\" SPDX-License-Identifier: MIT
.\"
.Dd July 20, 2026
.Dd August 1, 2026
.Dt RGBASM-OLD 5
.Os
.Sh NAME
+6 -2
View File
@@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: MIT
.\"
.Dd July 20, 2026
.Dd August 1, 2026
.Dt RGBASM 1
.Os
.Sh NAME
@@ -8,7 +8,8 @@
.Nd Game Boy assembler
.Sh SYNOPSIS
.Nm
.Op Fl EhVvw
.Op Fl EhVw
.Op Fl v Op Fl v No ...
.Op Fl B Ar param
.Op Fl b Ar chars
.Op Fl \-color Ar when
@@ -177,6 +178,9 @@ or
The defaults are 0123.
.It Fl h , Fl \-help
Print help text for the program and exit.
(Help text wraps to the value of the
.Dv COLUMNS
environment variable if that is defined as nonzero; or else to the console window width if output is to a TTY.)
.It Fl I Ar path , Fl \-include Ar path
Add a new
.Dq include path ;
+1 -1
View File
@@ -2,7 +2,7 @@
.\"
.\" SPDX-License-Identifier: MIT
.\"
.Dd July 20, 2026
.Dd August 1, 2026
.Dt RGBASM 5
.Os
.Sh NAME
+1 -1
View File
@@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: MIT
.\"
.Dd July 20, 2026
.Dd August 1, 2026
.Dt RGBDS 5
.Os
.Sh NAME
+1 -1
View File
@@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: MIT
.\"
.Dd July 20, 2026
.Dd August 1, 2026
.Dt RGBDS 7
.Os
.Sh NAME
+4 -1
View File
@@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: MIT
.\"
.Dd July 20, 2026
.Dd August 1, 2026
.Dt RGBFIX 1
.Os
.Sh NAME
@@ -152,6 +152,9 @@ Trash the global checksum.
.El
.It Fl h , Fl \-help
Print help text for the program and exit.
(Help text wraps to the value of the
.Dv COLUMNS
environment variable if that is defined as nonzero; or else to the console window width if output is to a TTY.)
.It Fl i Ar game_id , Fl \-game-id Ar game_id
Set the game ID string
.Pq Ad 0x13F Ns \(en Ns Ad 0x142
+4 -1
View File
@@ -2,7 +2,7 @@
.\"
.\" SPDX-License-Identifier: MIT
.\"
.Dd July 20, 2026
.Dd August 1, 2026
.Dt RGBGFX 1
.Os
.Sh NAME
@@ -257,6 +257,9 @@ Set the bit depth of the output tile data, in bits per pixel (bpp), either 1 or
This changes how tile data is output, and the maximum number of colors per palette (2 and 4 respectively).
.It Fl h , Fl \-help
Print help text for the program and exit.
(Help text wraps to the value of the
.Dv COLUMNS
environment variable if that is defined as nonzero; or else to the console window width if output is to a TTY.)
.It Fl i Ar input_tiles , Fl \-input-tileset Ar input_tiles
Use the specified input tiles in addition to having
.Nm
+6 -2
View File
@@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: MIT
.\"
.Dd July 20, 2026
.Dd August 1, 2026
.Dt RGBLINK 1
.Os
.Sh NAME
@@ -8,7 +8,8 @@
.Nd Game Boy linker
.Sh SYNOPSIS
.Nm
.Op Fl dhMtVvwx
.Op Fl dhMtVwx
.Op Fl v Op Fl v No ...
.Op Fl B Ar param
.Op Fl \-color Ar when
.Op Fl l Ar linker_script
@@ -152,6 +153,9 @@ This option automatically enables
.Fl w .
.It Fl h , Fl \-help
Print help text for the program and exit.
(Help text wraps to the value of the
.Dv COLUMNS
environment variable if that is defined as nonzero; or else to the console window width if output is to a TTY.)
.It Fl l Ar linker_script , Fl \-linkerscript Ar linker_script
Specify a linker script file that tells the linker how sections must be placed in the ROM.
The attributes assigned in the linker script must be consistent with any assigned in the code.
+1 -1
View File
@@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: MIT
.\"
.Dd July 20, 2026
.Dd August 1, 2026
.Dt RGBLINK 5
.Os
.Sh NAME
+1 -1
View File
@@ -9,7 +9,7 @@ add_library(common OBJECT
"util.cpp"
"version.cpp"
)
target_compile_definitions(common PRIVATE "BUILD_VERSION_STRING=\"${GIT_REV}\"")
target_compile_definitions(common PRIVATE "BUILD_VERSION_STRING=\"${RGBDS_VERSION_STRING}\"")
find_package(BISON 3.0.0 REQUIRED)
# BISON 4.0 deprecates passing this BISON_FLAGS string to `bison_target`'s `COMPILE_FLAGS`,
+4 -4
View File
@@ -151,11 +151,11 @@ std::optional<std::string> act_ReadFile(std::string const &name, uint32_t maxLen
Defer closeFile{[&] { xfclose(file); }};
size_t readSize = maxLen;
if (long fileSize = seekSize(file); fileSize != -1) {
if (std::optional<uint64_t> fileSize = seekSize(file); fileSize.has_value()) {
// If the file is seekable and shorter than the max length,
// just read as many bytes as there are
if (static_cast<size_t>(fileSize) < readSize) {
readSize = fileSize;
if (*fileSize < readSize) {
readSize = *fileSize;
}
// LCOV_EXCL_START
} else if (errno != ESPIPE) {
@@ -555,7 +555,7 @@ std::string act_StringFormat(
std::string str;
size_t argIndex = 0;
for (size_t i = 0; spec[i] != '\0';) {
for (size_t i = 0; i < spec.length();) {
if (int c = spec[i]; c != '%') {
str += c;
++i;
+8 -1
View File
@@ -77,7 +77,11 @@ int32_t fix_Div(int32_t i, int32_t j, int32_t q) {
}
int32_t fix_Mod(int32_t i, int32_t j, int32_t q) {
return double2fix(fmod(fix2double(i, q), fix2double(j, q)), q);
double divisor = fix2double(j, q);
if (fpclassify(divisor) == FP_ZERO) {
return 0;
}
return double2fix(fmod(fix2double(i, q), divisor), q);
}
int32_t fix_Pow(int32_t i, int32_t j, int32_t q) {
@@ -86,6 +90,9 @@ int32_t fix_Pow(int32_t i, int32_t j, int32_t q) {
int32_t fix_Log(int32_t i, int32_t j, int32_t q) {
double divisor = log(fix2double(j, q));
if (isnan(divisor) || isinf(divisor)) {
return 0;
}
if (fpclassify(divisor) == FP_ZERO) {
return INT32_MAX;
}
+1 -1
View File
@@ -542,7 +542,7 @@ static uint32_t readBracketedMacroArgNum() {
}
}
uint32_t n = readNumber<10>(bumpChar(), nullptr);
if (n > INT32_MAX) {
if (n > INT32_MAX && !(negative && n == static_cast<uint32_t>(INT32_MAX) + 1)) {
error("Number in bracketed macro argument is too large");
return 0;
}
+1 -1
View File
@@ -72,7 +72,7 @@ void MacroArgs::shiftArgs(int32_t count) {
count > 0 && (static_cast<uint32_t>(count) > nbArgs || shift > nbArgs - count)) {
warning(WARNING_MACRO_SHIFT, "Cannot shift macro arguments past their end");
shift = nbArgs;
} else if (count < 0 && shift < static_cast<uint32_t>(-count)) {
} else if (count < 0 && (count == INT32_MIN || shift < static_cast<uint32_t>(-count))) {
warning(WARNING_MACRO_SHIFT, "Cannot shift macro arguments past their beginning");
shift = 0;
} else {
+4
View File
@@ -162,6 +162,10 @@ void out_CreatePatch(uint32_t type, Expression const &expr, uint32_t ofs, uint32
void out_CreateAssert(
AssertionType type, Expression const &expr, std::string const &message, uint32_t ofs
) {
if (message.find('\0') != std::string::npos) {
fatal("Assertion messages cannot contain '\\0' characters");
}
Assertion &assertion = assertions.emplace_front();
initPatch(assertion.patch, type, expr, ofs);
+19 -12
View File
@@ -21,7 +21,8 @@
#include "helpers.hpp"
#include "itertools.hpp" // InsertionOrderedMap
#include "linkdefs.hpp"
#include "util.hpp" // xfclose, seekSize
#include "platform.hpp" // fseek
#include "util.hpp" // xfclose, seekSize
#include "asm/fstack.hpp"
#include "asm/lexer.hpp"
@@ -543,6 +544,10 @@ void sect_NewSection(
SectionSpec const &attrs,
SectionModifier mod
) {
if (name.find('\0') != std::string::npos) {
fatal("Section names cannot contain '\\0' characters");
}
for (SectionStackEntry &entry : sectionStack) {
if (entry.section && entry.section->name == name) {
fatal("Section \"%s\" is already on the stack", name.c_str());
@@ -977,13 +982,14 @@ bool sect_BinaryFile(std::string const &name, uint32_t startPos) {
}
Defer closeFile{[&] { xfclose(file); }};
if (long fileSize = seekSize(file); fileSize != -1) {
if (startPos > static_cast<size_t>(fileSize)) {
if (std::optional<uint64_t> fileSize = seekSize(file); fileSize.has_value()) {
if (startPos > *fileSize) {
error(
"Specified start position (%" PRIu32 ") is greater than length of \"%s\" (%ld)",
"Specified start position (%" PRIu32 ") is greater than length of \"%s\" (%" PRIu64
")",
startPos,
name.c_str(),
fileSize
*fileSize
);
return false;
}
@@ -1037,23 +1043,24 @@ bool sect_BinaryFileSlice(std::string const &name, uint32_t startPos, uint32_t l
}
Defer closeFile{[&] { xfclose(file); }};
if (long fileSize = seekSize(file); fileSize != -1) {
if (startPos > static_cast<size_t>(fileSize)) {
if (std::optional<uint64_t> fileSize = seekSize(file); fileSize.has_value()) {
if (startPos > *fileSize) {
error(
"Specified start position (%" PRIu32 ") is greater than length of \"%s\" (%ld)",
"Specified start position (%" PRIu32 ") is greater than length of \"%s\" (%" PRIu64
")",
startPos,
name.c_str(),
fileSize
*fileSize
);
return false;
} else if (startPos + length > static_cast<size_t>(fileSize)) {
} else if (startPos + length > *fileSize) {
error(
"Specified range in `INCBIN` file \"%s\" is out of bounds (%" PRIu32 " + %" PRIu32
" > %ld)",
" > %" PRIu64 ")",
name.c_str(),
startPos,
length,
fileSize
*fileSize
);
return false;
}
+1 -1
View File
@@ -130,7 +130,7 @@ void cli_ParseArgs(
// This happens if `--` is passed, process the remaining arg(s) as positional
assume(musl_optind < curArgc);
for (int i = musl_optind; i < curArgc; ++i) {
parseArg(1, argv[i]); // Positional argument
parseArg(1, curArgv[i]); // Positional argument
}
}
+25 -18
View File
@@ -136,6 +136,8 @@ public:
}
const_iterator end() const { return const_iterator{&_assigned, _assigned.end()}; }
iterator slotAt(size_t index) { return iterator{&_assigned, _assigned.begin() + index}; }
void assign(ColorSetAttrs const &&attrs) {
auto freeSlot =
std::find_if_not(RANGE(_assigned), [](std::optional<ColorSetAttrs> const &slot) {
@@ -160,7 +162,9 @@ public:
});
}
size_t nbColorSets() const { return std::distance(RANGE(*this)); }
size_t nbSlots() const { return _assigned.size(); }
bool isFree(size_t slotIndex) const { return !_assigned[slotIndex].has_value(); }
private:
template<typename IteratorT>
@@ -318,24 +322,30 @@ static void decant(std::vector<AssignedSets> &assignments, std::vector<ColorSet>
decantOn([&colorSets](AssignedSets &to, AssignedSets &from) {
// We need to iterate on all the "components", which are groups of color sets sharing at
// least one color with another color set in the group.
// We do this by adding the first available color set, and then looking for palettes with
// common colors. (As an optimization, we know we can skip palettes already scanned.)
std::vector<bool> processed(from.nbColorSets(), false);
for (std::vector<bool>::iterator wasProcessed;
(wasProcessed = std::find(RANGE(processed), false)) != processed.end();) {
auto attrs = from.begin();
std::advance(attrs, wasProcessed - processed.begin());
// We do this by adding the first free color set, and then looking for palettes with
// common colors. (As an optimization, we know we can skip palettes already processed.)
std::vector<bool> processed(from.nbSlots(), false);
for (size_t startIdx = 0; startIdx < processed.size(); ++startIdx) {
if (processed[startIdx] || from.isFree(startIdx)) {
continue;
}
size_t startColorSetIdx = from.slotAt(startIdx)->colorSetIndex;
// Build up the "component"; start by marking the first color set as processed
std::unordered_set<uint16_t> colors(RANGE(colorSets[attrs->colorSetIndex]));
std::vector<size_t> members = {static_cast<size_t>(wasProcessed - processed.begin())};
for (*wasProcessed = true; ++wasProcessed != processed.end(); ++attrs) {
std::unordered_set<uint16_t> colors(RANGE(colorSets[startColorSetIdx]));
std::vector<size_t> members = {startIdx};
processed[startIdx] = true;
for (size_t nextIdx = startIdx + 1; nextIdx < processed.size(); ++nextIdx) {
if (processed[nextIdx] || from.isFree(nextIdx)) {
continue;
}
size_t nextColorSetIdx = from.slotAt(nextIdx)->colorSetIndex;
// If at least one color matches, add it
if (ColorSet const &colorSet = colorSets[attrs->colorSetIndex];
if (ColorSet const &colorSet = colorSets[nextColorSetIdx];
std::find_first_of(RANGE(colors), RANGE(colorSet)) != colors.end()) {
colors.insert(RANGE(colorSet));
members.push_back(wasProcessed - processed.begin());
*wasProcessed = true; // Mark the added color set as processed
members.push_back(nextIdx);
processed[nextIdx] = true; // Mark the added color set as processed
}
}
@@ -344,11 +354,8 @@ static void decant(std::vector<AssignedSets> &assignments, std::vector<ColorSet>
}
// Iterate through the component's color sets, and transfer them
auto member = from.begin();
size_t curIndex = 0;
for (size_t index : members) {
std::advance(member, index - curIndex);
curIndex = index;
auto member = from.slotAt(index);
to.assign(std::move(*member));
from.remove(member); // Removing does not shift elements, so it's cheap
}
+1
View File
@@ -708,6 +708,7 @@ void parseBackgroundPalSpec(char const *arg) {
break;
default:
error("Unknown background color specification \"%s\"", arg);
return;
}
if (arg[size + 1] != '\0') {
+1
View File
@@ -72,6 +72,7 @@ void layout_SetSectionType(SectionType type, uint32_t bank) {
bank,
typeInfo.lastBank
);
bank = typeInfo.lastBank;
}
setActiveTypeAndIdx(type, bank - typeInfo.firstBank);
+8 -8
View File
@@ -6,6 +6,7 @@
#include <deque>
#include <errno.h>
#include <inttypes.h>
#include <optional>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -112,23 +113,23 @@ static uint32_t checkOverlaySize() {
return 0;
}
long overlaySize = seekSize(overlayFile);
std::optional<uint64_t> overlaySize = seekSize(overlayFile);
if (overlaySize == -1) {
if (!overlaySize.has_value()) {
warnx("Overlay file is not seekable, cannot check if properly formed");
return 0;
}
if (overlaySize % BANK_SIZE) {
if (*overlaySize % BANK_SIZE) {
warnx("Overlay file does not have a size multiple of 0x4000");
} else if (options.is32kMode && overlaySize != 0x8000) {
} else if (options.is32kMode && *overlaySize != 0x8000) {
warnx("Overlay is not exactly 0x8000 bytes large");
}
if (overlaySize < 0x8000) {
if (*overlaySize < 0x8000) {
warnx("Overlay is less than 0x8000 bytes large");
}
return (overlaySize + BANK_SIZE - 1) / BANK_SIZE;
return (*overlaySize + BANK_SIZE - 1) / BANK_SIZE;
}
// Expand `sections[SECTTYPE_ROMX]` to cover all the overlay banks.
@@ -278,8 +279,7 @@ static void writeSymName(std::string const &name, FILE *file) {
// Decode the UTF-8 codepoint; or at least attempt to
Utf8Decoder decoder;
do {
if (decoder.update(*ptr) != UTF8_REJECT) {
++ptr;
if (decoder.update(*ptr++) != UTF8_REJECT) {
continue;
}
// This sequence was invalid; emit a U+FFFD, and recover
+7 -2
View File
@@ -585,15 +585,20 @@ static void applyFilePatches(Section &section, Section &dataSection) {
rpnErrorAt(patch, "PC has no value outside of a section");
dataSection.data[offset] = 0;
} else {
// A `jr` is *encoded* in ROM as a 1-byte (8-bit) offset, so here `typeSize == 8`,
// but the object's *value* size is a 16-bit absolute address, so we pass 16 here.
checkPatchSize(patch, value, 16);
// Offset is relative to the byte *after* the operand
// PC as operand to `jr` is lower than reference PC by 2
uint16_t address = patch.pcSection->org + patch.pcOffset + 2;
int32_t jumpOffset = value - address;
// The 16-bit truncation of `value - address` is intentional, since
// a low ROM0 address may `jr` backwards to a high HRAM one.
int16_t jumpOffset = static_cast<int16_t>(value - address);
if (jumpOffset < -128 || jumpOffset > 127) {
firstErrorAt(
patch,
"`JR` target must be between -128 and 127 bytes away, not %" PRId32
"`JR` target must be between -128 and 127 bytes away, not %" PRId16
"; use `JP` instead",
jumpOffset
);
+2 -7
View File
@@ -294,11 +294,7 @@ void sdobj_ReadFile(FileStackNode const &src, FILE *file, std::vector<Symbol> &f
uint32_t tmp = readInt(where, token, numberBase);
if (tmp > UINT16_MAX) {
fatalAt(
where,
"Area \"%s\" is larger than the GB address space",
curSection->name.c_str()
);
fatalAt(where, "Area \"%s\" is larger than the GB address space", sectName);
}
curSection->size = tmp;
@@ -575,9 +571,8 @@ void sdobj_ReadFile(FileStackNode const &src, FILE *file, std::vector<Symbol> &f
expectRelocation();
uint16_t idx = readByte(where, token, numberBase);
expectRelocation();
idx |= static_cast<uint16_t>(readByte(where, token, numberBase));
idx |= static_cast<uint16_t>(readByte(where, token, numberBase)) << 8;
// Loudly fail on unknown flags
if (flags & (1 << RELOC_ZPAGE | 1 << RELOC_NPAGE)) {
+9
View File
@@ -146,6 +146,15 @@ static void mergeSections(Section &target, std::unique_ptr<Section> &&other) {
case SECTION_FRAGMENT:
checkPieceCompat(target, *other, target.size);
// Check that `target.size += other->size` below will not overflow
if (target.size + other->size > UINT16_MAX) {
fatalTwoAt(
target,
*other,
"Section \"%s\" fragments combined are larger than the GB address space",
target.name.c_str()
);
}
// Append `other` to `target`
other->offset = target.size;
target.size += other->size;
+18 -13
View File
@@ -10,6 +10,7 @@
#include "helpers.hpp"
#include "platform.hpp"
#include "style.hpp"
#include "util.hpp" // parseWholeNumber
#include "version.hpp"
#if defined(_MSC_VER) || defined(__MINGW32__)
@@ -24,20 +25,19 @@ void Usage::printVersion(bool error) const {
}
void Usage::printAndExit(int code) const {
FILE *file;
bool isTerminal;
if (code) {
file = stderr;
isTerminal = isatty(STDERR_FILENO);
} else {
file = stdout;
isTerminal = isatty(STDOUT_FILENO);
// Usage flags can be long lines, so wrap them at a maximum line length
uint64_t maxLineLen = 0;
// Use the conventional COLUMNS environment variable, if it is defined and nonzero
if (char const *columnsStr = getenv("COLUMNS"); columnsStr) {
if (std::optional<uint64_t> columns = parseWholeNumber(columnsStr, BASE_10);
columns && *columns > 0) {
maxLineLen = *columns;
} else {
warnx("Ignoring invalid `COLUMNS` value \"%s\"", columnsStr);
}
}
// Use the console window width minus 1 as the maximum line length for flags,
// or the historically common 80 minus 1 if the output is not to a console TTY
size_t maxLineLen = 79;
if (isTerminal) {
// Otherwise, use the console window width minus 1, if the output is to a console TTY
if (maxLineLen == 0 && isatty(code ? STDERR_FILENO : STDOUT_FILENO)) {
// LCOV_EXCL_START
#if defined(_MSC_VER) || defined(__MINGW32__)
CONSOLE_SCREEN_BUFFER_INFO csbi;
@@ -54,8 +54,13 @@ void Usage::printAndExit(int code) const {
#endif
// LCOV_EXCL_STOP
}
// Otherwise, just use the historically common 80 minus 1
if (maxLineLen == 0) {
maxLineLen = 79;
}
// Print "Usage: <program name>"
FILE *file = code ? stderr : stdout;
style_Set(file, STYLE_GREEN, true);
fputs("Usage: ", file);
style_Set(file, STYLE_CYAN, true);
+6 -6
View File
@@ -25,18 +25,18 @@ int xclose(int fd) {
return close(fd);
}
long seekSize(FILE *file) {
std::optional<uint64_t> seekSize(FILE *file) {
if (fseek(file, 0, SEEK_END) != 0) {
return -1;
return std::nullopt;
}
long size = ftell(file);
auto size = ftell(file); // Use `auto` since Windows' `_ftelli64` returns `__int64`, not `long`
if (size < 0) {
return -1;
return std::nullopt;
}
if (fseek(file, 0, SEEK_SET) != 0) {
return -1;
return std::nullopt;
}
return size;
return static_cast<uint64_t>(size);
}
bool isNewline(int c) {
+1
View File
@@ -0,0 +1 @@
assert x, "oops \0 null"
+2
View File
@@ -0,0 +1,2 @@
FATAL: Assertion messages cannot contain '\0' characters
at assert-nul.asm(1)
+1 -2
View File
@@ -12,9 +12,8 @@ error: Macro argument `\<2147483647>` not defined
at negative-macro-args.asm::mac(11) <- negative-macro-args.asm(21)
error: Macro argument `\<-2147483648>` not defined
at negative-macro-args.asm::mac(14) <- negative-macro-args.asm(21)
error: Number in bracketed macro argument is too large
error: Macro argument `\<-2147483648>` not defined
at negative-macro-args.asm::mac(14) <- negative-macro-args.asm(21)
while expanding symbol `i`
error: Macro argument `\<-2147483648>` not defined
at negative-macro-args.asm::mac(15) <- negative-macro-args.asm(21)
error: Number in bracketed macro argument is too large
+1 -1
View File
@@ -8,7 +8,7 @@
0: ==
-8: ==
2147483647: ==
-2147483648: == >
-2147483648: ==
2147483648: == >
-1: G == G
4294967295: G == >
+2 -4
View File
@@ -12,8 +12,6 @@ error: Macro argument `\1` not defined
at negative-shifted-macro-args.asm::test(15) <- negative-shifted-macro-args.asm(22)
error: Macro argument `\<-1>` not defined
at negative-shifted-macro-args.asm::test(16) <- negative-shifted-macro-args.asm(22)
error: Number in bracketed macro argument is too large
error: Macro argument `\<-2147483648>` not defined
at negative-shifted-macro-args.asm::test(19) <- negative-shifted-macro-args.asm(22)
error: syntax error, unexpected >
at negative-shifted-macro-args.asm::test(19) <- negative-shifted-macro-args.asm(22)
Assembly aborted with 9 errors
Assembly aborted with 8 errors
+1
View File
@@ -11,3 +11,4 @@ $0
$A
+2
View File
@@ -0,0 +1,2 @@
SECTION "test\0 foo", ROM0
SECTION "test\0 bar", ROM0
+2
View File
@@ -0,0 +1,2 @@
FATAL: Section names cannot contain '\0' characters
at section-name-nul.asm(1)
+6
View File
@@ -0,0 +1,6 @@
MACRO m
println \1
shift $8000_0000 ; INT32_MIN
println \1
ENDM
m 1, 2, 3
+2
View File
@@ -0,0 +1,2 @@
warning: Cannot shift macro arguments past their beginning [-Wmacro-shift]
at shift-int-min.asm::m(3) <- shift-int-min.asm(6)
+2
View File
@@ -0,0 +1,2 @@
$1
$1
+4
View File
@@ -26,3 +26,7 @@ PRINTLN STRFMT("%d eol %", 1)
PRINTLN STRFMT("invalid %w spec", 42)
PRINTLN STRFMT("one=%d two=%d three=%d", 1)
DEF NUL EQUS STRFMT("%s \0 %s", "goodbye", "world")
ASSERT #NUL === "goodbye \0 world"
PRINTLN #NUL
Binary file not shown.
+1 -1
View File
@@ -1,7 +1,7 @@
EXT_TEST_OWNER=pinobatch
EXT_TEST_REPO=libbet
EXT_TEST_DOMAIN=github.com
EXT_TEST_COMMIT=e42c0036b18e6e715987b88b4973389b283974c9
EXT_TEST_COMMIT=1232e71dc1ac852786ad9825316e19d2b341bc4a
EXT_TEST_IS_NONFREE=false
EXT_TEST_TARGET=all
+1 -1
View File
@@ -1,7 +1,7 @@
EXT_TEST_OWNER=pret
EXT_TEST_REPO=pokecrystal
EXT_TEST_DOMAIN=github.com
EXT_TEST_COMMIT=31880ea20ea44f79ebd459b1e735ae1f15db1671
EXT_TEST_COMMIT=3438c7003a57fa2987fcb223d14b660761b33c64
EXT_TEST_IS_NONFREE=true
EXT_TEST_TARGET=compare
+1 -1
View File
@@ -1,7 +1,7 @@
EXT_TEST_OWNER=pret
EXT_TEST_REPO=pokered
EXT_TEST_DOMAIN=github.com
EXT_TEST_COMMIT=1e96034092686d006e863cace09e87273051a3d8
EXT_TEST_COMMIT=405b6246372d7e5a2cb029cbb65219b13286b8c9
EXT_TEST_IS_NONFREE=true
EXT_TEST_TARGET=compare
+2
View File
@@ -0,0 +1,2 @@
SECTION "jr", ROM0
jr CONSTANT
+1
View File
@@ -0,0 +1 @@
EXPORT DEF CONSTANT EQU $1234_0078
+2
View File
@@ -0,0 +1,2 @@
warning: Value $12340078 is not 16-bit [-Wtruncation]
at jr-truncation/a.asm(2)
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
; File generated by rgblink
12340078 CONSTANT
+3
View File
@@ -0,0 +1,3 @@
SECTION "low", ROM0[$0048]
LCDInterrupt:
jr hLCDInterruptHandler
+4
View File
@@ -0,0 +1,4 @@
SECTION "high", HRAM[$FFE0]
hLCDInterruptHandler::
.jp: db ; should be $c3 (jp)
.address: dw
Binary file not shown.
+5
View File
@@ -0,0 +1,5 @@
; File generated by rgblink
00:0048 LCDInterrupt
00:ffe0 hLCDInterruptHandler
00:ffe0 hLCDInterruptHandler.jp
00:ffe1 hLCDInterruptHandler.address
+1
View File
@@ -1,5 +1,6 @@
rom0 1
romx 0
romx 0xffff_ffff
vram 2
wram0 1
wramx 0
+9 -7
View File
@@ -2,16 +2,18 @@ error: ROM0 bank 1 does not exist (the maximum is 0)
at script-oob-bank-num.link(1)
error: ROMX bank 0 does not exist (the minimum is 1)
at script-oob-bank-num.link(2)
error: VRAM bank 2 does not exist (the maximum is 1)
error: ROMX bank 4294967295 does not exist (the maximum is 65535)
at script-oob-bank-num.link(3)
error: WRAM0 bank 1 does not exist (the maximum is 0)
error: VRAM bank 2 does not exist (the maximum is 1)
at script-oob-bank-num.link(4)
error: WRAMX bank 0 does not exist (the minimum is 1)
error: WRAM0 bank 1 does not exist (the maximum is 0)
at script-oob-bank-num.link(5)
error: WRAMX bank 8 does not exist (the maximum is 7)
error: WRAMX bank 0 does not exist (the minimum is 1)
at script-oob-bank-num.link(6)
error: OAM bank 1 does not exist (the maximum is 0)
error: WRAMX bank 8 does not exist (the maximum is 7)
at script-oob-bank-num.link(7)
error: HRAM bank 1 does not exist (the maximum is 0)
error: OAM bank 1 does not exist (the maximum is 0)
at script-oob-bank-num.link(8)
Linking failed with 8 errors
error: HRAM bank 1 does not exist (the maximum is 0)
at script-oob-bank-num.link(9)
Linking failed with 9 errors
+1 -1
View File
@@ -7,4 +7,4 @@ SECTION "ROM2 1K", ROMX,BANK[2]
ds $1000
SECTION "ROM2 1", ROMX,BANK[2]
ds 1
SECTION "\\\"\'\n\r\t\0", ROM0
SECTION "\\\"\'\n\r\t", ROM0
@@ -0,0 +1,2 @@
SECTION FRAGMENT "output", ROM0
ds $4000
@@ -0,0 +1,5 @@
FATAL: Section "output" fragments combined are larger than the GB address space
at section-fragment/size-overflow/a.asm(1)
and also:
at section-fragment/size-overflow/a.asm(1)
Linking aborted with 1 error
+29
View File
@@ -191,6 +191,27 @@ tryDiff "$test"/ref.out.map "$outtemp"
tryDiff "$test"/ref.out.sym "$outtemp2"
evaluateTest
test="jr-wraparound"
startTest
"$RGBASM" -o "$otemp" "$test"/a.asm
"$RGBASM" -o "$outtemp" "$test"/b.asm
continueTest
rgblinkQuiet -o "$gbtemp" -n "$outtemp2" "$otemp" "$outtemp"
tryCmpRom "$test"/ref.out.bin
tryDiff "$test"/ref.out.sym "$outtemp2"
evaluateTest
test="jr-truncation"
startTest
"$RGBASM" -o "$otemp" "$test"/a.asm
"$RGBASM" -o "$outtemp" "$test"/b.asm
continueTest
rgblinkQuiet -o "$gbtemp" -n "$outtemp2" "$otemp" "$outtemp" 2>"$outtemp3"
tryDiff "$test"/out.err "$outtemp3"
tryCmpRom "$test"/ref.out.bin
tryDiff "$test"/ref.out.sym "$outtemp2"
evaluateTest
test="high-low"
startTest
"$RGBASM" -o "$otemp" "$test"/a.asm
@@ -377,6 +398,14 @@ rgblinkQuiet -o "$gbtemp" "$otemp" "$gbtemp2"
tryCmpRom "$test"/ref.out.bin
evaluateTest
test="section-fragment/size-overflow"
startTest
"$RGBASM" -o "$otemp" "$test"/a.asm
continueTest
rgblinkQuiet "$otemp" "$otemp" "$otemp" "$otemp" 2>"$outtemp"
tryDiff "$test"/out.err "$outtemp"
evaluateTest
test="section-fragment/jr-offset"
startTest
"$RGBASM" -o "$otemp" "$test"/a.asm