mirror of
https://github.com/gbdev/rgbds.git
synced 2026-08-23 07:14:31 +00:00
Compare commits
33
Commits
v1.0.2+hotfix
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2c52caca2 | ||
|
|
21a4b85a4f | ||
|
|
9fa5058add | ||
|
|
e287ee2724 | ||
|
|
0888600cb7 | ||
|
|
8bc7de35f9 | ||
|
|
ed0a2d1075 | ||
|
|
71d9e236bc | ||
|
|
d9003f633a | ||
|
|
cda77721d4 | ||
|
|
307846b03e | ||
|
|
e7e671b249 | ||
|
|
0c772ea049 | ||
|
|
6f64fd00f6 | ||
|
|
4384223951 | ||
|
|
4e87f61656 | ||
|
|
9d854114ea | ||
|
|
70d8d67ffc | ||
|
|
ca2b8e4cde | ||
|
|
80c8e77b19 | ||
|
|
604c69e048 | ||
|
|
ec5e6cbabb | ||
|
|
50678d0942 | ||
|
|
c691ec6607 | ||
|
|
c3073f4318 | ||
|
|
e5b03e3cb1 | ||
|
|
0d379e9016 | ||
|
|
9cbfec30c7 | ||
|
|
9def9c9aa8 | ||
|
|
b5681c0d34 | ||
|
|
ec43f06bd9 | ||
|
|
106ec399d5 | ||
|
|
d9c0d37fd5 |
Executable
+11
@@ -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"
|
||||
@@ -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`.
|
||||
|
||||
@@ -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
@@ -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
@@ -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 . .
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd July 20, 2026
|
||||
.Dd August 1, 2026
|
||||
.Dt GBZ80 7
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
+1
-1
@@ -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
@@ -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
@@ -2,7 +2,7 @@
|
||||
.\"
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd July 20, 2026
|
||||
.Dd August 1, 2026
|
||||
.Dt RGBASM 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd July 20, 2026
|
||||
.Dd August 1, 2026
|
||||
.Dt RGBDS 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd July 20, 2026
|
||||
.Dd August 1, 2026
|
||||
.Dt RGBDS 7
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
+4
-1
@@ -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
@@ -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
@@ -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
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd July 20, 2026
|
||||
.Dd August 1, 2026
|
||||
.Dt RGBLINK 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
+1
-1
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -708,6 +708,7 @@ void parseBackgroundPalSpec(char const *arg) {
|
||||
break;
|
||||
default:
|
||||
error("Unknown background color specification \"%s\"", arg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (arg[size + 1] != '\0') {
|
||||
|
||||
@@ -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
@@ -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
@@ -585,15 +585,20 @@ static void applyFilePatches(Section §ion, 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
|
||||
);
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
assert x, "oops \0 null"
|
||||
@@ -0,0 +1,2 @@
|
||||
FATAL: Assertion messages cannot contain '\0' characters
|
||||
at assert-nul.asm(1)
|
||||
@@ -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
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
0: ==
|
||||
-8: ==
|
||||
2147483647: ==
|
||||
-2147483648: == >
|
||||
-2147483648: ==
|
||||
2147483648: == >
|
||||
-1: G == G
|
||||
4294967295: G == >
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -11,3 +11,4 @@ $0
|
||||
|
||||
|
||||
$A
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
SECTION "test\0 foo", ROM0
|
||||
SECTION "test\0 bar", ROM0
|
||||
@@ -0,0 +1,2 @@
|
||||
FATAL: Section names cannot contain '\0' characters
|
||||
at section-name-nul.asm(1)
|
||||
@@ -0,0 +1,6 @@
|
||||
MACRO m
|
||||
println \1
|
||||
shift $8000_0000 ; INT32_MIN
|
||||
println \1
|
||||
ENDM
|
||||
m 1, 2, 3
|
||||
@@ -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)
|
||||
@@ -0,0 +1,2 @@
|
||||
$1
|
||||
$1
|
||||
@@ -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.
Vendored
+1
-1
@@ -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
|
||||
|
||||
Vendored
+1
-1
@@ -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
|
||||
|
||||
Vendored
+1
-1
@@ -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
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
SECTION "jr", ROM0
|
||||
jr CONSTANT
|
||||
@@ -0,0 +1 @@
|
||||
EXPORT DEF CONSTANT EQU $1234_0078
|
||||
@@ -0,0 +1,2 @@
|
||||
warning: Value $12340078 is not 16-bit [-Wtruncation]
|
||||
at jr-truncation/a.asm(2)
|
||||
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
; File generated by rgblink
|
||||
12340078 CONSTANT
|
||||
@@ -0,0 +1,3 @@
|
||||
SECTION "low", ROM0[$0048]
|
||||
LCDInterrupt:
|
||||
jr hLCDInterruptHandler
|
||||
@@ -0,0 +1,4 @@
|
||||
SECTION "high", HRAM[$FFE0]
|
||||
hLCDInterruptHandler::
|
||||
.jp: db ; should be $c3 (jp)
|
||||
.address: dw
|
||||
Binary file not shown.
@@ -0,0 +1,5 @@
|
||||
; File generated by rgblink
|
||||
00:0048 LCDInterrupt
|
||||
00:ffe0 hLCDInterruptHandler
|
||||
00:ffe0 hLCDInterruptHandler.jp
|
||||
00:ffe1 hLCDInterruptHandler.address
|
||||
@@ -1,5 +1,6 @@
|
||||
rom0 1
|
||||
romx 0
|
||||
romx 0xffff_ffff
|
||||
vram 2
|
||||
wram0 1
|
||||
wramx 0
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user