mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Compare commits
34 Commits
v0.9.0-rc1
...
v0.9.0-rc2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c495c31d9 | ||
|
|
90286ccbbc | ||
|
|
b33aa31944 | ||
|
|
dd6c741143 | ||
|
|
3b3263273c | ||
|
|
bc5a71ff88 | ||
|
|
e623aeb85d | ||
|
|
a2ff653a83 | ||
|
|
a13723c523 | ||
|
|
cf85146353 | ||
|
|
a9e49a09fd | ||
|
|
cbe44fed9b | ||
|
|
c439b8e27f | ||
|
|
86bf289452 | ||
|
|
e1ac7f389d | ||
|
|
d5159f66be | ||
|
|
c7a029a051 | ||
|
|
d5ded84501 | ||
|
|
4cd0dd5314 | ||
|
|
9783671399 | ||
|
|
8037b9e10a | ||
|
|
7c74653aa1 | ||
|
|
22767e36e2 | ||
|
|
6b89938da7 | ||
|
|
15919e550f | ||
|
|
f93587c805 | ||
|
|
a870f7de10 | ||
|
|
6b72067387 | ||
|
|
84c01f064f | ||
|
|
5d3e96662e | ||
|
|
91580043e0 | ||
|
|
3e28e92622 | ||
|
|
d494f73825 | ||
|
|
b03a5b13b7 |
44
.github/workflows/build-container.yml
vendored
Normal file
44
.github/workflows/build-container.yml
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
name: Build container image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- '*' # This triggers the action on all tag pushes
|
||||
|
||||
jobs:
|
||||
publish-docker-image:
|
||||
if: github.repository_owner == 'gbdev'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# So that the workflow can write to the ghcr an upload there
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push the master container image
|
||||
# When a commit is pushed to master
|
||||
if: github.ref == 'refs/heads/master'
|
||||
run: |
|
||||
COMMIT_HASH=$(git rev-parse --short HEAD)
|
||||
sed -i "2i LABEL org.opencontainers.image.description=\"RGBDS container image, containing the git version master:$COMMIT_HASH\"" Dockerfile
|
||||
docker build . --tag ghcr.io/gbdev/rgbds:master
|
||||
docker push ghcr.io/gbdev/rgbds:master
|
||||
|
||||
- name: Build and push the version-tagged container image
|
||||
# When a tag is pushed
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
run: |
|
||||
TAG_NAME=${GITHUB_REF#refs/tags/}
|
||||
sed -i "2i LABEL org.opencontainers.image.description=\"RGBDS container image for the release version $TAG_NAME\"" Dockerfile
|
||||
docker build . --tag ghcr.io/gbdev/rgbds:$TAG_NAME
|
||||
docker push ghcr.io/gbdev/rgbds:$TAG_NAME
|
||||
2
.github/workflows/checkdiff.yml
vendored
2
.github/workflows/checkdiff.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: "Code coverage checking"
|
||||
name: Code coverage checking
|
||||
on: pull_request
|
||||
|
||||
jobs:
|
||||
|
||||
26
.github/workflows/create-release-artifacts.yml
vendored
26
.github/workflows/create-release-artifacts.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: "Create release artifacts"
|
||||
name: Create release artifacts
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
@@ -24,30 +24,32 @@ jobs:
|
||||
run: | # Turn "vX.Y.Z" into "X.Y.Z"
|
||||
VERSION="${{ github.ref_name }}"
|
||||
echo "version=${VERSION#v}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v4
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
- name: Install deps
|
||||
run: .github/scripts/get_win_deps.ps1
|
||||
- uses: actions/cache@v4
|
||||
- name: Check libraries cache
|
||||
id: cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
zbuild
|
||||
pngbuild
|
||||
key: ${{ matrix.arch }}-${{ hashFiles('zlib/**', 'libpng/**') }}
|
||||
- name: Build zlib
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: | # BUILD_SHARED_LIBS causes the output DLL to be correctly called `zlib1.dll`
|
||||
cmake -S zlib -B zbuild -A ${{ matrix.platform }} -Wno-dev -DCMAKE_INSTALL_PREFIX=install_dir -DBUILD_SHARED_LIBS=ON
|
||||
cmake --build zbuild --config Release -j
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
- name: Install zlib
|
||||
run: |
|
||||
cmake --install zbuild
|
||||
- name: Build libpng
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
cmake -S libpng -B pngbuild -A ${{ matrix.platform }} -Wno-dev -DCMAKE_INSTALL_PREFIX=install_dir -DPNG_SHARED=ON -DPNG_STATIC=OFF -DPNG_TESTS=OFF
|
||||
cmake --build pngbuild --config Release -j
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
- name: Install libpng
|
||||
run: |
|
||||
cmake --install pngbuild
|
||||
@@ -74,7 +76,8 @@ jobs:
|
||||
run: | # Turn "refs/tags/vX.Y.Z" into "X.Y.Z"
|
||||
VERSION="${{ github.ref_name }}"
|
||||
echo "version=${VERSION#v}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v4
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
- name: Install deps
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -104,7 +107,8 @@ jobs:
|
||||
run: | # Turn "refs/tags/vX.Y.Z" into "X.Y.Z"
|
||||
VERSION="${{ github.ref_name }}"
|
||||
echo "version=${VERSION#v}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v4
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
- name: Install deps
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -133,17 +137,19 @@ jobs:
|
||||
run: | # Turn "refs/tags/vX.Y.Z" into "X.Y.Z"
|
||||
VERSION="${{ github.ref_name }}"
|
||||
echo "version=${VERSION#v}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v4
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
- name: Package sources
|
||||
run: |
|
||||
make dist Q=
|
||||
ls
|
||||
- uses: actions/download-artifact@v4
|
||||
- name: Download Linux binaries
|
||||
uses: actions/download-artifact@v4
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
body: |
|
||||
Please ensure that the four packages below work properly.
|
||||
Please ensure that the packages below work properly.
|
||||
Once that's done, replace this text with the changelog, un-draft the release, and update the `release` branch.
|
||||
By the way, if you forgot to update `include/version.hpp`, RGBASM's version test is gonna fail in the tag's regression testing! (Use `git push --delete origin <tag>` to delete it)
|
||||
draft: true # Don't publish the release quite yet...
|
||||
|
||||
4
.github/workflows/create-release-docs.yml
vendored
4
.github/workflows/create-release-docs.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: "Create release docs"
|
||||
name: Create release docs
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
if: github.repository_owner == 'gbdev'
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout rgbds@release
|
||||
uses: actions/checkout@v4
|
||||
|
||||
43
.github/workflows/testing.yml
vendored
43
.github/workflows/testing.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: "Regression testing"
|
||||
name: Regression testing
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
@@ -17,7 +17,8 @@ jobs:
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
- name: Install deps
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -32,9 +33,7 @@ jobs:
|
||||
run: |
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} -DSANITIZERS=ON -DMORE_WARNINGS=ON
|
||||
cmake --build build -j --verbose
|
||||
cp build/src/rgb{asm,link,fix,gfx} .
|
||||
sudo cmake --install build --verbose
|
||||
cmake --install build --verbose --component "Test support programs"
|
||||
- name: Package binaries
|
||||
run: |
|
||||
mkdir bins
|
||||
@@ -58,8 +57,8 @@ jobs:
|
||||
with:
|
||||
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
||||
key: ${{ matrix.os }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
||||
- if: steps.test-deps-cache.outputs.cache-hit != 'true'
|
||||
name: Fetch test dependency repositories
|
||||
- name: Fetch test dependency repositories
|
||||
if: steps.test-deps-cache.outputs.cache-hit != 'true'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
test/fetch-test-deps.sh
|
||||
@@ -75,7 +74,8 @@ jobs:
|
||||
macos-static:
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
- name: Install deps
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -109,8 +109,8 @@ jobs:
|
||||
with:
|
||||
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
||||
key: ${{ matrix.os }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
||||
- if: steps.test-deps-cache.outputs.cache-hit != 'true'
|
||||
name: Fetch test dependency repositories
|
||||
- name: Fetch test dependency repositories
|
||||
if: steps.test-deps-cache.outputs.cache-hit != 'true'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
test/fetch-test-deps.sh
|
||||
@@ -138,30 +138,32 @@ jobs:
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
- name: Install deps
|
||||
run: .github/scripts/get_win_deps.ps1
|
||||
- uses: actions/cache@v4
|
||||
- name: Check libraries cache
|
||||
id: cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
zbuild
|
||||
pngbuild
|
||||
key: ${{ matrix.arch }}-${{ hashFiles('zlib/**', 'libpng/**') }}
|
||||
- name: Build zlib
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: | # BUILD_SHARED_LIBS causes the output DLL to be correctly called `zlib1.dll`
|
||||
cmake -S zlib -B zbuild -A ${{ matrix.platform }} -Wno-dev -DCMAKE_INSTALL_PREFIX=install_dir -DBUILD_SHARED_LIBS=ON
|
||||
cmake --build zbuild --config Release -j
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
- name: Install zlib
|
||||
run: |
|
||||
cmake --install zbuild
|
||||
- name: Build libpng
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
cmake -S libpng -B pngbuild -A ${{ matrix.platform }} -Wno-dev -DCMAKE_INSTALL_PREFIX=install_dir -DPNG_SHARED=ON -DPNG_STATIC=OFF -DPNG_TESTS=OFF
|
||||
cmake --build pngbuild --config Release -j
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
- name: Install libpng
|
||||
run: |
|
||||
cmake --install pngbuild
|
||||
@@ -171,7 +173,6 @@ jobs:
|
||||
cmake -S . -B build -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build build --config Release -j --verbose
|
||||
cmake --install build --verbose --prefix install_dir
|
||||
cmake --install build --verbose --component "Test support programs"
|
||||
- name: Package binaries
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -196,8 +197,8 @@ jobs:
|
||||
with:
|
||||
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
||||
key: ${{ matrix.os }}-${{ matrix.bits }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
||||
- if: steps.test-deps-cache.outputs.cache-hit != 'true'
|
||||
name: Fetch test dependency repositories
|
||||
- name: Fetch test dependency repositories
|
||||
if: steps.test-deps-cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
continue-on-error: true
|
||||
run: |
|
||||
@@ -229,7 +230,8 @@ jobs:
|
||||
env:
|
||||
DIST_DIR: win${{ matrix.bits }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
- name: Install deps
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -273,7 +275,8 @@ jobs:
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
- name: Retrieve binaries
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -303,8 +306,8 @@ jobs:
|
||||
with:
|
||||
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
||||
key: mingw-${{ matrix.bits }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
||||
- if: steps.test-deps-cache.outputs.cache-hit != 'true'
|
||||
name: Fetch test dependency repositories
|
||||
- name: Fetch test dependency repositories
|
||||
if: steps.test-deps-cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
continue-on-error: true
|
||||
run: |
|
||||
|
||||
4
.github/workflows/update-master-docs.yml
vendored
4
.github/workflows/update-master-docs.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: "Update master docs"
|
||||
name: Update master docs
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -17,7 +17,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
if: github.repository_owner == 'gbdev'
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout rgbds@master
|
||||
uses: actions/checkout@v4
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -13,4 +13,5 @@
|
||||
CMakeCache.txt
|
||||
CMakeFiles/
|
||||
cmake_install.cmake
|
||||
build/
|
||||
callgrind.out.*
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
# 3.9 required for LTO checks
|
||||
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
|
||||
# 3.17 optional for CMAKE_CTEST_ARGUMENTS
|
||||
cmake_minimum_required(VERSION 3.9..3.17 FATAL_ERROR)
|
||||
|
||||
project(rgbds
|
||||
LANGUAGES CXX)
|
||||
|
||||
include(CTest)
|
||||
|
||||
# get real path of source and binary directories
|
||||
get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH)
|
||||
get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH)
|
||||
@@ -41,7 +44,6 @@ else()
|
||||
# does not recognize this yet.
|
||||
add_compile_options(-Wno-gnu-zero-variadic-macro-arguments)
|
||||
endif()
|
||||
add_definitions(-D_POSIX_C_SOURCE=200809L)
|
||||
if(SANITIZERS)
|
||||
set(SAN_FLAGS -fsanitize=shift -fsanitize=integer-divide-by-zero
|
||||
-fsanitize=unreachable -fsanitize=vla-bound
|
||||
@@ -88,6 +90,8 @@ endif(GIT)
|
||||
find_package(PkgConfig)
|
||||
if(MSVC OR NOT PKG_CONFIG_FOUND)
|
||||
# fallback to find_package
|
||||
# cmake's FindPNG is very fragile; it breaks when multiple versions are installed
|
||||
# this is most evident on macOS but can occur on Linux too
|
||||
find_package(PNG REQUIRED)
|
||||
else()
|
||||
pkg_check_modules(LIBPNG REQUIRED libpng)
|
||||
@@ -99,6 +103,7 @@ set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
|
||||
add_subdirectory(src)
|
||||
set(CMAKE_CTEST_ARGUMENTS "--verbose")
|
||||
add_subdirectory(test)
|
||||
|
||||
# By default, build in Release mode; Debug mode must be explicitly requested
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
FROM debian:11-slim
|
||||
LABEL org.opencontainers.image.source=https://github.com/gbdev/rgbds
|
||||
ARG version=0.9.0-rc1
|
||||
ARG version=0.9.0-rc2
|
||||
WORKDIR /rgbds
|
||||
|
||||
COPY . .
|
||||
|
||||
9
Makefile
9
Makefile
@@ -26,19 +26,16 @@ PNGLDLIBS := `${PKG_CONFIG} --libs-only-l libpng`
|
||||
# Note: if this comes up empty, `version.cpp` will automatically fall back to last release number
|
||||
VERSION_STRING := `git --git-dir=.git describe --tags --dirty --always 2>/dev/null`
|
||||
|
||||
WARNFLAGS := -Wall -pedantic -Wno-unknown-warning-option \
|
||||
-Wno-gnu-zero-variadic-macro-arguments
|
||||
WARNFLAGS := -Wall -pedantic -Wno-unknown-warning-option -Wno-gnu-zero-variadic-macro-arguments
|
||||
|
||||
# Overridable CXXFLAGS
|
||||
CXXFLAGS ?= -O3 -flto -DNDEBUG
|
||||
# Non-overridable CXXFLAGS
|
||||
REALCXXFLAGS := ${CXXFLAGS} ${WARNFLAGS} -std=c++2a -I include \
|
||||
-D_POSIX_C_SOURCE=200809L -fno-exceptions -fno-rtti
|
||||
REALCXXFLAGS := ${CXXFLAGS} ${WARNFLAGS} -std=c++2a -I include -fno-exceptions -fno-rtti
|
||||
# Overridable LDFLAGS
|
||||
LDFLAGS ?=
|
||||
# Non-overridable LDFLAGS
|
||||
REALLDFLAGS := ${LDFLAGS} ${WARNFLAGS} \
|
||||
-DBUILD_VERSION_STRING=\"${VERSION_STRING}\"
|
||||
REALLDFLAGS := ${LDFLAGS} ${WARNFLAGS} -DBUILD_VERSION_STRING=\"${VERSION_STRING}\"
|
||||
|
||||
# Wrapper around bison that passes flags depending on what the version supports
|
||||
BISON := src/bison.sh
|
||||
|
||||
19
RELEASE.md
19
RELEASE.md
@@ -3,13 +3,14 @@
|
||||
This describes for the maintainers of RGBDS how to publish a new release on
|
||||
GitHub.
|
||||
|
||||
1. Update, commit, and push [include/version.hpp](include/version.hpp) with
|
||||
values for `PACKAGE_VERSION_MAJOR`, `PACKAGE_VERSION_MINOR`,
|
||||
`PACKAGE_VERSION_PATCH`, and `PACKAGE_VERSION_RC`, as well as
|
||||
[Dockerfile](Dockerfile) with a value for `ARG version`. Only define
|
||||
`PACKAGE_VERSION_RC` if you are publishing a release candidate! You can
|
||||
use <code>git commit -m "Release <i><version></i>"</code> and
|
||||
`git push origin master`.
|
||||
1. Update the following files, then commit and push.
|
||||
You can use <code>git commit -m "Release <i><version></i>"</code> and `git push origin master`.
|
||||
|
||||
- [include/version.hpp](include/version.hpp): set appropriate values for `PACKAGE_VERSION_MAJOR`, `PACKAGE_VERSION_MINOR`, `PACKAGE_VERSION_PATCH`, and `PACKAGE_VERSION_RC`
|
||||
**Only** define `PACKAGE_VERSION_RC` if you are publishing a release candidate!
|
||||
- [Dockerfile](Dockerfile): update `ARG version`.
|
||||
- [test/fetch-test-deps.sh](test/fetch-test-deps.sh): update test dependency commits (preferably, use the latest available).
|
||||
- [man/\*](man/): update dates and authors.
|
||||
|
||||
2. Create a Git tag formatted as <code>v<i><MAJOR></i>.<i><MINOR></i>.<i><PATCH></i></code>,
|
||||
or <code>v<i><MAJOR></i>.<i><MINOR></i>.<i><PATCH></i>-rc<i><RC></i></code>
|
||||
@@ -38,7 +39,9 @@ GitHub.
|
||||
4. GitHub Actions will run the [create-release-docs.yml](.github/workflows/create-release-docs.yml)
|
||||
workflow to add the release documentation to [rgbds-www](https://github.com/gbdev/rgbds-www).
|
||||
|
||||
This is not done automatically for prereleases; if you want to do this manually,
|
||||
This is not done automatically for prereleases, since we do not normally publish documentation
|
||||
for them. If you want to manually publish prerelease documentation, such as for an April Fools
|
||||
joke prerelease,
|
||||
|
||||
1. Clone [rgbds-www](https://github.com/gbdev/rgbds-www). You can use
|
||||
`git clone https://github.com/gbdev/rgbds-www.git`.
|
||||
|
||||
@@ -192,6 +192,7 @@ _rgbasm_completions() {
|
||||
shift-amount
|
||||
truncation
|
||||
unmapped-char
|
||||
unterminated-load
|
||||
user
|
||||
all
|
||||
extra
|
||||
|
||||
@@ -26,6 +26,7 @@ _rgbasm_warnings() {
|
||||
'shift-amount:Warn when a shift'\''s operand it negative or \> 32'
|
||||
'truncation:Warn when implicit truncation loses bits'
|
||||
'unmapped-char:Warn on unmapped character'
|
||||
'unterminated-load:Warn on LOAD without ENDL'
|
||||
'user:Warn when executing the WARN built-in'
|
||||
)
|
||||
# TODO: handle `no-` and `error=` somehow?
|
||||
|
||||
@@ -70,7 +70,8 @@ void sect_SetLoadSection(
|
||||
SectionSpec const &attrs,
|
||||
SectionModifier mod
|
||||
);
|
||||
void sect_EndLoadSection();
|
||||
void sect_EndLoadSection(char const *cause);
|
||||
void sect_CheckLoadClosed();
|
||||
|
||||
Section *sect_GetSymbolSection();
|
||||
uint32_t sect_GetSymbolOffset();
|
||||
|
||||
@@ -5,31 +5,29 @@
|
||||
|
||||
extern unsigned int nbErrors, maxErrors;
|
||||
|
||||
enum WarningState { WARNING_DEFAULT, WARNING_DISABLED, WARNING_ENABLED, WARNING_ERROR };
|
||||
|
||||
enum WarningID {
|
||||
WARNING_ASSERT, // Assertions
|
||||
WARNING_BACKWARDS_FOR, // `for` loop with backwards range
|
||||
WARNING_BACKWARDS_FOR, // `FOR` loop with backwards range
|
||||
WARNING_BUILTIN_ARG, // Invalid args to builtins
|
||||
WARNING_CHARMAP_REDEF, // Charmap entry re-definition
|
||||
WARNING_DIV, // Division undefined behavior
|
||||
WARNING_DIV, // Undefined division behavior
|
||||
WARNING_EMPTY_DATA_DIRECTIVE, // `db`, `dw` or `dl` directive without data in ROM
|
||||
WARNING_EMPTY_MACRO_ARG, // Empty macro argument
|
||||
WARNING_EMPTY_STRRPL, // Empty second argument in `STRRPL`
|
||||
WARNING_LARGE_CONSTANT, // Constants too large
|
||||
WARNING_MACRO_SHIFT, // Shift past available arguments in macro
|
||||
WARNING_MACRO_SHIFT, // `SHIFT` past available arguments in macro
|
||||
WARNING_NESTED_COMMENT, // Comment-start delimiter in a block comment
|
||||
WARNING_OBSOLETE, // Obsolete things
|
||||
WARNING_SHIFT, // Shifting undefined behavior
|
||||
WARNING_SHIFT_AMOUNT, // Strange shift amount
|
||||
WARNING_USER, // User warnings
|
||||
WARNING_OBSOLETE, // Obsolete/deprecated things
|
||||
WARNING_SHIFT, // Undefined `SHIFT` behavior
|
||||
WARNING_SHIFT_AMOUNT, // Strange `SHIFT` amount
|
||||
WARNING_UNTERMINATED_LOAD, // `LOAD` without `ENDL`
|
||||
WARNING_USER, // User-defined `WARN`ings
|
||||
|
||||
NB_PLAIN_WARNINGS,
|
||||
|
||||
// Warnings past this point are "parametric" warnings, only mapping to a single flag
|
||||
#define PARAM_WARNINGS_START NB_PLAIN_WARNINGS
|
||||
// Warnings past this point are "parametric" warnings, only mapping to a single flag
|
||||
// Treating string as number may lose some bits
|
||||
WARNING_NUMERIC_STRING_1 = PARAM_WARNINGS_START,
|
||||
WARNING_NUMERIC_STRING_1 = NB_PLAIN_WARNINGS,
|
||||
WARNING_NUMERIC_STRING_2,
|
||||
// Purging an exported symbol or label
|
||||
WARNING_PURGE_1,
|
||||
@@ -41,20 +39,24 @@ enum WarningID {
|
||||
WARNING_UNMAPPED_CHAR_1,
|
||||
WARNING_UNMAPPED_CHAR_2,
|
||||
|
||||
NB_PLAIN_AND_PARAM_WARNINGS,
|
||||
#define NB_PARAM_WARNINGS (NB_PLAIN_AND_PARAM_WARNINGS - PARAM_WARNINGS_START)
|
||||
|
||||
// Warnings past this point are "meta" warnings
|
||||
#define META_WARNINGS_START NB_PLAIN_AND_PARAM_WARNINGS
|
||||
WARNING_ALL = META_WARNINGS_START,
|
||||
WARNING_EXTRA,
|
||||
WARNING_EVERYTHING,
|
||||
|
||||
NB_WARNINGS,
|
||||
#define NB_META_WARNINGS (NB_WARNINGS - META_WARNINGS_START)
|
||||
};
|
||||
|
||||
extern WarningState warningStates[NB_PLAIN_AND_PARAM_WARNINGS];
|
||||
enum WarningAbled { WARNING_DEFAULT, WARNING_ENABLED, WARNING_DISABLED };
|
||||
|
||||
struct WarningState {
|
||||
WarningAbled state;
|
||||
WarningAbled error;
|
||||
|
||||
void update(WarningState other);
|
||||
};
|
||||
|
||||
struct Diagnostics {
|
||||
WarningState flagStates[NB_WARNINGS];
|
||||
WarningState metaStates[NB_WARNINGS];
|
||||
};
|
||||
|
||||
extern Diagnostics warningStates;
|
||||
extern bool warningsAreErrors;
|
||||
|
||||
void processWarningFlag(char const *flag);
|
||||
|
||||
@@ -107,20 +107,18 @@ struct Palette {
|
||||
uint8_t size() const;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template<typename T, T... i>
|
||||
static constexpr auto flipTable(std::integer_sequence<T, i...>) {
|
||||
return std::array{[](uint8_t byte) {
|
||||
// Flipping tends to happen fairly often, so take a bite out of dcache to speed it up
|
||||
static constexpr auto flipTable = ([]() constexpr {
|
||||
std::array<uint16_t, 256> table{};
|
||||
for (uint16_t i = 0; i < table.size(); i++) {
|
||||
// To flip all the bits, we'll flip both nibbles, then each nibble half, etc.
|
||||
uint16_t byte = i;
|
||||
byte = (byte & 0b0000'1111) << 4 | (byte & 0b1111'0000) >> 4;
|
||||
byte = (byte & 0b0011'0011) << 2 | (byte & 0b1100'1100) >> 2;
|
||||
byte = (byte & 0b0101'0101) << 1 | (byte & 0b1010'1010) >> 1;
|
||||
return byte;
|
||||
}(i)...};
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
// Flipping tends to happen fairly often, so take a bite out of dcache to speed it up
|
||||
static constexpr auto flipTable = detail::flipTable(std::make_integer_sequence<uint16_t, 256>());
|
||||
table[i] = byte;
|
||||
}
|
||||
return table;
|
||||
})();
|
||||
|
||||
#endif // RGBDS_GFX_MAIN_HPP
|
||||
|
||||
@@ -6,19 +6,15 @@
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include "defaultinitalloc.hpp"
|
||||
#include "defaultinitvec.hpp"
|
||||
|
||||
struct Palette;
|
||||
class ProtoPalette;
|
||||
|
||||
namespace packing {
|
||||
|
||||
/*
|
||||
* Returns which palette each proto-palette maps to, and how many palettes are necessary
|
||||
*/
|
||||
std::tuple<DefaultInitVec<size_t>, size_t>
|
||||
overloadAndRemove(std::vector<ProtoPalette> const &protoPalettes);
|
||||
|
||||
} // namespace packing
|
||||
|
||||
#endif // RGBDS_GFX_PAL_PACKING_HPP
|
||||
|
||||
@@ -12,20 +12,16 @@
|
||||
|
||||
struct Palette;
|
||||
|
||||
namespace sorting {
|
||||
|
||||
void indexed(
|
||||
void sortIndexed(
|
||||
std::vector<Palette> &palettes,
|
||||
int palSize,
|
||||
png_color const *palRGB,
|
||||
int palAlphaSize,
|
||||
png_byte *palAlpha
|
||||
);
|
||||
void grayscale(
|
||||
void sortGrayscale(
|
||||
std::vector<Palette> &palettes, std::array<std::optional<Rgba>, 0x8001> const &colors
|
||||
);
|
||||
void rgb(std::vector<Palette> &palettes);
|
||||
|
||||
} // namespace sorting
|
||||
void sortRgb(std::vector<Palette> &palettes);
|
||||
|
||||
#endif // RGBDS_GFX_PAL_SORTING_HPP
|
||||
|
||||
@@ -18,12 +18,8 @@ private:
|
||||
std::array<uint16_t, capacity> _colorIndices{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX};
|
||||
|
||||
public:
|
||||
/*
|
||||
* Adds the specified color to the set, or **silently drops it** if the set is full.
|
||||
*
|
||||
* Returns whether the color was unique.
|
||||
*/
|
||||
bool add(uint16_t color);
|
||||
// Adds the specified color to the set, or **silently drops it** if the set is full.
|
||||
void add(uint16_t color);
|
||||
|
||||
enum ComparisonResult {
|
||||
NEITHER,
|
||||
|
||||
@@ -40,8 +40,8 @@ struct Rgba {
|
||||
auto shl = [](uint8_t val, unsigned shift) { return static_cast<uint32_t>(val) << shift; };
|
||||
return shl(red, 24) | shl(green, 16) | shl(blue, 8) | shl(alpha, 0);
|
||||
}
|
||||
friend bool operator==(Rgba const &lhs, Rgba const &rhs) { return lhs.toCSS() == rhs.toCSS(); }
|
||||
friend bool operator!=(Rgba const &lhs, Rgba const &rhs) { return lhs.toCSS() != rhs.toCSS(); }
|
||||
bool operator==(Rgba const &rhs) const { return toCSS() == rhs.toCSS(); }
|
||||
bool operator!=(Rgba const &rhs) const { return toCSS() != rhs.toCSS(); }
|
||||
|
||||
/*
|
||||
* CGB colors are RGB555, so we use bit 15 to signify that the color is transparent instead
|
||||
|
||||
@@ -7,51 +7,45 @@
|
||||
#include <utility>
|
||||
|
||||
template<typename T>
|
||||
class EnumSeqIterator {
|
||||
class EnumSeq {
|
||||
T _start;
|
||||
T _stop;
|
||||
|
||||
class Iterator {
|
||||
T _value;
|
||||
|
||||
public:
|
||||
explicit EnumSeqIterator(T value) : _value(value) {}
|
||||
public:
|
||||
explicit Iterator(T value) : _value(value) {}
|
||||
|
||||
EnumSeqIterator &operator++() {
|
||||
Iterator &operator++() {
|
||||
_value = (T)(_value + 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto operator*() const { return _value; }
|
||||
|
||||
friend auto operator==(EnumSeqIterator const &lhs, EnumSeqIterator const &rhs) {
|
||||
return lhs._value == rhs._value;
|
||||
}
|
||||
|
||||
friend auto operator!=(EnumSeqIterator const &lhs, EnumSeqIterator const &rhs) {
|
||||
return lhs._value != rhs._value;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class EnumSeq {
|
||||
T _start;
|
||||
T _stop;
|
||||
bool operator==(Iterator const &rhs) const { return _value == rhs._value; }
|
||||
bool operator!=(Iterator const &rhs) const { return _value != rhs._value; }
|
||||
};
|
||||
|
||||
public:
|
||||
explicit EnumSeq(T stop) : _start((T)0), _stop(stop) {}
|
||||
explicit EnumSeq(T start, T stop) : _start(start), _stop(stop) {}
|
||||
|
||||
EnumSeqIterator<T> begin() { return EnumSeqIterator(_start); }
|
||||
EnumSeqIterator<T> end() { return EnumSeqIterator(_stop); }
|
||||
Iterator begin() { return Iterator(_start); }
|
||||
Iterator end() { return Iterator(_stop); }
|
||||
};
|
||||
|
||||
// This is not a fully generic implementation; its current use cases only require for-loop behavior.
|
||||
// We also assume that all iterators have the same length.
|
||||
template<typename... Iters>
|
||||
class Zip {
|
||||
std::tuple<Iters...> _iters;
|
||||
template<typename... Ts>
|
||||
class ZipIterator {
|
||||
std::tuple<Ts...> _iters;
|
||||
|
||||
public:
|
||||
explicit Zip(std::tuple<Iters...> &&iters) : _iters(iters) {}
|
||||
explicit ZipIterator(std::tuple<Ts...> &&iters) : _iters(iters) {}
|
||||
|
||||
Zip &operator++() {
|
||||
ZipIterator &operator++() {
|
||||
std::apply([](auto &&...it) { (++it, ...); }, _iters);
|
||||
return *this;
|
||||
}
|
||||
@@ -62,26 +56,24 @@ public:
|
||||
);
|
||||
}
|
||||
|
||||
friend auto operator==(Zip const &lhs, Zip const &rhs) {
|
||||
return std::get<0>(lhs._iters) == std::get<0>(rhs._iters);
|
||||
bool operator==(ZipIterator const &rhs) const {
|
||||
return std::get<0>(_iters) == std::get<0>(rhs._iters);
|
||||
}
|
||||
|
||||
friend auto operator!=(Zip const &lhs, Zip const &rhs) {
|
||||
return std::get<0>(lhs._iters) != std::get<0>(rhs._iters);
|
||||
bool operator!=(ZipIterator const &rhs) const {
|
||||
return std::get<0>(_iters) != std::get<0>(rhs._iters);
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template<typename... Containers>
|
||||
template<typename... Ts>
|
||||
class ZipContainer {
|
||||
std::tuple<Containers...> _containers;
|
||||
std::tuple<Ts...> _containers;
|
||||
|
||||
public:
|
||||
explicit ZipContainer(Containers &&...containers)
|
||||
: _containers(std::forward<Containers>(containers)...) {}
|
||||
explicit ZipContainer(Ts &&...containers) : _containers(std::forward<Ts>(containers)...) {}
|
||||
|
||||
auto begin() {
|
||||
return Zip(std::apply(
|
||||
return ZipIterator(std::apply(
|
||||
[](auto &&...containers) {
|
||||
using std::begin;
|
||||
return std::make_tuple(begin(containers)...);
|
||||
@@ -91,7 +83,7 @@ public:
|
||||
}
|
||||
|
||||
auto end() {
|
||||
return Zip(std::apply(
|
||||
return ZipIterator(std::apply(
|
||||
[](auto &&...containers) {
|
||||
using std::end;
|
||||
return std::make_tuple(end(containers)...);
|
||||
@@ -105,12 +97,11 @@ public:
|
||||
template<typename T>
|
||||
using Holder = std::
|
||||
conditional_t<std::is_lvalue_reference_v<T>, T, std::remove_cv_t<std::remove_reference_t<T>>>;
|
||||
} // namespace detail
|
||||
|
||||
// Does the same number of iterations as the first container's iterator!
|
||||
template<typename... Containers>
|
||||
static constexpr auto zip(Containers &&...cs) {
|
||||
return detail::ZipContainer<detail::Holder<Containers>...>(std::forward<Containers>(cs)...);
|
||||
template<typename... Ts>
|
||||
static constexpr auto zip(Ts &&...cs) {
|
||||
return ZipContainer<Holder<Ts>...>(std::forward<Ts>(cs)...);
|
||||
}
|
||||
|
||||
#endif // RGBDS_ITERTOOLS_HPP
|
||||
|
||||
@@ -56,4 +56,9 @@
|
||||
#define setmode(fd, mode) (0)
|
||||
#endif
|
||||
|
||||
// MingGW and Cygwin need POSIX functions which are not standard C explicitly enabled
|
||||
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#endif
|
||||
|
||||
#endif // RGBDS_PLATFORM_HPP
|
||||
|
||||
@@ -8,7 +8,7 @@ extern "C" {
|
||||
#define PACKAGE_VERSION_MAJOR 0
|
||||
#define PACKAGE_VERSION_MINOR 9
|
||||
#define PACKAGE_VERSION_PATCH 0
|
||||
#define PACKAGE_VERSION_RC 1
|
||||
#define PACKAGE_VERSION_RC 2
|
||||
|
||||
char const *get_package_version_string();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd September 18, 2024
|
||||
.Dd October 21, 2024
|
||||
.Dt GBZ80 7
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
34
man/rgbasm.1
34
man/rgbasm.1
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd September 18, 2024
|
||||
.Dd October 21, 2024
|
||||
.Dt RGBASM 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -212,13 +212,19 @@ The following options alter the way warnings are processed.
|
||||
.Bl -tag -width Ds
|
||||
.It Fl Werror
|
||||
Make all warnings into errors.
|
||||
This can be negated as
|
||||
.Fl Wno-error
|
||||
to prevent turning all warnings into errors.
|
||||
.It Fl Werror=
|
||||
Make the specified warning into an error.
|
||||
Make the specified warning or meta warning into an error.
|
||||
A warning's name is appended
|
||||
.Pq example: Fl Werror=obsolete ,
|
||||
and this warning is implicitly enabled and turned into an error.
|
||||
This is an error if used with a meta warning, such as
|
||||
.Fl Werror=all .
|
||||
This can be negated as
|
||||
.Fl Wno-error=
|
||||
to prevent turning a specified warning into an error, even if
|
||||
.Fl Werror
|
||||
is in effect.
|
||||
.El
|
||||
.Pp
|
||||
The following warnings are
|
||||
@@ -240,6 +246,10 @@ Note that each of these flag also has a negation (for example,
|
||||
.Fl Wcharmap-redef
|
||||
enables the warning that
|
||||
.Fl Wno-charmap-redef
|
||||
disables; and
|
||||
.Fl Wall
|
||||
enables every warning that
|
||||
.Fl Wno-all
|
||||
disables).
|
||||
Only the non-default flag is listed here.
|
||||
Ignoring the
|
||||
@@ -291,6 +301,13 @@ This warning is enabled by
|
||||
Warn when shifting macro arguments past their limits.
|
||||
This warning is enabled by
|
||||
.Fl Wextra .
|
||||
.It Fl Wno-nested-comment
|
||||
Warn when the block comment start sequence
|
||||
.Ql /*
|
||||
is found inside of a block comment.
|
||||
Block comments cannot be nested, so the first
|
||||
.Ql */
|
||||
will end the whole comment.
|
||||
.It Fl Wno-obsolete
|
||||
Warn when obsolete constructs such as the
|
||||
.Ic _PI
|
||||
@@ -344,7 +361,7 @@ also warns when an N-bit value is less than -2**(N-1), which will not fit in two
|
||||
Warn when a character goes through charmap conversion but has no defined mapping.
|
||||
.Fl Wunmapped-char=0
|
||||
or
|
||||
.Fl Wunmapped-char
|
||||
.Fl Wno-unmapped-char
|
||||
disables this warning.
|
||||
.Fl Wunmapped-char=1
|
||||
or just
|
||||
@@ -353,6 +370,13 @@ only warns if the active charmap is not empty.
|
||||
.Fl Wunmapped-char=2
|
||||
warns if the active charmap is empty, and/or is not the default charmap
|
||||
.Sq main .
|
||||
.It Fl Wunterminated-load
|
||||
Warn when a
|
||||
.Ic LOAD
|
||||
block is not terminated by an
|
||||
.Ic ENDL .
|
||||
This warning is enabled by
|
||||
.Fl Wextra .
|
||||
.It Fl Wno-user
|
||||
Warn when the
|
||||
.Ic WARN
|
||||
|
||||
48
man/rgbasm.5
48
man/rgbasm.5
@@ -2,7 +2,7 @@
|
||||
.\"
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd September 18, 2024
|
||||
.Dd October 21, 2024
|
||||
.Dt RGBASM 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -41,7 +41,6 @@ or
|
||||
Labels tie a name to a specific location within a section (see
|
||||
.Sx Labels
|
||||
below).
|
||||
They must come first in the line.
|
||||
.Pp
|
||||
Instructions are assembled into Game Boy opcodes.
|
||||
Multiple instructions on one line can be separated by double colons
|
||||
@@ -273,16 +272,16 @@ further below.
|
||||
The instructions in the macro-language generally require constant expressions.
|
||||
.Ss Numeric formats
|
||||
There are a number of numeric formats.
|
||||
.Bl -column -offset indent "Precise fixed-point" "Prefix"
|
||||
.It Sy Format type Ta Sy Prefix Ta Sy Accepted characters
|
||||
.It Hexadecimal Ta $ Ta 0123456789ABCDEF
|
||||
.Bl -column -offset indent "Precise fixed-point" "Possible prefixes"
|
||||
.It Sy Format type Ta Sy Possible prefixes Ta Sy Accepted characters
|
||||
.It Decimal Ta none Ta 0123456789
|
||||
.It Octal Ta & Ta 01234567
|
||||
.It Binary Ta % Ta 01
|
||||
.It Hexadecimal Ta Li $ , 0x , 0X Ta 0123456789ABCDEF
|
||||
.It Octal Ta Li & , 0o , 0O Ta 01234567
|
||||
.It Binary Ta Li % , 0b , 0B Ta 01
|
||||
.It Fixed-point Ta none Ta 01234.56789
|
||||
.It Precise fixed-point Ta none Ta 12.34q8
|
||||
.It Character constant Ta none Ta \(dqABYZ\(dq
|
||||
.It Game Boy graphics Ta \` Ta 0123
|
||||
.It Game Boy graphics Ta Li \` Ta 0123
|
||||
.El
|
||||
.Pp
|
||||
Underscores are also accepted in numbers, except at the beginning of one.
|
||||
@@ -379,8 +378,8 @@ Even a non-constant operand with any non-zero bits will return 0.
|
||||
Besides operators, there are also some functions which have more specialized uses.
|
||||
.Bl -column "BITWIDTH(n)"
|
||||
.It Sy Name Ta Sy Operation
|
||||
.It Fn HIGH n Ta Equivalent to Ql Ar n No & $FF .
|
||||
.It Fn LOW n Ta Equivalent to Ql Po Ns Ar n No & $FF00 Pc >> 8 .
|
||||
.It Fn HIGH n Ta Equivalent to Ql Po Ns Ar n No & $FF00 Pc >> 8 .
|
||||
.It Fn LOW n Ta Equivalent to Ql Ar n No & $FF .
|
||||
.EQ
|
||||
delim $$
|
||||
.EN
|
||||
@@ -904,7 +903,7 @@ SECTION "LOAD example", ROMX
|
||||
CopyCode:
|
||||
ld de, RAMCode
|
||||
ld hl, RAMLocation
|
||||
ld c, RAMLocation.end - RAMLocation
|
||||
ld c, RAMCode.end - RAMCode
|
||||
\&.loop
|
||||
ld a, [de]
|
||||
inc de
|
||||
@@ -928,8 +927,8 @@ RAMLocation:
|
||||
|
||||
\&.string
|
||||
db "Hello World!\e0"
|
||||
\&.end
|
||||
ENDL
|
||||
\&.end
|
||||
.Ed
|
||||
.Pp
|
||||
A
|
||||
@@ -939,7 +938,9 @@ block feels similar to a
|
||||
declaration because it creates a new one.
|
||||
All data and code generated within such a block is placed in the current section like usual, but all labels are created as if they were placed in this newly-created section.
|
||||
.Pp
|
||||
In the example above, all of the code and data will end up in the "LOAD example" section.
|
||||
In the example above, all of the code and data will end up in the
|
||||
.Dq LOAD example
|
||||
section.
|
||||
You will notice the
|
||||
.Sq RAMCode
|
||||
and
|
||||
@@ -951,12 +952,25 @@ You cannot nest
|
||||
.Ic LOAD
|
||||
blocks, nor can you change or stop the current section within them.
|
||||
.Pp
|
||||
The current
|
||||
.Ic LOAD
|
||||
block can be ended by using
|
||||
.Ic ENDL .
|
||||
This directive is only necessary if you want to resume writing code in its containing ROM section.
|
||||
Any of
|
||||
.Ic LOAD , SECTION , ENDSECTION ,
|
||||
or
|
||||
.Ic POPS
|
||||
will end the current
|
||||
.Ic LOAD
|
||||
block before performing its own function.
|
||||
.Pp
|
||||
.Ic LOAD
|
||||
blocks can use the
|
||||
.Ic UNION
|
||||
or
|
||||
modifier as described below, but not the
|
||||
.Ic FRAGMENT
|
||||
modifiers, as described below.
|
||||
modifier.
|
||||
.Ss Unionized sections
|
||||
When you're tight on RAM, you may want to define overlapping static memory allocations, as explained in the
|
||||
.Sx Unions
|
||||
@@ -2283,9 +2297,9 @@ POPO
|
||||
.Pp
|
||||
.Ic OPT
|
||||
can modify the options
|
||||
.Cm b , g , p , Q ,
|
||||
.Cm b , g , p , Q , r ,
|
||||
and
|
||||
.Cm r .
|
||||
.Cm W .
|
||||
.Pp
|
||||
.Ic POPO
|
||||
and
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd September 18, 2024
|
||||
.Dd October 21, 2024
|
||||
.Dt RGBDS 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd September 18, 2024
|
||||
.Dd October 21, 2024
|
||||
.Dt RGBDS 7
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd September 18, 2024
|
||||
.Dd October 21, 2024
|
||||
.Dt RGBFIX 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
.\"
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd September 18, 2024
|
||||
.Dd October 21, 2024
|
||||
.Dt RGBGFX 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd September 18, 2024
|
||||
.Dd October 21, 2024
|
||||
.Dt RGBLINK 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd September 18, 2024
|
||||
.Dd October 21, 2024
|
||||
.Dt RGBLINK 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
@@ -91,10 +91,11 @@ foreach(PROG "asm" "fix" "gfx" "link")
|
||||
${rgb${PROG}_src}
|
||||
${common_src}
|
||||
)
|
||||
if(SUFFIX)
|
||||
set_target_properties(rgb${PROG} PROPERTIES SUFFIX ${SUFFIX})
|
||||
endif()
|
||||
install(TARGETS rgb${PROG} RUNTIME DESTINATION bin)
|
||||
# Required to run tests
|
||||
set_target_properties(rgb${PROG} PROPERTIES
|
||||
# hack for MSVC: no-op generator expression to stop generation of "per-configuration subdirectory"
|
||||
RUNTIME_OUTPUT_DIRECTORY $<1:${CMAKE_SOURCE_DIR}>)
|
||||
endforeach()
|
||||
|
||||
if(LIBPNG_FOUND) # pkg-config
|
||||
|
||||
@@ -186,7 +186,8 @@ void FormatSpec::appendNumber(std::string &str, uint32_t value) const {
|
||||
useExact = true;
|
||||
}
|
||||
|
||||
if (useType != 'X' && useType != 'x' && useType != 'b' && useType != 'o' && useType != 'f' && useExact)
|
||||
if (useType != 'X' && useType != 'x' && useType != 'b' && useType != 'o' && useType != 'f'
|
||||
&& useExact)
|
||||
error("Formatting type '%c' with exact flag '#'\n", useType);
|
||||
if (useType != 'f' && hasFrac)
|
||||
error("Formatting type '%c' with fractional width\n", useType);
|
||||
|
||||
@@ -797,11 +797,9 @@ static int peek() {
|
||||
} else if (c == '{' && !lexerState->disableInterpolation) {
|
||||
// If character is an open brace, do symbol interpolation
|
||||
shiftChar();
|
||||
|
||||
if (auto str = readInterpolation(0); str) {
|
||||
beginExpansion(str, *str);
|
||||
}
|
||||
|
||||
return peek();
|
||||
}
|
||||
|
||||
@@ -1201,9 +1199,9 @@ static std::shared_ptr<std::string> readInterpolation(size_t depth) {
|
||||
|
||||
if (c == '{') { // Nested interpolation
|
||||
shiftChar();
|
||||
auto str = readInterpolation(depth + 1);
|
||||
|
||||
if (auto str = readInterpolation(depth + 1); str) {
|
||||
beginExpansion(str, *str);
|
||||
}
|
||||
continue; // Restart, reading from the new buffer
|
||||
} else if (c == EOF || c == '\r' || c == '\n' || c == '"') {
|
||||
error("Missing }\n");
|
||||
@@ -1373,6 +1371,7 @@ static std::string readString(bool raw) {
|
||||
|
||||
// Line continuation
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
discardLineContinuation();
|
||||
@@ -1505,6 +1504,7 @@ static void appendStringLiteral(std::string &str, bool raw) {
|
||||
|
||||
// Line continuation
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
discardLineContinuation();
|
||||
@@ -1731,7 +1731,25 @@ static Token yylex_NORMAL() {
|
||||
|
||||
// Handle numbers
|
||||
|
||||
case '0': // Decimal or fixed-point number
|
||||
case '0': // Decimal, fixed-point, or base-prefix number
|
||||
switch (peek()) {
|
||||
case 'x':
|
||||
case 'X':
|
||||
shiftChar();
|
||||
return Token(T_(NUMBER), readHexNumber());
|
||||
case 'o':
|
||||
case 'O':
|
||||
shiftChar();
|
||||
return Token(T_(NUMBER), readNumber(8, 0));
|
||||
case 'b':
|
||||
case 'B':
|
||||
shiftChar();
|
||||
return Token(T_(NUMBER), readBinaryNumber());
|
||||
}
|
||||
[[fallthrough]];
|
||||
|
||||
// Decimal or fixed-point number
|
||||
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
@@ -1849,7 +1867,19 @@ static Token yylex_NORMAL() {
|
||||
}
|
||||
}
|
||||
|
||||
if (token.type == T_(ID) && (lexerState->atLineStart || peek() == ':'))
|
||||
// This is a "lexer hack"! We need it to distinguish between label definitions
|
||||
// (which start with `LABEL`) and macro invocations (which start with `ID`).
|
||||
//
|
||||
// If we had one `IDENTIFIER` token, the parser would need to perform "lookahead"
|
||||
// to determine which rule applies. But since macros need to enter "raw" mode to
|
||||
// parse their arguments, which may not even be valid tokens in "normal" mode, we
|
||||
// cannot use lookahead to check for the presence of a `COLON`.
|
||||
//
|
||||
// Instead, we have separate `ID` and `LABEL` tokens, lexing as a `LABEL` if a ':'
|
||||
// character *immediately* follows the identifier. Thus, at the beginning of a line,
|
||||
// "Label:" and "mac:" are treated as label definitions, but "Label :" and "mac :"
|
||||
// are treated as macro invocations.
|
||||
if (token.type == T_(ID) && peek() == ':')
|
||||
token.type = T_(LABEL);
|
||||
|
||||
return token;
|
||||
@@ -1970,6 +2000,7 @@ backslash:
|
||||
break;
|
||||
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
discardLineContinuation();
|
||||
|
||||
@@ -293,7 +293,7 @@ int main(int argc, char *argv[]) {
|
||||
break;
|
||||
|
||||
case 'W':
|
||||
processWarningFlag(musl_optarg);
|
||||
opt_W(musl_optarg);
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
@@ -381,6 +381,7 @@ int main(int argc, char *argv[]) {
|
||||
nbErrors = 1;
|
||||
|
||||
sect_CheckUnionClosed();
|
||||
sect_CheckLoadClosed();
|
||||
sect_CheckSizes();
|
||||
|
||||
if (nbErrors != 0)
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
#include "asm/section.hpp"
|
||||
#include "asm/warning.hpp"
|
||||
|
||||
static constexpr size_t numWarningStates = sizeof(warningStates);
|
||||
|
||||
struct OptStackEntry {
|
||||
char binary[2];
|
||||
char gbgfx[4];
|
||||
@@ -22,7 +20,7 @@ struct OptStackEntry {
|
||||
uint8_t fillByte;
|
||||
bool warningsAreErrors;
|
||||
size_t maxRecursionDepth;
|
||||
WarningState warningStates[numWarningStates];
|
||||
Diagnostics warningStates;
|
||||
};
|
||||
|
||||
static std::stack<OptStackEntry> stack;
|
||||
@@ -160,7 +158,7 @@ void opt_Push() {
|
||||
|
||||
// Both of these pulled from warning.hpp
|
||||
entry.warningsAreErrors = warningsAreErrors;
|
||||
memcpy(entry.warningStates, warningStates, numWarningStates);
|
||||
entry.warningStates = warningStates;
|
||||
|
||||
entry.maxRecursionDepth = maxRecursionDepth; // Pulled from fstack.h
|
||||
|
||||
@@ -184,5 +182,5 @@ void opt_Pop() {
|
||||
|
||||
// opt_W does not apply a whole warning state; it processes one flag string
|
||||
warningsAreErrors = entry.warningsAreErrors;
|
||||
memcpy(warningStates, entry.warningStates, numWarningStates);
|
||||
warningStates = entry.warningStates;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "error.hpp"
|
||||
#include "helpers.hpp" // assume, Defer
|
||||
#include "platform.hpp"
|
||||
|
||||
#include "asm/charmap.hpp"
|
||||
#include "asm/fstack.hpp"
|
||||
@@ -311,7 +312,8 @@ void out_WriteObject() {
|
||||
file = fopen(objectFileName.c_str(), "wb");
|
||||
} else {
|
||||
objectFileName = "<stdout>";
|
||||
file = fdopen(STDOUT_FILENO, "wb");
|
||||
(void)setmode(STDOUT_FILENO, O_BINARY);
|
||||
file = stdout;
|
||||
}
|
||||
if (!file)
|
||||
err("Failed to open object file '%s'", objectFileName.c_str());
|
||||
@@ -503,7 +505,8 @@ void out_WriteState(std::string name, std::vector<StateFeature> const &features)
|
||||
file = fopen(name.c_str(), "wb");
|
||||
} else {
|
||||
name = "<stdout>";
|
||||
file = fdopen(STDOUT_FILENO, "wb");
|
||||
(void)setmode(STDOUT_FILENO, O_BINARY);
|
||||
file = stdout;
|
||||
}
|
||||
if (!file)
|
||||
err("Failed to open state file '%s'", name.c_str());
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
/******************** Tokens ********************/
|
||||
|
||||
%token YYEOF 0 "end of file"
|
||||
%token NEWLINE "newline"
|
||||
%token NEWLINE "end of line"
|
||||
%token EOB "end of buffer"
|
||||
|
||||
// General punctuation
|
||||
@@ -433,22 +433,6 @@ line:
|
||||
fstk_StopRept();
|
||||
yyerrok;
|
||||
}
|
||||
// Hint about unindented macros parsed as labels
|
||||
| LABEL error {
|
||||
lexer_SetMode(LEXER_NORMAL);
|
||||
lexer_ToggleStringExpansion(true);
|
||||
} endofline {
|
||||
Symbol *macro = sym_FindExactSymbol($1);
|
||||
|
||||
if (macro && macro->type == SYM_MACRO)
|
||||
fprintf(
|
||||
stderr,
|
||||
" To invoke `%s` as a macro it must be indented\n",
|
||||
$1.c_str()
|
||||
);
|
||||
fstk_StopRept();
|
||||
yyerrok;
|
||||
}
|
||||
;
|
||||
|
||||
endofline: NEWLINE | EOB;
|
||||
@@ -865,7 +849,7 @@ load:
|
||||
sect_SetLoadSection($3, $5, $6, $7, $2);
|
||||
}
|
||||
| POP_ENDL {
|
||||
sect_EndLoadSection();
|
||||
sect_EndLoadSection(nullptr);
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
@@ -425,14 +425,14 @@ void sect_NewSection(
|
||||
SectionSpec const &attrs,
|
||||
SectionModifier mod
|
||||
) {
|
||||
if (currentLoadSection)
|
||||
fatalerror("Cannot change the section within a `LOAD` block\n");
|
||||
|
||||
for (SectionStackEntry &entry : sectionStack) {
|
||||
if (entry.section && entry.section->name == name)
|
||||
fatalerror("Section '%s' is already on the stack\n", name.c_str());
|
||||
}
|
||||
|
||||
if (currentLoadSection)
|
||||
sect_EndLoadSection("SECTION");
|
||||
|
||||
Section *sect = getSection(name, type, org, attrs, mod);
|
||||
|
||||
changeSection();
|
||||
@@ -457,11 +457,6 @@ void sect_SetLoadSection(
|
||||
if (!requireCodeSection())
|
||||
return;
|
||||
|
||||
if (currentLoadSection) {
|
||||
error("`LOAD` blocks cannot be nested\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (sect_HasData(type)) {
|
||||
error("`LOAD` blocks cannot create a ROM section\n");
|
||||
return;
|
||||
@@ -472,6 +467,9 @@ void sect_SetLoadSection(
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentLoadSection)
|
||||
sect_EndLoadSection("LOAD");
|
||||
|
||||
Section *sect = getSection(name, type, org, attrs, mod);
|
||||
|
||||
currentLoadLabelScopes = sym_GetCurrentLabelScopes();
|
||||
@@ -481,7 +479,14 @@ void sect_SetLoadSection(
|
||||
currentLoadSection = sect;
|
||||
}
|
||||
|
||||
void sect_EndLoadSection() {
|
||||
void sect_EndLoadSection(char const *cause) {
|
||||
if (cause)
|
||||
warning(
|
||||
WARNING_UNTERMINATED_LOAD,
|
||||
"`LOAD` block without `ENDL` terminated by `%s`\n",
|
||||
cause
|
||||
);
|
||||
|
||||
if (!currentLoadSection) {
|
||||
error("Found `ENDL` outside of a `LOAD` block\n");
|
||||
return;
|
||||
@@ -494,6 +499,11 @@ void sect_EndLoadSection() {
|
||||
sym_SetCurrentLabelScopes(currentLoadLabelScopes);
|
||||
}
|
||||
|
||||
void sect_CheckLoadClosed() {
|
||||
if (currentLoadSection)
|
||||
warning(WARNING_UNTERMINATED_LOAD, "`LOAD` block without `ENDL` terminated by EOF\n");
|
||||
}
|
||||
|
||||
Section *sect_GetSymbolSection() {
|
||||
return currentLoadSection ? currentLoadSection : currentSection;
|
||||
}
|
||||
@@ -954,7 +964,7 @@ void sect_PopSection() {
|
||||
fatalerror("No entries in the section stack\n");
|
||||
|
||||
if (currentLoadSection)
|
||||
fatalerror("Cannot change the section within a `LOAD` block\n");
|
||||
sect_EndLoadSection("POPS");
|
||||
|
||||
SectionStackEntry entry = sectionStack.front();
|
||||
sectionStack.pop_front();
|
||||
@@ -972,12 +982,12 @@ void sect_EndSection() {
|
||||
if (!currentSection)
|
||||
fatalerror("Cannot end the section outside of a SECTION\n");
|
||||
|
||||
if (currentLoadSection)
|
||||
fatalerror("Cannot end the section within a `LOAD` block\n");
|
||||
|
||||
if (!currentUnionStack.empty())
|
||||
fatalerror("Cannot end the section within a UNION\n");
|
||||
|
||||
if (currentLoadSection)
|
||||
sect_EndLoadSection("ENDSECTION");
|
||||
|
||||
// Reset the section scope
|
||||
currentSection = nullptr;
|
||||
sym_ResetCurrentLabelScopes();
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "error.hpp"
|
||||
#include "helpers.hpp" // QUOTEDSTRLEN
|
||||
#include "helpers.hpp"
|
||||
#include "itertools.hpp"
|
||||
|
||||
#include "asm/fstack.hpp"
|
||||
@@ -20,265 +20,164 @@
|
||||
unsigned int nbErrors = 0;
|
||||
unsigned int maxErrors = 0;
|
||||
|
||||
static WarningState const defaultWarnings[ARRAY_SIZE(warningStates)] = {
|
||||
WARNING_ENABLED, // WARNING_ASSERT
|
||||
WARNING_DISABLED, // WARNING_BACKWARDS_FOR
|
||||
WARNING_DISABLED, // WARNING_BUILTIN_ARG
|
||||
WARNING_DISABLED, // WARNING_CHARMAP_REDEF
|
||||
WARNING_DISABLED, // WARNING_DIV
|
||||
WARNING_DISABLED, // WARNING_EMPTY_DATA_DIRECTIVE
|
||||
WARNING_DISABLED, // WARNING_EMPTY_MACRO_ARG
|
||||
WARNING_DISABLED, // WARNING_EMPTY_STRRPL
|
||||
WARNING_DISABLED, // WARNING_LARGE_CONSTANT
|
||||
WARNING_DISABLED, // WARNING_MACRO_SHIFT
|
||||
WARNING_ENABLED, // WARNING_NESTED_COMMENT
|
||||
WARNING_ENABLED, // WARNING_OBSOLETE
|
||||
WARNING_DISABLED, // WARNING_SHIFT
|
||||
WARNING_DISABLED, // WARNING_SHIFT_AMOUNT
|
||||
WARNING_ENABLED, // WARNING_USER
|
||||
Diagnostics warningStates;
|
||||
bool warningsAreErrors;
|
||||
|
||||
WARNING_DISABLED, // WARNING_NUMERIC_STRING_1
|
||||
WARNING_DISABLED, // WARNING_NUMERIC_STRING_2
|
||||
WARNING_ENABLED, // WARNING_TRUNCATION_1
|
||||
WARNING_DISABLED, // WARNING_TRUNCATION_2
|
||||
WARNING_ENABLED, // WARNING_PURGE_1
|
||||
WARNING_DISABLED, // WARNING_PURGE_2
|
||||
WARNING_ENABLED, // WARNING_UNMAPPED_CHAR_1
|
||||
WARNING_DISABLED, // WARNING_UNMAPPED_CHAR_2
|
||||
enum WarningLevel {
|
||||
LEVEL_DEFAULT, // Warnings that are enabled by default
|
||||
LEVEL_ALL, // Warnings that probably indicate an error
|
||||
LEVEL_EXTRA, // Warnings that are less likely to indicate an error
|
||||
LEVEL_EVERYTHING, // Literally every warning
|
||||
};
|
||||
|
||||
WarningState warningStates[ARRAY_SIZE(warningStates)];
|
||||
struct WarningFlag {
|
||||
char const *name;
|
||||
WarningLevel level;
|
||||
};
|
||||
|
||||
bool warningsAreErrors; // Set if `-Werror` was specified
|
||||
|
||||
static WarningState warningState(WarningID id) {
|
||||
// Check if warnings are globally disabled
|
||||
if (!warnings)
|
||||
return WARNING_DISABLED;
|
||||
|
||||
// Get the actual state
|
||||
WarningState state = warningStates[id];
|
||||
|
||||
if (state == WARNING_DEFAULT)
|
||||
// The state isn't set, grab its default state
|
||||
state = defaultWarnings[id];
|
||||
|
||||
if (warningsAreErrors && state == WARNING_ENABLED)
|
||||
state = WARNING_ERROR;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static char const * const warningFlags[NB_WARNINGS] = {
|
||||
"assert",
|
||||
"backwards-for",
|
||||
"builtin-args",
|
||||
"charmap-redef",
|
||||
"div",
|
||||
"empty-data-directive",
|
||||
"empty-macro-arg",
|
||||
"empty-strrpl",
|
||||
"large-constant",
|
||||
"macro-shift",
|
||||
"nested-comment",
|
||||
"obsolete",
|
||||
"shift",
|
||||
"shift-amount",
|
||||
"user",
|
||||
static const WarningFlag metaWarnings[] = {
|
||||
{"all", LEVEL_ALL },
|
||||
{"extra", LEVEL_EXTRA },
|
||||
{"everything", LEVEL_EVERYTHING},
|
||||
};
|
||||
|
||||
static const WarningFlag warningFlags[NB_WARNINGS] = {
|
||||
{"assert", LEVEL_DEFAULT },
|
||||
{"backwards-for", LEVEL_ALL },
|
||||
{"builtin-args", LEVEL_ALL },
|
||||
{"charmap-redef", LEVEL_ALL },
|
||||
{"div", LEVEL_EVERYTHING},
|
||||
{"empty-data-directive", LEVEL_ALL },
|
||||
{"empty-macro-arg", LEVEL_EXTRA },
|
||||
{"empty-strrpl", LEVEL_ALL },
|
||||
{"large-constant", LEVEL_ALL },
|
||||
{"macro-shift", LEVEL_EXTRA },
|
||||
{"nested-comment", LEVEL_DEFAULT },
|
||||
{"obsolete", LEVEL_DEFAULT },
|
||||
{"shift", LEVEL_EVERYTHING},
|
||||
{"shift-amount", LEVEL_EVERYTHING},
|
||||
{"unterminated-load", LEVEL_EXTRA },
|
||||
{"user", LEVEL_DEFAULT },
|
||||
// Parametric warnings
|
||||
"numeric-string",
|
||||
"numeric-string",
|
||||
"purge",
|
||||
"purge",
|
||||
"truncation",
|
||||
"truncation",
|
||||
"unmapped-char",
|
||||
"unmapped-char",
|
||||
|
||||
// Meta warnings
|
||||
"all",
|
||||
"extra",
|
||||
"everything", // Especially useful for testing
|
||||
{"numeric-string", LEVEL_EVERYTHING},
|
||||
{"numeric-string", LEVEL_EVERYTHING},
|
||||
{"purge", LEVEL_DEFAULT },
|
||||
{"purge", LEVEL_ALL },
|
||||
{"truncation", LEVEL_DEFAULT },
|
||||
{"truncation", LEVEL_EXTRA },
|
||||
{"unmapped-char", LEVEL_DEFAULT },
|
||||
{"unmapped-char", LEVEL_ALL },
|
||||
};
|
||||
|
||||
static const struct {
|
||||
char const *name;
|
||||
uint8_t nbLevels;
|
||||
WarningID firstID;
|
||||
WarningID lastID;
|
||||
uint8_t defaultLevel;
|
||||
} paramWarnings[] = {
|
||||
{"numeric-string", 2, 1},
|
||||
{"purge", 2, 1},
|
||||
{"truncation", 2, 2},
|
||||
{"unmapped-char", 2, 1},
|
||||
{WARNING_NUMERIC_STRING_1, WARNING_NUMERIC_STRING_2, 1},
|
||||
{WARNING_PURGE_1, WARNING_PURGE_2, 1},
|
||||
{WARNING_TRUNCATION_1, WARNING_TRUNCATION_2, 2},
|
||||
{WARNING_UNMAPPED_CHAR_1, WARNING_UNMAPPED_CHAR_2, 1},
|
||||
};
|
||||
|
||||
static bool tryProcessParamWarning(char const *flag, uint8_t param, WarningState state) {
|
||||
WarningID baseID = PARAM_WARNINGS_START;
|
||||
enum WarningBehavior { DISABLED, ENABLED, ERROR };
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(paramWarnings); i++) {
|
||||
uint8_t maxParam = paramWarnings[i].nbLevels;
|
||||
static WarningBehavior getWarningBehavior(WarningID id) {
|
||||
// Check if warnings are globally disabled
|
||||
if (!warnings)
|
||||
return WarningBehavior::DISABLED;
|
||||
|
||||
if (!strcmp(flag, paramWarnings[i].name)) { // Match!
|
||||
if (!strcmp(flag, "numeric-string"))
|
||||
warning(WARNING_OBSOLETE, "Warning flag \"numeric-string\" is deprecated\n");
|
||||
// Get the state of this warning flag
|
||||
WarningState const &flagState = warningStates.flagStates[id];
|
||||
WarningState const &metaState = warningStates.metaStates[id];
|
||||
|
||||
// If making the warning an error but param is 0, set to the maximum
|
||||
// This accommodates `-Werror=flag`, but also `-Werror=flag=0`, which is
|
||||
// thus filtered out by the caller.
|
||||
// A param of 0 makes sense for disabling everything, but neither for
|
||||
// enabling nor "erroring". Use the default for those.
|
||||
if (param == 0 && state != WARNING_DISABLED) {
|
||||
param = paramWarnings[i].defaultLevel;
|
||||
} else if (param > maxParam) {
|
||||
if (param != 255) // Don't warn if already capped
|
||||
warnx(
|
||||
"Got parameter %" PRIu8
|
||||
" for warning flag \"%s\", but the maximum is %" PRIu8 "; capping.\n",
|
||||
param,
|
||||
flag,
|
||||
maxParam
|
||||
);
|
||||
param = maxParam;
|
||||
}
|
||||
// If subsequent checks determine that the warning flag is enabled, this checks whether it has
|
||||
// -Werror without -Wno-error=<flag> or -Wno-error=<meta>, which makes it into an error
|
||||
bool warningIsError = warningsAreErrors && flagState.error != WARNING_DISABLED
|
||||
&& metaState.error != WARNING_DISABLED;
|
||||
WarningBehavior enabledBehavior =
|
||||
warningIsError ? WarningBehavior::ERROR : WarningBehavior::ENABLED;
|
||||
|
||||
// Set the first <param> to enabled/error, and disable the rest
|
||||
for (uint8_t ofs = 0; ofs < maxParam; ofs++) {
|
||||
warningStates[baseID + ofs] = ofs < param ? state : WARNING_DISABLED;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// First, check the state of the specific warning flag
|
||||
if (flagState.state == WARNING_DISABLED) // -Wno-<flag>
|
||||
return WarningBehavior::DISABLED;
|
||||
if (flagState.error == WARNING_ENABLED) // -Werror=<flag>
|
||||
return WarningBehavior::ERROR;
|
||||
if (flagState.state == WARNING_ENABLED) // -W<flag>
|
||||
return enabledBehavior;
|
||||
|
||||
baseID = (WarningID)(baseID + maxParam);
|
||||
}
|
||||
return false;
|
||||
// If no flag is specified, check the state of the "meta" flags that affect this warning flag
|
||||
if (metaState.state == WARNING_DISABLED) // -Wno-<meta>
|
||||
return WarningBehavior::DISABLED;
|
||||
if (metaState.error == WARNING_ENABLED) // -Werror=<meta>
|
||||
return WarningBehavior::ERROR;
|
||||
if (metaState.state == WARNING_ENABLED) // -W<meta>
|
||||
return enabledBehavior;
|
||||
|
||||
// If no meta flag is specified, check the default state of this warning flag
|
||||
if (warningFlags[id].level == LEVEL_DEFAULT) // enabled by default
|
||||
return enabledBehavior;
|
||||
|
||||
// No flag enables this warning, explicitly or implicitly
|
||||
return WarningBehavior::DISABLED;
|
||||
}
|
||||
|
||||
enum MetaWarningCommand { META_WARNING_DONE = NB_WARNINGS };
|
||||
|
||||
// Warnings that probably indicate an error
|
||||
static uint8_t const _wallCommands[] = {
|
||||
WARNING_BACKWARDS_FOR,
|
||||
WARNING_BUILTIN_ARG,
|
||||
WARNING_CHARMAP_REDEF,
|
||||
WARNING_EMPTY_DATA_DIRECTIVE,
|
||||
WARNING_EMPTY_STRRPL,
|
||||
WARNING_LARGE_CONSTANT,
|
||||
WARNING_NESTED_COMMENT,
|
||||
WARNING_OBSOLETE,
|
||||
WARNING_PURGE_1,
|
||||
WARNING_PURGE_2,
|
||||
WARNING_UNMAPPED_CHAR_1,
|
||||
META_WARNING_DONE,
|
||||
};
|
||||
|
||||
// Warnings that are less likely to indicate an error
|
||||
static uint8_t const _wextraCommands[] = {
|
||||
WARNING_EMPTY_MACRO_ARG,
|
||||
WARNING_MACRO_SHIFT,
|
||||
WARNING_NESTED_COMMENT,
|
||||
WARNING_OBSOLETE,
|
||||
WARNING_PURGE_1,
|
||||
WARNING_PURGE_2,
|
||||
WARNING_TRUNCATION_1,
|
||||
WARNING_TRUNCATION_2,
|
||||
WARNING_UNMAPPED_CHAR_1,
|
||||
WARNING_UNMAPPED_CHAR_2,
|
||||
META_WARNING_DONE,
|
||||
};
|
||||
|
||||
// Literally everything. Notably useful for testing
|
||||
static uint8_t const _weverythingCommands[] = {
|
||||
WARNING_BACKWARDS_FOR,
|
||||
WARNING_BUILTIN_ARG,
|
||||
WARNING_DIV,
|
||||
WARNING_EMPTY_DATA_DIRECTIVE,
|
||||
WARNING_EMPTY_MACRO_ARG,
|
||||
WARNING_EMPTY_STRRPL,
|
||||
WARNING_LARGE_CONSTANT,
|
||||
WARNING_MACRO_SHIFT,
|
||||
WARNING_NESTED_COMMENT,
|
||||
WARNING_OBSOLETE,
|
||||
WARNING_PURGE_1,
|
||||
WARNING_PURGE_2,
|
||||
WARNING_SHIFT,
|
||||
WARNING_SHIFT_AMOUNT,
|
||||
WARNING_TRUNCATION_1,
|
||||
WARNING_TRUNCATION_2,
|
||||
WARNING_UNMAPPED_CHAR_1,
|
||||
WARNING_UNMAPPED_CHAR_2,
|
||||
// WARNING_USER,
|
||||
META_WARNING_DONE,
|
||||
};
|
||||
|
||||
static uint8_t const *metaWarningCommands[NB_META_WARNINGS] = {
|
||||
_wallCommands,
|
||||
_wextraCommands,
|
||||
_weverythingCommands,
|
||||
};
|
||||
void WarningState::update(WarningState other) {
|
||||
if (other.state != WARNING_DEFAULT)
|
||||
state = other.state;
|
||||
if (other.error != WARNING_DEFAULT)
|
||||
error = other.error;
|
||||
}
|
||||
|
||||
void processWarningFlag(char const *flag) {
|
||||
static bool setError = false;
|
||||
std::string rootFlag = flag;
|
||||
|
||||
// First, try to match against a "meta" warning
|
||||
for (WarningID id : EnumSeq(META_WARNINGS_START, NB_WARNINGS)) {
|
||||
// TODO: improve the matching performance?
|
||||
if (!strcmp(flag, warningFlags[id])) {
|
||||
// We got a match!
|
||||
if (setError)
|
||||
errx("Cannot make meta warning \"%s\" into an error", flag);
|
||||
|
||||
for (uint8_t const *ptr = metaWarningCommands[id - META_WARNINGS_START];
|
||||
*ptr != META_WARNING_DONE;
|
||||
ptr++) {
|
||||
// Warning flag, set without override
|
||||
if (warningStates[*ptr] == WARNING_DEFAULT)
|
||||
warningStates[*ptr] = WARNING_ENABLED;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If it's not a meta warning, specially check against `-Werror`
|
||||
if (!strncmp(flag, "error", QUOTEDSTRLEN("error"))) {
|
||||
char const *errorFlag = flag + QUOTEDSTRLEN("error");
|
||||
|
||||
switch (*errorFlag) {
|
||||
case '\0':
|
||||
// `-Werror`
|
||||
// Check for `-Werror` or `-Wno-error` to return early
|
||||
if (rootFlag == "error") {
|
||||
// `-Werror` promotes warnings to errors
|
||||
warningsAreErrors = true;
|
||||
return;
|
||||
|
||||
case '=':
|
||||
// `-Werror=XXX`
|
||||
setError = true;
|
||||
processWarningFlag(errorFlag + 1); // Skip the `=`
|
||||
setError = false;
|
||||
} else if (rootFlag == "no-error") {
|
||||
// `-Wno-error` disables promotion of warnings to errors
|
||||
warningsAreErrors = false;
|
||||
return;
|
||||
|
||||
// Otherwise, allow parsing as another flag
|
||||
}
|
||||
}
|
||||
|
||||
// Well, it's either a normal warning or a mistake
|
||||
// Check for prefixes that affect what the flag does
|
||||
WarningState state;
|
||||
if (rootFlag.starts_with("error=")) {
|
||||
// `-Werror=<flag>` enables the flag as an error
|
||||
state = {.state = WARNING_ENABLED, .error = WARNING_ENABLED};
|
||||
rootFlag.erase(0, QUOTEDSTRLEN("error="));
|
||||
} else if (rootFlag.starts_with("no-error=")) {
|
||||
// `-Wno-error=<flag>` prevents the flag from being an error,
|
||||
// without affecting whether it is enabled
|
||||
state = {.state = WARNING_DEFAULT, .error = WARNING_DISABLED};
|
||||
rootFlag.erase(0, QUOTEDSTRLEN("no-error="));
|
||||
} else if (rootFlag.starts_with("no-")) {
|
||||
// `-Wno-<flag>` disables the flag
|
||||
state = {.state = WARNING_DISABLED, .error = WARNING_DEFAULT};
|
||||
rootFlag.erase(0, QUOTEDSTRLEN("no-"));
|
||||
} else {
|
||||
// `-W<flag>` enables the flag
|
||||
state = {.state = WARNING_ENABLED, .error = WARNING_DEFAULT};
|
||||
}
|
||||
|
||||
WarningState state = setError ? WARNING_ERROR
|
||||
// Not an error, then check if this is a negation
|
||||
: strncmp(flag, "no-", QUOTEDSTRLEN("no-")) ? WARNING_ENABLED
|
||||
: WARNING_DISABLED;
|
||||
char const *rootFlag = state == WARNING_DISABLED ? flag + QUOTEDSTRLEN("no-") : flag;
|
||||
|
||||
// Is this a "parametric" warning?
|
||||
if (state != WARNING_DISABLED) { // The `no-` form cannot be parametrized
|
||||
// Check for an `=` parameter to process as a parametric warning
|
||||
// `-Wno-<flag>` and `-Wno-error=<flag>` negation cannot have an `=` parameter, but without a
|
||||
// parameter, the 0 value will apply to all levels of a parametric warning
|
||||
uint8_t param = 0;
|
||||
bool hasParam = false;
|
||||
if (state.state == WARNING_ENABLED) {
|
||||
// First, check if there is an "equals" sign followed by a decimal number
|
||||
char const *equals = strchr(rootFlag, '=');
|
||||
// Ignore an equal sign at the very end of the string
|
||||
if (auto equals = rootFlag.find('=');
|
||||
equals != rootFlag.npos && equals != rootFlag.size() - 1) {
|
||||
hasParam = true;
|
||||
|
||||
if (equals && equals[1] != '\0') { // Ignore an equal sign at the very end as well
|
||||
// Is the rest of the string a decimal number?
|
||||
// We want to avoid `strtoul`'s whitespace and sign, so we parse manually
|
||||
uint8_t param = 0;
|
||||
char const *ptr = equals + 1;
|
||||
char const *ptr = rootFlag.c_str() + equals + 1;
|
||||
bool warned = false;
|
||||
|
||||
// The `if`'s condition above ensures that this will run at least once
|
||||
@@ -289,7 +188,7 @@ void processWarningFlag(char const *flag) {
|
||||
// Avoid overflowing!
|
||||
if (param > UINT8_MAX - (*ptr - '0')) {
|
||||
if (!warned)
|
||||
warnx("Invalid warning flag \"%s\": capping parameter at 255\n", flag);
|
||||
warnx("Invalid warning flag \"%s\": capping parameter at 255", flag);
|
||||
warned = true; // Only warn once, cap always
|
||||
param = 255;
|
||||
continue;
|
||||
@@ -299,40 +198,83 @@ void processWarningFlag(char const *flag) {
|
||||
ptr++;
|
||||
} while (*ptr);
|
||||
|
||||
// If we managed to the end of the string, check that the warning indeed
|
||||
// accepts a parameter
|
||||
// If we reached the end of the string, truncate it at the '='
|
||||
if (*ptr == '\0') {
|
||||
if (setError && param == 0) {
|
||||
warnx("Ignoring nonsensical warning flag \"%s\"\n", flag);
|
||||
return;
|
||||
rootFlag.resize(equals);
|
||||
// `-W<flag>=0` is equivalent to `-Wno-<flag>`
|
||||
if (param == 0)
|
||||
state.state = WARNING_DISABLED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string truncFlag = rootFlag;
|
||||
// Try to match the flag against a parametric warning
|
||||
// If there was an equals sign, it will have set `param`; if not, `param` will be 0, which
|
||||
// applies to all levels
|
||||
for (auto const ¶mWarning : paramWarnings) {
|
||||
WarningID baseID = paramWarning.firstID;
|
||||
uint8_t maxParam = paramWarning.lastID - baseID + 1;
|
||||
assume(paramWarning.defaultLevel <= maxParam);
|
||||
|
||||
truncFlag.resize(equals - rootFlag); // Truncate the param at the '='
|
||||
if (tryProcessParamWarning(
|
||||
truncFlag.c_str(), param, param == 0 ? WARNING_DISABLED : state
|
||||
))
|
||||
if (rootFlag == warningFlags[baseID].name) { // Match!
|
||||
if (rootFlag == "numeric-string")
|
||||
warning(WARNING_OBSOLETE, "Warning flag \"numeric-string\" is deprecated\n");
|
||||
|
||||
// If making the warning an error but param is 0, set to the maximum
|
||||
// This accommodates `-Werror=<flag>`, but also `-Werror=<flag>=0`, which is
|
||||
// thus filtered out by the caller.
|
||||
// A param of 0 makes sense for disabling everything, but neither for
|
||||
// enabling nor "erroring". Use the default for those.
|
||||
if (param == 0) {
|
||||
param = paramWarning.defaultLevel;
|
||||
} else if (param > maxParam) {
|
||||
if (param != 255) // Don't warn if already capped
|
||||
warnx(
|
||||
"Invalid parameter %" PRIu8
|
||||
" for warning flag \"%s\"; capping at maximum %" PRIu8,
|
||||
param,
|
||||
rootFlag.c_str(),
|
||||
maxParam
|
||||
);
|
||||
param = maxParam;
|
||||
}
|
||||
|
||||
// Set the first <param> to enabled/error, and disable the rest
|
||||
for (uint8_t ofs = 0; ofs < maxParam; ofs++) {
|
||||
WarningState &warning = warningStates.flagStates[baseID + ofs];
|
||||
if (ofs < param)
|
||||
warning.update(state);
|
||||
else
|
||||
warning.state = WARNING_DISABLED;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to match against a non-parametric warning, unless there was an equals sign
|
||||
if (!hasParam) {
|
||||
// Try to match against a "meta" warning
|
||||
for (WarningFlag const &metaWarning : metaWarnings) {
|
||||
if (rootFlag == metaWarning.name) {
|
||||
// Set each of the warning flags that meets this level
|
||||
for (WarningID id : EnumSeq(NB_WARNINGS)) {
|
||||
if (metaWarning.level >= warningFlags[id].level)
|
||||
warningStates.metaStates[id].update(state);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to match the flag against a "normal" flag
|
||||
for (WarningID id : EnumSeq(NB_PLAIN_WARNINGS)) {
|
||||
if (!strcmp(rootFlag, warningFlags[id])) {
|
||||
// We got a match!
|
||||
warningStates[id] = state;
|
||||
if (rootFlag == warningFlags[id].name) {
|
||||
warningStates.flagStates[id].update(state);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Lastly, this might be a "parametric" warning without an equals sign
|
||||
// If it is, treat the param as 1 if enabling, or 0 if disabling
|
||||
if (tryProcessParamWarning(rootFlag, 0, state))
|
||||
return;
|
||||
|
||||
warnx("Unknown warning `%s`", flag);
|
||||
warnx("Unknown warning flag \"%s\"", flag);
|
||||
}
|
||||
|
||||
void printDiag(
|
||||
@@ -376,26 +318,22 @@ void error(char const *fmt, ...) {
|
||||
}
|
||||
|
||||
void warning(WarningID id, char const *fmt, ...) {
|
||||
char const *flag = warningFlags[id];
|
||||
char const *flag = warningFlags[id].name;
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
|
||||
switch (warningState(id)) {
|
||||
case WARNING_DISABLED:
|
||||
switch (getWarningBehavior(id)) {
|
||||
case WarningBehavior::DISABLED:
|
||||
break;
|
||||
|
||||
case WARNING_ENABLED:
|
||||
case WarningBehavior::ENABLED:
|
||||
printDiag(fmt, args, "warning", ": [-W%s]", flag);
|
||||
break;
|
||||
|
||||
case WARNING_ERROR:
|
||||
case WarningBehavior::ERROR:
|
||||
printDiag(fmt, args, "error", ": [-Werror=%s]", flag);
|
||||
break;
|
||||
|
||||
case WARNING_DEFAULT:
|
||||
unreachable_();
|
||||
// Not reached
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
|
||||
@@ -1160,9 +1160,9 @@ static bool processFilename(char const *name) {
|
||||
nbErrors = 0;
|
||||
|
||||
if (!strcmp(name, "-")) {
|
||||
name = "<stdin>";
|
||||
(void)setmode(STDIN_FILENO, O_BINARY);
|
||||
(void)setmode(STDOUT_FILENO, O_BINARY);
|
||||
name = "<stdin>";
|
||||
processFile(STDIN_FILENO, STDOUT_FILENO, name, 0);
|
||||
} else {
|
||||
// POSIX specifies that the results of O_RDWR on a FIFO are undefined.
|
||||
@@ -1435,7 +1435,8 @@ int main(int argc, char *argv[]) {
|
||||
logoFile = fopen(logoFilename, "rb");
|
||||
} else {
|
||||
logoFilename = "<stdin>";
|
||||
logoFile = fdopen(STDIN_FILENO, "rb");
|
||||
(void)setmode(STDIN_FILENO, O_BINARY);
|
||||
logoFile = stdin;
|
||||
}
|
||||
if (!logoFile) {
|
||||
fprintf(
|
||||
|
||||
@@ -15,10 +15,6 @@
|
||||
#include "gfx/main.hpp"
|
||||
#include "gfx/proto_palette.hpp"
|
||||
|
||||
using std::swap;
|
||||
|
||||
namespace packing {
|
||||
|
||||
// The solvers here are picked from the paper at https://arxiv.org/abs/1605.00558:
|
||||
// "Algorithms for the Pagination Problem, a Bin Packing with Overlapping Items"
|
||||
// Their formulation of the problem consists in packing "tiles" into "pages"; here is a
|
||||
@@ -114,8 +110,8 @@ private:
|
||||
}
|
||||
|
||||
friend void swap(Iter &lhs, Iter &rhs) {
|
||||
swap(lhs._array, rhs._array);
|
||||
swap(lhs._iter, rhs._iter);
|
||||
std::swap(lhs._array, rhs._array);
|
||||
std::swap(lhs._iter, rhs._iter);
|
||||
}
|
||||
};
|
||||
public:
|
||||
@@ -537,5 +533,3 @@ std::tuple<DefaultInitVec<size_t>, size_t>
|
||||
}
|
||||
return {mappings, assignments.size()};
|
||||
}
|
||||
|
||||
} // namespace packing
|
||||
|
||||
@@ -8,9 +8,7 @@
|
||||
|
||||
#include "gfx/main.hpp"
|
||||
|
||||
namespace sorting {
|
||||
|
||||
void indexed(
|
||||
void sortIndexed(
|
||||
std::vector<Palette> &palettes,
|
||||
int palSize,
|
||||
png_color const *palRGB,
|
||||
@@ -47,7 +45,7 @@ void indexed(
|
||||
}
|
||||
}
|
||||
|
||||
void grayscale(
|
||||
void sortGrayscale(
|
||||
std::vector<Palette> &palettes, std::array<std::optional<Rgba>, 0x8001> const &colors
|
||||
) {
|
||||
options.verbosePrint(Options::VERB_LOG_ACT, "Sorting grayscale-only palette...\n");
|
||||
@@ -73,7 +71,7 @@ static unsigned int legacyLuminance(uint16_t color) {
|
||||
return 2126 * red + 7152 * green + 722 * blue;
|
||||
}
|
||||
|
||||
void rgb(std::vector<Palette> &palettes) {
|
||||
void sortRgb(std::vector<Palette> &palettes) {
|
||||
options.verbosePrint(Options::VERB_LOG_ACT, "Sorting palettes by \"\"\"luminance\"\"\"...\n");
|
||||
|
||||
for (Palette &pal : palettes) {
|
||||
@@ -82,5 +80,3 @@ void rgb(std::vector<Palette> &palettes) {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sorting
|
||||
|
||||
@@ -211,11 +211,12 @@ static T readLE(U const *bytes) {
|
||||
[[gnu::warn_unused_result]] // Ignoring EOF is a bad idea.
|
||||
static bool
|
||||
readLine(std::filebuf &file, std::string &buffer) {
|
||||
assume(buffer.empty());
|
||||
// TODO: maybe this can be optimized to bulk reads?
|
||||
for (;;) {
|
||||
auto c = file.sbumpc();
|
||||
if (c == std::filebuf::traits_type::eof()) {
|
||||
return false;
|
||||
return !buffer.empty();
|
||||
}
|
||||
if (c == '\n') {
|
||||
// Discard a trailing CRLF
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "defaultinitalloc.hpp"
|
||||
#include "defaultinitvec.hpp"
|
||||
#include "file.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "itertools.hpp"
|
||||
@@ -446,7 +446,7 @@ public:
|
||||
};
|
||||
|
||||
private:
|
||||
struct iterator {
|
||||
struct Iterator {
|
||||
TilesVisitor const &parent;
|
||||
uint32_t const limit;
|
||||
uint32_t x, y;
|
||||
@@ -458,7 +458,7 @@ public:
|
||||
return {parent._png, x + options.inputSlice.left, y + options.inputSlice.top};
|
||||
}
|
||||
|
||||
iterator &operator++() {
|
||||
Iterator &operator++() {
|
||||
auto [major, minor] = parent._columnMajor ? std::tie(y, x) : std::tie(x, y);
|
||||
major += 8;
|
||||
if (major == limit) {
|
||||
@@ -468,19 +468,14 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend bool operator==(iterator const &lhs, iterator const &rhs) {
|
||||
return lhs.coords() == rhs.coords(); // Compare the returned coord pairs
|
||||
}
|
||||
|
||||
friend bool operator!=(iterator const &lhs, iterator const &rhs) {
|
||||
return lhs.coords() != rhs.coords(); // Compare the returned coord pairs
|
||||
}
|
||||
bool operator==(Iterator const &rhs) const { return coords() == rhs.coords(); }
|
||||
bool operator!=(Iterator const &rhs) const { return coords() != rhs.coords(); }
|
||||
};
|
||||
|
||||
public:
|
||||
iterator begin() const { return {*this, _limit, 0, 0}; }
|
||||
iterator end() const {
|
||||
iterator it{*this, _limit, _width - 8, _height - 8}; // Last valid one...
|
||||
Iterator begin() const { return {*this, _limit, 0, 0}; }
|
||||
Iterator end() const {
|
||||
Iterator it{*this, _limit, _width - 8, _height - 8}; // Last valid one...
|
||||
return ++it; // ...now one-past-last!
|
||||
}
|
||||
};
|
||||
@@ -567,7 +562,7 @@ static std::tuple<DefaultInitVec<size_t>, std::vector<Palette>>
|
||||
generatePalettes(std::vector<ProtoPalette> const &protoPalettes, Png const &png) {
|
||||
// Run a "pagination" problem solver
|
||||
// TODO: allow picking one of several solvers?
|
||||
auto [mappings, nbPalettes] = packing::overloadAndRemove(protoPalettes);
|
||||
auto [mappings, nbPalettes] = overloadAndRemove(protoPalettes);
|
||||
assume(mappings.size() == protoPalettes.size());
|
||||
|
||||
if (options.verbosity >= Options::VERB_INTERM) {
|
||||
@@ -601,11 +596,11 @@ static std::tuple<DefaultInitVec<size_t>, std::vector<Palette>>
|
||||
// "Sort" colors in the generated palettes, see the man page for the flowchart
|
||||
auto [embPalSize, embPalRGB, embPalAlphaSize, embPalAlpha] = png.getEmbeddedPal();
|
||||
if (embPalRGB != nullptr) {
|
||||
sorting::indexed(palettes, embPalSize, embPalRGB, embPalAlphaSize, embPalAlpha);
|
||||
sortIndexed(palettes, embPalSize, embPalRGB, embPalAlphaSize, embPalAlpha);
|
||||
} else if (png.isSuitableForGrayscale()) {
|
||||
sorting::grayscale(palettes, png.getColors().raw());
|
||||
sortGrayscale(palettes, png.getColors().raw());
|
||||
} else {
|
||||
sorting::rgb(palettes);
|
||||
sortRgb(palettes);
|
||||
}
|
||||
return {mappings, palettes};
|
||||
}
|
||||
@@ -826,9 +821,7 @@ public:
|
||||
|
||||
return MatchType::NOPE;
|
||||
}
|
||||
friend bool operator==(TileData const &lhs, TileData const &rhs) {
|
||||
return lhs.tryMatching(rhs) != MatchType::NOPE;
|
||||
}
|
||||
bool operator==(TileData const &rhs) const { return tryMatching(rhs) != MatchType::NOPE; }
|
||||
};
|
||||
|
||||
template<>
|
||||
@@ -836,9 +829,7 @@ struct std::hash<TileData> {
|
||||
std::size_t operator()(TileData const &tile) const { return tile.hash(); }
|
||||
};
|
||||
|
||||
namespace unoptimized {
|
||||
|
||||
static void outputTileData(
|
||||
static void outputUnoptimizedTileData(
|
||||
Png const &png,
|
||||
DefaultInitVec<AttrmapEntry> const &attrmap,
|
||||
std::vector<Palette> const &palettes,
|
||||
@@ -877,7 +868,7 @@ static void outputTileData(
|
||||
assume(remainingTiles == 0);
|
||||
}
|
||||
|
||||
static void outputMaps(
|
||||
static void outputUnoptimizedMaps(
|
||||
DefaultInitVec<AttrmapEntry> const &attrmap, DefaultInitVec<size_t> const &mappings
|
||||
) {
|
||||
std::optional<File> tilemapOutput, attrmapOutput, palmapOutput;
|
||||
@@ -916,10 +907,6 @@ static void outputMaps(
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace unoptimized
|
||||
|
||||
namespace optimized {
|
||||
|
||||
struct UniqueTiles {
|
||||
std::unordered_set<TileData> tileset;
|
||||
std::vector<TileData const *> tiles;
|
||||
@@ -1089,8 +1076,6 @@ static void outputPalmap(
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace optimized
|
||||
|
||||
void processPalettes() {
|
||||
options.verbosePrint(Options::VERB_CFG, "Using libpng %s\n", png_get_libpng_ver(nullptr));
|
||||
|
||||
@@ -1130,20 +1115,26 @@ void process() {
|
||||
DefaultInitVec<AttrmapEntry> attrmap{};
|
||||
|
||||
for (auto tile : png.visitAsTiles()) {
|
||||
ProtoPalette tileColors;
|
||||
AttrmapEntry &attrs = attrmap.emplace_back();
|
||||
uint8_t nbColorsInTile = 0;
|
||||
|
||||
// Count the unique non-transparent colors for packing
|
||||
std::unordered_set<uint16_t> tileColors;
|
||||
for (uint32_t y = 0; y < 8; ++y) {
|
||||
for (uint32_t x = 0; x < 8; ++x) {
|
||||
Rgba color = tile.pixel(x, y);
|
||||
if (!color.isTransparent()) { // Do not count transparency in for packing
|
||||
// Add the color to the proto-pal (if not full), and count it if it was unique.
|
||||
if (tileColors.add(color.cgbColor())) {
|
||||
++nbColorsInTile;
|
||||
if (Rgba color = tile.pixel(x, y); !color.isTransparent()) {
|
||||
tileColors.insert(color.cgbColor());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tileColors.size() > options.maxOpaqueColors()) {
|
||||
fatal(
|
||||
"Tile at (%" PRIu32 ", %" PRIu32 ") has %zu colors, more than %" PRIu8 "!",
|
||||
tile.x,
|
||||
tile.y,
|
||||
tileColors.size(),
|
||||
options.maxOpaqueColors()
|
||||
);
|
||||
}
|
||||
|
||||
if (tileColors.empty()) {
|
||||
@@ -1152,11 +1143,16 @@ void process() {
|
||||
continue;
|
||||
}
|
||||
|
||||
ProtoPalette protoPalette;
|
||||
for (uint16_t cgbColor : tileColors) {
|
||||
protoPalette.add(cgbColor);
|
||||
}
|
||||
|
||||
// Insert the proto-palette, making sure to avoid overlaps
|
||||
for (size_t n = 0; n < protoPalettes.size(); ++n) {
|
||||
switch (tileColors.compare(protoPalettes[n])) {
|
||||
switch (protoPalette.compare(protoPalettes[n])) {
|
||||
case ProtoPalette::WE_BIGGER:
|
||||
protoPalettes[n] = tileColors; // Override them
|
||||
protoPalettes[n] = protoPalette; // Override them
|
||||
// Remove any other proto-palettes that we encompass
|
||||
// (Example [(0, 1), (0, 2)], inserting (0, 1, 2))
|
||||
/*
|
||||
@@ -1166,7 +1162,7 @@ void process() {
|
||||
* Investigation is necessary, especially if pathological cases are found.
|
||||
*
|
||||
* for (size_t i = protoPalettes.size(); --i != n;) {
|
||||
* if (tileColors.compare(protoPalettes[i]) == ProtoPalette::WE_BIGGER) {
|
||||
* if (protoPalette.compare(protoPalettes[i]) == ProtoPalette::WE_BIGGER) {
|
||||
* protoPalettes.erase(protoPalettes.begin() + i);
|
||||
* }
|
||||
* }
|
||||
@@ -1183,17 +1179,6 @@ void process() {
|
||||
}
|
||||
}
|
||||
|
||||
if (nbColorsInTile > options.maxOpaqueColors()) {
|
||||
fatal(
|
||||
"Tile at (%" PRIu32 ", %" PRIu32 ") has %" PRIu8 " opaque colors, more than %" PRIu8
|
||||
"!",
|
||||
tile.x,
|
||||
tile.y,
|
||||
nbColorsInTile,
|
||||
options.maxOpaqueColors()
|
||||
);
|
||||
}
|
||||
|
||||
attrs.protoPaletteID = protoPalettes.size();
|
||||
if (protoPalettes.size() == AttrmapEntry::transparent) { // Check for overflow
|
||||
fatal(
|
||||
@@ -1201,7 +1186,7 @@ void process() {
|
||||
AttrmapEntry::transparent
|
||||
);
|
||||
}
|
||||
protoPalettes.push_back(tileColors);
|
||||
protoPalettes.push_back(protoPalette);
|
||||
continue_visiting_tiles:;
|
||||
}
|
||||
|
||||
@@ -1251,7 +1236,7 @@ continue_visiting_tiles:;
|
||||
|
||||
if (!options.output.empty()) {
|
||||
options.verbosePrint(Options::VERB_LOG_ACT, "Generating unoptimized tile data...\n");
|
||||
unoptimized::outputTileData(png, attrmap, palettes, mappings);
|
||||
outputUnoptimizedTileData(png, attrmap, palettes, mappings);
|
||||
}
|
||||
|
||||
if (!options.tilemap.empty() || !options.attrmap.empty() || !options.palmap.empty()) {
|
||||
@@ -1259,12 +1244,12 @@ continue_visiting_tiles:;
|
||||
Options::VERB_LOG_ACT,
|
||||
"Generating unoptimized tilemap and/or attrmap and/or palmap...\n"
|
||||
);
|
||||
unoptimized::outputMaps(attrmap, mappings);
|
||||
outputUnoptimizedMaps(attrmap, mappings);
|
||||
}
|
||||
} else {
|
||||
// All of these require the deduplication process to be performed to be output
|
||||
options.verbosePrint(Options::VERB_LOG_ACT, "Deduplicating tiles...\n");
|
||||
optimized::UniqueTiles tiles = optimized::dedupTiles(png, attrmap, palettes, mappings);
|
||||
UniqueTiles tiles = dedupTiles(png, attrmap, palettes, mappings);
|
||||
|
||||
if (tiles.size() > options.maxNbTiles[0] + options.maxNbTiles[1]) {
|
||||
fatal(
|
||||
@@ -1277,22 +1262,22 @@ continue_visiting_tiles:;
|
||||
|
||||
if (!options.output.empty()) {
|
||||
options.verbosePrint(Options::VERB_LOG_ACT, "Generating optimized tile data...\n");
|
||||
optimized::outputTileData(tiles);
|
||||
outputTileData(tiles);
|
||||
}
|
||||
|
||||
if (!options.tilemap.empty()) {
|
||||
options.verbosePrint(Options::VERB_LOG_ACT, "Generating optimized tilemap...\n");
|
||||
optimized::outputTilemap(attrmap);
|
||||
outputTilemap(attrmap);
|
||||
}
|
||||
|
||||
if (!options.attrmap.empty()) {
|
||||
options.verbosePrint(Options::VERB_LOG_ACT, "Generating optimized attrmap...\n");
|
||||
optimized::outputAttrmap(attrmap, mappings);
|
||||
outputAttrmap(attrmap, mappings);
|
||||
}
|
||||
|
||||
if (!options.palmap.empty()) {
|
||||
options.verbosePrint(Options::VERB_LOG_ACT, "Generating optimized palmap...\n");
|
||||
optimized::outputPalmap(attrmap, mappings);
|
||||
outputPalmap(attrmap, mappings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
bool ProtoPalette::add(uint16_t color) {
|
||||
void ProtoPalette::add(uint16_t color) {
|
||||
size_t i = 0;
|
||||
|
||||
// Seek the first slot greater than the new color
|
||||
@@ -16,12 +16,12 @@ bool ProtoPalette::add(uint16_t color) {
|
||||
++i;
|
||||
if (i == _colorIndices.size()) {
|
||||
// We reached the end of the array without finding the color, so it's a new one.
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// If we found it, great! Nothing else to do.
|
||||
if (_colorIndices[i] == color) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Swap entries until the end
|
||||
@@ -30,12 +30,11 @@ bool ProtoPalette::add(uint16_t color) {
|
||||
++i;
|
||||
if (i == _colorIndices.size()) {
|
||||
// The set is full, but doesn't include the new color.
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Write that last one into the new slot
|
||||
_colorIndices[i] = color;
|
||||
return true;
|
||||
}
|
||||
|
||||
ProtoPalette::ComparisonResult ProtoPalette::compare(ProtoPalette const &other) const {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
|
||||
#include "defaultinitalloc.hpp"
|
||||
#include "defaultinitvec.hpp"
|
||||
#include "file.hpp"
|
||||
#include "helpers.hpp" // assume
|
||||
|
||||
@@ -389,7 +389,9 @@ void reverse() {
|
||||
palmap = readInto(options.palmap);
|
||||
if (palmap->size() != mapSize) {
|
||||
fatal(
|
||||
"Palette map size (%zu tiles) doesn't match image size (%zu)", palmap->size(), mapSize
|
||||
"Palette map size (%zu tiles) doesn't match image size (%zu)",
|
||||
palmap->size(),
|
||||
mapSize
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,7 +359,7 @@ static void readSection(
|
||||
);
|
||||
tryReadLong(tmp, file, "%s: Cannot read \"%s\"'s' size: %s", fileName, section.name.c_str());
|
||||
if (tmp < 0 || tmp > UINT16_MAX)
|
||||
errx("\"%s\"'s section size (%" PRId32 ") is invalid", section.name.c_str(), tmp);
|
||||
errx("\"%s\"'s section size ($%" PRIx32 ") is invalid", section.name.c_str(), tmp);
|
||||
section.size = tmp;
|
||||
section.offset = 0;
|
||||
tryGetc(
|
||||
@@ -379,7 +379,7 @@ static void readSection(
|
||||
tryReadLong(tmp, file, "%s: Cannot read \"%s\"'s org: %s", fileName, section.name.c_str());
|
||||
section.isAddressFixed = tmp >= 0;
|
||||
if (tmp > UINT16_MAX) {
|
||||
error(nullptr, 0, "\"%s\"'s org is too large (%" PRId32 ")", section.name.c_str(), tmp);
|
||||
error(nullptr, 0, "\"%s\"'s org is too large ($%" PRIx32 ")", section.name.c_str(), tmp);
|
||||
tmp = UINT16_MAX;
|
||||
}
|
||||
section.org = tmp;
|
||||
@@ -405,7 +405,7 @@ static void readSection(
|
||||
error(
|
||||
nullptr,
|
||||
0,
|
||||
"\"%s\"'s alignment offset is too large (%" PRId32 ")",
|
||||
"\"%s\"'s alignment offset is too large ($%" PRIx32 ")",
|
||||
section.name.c_str(),
|
||||
tmp
|
||||
);
|
||||
@@ -490,7 +490,8 @@ void obj_ReadFile(char const *fileName, unsigned int fileID) {
|
||||
file = fopen(fileName, "rb");
|
||||
} else {
|
||||
fileName = "<stdin>";
|
||||
file = fdopen(STDIN_FILENO, "rb"); // `stdin` is in text mode by default
|
||||
(void)setmode(STDIN_FILENO, O_BINARY);
|
||||
file = stdin;
|
||||
}
|
||||
if (!file)
|
||||
err("Failed to open file \"%s\"", fileName);
|
||||
|
||||
@@ -207,7 +207,8 @@ static void writeROM() {
|
||||
outputFile = fopen(outputFileName, "wb");
|
||||
} else {
|
||||
outputFileName = "<stdout>";
|
||||
outputFile = fdopen(STDOUT_FILENO, "wb");
|
||||
(void)setmode(STDOUT_FILENO, O_BINARY);
|
||||
outputFile = stdout;
|
||||
}
|
||||
if (!outputFile)
|
||||
err("Failed to open output file \"%s\"", outputFileName);
|
||||
@@ -222,7 +223,8 @@ static void writeROM() {
|
||||
overlayFile = fopen(overlayFileName, "rb");
|
||||
} else {
|
||||
overlayFileName = "<stdin>";
|
||||
overlayFile = fdopen(STDIN_FILENO, "rb");
|
||||
(void)setmode(STDIN_FILENO, O_BINARY);
|
||||
overlayFile = stdin;
|
||||
}
|
||||
if (!overlayFile)
|
||||
err("Failed to open overlay file \"%s\"", overlayFileName);
|
||||
@@ -545,7 +547,8 @@ static void writeSym() {
|
||||
symFile = fopen(symFileName, "w");
|
||||
} else {
|
||||
symFileName = "<stdout>";
|
||||
symFile = fdopen(STDOUT_FILENO, "w");
|
||||
(void)setmode(STDOUT_FILENO, O_TEXT); // May have been set to O_BINARY previously
|
||||
symFile = stdout;
|
||||
}
|
||||
if (!symFile)
|
||||
err("Failed to open sym file \"%s\"", symFileName);
|
||||
@@ -589,7 +592,8 @@ static void writeMap() {
|
||||
mapFile = fopen(mapFileName, "w");
|
||||
} else {
|
||||
mapFileName = "<stdout>";
|
||||
mapFile = fdopen(STDOUT_FILENO, "w");
|
||||
(void)setmode(STDOUT_FILENO, O_TEXT); // May have been set to O_BINARY previously
|
||||
mapFile = stdout;
|
||||
}
|
||||
if (!mapFile)
|
||||
err("Failed to open map file \"%s\"", mapFileName);
|
||||
|
||||
@@ -101,9 +101,10 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
||||
case RPN_DIV:
|
||||
value = popRPN(patch);
|
||||
if (value == 0) {
|
||||
if (!isError)
|
||||
if (!isError) {
|
||||
error(patch.src, patch.lineNo, "Division by 0");
|
||||
isError = true;
|
||||
}
|
||||
popRPN(patch);
|
||||
value = INT32_MAX;
|
||||
} else {
|
||||
@@ -113,9 +114,10 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
||||
case RPN_MOD:
|
||||
value = popRPN(patch);
|
||||
if (value == 0) {
|
||||
if (!isError)
|
||||
if (!isError) {
|
||||
error(patch.src, patch.lineNo, "Modulo by 0");
|
||||
isError = true;
|
||||
}
|
||||
popRPN(patch);
|
||||
value = 0;
|
||||
} else {
|
||||
@@ -128,9 +130,10 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
||||
case RPN_EXP:
|
||||
value = popRPN(patch);
|
||||
if (value < 0) {
|
||||
if (!isError)
|
||||
error(patch.src, patch.lineNo, "Exponent by negative");
|
||||
if (!isError) {
|
||||
error(patch.src, patch.lineNo, "Exponent by negative value %" PRId32, value);
|
||||
isError = true;
|
||||
}
|
||||
popRPN(patch);
|
||||
value = 0;
|
||||
} else {
|
||||
@@ -342,10 +345,18 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
||||
|
||||
case RPN_HRAM:
|
||||
value = popRPN(patch);
|
||||
if (!isError && (value < 0 || (value > 0xFF && value < 0xFF00) || value > 0xFFFF)) {
|
||||
error(patch.src, patch.lineNo, "Value %" PRId32 " is not in HRAM range", value);
|
||||
if (value < 0 || (value > 0xFF && value < 0xFF00) || value > 0xFFFF) {
|
||||
if (!isError) {
|
||||
error(
|
||||
patch.src,
|
||||
patch.lineNo,
|
||||
"Address $%" PRIx32 " for LDH is not in HRAM range",
|
||||
value
|
||||
);
|
||||
isError = true;
|
||||
}
|
||||
value = 0;
|
||||
}
|
||||
value &= 0xFF;
|
||||
break;
|
||||
|
||||
@@ -354,10 +365,12 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
||||
// Acceptable values are 0x00, 0x08, 0x10, ..., 0x38
|
||||
// They can be easily checked with a bitmask
|
||||
if (value & ~0x38) {
|
||||
if (!isError)
|
||||
error(patch.src, patch.lineNo, "Value %" PRId32 " is not a RST vector", value);
|
||||
if (!isError) {
|
||||
error(patch.src, patch.lineNo, "Value $%" PRIx32 " is not a RST vector", value);
|
||||
isError = true;
|
||||
}
|
||||
value = 0;
|
||||
}
|
||||
value |= 0xC7;
|
||||
break;
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
/******************** Tokens and data types ********************/
|
||||
|
||||
%token YYEOF 0 "end of file"
|
||||
%token newline
|
||||
%token newline "end of line"
|
||||
%token COMMA ","
|
||||
%token ORG "ORG"
|
||||
FLOATING "FLOATING"
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
OPTION(USE_NONFREE_TESTS "run tests that build nonfree codebases" ON)
|
||||
|
||||
if(NOT USE_NONFREE_TESTS)
|
||||
set(ONLY_FREE "--only-free")
|
||||
endif()
|
||||
|
||||
add_executable(randtilegen gfx/randtilegen.cpp)
|
||||
add_executable(rgbgfx_test gfx/rgbgfx_test.cpp)
|
||||
set_target_properties(randtilegen rgbgfx_test PROPERTIES
|
||||
# hack for MSVC: no-op generator expression to stop generation of "per-configuration subdirectory"
|
||||
RUNTIME_OUTPUT_DIRECTORY $<1:${CMAKE_CURRENT_SOURCE_DIR}/gfx>)
|
||||
|
||||
install(TARGETS randtilegen rgbgfx_test
|
||||
DESTINATION ${rgbds_SOURCE_DIR}/test/gfx
|
||||
COMPONENT "Test support programs"
|
||||
EXCLUDE_FROM_ALL
|
||||
)
|
||||
configure_file(CTestCustom.cmake.in ${CMAKE_BINARY_DIR}/CTestCustom.cmake)
|
||||
|
||||
foreach(TARGET randtilegen rgbgfx_test)
|
||||
if(LIBPNG_FOUND) # pkg-config
|
||||
@@ -20,3 +25,8 @@ foreach(TARGET randtilegen rgbgfx_test)
|
||||
target_link_libraries(${TARGET} PRIVATE ${PNG_LIBRARIES})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
add_test(NAME all
|
||||
COMMAND ./run-tests.sh ${ONLY_FREE}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
1
test/CTestCustom.cmake.in
Normal file
1
test/CTestCustom.cmake.in
Normal file
@@ -0,0 +1 @@
|
||||
set(CTEST_CUSTOM_PRE_TEST "bash -c 'cd @CMAKE_CURRENT_SOURCE_DIR@\; ./fetch-test-deps.sh @ONLY_FREE@'")
|
||||
@@ -1,5 +1,5 @@
|
||||
error: charmap-empty.asm(1):
|
||||
Cannot map an empty string
|
||||
error: charmap-empty.asm(2):
|
||||
syntax error, unexpected newline
|
||||
syntax error, unexpected end of line
|
||||
error: Assembly aborted (2 errors)!
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
error: code-after-endm-endr-endc.asm(6):
|
||||
syntax error, unexpected PRINTLN, expecting newline or end of buffer
|
||||
syntax error, unexpected PRINTLN, expecting end of line or end of buffer
|
||||
error: code-after-endm-endr-endc.asm(7):
|
||||
Macro "mac" not defined
|
||||
error: code-after-endm-endr-endc.asm(12):
|
||||
syntax error, unexpected PRINTLN, expecting newline or end of buffer
|
||||
syntax error, unexpected PRINTLN, expecting end of line or end of buffer
|
||||
error: code-after-endm-endr-endc.asm(17):
|
||||
syntax error, unexpected PRINTLN, expecting newline
|
||||
syntax error, unexpected PRINTLN, expecting end of line
|
||||
error: code-after-endm-endr-endc.asm(19):
|
||||
syntax error, unexpected PRINTLN, expecting newline or end of buffer
|
||||
syntax error, unexpected PRINTLN, expecting end of line or end of buffer
|
||||
error: code-after-endm-endr-endc.asm(23):
|
||||
syntax error, unexpected PRINTLN, expecting newline
|
||||
syntax error, unexpected PRINTLN, expecting end of line
|
||||
error: code-after-endm-endr-endc.asm(25):
|
||||
syntax error, unexpected PRINTLN, expecting newline or end of buffer
|
||||
syntax error, unexpected PRINTLN, expecting end of line or end of buffer
|
||||
error: Assembly aborted (7 errors)!
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
FATAL: endsection-in-load.asm(3):
|
||||
Cannot end the section within a `LOAD` block
|
||||
warning: endsection-in-load.asm(3): [-Wunterminated-load]
|
||||
`LOAD` block without `ENDL` terminated by `ENDSECTION`
|
||||
error: endsection-in-load.asm(4):
|
||||
Found `ENDL` outside of a `LOAD` block
|
||||
error: Assembly aborted (1 error)!
|
||||
|
||||
@@ -17,7 +17,7 @@ error: invalid-opt.asm(8):
|
||||
error: invalid-opt.asm(9):
|
||||
Must specify an argument for option 'W'
|
||||
error: invalid-opt.asm(10):
|
||||
syntax error, unexpected newline, expecting string
|
||||
syntax error, unexpected end of line, expecting string
|
||||
error: invalid-opt.asm(11):
|
||||
No entries in the option stack
|
||||
error: Assembly aborted (11 errors)!
|
||||
|
||||
28
test/asm/lexer-hack.asm
Normal file
28
test/asm/lexer-hack.asm
Normal file
@@ -0,0 +1,28 @@
|
||||
MACRO mac
|
||||
println "got {d:_NARG} args: \#"
|
||||
ENDM
|
||||
|
||||
; indented, these were always macro invocations
|
||||
mac
|
||||
mac ro
|
||||
mac : ld a, 1
|
||||
|
||||
; in column 1, we historically treated these as labels
|
||||
mac
|
||||
mac ro
|
||||
mac : ld b, 2
|
||||
|
||||
SECTION "test", ROM0
|
||||
|
||||
; a colon makes these into labels
|
||||
Label1: ld c, 3
|
||||
Label2: ld d, 4
|
||||
|
||||
; a macro invocation when already defined as a label
|
||||
Label1 args
|
||||
; and a label definition when already defined as a macro
|
||||
mac: ld e, 5
|
||||
|
||||
; the space before the colon matters!
|
||||
undef :
|
||||
undef :
|
||||
9
test/asm/lexer-hack.err
Normal file
9
test/asm/lexer-hack.err
Normal file
@@ -0,0 +1,9 @@
|
||||
error: lexer-hack.asm(22):
|
||||
"Label1" is not a macro
|
||||
error: lexer-hack.asm(24):
|
||||
'mac' already defined at lexer-hack.asm(1)
|
||||
error: lexer-hack.asm(27):
|
||||
Macro "undef" not defined
|
||||
error: lexer-hack.asm(28):
|
||||
Macro "undef" not defined
|
||||
error: Assembly aborted (4 errors)!
|
||||
6
test/asm/lexer-hack.out
Normal file
6
test/asm/lexer-hack.out
Normal file
@@ -0,0 +1,6 @@
|
||||
got 0 args:
|
||||
got 1 args: ro
|
||||
got 2 args: : ld a,1
|
||||
got 0 args:
|
||||
got 1 args: ro
|
||||
got 2 args: : ld b,2
|
||||
51
test/asm/load-endings.asm
Normal file
51
test/asm/load-endings.asm
Normal file
@@ -0,0 +1,51 @@
|
||||
MACRO data
|
||||
db SECTION(@), \#
|
||||
ENDM
|
||||
|
||||
MACRO now_in
|
||||
if strcmp("\1", "nothing")
|
||||
assert !strcmp(SECTION(@), \1)
|
||||
else
|
||||
assert !def(@)
|
||||
endc
|
||||
ENDM
|
||||
|
||||
now_in nothing
|
||||
|
||||
SECTION "A", ROM0
|
||||
now_in "A"
|
||||
data 1
|
||||
LOAD "P", WRAM0
|
||||
now_in "P"
|
||||
data 2
|
||||
|
||||
; LOAD after LOAD
|
||||
LOAD "Q", WRAM0
|
||||
now_in "Q"
|
||||
data 3
|
||||
|
||||
; SECTION after LOAD
|
||||
SECTION "B", ROM0
|
||||
now_in "B"
|
||||
data 4
|
||||
LOAD "R", WRAM0
|
||||
now_in "R"
|
||||
data 5
|
||||
|
||||
; PUSHS after LOAD
|
||||
PUSHS
|
||||
SECTION "C", ROM0
|
||||
now_in "C"
|
||||
data 6
|
||||
LOAD "S", WRAM0
|
||||
now_in "S"
|
||||
data 7
|
||||
|
||||
; POPS after LOAD
|
||||
POPS
|
||||
now_in "R"
|
||||
data 8
|
||||
|
||||
; ENDSECTION after LOAD
|
||||
ENDSECTION
|
||||
now_in nothing
|
||||
8
test/asm/load-endings.err
Normal file
8
test/asm/load-endings.err
Normal file
@@ -0,0 +1,8 @@
|
||||
warning: load-endings.asm(23): [-Wunterminated-load]
|
||||
`LOAD` block without `ENDL` terminated by `LOAD`
|
||||
warning: load-endings.asm(28): [-Wunterminated-load]
|
||||
`LOAD` block without `ENDL` terminated by `SECTION`
|
||||
warning: load-endings.asm(45): [-Wunterminated-load]
|
||||
`LOAD` block without `ENDL` terminated by `POPS`
|
||||
warning: load-endings.asm(50): [-Wunterminated-load]
|
||||
`LOAD` block without `ENDL` terminated by `ENDSECTION`
|
||||
1
test/asm/load-endings.out.bin
Normal file
1
test/asm/load-endings.out.bin
Normal file
@@ -0,0 +1 @@
|
||||
BRRAPQCS
|
||||
@@ -1,5 +1,5 @@
|
||||
SECTION "outer", ROM0
|
||||
LOAD "inner", WRAM0
|
||||
LOAD "matryoshka", HRAM
|
||||
ENDL
|
||||
ENDL
|
||||
LOAD "inner1", WRAM0 ; starts "inner1"
|
||||
LOAD "inner2", HRAM ; ends "inner1", starts "inner2"
|
||||
ENDL ; ends "inner2"
|
||||
ENDL ; error
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
error: load-in-load.asm(3):
|
||||
`LOAD` blocks cannot be nested
|
||||
warning: load-in-load.asm(3): [-Wunterminated-load]
|
||||
`LOAD` block without `ENDL` terminated by `LOAD`
|
||||
error: load-in-load.asm(5):
|
||||
Found `ENDL` outside of a `LOAD` block
|
||||
error: Assembly aborted (2 errors)!
|
||||
error: Assembly aborted (1 error)!
|
||||
|
||||
@@ -2,6 +2,8 @@ warning: multiple-charmaps.asm(46) -> multiple-charmaps.asm::print_mapped(34): [
|
||||
Treating multi-unit strings as numbers is deprecated
|
||||
warning: multiple-charmaps.asm(54) -> multiple-charmaps.asm::print_mapped(34): [-Wobsolete]
|
||||
Treating multi-unit strings as numbers is deprecated
|
||||
warning: multiple-charmaps.asm(64): [-Wcharmap-redef]
|
||||
Overriding charmap mapping
|
||||
warning: multiple-charmaps.asm(73) -> multiple-charmaps.asm::print_mapped(34): [-Wobsolete]
|
||||
Treating multi-unit strings as numbers is deprecated
|
||||
warning: multiple-charmaps.asm(95) -> multiple-charmaps.asm::print_mapped(34): [-Wobsolete]
|
||||
|
||||
3
test/asm/nested-bad-interpolation.asm
Normal file
3
test/asm/nested-bad-interpolation.asm
Normal file
@@ -0,0 +1,3 @@
|
||||
def p = {{a}}
|
||||
def q = "{b}"
|
||||
def r = "{{c}}"
|
||||
17
test/asm/nested-bad-interpolation.err
Normal file
17
test/asm/nested-bad-interpolation.err
Normal file
@@ -0,0 +1,17 @@
|
||||
error: nested-bad-interpolation.asm(1):
|
||||
Interpolated symbol "a" is a reserved keyword; add a '#' prefix to use it as a raw symbol
|
||||
error: nested-bad-interpolation.asm(1):
|
||||
Interpolated symbol "" does not exist
|
||||
error: nested-bad-interpolation.asm(1):
|
||||
syntax error, unexpected end of line
|
||||
error: nested-bad-interpolation.asm(2):
|
||||
Interpolated symbol "b" is a reserved keyword; add a '#' prefix to use it as a raw symbol
|
||||
warning: nested-bad-interpolation.asm(2): [-Wobsolete]
|
||||
Treating multi-unit strings as numbers is deprecated
|
||||
error: nested-bad-interpolation.asm(3):
|
||||
Interpolated symbol "c" is a reserved keyword; add a '#' prefix to use it as a raw symbol
|
||||
error: nested-bad-interpolation.asm(3):
|
||||
Interpolated symbol "" does not exist
|
||||
warning: nested-bad-interpolation.asm(3): [-Wobsolete]
|
||||
Treating multi-unit strings as numbers is deprecated
|
||||
error: Assembly aborted (6 errors)!
|
||||
16
test/asm/number-prefixes.asm
Normal file
16
test/asm/number-prefixes.asm
Normal file
@@ -0,0 +1,16 @@
|
||||
MACRO test
|
||||
assert (\1) == (\2)
|
||||
ENDM
|
||||
|
||||
test 0xDEADbeef, $DEADbeef
|
||||
test 0o755, &755
|
||||
test 0b101010, %101010
|
||||
|
||||
test 0XcafeBABE, $cafeBABE
|
||||
test 0O644, &644
|
||||
test 0B11100100, %11100100
|
||||
|
||||
pusho b.X
|
||||
test 0b.X.X, %.X.X
|
||||
test 0BX.X., %X.X.
|
||||
popo
|
||||
@@ -1,5 +1,6 @@
|
||||
; Triggering a charmap realloc while the charmap has been pushed onto the stack used
|
||||
; to induce a use-after-free.
|
||||
opt Wno-charmap-redef
|
||||
pushc
|
||||
charmap "000000000000000000000000000000000",12
|
||||
popc
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
SECTION "outer", ROM0
|
||||
SECTION "outer1", ROM0
|
||||
LOAD "ram", WRAM0
|
||||
SECTION "inner", ROM0
|
||||
SECTION "outer2", ROM0
|
||||
ENDL
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
FATAL: section-in-load.asm(3):
|
||||
Cannot change the section within a `LOAD` block
|
||||
warning: section-in-load.asm(3): [-Wunterminated-load]
|
||||
`LOAD` block without `ENDL` terminated by `SECTION`
|
||||
error: section-in-load.asm(4):
|
||||
Found `ENDL` outside of a `LOAD` block
|
||||
error: Assembly aborted (1 error)!
|
||||
|
||||
31
test/asm/skip-expansions.asm
Normal file
31
test/asm/skip-expansions.asm
Normal file
@@ -0,0 +1,31 @@
|
||||
; skipping ahead to `elif`/`else`/`endc`/`endr`/`endm` disables expansions!
|
||||
|
||||
MACRO mac1
|
||||
if _NARG != 2
|
||||
println "args: \#"
|
||||
elif (\2) == 42
|
||||
println "forty-two!"
|
||||
else
|
||||
println "it's ", (\2)
|
||||
endc
|
||||
ENDM
|
||||
|
||||
mac1 elif, 6 * 7 ; this prints "forty-two!" because it takes the `elif`
|
||||
mac1 elif, 6 * 9
|
||||
mac1 elif
|
||||
mac1
|
||||
|
||||
MACRO mac2
|
||||
if _NARG != 2
|
||||
println "args: \#"
|
||||
\1 (\2) == 42 ; if the `if` is not taken, this is not expanded!
|
||||
println "forty-two!"
|
||||
else
|
||||
println "it's ", (\2)
|
||||
endc
|
||||
ENDM
|
||||
|
||||
mac2 elif, 6 * 7 ; this prints "it's $2A" because it skips the `\1` line and takes the `else`
|
||||
mac2 elif, 6 * 9
|
||||
mac2 elif
|
||||
mac2
|
||||
5
test/asm/skip-expansions.err
Normal file
5
test/asm/skip-expansions.err
Normal file
@@ -0,0 +1,5 @@
|
||||
error: skip-expansions.asm(31) -> skip-expansions.asm::mac2(21):
|
||||
Macro argument '\1' not defined
|
||||
error: skip-expansions.asm(31) -> skip-expansions.asm::mac2(21):
|
||||
syntax error, unexpected (
|
||||
error: Assembly aborted (2 errors)!
|
||||
8
test/asm/skip-expansions.out
Normal file
8
test/asm/skip-expansions.out
Normal file
@@ -0,0 +1,8 @@
|
||||
forty-two!
|
||||
it's $36
|
||||
args: elif
|
||||
args:
|
||||
it's $2A
|
||||
it's $36
|
||||
args: elif
|
||||
args:
|
||||
@@ -1,10 +1,11 @@
|
||||
MACRO mac
|
||||
println "got {d:_NARG} args"
|
||||
ENDM
|
||||
mac
|
||||
mac 42
|
||||
notmac
|
||||
mac
|
||||
mac 42
|
||||
mac::
|
||||
mac ::
|
||||
def x = 1 ; so far so good...
|
||||
def n equ 2 + * / ^ 3 ; oops
|
||||
def s equs "no closing quote, lol
|
||||
section "test", rom0 ; good again
|
||||
ld a, 42 ; keep going...
|
||||
ld xor, ret ; oh no :(
|
||||
label1: ; yes...
|
||||
label2:: ; yes...
|
||||
label3::: ; no!
|
||||
halt stop abort ; please
|
||||
println "finally!"
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
error: syntax-error-after-syntax-error.asm(2):
|
||||
syntax error, unexpected *
|
||||
error: syntax-error-after-syntax-error.asm(3):
|
||||
Unterminated string
|
||||
error: syntax-error-after-syntax-error.asm(6):
|
||||
syntax error, unexpected newline, expecting : or ::
|
||||
error: syntax-error-after-syntax-error.asm(7):
|
||||
syntax error, unexpected newline, expecting : or ::
|
||||
To invoke `mac` as a macro it must be indented
|
||||
error: syntax-error-after-syntax-error.asm(8):
|
||||
syntax error, unexpected number, expecting : or ::
|
||||
To invoke `mac` as a macro it must be indented
|
||||
syntax error, unexpected xor
|
||||
error: syntax-error-after-syntax-error.asm(9):
|
||||
'mac' already defined at syntax-error-after-syntax-error.asm(1)
|
||||
syntax error, unexpected :
|
||||
error: syntax-error-after-syntax-error.asm(10):
|
||||
'mac' already defined at syntax-error-after-syntax-error.asm(1)
|
||||
syntax error, unexpected stop, expecting end of line or end of buffer or ::
|
||||
error: Assembly aborted (5 errors)!
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
got 0 args
|
||||
got 1 args
|
||||
finally!
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
error: syntax-error-eof-newline.asm(5) -> syntax-error-eof-newline.inc(1):
|
||||
syntax error, unexpected newline
|
||||
syntax error, unexpected end of line
|
||||
error: Assembly aborted (1 error)!
|
||||
|
||||
2
test/asm/unterminated-load.asm
Normal file
2
test/asm/unterminated-load.asm
Normal file
@@ -0,0 +1,2 @@
|
||||
SECTION "rom", ROM0
|
||||
LOAD "ram", WRAM0
|
||||
2
test/asm/unterminated-load.err
Normal file
2
test/asm/unterminated-load.err
Normal file
@@ -0,0 +1,2 @@
|
||||
warning: unterminated-load.asm(3): [-Wunterminated-load]
|
||||
`LOAD` block without `ENDL` terminated by EOF
|
||||
@@ -98,10 +98,10 @@ case "$actionname" in
|
||||
esac
|
||||
|
||||
if "$nonfree"; then
|
||||
action pret pokecrystal 2024-08-27 7b5986006f6b325e471fee25903c769ce67f5da9
|
||||
action pret pokered 2024-09-08 1f6e2bf999401b9444f939bb40c1eb279bc51829
|
||||
action pret pokecrystal 2024-10-16 961fad9e150df324afc9bccd1ce15c3a65d3c124
|
||||
action pret pokered 2024-10-15 a891fc1168a7f998c570e1ea5f15014556df2d95
|
||||
action zladx LADX-Disassembly 2024-09-16 008d01541f8cab3f4590cbc94a690af2b9a7979f
|
||||
fi
|
||||
action AntonioND ucity 2024-08-03 f3c6377f1fb1ea29644bcd90722abaaa5d478a74
|
||||
action pinobatch libbet 2024-06-15 ee60f0e4712a938589edd3e5d258e519a475d754
|
||||
action LIJI32 SameBoy 2024-09-13 1931c2830fc46cb648dde4fb0bed4f0345b67b2d
|
||||
action pinobatch libbet 2024-10-20 71d04e850534cbe77d1c143153f664dac1960bc9
|
||||
action LIJI32 SameBoy 2024-10-12 52d5169cc82356288f337d30aa01fb3fa1b37155
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "defaultinitalloc.hpp"
|
||||
#include "defaultinitvec.hpp" // Reused from RGBDS
|
||||
|
||||
#include "gfx/rgba.hpp" // Reused from RGBGFX
|
||||
|
||||
|
||||
2
test/link/ldh-bad.asm
Normal file
2
test/link/ldh-bad.asm
Normal file
@@ -0,0 +1,2 @@
|
||||
SECTION "bad", ROM0
|
||||
ldh [$1234+@], a
|
||||
2
test/link/ldh-bad.out
Normal file
2
test/link/ldh-bad.out
Normal file
@@ -0,0 +1,2 @@
|
||||
error: ldh-bad.asm(2): Address $1234 for LDH is not in HRAM range
|
||||
Linking failed with 1 error
|
||||
@@ -1,2 +1,2 @@
|
||||
error: rst-bad.asm(2): Value 1 is not a RST vector
|
||||
error: rst-bad.asm(2): Value $1 is not a RST vector
|
||||
Linking failed with 1 error
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
error: script-syntax-error.link(2): syntax error, unexpected ORG, expecting newline or OPTIONAL
|
||||
error: script-syntax-error.link(5): syntax error, unexpected string, expecting newline or FLOATING or number
|
||||
error: script-syntax-error.link(2): syntax error, unexpected ORG, expecting end of line or OPTIONAL
|
||||
error: script-syntax-error.link(5): syntax error, unexpected string, expecting end of line or FLOATING or number
|
||||
Linking failed with 2 errors
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
diff --git a/makefile b/makefile
|
||||
index ccfc9d1..ba14638 100644
|
||||
--- a/makefile
|
||||
+++ b/makefile
|
||||
@@ -85,10 +85,10 @@ $(title).gb: $(objlisto)
|
||||
$(RGBFIX) -jvsc -k "OK" -l 0x33 -m ROM -p 0xFF -t "LIBBET" -v $@
|
||||
|
||||
obj/gb/%.o: src/%.z80 src/hardware.inc src/global.inc
|
||||
- ${RGBASM} -h -o $@ $<
|
||||
+ ${RGBASM} -o $@ $<
|
||||
|
||||
obj/gb/%.o: obj/gb/%.z80
|
||||
- ${RGBASM} -h -o $@ $<
|
||||
+ ${RGBASM} -o $@ $<
|
||||
|
||||
# Files that will be included with incbin
|
||||
|
||||
Reference in New Issue
Block a user