mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-21 02:32:06 +00:00
Compare commits
232 Commits
v0.9.2
...
v1.0.0-rc1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8df88f92ba | ||
|
|
534a4efee4 | ||
|
|
cc96b4d517 | ||
|
|
0ccdbf509a | ||
|
|
531278961f | ||
|
|
85176ef10a | ||
|
|
02b880e1b0 | ||
|
|
c6997fe73c | ||
|
|
c578a7b761 | ||
|
|
8564df51e5 | ||
|
|
62d3b44768 | ||
|
|
ead5337fe0 | ||
|
|
0d509aa65c | ||
|
|
fcfc931867 | ||
|
|
3d155d5695 | ||
|
|
92ed6ece53 | ||
|
|
e0e9ef190a | ||
|
|
386fb5f398 | ||
|
|
94e9ef5213 | ||
|
|
0c4c25b2d2 | ||
|
|
f7167d8115 | ||
|
|
b7e0783ae7 | ||
|
|
77a105e189 | ||
|
|
a16dcc0ea5 | ||
|
|
9f373d49ac | ||
|
|
272019beb0 | ||
|
|
db6793f444 | ||
|
|
ea1358bbe6 | ||
|
|
2bdf61da70 | ||
|
|
92826a726a | ||
|
|
9c3ce69180 | ||
|
|
50d0b101c3 | ||
|
|
1bf1219e07 | ||
|
|
7b405513d9 | ||
|
|
7df9c12a6c | ||
|
|
30a8503dcd | ||
|
|
02310489c6 | ||
|
|
5f8b7474b4 | ||
|
|
92a9c73ee7 | ||
|
|
7ade3e74b3 | ||
|
|
978e832914 | ||
|
|
2130a5ba1f | ||
|
|
9fc83efe06 | ||
|
|
e41ce49698 | ||
|
|
1574b5b1f7 | ||
|
|
3f6db080b4 | ||
|
|
f9a55bd5cd | ||
|
|
a4a830776b | ||
|
|
34d99b273c | ||
|
|
feb8365812 | ||
|
|
bf66e346f0 | ||
|
|
3a0a4b7f90 | ||
|
|
39f0f9edc0 | ||
|
|
a3983b7b0f | ||
|
|
504a45a4ed | ||
|
|
98c5c7f776 | ||
|
|
7020cf7188 | ||
|
|
70d129fcd7 | ||
|
|
2d5f4d8910 | ||
|
|
26c6911c8f | ||
|
|
f1fd3abcff | ||
|
|
2cae47a5a2 | ||
|
|
ac75a085fa | ||
|
|
0c1b422c36 | ||
|
|
23ce888d65 | ||
|
|
d992b21141 | ||
|
|
903492cce2 | ||
|
|
fc9b614225 | ||
|
|
543b7fa6c2 | ||
|
|
5a7ffd19b0 | ||
|
|
7be7483571 | ||
|
|
2eeb333be0 | ||
|
|
fe8baaec50 | ||
|
|
752b273aec | ||
|
|
b51056f743 | ||
|
|
ee1aed51d8 | ||
|
|
db2cda790c | ||
|
|
86b43ea14f | ||
|
|
d1829ed923 | ||
|
|
6d53bc4121 | ||
|
|
32b5ef5095 | ||
|
|
a36f2b3b7d | ||
|
|
cd4be6aa07 | ||
|
|
499edaecd0 | ||
|
|
225490163e | ||
|
|
d388a60daa | ||
|
|
6d05de9d4d | ||
|
|
5d6e571338 | ||
|
|
308d488833 | ||
|
|
cecbf0aa0e | ||
|
|
d21e6669ce | ||
|
|
2341d1ee50 | ||
|
|
53949761a7 | ||
|
|
f7eb986313 | ||
|
|
75aed1afd5 | ||
|
|
d16751f56a | ||
|
|
8b1a5244f7 | ||
|
|
b2747dfbd8 | ||
|
|
16e16cdf51 | ||
|
|
a353637a90 | ||
|
|
f3cbfcecf4 | ||
|
|
3bc8b1ff7c | ||
|
|
aa46c79db6 | ||
|
|
92acb6e547 | ||
|
|
ac632d9223 | ||
|
|
0df5b7b86d | ||
|
|
87c10988ed | ||
|
|
d6a28a6259 | ||
|
|
c6d0e8de63 | ||
|
|
ded4ef4072 | ||
|
|
1849a35e61 | ||
|
|
18e35053fa | ||
|
|
7e151f16c3 | ||
|
|
2ce4cdbff6 | ||
|
|
eea532ded1 | ||
|
|
c83b87e0a0 | ||
|
|
8d268e8a8a | ||
|
|
ee0f311c10 | ||
|
|
61730be6ce | ||
|
|
5f333d9753 | ||
|
|
d1493a9f96 | ||
|
|
d652212857 | ||
|
|
0cd60ea1e6 | ||
|
|
a0e23ee911 | ||
|
|
ad81c74cda | ||
|
|
9ef32e405c | ||
|
|
89ca6a325c | ||
|
|
9e0e7ef9a1 | ||
|
|
2dc948fefb | ||
|
|
e3a5290dad | ||
|
|
cfe1f60e47 | ||
|
|
0eed237517 | ||
|
|
68ffb01cac | ||
|
|
169ac61e14 | ||
|
|
0681110647 | ||
|
|
8d1b111692 | ||
|
|
2935942667 | ||
|
|
9a4593e823 | ||
|
|
250e08043b | ||
|
|
14f5e16ae8 | ||
|
|
bf69043a1d | ||
|
|
7086b8aeff | ||
|
|
53c39d01d4 | ||
|
|
4a2f9fc744 | ||
|
|
e7d63f5f6b | ||
|
|
b80b30fba1 | ||
|
|
8e84850679 | ||
|
|
e31256c0d4 | ||
|
|
9a9fd6603c | ||
|
|
e99ff5ac45 | ||
|
|
60cec85638 | ||
|
|
39f2ed1339 | ||
|
|
4c8724899b | ||
|
|
0c96234532 | ||
|
|
9dddd87893 | ||
|
|
5eb093f13e | ||
|
|
529989bde5 | ||
|
|
776e37980b | ||
|
|
7f24d46d44 | ||
|
|
cf6e5fec63 | ||
|
|
d8fc25ee43 | ||
|
|
a0eccceb01 | ||
|
|
2720224890 | ||
|
|
8bebab1db0 | ||
|
|
ee29579d3e | ||
|
|
5aec36350b | ||
|
|
1fecf80659 | ||
|
|
b6d77fbb9e | ||
|
|
8a19c5c30a | ||
|
|
0149122cd0 | ||
|
|
35335aadbe | ||
|
|
80df858ee3 | ||
|
|
eafc32fd68 | ||
|
|
2adeda0318 | ||
|
|
21a6d35b8b | ||
|
|
ce78280af3 | ||
|
|
041b86b8dd | ||
|
|
611b0041c4 | ||
|
|
ddb2acb652 | ||
|
|
da133baf17 | ||
|
|
d32b1912ed | ||
|
|
82513e5255 | ||
|
|
f2708ce967 | ||
|
|
01a5c94c7e | ||
|
|
e7a05b1db8 | ||
|
|
510a4aa99d | ||
|
|
3f4e8396aa | ||
|
|
276a200590 | ||
|
|
a40109e4e4 | ||
|
|
34cf959c9d | ||
|
|
bf6875f160 | ||
|
|
44f5b47bf0 | ||
|
|
41ab5dff5a | ||
|
|
5e43ece578 | ||
|
|
9acba4b412 | ||
|
|
8c50839109 | ||
|
|
6736d2ec66 | ||
|
|
6869e4807c | ||
|
|
5de05e2e4b | ||
|
|
fda54fd0c3 | ||
|
|
35962dedc4 | ||
|
|
991b74dd0d | ||
|
|
1a77667409 | ||
|
|
c9765ec158 | ||
|
|
202c91471c | ||
|
|
e14f68d1d7 | ||
|
|
185a3b29e6 | ||
|
|
d7b1569ee6 | ||
|
|
468f1cd912 | ||
|
|
965288de38 | ||
|
|
008920f533 | ||
|
|
20ed6a52ee | ||
|
|
8b85875b67 | ||
|
|
7054d81650 | ||
|
|
5942117ac3 | ||
|
|
e7a3b9d90e | ||
|
|
b13d623ad4 | ||
|
|
37bf9fae01 | ||
|
|
612cf3b7dd | ||
|
|
089e366ddc | ||
|
|
fa9e29e4ce | ||
|
|
fa3d83a3d1 | ||
|
|
804db4e073 | ||
|
|
5d998ef483 | ||
|
|
126b1e5726 | ||
|
|
4f2400c15b | ||
|
|
063d284cbf | ||
|
|
205bf5a11d | ||
|
|
41c94aa448 | ||
|
|
d413870e6d | ||
|
|
e95ac6fb06 | ||
|
|
e1ae92709c |
6
.clang-tidy
Normal file
6
.clang-tidy
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
Checks: ''
|
||||||
|
WarningsAsErrors: ''
|
||||||
|
HeaderFilterRegex: ''
|
||||||
|
FormatStyle: none
|
||||||
|
SystemHeaders: false
|
||||||
4
.github/scripts/build_libpng.sh
vendored
4
.github/scripts/build_libpng.sh
vendored
@@ -1,13 +1,13 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
pngver=1.6.45
|
pngver=1.6.50
|
||||||
|
|
||||||
## Grab sources and check them
|
## Grab sources and check them
|
||||||
|
|
||||||
curl -LOJ "http://prdownloads.sourceforge.net/libpng/libpng-$pngver.tar.xz?download"
|
curl -LOJ "http://prdownloads.sourceforge.net/libpng/libpng-$pngver.tar.xz?download"
|
||||||
# Brew doesn't provide any sha256sum, so we're making do with `sha2` instead.
|
# Brew doesn't provide any sha256sum, so we're making do with `sha2` instead.
|
||||||
if [ "$(sha2 -q -256 libpng-$pngver.tar.xz)" != 926485350139ffb51ef69760db35f78846c805fef3d59bfdcb2fba704663f370 ]; then
|
if [ "$(sha2 -q -256 libpng-$pngver.tar.xz)" != 4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307 ]; then
|
||||||
sha2 -256 libpng-$pngver.tar.xz
|
sha2 -256 libpng-$pngver.tar.xz
|
||||||
echo Checksum mismatch! Aborting. >&2
|
echo Checksum mismatch! Aborting. >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
4
.github/scripts/get_win_deps.ps1
vendored
4
.github/scripts/get_win_deps.ps1
vendored
@@ -16,8 +16,8 @@ function getlibrary ([string] $URI, [string] $filename, [string] $hash, [string]
|
|||||||
}
|
}
|
||||||
|
|
||||||
getlibrary 'https://www.zlib.net/zlib131.zip' 'zlib.zip' '72af66d44fcc14c22013b46b814d5d2514673dda3d115e64b690c1ad636e7b17' .
|
getlibrary 'https://www.zlib.net/zlib131.zip' 'zlib.zip' '72af66d44fcc14c22013b46b814d5d2514673dda3d115e64b690c1ad636e7b17' .
|
||||||
getlibrary 'https://github.com/glennrp/libpng/archive/refs/tags/v1.6.45.zip' 'libpng.zip' '1b3d94b2f1d137db1bf1842cb9f03df179772a517f7b86e26351742190632785' .
|
getlibrary 'https://github.com/pnggroup/libpng/archive/refs/tags/v1.6.50.zip' 'libpng.zip' 'f6bb2544d2cf5465af3a695dee0b7eacff82f11a50aa4672ef0e19df6e16d455' .
|
||||||
getlibrary 'https://github.com/lexxmark/winflexbison/releases/download/v2.5.25/win_flex_bison-2.5.25.zip' 'winflexbison.zip' '8d324b62be33604b2c45ad1dd34ab93d722534448f55a16ca7292de32b6ac135' install_dir
|
getlibrary 'https://github.com/lexxmark/winflexbison/releases/download/v2.5.25/win_flex_bison-2.5.25.zip' 'winflexbison.zip' '8d324b62be33604b2c45ad1dd34ab93d722534448f55a16ca7292de32b6ac135' install_dir
|
||||||
|
|
||||||
Move-Item zlib-1.3.1 zlib
|
Move-Item zlib-1.3.1 zlib
|
||||||
Move-Item libpng-1.6.45 libpng
|
Move-Item libpng-1.6.50 libpng
|
||||||
|
|||||||
9
.github/scripts/mingw-w64-libpng-dev.sh
vendored
9
.github/scripts/mingw-w64-libpng-dev.sh
vendored
@@ -1,22 +1,17 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
pngver=1.6.45
|
pngver=1.6.50
|
||||||
arch="$1"
|
arch="$1"
|
||||||
|
|
||||||
## Grab sources and check them
|
## Grab sources and check them
|
||||||
|
|
||||||
wget http://downloads.sourceforge.net/project/libpng/libpng16/$pngver/libpng-$pngver.tar.xz
|
wget http://downloads.sourceforge.net/project/libpng/libpng16/$pngver/libpng-$pngver.tar.xz
|
||||||
wget http://downloads.sourceforge.net/project/apng/libpng/libpng16/libpng-$pngver-apng.patch.gz
|
echo 4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307 libpng-$pngver.tar.xz | sha256sum -c -
|
||||||
sha256sum -c .github/scripts/mingw-w64-libpng-dev.sha256sums
|
|
||||||
|
|
||||||
## Extract sources and patch them
|
## Extract sources and patch them
|
||||||
|
|
||||||
tar -xf libpng-$pngver.tar.xz
|
tar -xf libpng-$pngver.tar.xz
|
||||||
gunzip libpng-$pngver-apng.patch.gz
|
|
||||||
|
|
||||||
# Patch in apng support
|
|
||||||
env -C libpng-$pngver patch -p0 ../libpng-$pngver-apng.patch
|
|
||||||
|
|
||||||
## Start building!
|
## Start building!
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
f7caa3b55f003ce23d6a087b1c2a643262647bfd1a1b31afdc9b18eabf1bbc7e libpng-1.6.45-apng.patch.gz
|
|
||||||
926485350139ffb51ef69760db35f78846c805fef3d59bfdcb2fba704663f370 libpng-1.6.45.tar.xz
|
|
||||||
2
.github/workflows/build-container.yml
vendored
2
.github/workflows/build-container.yml
vendored
@@ -37,7 +37,9 @@ jobs:
|
|||||||
TAG_NAME=${GITHUB_REF#refs/tags/}
|
TAG_NAME=${GITHUB_REF#refs/tags/}
|
||||||
sed -i "2i LABEL org.opencontainers.image.description=\"RGBDS container image for the release version $TAG_NAME\"" Dockerfile
|
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 build . --tag ghcr.io/gbdev/rgbds:$TAG_NAME
|
||||||
|
docker tag ghcr.io/gbdev/rgbds:$TAG_NAME ghcr.io/gbdev/rgbds:latest
|
||||||
docker push ghcr.io/gbdev/rgbds:$TAG_NAME
|
docker push ghcr.io/gbdev/rgbds:$TAG_NAME
|
||||||
|
docker push ghcr.io/gbdev/rgbds:latest
|
||||||
|
|
||||||
- name: Delete untagged container images
|
- name: Delete untagged container images
|
||||||
if: github.repository_owner == 'gbdev'
|
if: github.repository_owner == 'gbdev'
|
||||||
|
|||||||
2
.github/workflows/checkdiff.yml
vendored
2
.github/workflows/checkdiff.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: Code coverage checking
|
name: Diff completeness check
|
||||||
on: pull_request
|
on: pull_request
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|||||||
2
.github/workflows/checkformat.yml
vendored
2
.github/workflows/checkformat.yml
vendored
@@ -9,4 +9,4 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Check format
|
- name: Check format
|
||||||
run: |
|
run: |
|
||||||
./contrib/checkformat.bash
|
contrib/checkformat.bash
|
||||||
|
|||||||
34
.github/workflows/coverage.yml
vendored
Normal file
34
.github/workflows/coverage.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
name: Code coverage report
|
||||||
|
on:
|
||||||
|
- push
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
coverage:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Install deps
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
./.github/scripts/install_deps.sh ubuntu
|
||||||
|
- name: Install LCOV
|
||||||
|
run: |
|
||||||
|
sudo apt-get install lcov
|
||||||
|
- name: Install test dependency dependencies
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
test/fetch-test-deps.sh --get-deps ubuntu
|
||||||
|
- name: Generate coverage report
|
||||||
|
run: |
|
||||||
|
contrib/coverage.bash ubuntu-ci
|
||||||
|
- name: Upload coverage report
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: coverage-report
|
||||||
|
# Workaround for keeping the top-level coverage/ directory
|
||||||
|
# https://github.com/actions/upload-artifact/issues/174
|
||||||
|
path: |
|
||||||
|
coverage
|
||||||
|
dummy-file-to-keep-directory-structure.txt
|
||||||
24
.github/workflows/create-release-artifacts.yml
vendored
24
.github/workflows/create-release-artifacts.yml
vendored
@@ -61,12 +61,12 @@ jobs:
|
|||||||
cmake --install build --verbose --prefix install_dir --strip
|
cmake --install build --verbose --prefix install_dir --strip
|
||||||
- name: Package binaries
|
- name: Package binaries
|
||||||
run: |
|
run: |
|
||||||
Compress-Archive -LiteralPath @("install_dir/bin/rgbasm.exe", "install_dir/bin/rgblink.exe", "install_dir/bin/rgbfix.exe", "install_dir/bin/rgbgfx.exe", "install_dir/bin/zlib1.dll", "install_dir/bin/libpng16.dll") "rgbds-${{ env.version }}-win${{ matrix.bits }}.zip"
|
Compress-Archive -LiteralPath @("install_dir/bin/rgbasm.exe", "install_dir/bin/rgblink.exe", "install_dir/bin/rgbfix.exe", "install_dir/bin/rgbgfx.exe", "install_dir/bin/zlib1.dll", "install_dir/bin/libpng16.dll") "rgbds-win${{ matrix.bits }}.zip"
|
||||||
- name: Upload Windows binaries
|
- name: Upload Windows binaries
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: win${{ matrix.bits }}
|
name: win${{ matrix.bits }}
|
||||||
path: rgbds-${{ env.version }}-win${{ matrix.bits }}.zip
|
path: rgbds-win${{ matrix.bits }}.zip
|
||||||
|
|
||||||
macos:
|
macos:
|
||||||
runs-on: macos-14
|
runs-on: macos-14
|
||||||
@@ -92,12 +92,12 @@ jobs:
|
|||||||
strip rgb{asm,link,fix,gfx}
|
strip rgb{asm,link,fix,gfx}
|
||||||
- name: Package binaries
|
- name: Package binaries
|
||||||
run: |
|
run: |
|
||||||
zip --junk-paths rgbds-${{ env.version }}-macos.zip rgb{asm,link,fix,gfx} man/* .github/scripts/install.sh
|
zip --junk-paths rgbds-macos.zip rgb{asm,link,fix,gfx} man/* .github/scripts/install.sh
|
||||||
- name: Upload macOS binaries
|
- name: Upload macOS binaries
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: macos
|
name: macos
|
||||||
path: rgbds-${{ env.version }}-macos.zip
|
path: rgbds-macos.zip
|
||||||
|
|
||||||
linux:
|
linux:
|
||||||
runs-on: ubuntu-22.04 # Oldest supported, for best glibc compatibility.
|
runs-on: ubuntu-22.04 # Oldest supported, for best glibc compatibility.
|
||||||
@@ -119,12 +119,12 @@ jobs:
|
|||||||
strip rgb{asm,link,fix,gfx}
|
strip rgb{asm,link,fix,gfx}
|
||||||
- name: Package binaries
|
- name: Package binaries
|
||||||
run: |
|
run: |
|
||||||
tar caf rgbds-${{ env.version }}-linux-x86_64.tar.xz --transform='s#.*/##' rgb{asm,link,fix,gfx} man/* .github/scripts/install.sh
|
tar caf rgbds-linux-x86_64.tar.xz --transform='s#.*/##' rgb{asm,link,fix,gfx} man/* .github/scripts/install.sh
|
||||||
- name: Upload Linux binaries
|
- name: Upload Linux binaries
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: linux
|
name: linux
|
||||||
path: rgbds-${{ env.version }}-linux-x86_64.tar.xz
|
path: rgbds-linux-x86_64.tar.xz
|
||||||
|
|
||||||
release:
|
release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -151,15 +151,15 @@ jobs:
|
|||||||
body: |
|
body: |
|
||||||
Please ensure that the 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.
|
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)
|
By the way, if you forgot to update `include/version.hpp`, RGBASM's version test is going to 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...
|
draft: true # Don't publish the release quite yet...
|
||||||
prerelease: ${{ contains(github.ref, '-rc') }}
|
prerelease: ${{ contains(github.ref, '-rc') }}
|
||||||
files: |
|
files: |
|
||||||
win32/rgbds-${{ env.version }}-win32.zip
|
win32/rgbds-win32.zip
|
||||||
win64/rgbds-${{ env.version }}-win64.zip
|
win64/rgbds-win64.zip
|
||||||
macos/rgbds-${{ env.version }}-macos.zip
|
macos/rgbds-macos.zip
|
||||||
linux/rgbds-${{ env.version }}-linux-x86_64.tar.xz
|
linux/rgbds-linux-x86_64.tar.xz
|
||||||
rgbds-${{ env.version }}.tar.gz
|
rgbds-source.tar.gz
|
||||||
fail_on_unmatched_files: true
|
fail_on_unmatched_files: true
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
21
.github/workflows/testing.yml
vendored
21
.github/workflows/testing.yml
vendored
@@ -69,7 +69,7 @@ jobs:
|
|||||||
- name: Run tests
|
- name: Run tests
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
CXX=${{ matrix.cxx }} test/run-tests.sh
|
CXX=${{ matrix.cxx }} test/run-tests.sh --os ${{ matrix.os }}
|
||||||
|
|
||||||
macos-static:
|
macos-static:
|
||||||
runs-on: macos-14
|
runs-on: macos-14
|
||||||
@@ -121,13 +121,13 @@ jobs:
|
|||||||
- name: Run tests
|
- name: Run tests
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
test/run-tests.sh
|
test/run-tests.sh --os macos
|
||||||
|
|
||||||
windows:
|
windows:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
bits: [32, 64]
|
bits: [32, 64]
|
||||||
os: [windows-2019, windows-2022]
|
os: [windows-2022, windows-2025]
|
||||||
include:
|
include:
|
||||||
- bits: 32
|
- bits: 32
|
||||||
arch: x86
|
arch: x86
|
||||||
@@ -149,9 +149,10 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
zbuild
|
zbuild
|
||||||
pngbuild
|
pngbuild
|
||||||
key: ${{ matrix.arch }}-${{ hashFiles('zlib/**', 'libpng/**') }}
|
key: ${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles('zlib/**', 'libpng/**') }}
|
||||||
- name: Build zlib
|
- name: Build zlib
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
|
shell: bash
|
||||||
run: | # BUILD_SHARED_LIBS causes the output DLL to be correctly called `zlib1.dll`
|
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 -S zlib -B zbuild -A ${{ matrix.platform }} -Wno-dev -DCMAKE_INSTALL_PREFIX=install_dir -DBUILD_SHARED_LIBS=ON
|
||||||
cmake --build zbuild --config Release -j
|
cmake --build zbuild --config Release -j
|
||||||
@@ -212,7 +213,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cp bins/* .
|
cp bins/* .
|
||||||
cp bins/*.dll test/gfx
|
cp bins/*.dll test/gfx
|
||||||
test/run-tests.sh
|
test/run-tests.sh --os ${{ matrix.os }}
|
||||||
|
|
||||||
windows-mingw-build:
|
windows-mingw-build:
|
||||||
strategy:
|
strategy:
|
||||||
@@ -270,7 +271,7 @@ jobs:
|
|||||||
needs: windows-mingw-build
|
needs: windows-mingw-build
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [windows-2019, windows-2022]
|
os: [windows-2022, windows-2025]
|
||||||
bits: [32, 64]
|
bits: [32, 64]
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
@@ -319,7 +320,7 @@ jobs:
|
|||||||
- name: Run tests
|
- name: Run tests
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
test/run-tests.sh
|
test/run-tests.sh --os ${{ matrix.os }}
|
||||||
|
|
||||||
cygwin:
|
cygwin:
|
||||||
strategy:
|
strategy:
|
||||||
@@ -331,7 +332,7 @@ jobs:
|
|||||||
- bits: 64
|
- bits: 64
|
||||||
arch: x86_64
|
arch: x86_64
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
runs-on: windows-2019
|
runs-on: windows-2022
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
@@ -372,7 +373,7 @@ jobs:
|
|||||||
- name: Build & test using CMake on FreeBSD
|
- name: Build & test using CMake on FreeBSD
|
||||||
uses: vmactions/freebsd-vm@v1
|
uses: vmactions/freebsd-vm@v1
|
||||||
with:
|
with:
|
||||||
release: "15.0"
|
release: "14.3"
|
||||||
usesh: true
|
usesh: true
|
||||||
prepare: |
|
prepare: |
|
||||||
pkg install -y \
|
pkg install -y \
|
||||||
@@ -382,7 +383,7 @@ jobs:
|
|||||||
git \
|
git \
|
||||||
png
|
png
|
||||||
run: | # FreeBSD `c++` compiler does not support `make develop` sanitizers ASan or UBSan
|
run: | # FreeBSD `c++` compiler does not support `make develop` sanitizers ASan or UBSan
|
||||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=c++ -DUSE_EXTERNAL_TESTS=OFF
|
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=c++ -DUSE_EXTERNAL_TESTS=OFF -DOS=bsd
|
||||||
cmake --build build -j4 --verbose
|
cmake --build build -j4 --verbose
|
||||||
cmake --install build --verbose
|
cmake --install build --verbose
|
||||||
cmake --build build --target test
|
cmake --build build --target test
|
||||||
|
|||||||
69
ARCHITECTURE.md
Normal file
69
ARCHITECTURE.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# RGBDS Architecture
|
||||||
|
|
||||||
|
## Folder Organization
|
||||||
|
|
||||||
|
The RGBDS source code file structure is as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── .github/
|
||||||
|
│ ├── scripts/
|
||||||
|
│ │ └── ...
|
||||||
|
│ └── workflows/
|
||||||
|
│ └── ...
|
||||||
|
├── contrib/
|
||||||
|
│ ├── zsh_compl/
|
||||||
|
│ │ └── ...
|
||||||
|
│ └── ...
|
||||||
|
├── include/
|
||||||
|
│ └── ...
|
||||||
|
├── man/
|
||||||
|
│ └── ...
|
||||||
|
├── src/
|
||||||
|
│ ├── asm/
|
||||||
|
│ │ └── ...
|
||||||
|
│ ├── extern/
|
||||||
|
│ │ └── ...
|
||||||
|
│ ├── fix/
|
||||||
|
│ │ └── ...
|
||||||
|
│ ├── gfx/
|
||||||
|
│ │ └── ...
|
||||||
|
│ ├── link/
|
||||||
|
│ │ └── ...
|
||||||
|
│ ├── CMakeLists.txt
|
||||||
|
│ └── ...
|
||||||
|
├── test/
|
||||||
|
│ ├── ...
|
||||||
|
│ └── run-tests.sh
|
||||||
|
├── .clang-format
|
||||||
|
├── CMakeLists.txt
|
||||||
|
├── compile_flags.txt
|
||||||
|
├── Dockerfile
|
||||||
|
└── Makefile
|
||||||
|
```
|
||||||
|
|
||||||
|
- `.github/` - files and scripts related to the integration of the RGBDS codebase with
|
||||||
|
GitHub.
|
||||||
|
* `scripts/` - scripts used by workflow files.
|
||||||
|
* `workflows/` - CI workflow description files.
|
||||||
|
- `contrib/` - scripts and other resources which may be useful to users and developers of
|
||||||
|
RGBDS.
|
||||||
|
* `zsh_compl` - contains tab completion scripts for use with zsh. Put them somewhere in
|
||||||
|
your `fpath`, and they should auto-load.
|
||||||
|
* `bash_compl` - contains tab completion scripts for use with bash. Run them with `source`
|
||||||
|
somewhere in your `.bashrc`, and they should load every time you open a shell.
|
||||||
|
- `include/` - header files for the respective source files in `src`.
|
||||||
|
- `man/` - manual pages.
|
||||||
|
- `src/` - source code of RGBDS.
|
||||||
|
* Note that the code unique to each RGBDS tool is stored in its respective subdirectory
|
||||||
|
(RGBASM's code is in `src/asm/`, for example). `src/extern/` contains code imported from
|
||||||
|
external sources.
|
||||||
|
- `test/` - testing framework used to verify that changes to the code don't break or
|
||||||
|
modify the behavior of RGBDS.
|
||||||
|
- `.clang-format` - code style for automated C++ formatting with
|
||||||
|
[`clang-format`](https://clang.llvm.org/docs/ClangFormat.html).
|
||||||
|
- `CMakeLists.txt` - defines how to build RGBDS with CMake.
|
||||||
|
- `compile_flags.txt` - compiler flags for C++ static analysis with
|
||||||
|
[`clang-tidy`](https://clang.llvm.org/extra/clang-tidy/).
|
||||||
|
- `Dockerfile` - defines how to build RGBDS with Docker.
|
||||||
|
- `Makefile` - defines how to build RGBDS with `make`.
|
||||||
@@ -97,7 +97,7 @@ All tests begin by assembling the `.asm` file into an object file, which will be
|
|||||||
|
|
||||||
These simply check that RGBLINK's output matches some expected output.
|
These simply check that RGBLINK's output matches some expected output.
|
||||||
|
|
||||||
A `.out` file **must** exist, and RGBLINK's output must match that file's contents.
|
A `.out` file **must** exist, and RGBLINK's total output must match that file's contents.
|
||||||
|
|
||||||
Additionally, if a `.out.bin` file exists, the `.gb` file generated by RGBLINK must match it.
|
Additionally, if a `.out.bin` file exists, the `.gb` file generated by RGBLINK must match it.
|
||||||
|
|
||||||
@@ -106,14 +106,14 @@ Additionally, if a `.out.bin` file exists, the `.gb` file generated by RGBLINK m
|
|||||||
These allow applying various linker scripts to the same object file.
|
These allow applying various linker scripts to the same object file.
|
||||||
If one or more `.link` files exist, whose names start the same as the `.asm` file, then each of those files correspond to one test.
|
If one or more `.link` files exist, whose names start the same as the `.asm` file, then each of those files correspond to one test.
|
||||||
|
|
||||||
Each `.link` linker script **must** be accompanied by a `.out` file, and RGBLINK's output must match that file's contents when passed the corresponding linker script.
|
Each `.link` linker script **must** be accompanied by a `.out` file, and RGBLINK's total output must match that file's contents when passed the corresponding linker script.
|
||||||
|
|
||||||
#### Variant tests
|
#### Variant tests
|
||||||
|
|
||||||
These allow testing RGBLINK's `-d`, `-t`, and `-w` flags.
|
These allow testing RGBLINK's `-d`, `-t`, and `-w` flags.
|
||||||
If one or more <code>-<var><flag></var>.out</code> or <code>-no-<var><flag></var>.out</code> files exist, then each of them corresponds to one test.
|
If one or more <code>-<var><flag></var>.out</code> or <code>-no-<var><flag></var>.out</code> files exist, then each of them corresponds to one test.
|
||||||
|
|
||||||
The object file will be linked with and without said flag, respectively; and in each case, RGBLINK's output must match the `.out` file's contents.
|
The object file will be linked with and without said flag, respectively; and in each case, RGBLINK's total output must match the `.out` file's contents.
|
||||||
|
|
||||||
### RGBFIX
|
### RGBFIX
|
||||||
|
|
||||||
@@ -123,8 +123,11 @@ Each one is a text file whose first line contains flags to pass to RGBFIX.
|
|||||||
|
|
||||||
RGBFIX will be invoked on the `.bin` file if it exists, or else on default-input.bin.
|
RGBFIX will be invoked on the `.bin` file if it exists, or else on default-input.bin.
|
||||||
|
|
||||||
|
If no `.out` file exist, RGBFIX is not expected to output anything.
|
||||||
|
If one *does* exist, RGBFIX's output **must** match the `.out` file's contents.
|
||||||
|
|
||||||
If no `.err` file exists, RGBFIX is simply expected to be able to process the file normally.
|
If no `.err` file exists, RGBFIX is simply expected to be able to process the file normally.
|
||||||
If one *does* exist, RGBFIX's return status is ignored, but its output **must** match the `.err` file's contents.
|
If one *does* exist, RGBFIX's return status is ignored, but its error output **must** match the `.err` file's contents.
|
||||||
|
|
||||||
Additionally, if a `.gb` file exists, the output of RGBFIX must match the `.gb`.
|
Additionally, if a `.gb` file exists, the output of RGBFIX must match the `.gb`.
|
||||||
|
|
||||||
|
|||||||
@@ -32,3 +32,17 @@
|
|||||||
- yenatch <yenatch@gmail.com>
|
- yenatch <yenatch@gmail.com>
|
||||||
- phs <phil@philhsmith.com>
|
- phs <phil@philhsmith.com>
|
||||||
- jidoc01 <jidoc01@naver.com>
|
- jidoc01 <jidoc01@naver.com>
|
||||||
|
|
||||||
|
[<img src="https://contrib.rocks/image?repo=gbdev/rgbds">](https://github.com/gbdev/rgbds/graphs/contributors)
|
||||||
|
|
||||||
|
Contributor image made with [contrib.rocks](https://contrib.rocks).
|
||||||
|
|
||||||
|
## Acknowledgements
|
||||||
|
|
||||||
|
RGBGFX generates palettes using algorithms found in the paper
|
||||||
|
["Algorithms for the Pagination Problem, a Bin Packing with Overlapping Items"](https://arxiv.org/abs/1605.00558)
|
||||||
|
([GitHub](https://github.com/pagination-problem/pagination), MIT license),
|
||||||
|
by Aristide Grange, Imed Kacem, and Sébastien Martin.
|
||||||
|
|
||||||
|
RGBGFX's color palette was taken from [SameBoy](https://sameboy.github.io), with permission and help
|
||||||
|
by [LIJI](https://github.com/LIJI32).
|
||||||
|
|||||||
11
Dockerfile
11
Dockerfile
@@ -1,6 +1,6 @@
|
|||||||
FROM debian:12-slim
|
FROM debian:12-slim
|
||||||
LABEL org.opencontainers.image.source=https://github.com/gbdev/rgbds
|
LABEL org.opencontainers.image.source=https://github.com/gbdev/rgbds
|
||||||
ARG version=0.9.2
|
ARG version=1.0.0-rc1
|
||||||
WORKDIR /rgbds
|
WORKDIR /rgbds
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
@@ -8,7 +8,14 @@ COPY . .
|
|||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install sudo make cmake gcc build-essential -y
|
apt-get install sudo make cmake gcc build-essential -y
|
||||||
|
|
||||||
|
# Install dependencies and compile RGBDS
|
||||||
RUN ./.github/scripts/install_deps.sh ubuntu-22.04
|
RUN ./.github/scripts/install_deps.sh ubuntu-22.04
|
||||||
RUN make -j CXXFLAGS="-O3 -flto -DNDEBUG -static" PKG_CONFIG="pkg-config --static" Q=
|
RUN make -j CXXFLAGS="-O3 -flto -DNDEBUG -static" PKG_CONFIG="pkg-config --static" Q=
|
||||||
|
|
||||||
RUN tar caf rgbds-${version}-linux-x86_64.tar.xz --transform='s#.*/##' rgbasm rgblink rgbfix rgbgfx man/* .github/scripts/install.sh
|
# Create an archive with the compiled executables and all the necessary to install it,
|
||||||
|
# so it can be copied outside of the container and installed/used in another system
|
||||||
|
RUN tar caf rgbds-linux-x86_64.tar.xz --transform='s#.*/##' rgbasm rgblink rgbfix rgbgfx man/* .github/scripts/install.sh
|
||||||
|
|
||||||
|
# Install RGBDS on the container so all the executables will be available in the PATH
|
||||||
|
RUN cp man/* .
|
||||||
|
RUN ./.github/scripts/install.sh
|
||||||
|
|||||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
The MIT License
|
The MIT License
|
||||||
|
|
||||||
Copyright (c) 1997-2024, Carsten Sørensen and RGBDS contributors.
|
Copyright (c) 1996-2025, Carsten Sørensen and RGBDS contributors.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to
|
of this software and associated documentation files (the "Software"), to
|
||||||
|
|||||||
81
Makefile
81
Makefile
@@ -3,7 +3,7 @@
|
|||||||
.SUFFIXES:
|
.SUFFIXES:
|
||||||
.SUFFIXES: .cpp .y .o
|
.SUFFIXES: .cpp .y .o
|
||||||
|
|
||||||
.PHONY: all clean install checkdiff develop debug profile coverage iwyu mingw32 mingw64 wine-shim dist
|
.PHONY: all clean install checkdiff develop debug profile coverage format tidy iwyu mingw32 mingw64 wine-shim dist
|
||||||
|
|
||||||
# User-defined variables
|
# User-defined variables
|
||||||
|
|
||||||
@@ -49,7 +49,16 @@ BASE_REF := origin/master
|
|||||||
|
|
||||||
all: rgbasm rgblink rgbfix rgbgfx
|
all: rgbasm rgblink rgbfix rgbgfx
|
||||||
|
|
||||||
|
common_obj := \
|
||||||
|
src/extern/getopt.o \
|
||||||
|
src/diagnostics.o \
|
||||||
|
src/style.o \
|
||||||
|
src/usage.o \
|
||||||
|
src/util.o
|
||||||
|
|
||||||
rgbasm_obj := \
|
rgbasm_obj := \
|
||||||
|
${common_obj} \
|
||||||
|
src/asm/actions.o \
|
||||||
src/asm/charmap.o \
|
src/asm/charmap.o \
|
||||||
src/asm/fixpoint.o \
|
src/asm/fixpoint.o \
|
||||||
src/asm/format.o \
|
src/asm/format.o \
|
||||||
@@ -64,17 +73,20 @@ rgbasm_obj := \
|
|||||||
src/asm/section.o \
|
src/asm/section.o \
|
||||||
src/asm/symbol.o \
|
src/asm/symbol.o \
|
||||||
src/asm/warning.o \
|
src/asm/warning.o \
|
||||||
src/extern/getopt.o \
|
|
||||||
src/extern/utf8decoder.o \
|
src/extern/utf8decoder.o \
|
||||||
src/error.o \
|
src/backtrace.o \
|
||||||
src/linkdefs.o \
|
src/linkdefs.o \
|
||||||
src/opmath.o \
|
src/opmath.o \
|
||||||
src/util.o
|
src/verbosity.o
|
||||||
|
|
||||||
src/asm/lexer.o src/asm/main.o: src/asm/parser.hpp
|
src/asm/lexer.o src/asm/main.o: src/asm/parser.hpp
|
||||||
|
|
||||||
rgblink_obj := \
|
rgblink_obj := \
|
||||||
|
${common_obj} \
|
||||||
src/link/assign.o \
|
src/link/assign.o \
|
||||||
|
src/link/fstack.o \
|
||||||
|
src/link/lexer.o \
|
||||||
|
src/link/layout.o \
|
||||||
src/link/main.o \
|
src/link/main.o \
|
||||||
src/link/object.o \
|
src/link/object.o \
|
||||||
src/link/output.o \
|
src/link/output.o \
|
||||||
@@ -83,34 +95,38 @@ rgblink_obj := \
|
|||||||
src/link/sdas_obj.o \
|
src/link/sdas_obj.o \
|
||||||
src/link/section.o \
|
src/link/section.o \
|
||||||
src/link/symbol.o \
|
src/link/symbol.o \
|
||||||
src/extern/getopt.o \
|
src/link/warning.o \
|
||||||
src/extern/utf8decoder.o \
|
src/extern/utf8decoder.o \
|
||||||
src/error.o \
|
src/backtrace.o \
|
||||||
src/linkdefs.o \
|
src/linkdefs.o \
|
||||||
src/opmath.o \
|
src/opmath.o \
|
||||||
src/util.o
|
src/verbosity.o
|
||||||
|
|
||||||
src/link/main.o: src/link/script.hpp
|
src/link/lexer.o src/link/main.o: src/link/script.hpp
|
||||||
|
|
||||||
rgbfix_obj := \
|
rgbfix_obj := \
|
||||||
|
${common_obj} \
|
||||||
|
src/fix/fix.o \
|
||||||
src/fix/main.o \
|
src/fix/main.o \
|
||||||
src/extern/getopt.o \
|
src/fix/mbc.o \
|
||||||
src/error.o
|
src/fix/warning.o
|
||||||
|
|
||||||
rgbgfx_obj := \
|
rgbgfx_obj := \
|
||||||
|
${common_obj} \
|
||||||
|
src/gfx/color_set.o \
|
||||||
src/gfx/main.o \
|
src/gfx/main.o \
|
||||||
src/gfx/pal_packing.o \
|
src/gfx/pal_packing.o \
|
||||||
src/gfx/pal_sorting.o \
|
src/gfx/pal_sorting.o \
|
||||||
src/gfx/pal_spec.o \
|
src/gfx/pal_spec.o \
|
||||||
|
src/gfx/png.o \
|
||||||
src/gfx/process.o \
|
src/gfx/process.o \
|
||||||
src/gfx/proto_palette.o \
|
|
||||||
src/gfx/reverse.o \
|
src/gfx/reverse.o \
|
||||||
src/gfx/rgba.o \
|
src/gfx/rgba.o \
|
||||||
src/extern/getopt.o \
|
src/gfx/warning.o \
|
||||||
src/error.o
|
src/verbosity.o
|
||||||
|
|
||||||
rgbasm: ${rgbasm_obj}
|
rgbasm: ${rgbasm_obj}
|
||||||
$Q${CXX} ${REALLDFLAGS} -o $@ ${rgbasm_obj} ${REALCXXFLAGS} src/version.cpp -lm
|
$Q${CXX} ${REALLDFLAGS} -o $@ ${rgbasm_obj} ${REALCXXFLAGS} src/version.cpp
|
||||||
|
|
||||||
rgblink: ${rgblink_obj}
|
rgblink: ${rgblink_obj}
|
||||||
$Q${CXX} ${REALLDFLAGS} -o $@ ${rgblink_obj} ${REALCXXFLAGS} src/version.cpp
|
$Q${CXX} ${REALLDFLAGS} -o $@ ${rgblink_obj} ${REALCXXFLAGS} src/version.cpp
|
||||||
@@ -142,6 +158,8 @@ src/link/script.hpp: src/link/script.cpp
|
|||||||
$Qtouch $@
|
$Qtouch $@
|
||||||
|
|
||||||
# Only RGBGFX uses libpng (POSIX make doesn't support pattern rules to cover all these)
|
# Only RGBGFX uses libpng (POSIX make doesn't support pattern rules to cover all these)
|
||||||
|
src/gfx/color_set.o: src/gfx/color_set.cpp
|
||||||
|
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
||||||
src/gfx/main.o: src/gfx/main.cpp
|
src/gfx/main.o: src/gfx/main.cpp
|
||||||
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
||||||
src/gfx/pal_packing.o: src/gfx/pal_packing.cpp
|
src/gfx/pal_packing.o: src/gfx/pal_packing.cpp
|
||||||
@@ -150,9 +168,9 @@ src/gfx/pal_sorting.o: src/gfx/pal_sorting.cpp
|
|||||||
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
||||||
src/gfx/pal_spec.o: src/gfx/pal_spec.cpp
|
src/gfx/pal_spec.o: src/gfx/pal_spec.cpp
|
||||||
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
||||||
src/gfx/process.o: src/gfx/process.cpp
|
src/gfx/png.o: src/gfx/png.cpp
|
||||||
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
||||||
src/gfx/proto_palette.o: src/gfx/proto_palette.cpp
|
src/gfx/process.o: src/gfx/process.cpp
|
||||||
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
||||||
src/gfx/reverse.o: src/gfx/reverse.cpp
|
src/gfx/reverse.o: src/gfx/reverse.cpp
|
||||||
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
||||||
@@ -163,7 +181,6 @@ src/gfx/rgba.o: src/gfx/rgba.cpp
|
|||||||
$Q${CXX} ${REALCXXFLAGS} -c -o $@ $<
|
$Q${CXX} ${REALCXXFLAGS} -c -o $@ $<
|
||||||
|
|
||||||
# Target used to remove all files generated by other Makefile targets
|
# Target used to remove all files generated by other Makefile targets
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$Q${RM} rgbasm rgbasm.exe
|
$Q${RM} rgbasm rgbasm.exe
|
||||||
$Q${RM} rgblink rgblink.exe
|
$Q${RM} rgblink rgblink.exe
|
||||||
@@ -177,7 +194,6 @@ clean:
|
|||||||
$Q${RM} test/gfx/randtilegen test/gfx/rgbgfx_test
|
$Q${RM} test/gfx/randtilegen test/gfx/rgbgfx_test
|
||||||
|
|
||||||
# Target used to install the binaries and man pages.
|
# Target used to install the binaries and man pages.
|
||||||
|
|
||||||
install: all
|
install: all
|
||||||
$Qinstall -d ${DESTDIR}${bindir}/ ${DESTDIR}${mandir}/man1/ ${DESTDIR}${mandir}/man5/ ${DESTDIR}${mandir}/man7/
|
$Qinstall -d ${DESTDIR}${bindir}/ ${DESTDIR}${mandir}/man1/ ${DESTDIR}${mandir}/man5/ ${DESTDIR}${mandir}/man7/
|
||||||
$Qinstall ${STRIP} -m ${BINMODE} rgbasm ${DESTDIR}${bindir}/rgbasm${SUFFIX}
|
$Qinstall ${STRIP} -m ${BINMODE} rgbasm ${DESTDIR}${bindir}/rgbasm${SUFFIX}
|
||||||
@@ -189,15 +205,13 @@ install: all
|
|||||||
$Qinstall -m ${MANMODE} man/rgbds.7 man/gbz80.7 ${DESTDIR}${mandir}/man7/
|
$Qinstall -m ${MANMODE} man/rgbds.7 man/gbz80.7 ${DESTDIR}${mandir}/man7/
|
||||||
|
|
||||||
# Target used to check for suspiciously missing changed files.
|
# Target used to check for suspiciously missing changed files.
|
||||||
|
|
||||||
checkdiff:
|
checkdiff:
|
||||||
$Qcontrib/checkdiff.bash `git merge-base HEAD ${BASE_REF}`
|
$Qcontrib/checkdiff.bash `git merge-base HEAD ${BASE_REF}`
|
||||||
|
|
||||||
# This target is used during development in order to prevent adding new issues
|
# Target used in development to prevent adding new issues to the source code.
|
||||||
# to the source code. All warnings are treated as errors in order to block the
|
# All warnings are treated as errors to block the compilation and make the
|
||||||
# compilation and make the continous integration infrastructure return failure.
|
# continous integration infrastructure return failure.
|
||||||
# The rationale for some of the flags is documented in the CMakeLists.
|
# The rationale for some of the flags is documented in the CMakeLists.
|
||||||
|
|
||||||
develop:
|
develop:
|
||||||
$Q${MAKE} WARNFLAGS="${WARNFLAGS} -Werror -Wextra \
|
$Q${MAKE} WARNFLAGS="${WARNFLAGS} -Werror -Wextra \
|
||||||
-Walloc-zero -Wcast-align -Wcast-qual -Wduplicated-branches -Wduplicated-cond \
|
-Walloc-zero -Wcast-align -Wcast-qual -Wduplicated-branches -Wduplicated-cond \
|
||||||
@@ -210,26 +224,31 @@ develop:
|
|||||||
-fsanitize=float-divide-by-zero" \
|
-fsanitize=float-divide-by-zero" \
|
||||||
CXXFLAGS="-ggdb3 -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls"
|
CXXFLAGS="-ggdb3 -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls"
|
||||||
|
|
||||||
# This target is used during development in order to more easily debug with gdb.
|
# Target used in development to debug with gdb.
|
||||||
|
|
||||||
debug:
|
debug:
|
||||||
$Qenv ${MAKE} \
|
$Qenv ${MAKE} \
|
||||||
CXXFLAGS="-ggdb3 -O0 -fno-omit-frame-pointer -fno-optimize-sibling-calls"
|
CXXFLAGS="-ggdb3 -O0 -fno-omit-frame-pointer -fno-optimize-sibling-calls"
|
||||||
|
|
||||||
# This target is used during development in order to more easily profile with callgrind.
|
# Target used in development to profile with callgrind.
|
||||||
|
|
||||||
profile:
|
profile:
|
||||||
$Qenv ${MAKE} \
|
$Qenv ${MAKE} \
|
||||||
CXXFLAGS="-ggdb3 -O3 -fno-omit-frame-pointer -fno-optimize-sibling-calls"
|
CXXFLAGS="-ggdb3 -O3 -fno-omit-frame-pointer -fno-optimize-sibling-calls"
|
||||||
|
|
||||||
# This target is used during development in order to inspect code coverage with gcov.
|
# Target used in development to inspect code coverage with gcov.
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
$Qenv ${MAKE} \
|
$Qenv ${MAKE} \
|
||||||
CXXFLAGS="-ggdb3 -Og --coverage -fno-omit-frame-pointer -fno-optimize-sibling-calls"
|
CXXFLAGS="-ggdb3 -Og --coverage -fno-omit-frame-pointer -fno-optimize-sibling-calls"
|
||||||
|
|
||||||
# This target is used during development in order to remove unused `#include` headers.
|
# Target used in development to format source code with clang-format.
|
||||||
|
format:
|
||||||
|
$Qclang-format -i $$(git ls-files 'include/**/*.hpp' 'src/**/*.cpp')
|
||||||
|
|
||||||
|
# Target used in development to check code with clang-tidy.
|
||||||
|
# Requires Bison-generated header files to exist.
|
||||||
|
tidy: src/asm/parser.hpp src/link/script.hpp
|
||||||
|
$Qclang-tidy -p . $$(git ls-files 'include/**/*.hpp' 'src/**/*.cpp')
|
||||||
|
|
||||||
|
# Target used in development to remove unused `#include` headers.
|
||||||
iwyu:
|
iwyu:
|
||||||
$Qenv ${MAKE} \
|
$Qenv ${MAKE} \
|
||||||
CXX="include-what-you-use" \
|
CXX="include-what-you-use" \
|
||||||
@@ -265,4 +284,4 @@ wine-shim:
|
|||||||
|
|
||||||
dist:
|
dist:
|
||||||
$Qgit ls-files | sed s~^~$${PWD##*/}/~ \
|
$Qgit ls-files | sed s~^~$${PWD##*/}/~ \
|
||||||
| tar -czf rgbds-`git -c safe.directory='*' describe --tags | cut -c 2-`.tar.gz -C .. -T -
|
| tar -czf rgbds-source.tar.gz -C .. -T -
|
||||||
|
|||||||
121
README.md
121
README.md
@@ -6,24 +6,24 @@ for the Game Boy and Game Boy Color. It consists of:
|
|||||||
- RGBASM (assembler)
|
- RGBASM (assembler)
|
||||||
- RGBLINK (linker)
|
- RGBLINK (linker)
|
||||||
- RGBFIX (checksum/header fixer)
|
- RGBFIX (checksum/header fixer)
|
||||||
- RGBGFX (PNG‐to‐Game Boy graphics converter)
|
- RGBGFX (PNG-to-Game Boy graphics converter)
|
||||||
|
|
||||||
This is a fork of the original RGBDS which aims to make the programs more like
|
This is a fork of the original RGBDS which aims to make the programs more like
|
||||||
other UNIX tools.
|
other UNIX tools.
|
||||||
|
|
||||||
|
|
||||||
This toolchain is maintained [on GitHub](https://github.com/gbdev/rgbds).
|
This toolchain is maintained [on GitHub](https://github.com/gbdev/rgbds).
|
||||||
|
|
||||||
The documentation of this toolchain can be [viewed online](https://rgbds.gbdev.io/docs/).
|
The documentation of this toolchain can be [viewed online](https://rgbds.gbdev.io/docs/),
|
||||||
|
including its [basic usage and development history](https://rgbds.gbdev.io/docs/rgbds.7).
|
||||||
It is generated from the man pages found in this repository.
|
It is generated from the man pages found in this repository.
|
||||||
The source code of the website itself is on GitHub as well under the repository
|
The source code of the website itself is on GitHub as well under the repository
|
||||||
[rgbds-www](https://github.com/gbdev/rgbds-www).
|
[rgbds-www](https://github.com/gbdev/rgbds-www).
|
||||||
|
|
||||||
If you want to contribute or maintain RGBDS, or you have questions regarding the code, its
|
If you want to contribute or maintain RGBDS, read [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||||
organization, etc. you can find the maintainers [on the gbdev community channels](https://gbdev.io/chat)
|
If you have questions regarding the code, its organization, etc. you can find the maintainers
|
||||||
or via mail at `rgbds at gbdev dot io`.
|
[on the GBDev community channels](https://gbdev.io/chat) or via mail at `rgbds at gbdev dot io`.
|
||||||
|
|
||||||
## 1. Installing RGBDS
|
## Installing RGBDS
|
||||||
|
|
||||||
The [installation procedure](https://rgbds.gbdev.io/install) is available
|
The [installation procedure](https://rgbds.gbdev.io/install) is available
|
||||||
online for various platforms. [Building from source](https://rgbds.gbdev.io/install/source)
|
online for various platforms. [Building from source](https://rgbds.gbdev.io/install/source)
|
||||||
@@ -55,110 +55,3 @@ cmake --install build --prefix install_dir
|
|||||||
```
|
```
|
||||||
|
|
||||||
(If you set a `SUFFIX`, it should include the `.exe` extension on Windows.)
|
(If you set a `SUFFIX`, it should include the `.exe` extension on Windows.)
|
||||||
|
|
||||||
## 2. RGBDS Folder Organization
|
|
||||||
|
|
||||||
The RGBDS source code file structure is as follows:
|
|
||||||
|
|
||||||
```
|
|
||||||
.
|
|
||||||
├── .github/
|
|
||||||
│ ├── scripts/
|
|
||||||
│ │ └── ...
|
|
||||||
│ └── workflows/
|
|
||||||
│ └── ...
|
|
||||||
├── contrib/
|
|
||||||
│ ├── zsh_compl/
|
|
||||||
│ │ └── ...
|
|
||||||
│ └── ...
|
|
||||||
├── include/
|
|
||||||
│ └── ...
|
|
||||||
├── man/
|
|
||||||
│ └── ...
|
|
||||||
├── src/
|
|
||||||
│ ├── asm/
|
|
||||||
│ │ └── ...
|
|
||||||
│ ├── extern/
|
|
||||||
│ │ └── ...
|
|
||||||
│ ├── fix/
|
|
||||||
│ │ └── ...
|
|
||||||
│ ├── gfx/
|
|
||||||
│ │ └── ...
|
|
||||||
│ ├── link/
|
|
||||||
│ │ └── ...
|
|
||||||
│ ├── CMakeLists.txt
|
|
||||||
│ └── ...
|
|
||||||
├── test/
|
|
||||||
│ ├── ...
|
|
||||||
│ └── run-tests.sh
|
|
||||||
├── .clang-format
|
|
||||||
├── CMakeLists.txt
|
|
||||||
├── Dockerfile
|
|
||||||
├── Makefile
|
|
||||||
└── README.md
|
|
||||||
```
|
|
||||||
|
|
||||||
- `.github/` - files and scripts related to the integration of the RGBDS codebase with
|
|
||||||
GitHub.
|
|
||||||
* `scripts/` - scripts used by workflow files.
|
|
||||||
* `workflows/` - CI workflow description files.
|
|
||||||
- `contrib/` - scripts and other resources which may be useful to users and developers of
|
|
||||||
RGBDS.
|
|
||||||
* `zsh_compl` contains tab completion scripts for use with zsh. Put them somewhere in
|
|
||||||
your `fpath`, and they should auto-load.
|
|
||||||
* `bash_compl` contains tab completion scripts for use with bash. Run them with `source`
|
|
||||||
somewhere in your `.bashrc`, and they should load every time you open a shell.
|
|
||||||
- `include/` - header files for the respective source files in `src`.
|
|
||||||
- `man/` - manual pages.
|
|
||||||
- `src/` - source code of RGBDS.
|
|
||||||
* Note that the code unique to each RGBDS tool is stored in its respective subdirectory
|
|
||||||
(RGBASM's code is in `src/asm/`, for example). `src/extern/` contains code imported from
|
|
||||||
external sources.
|
|
||||||
- `test/` - testing framework used to verify that changes to the code don't break or
|
|
||||||
modify the behavior of RGBDS.
|
|
||||||
- `.clang-format` - code style for automated C++ formatting with
|
|
||||||
[`clang-format`](https://clang.llvm.org/docs/ClangFormat.html).
|
|
||||||
- `Dockerfile` - defines how to build RGBDS with Docker.
|
|
||||||
|
|
||||||
## 3. History
|
|
||||||
|
|
||||||
- 1996-10-01: Carsten Sørensen (a.k.a. SurfSmurf) releases
|
|
||||||
[xAsm](http://otakunozoku.com/RGBDSdocs/asm.htm),
|
|
||||||
[xLink](http://otakunozoku.com/RGBDSdocs/link.htm), and
|
|
||||||
[RGBFix](http://otakunozoku.com/RGBDSdocs/fix.htm),
|
|
||||||
a Game Boy SM83 (GBZ80) assembler/linker system for DOS/Win32.
|
|
||||||
- 1997-07-03: Sørensen releases [ASMotor](http://otakunozoku.com/RGBDSdocs/geninfo.htm),
|
|
||||||
packaging the three programs together and moving towards making them a
|
|
||||||
general-purpose target-independent system.
|
|
||||||
- 1999-08-01: Justin Lloyd (a.k.a. Otaku no Zoku) adapts ASMotor to re-focus
|
|
||||||
on SM83 assembly/machine code, and releases this version as
|
|
||||||
[RGBDS](http://otakunozoku.com/rednex-gameboy-development-system/).
|
|
||||||
- 2009-06-11: Vegard Nossum adapts the code to be more UNIX-like and releases
|
|
||||||
this version as [rgbds-linux](https://github.com/vegard/rgbds-linux).
|
|
||||||
- 2010-01-12: Anthony J. Bentley [forks](https://github.com/bentley) Nossum's
|
|
||||||
repository. The fork becomes the reference implementation of RGBDS.
|
|
||||||
- 2010-09-25: Sørensen continues development of
|
|
||||||
[ASMotor](https://github.com/asmotor/asmotor) to this day.
|
|
||||||
- 2015-01-18: stag019 begins implementing [RGBGFX](https://github.com/stag019/rgbgfx),
|
|
||||||
a PNG‐to‐Game Boy graphics converter, for eventual integration into RGBDS.
|
|
||||||
- 2016-09-05: RGBGFX is
|
|
||||||
[integrated](https://github.com/gbdev/rgbds/commit/c3c31138ddbd8680d4e67957e387f2816798a71b)
|
|
||||||
into Bentley's repository.
|
|
||||||
- 2017-02-23: Bentley's repository is moved to the [rednex](https://github.com/rednex)
|
|
||||||
organization.
|
|
||||||
- 2018-01-26: The codebase is [relicensed](https://github.com/gbdev/rgbds/issues/128)
|
|
||||||
under the MIT license.
|
|
||||||
- 2020-09-15: The repository is [moved](https://github.com/gbdev/rgbds/issues/567)
|
|
||||||
to the [gbdev](https://github.com/gbdev) organization.
|
|
||||||
- 2022-05-17: The [rgbds.gbdev.io](https://rgbds.gbdev.io) website for RGBDS
|
|
||||||
documentation and downloads is published.
|
|
||||||
|
|
||||||
## 4. Acknowledgements
|
|
||||||
|
|
||||||
RGBGFX generates palettes using algorithms found in the paper
|
|
||||||
["Algorithms for the Pagination Problem, a Bin Packing with Overlapping Items"](https://arxiv.org/abs/1605.00558)
|
|
||||||
([GitHub](https://github.com/pagination-problem/pagination), MIT license),
|
|
||||||
by Aristide Grange, Imed Kacem, and Sébastien Martin.
|
|
||||||
|
|
||||||
RGBGFX's color palette was taken from [SameBoy](https://sameboy.github.io), with permission and help
|
|
||||||
by [LIJI](https://github.com/LIJI32).
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ GitHub.
|
|||||||
|
|
||||||
6. Click the "Publish release" button to publish it!
|
6. Click the "Publish release" button to publish it!
|
||||||
|
|
||||||
7. Update the `release` branch. You can use `git push origin release`.
|
7. Update the `release` branch. You can use `git push origin master:release`.
|
||||||
|
|
||||||
8. Update the following related projects.
|
8. Update the following related projects.
|
||||||
|
|
||||||
@@ -77,9 +77,7 @@ GitHub.
|
|||||||
to list the new release.
|
to list the new release.
|
||||||
2. [rgbds-live](https://github.com/gbdev/rgbds-live): update the `rgbds` submodule (and
|
2. [rgbds-live](https://github.com/gbdev/rgbds-live): update the `rgbds` submodule (and
|
||||||
[patches/rgbds.patch](https://github.com/gbdev/rgbds-live/blob/master/patches/rgbds.patch)
|
[patches/rgbds.patch](https://github.com/gbdev/rgbds-live/blob/master/patches/rgbds.patch)
|
||||||
if necessary) to use the new release, and
|
if necessary) to use the new release.
|
||||||
[index.html](https://github.com/gbdev/rgbds-live/blob/master/index.html)
|
|
||||||
to link to the new manual version.
|
|
||||||
3. [rgbobj](https://github.com/gbdev/rgbobj) and [rgbds-obj](https://github.com/gbdev/rgbds-obj):
|
3. [rgbobj](https://github.com/gbdev/rgbobj) and [rgbds-obj](https://github.com/gbdev/rgbds-obj):
|
||||||
make sure that object files created by the latest RGBASM can be parsed and displayed.
|
make sure that object files created by the latest RGBASM can be parsed and displayed.
|
||||||
If the object file revision has been updated, `rgbobj` will need a corresponding release.
|
If the object file revision has been updated, `rgbobj` will need a corresponding release.
|
||||||
|
|||||||
6
compile_flags.txt
Normal file
6
compile_flags.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
-std=c++2a
|
||||||
|
-I
|
||||||
|
include
|
||||||
|
-fno-exceptions
|
||||||
|
-fno-rtti
|
||||||
|
-fno-caret-diagnostics
|
||||||
@@ -24,13 +24,14 @@ _rgbasm_completions() {
|
|||||||
# Empty long opt = it doesn't exit
|
# Empty long opt = it doesn't exit
|
||||||
# See the `state` variable below for info about `state_after`
|
# See the `state` variable below for info about `state_after`
|
||||||
declare -A opts=(
|
declare -A opts=(
|
||||||
[V]="version:normal"
|
|
||||||
[h]="help:normal"
|
[h]="help:normal"
|
||||||
[E]="export-all:normal"
|
[V]="version:normal"
|
||||||
[v]="verbose:normal"
|
[W]="warning:warning"
|
||||||
[w]=":normal"
|
[w]=":normal"
|
||||||
|
[B]="backtrace:unk"
|
||||||
[b]="binary-digits:unk"
|
[b]="binary-digits:unk"
|
||||||
[D]="define:unk"
|
[D]="define:unk"
|
||||||
|
[E]="export-all:normal"
|
||||||
[g]="gfx-chars:unk"
|
[g]="gfx-chars:unk"
|
||||||
[I]="include:dir"
|
[I]="include:dir"
|
||||||
[M]="dependfile:glob-*.mk *.d"
|
[M]="dependfile:glob-*.mk *.d"
|
||||||
@@ -40,7 +41,7 @@ _rgbasm_completions() {
|
|||||||
[Q]="q-precision:unk"
|
[Q]="q-precision:unk"
|
||||||
[r]="recursion-depth:unk"
|
[r]="recursion-depth:unk"
|
||||||
[s]="state:unk"
|
[s]="state:unk"
|
||||||
[W]="warning:warning"
|
[v]="verbose:normal"
|
||||||
[X]="max-errors:unk"
|
[X]="max-errors:unk"
|
||||||
)
|
)
|
||||||
# Parse command-line up to current word
|
# Parse command-line up to current word
|
||||||
@@ -61,7 +62,7 @@ _rgbasm_completions() {
|
|||||||
parse_short_opt() {
|
parse_short_opt() {
|
||||||
# These options act like a long option (= takes up the entire word), but only use a single dash
|
# These options act like a long option (= takes up the entire word), but only use a single dash
|
||||||
# So, they need some special handling
|
# So, they need some special handling
|
||||||
if [[ "$1" = "-M"[GP] ]]; then
|
if [[ "$1" = "-M"[CGP] ]]; then
|
||||||
state=normal
|
state=normal
|
||||||
optlen=${#1}
|
optlen=${#1}
|
||||||
return;
|
return;
|
||||||
@@ -146,7 +147,7 @@ _rgbasm_completions() {
|
|||||||
# It is, try to complete one
|
# It is, try to complete one
|
||||||
mapfile -t COMPREPLY < <(compgen -W "${opts[*]%%:*}" -P '--' -- "${cur_word#--}")
|
mapfile -t COMPREPLY < <(compgen -W "${opts[*]%%:*}" -P '--' -- "${cur_word#--}")
|
||||||
return 0
|
return 0
|
||||||
elif [[ "$cur_word" = '-M'[GPQT] ]]; then
|
elif [[ "$cur_word" = '-M'[CGPQT] ]]; then
|
||||||
# These options act like long opts with no arguments, so return them and exactly them
|
# These options act like long opts with no arguments, so return them and exactly them
|
||||||
COMPREPLY=( "$cur_word" )
|
COMPREPLY=( "$cur_word" )
|
||||||
return 0
|
return 0
|
||||||
@@ -155,7 +156,7 @@ _rgbasm_completions() {
|
|||||||
parse_short_opt "$cur_word"
|
parse_short_opt "$cur_word"
|
||||||
|
|
||||||
if [[ "$state" = 'normal' ]]; then
|
if [[ "$state" = 'normal' ]]; then
|
||||||
mapfile -t COMPREPLY < <(compgen -W "${!opts[*]}" -P "$cur_word" ''; compgen -W '-MG -MP -MQ -MT' "$cur_word")
|
mapfile -t COMPREPLY < <(compgen -W "${!opts[*]}" -P "$cur_word" ''; compgen -W '-MC -MG -MP -MQ -MT' "$cur_word")
|
||||||
return 0
|
return 0
|
||||||
elif [[ "$optlen" = "${#cur_word}" && "$state" != "warning" ]]; then
|
elif [[ "$optlen" = "${#cur_word}" && "$state" != "warning" ]]; then
|
||||||
# This short option group only awaits its argument!
|
# This short option group only awaits its argument!
|
||||||
@@ -183,6 +184,7 @@ _rgbasm_completions() {
|
|||||||
empty-data-directive
|
empty-data-directive
|
||||||
empty-macro-arg
|
empty-macro-arg
|
||||||
empty-strrpl
|
empty-strrpl
|
||||||
|
export-undefined
|
||||||
large-constant
|
large-constant
|
||||||
macro-shift
|
macro-shift
|
||||||
nested-comment
|
nested-comment
|
||||||
|
|||||||
@@ -7,15 +7,15 @@ _rgbfix_completions() {
|
|||||||
# Empty long opt = it doesn't exit
|
# Empty long opt = it doesn't exit
|
||||||
# See the `state` variable below for info about `state_after`
|
# See the `state` variable below for info about `state_after`
|
||||||
declare -A opts=(
|
declare -A opts=(
|
||||||
[V]="version:normal"
|
|
||||||
[h]="help:normal"
|
[h]="help:normal"
|
||||||
[j]="non-japanese:normal"
|
[V]="version:normal"
|
||||||
[s]="sgb-compatible:normal"
|
[W]="warning:warning"
|
||||||
[v]="validate:normal"
|
[w]=":normal"
|
||||||
[C]="color-only:normal"
|
[C]="color-only:normal"
|
||||||
[c]="color-compatible:normal"
|
[c]="color-compatible:normal"
|
||||||
[f]="fix-spec:fix-spec"
|
[f]="fix-spec:fix-spec"
|
||||||
[i]="game-id:unk"
|
[i]="game-id:unk"
|
||||||
|
[j]="non-japanese:normal"
|
||||||
[k]="new-licensee:unk"
|
[k]="new-licensee:unk"
|
||||||
[L]="custom-logo:glob-*.1bpp"
|
[L]="custom-logo:glob-*.1bpp"
|
||||||
[l]="old-licensee:unk"
|
[l]="old-licensee:unk"
|
||||||
@@ -24,7 +24,9 @@ _rgbfix_completions() {
|
|||||||
[o]="output:glob-*.gb *.gbc *.sgb"
|
[o]="output:glob-*.gb *.gbc *.sgb"
|
||||||
[p]="pad-value:unk"
|
[p]="pad-value:unk"
|
||||||
[r]="ram-size:unk"
|
[r]="ram-size:unk"
|
||||||
|
[s]="sgb-compatible:normal"
|
||||||
[t]="title:unk"
|
[t]="title:unk"
|
||||||
|
[v]="validate:normal"
|
||||||
)
|
)
|
||||||
# Parse command-line up to current word
|
# Parse command-line up to current word
|
||||||
local opt_ena=true
|
local opt_ena=true
|
||||||
@@ -140,6 +142,17 @@ _rgbfix_completions() {
|
|||||||
case "$state" in
|
case "$state" in
|
||||||
unk) # Return with no replies: no idea what to complete!
|
unk) # Return with no replies: no idea what to complete!
|
||||||
;;
|
;;
|
||||||
|
warning)
|
||||||
|
mapfile -t COMPREPLY < <(compgen -W "
|
||||||
|
mbc
|
||||||
|
obsolete
|
||||||
|
overwrite
|
||||||
|
sgb
|
||||||
|
truncation
|
||||||
|
all
|
||||||
|
everything
|
||||||
|
error" -P "${cur_word:0:$optlen}" -- "${cur_word:$optlen}")
|
||||||
|
;;
|
||||||
fix-spec)
|
fix-spec)
|
||||||
COMPREPLY=( "${cur_word}"{l,h,g,L,H,G} )
|
COMPREPLY=( "${cur_word}"{l,h,g,L,H,G} )
|
||||||
;;
|
;;
|
||||||
|
|||||||
@@ -7,36 +7,38 @@ _rgbgfx_completions() {
|
|||||||
# Empty long opt = it doesn't exit
|
# Empty long opt = it doesn't exit
|
||||||
# See the `state` variable below for info about `state_after`
|
# See the `state` variable below for info about `state_after`
|
||||||
declare -A opts=(
|
declare -A opts=(
|
||||||
[V]="version:normal"
|
|
||||||
[h]="help:normal"
|
[h]="help:normal"
|
||||||
[C]="color-curve:normal"
|
[V]="version:normal"
|
||||||
[m]="mirror-tiles:normal"
|
[W]="warning:warning"
|
||||||
[O]="group-outputs:normal"
|
[w]=":normal"
|
||||||
[u]="unique-tiles:normal"
|
|
||||||
[v]="verbose:normal"
|
|
||||||
[X]="mirror-x:normal"
|
|
||||||
[Y]="mirror-y:normal"
|
|
||||||
[Z]="columns:normal"
|
|
||||||
[a]="attr-map:glob-*.attrmap"
|
|
||||||
[A]="auto-attr-map:normal"
|
[A]="auto-attr-map:normal"
|
||||||
|
[a]="attr-map:glob-*.attrmap"
|
||||||
[B]="background-color:unk"
|
[B]="background-color:unk"
|
||||||
[b]="base-tiles:unk"
|
[b]="base-tiles:unk"
|
||||||
|
[C]="color-curve:normal"
|
||||||
[c]="colors:unk"
|
[c]="colors:unk"
|
||||||
[d]="depth:unk"
|
[d]="depth:unk"
|
||||||
[i]="input-tileset:glob-*.2bpp"
|
[i]="input-tileset:glob-*.2bpp"
|
||||||
[L]="slice:unk"
|
[L]="slice:unk"
|
||||||
|
[m]="mirror-tiles:normal"
|
||||||
[N]="nb-tiles:unk"
|
[N]="nb-tiles:unk"
|
||||||
[n]="nb-palettes:unk"
|
[n]="nb-palettes:unk"
|
||||||
|
[O]="group-outputs:normal"
|
||||||
[o]="output:glob-*.2bpp"
|
[o]="output:glob-*.2bpp"
|
||||||
[p]="palette:glob-*.pal"
|
|
||||||
[P]="auto-palette:normal"
|
[P]="auto-palette:normal"
|
||||||
[q]="palette-map:glob-*.palmap"
|
[p]="palette:glob-*.pal"
|
||||||
[Q]="auto-palette-map:normal"
|
[Q]="auto-palette-map:normal"
|
||||||
|
[q]="palette-map:glob-*.palmap"
|
||||||
[r]="reverse:unk"
|
[r]="reverse:unk"
|
||||||
[s]="palette-size:unk"
|
[s]="palette-size:unk"
|
||||||
[t]="tilemap:glob-*.tilemap"
|
|
||||||
[T]="auto-tilemap:normal"
|
[T]="auto-tilemap:normal"
|
||||||
|
[t]="tilemap:glob-*.tilemap"
|
||||||
|
[u]="unique-tiles:normal"
|
||||||
|
[v]="verbose:normal"
|
||||||
|
[X]="mirror-x:normal"
|
||||||
[x]="trim-end:unk"
|
[x]="trim-end:unk"
|
||||||
|
[Y]="mirror-y:normal"
|
||||||
|
[Z]="columns:normal"
|
||||||
)
|
)
|
||||||
# Parse command-line up to current word
|
# Parse command-line up to current word
|
||||||
local opt_ena=true
|
local opt_ena=true
|
||||||
@@ -152,6 +154,15 @@ _rgbgfx_completions() {
|
|||||||
case "$state" in
|
case "$state" in
|
||||||
unk) # Return with no replies: no idea what to complete!
|
unk) # Return with no replies: no idea what to complete!
|
||||||
;;
|
;;
|
||||||
|
warning)
|
||||||
|
mapfile -t COMPREPLY < <(compgen -W "
|
||||||
|
embedded
|
||||||
|
obsolete
|
||||||
|
trim-nonempty
|
||||||
|
all
|
||||||
|
everything
|
||||||
|
error" -P "${cur_word:0:$optlen}" -- "${cur_word:$optlen}")
|
||||||
|
;;
|
||||||
normal) # Acts like a glob...
|
normal) # Acts like a glob...
|
||||||
state="glob-*.png"
|
state="glob-*.png"
|
||||||
;&
|
;&
|
||||||
|
|||||||
@@ -7,20 +7,22 @@ _rgblink_completions() {
|
|||||||
# Empty long opt = it doesn't exit
|
# Empty long opt = it doesn't exit
|
||||||
# See the `state` variable below for info about `state_after`
|
# See the `state` variable below for info about `state_after`
|
||||||
declare -A opts=(
|
declare -A opts=(
|
||||||
[V]="version:normal"
|
|
||||||
[h]="help:normal"
|
[h]="help:normal"
|
||||||
[d]="dmg:normal"
|
[V]="version:normal"
|
||||||
[t]="tiny:normal"
|
[W]="warning:warning"
|
||||||
[v]="verbose:normal"
|
|
||||||
[w]="wramx:normal"
|
|
||||||
[x]="nopad:normal"
|
|
||||||
[l]="linkerscript:glob-*"
|
|
||||||
[M]="no-sym-in-map:normal"
|
[M]="no-sym-in-map:normal"
|
||||||
|
[d]="dmg:normal"
|
||||||
|
[B]="backtrace:unk"
|
||||||
|
[l]="linkerscript:glob-*"
|
||||||
[m]="map:glob-*.map"
|
[m]="map:glob-*.map"
|
||||||
[n]="sym:glob-*.sym"
|
[n]="sym:glob-*.sym"
|
||||||
[O]="overlay:glob-*.gb *.gbc *.sgb"
|
[O]="overlay:glob-*.gb *.gbc *.sgb"
|
||||||
[o]="output:glob-*.gb *.gbc *.sgb"
|
[o]="output:glob-*.gb *.gbc *.sgb"
|
||||||
[p]="pad:unk"
|
[p]="pad:unk"
|
||||||
|
[t]="tiny:normal"
|
||||||
|
[v]="verbose:normal"
|
||||||
|
[w]="wramx:normal"
|
||||||
|
[x]="nopad:normal"
|
||||||
)
|
)
|
||||||
# Parse command-line up to current word
|
# Parse command-line up to current word
|
||||||
local opt_ena=true
|
local opt_ena=true
|
||||||
@@ -136,6 +138,18 @@ _rgblink_completions() {
|
|||||||
case "$state" in
|
case "$state" in
|
||||||
unk) # Return with no replies: no idea what to complete!
|
unk) # Return with no replies: no idea what to complete!
|
||||||
;;
|
;;
|
||||||
|
warning)
|
||||||
|
mapfile -t COMPREPLY < <(compgen -W "
|
||||||
|
assert
|
||||||
|
div
|
||||||
|
obsolete
|
||||||
|
shift
|
||||||
|
shift-amount
|
||||||
|
truncation
|
||||||
|
all
|
||||||
|
everything
|
||||||
|
error" -P "${cur_word:0:$optlen}" -- "${cur_word:$optlen}")
|
||||||
|
;;
|
||||||
normal) # Acts like a glob...
|
normal) # Acts like a glob...
|
||||||
state="glob-*.o *.obj"
|
state="glob-*.o *.obj"
|
||||||
;&
|
;&
|
||||||
|
|||||||
@@ -26,11 +26,23 @@ dependency include/linkdefs.hpp man/rgbds.5 \
|
|||||||
dependency src/asm/parser.y man/rgbasm.5 \
|
dependency src/asm/parser.y man/rgbasm.5 \
|
||||||
"Was the rgbasm grammar changed?"
|
"Was the rgbasm grammar changed?"
|
||||||
|
|
||||||
|
dependency src/asm/actions.cpp man/rgbasm.5 \
|
||||||
|
"Was the rgbasm grammar changed?"
|
||||||
|
|
||||||
dependency src/link/script.y man/rgblink.5 \
|
dependency src/link/script.y man/rgblink.5 \
|
||||||
"Was the linker script grammar changed?"
|
"Was the linker script grammar changed?"
|
||||||
|
|
||||||
|
dependency src/link/layout.cpp man/rgblink.5 \
|
||||||
|
"Was the linker script grammar changed?"
|
||||||
|
|
||||||
dependency include/asm/warning.hpp man/rgbasm.1 \
|
dependency include/asm/warning.hpp man/rgbasm.1 \
|
||||||
"Were the rgbasm warnings changed?"
|
"Were the rgbasm warnings changed?"
|
||||||
|
dependency include/link/warning.hpp man/rgblink.1 \
|
||||||
|
"Were the rgblink warnings changed?"
|
||||||
|
dependency include/fix/warning.hpp man/rgbfix.1 \
|
||||||
|
"Were the rgbfix warnings changed?"
|
||||||
|
dependency include/gfx/warning.hpp man/rgbgfx.1 \
|
||||||
|
"Were the rgbgfx warnings changed?"
|
||||||
|
|
||||||
dependency src/asm/object.cpp include/linkdefs.hpp \
|
dependency src/asm/object.cpp include/linkdefs.hpp \
|
||||||
"Should the object file revision be bumped?"
|
"Should the object file revision be bumped?"
|
||||||
|
|||||||
@@ -11,5 +11,5 @@ if ! git diff-index --quiet HEAD --; then
|
|||||||
git diff-index --name-only HEAD --
|
git diff-index --name-only HEAD --
|
||||||
echo
|
echo
|
||||||
git diff HEAD --
|
git diff HEAD --
|
||||||
return 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -7,20 +7,29 @@ make coverage -j
|
|||||||
# Run the tests
|
# Run the tests
|
||||||
pushd test
|
pushd test
|
||||||
./fetch-test-deps.sh
|
./fetch-test-deps.sh
|
||||||
./run-tests.sh
|
if [[ $# -eq 0 ]]; then
|
||||||
|
./run-tests.sh
|
||||||
|
else
|
||||||
|
./run-tests.sh --os "$1"
|
||||||
|
fi
|
||||||
popd
|
popd
|
||||||
|
|
||||||
# Generate coverage logs
|
# Generate coverage logs
|
||||||
gcov src/**/*.cpp
|
gcov src/**/*.cpp
|
||||||
mkdir -p coverage
|
mkdir -p coverage
|
||||||
|
|
||||||
# Generate coverage report
|
# Generate coverage report, excluding Bison-generated files
|
||||||
lcov -c --no-external -d . -o coverage/coverage.info
|
COVERAGE_INFO=coverage/coverage.info
|
||||||
genhtml -f -s -o coverage/ coverage/coverage.info
|
lcov -c --no-external -d . -o "$COVERAGE_INFO"
|
||||||
|
lcov -r "$COVERAGE_INFO" src/asm/parser.{hpp,cpp} src/link/script.{hpp,cpp} -o "$COVERAGE_INFO"
|
||||||
|
genhtml --dark-mode -f -s -o coverage/ "$COVERAGE_INFO"
|
||||||
|
|
||||||
# Open report in web browser
|
# Check whether running from coverage.yml workflow
|
||||||
if [ "$(uname)" == "Darwin" ]; then
|
if [ "$1" != "ubuntu-ci" ]; then
|
||||||
|
# Open report in web browser
|
||||||
|
if [ "$(uname)" == "Darwin" ]; then
|
||||||
open coverage/index.html
|
open coverage/index.html
|
||||||
else
|
else
|
||||||
xdg-open coverage/index.html
|
xdg-open coverage/index.html
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ _rgbasm_warnings() {
|
|||||||
'empty-data-directive:Warn on arg-less d[bwl] in ROM'
|
'empty-data-directive:Warn on arg-less d[bwl] in ROM'
|
||||||
'empty-macro-arg:Warn on empty macro arg'
|
'empty-macro-arg:Warn on empty macro arg'
|
||||||
'empty-strrpl:Warn on calling STRRPL with empty pattern'
|
'empty-strrpl:Warn on calling STRRPL with empty pattern'
|
||||||
|
'export-undefined:Warn on EXPORT of an undefined symbol'
|
||||||
'large-constant:Warn on constants too large for a signed 32-bit int'
|
'large-constant:Warn on constants too large for a signed 32-bit int'
|
||||||
'macro-shift:Warn when shifting macro args part their limits'
|
'macro-shift:Warn when shifting macro args part their limits'
|
||||||
'nested-comment:Warn on "/*" inside block comments'
|
'nested-comment:Warn on "/*" inside block comments'
|
||||||
@@ -23,15 +24,13 @@ _rgbasm_warnings() {
|
|||||||
'obsolete:Warn when using deprecated features'
|
'obsolete:Warn when using deprecated features'
|
||||||
'purge:Warn when purging exported symbols or labels'
|
'purge:Warn when purging exported symbols or labels'
|
||||||
'shift:Warn when shifting negative values'
|
'shift:Warn when shifting negative values'
|
||||||
'shift-amount:Warn when a shift'\''s operand it negative or \> 32'
|
'shift-amount:Warn when a shift'\''s operand is negative or \> 32'
|
||||||
'truncation:Warn when implicit truncation loses bits'
|
'truncation:Warn when implicit truncation loses bits'
|
||||||
'unmapped-char:Warn on unmapped character'
|
'unmapped-char:Warn on unmapped character'
|
||||||
'unmatched-directive:Warn on unmatched directive pair'
|
'unmatched-directive:Warn on unmatched directive pair'
|
||||||
'unterminated-load:Warn on LOAD without ENDL'
|
'unterminated-load:Warn on LOAD without ENDL'
|
||||||
'user:Warn when executing the WARN built-in'
|
'user:Warn when executing the WARN built-in'
|
||||||
)
|
)
|
||||||
# TODO: handle `no-` and `error=` somehow?
|
|
||||||
# TODO: handle `=0|1|2` levels for `numeric-string`, `purge`, `truncation`, and `unmapped-char`?
|
|
||||||
_describe warning warnings
|
_describe warning warnings
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,16 +40,19 @@ local args=(
|
|||||||
'(- : * options)'{-h,--help}'[Print help text and exit]'
|
'(- : * options)'{-h,--help}'[Print help text and exit]'
|
||||||
|
|
||||||
'(-E --export-all)'{-E,--export-all}'[Export all symbols]'
|
'(-E --export-all)'{-E,--export-all}'[Export all symbols]'
|
||||||
'(-v --verbose)'{-v,--verbose}'[Print additional messages regarding progression]'
|
'(-v --verbose)'{-v,--verbose}'[Enable verbose output]'
|
||||||
-w'[Disable all warnings]'
|
-w'[Disable all warnings]'
|
||||||
|
|
||||||
|
'(-B --backtrace)'{-B,--backtrace}'+[Set backtrace depth or style]:param:'
|
||||||
'(-b --binary-digits)'{-b,--binary-digits}'+[Change chars for binary constants]:digit spec:'
|
'(-b --binary-digits)'{-b,--binary-digits}'+[Change chars for binary constants]:digit spec:'
|
||||||
|
--color'[Whether to use color in output]:color:(auto always never)'
|
||||||
'*'{-D,--define}'+[Define a string symbol]:name + value (default 1):'
|
'*'{-D,--define}'+[Define a string symbol]:name + value (default 1):'
|
||||||
'(-g --gfx-chars)'{-g,--gfx-chars}'+[Change chars for gfx constants]:chars spec:'
|
'(-g --gfx-chars)'{-g,--gfx-chars}'+[Change chars for gfx constants]:chars spec:'
|
||||||
'(-I --include)'{-I,--include}'+[Add an include directory]:include path:_files -/'
|
'(-I --include)'{-I,--include}'+[Add an include directory]:include path:_files -/'
|
||||||
'(-M --dependfile)'{-M,--dependfile}"+[Write deps in make format]:output file:_files -g '*.{d,mk}'"
|
'(-M --dependfile)'{-M,--dependfile}"+[Write dependencies in Makefile format]:output file:_files -g '*.{d,mk}'"
|
||||||
-MG'[Assume missing files should be generated]'
|
-MC'[Continue after missing dependencies]'
|
||||||
-MP'[Add phony targets to all deps]'
|
-MG'[Assume missing dependencies should be generated]'
|
||||||
|
-MP'[Add phony targets to all dependencies]'
|
||||||
'*'-MT"+[Add a target to the rules]:target:_files -g '*.{d,mk,o}'"
|
'*'-MT"+[Add a target to the rules]:target:_files -g '*.{d,mk,o}'"
|
||||||
'*'-MQ"+[Add a target to the rules]:target:_files -g '*.{d,mk,o}'"
|
'*'-MQ"+[Add a target to the rules]:target:_files -g '*.{d,mk,o}'"
|
||||||
'(-o --output)'{-o,--output}'+[Output file]:output file:_files'
|
'(-o --output)'{-o,--output}'+[Output file]:output file:_files'
|
||||||
|
|||||||
@@ -34,6 +34,22 @@ _mbc_names() {
|
|||||||
_describe "MBC name" mbc_names
|
_describe "MBC name" mbc_names
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_rgbfix_warnings() {
|
||||||
|
local warnings=(
|
||||||
|
'error:Turn all warnings into errors'
|
||||||
|
|
||||||
|
'all:Enable most warning messages'
|
||||||
|
'everything:Enable literally everything'
|
||||||
|
|
||||||
|
'mbc:Warn about issues with MBC specs'
|
||||||
|
'obsolete:Warn when using deprecated features'
|
||||||
|
'overwrite:Warn when overwriting non-zero bytes'
|
||||||
|
'sgb:Warn when SGB flag conflicts with old licensee code'
|
||||||
|
'truncation:Warn when values are truncated to fit'
|
||||||
|
)
|
||||||
|
_describe warning warnings
|
||||||
|
}
|
||||||
|
|
||||||
local args=(
|
local args=(
|
||||||
# Arguments are listed here in the same order as in the manual, except for the version and help
|
# Arguments are listed here in the same order as in the manual, except for the version and help
|
||||||
'(- : * options)'{-V,--version}'[Print version number and exit]'
|
'(- : * options)'{-V,--version}'[Print version number and exit]'
|
||||||
@@ -45,7 +61,9 @@ local args=(
|
|||||||
'(-O --overwrite)'{-O,--overwrite}'[Allow overwriting non-zero bytes]'
|
'(-O --overwrite)'{-O,--overwrite}'[Allow overwriting non-zero bytes]'
|
||||||
'(-s --sgb-compatible)'{-s,--sgb-compatible}'[Set the SGB flag]'
|
'(-s --sgb-compatible)'{-s,--sgb-compatible}'[Set the SGB flag]'
|
||||||
'(-f --fix-spec -v --validate)'{-v,--validate}'[Shorthand for -f lhg]'
|
'(-f --fix-spec -v --validate)'{-v,--validate}'[Shorthand for -f lhg]'
|
||||||
|
-w'[Disable all warnings]'
|
||||||
|
|
||||||
|
--color'[Whether to use color in output]:color:(auto always never)'
|
||||||
'(-f --fix-spec -v --validate)'{-f,--fix-spec}'+[Fix or trash some header values]:fix spec:'
|
'(-f --fix-spec -v --validate)'{-f,--fix-spec}'+[Fix or trash some header values]:fix spec:'
|
||||||
'(-i --game-id)'{-i,--game-id}'+[Set game ID string]:4-char game ID:'
|
'(-i --game-id)'{-i,--game-id}'+[Set game ID string]:4-char game ID:'
|
||||||
'(-k --new-licensee)'{-k,--new-licensee}'+[Set new licensee string]:2-char licensee ID:'
|
'(-k --new-licensee)'{-k,--new-licensee}'+[Set new licensee string]:2-char licensee ID:'
|
||||||
@@ -57,6 +75,7 @@ local args=(
|
|||||||
'(-p --pad-value)'{-p,--pad-value}'+[Pad to next valid size using this byte as padding]:padding byte:'
|
'(-p --pad-value)'{-p,--pad-value}'+[Pad to next valid size using this byte as padding]:padding byte:'
|
||||||
'(-r --ram-size)'{-r,--ram-size}'+[Set RAM size]:ram size byte:'
|
'(-r --ram-size)'{-r,--ram-size}'+[Set RAM size]:ram size byte:'
|
||||||
'(-t --title)'{-t,--title}'+[Set title string]:11-char title string:'
|
'(-t --title)'{-t,--title}'+[Set title string]:11-char title string:'
|
||||||
|
'(-W --warning)'{-W,--warning}'+[Toggle warning flags]:warning flag:_rgbfix_warnings'
|
||||||
|
|
||||||
'*'":ROM files:_files -g '*.{gb,sgb,gbc}'"
|
'*'":ROM files:_files -g '*.{gb,sgb,gbc}'"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,6 +9,20 @@ _depths() {
|
|||||||
_describe 'bit depth' depths
|
_describe 'bit depth' depths
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_rgbgfx_warnings() {
|
||||||
|
local warnings=(
|
||||||
|
'error:Turn all warnings into errors'
|
||||||
|
|
||||||
|
'all:Enable most warning messages'
|
||||||
|
'everything:Enable literally everything'
|
||||||
|
|
||||||
|
'embedded:Warn when using embedded PLTE without "-c embedded"'
|
||||||
|
'obsolete:Warn when using deprecated features'
|
||||||
|
'trim-nonempty:Warn when "-x" trims nonempty tiles'
|
||||||
|
)
|
||||||
|
_describe warning warnings
|
||||||
|
}
|
||||||
|
|
||||||
local args=(
|
local args=(
|
||||||
# Arguments are listed here in the same order as in the manual, except for the version and help
|
# Arguments are listed here in the same order as in the manual, except for the version and help
|
||||||
'(- : * options)'{-V,--version}'[Print version number and exit]'
|
'(- : * options)'{-V,--version}'[Print version number and exit]'
|
||||||
@@ -22,7 +36,8 @@ local args=(
|
|||||||
'(-q --palette-map -Q --auto-palette-map)'{-Q,--auto-palette-map}'[Shortcut for -p <file>.palmap]'
|
'(-q --palette-map -Q --auto-palette-map)'{-Q,--auto-palette-map}'[Shortcut for -p <file>.palmap]'
|
||||||
'(-t --tilemap -T --auto-tilemap)'{-T,--auto-tilemap}'[Shortcut for -t <file>.tilemap]'
|
'(-t --tilemap -T --auto-tilemap)'{-T,--auto-tilemap}'[Shortcut for -t <file>.tilemap]'
|
||||||
'(-u --unique-tiles)'{-u,--unique-tiles}'[Eliminate redundant tiles]'
|
'(-u --unique-tiles)'{-u,--unique-tiles}'[Eliminate redundant tiles]'
|
||||||
{-v,--verbose}'[Enable verbose output]'
|
'(-v --verbose)'{-v,--verbose}'[Enable verbose output]'
|
||||||
|
-w'[Disable all warnings]'
|
||||||
'(-X --mirror-x)'{-X,--mirror-x}'[Eliminate horizontally mirrored tiles from output]'
|
'(-X --mirror-x)'{-X,--mirror-x}'[Eliminate horizontally mirrored tiles from output]'
|
||||||
'(-Y --mirror-y)'{-Y,--mirror-y}'[Eliminate vertically mirrored tiles from output]'
|
'(-Y --mirror-y)'{-Y,--mirror-y}'[Eliminate vertically mirrored tiles from output]'
|
||||||
'(-Z --columns)'{-Z,--columns}'[Read the image in column-major order]'
|
'(-Z --columns)'{-Z,--columns}'[Read the image in column-major order]'
|
||||||
@@ -30,6 +45,7 @@ local args=(
|
|||||||
'(-a --attr-map -A --auto-attr-map)'{-a,--attr-map}'+[Generate a map of tile attributes (mirroring)]:attrmap file:_files'
|
'(-a --attr-map -A --auto-attr-map)'{-a,--attr-map}'+[Generate a map of tile attributes (mirroring)]:attrmap file:_files'
|
||||||
'(-B --background-color)'{-B,--background-color}'+[Ignore tiles containing only specified color]:color:'
|
'(-B --background-color)'{-B,--background-color}'+[Ignore tiles containing only specified color]:color:'
|
||||||
'(-b --base-tiles)'{-b,--base-tiles}'+[Base tile IDs for tile map output]:base tile IDs:'
|
'(-b --base-tiles)'{-b,--base-tiles}'+[Base tile IDs for tile map output]:base tile IDs:'
|
||||||
|
--color'[Whether to use color in output]:color:(auto always never)'
|
||||||
'(-c --colors)'{-c,--colors}'+[Specify color palettes]:palette spec:'
|
'(-c --colors)'{-c,--colors}'+[Specify color palettes]:palette spec:'
|
||||||
'(-d --depth)'{-d,--depth}'+[Set bit depth]:bit depth:_depths'
|
'(-d --depth)'{-d,--depth}'+[Set bit depth]:bit depth:_depths'
|
||||||
'(-i --input-tileset)'{-i,--input-tileset}'+[Use specific tiles]:tileset file:_files -g "*.2bpp"'
|
'(-i --input-tileset)'{-i,--input-tileset}'+[Use specific tiles]:tileset file:_files -g "*.2bpp"'
|
||||||
@@ -42,6 +58,7 @@ local args=(
|
|||||||
'(-r --reverse)'{-r,--reverse}'+[Yield an image from binary data]:image width (in tiles):'
|
'(-r --reverse)'{-r,--reverse}'+[Yield an image from binary data]:image width (in tiles):'
|
||||||
'(-s --palette-size)'{-s,--palette-size}'+[Limit palette size]:palette size:'
|
'(-s --palette-size)'{-s,--palette-size}'+[Limit palette size]:palette size:'
|
||||||
'(-t --tilemap -T --auto-tilemap)'{-t,--tilemap}'+[Generate a map of tile indices]:tilemap file:_files'
|
'(-t --tilemap -T --auto-tilemap)'{-t,--tilemap}'+[Generate a map of tile indices]:tilemap file:_files'
|
||||||
|
'(-W --warning)'{-W,--warning}'+[Toggle warning flags]:warning flag:_rgbgfx_warnings'
|
||||||
'(-x --trim-end)'{-x,--trim-end}'+[Trim end of output by this many tiles]:tile count:'
|
'(-x --trim-end)'{-x,--trim-end}'+[Trim end of output by this many tiles]:tile count:'
|
||||||
|
|
||||||
":input png file:_files -g '*.png'"
|
":input png file:_files -g '*.png'"
|
||||||
|
|||||||
@@ -1,5 +1,22 @@
|
|||||||
#compdef rgblink
|
#compdef rgblink
|
||||||
|
|
||||||
|
_rgblink_warnings() {
|
||||||
|
local warnings=(
|
||||||
|
'error:Turn all warnings into errors'
|
||||||
|
|
||||||
|
'all:Enable most warning messages'
|
||||||
|
'everything:Enable literally everything'
|
||||||
|
|
||||||
|
'assert:Warn when WARN-type asserts fail'
|
||||||
|
'div:Warn when dividing the smallest int by -1'
|
||||||
|
'obsolete:Warn when using deprecated features'
|
||||||
|
'shift:Warn when shifting negative values'
|
||||||
|
'shift-amount:Warn when a shift'\''s operand is negative or \> 32'
|
||||||
|
'truncation:Warn when implicit truncation loses bits'
|
||||||
|
)
|
||||||
|
_describe warning warnings
|
||||||
|
}
|
||||||
|
|
||||||
local args=(
|
local args=(
|
||||||
# Arguments are listed here in the same order as in the manual, except for the version and help
|
# Arguments are listed here in the same order as in the manual, except for the version and help
|
||||||
'(- : * options)'{-V,--version}'[Print version number and exit]'
|
'(- : * options)'{-V,--version}'[Print version number and exit]'
|
||||||
@@ -11,6 +28,8 @@ local args=(
|
|||||||
'(-w --wramx)'{-w,--wramx}'[Disable WRAM banking]'
|
'(-w --wramx)'{-w,--wramx}'[Disable WRAM banking]'
|
||||||
'(-x --nopad)'{-x,--nopad}'[Disable padding the end of the final file]'
|
'(-x --nopad)'{-x,--nopad}'[Disable padding the end of the final file]'
|
||||||
|
|
||||||
|
'(-B --backtrace)'{-B,--backtrace}'+[Set backtrace depth or style]:param:'
|
||||||
|
--color'[Whether to use color in output]:color:(auto always never)'
|
||||||
'(-l --linkerscript)'{-l,--linkerscript}"+[Use a linker script]:linker script:_files -g '*.link'"
|
'(-l --linkerscript)'{-l,--linkerscript}"+[Use a linker script]:linker script:_files -g '*.link'"
|
||||||
'(-M --no-sym-in-map)'{-M,--no-sym-in-map}'[Do not output symbol names in map file]'
|
'(-M --no-sym-in-map)'{-M,--no-sym-in-map}'[Do not output symbol names in map file]'
|
||||||
'(-m --map)'{-m,--map}"+[Produce a map file]:map file:_files -g '*.map'"
|
'(-m --map)'{-m,--map}"+[Produce a map file]:map file:_files -g '*.map'"
|
||||||
@@ -19,6 +38,7 @@ local args=(
|
|||||||
'(-o --output)'{-o,--output}"+[Write ROM image to this file]:rom file:_files -g '*.{gb,sgb,gbc}'"
|
'(-o --output)'{-o,--output}"+[Write ROM image to this file]:rom file:_files -g '*.{gb,sgb,gbc}'"
|
||||||
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
|
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
|
||||||
'(-S --scramble)'{-s,--scramble}'+[Activate scrambling]:scramble spec'
|
'(-S --scramble)'{-s,--scramble}'+[Activate scrambling]:scramble spec'
|
||||||
|
'(-W --warning)'{-W,--warning}'+[Toggle warning flags]:warning flag:_rgblink_warnings'
|
||||||
|
|
||||||
'*'":object files:_files -g '*.o'"
|
'*'":object files:_files -g '*.o'"
|
||||||
)
|
)
|
||||||
|
|||||||
61
include/asm/actions.hpp
Normal file
61
include/asm/actions.hpp
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#ifndef RGBDS_ASM_ACTIONS_HPP
|
||||||
|
#define RGBDS_ASM_ACTIONS_HPP
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <variant>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "linkdefs.hpp" // AssertionType, RPNCommand
|
||||||
|
|
||||||
|
#include "asm/rpn.hpp" // Expression
|
||||||
|
|
||||||
|
struct AlignmentSpec {
|
||||||
|
uint8_t alignment;
|
||||||
|
uint16_t alignOfs;
|
||||||
|
};
|
||||||
|
|
||||||
|
void act_If(int32_t condition);
|
||||||
|
void act_Elif(int32_t condition);
|
||||||
|
void act_Else();
|
||||||
|
void act_Endc();
|
||||||
|
|
||||||
|
AlignmentSpec act_Alignment(int32_t alignment, int32_t alignOfs);
|
||||||
|
|
||||||
|
void act_Assert(AssertionType type, Expression const &expr, std::string const &message);
|
||||||
|
void act_StaticAssert(AssertionType type, int32_t condition, std::string const &message);
|
||||||
|
|
||||||
|
std::optional<std::string> act_ReadFile(std::string const &name, uint32_t maxLen);
|
||||||
|
|
||||||
|
uint32_t act_CharToNum(std::string const &str);
|
||||||
|
uint32_t act_StringToNum(std::string const &str);
|
||||||
|
|
||||||
|
int32_t act_CharVal(std::string const &str);
|
||||||
|
int32_t act_CharVal(std::string const &str, int32_t negIdx);
|
||||||
|
uint8_t act_StringByte(std::string const &str, int32_t negIdx);
|
||||||
|
|
||||||
|
size_t act_StringLen(std::string const &str, bool printErrors);
|
||||||
|
std::string
|
||||||
|
act_StringSlice(std::string const &str, int32_t negStart, std::optional<int32_t> negStop);
|
||||||
|
std::string act_StringSub(std::string const &str, int32_t negPos, std::optional<uint32_t> optLen);
|
||||||
|
|
||||||
|
size_t act_CharLen(std::string const &str);
|
||||||
|
std::string act_StringChar(std::string const &str, int32_t negIdx);
|
||||||
|
std::string act_CharSub(std::string const &str, int32_t negPos);
|
||||||
|
int32_t act_CharCmp(std::string_view str1, std::string_view str2);
|
||||||
|
|
||||||
|
std::string act_StringReplace(std::string_view str, std::string const &old, std::string const &rep);
|
||||||
|
std::string act_StringFormat(
|
||||||
|
std::string const &spec, std::vector<std::variant<uint32_t, std::string>> const &args
|
||||||
|
);
|
||||||
|
|
||||||
|
std::string act_SectionName(std::string const &symName);
|
||||||
|
|
||||||
|
void act_CompoundAssignment(std::string const &symName, RPNCommand op, int32_t constValue);
|
||||||
|
|
||||||
|
#endif // RGBDS_ASM_ACTIONS_HPP
|
||||||
@@ -3,6 +3,8 @@
|
|||||||
#ifndef RGBDS_ASM_CHARMAP_HPP
|
#ifndef RGBDS_ASM_CHARMAP_HPP
|
||||||
#define RGBDS_ASM_CHARMAP_HPP
|
#define RGBDS_ASM_CHARMAP_HPP
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
@@ -22,6 +24,7 @@ void charmap_CheckStack();
|
|||||||
void charmap_Add(std::string const &mapping, std::vector<int32_t> &&value);
|
void charmap_Add(std::string const &mapping, std::vector<int32_t> &&value);
|
||||||
bool charmap_HasChar(std::string const &mapping);
|
bool charmap_HasChar(std::string const &mapping);
|
||||||
size_t charmap_CharSize(std::string const &mapping);
|
size_t charmap_CharSize(std::string const &mapping);
|
||||||
|
std::optional<int32_t> charmap_CharValue(std::string const &mapping, size_t idx);
|
||||||
std::vector<int32_t> charmap_Convert(std::string const &input);
|
std::vector<int32_t> charmap_Convert(std::string const &input);
|
||||||
size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output);
|
size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output);
|
||||||
std::string charmap_Reverse(std::vector<int32_t> const &value, bool &unique);
|
std::string charmap_Reverse(std::vector<int32_t> const &value, bool &unique);
|
||||||
|
|||||||
@@ -5,9 +5,6 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
extern uint8_t fixPrecision;
|
|
||||||
|
|
||||||
uint8_t fix_Precision();
|
|
||||||
int32_t fix_Sin(int32_t i, int32_t q);
|
int32_t fix_Sin(int32_t i, int32_t q);
|
||||||
int32_t fix_Cos(int32_t i, int32_t q);
|
int32_t fix_Cos(int32_t i, int32_t q);
|
||||||
int32_t fix_Tan(int32_t i, int32_t q);
|
int32_t fix_Tan(int32_t i, int32_t q);
|
||||||
|
|||||||
@@ -7,19 +7,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
enum FormatState {
|
|
||||||
FORMAT_SIGN, // expects '+' or ' ' (optional)
|
|
||||||
FORMAT_EXACT, // expects '#' (optional)
|
|
||||||
FORMAT_ALIGN, // expects '-' (optional)
|
|
||||||
FORMAT_WIDTH, // expects '0'-'9', max 255 (optional) (leading '0' indicates pad)
|
|
||||||
FORMAT_FRAC, // got '.', expects '0'-'9', max 255 (optional)
|
|
||||||
FORMAT_PREC, // got 'q', expects '0'-'9', range 1-31 (optional)
|
|
||||||
FORMAT_DONE, // got [duXxbofs] (required)
|
|
||||||
FORMAT_INVALID, // got unexpected character
|
|
||||||
};
|
|
||||||
|
|
||||||
class FormatSpec {
|
class FormatSpec {
|
||||||
FormatState state;
|
|
||||||
int sign;
|
int sign;
|
||||||
bool exact;
|
bool exact;
|
||||||
bool alignLeft;
|
bool alignLeft;
|
||||||
@@ -30,15 +18,13 @@ class FormatSpec {
|
|||||||
bool hasPrec;
|
bool hasPrec;
|
||||||
size_t precision;
|
size_t precision;
|
||||||
int type;
|
int type;
|
||||||
bool valid;
|
bool parsed;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool isEmpty() const { return !state; }
|
bool isValid() const { return !!type; }
|
||||||
bool isValid() const { return valid || state == FORMAT_DONE; }
|
bool isParsed() const { return parsed; }
|
||||||
bool isFinished() const { return state >= FORMAT_DONE; }
|
|
||||||
|
|
||||||
void useCharacter(int c);
|
size_t parseSpec(char const *spec);
|
||||||
void finishCharacters();
|
|
||||||
|
|
||||||
void appendString(std::string &str, std::string const &value) const;
|
void appendString(std::string &str, std::string const &value) const;
|
||||||
void appendNumber(std::string &str, uint32_t value) const;
|
void appendNumber(std::string &str, uint32_t value) const;
|
||||||
|
|||||||
@@ -10,20 +10,21 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "either.hpp"
|
|
||||||
#include "linkdefs.hpp"
|
#include "linkdefs.hpp"
|
||||||
|
|
||||||
#include "asm/lexer.hpp"
|
#include "asm/lexer.hpp"
|
||||||
|
|
||||||
struct FileStackNode {
|
struct FileStackNode {
|
||||||
FileStackNodeType type;
|
FileStackNodeType type;
|
||||||
Either<
|
std::variant<
|
||||||
std::vector<uint32_t>, // NODE_REPT
|
std::vector<uint32_t>, // NODE_REPT
|
||||||
std::string // NODE_FILE, NODE_MACRO
|
std::string // NODE_FILE, NODE_MACRO
|
||||||
>
|
>
|
||||||
data;
|
data;
|
||||||
|
bool isQuiet; // Whether to omit this node from error reporting
|
||||||
|
|
||||||
std::shared_ptr<FileStackNode> parent; // Pointer to parent node, for error reporting
|
std::shared_ptr<FileStackNode> parent; // Pointer to parent node, for error reporting
|
||||||
// Line at which the parent context was exited
|
// Line at which the parent context was exited
|
||||||
@@ -34,47 +35,55 @@ struct FileStackNode {
|
|||||||
uint32_t ID = UINT32_MAX;
|
uint32_t ID = UINT32_MAX;
|
||||||
|
|
||||||
// REPT iteration counts since last named node, in reverse depth order
|
// REPT iteration counts since last named node, in reverse depth order
|
||||||
std::vector<uint32_t> &iters() { return data.get<std::vector<uint32_t>>(); }
|
std::vector<uint32_t> &iters() { return std::get<std::vector<uint32_t>>(data); }
|
||||||
std::vector<uint32_t> const &iters() const { return data.get<std::vector<uint32_t>>(); }
|
std::vector<uint32_t> const &iters() const { return std::get<std::vector<uint32_t>>(data); }
|
||||||
// File name for files, file::macro name for macros
|
// File name for files, file::macro name for macros
|
||||||
std::string &name() { return data.get<std::string>(); }
|
std::string &name() { return std::get<std::string>(data); }
|
||||||
std::string const &name() const { return data.get<std::string>(); }
|
std::string const &name() const { return std::get<std::string>(data); }
|
||||||
|
|
||||||
FileStackNode(FileStackNodeType type_, Either<std::vector<uint32_t>, std::string> data_)
|
FileStackNode(
|
||||||
: type(type_), data(data_) {}
|
FileStackNodeType type_,
|
||||||
|
std::variant<std::vector<uint32_t>, std::string> data_,
|
||||||
|
bool isQuiet_
|
||||||
|
)
|
||||||
|
: type(type_), data(data_), isQuiet(isQuiet_) {}
|
||||||
|
|
||||||
std::string const &dump(uint32_t curLineNo) const;
|
void printBacktrace(uint32_t curLineNo) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern size_t maxRecursionDepth;
|
|
||||||
|
|
||||||
struct MacroArgs;
|
struct MacroArgs;
|
||||||
|
|
||||||
void fstk_DumpCurrent();
|
void fstk_VerboseOutputConfig();
|
||||||
|
|
||||||
|
void fstk_TraceCurrent();
|
||||||
std::shared_ptr<FileStackNode> fstk_GetFileStack();
|
std::shared_ptr<FileStackNode> fstk_GetFileStack();
|
||||||
std::shared_ptr<std::string> fstk_GetUniqueIDStr();
|
std::shared_ptr<std::string> fstk_GetUniqueIDStr();
|
||||||
MacroArgs *fstk_GetCurrentMacroArgs();
|
MacroArgs *fstk_GetCurrentMacroArgs();
|
||||||
|
|
||||||
void fstk_AddIncludePath(std::string const &path);
|
void fstk_AddIncludePath(std::string const &path);
|
||||||
void fstk_SetPreIncludeFile(std::string const &path);
|
void fstk_AddPreIncludeFile(std::string const &path);
|
||||||
std::optional<std::string> fstk_FindFile(std::string const &path);
|
std::optional<std::string> fstk_FindFile(std::string const &path);
|
||||||
|
bool fstk_FileError(std::string const &path, char const *functionName);
|
||||||
|
bool fstk_FailedOnMissingInclude();
|
||||||
|
|
||||||
bool yywrap();
|
bool yywrap();
|
||||||
void fstk_RunInclude(std::string const &path, bool updateStateNow);
|
bool fstk_RunInclude(std::string const &path, bool isQuiet);
|
||||||
void fstk_RunMacro(std::string const ¯oName, std::shared_ptr<MacroArgs> macroArgs);
|
void fstk_RunMacro(
|
||||||
void fstk_RunRept(uint32_t count, int32_t reptLineNo, ContentSpan const &span);
|
std::string const ¯oName, std::shared_ptr<MacroArgs> macroArgs, bool isQuiet
|
||||||
|
);
|
||||||
|
void fstk_RunRept(uint32_t count, int32_t reptLineNo, ContentSpan const &span, bool isQuiet);
|
||||||
void fstk_RunFor(
|
void fstk_RunFor(
|
||||||
std::string const &symName,
|
std::string const &symName,
|
||||||
int32_t start,
|
int32_t start,
|
||||||
int32_t stop,
|
int32_t stop,
|
||||||
int32_t step,
|
int32_t step,
|
||||||
int32_t reptLineNo,
|
int32_t reptLineNo,
|
||||||
ContentSpan const &span
|
ContentSpan const &span,
|
||||||
|
bool isQuiet
|
||||||
);
|
);
|
||||||
void fstk_StopRept();
|
|
||||||
bool fstk_Break();
|
bool fstk_Break();
|
||||||
|
|
||||||
void fstk_NewRecursionDepth(size_t newDepth);
|
void fstk_NewRecursionDepth(size_t newDepth);
|
||||||
void fstk_Init(std::string const &mainPath, size_t maxDepth);
|
void fstk_Init(std::string const &mainPath);
|
||||||
|
|
||||||
#endif // RGBDS_ASM_FSTACK_HPP
|
#endif // RGBDS_ASM_FSTACK_HPP
|
||||||
|
|||||||
@@ -6,15 +6,16 @@
|
|||||||
#include <deque>
|
#include <deque>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "either.hpp"
|
|
||||||
#include "platform.hpp" // SSIZE_MAX
|
#include "platform.hpp" // SSIZE_MAX
|
||||||
|
|
||||||
// This value is a compromise between `LexerState` allocation performance when `mmap` works, and
|
// This value is a compromise between `LexerState` allocation performance when reading the entire
|
||||||
// buffering performance when it doesn't/can't (e.g. when piping a file into RGBASM).
|
// file works, and buffering performance when it doesn't (e.g. when piping a file into RGBASM).
|
||||||
static constexpr size_t LEXER_BUF_SIZE = 64;
|
static constexpr size_t LEXER_BUF_SIZE = 64;
|
||||||
// The buffer needs to be large enough for the maximum `lexerState->peek()` lookahead distance
|
// The buffer needs to be large enough for the maximum `lexerState->peek()` lookahead distance
|
||||||
static_assert(LEXER_BUF_SIZE > 1, "Lexer buffer size is too small");
|
static_assert(LEXER_BUF_SIZE > 1, "Lexer buffer size is too small");
|
||||||
@@ -84,6 +85,7 @@ struct LexerState {
|
|||||||
bool atLineStart;
|
bool atLineStart;
|
||||||
uint32_t lineNo;
|
uint32_t lineNo;
|
||||||
int lastToken;
|
int lastToken;
|
||||||
|
int nextToken;
|
||||||
|
|
||||||
std::deque<IfStackEntry> ifStack;
|
std::deque<IfStackEntry> ifStack;
|
||||||
|
|
||||||
@@ -91,13 +93,12 @@ struct LexerState {
|
|||||||
size_t captureSize; // Amount of text captured
|
size_t captureSize; // Amount of text captured
|
||||||
std::shared_ptr<std::vector<char>> captureBuf; // Buffer to send the captured text to if set
|
std::shared_ptr<std::vector<char>> captureBuf; // Buffer to send the captured text to if set
|
||||||
|
|
||||||
bool disableMacroArgs;
|
bool disableExpansions;
|
||||||
bool disableInterpolation;
|
size_t expansionScanDistance; // Max distance already scanned for expansions
|
||||||
size_t macroArgScanDistance; // Max distance already scanned for macro args
|
|
||||||
bool expandStrings;
|
bool expandStrings;
|
||||||
std::deque<Expansion> expansions; // Front is the innermost current expansion
|
std::deque<Expansion> expansions; // Front is the innermost current expansion
|
||||||
|
|
||||||
Either<ViewedContent, BufferedContent> content;
|
std::variant<std::monostate, ViewedContent, BufferedContent> content;
|
||||||
|
|
||||||
~LexerState();
|
~LexerState();
|
||||||
|
|
||||||
@@ -109,30 +110,17 @@ struct LexerState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setAsCurrentState();
|
void setAsCurrentState();
|
||||||
bool setFileAsNextState(std::string const &filePath, bool updateStateNow);
|
void setFileAsNextState(std::string const &filePath, bool updateStateNow);
|
||||||
void setViewAsNextState(char const *name, ContentSpan const &span, uint32_t lineNo_);
|
void setViewAsNextState(char const *name, ContentSpan const &span, uint32_t lineNo_);
|
||||||
|
|
||||||
void clear(uint32_t lineNo_);
|
void clear(uint32_t lineNo_);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern char binDigits[2];
|
void lexer_SetBinDigits(char const digits[2]);
|
||||||
extern char gfxDigits[4];
|
void lexer_SetGfxDigits(char const digits[4]);
|
||||||
|
|
||||||
static inline void lexer_SetBinDigits(char const digits[2]) {
|
|
||||||
binDigits[0] = digits[0];
|
|
||||||
binDigits[1] = digits[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void lexer_SetGfxDigits(char const digits[4]) {
|
|
||||||
gfxDigits[0] = digits[0];
|
|
||||||
gfxDigits[1] = digits[1];
|
|
||||||
gfxDigits[2] = digits[2];
|
|
||||||
gfxDigits[3] = digits[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool lexer_AtTopLevel();
|
bool lexer_AtTopLevel();
|
||||||
void lexer_RestartRept(uint32_t lineNo);
|
void lexer_RestartRept(uint32_t lineNo);
|
||||||
void lexer_Init();
|
|
||||||
void lexer_SetMode(LexerMode mode);
|
void lexer_SetMode(LexerMode mode);
|
||||||
void lexer_ToggleStringExpansion(bool enable);
|
void lexer_ToggleStringExpansion(bool enable);
|
||||||
|
|
||||||
@@ -146,7 +134,7 @@ void lexer_ReachELSEBlock();
|
|||||||
|
|
||||||
void lexer_CheckRecursionDepth();
|
void lexer_CheckRecursionDepth();
|
||||||
uint32_t lexer_GetLineNo();
|
uint32_t lexer_GetLineNo();
|
||||||
void lexer_DumpStringExpansions();
|
void lexer_TraceStringExpansions();
|
||||||
|
|
||||||
struct Capture {
|
struct Capture {
|
||||||
uint32_t lineNo;
|
uint32_t lineNo;
|
||||||
|
|||||||
@@ -3,16 +3,43 @@
|
|||||||
#ifndef RGBDS_ASM_MAIN_HPP
|
#ifndef RGBDS_ASM_MAIN_HPP
|
||||||
#define RGBDS_ASM_MAIN_HPP
|
#define RGBDS_ASM_MAIN_HPP
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
extern bool verbose;
|
enum MissingInclude {
|
||||||
extern bool warnings; // True to enable warnings, false to disable them.
|
INC_ERROR, // A missing included file is an error that halts assembly
|
||||||
|
GEN_EXIT, // A missing included file is assumed to be generated; exit normally
|
||||||
|
GEN_CONTINUE, // A missing included file is assumed to be generated; continue assembling
|
||||||
|
};
|
||||||
|
|
||||||
extern FILE *dependFile;
|
struct Options {
|
||||||
extern std::string targetFileName;
|
bool exportAll = false; // -E
|
||||||
extern bool generatedMissingIncludes;
|
uint8_t fixPrecision = 16; // -Q
|
||||||
extern bool failedOnMissingInclude;
|
size_t maxRecursionDepth = 64; // -r
|
||||||
extern bool generatePhonyDeps;
|
char binDigits[2] = {'0', '1'}; // -b
|
||||||
|
char gfxDigits[4] = {'0', '1', '2', '3'}; // -g
|
||||||
|
FILE *dependFile = nullptr; // -M
|
||||||
|
std::string targetFileName; // -MQ, -MT
|
||||||
|
MissingInclude missingIncludeState = INC_ERROR; // -MC, -MG
|
||||||
|
bool generatePhonyDeps = false; // -MP
|
||||||
|
std::string objectFileName; // -o
|
||||||
|
uint8_t padByte = 0; // -p
|
||||||
|
uint64_t maxErrors = 0; // -X
|
||||||
|
|
||||||
|
~Options() {
|
||||||
|
if (dependFile) {
|
||||||
|
fclose(dependFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void printDep(std::string const &depName) {
|
||||||
|
if (dependFile) {
|
||||||
|
fprintf(dependFile, "%s: %s\n", targetFileName.c_str(), depName.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Options options;
|
||||||
|
|
||||||
#endif // RGBDS_ASM_MAIN_HPP
|
#endif // RGBDS_ASM_MAIN_HPP
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
void opt_B(char const chars[2]);
|
void opt_B(char const binDigits[2]);
|
||||||
void opt_G(char const chars[4]);
|
void opt_G(char const gfxDigits[4]);
|
||||||
void opt_P(uint8_t padByte);
|
void opt_P(uint8_t padByte);
|
||||||
void opt_Q(uint8_t precision);
|
void opt_Q(uint8_t fixPrecision);
|
||||||
void opt_W(char const *flag);
|
void opt_W(char const *flag);
|
||||||
void opt_Parse(char const *option);
|
void opt_Parse(char const *option);
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "linkdefs.hpp"
|
#include "linkdefs.hpp"
|
||||||
@@ -16,10 +15,7 @@ struct FileStackNode;
|
|||||||
|
|
||||||
enum StateFeature { STATE_EQU, STATE_VAR, STATE_EQUS, STATE_CHAR, STATE_MACRO, NB_STATE_FEATURES };
|
enum StateFeature { STATE_EQU, STATE_VAR, STATE_EQUS, STATE_CHAR, STATE_MACRO, NB_STATE_FEATURES };
|
||||||
|
|
||||||
extern std::string objectFileName;
|
|
||||||
|
|
||||||
void out_RegisterNode(std::shared_ptr<FileStackNode> node);
|
void out_RegisterNode(std::shared_ptr<FileStackNode> node);
|
||||||
void out_SetFileName(std::string const &name);
|
|
||||||
void out_CreatePatch(uint32_t type, Expression const &expr, uint32_t ofs, uint32_t pcShift);
|
void out_CreatePatch(uint32_t type, Expression const &expr, uint32_t ofs, uint32_t pcShift);
|
||||||
void out_CreateAssert(
|
void out_CreateAssert(
|
||||||
AssertionType type, Expression const &expr, std::string const &message, uint32_t ofs
|
AssertionType type, Expression const &expr, std::string const &message, uint32_t ofs
|
||||||
|
|||||||
@@ -5,15 +5,15 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "either.hpp"
|
|
||||||
#include "linkdefs.hpp"
|
#include "linkdefs.hpp"
|
||||||
|
|
||||||
struct Symbol;
|
struct Symbol;
|
||||||
|
|
||||||
struct Expression {
|
struct Expression {
|
||||||
Either<
|
std::variant<
|
||||||
int32_t, // If the expression's value is known, it's here
|
int32_t, // If the expression's value is known, it's here
|
||||||
std::string // Why the expression is not known, if it isn't
|
std::string // Why the expression is not known, if it isn't
|
||||||
>
|
>
|
||||||
@@ -22,8 +22,8 @@ struct Expression {
|
|||||||
std::vector<uint8_t> rpn{}; // Bytes serializing the RPN expression
|
std::vector<uint8_t> rpn{}; // Bytes serializing the RPN expression
|
||||||
uint32_t rpnPatchSize = 0; // Size the expression will take in the object file
|
uint32_t rpnPatchSize = 0; // Size the expression will take in the object file
|
||||||
|
|
||||||
bool isKnown() const { return data.holds<int32_t>(); }
|
bool isKnown() const { return std::holds_alternative<int32_t>(data); }
|
||||||
int32_t value() const { return data.get<int32_t>(); }
|
int32_t value() const { return std::get<int32_t>(data); }
|
||||||
|
|
||||||
int32_t getConstVal() const;
|
int32_t getConstVal() const;
|
||||||
Symbol const *symbolOf() const;
|
Symbol const *symbolOf() const;
|
||||||
@@ -40,7 +40,7 @@ struct Expression {
|
|||||||
void makeUnaryOp(RPNCommand op, Expression &&src);
|
void makeUnaryOp(RPNCommand op, Expression &&src);
|
||||||
void makeBinaryOp(RPNCommand op, Expression &&src1, Expression const &src2);
|
void makeBinaryOp(RPNCommand op, Expression &&src1, Expression const &src2);
|
||||||
|
|
||||||
bool makeCheckHRAM();
|
void makeCheckHRAM();
|
||||||
void makeCheckRST();
|
void makeCheckRST();
|
||||||
void makeCheckBitIndex(uint8_t mask);
|
void makeCheckBitIndex(uint8_t mask);
|
||||||
|
|
||||||
|
|||||||
@@ -5,15 +5,14 @@
|
|||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "linkdefs.hpp"
|
#include "linkdefs.hpp"
|
||||||
|
|
||||||
extern uint8_t fillByte;
|
|
||||||
|
|
||||||
struct Expression;
|
struct Expression;
|
||||||
struct FileStackNode;
|
struct FileStackNode;
|
||||||
struct Section;
|
struct Section;
|
||||||
@@ -42,6 +41,7 @@ struct Section {
|
|||||||
std::deque<Patch> patches;
|
std::deque<Patch> patches;
|
||||||
std::vector<uint8_t> data;
|
std::vector<uint8_t> data;
|
||||||
|
|
||||||
|
uint32_t getID() const; // ID of the section in the object file (`UINT32_MAX` if none)
|
||||||
bool isSizeKnown() const;
|
bool isSizeKnown() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -51,9 +51,8 @@ struct SectionSpec {
|
|||||||
uint16_t alignOfs;
|
uint16_t alignOfs;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern std::deque<Section> sectionList;
|
size_t sect_CountSections();
|
||||||
extern std::unordered_map<std::string, size_t> sectionMap; // Indexes into `sectionList`
|
void sect_ForEach(void (*callback)(Section &));
|
||||||
extern Section *currentSection;
|
|
||||||
|
|
||||||
Section *sect_FindSectionByName(std::string const &name);
|
Section *sect_FindSectionByName(std::string const &name);
|
||||||
void sect_NewSection(
|
void sect_NewSection(
|
||||||
@@ -76,6 +75,10 @@ void sect_CheckLoadClosed();
|
|||||||
Section *sect_GetSymbolSection();
|
Section *sect_GetSymbolSection();
|
||||||
uint32_t sect_GetSymbolOffset();
|
uint32_t sect_GetSymbolOffset();
|
||||||
uint32_t sect_GetOutputOffset();
|
uint32_t sect_GetOutputOffset();
|
||||||
|
std::optional<uint32_t> sect_GetOutputBank();
|
||||||
|
|
||||||
|
Patch *sect_AddOutputPatch();
|
||||||
|
|
||||||
uint32_t sect_GetAlignBytes(uint8_t alignment, uint16_t offset);
|
uint32_t sect_GetAlignBytes(uint8_t alignment, uint16_t offset);
|
||||||
void sect_AlignPC(uint8_t alignment, uint16_t offset);
|
void sect_AlignPC(uint8_t alignment, uint16_t offset);
|
||||||
|
|
||||||
@@ -87,21 +90,23 @@ void sect_EndUnion();
|
|||||||
void sect_CheckUnionClosed();
|
void sect_CheckUnionClosed();
|
||||||
|
|
||||||
void sect_ConstByte(uint8_t byte);
|
void sect_ConstByte(uint8_t byte);
|
||||||
void sect_ByteString(std::vector<int32_t> const &string);
|
void sect_ByteString(std::vector<int32_t> const &str);
|
||||||
void sect_WordString(std::vector<int32_t> const &string);
|
void sect_WordString(std::vector<int32_t> const &str);
|
||||||
void sect_LongString(std::vector<int32_t> const &string);
|
void sect_LongString(std::vector<int32_t> const &str);
|
||||||
void sect_Skip(uint32_t skip, bool ds);
|
void sect_Skip(uint32_t skip, bool ds);
|
||||||
void sect_RelByte(Expression const &expr, uint32_t pcShift);
|
void sect_RelByte(Expression const &expr, uint32_t pcShift);
|
||||||
void sect_RelBytes(uint32_t n, std::vector<Expression> const &exprs);
|
void sect_RelBytes(uint32_t n, std::vector<Expression> const &exprs);
|
||||||
void sect_RelWord(Expression const &expr, uint32_t pcShift);
|
void sect_RelWord(Expression const &expr, uint32_t pcShift);
|
||||||
void sect_RelLong(Expression const &expr, uint32_t pcShift);
|
void sect_RelLong(Expression const &expr, uint32_t pcShift);
|
||||||
void sect_PCRelByte(Expression const &expr, uint32_t pcShift);
|
void sect_PCRelByte(Expression const &expr, uint32_t pcShift);
|
||||||
void sect_BinaryFile(std::string const &name, int32_t startPos);
|
bool sect_BinaryFile(std::string const &name, uint32_t startPos);
|
||||||
void sect_BinaryFileSlice(std::string const &name, int32_t startPos, int32_t length);
|
bool sect_BinaryFileSlice(std::string const &name, uint32_t startPos, uint32_t length);
|
||||||
|
|
||||||
void sect_EndSection();
|
void sect_EndSection();
|
||||||
void sect_PushSection();
|
void sect_PushSection();
|
||||||
void sect_PopSection();
|
void sect_PopSection();
|
||||||
void sect_CheckStack();
|
void sect_CheckStack();
|
||||||
|
|
||||||
|
std::string sect_PushSectionFragmentLiteral();
|
||||||
|
|
||||||
#endif // RGBDS_ASM_SECTION_HPP
|
#endif // RGBDS_ASM_SECTION_HPP
|
||||||
|
|||||||
@@ -5,9 +5,7 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
@@ -24,14 +22,15 @@ enum SymbolType {
|
|||||||
SYM_REF // Forward reference to a label
|
SYM_REF // Forward reference to a label
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Symbol; // For the `sym_IsPC` forward declaration
|
struct Symbol; // Forward declaration for `sym_IsPC`
|
||||||
bool sym_IsPC(Symbol const *sym); // For the inline `getSection` method
|
bool sym_IsPC(Symbol const *sym); // Forward declaration for `getSection`
|
||||||
|
|
||||||
struct Symbol {
|
struct Symbol {
|
||||||
std::string name;
|
std::string name;
|
||||||
SymbolType type;
|
SymbolType type;
|
||||||
bool isExported; // Whether the symbol is to be exported
|
bool isBuiltin;
|
||||||
bool isBuiltin; // Whether the symbol is a built-in
|
bool isExported; // Not relevant for SYM_MACRO or SYM_EQUS
|
||||||
|
bool isQuiet; // Only relevant for SYM_MACRO
|
||||||
Section *section;
|
Section *section;
|
||||||
std::shared_ptr<FileStackNode> src; // Where the symbol was defined
|
std::shared_ptr<FileStackNode> src; // Where the symbol was defined
|
||||||
uint32_t fileLine; // Line where the symbol was defined
|
uint32_t fileLine; // Line where the symbol was defined
|
||||||
@@ -71,7 +70,6 @@ struct Symbol {
|
|||||||
|
|
||||||
void sym_ForEach(void (*callback)(Symbol &));
|
void sym_ForEach(void (*callback)(Symbol &));
|
||||||
|
|
||||||
void sym_SetExportAll(bool set);
|
|
||||||
Symbol *sym_AddLocalLabel(std::string const &symName);
|
Symbol *sym_AddLocalLabel(std::string const &symName);
|
||||||
Symbol *sym_AddLabel(std::string const &symName);
|
Symbol *sym_AddLabel(std::string const &symName);
|
||||||
Symbol *sym_AddAnonLabel();
|
Symbol *sym_AddAnonLabel();
|
||||||
@@ -89,7 +87,9 @@ Symbol *sym_FindScopedSymbol(std::string const &symName);
|
|||||||
// Find a scoped symbol by name; do not return `@` or `_NARG` when they have no value
|
// Find a scoped symbol by name; do not return `@` or `_NARG` when they have no value
|
||||||
Symbol *sym_FindScopedValidSymbol(std::string const &symName);
|
Symbol *sym_FindScopedValidSymbol(std::string const &symName);
|
||||||
Symbol const *sym_GetPC();
|
Symbol const *sym_GetPC();
|
||||||
Symbol *sym_AddMacro(std::string const &symName, int32_t defLineNo, ContentSpan const &span);
|
Symbol *sym_AddMacro(
|
||||||
|
std::string const &symName, int32_t defLineNo, ContentSpan const &span, bool isQuiet
|
||||||
|
);
|
||||||
Symbol *sym_Ref(std::string const &symName);
|
Symbol *sym_Ref(std::string const &symName);
|
||||||
Symbol *sym_AddString(std::string const &symName, std::shared_ptr<std::string> value);
|
Symbol *sym_AddString(std::string const &symName, std::shared_ptr<std::string> value);
|
||||||
Symbol *sym_RedefString(std::string const &symName, std::shared_ptr<std::string> value);
|
Symbol *sym_RedefString(std::string const &symName, std::shared_ptr<std::string> value);
|
||||||
|
|||||||
@@ -3,7 +3,16 @@
|
|||||||
#ifndef RGBDS_ASM_WARNING_HPP
|
#ifndef RGBDS_ASM_WARNING_HPP
|
||||||
#define RGBDS_ASM_WARNING_HPP
|
#define RGBDS_ASM_WARNING_HPP
|
||||||
|
|
||||||
extern unsigned int nbErrors, maxErrors;
|
#include <functional>
|
||||||
|
|
||||||
|
#include "diagnostics.hpp"
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
enum WarningID {
|
enum WarningID {
|
||||||
WARNING_ASSERT, // Assertions
|
WARNING_ASSERT, // Assertions
|
||||||
@@ -14,6 +23,7 @@ enum WarningID {
|
|||||||
WARNING_EMPTY_DATA_DIRECTIVE, // `db`, `dw` or `dl` directive without data in ROM
|
WARNING_EMPTY_DATA_DIRECTIVE, // `db`, `dw` or `dl` directive without data in ROM
|
||||||
WARNING_EMPTY_MACRO_ARG, // Empty macro argument
|
WARNING_EMPTY_MACRO_ARG, // Empty macro argument
|
||||||
WARNING_EMPTY_STRRPL, // Empty second argument in `STRRPL`
|
WARNING_EMPTY_STRRPL, // Empty second argument in `STRRPL`
|
||||||
|
WARNING_EXPORT_UNDEFINED, // `EXPORT` of an undefined symbol
|
||||||
WARNING_LARGE_CONSTANT, // Constants too large
|
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_NESTED_COMMENT, // Comment-start delimiter in a block comment
|
||||||
@@ -43,24 +53,7 @@ enum WarningID {
|
|||||||
NB_WARNINGS,
|
NB_WARNINGS,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum WarningAbled { WARNING_DEFAULT, WARNING_ENABLED, WARNING_DISABLED };
|
extern Diagnostics<WarningLevel, WarningID> warnings;
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
// Used to warn the user about problems that don't prevent the generation of
|
// Used to warn the user about problems that don't prevent the generation of
|
||||||
// valid code.
|
// valid code.
|
||||||
@@ -73,7 +66,7 @@ void warning(WarningID id, char const *fmt, ...);
|
|||||||
// It is also used when the assembler goes into an invalid state (for example,
|
// It is also used when the assembler goes into an invalid state (for example,
|
||||||
// when it fails to allocate memory).
|
// when it fails to allocate memory).
|
||||||
[[gnu::format(printf, 1, 2), noreturn]]
|
[[gnu::format(printf, 1, 2), noreturn]]
|
||||||
void fatalerror(char const *fmt, ...);
|
void fatal(char const *fmt, ...);
|
||||||
|
|
||||||
// Used for errors that make it impossible to assemble correctly, but don't
|
// Used for errors that make it impossible to assemble correctly, but don't
|
||||||
// affect the following code. The code will fail to assemble but the user will
|
// affect the following code. The code will fail to assemble but the user will
|
||||||
@@ -82,4 +75,11 @@ void fatalerror(char const *fmt, ...);
|
|||||||
[[gnu::format(printf, 1, 2)]]
|
[[gnu::format(printf, 1, 2)]]
|
||||||
void error(char const *fmt, ...);
|
void error(char const *fmt, ...);
|
||||||
|
|
||||||
|
// Used for errors that handle their own backtrace output. The code will fail
|
||||||
|
// to assemble but the user will get a list of all errors at the end, making it
|
||||||
|
// easier to fix all of them at once.
|
||||||
|
void errorNoTrace(std::function<void()> callback);
|
||||||
|
|
||||||
|
void requireZeroErrors();
|
||||||
|
|
||||||
#endif // RGBDS_ASM_WARNING_HPP
|
#endif // RGBDS_ASM_WARNING_HPP
|
||||||
|
|||||||
85
include/backtrace.hpp
Normal file
85
include/backtrace.hpp
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#ifndef RGBDS_BACKTRACE_HPP
|
||||||
|
#define RGBDS_BACKTRACE_HPP
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "style.hpp"
|
||||||
|
|
||||||
|
struct Tracing {
|
||||||
|
uint64_t depth = 0;
|
||||||
|
bool collapse = false;
|
||||||
|
bool loud = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Tracing tracing;
|
||||||
|
|
||||||
|
bool trace_ParseTraceDepth(char const *arg);
|
||||||
|
|
||||||
|
template<typename T, typename M, typename N>
|
||||||
|
void trace_PrintBacktrace(std::vector<T> const &stack, M getName, N getLineNo) {
|
||||||
|
size_t n = stack.size();
|
||||||
|
if (n == 0) {
|
||||||
|
return; // LCOV_EXCL_LINE
|
||||||
|
}
|
||||||
|
|
||||||
|
auto printLocation = [&](size_t i) {
|
||||||
|
T const &item = stack[n - i - 1];
|
||||||
|
style_Reset(stderr);
|
||||||
|
if (!tracing.collapse) {
|
||||||
|
fputs(" ", stderr); // Just three spaces; the fourth will be printed next
|
||||||
|
}
|
||||||
|
fprintf(stderr, " %s ", i == 0 ? "at" : "<-");
|
||||||
|
style_Set(stderr, STYLE_CYAN, true);
|
||||||
|
fputs(getName(item), stderr);
|
||||||
|
style_Set(stderr, STYLE_CYAN, false);
|
||||||
|
fprintf(stderr, "(%" PRIu32 ")", getLineNo(item));
|
||||||
|
if (!tracing.collapse) {
|
||||||
|
putc('\n', stderr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (tracing.collapse) {
|
||||||
|
fputs(" ", stderr); // Just three spaces; the fourth will be handled by the loop
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tracing.depth == 0 || static_cast<size_t>(tracing.depth) >= n) {
|
||||||
|
for (size_t i = 0; i < n; ++i) {
|
||||||
|
printLocation(i);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
size_t last = tracing.depth / 2;
|
||||||
|
size_t first = tracing.depth - last;
|
||||||
|
size_t skipped = n - tracing.depth;
|
||||||
|
for (size_t i = 0; i < first; ++i) {
|
||||||
|
printLocation(i);
|
||||||
|
}
|
||||||
|
style_Reset(stderr);
|
||||||
|
|
||||||
|
if (tracing.collapse) {
|
||||||
|
fputs(" <-", stderr);
|
||||||
|
} else {
|
||||||
|
fputs(" ", stderr); // Just three spaces; the fourth will be printed next
|
||||||
|
}
|
||||||
|
fprintf(stderr, " ...%zu more%s", skipped, last ? "..." : "");
|
||||||
|
if (!tracing.collapse) {
|
||||||
|
putc('\n', stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = n - last; i < n; ++i) {
|
||||||
|
printLocation(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tracing.collapse) {
|
||||||
|
putc('\n', stderr);
|
||||||
|
}
|
||||||
|
style_Reset(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // RGBDS_BACKTRACE_HPP
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
#ifndef RGBDS_DEFAULT_INIT_ALLOC_HPP
|
|
||||||
#define RGBDS_DEFAULT_INIT_ALLOC_HPP
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
// Allocator adaptor that interposes construct() calls to convert value-initialization
|
|
||||||
// (which is what you get with e.g. `vector::resize`) into default-initialization (which does not
|
|
||||||
// zero out non-class types).
|
|
||||||
// From
|
|
||||||
// https://stackoverflow.com/questions/21028299/is-this-behavior-of-vectorresizesize-type-n-under-c11-and-boost-container/21028912#21028912
|
|
||||||
template<typename T, typename A = std::allocator<T>>
|
|
||||||
class default_init_allocator : public A {
|
|
||||||
using a_t = std::allocator_traits<A>;
|
|
||||||
public:
|
|
||||||
template<typename U>
|
|
||||||
struct rebind {
|
|
||||||
using other = default_init_allocator<U, typename a_t::template rebind_alloc<U>>;
|
|
||||||
};
|
|
||||||
|
|
||||||
using A::A; // Inherit the allocator's constructors
|
|
||||||
|
|
||||||
template<typename U>
|
|
||||||
void construct(U *ptr) noexcept(std::is_nothrow_default_constructible_v<U>) {
|
|
||||||
::new (static_cast<void *>(ptr)) U;
|
|
||||||
}
|
|
||||||
template<typename U, typename... Args>
|
|
||||||
void construct(U *ptr, Args &&...args) {
|
|
||||||
a_t::construct(static_cast<A &>(*this), ptr, std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
using DefaultInitVec = std::vector<T, default_init_allocator<T>>;
|
|
||||||
|
|
||||||
#endif // RGBDS_DEFAULT_INIT_ALLOC_HPP
|
|
||||||
211
include/diagnostics.hpp
Normal file
211
include/diagnostics.hpp
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#ifndef RGBDS_DIAGNOSTICS_HPP
|
||||||
|
#define RGBDS_DIAGNOSTICS_HPP
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <optional>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "helpers.hpp"
|
||||||
|
#include "itertools.hpp"
|
||||||
|
|
||||||
|
[[gnu::format(printf, 1, 2)]]
|
||||||
|
void warnx(char const *fmt, ...);
|
||||||
|
|
||||||
|
enum WarningAbled { WARNING_DEFAULT, WARNING_ENABLED, WARNING_DISABLED };
|
||||||
|
|
||||||
|
struct WarningState {
|
||||||
|
WarningAbled state;
|
||||||
|
WarningAbled error;
|
||||||
|
|
||||||
|
void update(WarningState other);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::pair<WarningState, std::optional<uint32_t>> getInitialWarningState(std::string &flag);
|
||||||
|
|
||||||
|
template<typename L>
|
||||||
|
struct WarningFlag {
|
||||||
|
char const *name;
|
||||||
|
L level;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum WarningBehavior { DISABLED, ENABLED, ERROR };
|
||||||
|
|
||||||
|
template<typename W>
|
||||||
|
struct ParamWarning {
|
||||||
|
W firstID;
|
||||||
|
W lastID;
|
||||||
|
uint8_t defaultLevel;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename W>
|
||||||
|
struct DiagnosticsState {
|
||||||
|
WarningState flagStates[W::NB_WARNINGS];
|
||||||
|
WarningState metaStates[W::NB_WARNINGS];
|
||||||
|
bool warningsEnabled = true;
|
||||||
|
bool warningsAreErrors = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename L, typename W>
|
||||||
|
struct Diagnostics {
|
||||||
|
std::vector<WarningFlag<L>> metaWarnings;
|
||||||
|
std::vector<WarningFlag<L>> warningFlags;
|
||||||
|
std::vector<ParamWarning<W>> paramWarnings;
|
||||||
|
DiagnosticsState<W> state;
|
||||||
|
uint64_t nbErrors;
|
||||||
|
|
||||||
|
void incrementErrors() {
|
||||||
|
if (nbErrors != UINT64_MAX) {
|
||||||
|
++nbErrors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WarningBehavior getWarningBehavior(W id) const;
|
||||||
|
void processWarningFlag(char const *flag);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename L, typename W>
|
||||||
|
WarningBehavior Diagnostics<L, W>::getWarningBehavior(W id) const {
|
||||||
|
// Check if warnings are globally disabled
|
||||||
|
if (!state.warningsEnabled) {
|
||||||
|
return WarningBehavior::DISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the state of this warning flag
|
||||||
|
WarningState const &flagState = state.flagStates[id];
|
||||||
|
WarningState const &metaState = state.metaStates[id];
|
||||||
|
|
||||||
|
// 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 = state.warningsAreErrors && flagState.error != WARNING_DISABLED
|
||||||
|
&& metaState.error != WARNING_DISABLED;
|
||||||
|
WarningBehavior enabledBehavior =
|
||||||
|
warningIsError ? WarningBehavior::ERROR : WarningBehavior::ENABLED;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 == L::LEVEL_DEFAULT) { // enabled by default
|
||||||
|
return enabledBehavior;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No flag enables this warning, explicitly or implicitly
|
||||||
|
return WarningBehavior::DISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename L, typename W>
|
||||||
|
void Diagnostics<L, W>::processWarningFlag(char const *flag) {
|
||||||
|
std::string rootFlag = flag;
|
||||||
|
|
||||||
|
// Check for `-Werror` or `-Wno-error` to return early
|
||||||
|
if (rootFlag == "error") {
|
||||||
|
// `-Werror` promotes warnings to errors
|
||||||
|
state.warningsAreErrors = true;
|
||||||
|
return;
|
||||||
|
} else if (rootFlag == "no-error") {
|
||||||
|
// `-Wno-error` disables promotion of warnings to errors
|
||||||
|
state.warningsAreErrors = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto [flagState, param] = getInitialWarningState(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 (ParamWarning<W> const ¶mWarning : paramWarnings) {
|
||||||
|
W baseID = paramWarning.firstID;
|
||||||
|
uint8_t maxParam = paramWarning.lastID - baseID + 1;
|
||||||
|
assume(paramWarning.defaultLevel <= maxParam);
|
||||||
|
|
||||||
|
if (rootFlag != warningFlags[baseID].name) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.has_value() || *param == 0) {
|
||||||
|
param = paramWarning.defaultLevel;
|
||||||
|
} else if (*param > maxParam) {
|
||||||
|
warnx(
|
||||||
|
"Invalid warning flag parameter \"%s=%" PRIu32 "\"; capping at maximum %" PRIu8,
|
||||||
|
rootFlag.c_str(),
|
||||||
|
*param,
|
||||||
|
maxParam
|
||||||
|
);
|
||||||
|
*param = maxParam;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the first <param> to enabled/error, and disable the rest
|
||||||
|
for (uint32_t ofs = 0; ofs < maxParam; ++ofs) {
|
||||||
|
if (WarningState &warning = state.flagStates[baseID + ofs]; ofs < *param) {
|
||||||
|
warning.update(flagState);
|
||||||
|
} else {
|
||||||
|
warning.state = WARNING_DISABLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param.has_value()) {
|
||||||
|
warnx("Unknown warning flag parameter \"%s=%" PRIu32 "\"", rootFlag.c_str(), *param);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to match against a "meta" warning
|
||||||
|
for (WarningFlag<L> const &metaWarning : metaWarnings) {
|
||||||
|
if (rootFlag != metaWarning.name) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set each of the warning flags that meets this level
|
||||||
|
for (W id : EnumSeq(W::NB_WARNINGS)) {
|
||||||
|
if (metaWarning.level >= warningFlags[id].level) {
|
||||||
|
state.metaStates[id].update(flagState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to match against a "normal" flag
|
||||||
|
for (W id : EnumSeq(W::NB_PLAIN_WARNINGS)) {
|
||||||
|
if (rootFlag == warningFlags[id].name) {
|
||||||
|
state.flagStates[id].update(flagState);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
warnx("Unknown warning flag \"%s\"", rootFlag.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // RGBDS_DIAGNOSTICS_HPP
|
||||||
@@ -1,176 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
#ifndef RGBDS_EITHER_HPP
|
|
||||||
#define RGBDS_EITHER_HPP
|
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include "helpers.hpp" // assume
|
|
||||||
|
|
||||||
template<typename T1, typename T2>
|
|
||||||
union Either {
|
|
||||||
typedef T1 type1;
|
|
||||||
typedef T2 type2;
|
|
||||||
|
|
||||||
private:
|
|
||||||
template<typename T, unsigned V>
|
|
||||||
struct Field {
|
|
||||||
constexpr static unsigned tag_value = V;
|
|
||||||
|
|
||||||
unsigned tag = tag_value;
|
|
||||||
T value;
|
|
||||||
|
|
||||||
Field() : value() {}
|
|
||||||
Field(T &value_) : value(value_) {}
|
|
||||||
Field(T const &value_) : value(value_) {}
|
|
||||||
Field(T &&value_) : value(std::move(value_)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// The `_tag` unifies with the first `tag` member of each `struct`.
|
|
||||||
constexpr static unsigned nulltag = 0;
|
|
||||||
unsigned _tag = nulltag;
|
|
||||||
Field<T1, 1> _t1;
|
|
||||||
Field<T2, 2> _t2;
|
|
||||||
|
|
||||||
// Value accessors; the function parameters are dummies for overload resolution.
|
|
||||||
// Only used to implement `field()` below.
|
|
||||||
auto &pick(T1 *) { return _t1; }
|
|
||||||
auto const &pick(T1 *) const { return _t1; }
|
|
||||||
auto &pick(T2 *) { return _t2; }
|
|
||||||
auto const &pick(T2 *) const { return _t2; }
|
|
||||||
|
|
||||||
// Generic field accessors; for internal use only.
|
|
||||||
template<typename T>
|
|
||||||
auto &field() {
|
|
||||||
return pick(static_cast<T *>(nullptr));
|
|
||||||
}
|
|
||||||
template<typename T>
|
|
||||||
auto const &field() const {
|
|
||||||
return pick(static_cast<T *>(nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Equivalent of `std::monostate` for `std::variant`s.
|
|
||||||
Either() : _tag() {}
|
|
||||||
// These constructors cannot be generic over the value type, because that would prevent
|
|
||||||
// constructible values from being inferred, e.g. a `const char *` string literal for an
|
|
||||||
// `std::string` field value.
|
|
||||||
Either(T1 &value) : _t1(value) {}
|
|
||||||
Either(T2 &value) : _t2(value) {}
|
|
||||||
Either(T1 const &value) : _t1(value) {}
|
|
||||||
Either(T2 const &value) : _t2(value) {}
|
|
||||||
Either(T1 &&value) : _t1(std::move(value)) {}
|
|
||||||
Either(T2 &&value) : _t2(std::move(value)) {}
|
|
||||||
|
|
||||||
// Destructor manually calls the appropriate value destructor.
|
|
||||||
~Either() {
|
|
||||||
if (_tag == _t1.tag_value) {
|
|
||||||
_t1.value.~T1();
|
|
||||||
} else if (_tag == _t2.tag_value) {
|
|
||||||
_t2.value.~T2();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy assignment operators for each possible value.
|
|
||||||
Either &operator=(T1 const &value) {
|
|
||||||
_t1.tag = _t1.tag_value;
|
|
||||||
new (&_t1.value) T1(value);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
Either &operator=(T2 const &value) {
|
|
||||||
_t2.tag = _t2.tag_value;
|
|
||||||
new (&_t2.value) T2(value);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move assignment operators for each possible value.
|
|
||||||
Either &operator=(T1 &&value) {
|
|
||||||
_t1.tag = _t1.tag_value;
|
|
||||||
new (&_t1.value) T1(std::move(value));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
Either &operator=(T2 &&value) {
|
|
||||||
_t2.tag = _t2.tag_value;
|
|
||||||
new (&_t2.value) T2(std::move(value));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy assignment operator from another `Either`.
|
|
||||||
Either &operator=(Either other) {
|
|
||||||
if (other._tag == other._t1.tag_value) {
|
|
||||||
*this = other._t1.value;
|
|
||||||
} else if (other._tag == other._t2.tag_value) {
|
|
||||||
*this = other._t2.value;
|
|
||||||
} else {
|
|
||||||
_tag = nulltag; // LCOV_EXCL_LINE
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy constructor from another `Either`; implemented in terms of value assignment operators.
|
|
||||||
Either(Either const &other) {
|
|
||||||
if (other._tag == other._t1.tag_value) {
|
|
||||||
*this = other._t1.value;
|
|
||||||
} else if (other._tag == other._t2.tag_value) {
|
|
||||||
*this = other._t2.value;
|
|
||||||
} else {
|
|
||||||
_tag = nulltag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move constructor from another `Either`; implemented in terms of value assignment operators.
|
|
||||||
Either(Either &&other) {
|
|
||||||
if (other._tag == other._t1.tag_value) {
|
|
||||||
*this = std::move(other._t1.value);
|
|
||||||
} else if (other._tag == other._t2.tag_value) {
|
|
||||||
*this = std::move(other._t2.value);
|
|
||||||
} else {
|
|
||||||
_tag = nulltag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equivalent of `.emplace<T>()` for `std::variant`s.
|
|
||||||
template<typename T, typename... Args>
|
|
||||||
void emplace(Args &&...args) {
|
|
||||||
this->~Either();
|
|
||||||
if constexpr (std::is_same_v<T, T1>) {
|
|
||||||
_t1.tag = _t1.tag_value;
|
|
||||||
new (&_t1.value) T1(std::forward<Args>(args)...);
|
|
||||||
} else if constexpr (std::is_same_v<T, T2>) {
|
|
||||||
_t2.tag = _t2.tag_value;
|
|
||||||
new (&_t2.value) T2(std::forward<Args>(args)...);
|
|
||||||
} else {
|
|
||||||
_tag = nulltag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equivalent of `std::holds_alternative<std::monostate>()` for `std::variant`s.
|
|
||||||
bool empty() const { return _tag == nulltag; }
|
|
||||||
|
|
||||||
// Equivalent of `std::holds_alternative<T>()` for `std::variant`s.
|
|
||||||
template<typename T>
|
|
||||||
bool holds() const {
|
|
||||||
if constexpr (std::is_same_v<T, T1>) {
|
|
||||||
return _tag == _t1.tag_value;
|
|
||||||
} else if constexpr (std::is_same_v<T, T2>) {
|
|
||||||
return _tag == _t2.tag_value;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equivalent of `std::get<T>()` for `std::variant`s.
|
|
||||||
template<typename T>
|
|
||||||
auto &get() {
|
|
||||||
assume(holds<T>());
|
|
||||||
return field<T>().value;
|
|
||||||
}
|
|
||||||
template<typename T>
|
|
||||||
auto const &get() const {
|
|
||||||
assume(holds<T>());
|
|
||||||
return field<T>().value;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // RGBDS_EITHER_HPP
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
#ifndef RGBDS_ERROR_HPP
|
|
||||||
#define RGBDS_ERROR_HPP
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
[[gnu::format(printf, 1, 2)]]
|
|
||||||
void warn(char const *fmt...);
|
|
||||||
[[gnu::format(printf, 1, 2)]]
|
|
||||||
void warnx(char const *fmt, ...);
|
|
||||||
|
|
||||||
[[gnu::format(printf, 1, 2), noreturn]]
|
|
||||||
void err(char const *fmt, ...);
|
|
||||||
[[gnu::format(printf, 1, 2), noreturn]]
|
|
||||||
void errx(char const *fmt, ...);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // RGBDS_ERROR_HPP
|
|
||||||
3
include/extern/utf8decoder.hpp
vendored
3
include/extern/utf8decoder.hpp
vendored
@@ -5,6 +5,9 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define UTF8_ACCEPT 0
|
||||||
|
#define UTF8_REJECT 12
|
||||||
|
|
||||||
uint32_t decode(uint32_t *state, uint32_t *codep, uint8_t byte);
|
uint32_t decode(uint32_t *state, uint32_t *codep, uint8_t byte);
|
||||||
|
|
||||||
#endif // RGBDS_EXTERN_UTF8DECODER_HPP
|
#endif // RGBDS_EXTERN_UTF8DECODER_HPP
|
||||||
|
|||||||
@@ -8,17 +8,14 @@
|
|||||||
#include <ios>
|
#include <ios>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <streambuf>
|
#include <streambuf>
|
||||||
#include <string.h>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
#include "either.hpp"
|
|
||||||
#include "helpers.hpp" // assume
|
#include "helpers.hpp" // assume
|
||||||
#include "platform.hpp"
|
#include "platform.hpp"
|
||||||
|
|
||||||
#include "gfx/main.hpp"
|
|
||||||
|
|
||||||
class File {
|
class File {
|
||||||
Either<std::streambuf *, std::filebuf> _file;
|
std::variant<std::streambuf *, std::filebuf> _file;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
File() : _file(nullptr) {}
|
File() : _file(nullptr) {}
|
||||||
@@ -27,17 +24,12 @@ public:
|
|||||||
// Returns `nullptr` on error, and a non-null pointer otherwise.
|
// Returns `nullptr` on error, and a non-null pointer otherwise.
|
||||||
File *open(std::string const &path, std::ios_base::openmode mode) {
|
File *open(std::string const &path, std::ios_base::openmode mode) {
|
||||||
if (path != "-") {
|
if (path != "-") {
|
||||||
_file.emplace<std::filebuf>();
|
return _file.emplace<std::filebuf>().open(path, mode) ? this : nullptr;
|
||||||
return _file.get<std::filebuf>().open(path, mode) ? this : nullptr;
|
|
||||||
} else if (mode & std::ios_base::in) {
|
} else if (mode & std::ios_base::in) {
|
||||||
assume(!(mode & std::ios_base::out));
|
assume(!(mode & std::ios_base::out));
|
||||||
_file.emplace<std::streambuf *>(std::cin.rdbuf());
|
_file.emplace<std::streambuf *>(std::cin.rdbuf());
|
||||||
if (setmode(STDIN_FILENO, (mode & std::ios_base::binary) ? O_BINARY : O_TEXT) == -1) {
|
if (setmode(STDIN_FILENO, (mode & std::ios_base::binary) ? O_BINARY : O_TEXT) == -1) {
|
||||||
fatal(
|
return nullptr;
|
||||||
"Failed to set stdin to %s mode: %s",
|
|
||||||
mode & std::ios_base::binary ? "binary" : "text",
|
|
||||||
strerror(errno)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assume(mode & std::ios_base::out);
|
assume(mode & std::ios_base::out);
|
||||||
@@ -46,8 +38,8 @@ public:
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
std::streambuf &operator*() {
|
std::streambuf &operator*() {
|
||||||
return _file.holds<std::filebuf>() ? _file.get<std::filebuf>()
|
return std::holds_alternative<std::filebuf>(_file) ? std::get<std::filebuf>(_file)
|
||||||
: *_file.get<std::streambuf *>();
|
: *std::get<std::streambuf *>(_file);
|
||||||
}
|
}
|
||||||
std::streambuf const &operator*() const {
|
std::streambuf const &operator*() const {
|
||||||
// The non-`const` version does not perform any modifications, so it's okay.
|
// The non-`const` version does not perform any modifications, so it's okay.
|
||||||
@@ -60,8 +52,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
char const *c_str(std::string const &path) const {
|
char const *c_str(std::string const &path) const {
|
||||||
return _file.holds<std::filebuf>() ? path.c_str()
|
return std::holds_alternative<std::filebuf>(_file) ? path.c_str()
|
||||||
: _file.get<std::streambuf *>() == std::cin.rdbuf() ? "<stdin>"
|
: std::get<std::streambuf *>(_file) == std::cin.rdbuf() ? "<stdin>"
|
||||||
: "<stdout>";
|
: "<stdout>";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
8
include/fix/fix.hpp
Normal file
8
include/fix/fix.hpp
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#ifndef RGBDS_FIX_FIX_HPP
|
||||||
|
#define RGBDS_FIX_FIX_HPP
|
||||||
|
|
||||||
|
bool fix_ProcessFile(char const *name, char const *outputName);
|
||||||
|
|
||||||
|
#endif // RGBDS_FIX_FIX_HPP
|
||||||
49
include/fix/main.hpp
Normal file
49
include/fix/main.hpp
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#ifndef RGBDS_FIX_MAIN_HPP
|
||||||
|
#define RGBDS_FIX_MAIN_HPP
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "fix/mbc.hpp" // UNSPECIFIED, MbcType
|
||||||
|
|
||||||
|
// clang-format off: vertically align values
|
||||||
|
static constexpr uint8_t FIX_LOGO = 1 << 7;
|
||||||
|
static constexpr uint8_t TRASH_LOGO = 1 << 6;
|
||||||
|
static constexpr uint8_t FIX_HEADER_SUM = 1 << 5;
|
||||||
|
static constexpr uint8_t TRASH_HEADER_SUM = 1 << 4;
|
||||||
|
static constexpr uint8_t FIX_GLOBAL_SUM = 1 << 3;
|
||||||
|
static constexpr uint8_t TRASH_GLOBAL_SUM = 1 << 2;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
enum Model { DMG, BOTH, CGB };
|
||||||
|
|
||||||
|
struct Options {
|
||||||
|
uint8_t fixSpec = 0; // -f, -v
|
||||||
|
Model model = DMG; // -C, -c
|
||||||
|
bool japanese = true; // -j
|
||||||
|
uint16_t oldLicensee = UNSPECIFIED; // -l
|
||||||
|
uint16_t romVersion = UNSPECIFIED; // -n
|
||||||
|
uint16_t padValue = UNSPECIFIED; // -p
|
||||||
|
uint16_t ramSize = UNSPECIFIED; // -r
|
||||||
|
bool sgb = false; // -s
|
||||||
|
|
||||||
|
char const *gameID = nullptr; // -i
|
||||||
|
uint8_t gameIDLen;
|
||||||
|
|
||||||
|
char const *newLicensee = nullptr; // -k
|
||||||
|
uint8_t newLicenseeLen;
|
||||||
|
|
||||||
|
char const *logoFilename = nullptr; // -L
|
||||||
|
uint8_t logo[48] = {};
|
||||||
|
|
||||||
|
MbcType cartridgeType = MBC_NONE; // -m
|
||||||
|
uint8_t tpp1Rev[2];
|
||||||
|
|
||||||
|
char const *title = nullptr; // -t
|
||||||
|
uint8_t titleLen;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Options options;
|
||||||
|
|
||||||
|
#endif // RGBDS_FIX_MAIN_HPP
|
||||||
79
include/fix/mbc.hpp
Normal file
79
include/fix/mbc.hpp
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#ifndef RGBDS_FIX_MBC_HPP
|
||||||
|
#define RGBDS_FIX_MBC_HPP
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
constexpr uint16_t UNSPECIFIED = 0x200;
|
||||||
|
static_assert(UNSPECIFIED > 0xFF, "UNSPECIFIED should not be in byte range!");
|
||||||
|
|
||||||
|
enum MbcType {
|
||||||
|
ROM = 0x00,
|
||||||
|
ROM_RAM = 0x08,
|
||||||
|
ROM_RAM_BATTERY = 0x09,
|
||||||
|
|
||||||
|
MBC1 = 0x01,
|
||||||
|
MBC1_RAM = 0x02,
|
||||||
|
MBC1_RAM_BATTERY = 0x03,
|
||||||
|
|
||||||
|
MBC2 = 0x05,
|
||||||
|
MBC2_BATTERY = 0x06,
|
||||||
|
|
||||||
|
MMM01 = 0x0B,
|
||||||
|
MMM01_RAM = 0x0C,
|
||||||
|
MMM01_RAM_BATTERY = 0x0D,
|
||||||
|
|
||||||
|
MBC3 = 0x11,
|
||||||
|
MBC3_TIMER_BATTERY = 0x0F,
|
||||||
|
MBC3_TIMER_RAM_BATTERY = 0x10,
|
||||||
|
MBC3_RAM = 0x12,
|
||||||
|
MBC3_RAM_BATTERY = 0x13,
|
||||||
|
|
||||||
|
MBC5 = 0x19,
|
||||||
|
MBC5_RAM = 0x1A,
|
||||||
|
MBC5_RAM_BATTERY = 0x1B,
|
||||||
|
MBC5_RUMBLE = 0x1C,
|
||||||
|
MBC5_RUMBLE_RAM = 0x1D,
|
||||||
|
MBC5_RUMBLE_RAM_BATTERY = 0x1E,
|
||||||
|
|
||||||
|
MBC6 = 0x20,
|
||||||
|
|
||||||
|
MBC7_SENSOR_RUMBLE_RAM_BATTERY = 0x22,
|
||||||
|
|
||||||
|
POCKET_CAMERA = 0xFC,
|
||||||
|
|
||||||
|
BANDAI_TAMA5 = 0xFD,
|
||||||
|
|
||||||
|
HUC3 = 0xFE,
|
||||||
|
|
||||||
|
HUC1_RAM_BATTERY = 0xFF,
|
||||||
|
|
||||||
|
// "Extended" values (still valid, but not directly actionable)
|
||||||
|
|
||||||
|
// A high byte of 0x01 means TPP1, the low byte is the requested features
|
||||||
|
// This does not include SRAM, which is instead implied by a non-zero SRAM size
|
||||||
|
// Note: Multiple rumble speeds imply rumble
|
||||||
|
TPP1 = 0x100,
|
||||||
|
TPP1_RUMBLE = 0x101,
|
||||||
|
TPP1_MULTIRUMBLE_RUMBLE = 0x103,
|
||||||
|
TPP1_TIMER = 0x104,
|
||||||
|
TPP1_TIMER_RUMBLE = 0x105,
|
||||||
|
TPP1_TIMER_MULTIRUMBLE_RUMBLE = 0x107,
|
||||||
|
TPP1_BATTERY = 0x108,
|
||||||
|
TPP1_BATTERY_RUMBLE = 0x109,
|
||||||
|
TPP1_BATTERY_MULTIRUMBLE_RUMBLE = 0x10B,
|
||||||
|
TPP1_BATTERY_TIMER = 0x10C,
|
||||||
|
TPP1_BATTERY_TIMER_RUMBLE = 0x10D,
|
||||||
|
TPP1_BATTERY_TIMER_MULTIRUMBLE_RUMBLE = 0x10F,
|
||||||
|
|
||||||
|
// Error values
|
||||||
|
MBC_NONE = UNSPECIFIED, // No MBC specified, do not act on it
|
||||||
|
};
|
||||||
|
|
||||||
|
bool mbc_HasRAM(MbcType type);
|
||||||
|
char const *mbc_Name(MbcType type);
|
||||||
|
MbcType mbc_ParseName(char const *name, uint8_t &tpp1Major, uint8_t &tpp1Minor);
|
||||||
|
|
||||||
|
#endif // RGBDS_FIX_MBC_HPP
|
||||||
44
include/fix/warning.hpp
Normal file
44
include/fix/warning.hpp
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#ifndef RGBDS_FIX_WARNING_HPP
|
||||||
|
#define RGBDS_FIX_WARNING_HPP
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "diagnostics.hpp"
|
||||||
|
|
||||||
|
enum WarningLevel {
|
||||||
|
LEVEL_DEFAULT, // Warnings that are enabled by default
|
||||||
|
LEVEL_ALL, // Warnings that probably indicate an error
|
||||||
|
LEVEL_EVERYTHING, // Literally every warning
|
||||||
|
};
|
||||||
|
|
||||||
|
enum WarningID {
|
||||||
|
WARNING_MBC, // Issues with MBC specs
|
||||||
|
WARNING_OBSOLETE, // Obsolete/deprecated things
|
||||||
|
WARNING_OVERWRITE, // Overwriting non-zero bytes
|
||||||
|
WARNING_SGB, // SGB flag conflicts with old licensee code
|
||||||
|
WARNING_TRUNCATION, // Truncating values to fit
|
||||||
|
|
||||||
|
NB_PLAIN_WARNINGS,
|
||||||
|
|
||||||
|
NB_WARNINGS = NB_PLAIN_WARNINGS,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Diagnostics<WarningLevel, WarningID> warnings;
|
||||||
|
|
||||||
|
// Warns the user about problems that don't prevent fixing the ROM header
|
||||||
|
[[gnu::format(printf, 2, 3)]]
|
||||||
|
void warning(WarningID id, char const *fmt, ...);
|
||||||
|
|
||||||
|
// Prints an error, and increments the error count
|
||||||
|
[[gnu::format(printf, 1, 2)]]
|
||||||
|
void error(char const *fmt, ...);
|
||||||
|
|
||||||
|
// Prints an error, and exits with failure
|
||||||
|
[[gnu::format(printf, 1, 2), noreturn]]
|
||||||
|
void fatal(char const *fmt, ...);
|
||||||
|
|
||||||
|
uint32_t checkErrors(char const *filename);
|
||||||
|
|
||||||
|
#endif // RGBDS_FIX_WARNING_HPP
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_GFX_PROTO_PALETTE_HPP
|
#ifndef RGBDS_GFX_COLOR_SET_HPP
|
||||||
#define RGBDS_GFX_PROTO_PALETTE_HPP
|
#define RGBDS_GFX_COLOR_SET_HPP
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
class ProtoPalette {
|
class ColorSet {
|
||||||
public:
|
public:
|
||||||
static constexpr size_t capacity = 4;
|
static constexpr size_t capacity = 4;
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ public:
|
|||||||
WE_BIGGER,
|
WE_BIGGER,
|
||||||
THEY_BIGGER = -1,
|
THEY_BIGGER = -1,
|
||||||
};
|
};
|
||||||
ComparisonResult compare(ProtoPalette const &other) const;
|
ComparisonResult compare(ColorSet const &other) const;
|
||||||
|
|
||||||
size_t size() const;
|
size_t size() const;
|
||||||
bool empty() const;
|
bool empty() const;
|
||||||
@@ -35,4 +35,4 @@ public:
|
|||||||
decltype(_colorIndices)::const_iterator end() const;
|
decltype(_colorIndices)::const_iterator end() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // RGBDS_GFX_PROTO_PALETTE_HPP
|
#endif // RGBDS_GFX_COLOR_SET_HPP
|
||||||
@@ -5,9 +5,9 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
@@ -20,7 +20,6 @@ struct Options {
|
|||||||
bool allowMirroringX = false; // -X, -m
|
bool allowMirroringX = false; // -X, -m
|
||||||
bool allowMirroringY = false; // -Y, -m
|
bool allowMirroringY = false; // -Y, -m
|
||||||
bool columnMajor = false; // -Z
|
bool columnMajor = false; // -Z
|
||||||
uint8_t verbosity = 0; // -v
|
|
||||||
|
|
||||||
std::string attrmap{}; // -a, -A
|
std::string attrmap{}; // -a, -A
|
||||||
std::optional<Rgba> bgColor{}; // -B
|
std::optional<Rgba> bgColor{}; // -B
|
||||||
@@ -29,8 +28,10 @@ struct Options {
|
|||||||
NO_SPEC,
|
NO_SPEC,
|
||||||
EXPLICIT,
|
EXPLICIT,
|
||||||
EMBEDDED,
|
EMBEDDED,
|
||||||
|
DMG,
|
||||||
} palSpecType = NO_SPEC; // -c
|
} palSpecType = NO_SPEC; // -c
|
||||||
std::vector<std::array<std::optional<Rgba>, 4>> palSpec{};
|
std::vector<std::array<std::optional<Rgba>, 4>> palSpec{};
|
||||||
|
uint8_t palSpecDmg = 0;
|
||||||
uint8_t bitDepth = 2; // -d
|
uint8_t bitDepth = 2; // -d
|
||||||
std::string inputTileset{}; // -i
|
std::string inputTileset{}; // -i
|
||||||
struct {
|
struct {
|
||||||
@@ -38,7 +39,11 @@ struct Options {
|
|||||||
uint16_t top;
|
uint16_t top;
|
||||||
uint16_t width;
|
uint16_t width;
|
||||||
uint16_t height;
|
uint16_t height;
|
||||||
|
uint32_t right() const { return left + width * 8; }
|
||||||
|
uint32_t bottom() const { return top + height * 8; }
|
||||||
|
bool specified() const { return left || top || width || height; }
|
||||||
} inputSlice{0, 0, 0, 0}; // -L (margins in clockwise order, like CSS)
|
} inputSlice{0, 0, 0, 0}; // -L (margins in clockwise order, like CSS)
|
||||||
|
uint8_t basePalID = 0; // -l
|
||||||
std::array<uint16_t, 2> maxNbTiles{UINT16_MAX, 0}; // -N
|
std::array<uint16_t, 2> maxNbTiles{UINT16_MAX, 0}; // -N
|
||||||
uint16_t nbPalettes = 8; // -n
|
uint16_t nbPalettes = 8; // -n
|
||||||
std::string output{}; // -o
|
std::string output{}; // -o
|
||||||
@@ -51,43 +56,20 @@ struct Options {
|
|||||||
|
|
||||||
std::string input{}; // positional arg
|
std::string input{}; // positional arg
|
||||||
|
|
||||||
// clang-format off: vertically align values
|
|
||||||
static constexpr uint8_t VERB_NONE = 0; // Normal, no extra output
|
|
||||||
static constexpr uint8_t VERB_CFG = 1; // Print configuration after parsing options
|
|
||||||
static constexpr uint8_t VERB_LOG_ACT = 2; // Log actions before doing them
|
|
||||||
static constexpr uint8_t VERB_INTERM = 3; // Print some intermediate results
|
|
||||||
static constexpr uint8_t VERB_DEBUG = 4; // Internals are logged
|
|
||||||
static constexpr uint8_t VERB_TRACE = 5; // Step-by-step algorithm details
|
|
||||||
static constexpr uint8_t VERB_VVVVVV = 6; // What, can't I have a little fun?
|
|
||||||
// clang-format on
|
|
||||||
[[gnu::format(printf, 3, 4)]]
|
|
||||||
void verbosePrint(uint8_t level, char const *fmt, ...) const;
|
|
||||||
|
|
||||||
mutable bool hasTransparentPixels = false;
|
mutable bool hasTransparentPixels = false;
|
||||||
uint8_t maxOpaqueColors() const { return nbColorsPerPal - hasTransparentPixels; }
|
uint8_t maxOpaqueColors() const { return nbColorsPerPal - hasTransparentPixels; }
|
||||||
|
|
||||||
|
uint16_t maxNbColors() const { return nbColorsPerPal * nbPalettes; }
|
||||||
|
|
||||||
|
uint8_t dmgColors[4] = {};
|
||||||
|
uint8_t dmgValue(uint8_t i) const {
|
||||||
|
assume(i < 4);
|
||||||
|
return (palSpecDmg >> (2 * i)) & 0b11;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Options options;
|
extern Options options;
|
||||||
|
|
||||||
// Prints the error count, and exits with failure
|
|
||||||
[[noreturn]]
|
|
||||||
void giveUp();
|
|
||||||
// If any error has been emitted thus far, calls `giveUp()`.
|
|
||||||
void requireZeroErrors();
|
|
||||||
// Prints a warning, and does not change the error count
|
|
||||||
[[gnu::format(printf, 1, 2)]]
|
|
||||||
void warning(char const *fmt, ...);
|
|
||||||
// Prints an error, and increments the error count
|
|
||||||
[[gnu::format(printf, 1, 2)]]
|
|
||||||
void error(char const *fmt, ...);
|
|
||||||
// Prints an error, and increments the error count
|
|
||||||
// Does not take format arguments so `format_` and `-Wformat-security` won't complain about
|
|
||||||
// calling `errorMessage(msg)`.
|
|
||||||
void errorMessage(char const *msg);
|
|
||||||
// Prints a fatal error, increments the error count, and gives up
|
|
||||||
[[gnu::format(printf, 1, 2), noreturn]]
|
|
||||||
void fatal(char const *fmt, ...);
|
|
||||||
|
|
||||||
struct Palette {
|
struct Palette {
|
||||||
// An array of 4 GBC-native (RGB555) colors
|
// An array of 4 GBC-native (RGB555) colors
|
||||||
std::array<uint16_t, 4> colors{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX};
|
std::array<uint16_t, 4> colors{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX};
|
||||||
@@ -106,9 +88,9 @@ struct Palette {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Flipping tends to happen fairly often, so take a bite out of dcache to speed it up
|
// Flipping tends to happen fairly often, so take a bite out of dcache to speed it up
|
||||||
static constexpr auto flipTable = ([]() constexpr {
|
static std::array<uint16_t, 256> flipTable = ([]() constexpr {
|
||||||
std::array<uint16_t, 256> table{};
|
std::array<uint16_t, 256> table{};
|
||||||
for (uint16_t i = 0; i < table.size(); i++) {
|
for (uint16_t i = 0; i < table.size(); ++i) {
|
||||||
// To flip all the bits, we'll flip both nibbles, then each nibble half, etc.
|
// To flip all the bits, we'll flip both nibbles, then each nibble half, etc.
|
||||||
uint16_t byte = i;
|
uint16_t byte = i;
|
||||||
byte = (byte & 0b0000'1111) << 4 | (byte & 0b1111'0000) >> 4;
|
byte = (byte & 0b0000'1111) << 4 | (byte & 0b1111'0000) >> 4;
|
||||||
@@ -119,27 +101,4 @@ static constexpr auto flipTable = ([]() constexpr {
|
|||||||
return table;
|
return table;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// Parsing helpers.
|
|
||||||
|
|
||||||
static constexpr uint8_t nibble(char c) {
|
|
||||||
if (c >= 'a') {
|
|
||||||
assume(c <= 'f');
|
|
||||||
return c - 'a' + 10;
|
|
||||||
} else if (c >= 'A') {
|
|
||||||
assume(c <= 'F');
|
|
||||||
return c - 'A' + 10;
|
|
||||||
} else {
|
|
||||||
assume(c >= '0' && c <= '9');
|
|
||||||
return c - '0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr uint8_t toHex(char c1, char c2) {
|
|
||||||
return nibble(c1) * 16 + nibble(c2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr uint8_t singleToHex(char c) {
|
|
||||||
return toHex(c, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // RGBDS_GFX_MAIN_HPP
|
#endif // RGBDS_GFX_MAIN_HPP
|
||||||
|
|||||||
@@ -3,16 +3,14 @@
|
|||||||
#ifndef RGBDS_GFX_PAL_PACKING_HPP
|
#ifndef RGBDS_GFX_PAL_PACKING_HPP
|
||||||
#define RGBDS_GFX_PAL_PACKING_HPP
|
#define RGBDS_GFX_PAL_PACKING_HPP
|
||||||
|
|
||||||
#include <tuple>
|
#include <stddef.h>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "defaultinitvec.hpp"
|
|
||||||
|
|
||||||
struct Palette;
|
struct Palette;
|
||||||
class ProtoPalette;
|
class ColorSet;
|
||||||
|
|
||||||
// Returns which palette each proto-palette maps to, and how many palettes are necessary
|
// Returns which palette each color set maps to, and how many palettes are necessary
|
||||||
std::tuple<DefaultInitVec<size_t>, size_t>
|
std::pair<std::vector<size_t>, size_t> overloadAndRemove(std::vector<ColorSet> const &colorSets);
|
||||||
overloadAndRemove(std::vector<ProtoPalette> const &protoPalettes);
|
|
||||||
|
|
||||||
#endif // RGBDS_GFX_PAL_PACKING_HPP
|
#endif // RGBDS_GFX_PAL_PACKING_HPP
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <png.h>
|
#include <stddef.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "gfx/rgba.hpp"
|
#include "gfx/rgba.hpp"
|
||||||
@@ -16,13 +16,7 @@ static constexpr size_t NB_COLOR_SLOTS = (1 << (5 * 3)) + 1;
|
|||||||
|
|
||||||
struct Palette;
|
struct Palette;
|
||||||
|
|
||||||
void sortIndexed(
|
void sortIndexed(std::vector<Palette> &palettes, std::vector<Rgba> const &embPal);
|
||||||
std::vector<Palette> &palettes,
|
|
||||||
int palSize,
|
|
||||||
png_color const *palRGB,
|
|
||||||
int palAlphaSize,
|
|
||||||
png_byte *palAlpha
|
|
||||||
);
|
|
||||||
void sortGrayscale(
|
void sortGrayscale(
|
||||||
std::vector<Palette> &palettes, std::array<std::optional<Rgba>, NB_COLOR_SLOTS> const &colors
|
std::vector<Palette> &palettes, std::array<std::optional<Rgba>, NB_COLOR_SLOTS> const &colors
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,7 +3,13 @@
|
|||||||
#ifndef RGBDS_GFX_PAL_SPEC_HPP
|
#ifndef RGBDS_GFX_PAL_SPEC_HPP
|
||||||
#define RGBDS_GFX_PAL_SPEC_HPP
|
#define RGBDS_GFX_PAL_SPEC_HPP
|
||||||
|
|
||||||
void parseInlinePalSpec(char const * const arg);
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void parseInlinePalSpec(char const * const rawArg);
|
||||||
void parseExternalPalSpec(char const *arg);
|
void parseExternalPalSpec(char const *arg);
|
||||||
|
void parseDmgPalSpec(char const * const rawArg);
|
||||||
|
void parseDmgPalSpec(uint8_t palSpecDmg);
|
||||||
|
|
||||||
|
void parseBackgroundPalSpec(char const *arg);
|
||||||
|
|
||||||
#endif // RGBDS_GFX_PAL_SPEC_HPP
|
#endif // RGBDS_GFX_PAL_SPEC_HPP
|
||||||
|
|||||||
21
include/gfx/png.hpp
Normal file
21
include/gfx/png.hpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#ifndef RGBDS_GFX_PNG_HPP
|
||||||
|
#define RGBDS_GFX_PNG_HPP
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <streambuf>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "gfx/rgba.hpp"
|
||||||
|
|
||||||
|
struct Png {
|
||||||
|
uint32_t width, height;
|
||||||
|
std::vector<Rgba> pixels{};
|
||||||
|
std::vector<Rgba> palette{};
|
||||||
|
|
||||||
|
Png() {}
|
||||||
|
Png(char const *filename, std::streambuf &file);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // RGBDS_GFX_PNG_HPP
|
||||||
@@ -17,23 +17,25 @@ struct Rgba {
|
|||||||
explicit constexpr Rgba(uint32_t rgba = 0)
|
explicit constexpr Rgba(uint32_t rgba = 0)
|
||||||
: red(rgba >> 24), green(rgba >> 16), blue(rgba >> 8), alpha(rgba) {}
|
: red(rgba >> 24), green(rgba >> 16), blue(rgba >> 8), alpha(rgba) {}
|
||||||
|
|
||||||
static constexpr Rgba fromCGBColor(uint16_t cgbColor) {
|
static constexpr Rgba fromCGBColor(uint16_t color) {
|
||||||
constexpr auto _5to8 = [](uint8_t fiveBpp) -> uint8_t {
|
constexpr auto _5to8 = [](uint8_t channel) -> uint8_t {
|
||||||
fiveBpp &= 0b11111; // For caller's convenience
|
channel &= 0b11111; // For caller's convenience
|
||||||
return fiveBpp << 3 | fiveBpp >> 2;
|
return channel << 3 | channel >> 2;
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
_5to8(cgbColor),
|
_5to8(color),
|
||||||
_5to8(cgbColor >> 5),
|
_5to8(color >> 5),
|
||||||
_5to8(cgbColor >> 10),
|
_5to8(color >> 10),
|
||||||
static_cast<uint8_t>(cgbColor & 0x8000 ? 0x00 : 0xFF),
|
static_cast<uint8_t>(color & 0x8000 ? 0x00 : 0xFF),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns this RGBA as a 32-bit number that can be printed in hex (`%08x`) to yield its CSS
|
// Returns this RGBA as a 32-bit number that can be printed in hex (`%08x`) to yield its CSS
|
||||||
// representation
|
// representation
|
||||||
uint32_t toCSS() const {
|
uint32_t toCSS() const {
|
||||||
auto shl = [](uint8_t val, unsigned shift) { return static_cast<uint32_t>(val) << shift; };
|
constexpr 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);
|
return shl(red, 24) | shl(green, 16) | shl(blue, 8) | shl(alpha, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
45
include/gfx/warning.hpp
Normal file
45
include/gfx/warning.hpp
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#ifndef RGBDS_GFX_WARNING_HPP
|
||||||
|
#define RGBDS_GFX_WARNING_HPP
|
||||||
|
|
||||||
|
#include "diagnostics.hpp"
|
||||||
|
|
||||||
|
enum WarningLevel {
|
||||||
|
LEVEL_DEFAULT, // Warnings that are enabled by default
|
||||||
|
LEVEL_ALL, // Warnings that probably indicate an error
|
||||||
|
LEVEL_EVERYTHING, // Literally every warning
|
||||||
|
};
|
||||||
|
|
||||||
|
enum WarningID {
|
||||||
|
WARNING_EMBEDDED, // Using an embedded PNG palette without '-c embedded'
|
||||||
|
WARNING_OBSOLETE, // Obsolete/deprecated things
|
||||||
|
WARNING_TRIM_NONEMPTY, // '-x' trims nonempty tiles
|
||||||
|
|
||||||
|
NB_PLAIN_WARNINGS,
|
||||||
|
|
||||||
|
NB_WARNINGS = NB_PLAIN_WARNINGS,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Diagnostics<WarningLevel, WarningID> warnings;
|
||||||
|
|
||||||
|
// Warns the user about problems that don't prevent valid graphics conversion
|
||||||
|
[[gnu::format(printf, 2, 3)]]
|
||||||
|
void warning(WarningID id, char const *fmt, ...);
|
||||||
|
|
||||||
|
// Prints the error count, and exits with failure
|
||||||
|
[[noreturn]]
|
||||||
|
void giveUp();
|
||||||
|
|
||||||
|
// If any error has been emitted thus far, calls `giveUp()`
|
||||||
|
void requireZeroErrors();
|
||||||
|
|
||||||
|
// Prints an error, and increments the error count
|
||||||
|
[[gnu::format(printf, 1, 2)]]
|
||||||
|
void error(char const *fmt, ...);
|
||||||
|
|
||||||
|
// Prints a fatal error, increments the error count, and gives up
|
||||||
|
[[gnu::format(printf, 1, 2), noreturn]]
|
||||||
|
void fatal(char const *fmt, ...);
|
||||||
|
|
||||||
|
#endif // RGBDS_GFX_WARNING_HPP
|
||||||
@@ -71,7 +71,7 @@ static inline int ctz(unsigned int x) {
|
|||||||
|
|
||||||
while (!(x & 1)) {
|
while (!(x & 1)) {
|
||||||
x >>= 1;
|
x >>= 1;
|
||||||
cnt++;
|
++cnt;
|
||||||
}
|
}
|
||||||
return cnt;
|
return cnt;
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ static inline int clz(unsigned int x) {
|
|||||||
|
|
||||||
while (x <= UINT_MAX / 2) {
|
while (x <= UINT_MAX / 2) {
|
||||||
x <<= 1;
|
x <<= 1;
|
||||||
cnt++;
|
++cnt;
|
||||||
}
|
}
|
||||||
return cnt;
|
return cnt;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,57 @@
|
|||||||
#ifndef RGBDS_ITERTOOLS_HPP
|
#ifndef RGBDS_ITERTOOLS_HPP
|
||||||
#define RGBDS_ITERTOOLS_HPP
|
#define RGBDS_ITERTOOLS_HPP
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
#include <optional>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class InsertionOrderedMap {
|
||||||
|
std::deque<T> list;
|
||||||
|
std::unordered_map<std::string, size_t> map; // Indexes into `list`
|
||||||
|
|
||||||
|
public:
|
||||||
|
size_t size() const { return list.size(); }
|
||||||
|
|
||||||
|
bool empty() const { return list.empty(); }
|
||||||
|
|
||||||
|
bool contains(std::string const &name) const { return map.find(name) != map.end(); }
|
||||||
|
|
||||||
|
T &operator[](size_t i) { return list[i]; }
|
||||||
|
|
||||||
|
typename decltype(list)::iterator begin() { return list.begin(); }
|
||||||
|
typename decltype(list)::iterator end() { return list.end(); }
|
||||||
|
typename decltype(list)::const_iterator begin() const { return list.begin(); }
|
||||||
|
typename decltype(list)::const_iterator end() const { return list.end(); }
|
||||||
|
|
||||||
|
T &add(std::string const &name) {
|
||||||
|
map[name] = list.size();
|
||||||
|
return list.emplace_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
T &add(std::string const &name, T &&value) {
|
||||||
|
map[name] = list.size();
|
||||||
|
list.emplace_back(std::move(value));
|
||||||
|
return list.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
T &addAnonymous() {
|
||||||
|
// Add the new item to the list, but do not update the map
|
||||||
|
return list.emplace_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<size_t> findIndex(std::string const &name) const {
|
||||||
|
if (auto search = map.find(name); search != map.end()) {
|
||||||
|
return search->second;
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class EnumSeq {
|
class EnumSeq {
|
||||||
T _start;
|
T _start;
|
||||||
@@ -22,7 +70,7 @@ class EnumSeq {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto operator*() const { return _value; }
|
T operator*() const { return _value; }
|
||||||
|
|
||||||
bool operator==(Iterator const &rhs) const { return _value == rhs._value; }
|
bool operator==(Iterator const &rhs) const { return _value == rhs._value; }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,10 +3,6 @@
|
|||||||
#ifndef RGBDS_LINK_ASSIGN_HPP
|
#ifndef RGBDS_LINK_ASSIGN_HPP
|
||||||
#define RGBDS_LINK_ASSIGN_HPP
|
#define RGBDS_LINK_ASSIGN_HPP
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
extern uint64_t nbSectionsToAssign;
|
|
||||||
|
|
||||||
// Assigns all sections a slice of the address space
|
// Assigns all sections a slice of the address space
|
||||||
void assign_AssignSections();
|
void assign_AssignSections();
|
||||||
|
|
||||||
|
|||||||
38
include/link/fstack.hpp
Normal file
38
include/link/fstack.hpp
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#ifndef RGBDS_LINK_FSTACK_HPP
|
||||||
|
#define RGBDS_LINK_FSTACK_HPP
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string>
|
||||||
|
#include <variant>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "linkdefs.hpp"
|
||||||
|
|
||||||
|
struct FileStackNode {
|
||||||
|
FileStackNodeType type;
|
||||||
|
std::variant<
|
||||||
|
std::monostate, // Default constructed; `.type` and `.data` must be set manually
|
||||||
|
std::vector<uint32_t>, // NODE_REPT
|
||||||
|
std::string // NODE_FILE, NODE_MACRO
|
||||||
|
>
|
||||||
|
data;
|
||||||
|
bool isQuiet; // Whether to omit this node from error reporting
|
||||||
|
|
||||||
|
FileStackNode *parent;
|
||||||
|
// Line at which the parent context was exited; meaningless for the root level
|
||||||
|
uint32_t lineNo;
|
||||||
|
|
||||||
|
// REPT iteration counts since last named node, in reverse depth order
|
||||||
|
std::vector<uint32_t> &iters() { return std::get<std::vector<uint32_t>>(data); }
|
||||||
|
std::vector<uint32_t> const &iters() const { return std::get<std::vector<uint32_t>>(data); }
|
||||||
|
// File name for files, file::macro name for macros
|
||||||
|
std::string &name() { return std::get<std::string>(data); }
|
||||||
|
std::string const &name() const { return std::get<std::string>(data); }
|
||||||
|
|
||||||
|
void printBacktrace(uint32_t curLineNo) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // RGBDS_LINK_FSTACK_HPP
|
||||||
22
include/link/layout.hpp
Normal file
22
include/link/layout.hpp
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#ifndef RGBDS_LINK_LAYOUT_HPP
|
||||||
|
#define RGBDS_LINK_LAYOUT_HPP
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "linkdefs.hpp"
|
||||||
|
|
||||||
|
void layout_SetFloatingSectionType(SectionType type);
|
||||||
|
void layout_SetSectionType(SectionType type);
|
||||||
|
void layout_SetSectionType(SectionType type, uint32_t bank);
|
||||||
|
|
||||||
|
void layout_SetAddr(uint32_t addr);
|
||||||
|
void layout_MakeAddrFloating();
|
||||||
|
void layout_AlignTo(uint32_t alignment, uint32_t offset);
|
||||||
|
void layout_Pad(uint32_t length);
|
||||||
|
|
||||||
|
void layout_PlaceSection(std::string const &name, bool isOptional);
|
||||||
|
|
||||||
|
#endif // RGBDS_LINK_LAYOUT_HPP
|
||||||
15
include/link/lexer.hpp
Normal file
15
include/link/lexer.hpp
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#ifndef RGBDS_LINK_LEXER_HPP
|
||||||
|
#define RGBDS_LINK_LEXER_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
void lexer_TraceCurrent();
|
||||||
|
|
||||||
|
void lexer_IncludeFile(std::string &&path);
|
||||||
|
void lexer_IncLineNo();
|
||||||
|
|
||||||
|
bool lexer_Init(char const *linkerScriptName);
|
||||||
|
|
||||||
|
#endif // RGBDS_LINK_LEXER_HPP
|
||||||
@@ -4,66 +4,25 @@
|
|||||||
#define RGBDS_LINK_MAIN_HPP
|
#define RGBDS_LINK_MAIN_HPP
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "either.hpp"
|
struct Options {
|
||||||
#include "linkdefs.hpp"
|
bool isDmgMode; // -d
|
||||||
|
char const *mapFileName; // -m
|
||||||
// Variables related to CLI options
|
bool noSymInMap; // -M
|
||||||
extern bool isDmgMode;
|
char const *symFileName; // -n
|
||||||
extern char const *linkerScriptName;
|
char const *overlayFileName; // -O
|
||||||
extern char const *mapFileName;
|
char const *outputFileName; // -o
|
||||||
extern bool noSymInMap;
|
uint8_t padValue; // -p
|
||||||
extern char const *symFileName;
|
bool hasPadValue = false;
|
||||||
extern char const *overlayFileName;
|
// Setting these three to 0 disables the functionality
|
||||||
extern char const *outputFileName;
|
uint16_t scrambleROMX; // -S
|
||||||
extern uint8_t padValue;
|
uint16_t scrambleWRAMX;
|
||||||
extern bool hasPadValue;
|
uint16_t scrambleSRAM;
|
||||||
extern uint16_t scrambleROMX;
|
bool is32kMode; // -t
|
||||||
extern uint8_t scrambleWRAMX;
|
bool isWRAM0Mode; // -w
|
||||||
extern uint8_t scrambleSRAM;
|
bool disablePadding; // -x
|
||||||
extern bool is32kMode;
|
|
||||||
extern bool beVerbose;
|
|
||||||
extern bool isWRAM0Mode;
|
|
||||||
extern bool disablePadding;
|
|
||||||
|
|
||||||
// Helper macro for printing verbose-mode messages
|
|
||||||
#define verbosePrint(...) \
|
|
||||||
do { \
|
|
||||||
if (beVerbose) { \
|
|
||||||
fprintf(stderr, __VA_ARGS__); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
struct FileStackNode {
|
|
||||||
FileStackNodeType type;
|
|
||||||
Either<
|
|
||||||
std::vector<uint32_t>, // NODE_REPT
|
|
||||||
std::string // NODE_FILE, NODE_MACRO
|
|
||||||
>
|
|
||||||
data;
|
|
||||||
|
|
||||||
FileStackNode *parent;
|
|
||||||
// Line at which the parent context was exited; meaningless for the root level
|
|
||||||
uint32_t lineNo;
|
|
||||||
|
|
||||||
// REPT iteration counts since last named node, in reverse depth order
|
|
||||||
std::vector<uint32_t> &iters() { return data.get<std::vector<uint32_t>>(); }
|
|
||||||
std::vector<uint32_t> const &iters() const { return data.get<std::vector<uint32_t>>(); }
|
|
||||||
// File name for files, file::macro name for macros
|
|
||||||
std::string &name() { return data.get<std::string>(); }
|
|
||||||
std::string const &name() const { return data.get<std::string>(); }
|
|
||||||
|
|
||||||
std::string const &dump(uint32_t curLineNo) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
[[gnu::format(printf, 3, 4)]]
|
extern Options options;
|
||||||
void warning(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...);
|
|
||||||
[[gnu::format(printf, 3, 4)]]
|
|
||||||
void error(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...);
|
|
||||||
[[gnu::format(printf, 3, 4), noreturn]]
|
|
||||||
void fatal(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...);
|
|
||||||
|
|
||||||
#endif // RGBDS_LINK_MAIN_HPP
|
#endif // RGBDS_LINK_MAIN_HPP
|
||||||
|
|||||||
@@ -3,6 +3,23 @@
|
|||||||
#ifndef RGBDS_LINK_PATCH_HPP
|
#ifndef RGBDS_LINK_PATCH_HPP
|
||||||
#define RGBDS_LINK_PATCH_HPP
|
#define RGBDS_LINK_PATCH_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "link/section.hpp"
|
||||||
|
|
||||||
|
struct Symbol;
|
||||||
|
|
||||||
|
struct Assertion {
|
||||||
|
Patch patch; // Also used for its `.type`
|
||||||
|
std::string message;
|
||||||
|
// This would be redundant with `patch.pcSection->fileSymbols`, but `section` is sometimes
|
||||||
|
// `nullptr`!
|
||||||
|
std::vector<Symbol> *fileSymbols;
|
||||||
|
};
|
||||||
|
|
||||||
|
Assertion &patch_AddAssertion();
|
||||||
|
|
||||||
// Checks all assertions
|
// Checks all assertions
|
||||||
void patch_CheckAssertions();
|
void patch_CheckAssertions();
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,6 @@
|
|||||||
#ifndef RGBDS_LINK_SECTION_HPP
|
#ifndef RGBDS_LINK_SECTION_HPP
|
||||||
#define RGBDS_LINK_SECTION_HPP
|
#define RGBDS_LINK_SECTION_HPP
|
||||||
|
|
||||||
// GUIDELINE: external code MUST NOT BE AWARE of the data structure used!
|
|
||||||
|
|
||||||
#include <deque>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -13,8 +10,6 @@
|
|||||||
|
|
||||||
#include "linkdefs.hpp"
|
#include "linkdefs.hpp"
|
||||||
|
|
||||||
#include "link/main.hpp"
|
|
||||||
|
|
||||||
struct FileStackNode;
|
struct FileStackNode;
|
||||||
struct Section;
|
struct Section;
|
||||||
struct Symbol;
|
struct Symbol;
|
||||||
@@ -56,15 +51,6 @@ struct Section {
|
|||||||
std::unique_ptr<Section> nextu; // The next "component" of this unionized sect
|
std::unique_ptr<Section> nextu; // The next "component" of this unionized sect
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Assertion {
|
|
||||||
Patch patch; // Also used for its `.type`
|
|
||||||
std::string message;
|
|
||||||
// This would be redundant with `.section->fileSymbols`, but `section` is sometimes `nullptr`!
|
|
||||||
std::vector<Symbol> *fileSymbols;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern std::deque<Assertion> assertions;
|
|
||||||
|
|
||||||
// Execute a callback for each section currently registered.
|
// Execute a callback for each section currently registered.
|
||||||
// This is to avoid exposing the data structure in which sections are stored.
|
// This is to avoid exposing the data structure in which sections are stored.
|
||||||
void sect_ForEach(void (*callback)(Section &));
|
void sect_ForEach(void (*callback)(Section &));
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
#include "either.hpp"
|
|
||||||
#include "linkdefs.hpp"
|
#include "linkdefs.hpp"
|
||||||
|
|
||||||
struct FileStackNode;
|
struct FileStackNode;
|
||||||
@@ -27,14 +27,14 @@ struct Symbol {
|
|||||||
ExportLevel type;
|
ExportLevel type;
|
||||||
FileStackNode const *src;
|
FileStackNode const *src;
|
||||||
int32_t lineNo;
|
int32_t lineNo;
|
||||||
Either<
|
std::variant<
|
||||||
int32_t, // Constants just have a numeric value
|
int32_t, // Constants just have a numeric value
|
||||||
Label // Label values refer to an offset within a specific section
|
Label // Label values refer to an offset within a specific section
|
||||||
>
|
>
|
||||||
data;
|
data;
|
||||||
|
|
||||||
Label &label() { return data.get<Label>(); }
|
Label &label() { return std::get<Label>(data); }
|
||||||
Label const &label() const { return data.get<Label>(); }
|
Label const &label() const { return std::get<Label>(data); }
|
||||||
};
|
};
|
||||||
|
|
||||||
void sym_ForEach(void (*callback)(Symbol &));
|
void sym_ForEach(void (*callback)(Symbol &));
|
||||||
@@ -44,6 +44,6 @@ void sym_AddSymbol(Symbol &symbol);
|
|||||||
// Finds a symbol in all the defined symbols.
|
// Finds a symbol in all the defined symbols.
|
||||||
Symbol *sym_GetSymbol(std::string const &name);
|
Symbol *sym_GetSymbol(std::string const &name);
|
||||||
|
|
||||||
void sym_DumpLocalAliasedSymbols(std::string const &name);
|
void sym_TraceLocalAliasedSymbols(std::string const &name);
|
||||||
|
|
||||||
#endif // RGBDS_LINK_SYMBOL_HPP
|
#endif // RGBDS_LINK_SYMBOL_HPP
|
||||||
|
|||||||
75
include/link/warning.hpp
Normal file
75
include/link/warning.hpp
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#ifndef RGBDS_LINK_WARNING_HPP
|
||||||
|
#define RGBDS_LINK_WARNING_HPP
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "diagnostics.hpp"
|
||||||
|
|
||||||
|
#define warningAt(where, ...) warning((where).src, (where).lineNo, __VA_ARGS__)
|
||||||
|
#define errorAt(where, ...) error((where).src, (where).lineNo, __VA_ARGS__)
|
||||||
|
#define fatalAt(where, ...) fatal((where).src, (where).lineNo, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define fatalTwoAt(where1, where2, ...) \
|
||||||
|
fatalTwo(*(where1).src, (where1).lineNo, *(where2).src, (where2).lineNo, __VA_ARGS__)
|
||||||
|
|
||||||
|
enum WarningLevel {
|
||||||
|
LEVEL_DEFAULT, // Warnings that are enabled by default
|
||||||
|
LEVEL_ALL, // Warnings that probably indicate an error
|
||||||
|
LEVEL_EVERYTHING, // Literally every warning
|
||||||
|
};
|
||||||
|
|
||||||
|
enum WarningID {
|
||||||
|
WARNING_ASSERT, // Assertions
|
||||||
|
WARNING_DIV, // Undefined division behavior
|
||||||
|
WARNING_OBSOLETE, // Obsolete/deprecated things
|
||||||
|
WARNING_SHIFT, // Undefined `SHIFT` behavior
|
||||||
|
WARNING_SHIFT_AMOUNT, // Strange `SHIFT` amount
|
||||||
|
|
||||||
|
NB_PLAIN_WARNINGS,
|
||||||
|
|
||||||
|
// Implicit truncation loses some bits
|
||||||
|
WARNING_TRUNCATION_1 = NB_PLAIN_WARNINGS,
|
||||||
|
WARNING_TRUNCATION_2,
|
||||||
|
|
||||||
|
NB_WARNINGS,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Diagnostics<WarningLevel, WarningID> warnings;
|
||||||
|
|
||||||
|
struct FileStackNode;
|
||||||
|
|
||||||
|
[[gnu::format(printf, 4, 5)]]
|
||||||
|
void warning(FileStackNode const *src, uint32_t lineNo, WarningID id, char const *fmt, ...);
|
||||||
|
[[gnu::format(printf, 3, 4)]]
|
||||||
|
void warning(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...);
|
||||||
|
[[gnu::format(printf, 1, 2)]]
|
||||||
|
void warning(char const *fmt, ...);
|
||||||
|
|
||||||
|
[[gnu::format(printf, 3, 4)]]
|
||||||
|
void error(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...);
|
||||||
|
[[gnu::format(printf, 1, 2)]]
|
||||||
|
void error(char const *fmt, ...);
|
||||||
|
[[gnu::format(printf, 1, 2)]]
|
||||||
|
void scriptError(char const *fmt, ...);
|
||||||
|
|
||||||
|
[[gnu::format(printf, 3, 4), noreturn]]
|
||||||
|
void fatal(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...);
|
||||||
|
[[gnu::format(printf, 1, 2), noreturn]]
|
||||||
|
void fatal(char const *fmt, ...);
|
||||||
|
|
||||||
|
[[gnu::format(printf, 5, 6), noreturn]]
|
||||||
|
void fatalTwo(
|
||||||
|
FileStackNode const &src1,
|
||||||
|
uint32_t lineNo1,
|
||||||
|
FileStackNode const &src2,
|
||||||
|
uint32_t lineNo2,
|
||||||
|
char const *fmt,
|
||||||
|
...
|
||||||
|
);
|
||||||
|
|
||||||
|
void requireZeroErrors();
|
||||||
|
|
||||||
|
#endif // RGBDS_LINK_WARNING_HPP
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
#include "helpers.hpp" // assume
|
#include "helpers.hpp" // assume
|
||||||
|
|
||||||
#define RGBDS_OBJECT_VERSION_STRING "RGB9"
|
#define RGBDS_OBJECT_VERSION_STRING "RGB9"
|
||||||
#define RGBDS_OBJECT_REV 12U
|
#define RGBDS_OBJECT_REV 13U
|
||||||
|
|
||||||
enum AssertionType { ASSERT_WARN, ASSERT_ERROR, ASSERT_FATAL };
|
enum AssertionType { ASSERT_WARN, ASSERT_ERROR, ASSERT_FATAL };
|
||||||
|
|
||||||
@@ -78,12 +78,18 @@ enum SectionType {
|
|||||||
SECTTYPE_INVALID
|
SECTTYPE_INVALID
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static constexpr uint8_t SECTTYPE_TYPE_MASK = 0b111;
|
||||||
|
static constexpr uint8_t SECTTYPE_UNION_BIT = 7;
|
||||||
|
static constexpr uint8_t SECTTYPE_FRAGMENT_BIT = 6;
|
||||||
|
|
||||||
enum FileStackNodeType {
|
enum FileStackNodeType {
|
||||||
NODE_REPT,
|
NODE_REPT,
|
||||||
NODE_FILE,
|
NODE_FILE,
|
||||||
NODE_MACRO,
|
NODE_MACRO,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static constexpr uint8_t FSTACKNODE_QUIET_BIT = 7;
|
||||||
|
|
||||||
// Nont-`const` members may be patched in RGBLINK depending on CLI flags
|
// Nont-`const` members may be patched in RGBLINK depending on CLI flags
|
||||||
extern struct SectionTypeInfo {
|
extern struct SectionTypeInfo {
|
||||||
std::string const name;
|
std::string const name;
|
||||||
@@ -95,18 +101,18 @@ extern struct SectionTypeInfo {
|
|||||||
|
|
||||||
// Tells whether a section has data in its object file definition,
|
// Tells whether a section has data in its object file definition,
|
||||||
// depending on type.
|
// depending on type.
|
||||||
static inline bool sect_HasData(SectionType type) {
|
static inline bool sectTypeHasData(SectionType type) {
|
||||||
assume(type != SECTTYPE_INVALID);
|
assume(type != SECTTYPE_INVALID);
|
||||||
return type == SECTTYPE_ROM0 || type == SECTTYPE_ROMX;
|
return type == SECTTYPE_ROM0 || type == SECTTYPE_ROMX;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a memory region's end address (last byte), e.g. 0x7FFF
|
// Returns a memory region's end address (last byte), e.g. 0x7FFF
|
||||||
static inline uint16_t endaddr(SectionType type) {
|
static inline uint16_t sectTypeEndAddr(SectionType type) {
|
||||||
return sectionTypeInfo[type].startAddr + sectionTypeInfo[type].size - 1;
|
return sectionTypeInfo[type].startAddr + sectionTypeInfo[type].size - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a memory region's number of banks, or 1 for regions without banking
|
// Returns a memory region's number of banks, or 1 for regions without banking
|
||||||
static inline uint32_t nbbanks(SectionType type) {
|
static inline uint32_t sectTypeBanks(SectionType type) {
|
||||||
return sectionTypeInfo[type].lastBank - sectionTypeInfo[type].firstBank + 1;
|
return sectionTypeInfo[type].lastBank - sectionTypeInfo[type].firstBank + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,17 @@
|
|||||||
int32_t op_divide(int32_t dividend, int32_t divisor);
|
int32_t op_divide(int32_t dividend, int32_t divisor);
|
||||||
int32_t op_modulo(int32_t dividend, int32_t divisor);
|
int32_t op_modulo(int32_t dividend, int32_t divisor);
|
||||||
int32_t op_exponent(int32_t base, uint32_t power);
|
int32_t op_exponent(int32_t base, uint32_t power);
|
||||||
|
|
||||||
int32_t op_shift_left(int32_t value, int32_t amount);
|
int32_t op_shift_left(int32_t value, int32_t amount);
|
||||||
int32_t op_shift_right(int32_t value, int32_t amount);
|
int32_t op_shift_right(int32_t value, int32_t amount);
|
||||||
int32_t op_shift_right_unsigned(int32_t value, int32_t amount);
|
int32_t op_shift_right_unsigned(int32_t value, int32_t amount);
|
||||||
|
|
||||||
|
int32_t op_neg(int32_t value);
|
||||||
|
|
||||||
|
int32_t op_high(int32_t value);
|
||||||
|
int32_t op_low(int32_t value);
|
||||||
|
|
||||||
|
int32_t op_bitwidth(int32_t value);
|
||||||
|
int32_t op_tzcount(int32_t value);
|
||||||
|
|
||||||
#endif // RGBDS_OP_MATH_HPP
|
#endif // RGBDS_OP_MATH_HPP
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
// platform-specific hacks
|
|
||||||
|
|
||||||
#ifndef RGBDS_PLATFORM_HPP
|
#ifndef RGBDS_PLATFORM_HPP
|
||||||
#define RGBDS_PLATFORM_HPP
|
#define RGBDS_PLATFORM_HPP
|
||||||
|
|
||||||
@@ -30,6 +28,7 @@
|
|||||||
#define STDERR_FILENO 2
|
#define STDERR_FILENO 2
|
||||||
#define ssize_t int
|
#define ssize_t int
|
||||||
#define SSIZE_MAX INT_MAX
|
#define SSIZE_MAX INT_MAX
|
||||||
|
#define isatty _isatty
|
||||||
#else
|
#else
|
||||||
#include <fcntl.h> // IWYU pragma: export
|
#include <fcntl.h> // IWYU pragma: export
|
||||||
#include <limits.h> // IWYU pragma: export
|
#include <limits.h> // IWYU pragma: export
|
||||||
|
|||||||
42
include/style.hpp
Normal file
42
include/style.hpp
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#ifndef RGBDS_STYLE_HPP
|
||||||
|
#define RGBDS_STYLE_HPP
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) || defined(__MINGW32__) || defined(__CYGWIN__)
|
||||||
|
#define STYLE_ANSI 0
|
||||||
|
#else
|
||||||
|
#define STYLE_ANSI 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum StyleColor {
|
||||||
|
#if STYLE_ANSI
|
||||||
|
// Values analogous to ANSI foreground and background SGR colors
|
||||||
|
STYLE_BLACK,
|
||||||
|
STYLE_RED,
|
||||||
|
STYLE_GREEN,
|
||||||
|
STYLE_YELLOW,
|
||||||
|
STYLE_BLUE,
|
||||||
|
STYLE_MAGENTA,
|
||||||
|
STYLE_CYAN,
|
||||||
|
STYLE_GRAY,
|
||||||
|
#else
|
||||||
|
// Values analogous to `FOREGROUND_*` constants from `windows.h`
|
||||||
|
STYLE_BLACK,
|
||||||
|
STYLE_BLUE, // bit 0
|
||||||
|
STYLE_GREEN, // bit 1
|
||||||
|
STYLE_CYAN, // STYLE_BLUE | STYLE_GREEN
|
||||||
|
STYLE_RED, // bit 2
|
||||||
|
STYLE_MAGENTA, // STYLE_BLUE | STYLE_RED
|
||||||
|
STYLE_YELLOW, // STYLE_GREEN | STYLE_RED
|
||||||
|
STYLE_GRAY, // STYLE_BLUE | STYLE_GREEN | STYLE_RED
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
bool style_Parse(char const *arg);
|
||||||
|
void style_Set(FILE *file, StyleColor color, bool bold);
|
||||||
|
void style_Reset(FILE *file);
|
||||||
|
|
||||||
|
#endif // RGBDS_STYLE_HPP
|
||||||
23
include/usage.hpp
Normal file
23
include/usage.hpp
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#ifndef RGBDS_USAGE_HPP
|
||||||
|
#define RGBDS_USAGE_HPP
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct Usage {
|
||||||
|
std::string name;
|
||||||
|
std::vector<std::string> flags;
|
||||||
|
std::vector<std::pair<std::vector<std::string>, std::vector<std::string>>> options;
|
||||||
|
|
||||||
|
[[noreturn]]
|
||||||
|
void printAndExit(int code) const;
|
||||||
|
|
||||||
|
[[gnu::format(printf, 2, 3), noreturn]]
|
||||||
|
void printAndExit(char const *fmt, ...) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // RGBDS_USAGE_HPP
|
||||||
@@ -3,6 +3,47 @@
|
|||||||
#ifndef RGBDS_UTIL_HPP
|
#ifndef RGBDS_UTIL_HPP
|
||||||
#define RGBDS_UTIL_HPP
|
#define RGBDS_UTIL_HPP
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <numeric>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "helpers.hpp"
|
||||||
|
|
||||||
|
bool isNewline(int c);
|
||||||
|
bool isBlankSpace(int c);
|
||||||
|
bool isWhitespace(int c);
|
||||||
|
bool isPrintable(int c);
|
||||||
|
bool isLetter(int c);
|
||||||
|
bool isDigit(int c);
|
||||||
|
bool isOctDigit(int c);
|
||||||
|
bool isHexDigit(int c);
|
||||||
|
bool isAlphanumeric(int c);
|
||||||
|
|
||||||
|
bool startsIdentifier(int c);
|
||||||
|
bool continuesIdentifier(int c);
|
||||||
|
|
||||||
char const *printChar(int c);
|
char const *printChar(int c);
|
||||||
|
|
||||||
|
struct Uppercase {
|
||||||
|
// FNV-1a hash of an uppercased string
|
||||||
|
constexpr size_t operator()(std::string const &str) const {
|
||||||
|
return std::accumulate(RANGE(str), 0x811C9DC5, [](size_t hash, char c) {
|
||||||
|
return (hash ^ toupper(c)) * 16777619;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare two strings without case-sensitivity (by converting to uppercase)
|
||||||
|
constexpr bool operator()(std::string const &str1, std::string const &str2) const {
|
||||||
|
return std::equal(RANGE(str1), RANGE(str2), [](char c1, char c2) {
|
||||||
|
return toupper(c1) == toupper(c2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using UpperMap = std::unordered_map<std::string, T, Uppercase, Uppercase>;
|
||||||
|
|
||||||
#endif // RGBDS_UTIL_HPP
|
#endif // RGBDS_UTIL_HPP
|
||||||
|
|||||||
33
include/verbosity.hpp
Normal file
33
include/verbosity.hpp
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#ifndef RGBDS_VERBOSITY_HPP
|
||||||
|
#define RGBDS_VERBOSITY_HPP
|
||||||
|
|
||||||
|
#include "style.hpp"
|
||||||
|
|
||||||
|
// This macro does not evaluate its arguments unless the condition is true.
|
||||||
|
#define verbosePrint(level, ...) \
|
||||||
|
do { \
|
||||||
|
if (checkVerbosity(level)) { \
|
||||||
|
style_Set(stderr, STYLE_MAGENTA, false); \
|
||||||
|
fprintf(stderr, __VA_ARGS__); \
|
||||||
|
style_Reset(stderr); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
enum Verbosity {
|
||||||
|
VERB_NONE, // 0. Default, no extra output
|
||||||
|
VERB_CONFIG, // 1. Basic configuration, after parsing CLI options
|
||||||
|
VERB_NOTICE, // 2. Before significant actions
|
||||||
|
VERB_INFO, // 3. Some intermediate action results
|
||||||
|
VERB_DEBUG, // 4. Internals useful for debugging
|
||||||
|
VERB_TRACE, // 5. Step-by-step algorithm details
|
||||||
|
VERB_VVVVVV, // 6. What, can't I have a little fun?
|
||||||
|
};
|
||||||
|
|
||||||
|
void incrementVerbosity();
|
||||||
|
bool checkVerbosity(Verbosity level);
|
||||||
|
|
||||||
|
void printVVVVVVerbosity();
|
||||||
|
|
||||||
|
#endif // RGBDS_VERBOSITY_HPP
|
||||||
@@ -3,9 +3,10 @@
|
|||||||
#ifndef RGBDS_VERSION_HPP
|
#ifndef RGBDS_VERSION_HPP
|
||||||
#define RGBDS_VERSION_HPP
|
#define RGBDS_VERSION_HPP
|
||||||
|
|
||||||
#define PACKAGE_VERSION_MAJOR 0
|
#define PACKAGE_VERSION_MAJOR 1
|
||||||
#define PACKAGE_VERSION_MINOR 9
|
#define PACKAGE_VERSION_MINOR 0
|
||||||
#define PACKAGE_VERSION_PATCH 2
|
#define PACKAGE_VERSION_PATCH 0
|
||||||
|
#define PACKAGE_VERSION_RC 1
|
||||||
|
|
||||||
char const *get_package_version_string();
|
char const *get_package_version_string();
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd May 4, 2025
|
.Dd September 1, 2025
|
||||||
.Dt GBZ80 7
|
.Dt GBZ80 7
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -657,7 +657,7 @@ Set if result is 0.
|
|||||||
.It Sy H
|
.It Sy H
|
||||||
0
|
0
|
||||||
.It Sy C
|
.It Sy C
|
||||||
Set or reset depending on the operation.
|
Set or unaffected depending on the operation.
|
||||||
.El
|
.El
|
||||||
.Ss DEC r8
|
.Ss DEC r8
|
||||||
Decrement the value in register
|
Decrement the value in register
|
||||||
|
|||||||
111
man/rgbasm-old.5
111
man/rgbasm-old.5
@@ -2,7 +2,7 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd May 4, 2025
|
.Dd September 1, 2025
|
||||||
.Dt RGBASM-OLD 5
|
.Dt RGBASM-OLD 5
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -99,6 +99,23 @@ in RAM sections, allowing labeled space allocations to overlap.
|
|||||||
.Pp
|
.Pp
|
||||||
Instead, use
|
Instead, use
|
||||||
.Ic UNION .
|
.Ic UNION .
|
||||||
|
.Ss Section-local charmaps
|
||||||
|
Deprecated in 0.3.9, removed in 0.4.0.
|
||||||
|
.Pp
|
||||||
|
Defining a
|
||||||
|
.Ic CHARMAP
|
||||||
|
inside a
|
||||||
|
.Ic SECTION
|
||||||
|
when the current global charmap was the
|
||||||
|
.Sq main
|
||||||
|
one used to only define that character mapping within that
|
||||||
|
.Ic SECTION .
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Ic PUSHC
|
||||||
|
and
|
||||||
|
.Ic POPC
|
||||||
|
and switch to a different character mapping for that section.
|
||||||
.Ss __FILE__ and __LINE__
|
.Ss __FILE__ and __LINE__
|
||||||
Deprecated in 0.6.0, removed in 0.7.0.
|
Deprecated in 0.6.0, removed in 0.7.0.
|
||||||
.Pp
|
.Pp
|
||||||
@@ -112,12 +129,23 @@ Deprecated in 0.5.0, removed in 0.6.0.
|
|||||||
.Pp
|
.Pp
|
||||||
Instead, use
|
Instead, use
|
||||||
.Ql 3.141592653 .
|
.Ql 3.141592653 .
|
||||||
|
.Ss __DATE__ and __TIME__
|
||||||
|
Deprecated in 1.0.0.
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Ql __ISO_8601_LOCAL__ .
|
||||||
.Ss Treating multi-character strings as numbers
|
.Ss Treating multi-character strings as numbers
|
||||||
Deprecated in 0.9.0.
|
Deprecated in 0.9.0, removed in 1.0.0.
|
||||||
.Pp
|
.Pp
|
||||||
Instead, use a multi-value
|
Instead, use a multi-value
|
||||||
.Ic CHARMAP ,
|
.Ic CHARMAP ,
|
||||||
or explicitly combine the values of individual characters.
|
or explicitly combine the values of individual characters.
|
||||||
|
.Ss Treating strings as numbers
|
||||||
|
Deprecated in 1.0.0.
|
||||||
|
.Pp
|
||||||
|
Instead, use character constants or the
|
||||||
|
.Ic CHARVAL
|
||||||
|
function.
|
||||||
.Ss rgbgfx -f/--fix and -F/--fix-and-save
|
.Ss rgbgfx -f/--fix and -F/--fix-and-save
|
||||||
Removed in 0.6.0.
|
Removed in 0.6.0.
|
||||||
.Pp
|
.Pp
|
||||||
@@ -192,6 +220,31 @@ typed in column 1.
|
|||||||
Instead, use
|
Instead, use
|
||||||
.Ql \&;
|
.Ql \&;
|
||||||
comments.
|
comments.
|
||||||
|
.Ss STRIN, STRRIN, STRSUB, and CHARSUB
|
||||||
|
Deprecated in 1.0.0.
|
||||||
|
.Pp
|
||||||
|
These functions used 1-based indexing of string characters, which was inconsistent with the 0-based indexing used more often in programming.
|
||||||
|
.Pp
|
||||||
|
Instead of
|
||||||
|
.Ic STRIN ,
|
||||||
|
use
|
||||||
|
.Ic STRFIND ;
|
||||||
|
instead of
|
||||||
|
.Ic STRRIN ,
|
||||||
|
use
|
||||||
|
.Ic STRRFIND ;
|
||||||
|
instead of
|
||||||
|
.Ic STRSUB ,
|
||||||
|
use
|
||||||
|
.Ic STRSLICE ;
|
||||||
|
and instead of
|
||||||
|
.Ic CHARSUB ,
|
||||||
|
use
|
||||||
|
.Ic STRCHAR .
|
||||||
|
.Pp
|
||||||
|
Note that
|
||||||
|
.Ic STRSLICE
|
||||||
|
takes a start and end index instead of a start index and a length.
|
||||||
.Ss PRINTT, PRINTI, PRINTV, and PRINTF
|
.Ss PRINTT, PRINTI, PRINTV, and PRINTF
|
||||||
Deprecated in 0.5.0, removed in 0.6.0.
|
Deprecated in 0.5.0, removed in 0.6.0.
|
||||||
.Pp
|
.Pp
|
||||||
@@ -230,7 +283,7 @@ and
|
|||||||
.Ic DATA ,
|
.Ic DATA ,
|
||||||
use
|
use
|
||||||
.Ic ROMX ;
|
.Ic ROMX ;
|
||||||
instead of
|
and instead of
|
||||||
.Ic BSS ,
|
.Ic BSS ,
|
||||||
use
|
use
|
||||||
.Ic WRAM0 .
|
.Ic WRAM0 .
|
||||||
@@ -255,12 +308,12 @@ or
|
|||||||
and
|
and
|
||||||
.Ql LD A, [HL-] ) .
|
.Ql LD A, [HL-] ) .
|
||||||
.Ss LDIO
|
.Ss LDIO
|
||||||
Deprecated in 0.9.0.
|
Deprecated in 0.9.0, removed in 1.0.0.
|
||||||
.Pp
|
.Pp
|
||||||
Instead, use
|
Instead, use
|
||||||
.Ql LDH .
|
.Ql LDH .
|
||||||
.Ss LD [C], A and LD A, [C]
|
.Ss LD [C], A and LD A, [C]
|
||||||
Deprecated in 0.9.0.
|
Deprecated in 0.9.0, removed in 1.0.0.
|
||||||
.Pp
|
.Pp
|
||||||
Instead, use
|
Instead, use
|
||||||
.Ql LDH [C], A
|
.Ql LDH [C], A
|
||||||
@@ -275,7 +328,7 @@ were also deprecated in 0.9.0, but were
|
|||||||
.Em undeprecated
|
.Em undeprecated
|
||||||
in 0.9.1.
|
in 0.9.1.
|
||||||
.Ss LDH [n8], A and LDH A, [n8]
|
.Ss LDH [n8], A and LDH A, [n8]
|
||||||
Deprecated in 0.9.0.
|
Deprecated in 0.9.0, removed in 1.0.0.
|
||||||
.Pp
|
.Pp
|
||||||
.Ql LDH
|
.Ql LDH
|
||||||
used to treat "addresses" from
|
used to treat "addresses" from
|
||||||
@@ -301,25 +354,35 @@ Supported in ASMotor, removed in RGBDS.
|
|||||||
.Pp
|
.Pp
|
||||||
Instead, use
|
Instead, use
|
||||||
.Ql LD HL, SP + e8 .
|
.Ql LD HL, SP + e8 .
|
||||||
|
.Ss OPT z
|
||||||
|
Deprecated in 0.4.0, removed in 0.5.0.
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Ic OPT p .
|
||||||
.Ss rgbasm -i
|
.Ss rgbasm -i
|
||||||
Deprecated in 0.6.0, removed in 0.8.0.
|
Deprecated in 0.6.0, removed in 0.8.0.
|
||||||
.Pp
|
.Pp
|
||||||
Instead, use
|
Instead, use
|
||||||
.Fl I
|
.Fl I
|
||||||
or
|
or
|
||||||
.Fl -include .
|
.Fl \-include .
|
||||||
|
.Ss rgbfix -O/--overwrite
|
||||||
|
Deprecated in 1.0.0.
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Dl -Wno-overwrite .
|
||||||
.Ss rgbgfx -h
|
.Ss rgbgfx -h
|
||||||
Removed in 0.6.0.
|
Removed in 0.6.0.
|
||||||
.Pp
|
.Pp
|
||||||
Instead, use
|
Instead, use
|
||||||
.Fl Z
|
.Fl Z
|
||||||
or
|
or
|
||||||
.Fl -columns .
|
.Fl \-columns .
|
||||||
.Ss rgbgfx --output-*
|
.Ss rgbgfx --output-*
|
||||||
Deprecated in 0.7.0, removed in 0.8.0.
|
Deprecated in 0.7.0, removed in 0.8.0.
|
||||||
.Pp
|
.Pp
|
||||||
Instead, use
|
Instead, use
|
||||||
.Fl -auto-* .
|
.Fl \-auto-* .
|
||||||
.Sh CHANGED
|
.Sh CHANGED
|
||||||
These are breaking changes that did not alter syntax, and so could not practically be deprecated.
|
These are breaking changes that did not alter syntax, and so could not practically be deprecated.
|
||||||
.Ss Trigonometry function units
|
.Ss Trigonometry function units
|
||||||
@@ -356,6 +419,36 @@ because $16384$ turns = $16384 tau$ radians = $32768 pi$ radians, and $sin ( 327
|
|||||||
.EQ
|
.EQ
|
||||||
delim off
|
delim off
|
||||||
.EN
|
.EN
|
||||||
|
.Ss % operator behavior with negative dividend or divisor
|
||||||
|
Changed in 0.5.0.
|
||||||
|
.Pp
|
||||||
|
Instead of having the same sign as the dividend (a remainder operation),
|
||||||
|
.Ql %
|
||||||
|
has the same sign as the divisor (a modulo operation).
|
||||||
|
.Pp
|
||||||
|
For example, previously we had:
|
||||||
|
.Bl -bullet -offset indent
|
||||||
|
.It
|
||||||
|
.Ql 13 % 10 == 3
|
||||||
|
.It
|
||||||
|
.Ql -13 % 10 == -3
|
||||||
|
.It
|
||||||
|
.Ql 13 % -10 == 3
|
||||||
|
.It
|
||||||
|
.Ql -13 % -10 == -3
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
Instead, now we have:
|
||||||
|
.Bl -bullet -offset indent
|
||||||
|
.It
|
||||||
|
.Ql 13 % 10 == 3
|
||||||
|
.It
|
||||||
|
.Ql -13 % 10 == 7
|
||||||
|
.It
|
||||||
|
.Ql 13 % -10 == -7
|
||||||
|
.It
|
||||||
|
.Ql -13 % -10 == -3
|
||||||
|
.El
|
||||||
.Ss ** operator associativity
|
.Ss ** operator associativity
|
||||||
Changed in 0.9.0.
|
Changed in 0.9.0.
|
||||||
.Pp
|
.Pp
|
||||||
|
|||||||
143
man/rgbasm.1
143
man/rgbasm.1
@@ -1,6 +1,6 @@
|
|||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd May 4, 2025
|
.Dd September 1, 2025
|
||||||
.Dt RGBASM 1
|
.Dt RGBASM 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -9,12 +9,15 @@
|
|||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm
|
.Nm
|
||||||
.Op Fl EhVvw
|
.Op Fl EhVvw
|
||||||
|
.Op Fl B Ar param
|
||||||
.Op Fl b Ar chars
|
.Op Fl b Ar chars
|
||||||
|
.Op Fl \-color Ar when
|
||||||
.Op Fl D Ar name Ns Op = Ns Ar value
|
.Op Fl D Ar name Ns Op = Ns Ar value
|
||||||
.Op Fl g Ar chars
|
.Op Fl g Ar chars
|
||||||
.Op Fl I Ar path
|
.Op Fl I Ar path
|
||||||
.Op Fl M Ar depend_file
|
.Op Fl M Ar depend_file
|
||||||
.Op Fl MG
|
.Op Fl MG
|
||||||
|
.Op Fl MC
|
||||||
.Op Fl MP
|
.Op Fl MP
|
||||||
.Op Fl MT Ar target_file
|
.Op Fl MT Ar target_file
|
||||||
.Op Fl MQ Ar target_file
|
.Op Fl MQ Ar target_file
|
||||||
@@ -50,9 +53,55 @@ is invalid because it could also be
|
|||||||
.Fl \-version .
|
.Fl \-version .
|
||||||
The arguments are as follows:
|
The arguments are as follows:
|
||||||
.Bl -tag -width Ds
|
.Bl -tag -width Ds
|
||||||
|
.It Fl B Ar param , Fl \-backtrace Ar param
|
||||||
|
Configures how location backtraces are printed if warnings or errors occur.
|
||||||
|
This flag may be specified multiple times with different parameters that combine meaningfully.
|
||||||
|
If
|
||||||
|
.Ar param
|
||||||
|
is a positive number, it specifies the maximum backtrace depth, abbreviating deeper ones.
|
||||||
|
Other valid parameter values are the following:
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It Cm 0
|
||||||
|
Do not limit the maximum backtrace depth; this is the default.
|
||||||
|
.It Cm all
|
||||||
|
Force all locations to be printed, even "quiet" ones (see
|
||||||
|
.Dq Excluding locations from backtraces
|
||||||
|
in
|
||||||
|
.Xr rgbasm 5
|
||||||
|
for details).
|
||||||
|
.It Cm no-all
|
||||||
|
Do not print "quieted" locations in backtraces; this is the default.
|
||||||
|
.It Cm collapse
|
||||||
|
Print all locations on one line.
|
||||||
|
.It Cm no-collapse
|
||||||
|
Print one location per line; this is the default.
|
||||||
|
.El
|
||||||
.It Fl b Ar chars , Fl \-binary-digits Ar chars
|
.It Fl b Ar chars , Fl \-binary-digits Ar chars
|
||||||
Change the two characters used for binary constants.
|
Allow two characters to be used for binary constants in addition to the default
|
||||||
The defaults are 01.
|
.Sq 0
|
||||||
|
and
|
||||||
|
.Sq 1 .
|
||||||
|
Valid characters are numbers other than
|
||||||
|
.Sq 0
|
||||||
|
and
|
||||||
|
.Sq 1 ,
|
||||||
|
letters,
|
||||||
|
.Sq \&. ,
|
||||||
|
.Sq # ,
|
||||||
|
or
|
||||||
|
.Sq @ .
|
||||||
|
.It Fl \-color Ar when
|
||||||
|
Specify when to highlight warning and error messages with color:
|
||||||
|
.Ql always ,
|
||||||
|
.Ql never ,
|
||||||
|
or
|
||||||
|
.Ql auto .
|
||||||
|
.Ql auto
|
||||||
|
determines whether to use colors based on the
|
||||||
|
.Ql Lk https://no-color.org/ NO_COLOR
|
||||||
|
or
|
||||||
|
.Ql Lk https://force-color.org/ FORCE_COLOR
|
||||||
|
environment variables, or whether the output is to a TTY.
|
||||||
.It Fl D Ar name Ns Oo = Ns Ar value Oc , Fl \-define Ar name Ns Oo = Ns Ar value Oc
|
.It Fl D Ar name Ns Oo = Ns Ar value Oc , Fl \-define Ar name Ns Oo = Ns Ar value Oc
|
||||||
Add a string symbol to the compiled source code.
|
Add a string symbol to the compiled source code.
|
||||||
This is equivalent to
|
This is equivalent to
|
||||||
@@ -65,7 +114,21 @@ is not specified.
|
|||||||
.It Fl E , Fl \-export-all
|
.It Fl E , Fl \-export-all
|
||||||
Export all labels, including unreferenced and local labels.
|
Export all labels, including unreferenced and local labels.
|
||||||
.It Fl g Ar chars , Fl \-gfx-chars Ar chars
|
.It Fl g Ar chars , Fl \-gfx-chars Ar chars
|
||||||
Change the four characters used for gfx constants.
|
Allow four characters to be used for graphics constants in addition to the default
|
||||||
|
.Sq 0 ,
|
||||||
|
.Sq 1 ,
|
||||||
|
.Sq 2 ,
|
||||||
|
and
|
||||||
|
.Sq 3 .
|
||||||
|
Valid characters are numbers other than
|
||||||
|
.Sq 0
|
||||||
|
to
|
||||||
|
.Sq 3 ,
|
||||||
|
letters,
|
||||||
|
.Sq \&. ,
|
||||||
|
.Sq # ,
|
||||||
|
or
|
||||||
|
.Sq @ .
|
||||||
The defaults are 0123.
|
The defaults are 0123.
|
||||||
.It Fl h , Fl \-help
|
.It Fl h , Fl \-help
|
||||||
Print help text for the program and exit.
|
Print help text for the program and exit.
|
||||||
@@ -74,11 +137,12 @@ Add a new
|
|||||||
.Dq include path ;
|
.Dq include path ;
|
||||||
.Ar path
|
.Ar path
|
||||||
must point to a directory.
|
must point to a directory.
|
||||||
When a
|
When any
|
||||||
.Ic INCLUDE
|
.Ic INCLUDE
|
||||||
.Pq including the implicit one from Fl P
|
.Pq including the implicit one from Fl P ,
|
||||||
|
.Ic INCBIN ,
|
||||||
or
|
or
|
||||||
.Ic INCBIN
|
.Ic READFILE
|
||||||
is attempted,
|
is attempted,
|
||||||
.Nm
|
.Nm
|
||||||
first looks up the provided path from its working directory; if this fails, it tries again from each of the
|
first looks up the provided path from its working directory; if this fails, it tries again from each of the
|
||||||
@@ -94,17 +158,33 @@ To be used in conjunction with
|
|||||||
.Fl M .
|
.Fl M .
|
||||||
This makes
|
This makes
|
||||||
.Nm
|
.Nm
|
||||||
assume that missing files are auto-generated: when
|
assume that missing files are auto-generated: when any
|
||||||
.Ic INCLUDE
|
.Ic INCLUDE
|
||||||
.Pq including the implicit one from Fl P
|
.Pq including the implicit one from Fl P ,
|
||||||
|
.Ic INCBIN ,
|
||||||
or
|
or
|
||||||
.Ic INCBIN
|
.Ic READFILE
|
||||||
is attempted on a non-existent file, it is added as a dependency, then
|
is attempted on a non-existent file, it is added as a dependency, then
|
||||||
.Nm
|
.Nm
|
||||||
exits normally instead of erroring out.
|
exits normally or continues processing (depending on whether
|
||||||
This feature is used in automatic updating of makefiles.
|
.Fl MC
|
||||||
|
was enabled) instead of erroring out.
|
||||||
|
This feature is used in automatic updating of Makefiles.
|
||||||
|
.It Fl MC
|
||||||
|
Implies
|
||||||
|
.Fl MG .
|
||||||
|
This makes
|
||||||
|
.Nm
|
||||||
|
continue processing after a non-existent dependency file, instead of exiting.
|
||||||
|
Note that this is
|
||||||
|
.Em not
|
||||||
|
recommended if any non-existent dependencies would have influenced subsequent processing, e.g. by causing an
|
||||||
|
.Ic IF
|
||||||
|
condition to take a different branch.
|
||||||
.It Fl MP
|
.It Fl MP
|
||||||
When enabled, this causes a phony target to be added for each dependency other than the main file.
|
When enabled, this adds a phony target to the rules emitted by
|
||||||
|
.Fl M
|
||||||
|
for each dependency other than the main file.
|
||||||
This prevents
|
This prevents
|
||||||
.Xr make 1
|
.Xr make 1
|
||||||
from erroring out when dependency files are deleted.
|
from erroring out when dependency files are deleted.
|
||||||
@@ -133,6 +213,7 @@ This acts as if a
|
|||||||
.Ql Ic INCLUDE Qq Ar include_file
|
.Ql Ic INCLUDE Qq Ar include_file
|
||||||
was read before the input
|
was read before the input
|
||||||
.Ar asmfile .
|
.Ar asmfile .
|
||||||
|
Multiple files can be pre-included in the order they were provided.
|
||||||
.It Fl p Ar pad_value , Fl \-pad-value Ar pad_value
|
.It Fl p Ar pad_value , Fl \-pad-value Ar pad_value
|
||||||
Use this as the value for
|
Use this as the value for
|
||||||
.Ic DS
|
.Ic DS
|
||||||
@@ -189,6 +270,24 @@ below).
|
|||||||
Print the version of the program and exit.
|
Print the version of the program and exit.
|
||||||
.It Fl v , Fl \-verbose
|
.It Fl v , Fl \-verbose
|
||||||
Be verbose.
|
Be verbose.
|
||||||
|
The verbosity level is increased by one each time the flag is specified, with each level including the previous:
|
||||||
|
.Bl -enum -compact
|
||||||
|
.It
|
||||||
|
Print the
|
||||||
|
.Nm
|
||||||
|
configuration before taking actions.
|
||||||
|
.It
|
||||||
|
Print a notice before significant actions.
|
||||||
|
.It
|
||||||
|
Print some of the actions' intermediate results.
|
||||||
|
.It
|
||||||
|
Print some internal debug information.
|
||||||
|
.It
|
||||||
|
Print detailed internal information.
|
||||||
|
.El
|
||||||
|
The verbosity level does not go past 6.
|
||||||
|
.Pp
|
||||||
|
Note that verbose output is only intended to be consumed by humans, and may change without notice between RGBDS releases; relying on those for scripts is not advised.
|
||||||
.It Fl W Ar warning , Fl \-warning Ar warning
|
.It Fl W Ar warning , Fl \-warning Ar warning
|
||||||
Set warning flag
|
Set warning flag
|
||||||
.Ar warning .
|
.Ar warning .
|
||||||
@@ -244,7 +343,7 @@ Enables literally every warning.
|
|||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
The following warnings are actual warning flags; with each description, the corresponding warning flag is included.
|
The following warnings are actual warning flags; with each description, the corresponding warning flag is included.
|
||||||
Note that each of these flag also has a negation (for example,
|
Note that each of these flags also has a negation (for example,
|
||||||
.Fl Wcharmap-redef
|
.Fl Wcharmap-redef
|
||||||
enables the warning that
|
enables the warning that
|
||||||
.Fl Wno-charmap-redef
|
.Fl Wno-charmap-redef
|
||||||
@@ -275,7 +374,7 @@ This warning is enabled by
|
|||||||
.Fl Wall .
|
.Fl Wall .
|
||||||
.It Fl Wbuiltin-args
|
.It Fl Wbuiltin-args
|
||||||
Warn about incorrect arguments to built-in functions, such as
|
Warn about incorrect arguments to built-in functions, such as
|
||||||
.Fn STRSUB
|
.Fn STRSLICE
|
||||||
with indexes outside of the string's bounds.
|
with indexes outside of the string's bounds.
|
||||||
This warning is enabled by
|
This warning is enabled by
|
||||||
.Fl Wall .
|
.Fl Wall .
|
||||||
@@ -295,10 +394,12 @@ Warn when
|
|||||||
is called with an empty string as its second argument (the substring to replace).
|
is called with an empty string as its second argument (the substring to replace).
|
||||||
This warning is enabled by
|
This warning is enabled by
|
||||||
.Fl Wall .
|
.Fl Wall .
|
||||||
.It Fl Wlarge-constant
|
.It Fl Wexport-undefined
|
||||||
Warn when a constant too large to fit in a signed 32-bit integer is encountered.
|
Warn when exporting an undefined symbol.
|
||||||
This warning is enabled by
|
This warning is enabled by
|
||||||
.Fl Wall .
|
.Fl Wall .
|
||||||
|
.It Fl Wno-large-constant
|
||||||
|
Warn when a constant too large to fit in a signed 32-bit integer is encountered.
|
||||||
.It Fl Wmacro-shift
|
.It Fl Wmacro-shift
|
||||||
Warn when shifting macro arguments past their limits.
|
Warn when shifting macro arguments past their limits.
|
||||||
This warning is enabled by
|
This warning is enabled by
|
||||||
@@ -311,11 +412,7 @@ Block comments cannot be nested, so the first
|
|||||||
.Ql */
|
.Ql */
|
||||||
will end the whole comment.
|
will end the whole comment.
|
||||||
.It Fl Wno-obsolete
|
.It Fl Wno-obsolete
|
||||||
Warn when obsolete constructs such as the
|
Warn when obsolete features are encountered, which have been deprecated and may later be removed.
|
||||||
.Ic _PI
|
|
||||||
constant or
|
|
||||||
.Ic PRINTT
|
|
||||||
directive are encountered.
|
|
||||||
.It Fl Wnumeric-string=
|
.It Fl Wnumeric-string=
|
||||||
Warn when a multi-character string is treated as a number.
|
Warn when a multi-character string is treated as a number.
|
||||||
.Fl Wnumeric-string=0
|
.Fl Wnumeric-string=0
|
||||||
@@ -421,7 +518,7 @@ Writing the final assembler state to a file:
|
|||||||
Or to multiple files:
|
Or to multiple files:
|
||||||
.Dl $ rgbasm -s equ,var:numbers.dump.asm -s equs:strings.dump.asm foo.asm
|
.Dl $ rgbasm -s equ,var:numbers.dump.asm -s equs:strings.dump.asm foo.asm
|
||||||
.Sh BUGS
|
.Sh BUGS
|
||||||
Please report bugs on
|
Please report bugs or mistakes in this documentation on
|
||||||
.Lk https://github.com/gbdev/rgbds/issues GitHub .
|
.Lk https://github.com/gbdev/rgbds/issues GitHub .
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr rgbasm 5 ,
|
.Xr rgbasm 5 ,
|
||||||
|
|||||||
269
man/rgbasm.5
269
man/rgbasm.5
@@ -2,7 +2,7 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd May 4, 2025
|
.Dd September 1, 2025
|
||||||
.Dt RGBASM 5
|
.Dt RGBASM 5
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -43,7 +43,9 @@ Labels tie a name to a specific location within a section (see
|
|||||||
below).
|
below).
|
||||||
.Pp
|
.Pp
|
||||||
Instructions are assembled into Game Boy opcodes.
|
Instructions are assembled into Game Boy opcodes.
|
||||||
Multiple instructions on one line can be separated by double colons
|
Multiple instructions on one line, as well as data directives (see
|
||||||
|
.Sx Defining constant data in ROM
|
||||||
|
below), can be separated by double colons
|
||||||
.Ql :: .
|
.Ql :: .
|
||||||
.Pp
|
.Pp
|
||||||
The available instructions are documented in
|
The available instructions are documented in
|
||||||
@@ -194,12 +196,19 @@ If specified, pads right-aligned numbers with zeros instead of spaces.
|
|||||||
If specified, pads the value to this width, right-aligned with spaces by default.
|
If specified, pads the value to this width, right-aligned with spaces by default.
|
||||||
.It Ql <frac> Ta May be
|
.It Ql <frac> Ta May be
|
||||||
.Ql \&.
|
.Ql \&.
|
||||||
followed by one or more
|
followed by zero or more
|
||||||
.Ql 0
|
.Ql 0
|
||||||
\[en]
|
\[en]
|
||||||
.Ql 9 .
|
.Ql 9 .
|
||||||
If specified, prints this many fractional digits of a fixed-point number.
|
If specified, prints this many fractional digits of a fixed-point number.
|
||||||
Defaults to 5 digits, maximum 255 digits.
|
Defaults to 5 digits, maximum 255 digits.
|
||||||
|
(A
|
||||||
|
.Ql \&.
|
||||||
|
followed by zero
|
||||||
|
.Ql 0
|
||||||
|
\[en]
|
||||||
|
.Ql 9
|
||||||
|
prints zero fractional digits.)
|
||||||
.It Ql <prec> Ta May be
|
.It Ql <prec> Ta May be
|
||||||
.Ql q
|
.Ql q
|
||||||
followed by one or more
|
followed by one or more
|
||||||
@@ -280,7 +289,7 @@ There are a number of numeric formats.
|
|||||||
.It Binary Ta Li % , 0b , 0B Ta 01
|
.It Binary Ta Li % , 0b , 0B Ta 01
|
||||||
.It Fixed-point Ta none Ta 01234.56789
|
.It Fixed-point Ta none Ta 01234.56789
|
||||||
.It Precise fixed-point Ta none Ta 12.34q8
|
.It Precise fixed-point Ta none Ta 12.34q8
|
||||||
.It Character constant Ta none Ta \(dqABYZ\(dq
|
.It Character constant Ta none Ta 'ABYZ'
|
||||||
.It Game Boy graphics Ta Li \` Ta 0123
|
.It Game Boy graphics Ta Li \` Ta 0123
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
@@ -293,11 +302,14 @@ or
|
|||||||
The "character constant" form yields the value the character maps to in the current charmap.
|
The "character constant" form yields the value the character maps to in the current charmap.
|
||||||
For example, by default
|
For example, by default
|
||||||
.Pq refer to Xr ascii 7
|
.Pq refer to Xr ascii 7
|
||||||
.Sq \(dqA\(dq
|
.Sq 'A'
|
||||||
yields 65.
|
yields 65.
|
||||||
|
A character constant must represent a single value, so it cannot include multiple characters, or characters which map to multiple values.
|
||||||
See
|
See
|
||||||
.Sx Character maps
|
.Sx Character maps
|
||||||
for information on charmaps.
|
for information on charmaps, and
|
||||||
|
.Sx String expressions
|
||||||
|
for information on escape characters allowed in character constants.
|
||||||
.Pp
|
.Pp
|
||||||
The last one, Game Boy graphics, is quite interesting and useful.
|
The last one, Game Boy graphics, is quite interesting and useful.
|
||||||
After the backtick, 8 digits between 0 and 3 are expected, corresponding to pixel values.
|
After the backtick, 8 digits between 0 and 3 are expected, corresponding to pixel values.
|
||||||
@@ -538,7 +550,8 @@ There are a number of escape sequences you can use within a string:
|
|||||||
.Bl -column -offset indent "Sequence"
|
.Bl -column -offset indent "Sequence"
|
||||||
.It Sy Sequence Ta Sy Meaning
|
.It Sy Sequence Ta Sy Meaning
|
||||||
.It Ql \e\e Ta Backslash Pq escapes the escape character itself
|
.It Ql \e\e Ta Backslash Pq escapes the escape character itself
|
||||||
.It Ql \e" Ta Double quote Pq does not terminate the string
|
.It Ql \e" Ta Double quote Pq does not terminate a string
|
||||||
|
.It Ql \e' Ta Single quote Pq does not terminate a character literal
|
||||||
.It Ql \e{ Ta Open curly brace Pq does not start interpolation
|
.It Ql \e{ Ta Open curly brace Pq does not start interpolation
|
||||||
.It Ql \e} Ta Close curly brace Pq does not end interpolation
|
.It Ql \e} Ta Close curly brace Pq does not end interpolation
|
||||||
.It Ql \en Ta Newline Pq ASCII $0A
|
.It Ql \en Ta Newline Pq ASCII $0A
|
||||||
@@ -548,7 +561,7 @@ There are a number of escape sequences you can use within a string:
|
|||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
Multi-line strings are contained in triple quotes
|
Multi-line strings are contained in triple quotes
|
||||||
.Pq Ql \&"\&"\&"for instance\&"\&"\&" .
|
.Pq Ql \&"\&"\&"for instance""" .
|
||||||
Escape sequences work the same way in multi-line strings; however, literal newline characters will be included as-is, without needing to escape them with
|
Escape sequences work the same way in multi-line strings; however, literal newline characters will be included as-is, without needing to escape them with
|
||||||
.Ql \er
|
.Ql \er
|
||||||
or
|
or
|
||||||
@@ -560,10 +573,19 @@ Inside them, backslashes and braces are treated like regular characters, so they
|
|||||||
For example, the raw string
|
For example, the raw string
|
||||||
.Ql #"\et\e1{s}\e"
|
.Ql #"\et\e1{s}\e"
|
||||||
is equivalent to the regular string
|
is equivalent to the regular string
|
||||||
.Ql "\e\et\e\e1\e{s}\e\e" .
|
.Ql \&"\e\et\e\e1\e{s}\e\e" .
|
||||||
(Note that this prevents raw strings from including the double quote character.)
|
(Note that this prevents raw strings from including the double quote character.)
|
||||||
Raw strings also may be contained in triple quotes for them to be multi-line, so they can include literal newline or quote characters (although still not three quotes in a row).
|
Raw strings also may be contained in triple quotes for them to be multi-line, so they can include literal newline or quote characters (although still not three quotes in a row).
|
||||||
.Pp
|
.Pp
|
||||||
|
You can use the
|
||||||
|
.Sq ++
|
||||||
|
operator to concatenate two strings.
|
||||||
|
.Ql \&"str" ++ \&"ing"
|
||||||
|
is equivalent to
|
||||||
|
.Ql \&"string" ,
|
||||||
|
or to
|
||||||
|
.Ql STRCAT("str", \&"ing") .
|
||||||
|
.Pp
|
||||||
The following functions operate on string expressions, and return strings themselves.
|
The following functions operate on string expressions, and return strings themselves.
|
||||||
.Bl -column "STRSLICE(str, start, stop)"
|
.Bl -column "STRSLICE(str, start, stop)"
|
||||||
.It Sy Name Ta Sy Operation
|
.It Sy Name Ta Sy Operation
|
||||||
@@ -574,7 +596,7 @@ in uppercase.
|
|||||||
.It Fn STRLWR str Ta Returns Ar str No with all ASCII letters
|
.It Fn STRLWR str Ta Returns Ar str No with all ASCII letters
|
||||||
.Pq Ql A-Z
|
.Pq Ql A-Z
|
||||||
in lowercase.
|
in lowercase.
|
||||||
.It Fn STRSLICE str start stop Ta Returns a substring of Ar str No starting at Ar start No and ending at Ar stop No (exclusive). If Ar stop No is not specified, the substring continues to the end of Ar str Ns .
|
.It Fn STRSLICE str start stop Ta Returns a substring of Ar str No starting at Ar start No and ending at Ar stop No (exclusive). If Ar stop No is not specified, the substring continues to the end of Ar str .
|
||||||
.It Fn STRRPL str old new Ta Returns Ar str No with each non-overlapping occurrence of the substring Ar old No replaced with Ar new .
|
.It Fn STRRPL str old new Ta Returns Ar str No with each non-overlapping occurrence of the substring Ar old No replaced with Ar new .
|
||||||
.It Fn STRFMT fmt args... Ta Returns the string Ar fmt No with each
|
.It Fn STRFMT fmt args... Ta Returns the string Ar fmt No with each
|
||||||
.Ql %spec
|
.Ql %spec
|
||||||
@@ -586,6 +608,7 @@ with its corresponding argument in
|
|||||||
.Pq So %% Sc is replaced by the So % Sc character .
|
.Pq So %% Sc is replaced by the So % Sc character .
|
||||||
.It Fn STRCHAR str idx Ta Returns the substring of Ar str No for the charmap entry at Ar idx No with the current charmap . Pq Ar idx No counts charmap entries, not characters.
|
.It Fn STRCHAR str idx Ta Returns the substring of Ar str No for the charmap entry at Ar idx No with the current charmap . Pq Ar idx No counts charmap entries, not characters.
|
||||||
.It Fn REVCHAR vals... Ta Returns the string that is mapped to Ar vals No with the current charmap. If there is no unique charmap entry for Ar vals Ns , an error occurs.
|
.It Fn REVCHAR vals... Ta Returns the string that is mapped to Ar vals No with the current charmap. If there is no unique charmap entry for Ar vals Ns , an error occurs.
|
||||||
|
.It Fn READFILE name max Ta Returns the contents of the file Ar name No as a string. Reads up to Ar max No bytes, or the entire contents if Ar max No is not specified. If the file isn't found in the current directory, the include-path list passed to Xr rgbasm 1 Ap s Fl I No option on the command line will be searched.
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
The following functions operate on string expressions, but return integers.
|
The following functions operate on string expressions, but return integers.
|
||||||
@@ -595,25 +618,22 @@ The following functions operate on string expressions, but return integers.
|
|||||||
.It Fn STRCMP str1 str2 Ta Compares Ar str1 No and Ar str2 No according to ASCII ordering of their characters. Returns -1 if Ar str1 No is lower than Ar str2 Ns , 1 if Ar str1 No is greater than Ar str2 Ns , or 0 if they match.
|
.It Fn STRCMP str1 str2 Ta Compares Ar str1 No and Ar str2 No according to ASCII ordering of their characters. Returns -1 if Ar str1 No is lower than Ar str2 Ns , 1 if Ar str1 No is greater than Ar str2 Ns , or 0 if they match.
|
||||||
.It Fn STRFIND str sub Ta Returns the first index of Ar sub No in Ar str Ns , or -1 if it's not present.
|
.It Fn STRFIND str sub Ta Returns the first index of Ar sub No in Ar str Ns , or -1 if it's not present.
|
||||||
.It Fn STRRFIND str sub Ta Returns the last index of Ar sub No in Ar str Ns , or -1 if it's not present.
|
.It Fn STRRFIND str sub Ta Returns the last index of Ar sub No in Ar str Ns , or -1 if it's not present.
|
||||||
.It Fn INCHARMAP str Ta Returns 1 if Ar str No has an entry in the current charmap, or 0 otherwise .
|
.It Fn BYTELEN str Ta Returns the number of bytes in Ar str . Pq Non-ASCII characters can be multiple bytes.
|
||||||
.It Fn CHARLEN str Ta Returns the number of charmap entries in Ar str No with the current charmap .
|
.It Fn STRBYTE str idx Ta Returns the byte value at Ar idx No in Ar str .
|
||||||
|
.It Fn INCHARMAP str Ta Returns 1 if Ar str No has an entry in the current charmap, or 0 otherwise.
|
||||||
|
.It Fn CHARLEN str Ta Returns the number of charmap entries in Ar str No with the current charmap.
|
||||||
.It Fn CHARCMP str1 str2 Ta Compares Ar str1 No and Ar str2 No according to their charmap entry values with the current charmap. Returns -1 if Ar str1 No is lower than Ar str2 Ns , 1 if Ar str1 No is greater than Ar str2 Ns , or 0 if they match.
|
.It Fn CHARCMP str1 str2 Ta Compares Ar str1 No and Ar str2 No according to their charmap entry values with the current charmap. Returns -1 if Ar str1 No is lower than Ar str2 Ns , 1 if Ar str1 No is greater than Ar str2 Ns , or 0 if they match.
|
||||||
.It Fn CHARSIZE char Ta Returns how many values are in the charmap entry for Ar char No with the current charmap.
|
.It Fn CHARSIZE char Ta Returns how many values are in the charmap entry for Ar char No with the current charmap.
|
||||||
|
.It Fn CHARVAL char idx Ta Returns the value at Ar idx No of the charmap entry for Ar char . If Ar idx No is not specified, Ar char No must have a single value, which is returned.
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
Note that the first character of a string is at index 0, and the last is at index -1.
|
Note that indexes count starting from 0 at the beginning, or from -1 at the end.
|
||||||
.Pp
|
The characters of a string are counted by
|
||||||
The following legacy functions are similar to other functions that operate on string expressions, but for historical reasons, they count characters starting from
|
.Ql STRLEN ;
|
||||||
.Em position 1 ,
|
the charmap entries of a string are counted by
|
||||||
not from index 0!
|
.Ql CHARLEN ;
|
||||||
(Position -1 still counts from the last character.)
|
and the values of a charmap entry are counted by
|
||||||
.Bl -column "STRSUB(str, pos, len)"
|
.Ql CHARSIZE .
|
||||||
.It Sy Name Ta Sy Operation
|
|
||||||
.It Fn STRSUB str pos len Ta Returns a substring of Ar str No starting at Ar pos No and Ar len No characters long. If Ar len No is not specified, the substring continues to the end of Ar str No .
|
|
||||||
.It Fn STRIN str sub Ta Returns the first position of Ar sub No in Ar str Ns , or 0 if it's not present.
|
|
||||||
.It Fn STRRIN str sub Ta Returns the last position of Ar sub No in Ar str Ns , or 0 if it's not present.
|
|
||||||
.It Fn CHARSUB str pos Ta Returns the substring of Ar str No for the charmap entry at Ar pos No with the current charmap . Pq Ar pos No counts charmap entries, not characters.
|
|
||||||
.El
|
|
||||||
.Ss Character maps
|
.Ss Character maps
|
||||||
When writing text strings that are meant to be displayed on the Game Boy, the character encoding in the ROM may need to be different than the source file encoding.
|
When writing text strings that are meant to be displayed on the Game Boy, the character encoding in the ROM may need to be different than the source file encoding.
|
||||||
For example, the tiles used for uppercase letters may be placed starting at tile index 128, which differs from ASCII starting at 65.
|
For example, the tiles used for uppercase letters may be placed starting at tile index 128, which differs from ASCII starting at 65.
|
||||||
@@ -694,6 +714,9 @@ If
|
|||||||
.Ar arg
|
.Ar arg
|
||||||
is a section type keyword, it returns the size of that section type.
|
is a section type keyword, it returns the size of that section type.
|
||||||
The result is not constant, since only RGBLINK can compute its value.
|
The result is not constant, since only RGBLINK can compute its value.
|
||||||
|
If
|
||||||
|
.Ar arg
|
||||||
|
is an 8-bit or 16-bit register, it returns the size of that register.
|
||||||
.It Fn STARTOF arg Ta If
|
.It Fn STARTOF arg Ta If
|
||||||
.Ar arg
|
.Ar arg
|
||||||
is a string, this function returns the starting address of the section named
|
is a string, this function returns the starting address of the section named
|
||||||
@@ -1096,6 +1119,120 @@ first, followed by the one from
|
|||||||
and the one from
|
and the one from
|
||||||
.Ql bar.o
|
.Ql bar.o
|
||||||
last.
|
last.
|
||||||
|
.Ss Fragment literals
|
||||||
|
Fragment literals are useful for short blocks of code or data that are only referenced once.
|
||||||
|
They are section fragments created by surrounding instructions or directives with
|
||||||
|
.Ql [[
|
||||||
|
double brackets
|
||||||
|
.Ql ]] ,
|
||||||
|
without a separate
|
||||||
|
.Ic SECTION FRAGMENT
|
||||||
|
declaration.
|
||||||
|
.Pp
|
||||||
|
The content of a fragment literal becomes a
|
||||||
|
.Ic SECTION FRAGMENT ,
|
||||||
|
sharing the same name and bank as its parent ROM section, but without any other constraints.
|
||||||
|
The parent section also becomes a
|
||||||
|
.Ic FRAGMENT
|
||||||
|
if it was not one already, so that it can be merged with its fragment literals.
|
||||||
|
RGBLINK merges the fragments in no particular order.
|
||||||
|
.Pp
|
||||||
|
A fragment literal can take the place of any 16-bit integer constant
|
||||||
|
.Ql n16
|
||||||
|
from the
|
||||||
|
.Xr gbz80 7
|
||||||
|
documentation, as well as a
|
||||||
|
.Ic DW
|
||||||
|
item.
|
||||||
|
The fragment literal then evaluates to its starting address.
|
||||||
|
For example, you can
|
||||||
|
.Ic CALL
|
||||||
|
or
|
||||||
|
.Ic JP
|
||||||
|
to a fragment literal.
|
||||||
|
.Pp
|
||||||
|
This code using named labels:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
DataTable:
|
||||||
|
dw First
|
||||||
|
dw Second
|
||||||
|
dw Third
|
||||||
|
First: db 1
|
||||||
|
Second: db 4
|
||||||
|
Third: db 9
|
||||||
|
Routine:
|
||||||
|
push hl
|
||||||
|
ld hl, Left
|
||||||
|
jr z, .got_it
|
||||||
|
ld hl, Right
|
||||||
|
\&.got_it
|
||||||
|
call .print
|
||||||
|
pop hl
|
||||||
|
ret
|
||||||
|
\&.print:
|
||||||
|
ld de, $1003
|
||||||
|
ld bc, STARTOF(VRAM)
|
||||||
|
jp Print
|
||||||
|
Left: db "left\e0"
|
||||||
|
Right: db "right\e0"
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
is equivalent to this code using fragment literals:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
DataTable:
|
||||||
|
dw [[ db 1 ]]
|
||||||
|
dw [[ db 4 ]]
|
||||||
|
dw [[ db 9 ]]
|
||||||
|
Routine:
|
||||||
|
push hl
|
||||||
|
ld hl, [[ db "left\e0" ]]
|
||||||
|
jr z, .got_it
|
||||||
|
ld hl, [[ db "right\e0" ]]
|
||||||
|
\&.got_it
|
||||||
|
call [[
|
||||||
|
ld de, $1003
|
||||||
|
ld bc, STARTOF(VRAM)
|
||||||
|
jp Print
|
||||||
|
]]
|
||||||
|
pop hl
|
||||||
|
ret
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
The difference is that the example using fragment literals does not declare a particular order for its pieces.
|
||||||
|
.Pp
|
||||||
|
Fragment literals can be arbitrarily nested, so extreme use cases are
|
||||||
|
.Em technically
|
||||||
|
possible.
|
||||||
|
This code using named labels:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
dw FortyTwo
|
||||||
|
FortyTwo:
|
||||||
|
call Sub1
|
||||||
|
jr Sub2
|
||||||
|
Sub1:
|
||||||
|
ld a, [Twenty]
|
||||||
|
ret
|
||||||
|
Twenty: db 20
|
||||||
|
Sub2:
|
||||||
|
jp Sub3
|
||||||
|
Sub3:
|
||||||
|
call Sub1
|
||||||
|
inc a
|
||||||
|
add a
|
||||||
|
ret
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
is equivalent to this code using fragment literals:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
dw [[
|
||||||
|
call [[
|
||||||
|
Sub1: ld a, [ [[db 20]] ] :: ret
|
||||||
|
]]
|
||||||
|
jr [[
|
||||||
|
jp [[ call Sub1 :: inc a :: add a :: ret ]]
|
||||||
|
]]
|
||||||
|
]]
|
||||||
|
.Ed
|
||||||
.Sh SYMBOLS
|
.Sh SYMBOLS
|
||||||
RGBDS supports several types of symbols:
|
RGBDS supports several types of symbols:
|
||||||
.Bl -hang
|
.Bl -hang
|
||||||
@@ -1551,7 +1688,8 @@ $ rgbasm -o a.o a.asm
|
|||||||
$ rgbasm -o b.o b.asm
|
$ rgbasm -o b.o b.asm
|
||||||
$ rgbasm -o c.o c.asm
|
$ rgbasm -o c.o c.asm
|
||||||
$ rgblink a.o b.o c.o
|
$ rgblink a.o b.o c.o
|
||||||
error: c.asm(2): Unknown symbol "LabelA"
|
error: Undefined symbol "LabelA"
|
||||||
|
at c.asm(2)
|
||||||
Linking failed with 1 error
|
Linking failed with 1 error
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
@@ -1580,8 +1718,6 @@ The following symbols are defined by the assembler:
|
|||||||
.It Dv .. Ta Ic EQUS Ta The current local label scope
|
.It Dv .. Ta Ic EQUS Ta The current local label scope
|
||||||
.It Dv _RS Ta Ic = Ta _RS Counter
|
.It Dv _RS Ta Ic = Ta _RS Counter
|
||||||
.It Dv _NARG Ta Ic EQU Ta Number of arguments passed to macro, updated by Ic SHIFT
|
.It Dv _NARG Ta Ic EQU Ta Number of arguments passed to macro, updated by Ic SHIFT
|
||||||
.It Dv __DATE__ Ta Ic EQUS Ta Today's date
|
|
||||||
.It Dv __TIME__ Ta Ic EQUS Ta The current time
|
|
||||||
.It Dv __ISO_8601_LOCAL__ Ta Ic EQUS Ta ISO 8601 timestamp (local)
|
.It Dv __ISO_8601_LOCAL__ Ta Ic EQUS Ta ISO 8601 timestamp (local)
|
||||||
.It Dv __ISO_8601_UTC__ Ta Ic EQUS Ta ISO 8601 timestamp (UTC)
|
.It Dv __ISO_8601_UTC__ Ta Ic EQUS Ta ISO 8601 timestamp (UTC)
|
||||||
.It Dv __UTC_YEAR__ Ta Ic EQU Ta Today's year
|
.It Dv __UTC_YEAR__ Ta Ic EQU Ta Today's year
|
||||||
@@ -1675,10 +1811,9 @@ Use
|
|||||||
.Ic INCBIN
|
.Ic INCBIN
|
||||||
to include a raw binary file as it is.
|
to include a raw binary file as it is.
|
||||||
If the file isn't found in the current directory, the include-path list passed to
|
If the file isn't found in the current directory, the include-path list passed to
|
||||||
.Xr rgbasm 1
|
.Xr rgbasm 1 Ap s
|
||||||
(see the
|
|
||||||
.Fl I
|
.Fl I
|
||||||
option) on the command line will be searched.
|
option on the command line will be searched.
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
INCBIN "titlepic.bin"
|
INCBIN "titlepic.bin"
|
||||||
INCBIN "sprites/hero.bin"
|
INCBIN "sprites/hero.bin"
|
||||||
@@ -2231,11 +2366,10 @@ block, all of them but the first one are ignored.
|
|||||||
Use
|
Use
|
||||||
.Ic INCLUDE
|
.Ic INCLUDE
|
||||||
to process another assembler file and then return to the current file when done.
|
to process another assembler file and then return to the current file when done.
|
||||||
If the file isn't found in the current directory, the include path list (see the
|
If the file isn't found in the current directory, the include-path list passed to
|
||||||
|
.Xr rgbasm 1 Ap s
|
||||||
.Fl I
|
.Fl I
|
||||||
option in
|
option on the command line will be searched.
|
||||||
.Xr rgbasm 1 )
|
|
||||||
will be searched.
|
|
||||||
You may nest
|
You may nest
|
||||||
.Ic INCLUDE
|
.Ic INCLUDE
|
||||||
calls infinitely (or until you run out of memory, whichever comes first).
|
calls infinitely (or until you run out of memory, whichever comes first).
|
||||||
@@ -2356,7 +2490,8 @@ can be used to change some of the options during assembling from within the sour
|
|||||||
takes a comma-separated list of options as its argument:
|
takes a comma-separated list of options as its argument:
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
PUSHO
|
PUSHO
|
||||||
OPT g.oOX, Wdiv ; acts like command-line -g.oOX -Wdiv
|
OPT g.oOX, Wdiv ; acts like command-line `-g.oOX -Wdiv`
|
||||||
|
OPT -Wdiv ; dashes before the options are optional
|
||||||
DW `..ooOOXX ; uses the graphics constant characters from OPT g
|
DW `..ooOOXX ; uses the graphics constant characters from OPT g
|
||||||
PRINTLN $80000000/-1 ; prints a warning about division
|
PRINTLN $80000000/-1 ; prints a warning about division
|
||||||
POPO
|
POPO
|
||||||
@@ -2389,6 +2524,68 @@ PUSHO b.X, g.oOX
|
|||||||
DW `..ooOOXX
|
DW `..ooOOXX
|
||||||
POPO
|
POPO
|
||||||
.Ed
|
.Ed
|
||||||
|
.Ss Excluding locations from backtraces
|
||||||
|
Errors and warnings print
|
||||||
|
.Em backtraces
|
||||||
|
showing the location in the source file where the problem occurred, tracing the origin of the problem even through a chain of
|
||||||
|
.Ic REPT ,
|
||||||
|
.Ic FOR ,
|
||||||
|
.Ic MACRO ,
|
||||||
|
and
|
||||||
|
.Ic INCLUDE
|
||||||
|
locations.
|
||||||
|
Sometimes there are locations you would like to ignore; for example, a common utility macro when you only care about the line where the macro is used, or an
|
||||||
|
.Ic INCLUDE
|
||||||
|
file that only serves to include other files and is just filler in the backtrace.
|
||||||
|
.Pp
|
||||||
|
In those cases, you can
|
||||||
|
.Em silence
|
||||||
|
a location with a question mark
|
||||||
|
.Sq \&?
|
||||||
|
after the token: all of the locations created by a
|
||||||
|
.Sq REPT? ,
|
||||||
|
.Sq FOR? ,
|
||||||
|
or
|
||||||
|
.Sq MACRO?
|
||||||
|
will not be printed, and any location created by a
|
||||||
|
.Sq INCLUDE? ,
|
||||||
|
or a macro invocation whose name is immediately followed by a
|
||||||
|
.Sq \&? ,
|
||||||
|
will not be printed.
|
||||||
|
For example, if this were assembled as
|
||||||
|
.Ql example.asm :
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
MACRO lb
|
||||||
|
assert -128 <= (\e2) && (\e2) < 256, "\e2 is not a byte"
|
||||||
|
assert -128 <= (\e3) && (\e3) < 256, "\e3 is not a byte"
|
||||||
|
ld \e1, (LOW(\e2) << 8) | LOW(\e3)
|
||||||
|
ENDM
|
||||||
|
SECTION "Code", ROM0
|
||||||
|
lb hl, $123, $45
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
This would print an error backtrace:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
error: Assertion failed: $123 is not a byte
|
||||||
|
at example.asm::lb(2)
|
||||||
|
<- example.asm(7)
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
But if
|
||||||
|
.Ql MACRO
|
||||||
|
were changed to
|
||||||
|
.Ql MACRO? ,
|
||||||
|
or
|
||||||
|
.Ql lb hl
|
||||||
|
were changed to
|
||||||
|
.Ql lb? hl ,
|
||||||
|
then the error backtrace would not mention the location within the
|
||||||
|
.Ql lb
|
||||||
|
macro:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
error: Assertion failed: $123 is not a byte
|
||||||
|
at example.asm(7)
|
||||||
|
.Ed
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr rgbasm 1 ,
|
.Xr rgbasm 1 ,
|
||||||
.Xr rgblink 1 ,
|
.Xr rgblink 1 ,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd May 4, 2025
|
.Dd September 1, 2025
|
||||||
.Dt RGBDS 5
|
.Dt RGBDS 5
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -79,12 +79,16 @@ order, meaning the node with ID 0 is the last one in the list!
|
|||||||
.It Cm LONG Ar ParentLineNo
|
.It Cm LONG Ar ParentLineNo
|
||||||
Line at which the parent node's context was exited; meaningless for the root node.
|
Line at which the parent node's context was exited; meaningless for the root node.
|
||||||
.It Cm BYTE Ar Type
|
.It Cm BYTE Ar Type
|
||||||
|
Bits 0\(en6 indicate the node's type:
|
||||||
.Bl -column "Value" -compact
|
.Bl -column "Value" -compact
|
||||||
.It Sy Value Ta Sy Meaning
|
.It Sy Value Ta Sy Meaning
|
||||||
.It 0 Ta REPT node
|
.It 0 Ta REPT node
|
||||||
.It 1 Ta File node
|
.It 1 Ta File node
|
||||||
.It 2 Ta Macro node
|
.It 2 Ta Macro node
|
||||||
.El
|
.El
|
||||||
|
.Pp
|
||||||
|
Bit\ 7 being set means that the node is "quieted"
|
||||||
|
.Pq see Do Excluding locations from backtraces Dc in Xr rgbasm 5 .
|
||||||
.It Cm IF Ar Type No \(!= 0
|
.It Cm IF Ar Type No \(!= 0
|
||||||
If the node is not a REPT node...
|
If the node is not a REPT node...
|
||||||
.Pp
|
.Pp
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd May 4, 2025
|
.Dd September 1, 2025
|
||||||
.Dt RGBDS 7
|
.Dt RGBDS 7
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -57,7 +57,7 @@ to this day.
|
|||||||
.It
|
.It
|
||||||
2015-01-18:
|
2015-01-18:
|
||||||
.An stag019
|
.An stag019
|
||||||
begins implementing RGBGFX, a PNG‐to‐Game Boy graphics converter, for eventual integration into RGBDS.
|
begins implementing RGBGFX, a PNG-to-Game Boy graphics converter, for eventual integration into RGBDS.
|
||||||
.It
|
.It
|
||||||
2016-09-05: RGBGFX is integrated into Bentley's repository.
|
2016-09-05: RGBGFX is integrated into Bentley's repository.
|
||||||
.It
|
.It
|
||||||
|
|||||||
124
man/rgbfix.1
124
man/rgbfix.1
@@ -1,6 +1,6 @@
|
|||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd May 4, 2025
|
.Dd September 1, 2025
|
||||||
.Dt RGBFIX 1
|
.Dt RGBFIX 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -8,8 +8,9 @@
|
|||||||
.Nd Game Boy header utility and checksum fixer
|
.Nd Game Boy header utility and checksum fixer
|
||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm
|
.Nm
|
||||||
.Op Fl hjOsVv
|
.Op Fl hjsVvw
|
||||||
.Op Fl C | c
|
.Op Fl C | c
|
||||||
|
.Op Fl \-color Ar when
|
||||||
.Op Fl f Ar fix_spec
|
.Op Fl f Ar fix_spec
|
||||||
.Op Fl i Ar game_id
|
.Op Fl i Ar game_id
|
||||||
.Op Fl k Ar licensee_str
|
.Op Fl k Ar licensee_str
|
||||||
@@ -21,7 +22,8 @@
|
|||||||
.Op Fl p Ar pad_value
|
.Op Fl p Ar pad_value
|
||||||
.Op Fl r Ar ram_size
|
.Op Fl r Ar ram_size
|
||||||
.Op Fl t Ar title_str
|
.Op Fl t Ar title_str
|
||||||
.Op Ar
|
.Op Fl W Ar warning
|
||||||
|
.Ar
|
||||||
.Sh DESCRIPTION
|
.Sh DESCRIPTION
|
||||||
The
|
The
|
||||||
.Nm
|
.Nm
|
||||||
@@ -39,19 +41,19 @@ and to have already populated whichever fields they don't specify using
|
|||||||
.Nm .
|
.Nm .
|
||||||
.Pp
|
.Pp
|
||||||
The input
|
The input
|
||||||
.Ar asmfile
|
.Ar file
|
||||||
can be a path to a file, or
|
can be a path to a file, or
|
||||||
.Cm \-
|
.Cm \-
|
||||||
to read from standard input.
|
to read from standard input.
|
||||||
.Pp
|
.Pp
|
||||||
Note that options can be abbreviated as long as the abbreviation is unambiguous:
|
Note that options can be abbreviated as long as the abbreviation is unambiguous:
|
||||||
.Fl \-color-o
|
.Fl \-verb
|
||||||
is
|
is
|
||||||
.Fl \-color-only ,
|
.Fl \-verbose ,
|
||||||
but
|
but
|
||||||
.Fl \-color
|
.Fl \-ver
|
||||||
is invalid because it could also be
|
is invalid because it could also be
|
||||||
.Fl \-color-compatible .
|
.Fl \-version .
|
||||||
Options later in the command line override those set earlier.
|
Options later in the command line override those set earlier.
|
||||||
Accepted options are as follows:
|
Accepted options are as follows:
|
||||||
.Bl -tag -width Ds
|
.Bl -tag -width Ds
|
||||||
@@ -69,6 +71,18 @@ to 0x80.
|
|||||||
This overrides
|
This overrides
|
||||||
.Fl c
|
.Fl c
|
||||||
if it was set prior.
|
if it was set prior.
|
||||||
|
.It Fl \-color Ar when
|
||||||
|
Specify when to highlight warning and error messages with color:
|
||||||
|
.Ql always ,
|
||||||
|
.Ql never ,
|
||||||
|
or
|
||||||
|
.Ql auto .
|
||||||
|
.Ql auto
|
||||||
|
determines whether to use colors based on the
|
||||||
|
.Ql Lk https://no-color.org/ NO_COLOR
|
||||||
|
or
|
||||||
|
.Ql Lk https://force-color.org/ FORCE_COLOR
|
||||||
|
environment variables, or whether the output is to a TTY.
|
||||||
.It Fl f Ar fix_spec , Fl \-fix-spec Ar fix_spec
|
.It Fl f Ar fix_spec , Fl \-fix-spec Ar fix_spec
|
||||||
Fix certain header values that the Game Boy checks for correctness.
|
Fix certain header values that the Game Boy checks for correctness.
|
||||||
Alternatively, intentionally trash these values by writing their binary inverse instead.
|
Alternatively, intentionally trash these values by writing their binary inverse instead.
|
||||||
@@ -98,7 +112,7 @@ Print help text for the program and exit.
|
|||||||
Set the game ID string
|
Set the game ID string
|
||||||
.Pq Ad 0x13F Ns \(en Ns Ad 0x142
|
.Pq Ad 0x13F Ns \(en Ns Ad 0x142
|
||||||
to a given string.
|
to a given string.
|
||||||
If it's longer than 4 chars, it will be truncated, and a warning emitted.
|
If it's longer than 4 characters, it will be truncated.
|
||||||
.It Fl j , Fl \-non-japanese
|
.It Fl j , Fl \-non-japanese
|
||||||
Set the non-Japanese region flag
|
Set the non-Japanese region flag
|
||||||
.Pq Ad 0x14A
|
.Pq Ad 0x14A
|
||||||
@@ -107,7 +121,7 @@ to 0x01.
|
|||||||
Set the new licensee string
|
Set the new licensee string
|
||||||
.Pq Ad 0x144 Ns \(en Ns Ad 0x145
|
.Pq Ad 0x144 Ns \(en Ns Ad 0x145
|
||||||
to a given string.
|
to a given string.
|
||||||
If it's longer than 2 chars, it will be truncated, and a warning emitted.
|
If it's longer than 2 characters, it will be truncated.
|
||||||
.It Fl L Ar logo_file , Fl \-logo Ar logo_file
|
.It Fl L Ar logo_file , Fl \-logo Ar logo_file
|
||||||
Specify a logo file to use instead of the official Nintendo logo.
|
Specify a logo file to use instead of the official Nintendo logo.
|
||||||
The file must be 48 bytes of 1bpp tile data; the source image should be 48 pixels wide and 8 pixels tall.
|
The file must be 48 bytes of 1bpp tile data; the source image should be 48 pixels wide and 8 pixels tall.
|
||||||
@@ -124,6 +138,8 @@ to a given value from 0 to 0xFF.
|
|||||||
This value may also be an MBC name.
|
This value may also be an MBC name.
|
||||||
The list of accepted names can be obtained by passing
|
The list of accepted names can be obtained by passing
|
||||||
.Ql Cm help
|
.Ql Cm help
|
||||||
|
or
|
||||||
|
.Ql Cm list
|
||||||
as the argument.
|
as the argument.
|
||||||
Any amount of whitespace (space and tabs) is allowed around plus signs, and the order of "components" is free, as long as the MBC name is first.
|
Any amount of whitespace (space and tabs) is allowed around plus signs, and the order of "components" is free, as long as the MBC name is first.
|
||||||
There are special considerations to take for the TPP1 mapper; see the
|
There are special considerations to take for the TPP1 mapper; see the
|
||||||
@@ -133,8 +149,6 @@ section below.
|
|||||||
Set the ROM version
|
Set the ROM version
|
||||||
.Pq Ad 0x14C
|
.Pq Ad 0x14C
|
||||||
to a given value from 0 to 0xFF.
|
to a given value from 0 to 0xFF.
|
||||||
.It Fl O , Fl \-overwrite
|
|
||||||
Allow overwriting different non-zero bytes in the header without a warning being emitted.
|
|
||||||
.It Fl o Ar out_file , Fl \-output Ar out_file
|
.It Fl o Ar out_file , Fl \-output Ar out_file
|
||||||
Write the modified ROM image to the given file, or '-' to write to standard output.
|
Write the modified ROM image to the given file, or '-' to write to standard output.
|
||||||
If not specified, the input files are modified in-place, or written to standard output if read from standard input.
|
If not specified, the input files are modified in-place, or written to standard output if read from standard input.
|
||||||
@@ -154,15 +168,14 @@ to a given value from 0 to 0xFF.
|
|||||||
Set the SGB flag
|
Set the SGB flag
|
||||||
.Pq Ad 0x146
|
.Pq Ad 0x146
|
||||||
to 0x03.
|
to 0x03.
|
||||||
This flag will be ignored by the SGB unless the old licensee code is 0x33!
|
This flag will be ignored by the SGB unless the old licensee code
|
||||||
If this is given as well as
|
.Pq Fl l
|
||||||
.Fl l ,
|
is 0x33!
|
||||||
but is not set to 0x33, a warning will be printed.
|
|
||||||
.It Fl t Ar title , Fl \-title Ar title
|
.It Fl t Ar title , Fl \-title Ar title
|
||||||
Set the title string
|
Set the title string
|
||||||
.Pq Ad 0x134 Ns \(en Ns Ad 0x143
|
.Pq Ad 0x134 Ns \(en Ns Ad 0x143
|
||||||
to a given string.
|
to a given string.
|
||||||
If the title is longer than the max length, it will be truncated, and a warning emitted.
|
If the title is longer than the maximum length, it will be truncated.
|
||||||
The max length is 11 characters if the game ID
|
The max length is 11 characters if the game ID
|
||||||
.Pq Fl i
|
.Pq Fl i
|
||||||
is specified, 15 characters if the CGB flag
|
is specified, 15 characters if the CGB flag
|
||||||
@@ -175,6 +188,79 @@ Print the version of the program and exit.
|
|||||||
.It Fl v , Fl \-validate
|
.It Fl v , Fl \-validate
|
||||||
Equivalent to
|
Equivalent to
|
||||||
.Fl f Cm lhg .
|
.Fl f Cm lhg .
|
||||||
|
.It Fl W Ar warning , Fl \-warning Ar warning
|
||||||
|
Set warning flag
|
||||||
|
.Ar warning .
|
||||||
|
A warning message will be printed if
|
||||||
|
.Ar warning
|
||||||
|
is an unknown warning flag.
|
||||||
|
See the
|
||||||
|
.Sx DIAGNOSTICS
|
||||||
|
section for a list of warnings.
|
||||||
|
.It Fl w
|
||||||
|
Disable all warning output, even when turned into errors.
|
||||||
|
.El
|
||||||
|
.Sh DIAGNOSTICS
|
||||||
|
Warnings are diagnostic messages that indicate possibly erroneous behavior that does not necessarily compromise the header-fixing process.
|
||||||
|
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 or meta warning into an error.
|
||||||
|
A warning's name is appended
|
||||||
|
.Pq example: Fl Werror=overwrite ,
|
||||||
|
and this warning is implicitly enabled and turned into an error.
|
||||||
|
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
|
||||||
|
.Dq meta
|
||||||
|
warnings, that enable a collection of other warnings.
|
||||||
|
If a specific warning is toggled via a meta flag and a specific one, the more specific one takes priority.
|
||||||
|
The position on the command-line acts as a tie breaker, the last one taking effect.
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It Fl Wall
|
||||||
|
This enables warnings that are likely to indicate an error or undesired behavior, and that can easily be fixed.
|
||||||
|
.It Fl Weverything
|
||||||
|
Enables literally every warning.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
The following warnings are actual warning flags; with each description, the corresponding warning flag is included.
|
||||||
|
Note that each of these flag also has a negation (for example,
|
||||||
|
.Fl Wtruncation
|
||||||
|
enables the warning that
|
||||||
|
.Fl Wno-truncation
|
||||||
|
disables; and
|
||||||
|
.Fl Wall
|
||||||
|
enables every warning that
|
||||||
|
.Fl Wno-all
|
||||||
|
disables).
|
||||||
|
Only the non-default flag is listed here.
|
||||||
|
Ignoring the
|
||||||
|
.Dq no-
|
||||||
|
prefix, entries are listed alphabetically.
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It Fl Wno-mbc
|
||||||
|
Warn when there are inconsistencies with or caveats about the specified MBC type.
|
||||||
|
.It Fl Wno-obsolete
|
||||||
|
Warn when obsolete features are encountered, which have been deprecated and may later be removed.
|
||||||
|
.It Fl Wno-overwrite
|
||||||
|
Warn when overwriting different non-zero bytes in the header.
|
||||||
|
.It Fl Wno-sgb
|
||||||
|
Warn when the SGB flag
|
||||||
|
.Pq Fl s
|
||||||
|
conflicts with the old licensee code
|
||||||
|
.Pq Fl l .
|
||||||
|
.It Fl Wno-truncation
|
||||||
|
Warn when truncating values to fit the available space.
|
||||||
.El
|
.El
|
||||||
.Sh EXAMPLES
|
.Sh EXAMPLES
|
||||||
Most values in the ROM header do not matter to the actual console, and most are seldom useful anyway.
|
Most values in the ROM header do not matter to the actual console, and most are seldom useful anyway.
|
||||||
@@ -228,7 +314,7 @@ Therefore,
|
|||||||
.Nm
|
.Nm
|
||||||
will ignore the
|
will ignore the
|
||||||
.Ql RAM
|
.Ql RAM
|
||||||
feature on a TPP1 mapper with a warning.
|
feature on a TPP1 mapper.
|
||||||
.Ss Special considerations
|
.Ss Special considerations
|
||||||
TPP1 overwrites the byte at
|
TPP1 overwrites the byte at
|
||||||
.Ad 0x14A ,
|
.Ad 0x14A ,
|
||||||
@@ -241,7 +327,7 @@ will warn about and ignore
|
|||||||
.Fl j
|
.Fl j
|
||||||
if used in combination with TPP1.
|
if used in combination with TPP1.
|
||||||
.Sh BUGS
|
.Sh BUGS
|
||||||
Please report bugs on
|
Please report bugs or mistakes in this documentation on
|
||||||
.Lk https://github.com/gbdev/rgbds/issues GitHub .
|
.Lk https://github.com/gbdev/rgbds/issues GitHub .
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr rgbasm 1 ,
|
.Xr rgbasm 1 ,
|
||||||
|
|||||||
158
man/rgbgfx.1
158
man/rgbgfx.1
@@ -2,7 +2,7 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd May 4, 2025
|
.Dd September 1, 2025
|
||||||
.Dt RGBGFX 1
|
.Dt RGBGFX 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -10,14 +10,16 @@
|
|||||||
.Nd Game Boy graphics converter
|
.Nd Game Boy graphics converter
|
||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm
|
.Nm
|
||||||
.Op Fl CmhOuVXYZ
|
.Op Fl CmhOuVwXYZ
|
||||||
.Op Fl v Op Fl v No ...
|
.Op Fl v Op Fl v No ...
|
||||||
.Op Fl a Ar attrmap | Fl A
|
.Op Fl a Ar attrmap | Fl A
|
||||||
.Op Fl b Ar base_ids
|
.Op Fl b Ar base_ids
|
||||||
.Op Fl c Ar pal_spec
|
.Op Fl c Ar pal_spec
|
||||||
|
.Op Fl \-color Ar when
|
||||||
.Op Fl d Ar depth
|
.Op Fl d Ar depth
|
||||||
.Op Fl i Ar input_tiles
|
.Op Fl i Ar input_tiles
|
||||||
.Op Fl L Ar slice
|
.Op Fl L Ar slice
|
||||||
|
.Op Fl l Ar base_pal
|
||||||
.Op Fl N Ar nb_tiles
|
.Op Fl N Ar nb_tiles
|
||||||
.Op Fl n Ar nb_pals
|
.Op Fl n Ar nb_pals
|
||||||
.Op Fl o Ar out_file
|
.Op Fl o Ar out_file
|
||||||
@@ -26,6 +28,7 @@
|
|||||||
.Op Fl r Ar width
|
.Op Fl r Ar width
|
||||||
.Op Fl s Ar nb_colors
|
.Op Fl s Ar nb_colors
|
||||||
.Op Fl t Ar tilemap | Fl T
|
.Op Fl t Ar tilemap | Fl T
|
||||||
|
.Op Fl W Ar warning
|
||||||
.Op Fl x Ar quantity
|
.Op Fl x Ar quantity
|
||||||
.Ar file
|
.Ar file
|
||||||
.Sh DESCRIPTION
|
.Sh DESCRIPTION
|
||||||
@@ -159,6 +162,41 @@ is the case-insensitive word
|
|||||||
then the first four colors of the input PNG's embedded palette are used.
|
then the first four colors of the input PNG's embedded palette are used.
|
||||||
It is an error if the PNG is not indexed, or if colors other than these 4 are used.
|
It is an error if the PNG is not indexed, or if colors other than these 4 are used.
|
||||||
.Pq This is different from the default behavior of indexed PNGs, as then unused entries in the embedded palette are ignored, whereas they are not with Fl c Cm embedded .
|
.Pq This is different from the default behavior of indexed PNGs, as then unused entries in the embedded palette are ignored, whereas they are not with Fl c Cm embedded .
|
||||||
|
.It Sy DMG palette spec
|
||||||
|
If
|
||||||
|
.Ar pal_spec
|
||||||
|
starts with case-insensitive
|
||||||
|
.Cm dmg= ,
|
||||||
|
then the following two-digit hexadecimal number specifies four grayscale DMG color indexes.
|
||||||
|
The number functions like the DMG's $FF47
|
||||||
|
.Sy BGP
|
||||||
|
register
|
||||||
|
(see
|
||||||
|
.Lk https://gbdev.io/pandocs/Palettes.html Pan Docs
|
||||||
|
for more information):
|
||||||
|
the low two bits 0-1 specify which gray shade goes in color index 0,
|
||||||
|
the next two bits 2-3 specify which gray shade goes in color index 1,
|
||||||
|
and so on.
|
||||||
|
Gray shade 0 is the lightest (white), 3 is the darkest (black).
|
||||||
|
If
|
||||||
|
.Ar pal_spec
|
||||||
|
is the case-insensitive word
|
||||||
|
.Cm dmg ,
|
||||||
|
then it acts like
|
||||||
|
.Cm dmg=E4 ,
|
||||||
|
i.e. the darkest gray will end up in color index 0, and so on.
|
||||||
|
The same gray shade cannot go in two color indexes.
|
||||||
|
To specify a DMG palette, the input PNG must have all its colors in shades of gray, without any transparent colors.
|
||||||
|
.It Sy automatic palette generation
|
||||||
|
If
|
||||||
|
.Ar pal_spec
|
||||||
|
is the case-insensitive word
|
||||||
|
.Cm auto ,
|
||||||
|
then a palette is automatically generated using the procedure described in
|
||||||
|
.Sx PALETTE GENERATION .
|
||||||
|
This is the default behavior if
|
||||||
|
.Fl c
|
||||||
|
was not specified.
|
||||||
.It Sy external palette spec
|
.It Sy external palette spec
|
||||||
Otherwise,
|
Otherwise,
|
||||||
.Ar pal_spec
|
.Ar pal_spec
|
||||||
@@ -175,6 +213,18 @@ See
|
|||||||
.Sx PALETTE SPECIFICATION FORMATS
|
.Sx PALETTE SPECIFICATION FORMATS
|
||||||
for a list of formats and their descriptions.
|
for a list of formats and their descriptions.
|
||||||
.El
|
.El
|
||||||
|
.It Fl \-color Ar when
|
||||||
|
Specify when to highlight warning and error messages with color:
|
||||||
|
.Ql always ,
|
||||||
|
.Ql never ,
|
||||||
|
or
|
||||||
|
.Ql auto .
|
||||||
|
.Ql auto
|
||||||
|
determines whether to use colors based on the
|
||||||
|
.Ql Lk https://no-color.org/ NO_COLOR
|
||||||
|
or
|
||||||
|
.Ql Lk https://force-color.org/ FORCE_COLOR
|
||||||
|
environment variables, or whether the output is to a TTY.
|
||||||
.It Fl d Ar depth , Fl \-depth Ar depth
|
.It Fl d Ar depth , Fl \-depth Ar depth
|
||||||
Set the bit depth of the output tile data, in bits per pixel (bpp), either 1 or 2 (the default).
|
Set the bit depth of the output tile data, in bits per pixel (bpp), either 1 or 2 (the default).
|
||||||
This changes how tile data is output, and the maximum number of colors per palette (2 and 4 respectively).
|
This changes how tile data is output, and the maximum number of colors per palette (2 and 4 respectively).
|
||||||
@@ -243,6 +293,11 @@ The first number pair specifies the X and Y coordinates of the top-left pixel th
|
|||||||
The second number pair specifies how many tiles to process horizontally and vertically, respectively.
|
The second number pair specifies how many tiles to process horizontally and vertically, respectively.
|
||||||
.Pp
|
.Pp
|
||||||
.Fl L Sy is ignored in reverse mode , No no padding is inserted .
|
.Fl L Sy is ignored in reverse mode , No no padding is inserted .
|
||||||
|
.It Fl l Ar base_pal , Fl \-base-palette Ar base_pal
|
||||||
|
Set the base ID for attribute map and palette map output.
|
||||||
|
.Ar base_pal
|
||||||
|
should be a number between 0 and 255.
|
||||||
|
It defaults to 0.
|
||||||
.It Fl m , Fl \-mirror-tiles
|
.It Fl m , Fl \-mirror-tiles
|
||||||
Deduplicate tiles that are horizontally and/or vertically symmetrical mirror images of each other.
|
Deduplicate tiles that are horizontally and/or vertically symmetrical mirror images of each other.
|
||||||
Only one of each unique tile will be saved in the tile data file, with mirror images counting as duplicates.
|
Only one of each unique tile will be saved in the tile data file, with mirror images counting as duplicates.
|
||||||
@@ -357,18 +412,32 @@ Be verbose.
|
|||||||
The verbosity level is increased by one each time the flag is specified, with each level including the previous:
|
The verbosity level is increased by one each time the flag is specified, with each level including the previous:
|
||||||
.Bl -enum -compact
|
.Bl -enum -compact
|
||||||
.It
|
.It
|
||||||
|
Print the
|
||||||
.Nm
|
.Nm
|
||||||
prints out its configuration before doing anything.
|
configuration before taking actions.
|
||||||
.It
|
.It
|
||||||
A generic message is printed before doing most actions.
|
Print a notice before significant actions.
|
||||||
.It
|
.It
|
||||||
Some of the actions' intermediate results are printed.
|
Print some of the actions' intermediate results.
|
||||||
.It
|
.It
|
||||||
Some internal debug printing is enabled.
|
Print some internal debug information.
|
||||||
|
.It
|
||||||
|
Print detailed internal information.
|
||||||
.El
|
.El
|
||||||
The verbosity level does not go past 6.
|
The verbosity level does not go past 6.
|
||||||
.Pp
|
.Pp
|
||||||
Note that verbose output is only intended to be consumed by humans, and may change without notice between RGBDS releases; relying on those for scripts is not advised.
|
Note that verbose output is only intended to be consumed by humans, and may change without notice between RGBDS releases; relying on those for scripts is not advised.
|
||||||
|
.It Fl W Ar warning , Fl \-warning Ar warning
|
||||||
|
Set warning flag
|
||||||
|
.Ar warning .
|
||||||
|
A warning message will be printed if
|
||||||
|
.Ar warning
|
||||||
|
is an unknown warning flag.
|
||||||
|
See the
|
||||||
|
.Sx DIAGNOSTICS
|
||||||
|
section for a list of warnings.
|
||||||
|
.It Fl w
|
||||||
|
Disable all warning output, even when turned into errors.
|
||||||
.It Fl X , Fl \-mirror-x
|
.It Fl X , Fl \-mirror-x
|
||||||
Deduplicate tiles that are horizontally symmetrical mirror images of each other across the X axis.
|
Deduplicate tiles that are horizontally symmetrical mirror images of each other across the X axis.
|
||||||
Implies
|
Implies
|
||||||
@@ -480,6 +549,9 @@ Useful to force several images to share the same palette.
|
|||||||
Plaintext lines of hexadecimal colors in
|
Plaintext lines of hexadecimal colors in
|
||||||
.Ql rrggbb
|
.Ql rrggbb
|
||||||
format.
|
format.
|
||||||
|
.It Cm png
|
||||||
|
An image of square color swatches, with each row defining the colors for one palette.
|
||||||
|
Color swatches can be any square size.
|
||||||
.It Cm psp
|
.It Cm psp
|
||||||
.Lk https://www.selapa.net/swatches/colors/fileformats.php#psp_pal Paint Shop Pro palette .
|
.Lk https://www.selapa.net/swatches/colors/fileformats.php#psp_pal Paint Shop Pro palette .
|
||||||
.El
|
.El
|
||||||
@@ -528,6 +600,8 @@ Otherwise, if the PNG only contains shades of gray, they will be categorized int
|
|||||||
.Dq bins
|
.Dq bins
|
||||||
as there are colors per palette, and the palette is set to these bins.
|
as there are colors per palette, and the palette is set to these bins.
|
||||||
The darkest gray will end up in bin #0, and so on; note that this is the opposite of the RGB method below.
|
The darkest gray will end up in bin #0, and so on; note that this is the opposite of the RGB method below.
|
||||||
|
This is equivalent to having specified a DMG palette of
|
||||||
|
.Fl c Cm dmg=E4 .
|
||||||
If two distinct grays end up in the same bin, the RGB method is used instead.
|
If two distinct grays end up in the same bin, the RGB method is used instead.
|
||||||
.Pp
|
.Pp
|
||||||
Be careful that
|
Be careful that
|
||||||
@@ -559,9 +633,10 @@ behavior depends on an internal detail of how the PNG is saved, specifically its
|
|||||||
chunk.
|
chunk.
|
||||||
Since few image editors (such as GIMP) expose that detail, this behavior is only kept for compatibility and should be considered deprecated.
|
Since few image editors (such as GIMP) expose that detail, this behavior is only kept for compatibility and should be considered deprecated.
|
||||||
.Pp
|
.Pp
|
||||||
It turns out that palette generation is an NP-complete problem, so
|
It turns out that palette generation is an NP-complete problem known as "pagination", so
|
||||||
.Nm
|
.Nm
|
||||||
does not attempt to find the optimal solution, but instead to find a good one in a reasonable amount of time.
|
does not attempt to find the optimal solution, but instead uses an "overload-and-remove" heuristic to find a good one in a reasonable amount of time.
|
||||||
|
(There are no guarantees about how this algorithm will generate palettes, apart from the constraints documented above.)
|
||||||
It is possible to compute the optimal solution externally (using a solver, for example), and then provide it to
|
It is possible to compute the optimal solution externally (using a solver, for example), and then provide it to
|
||||||
.Nm
|
.Nm
|
||||||
via
|
via
|
||||||
@@ -682,6 +757,70 @@ assume that tiles were not deduplicated, and should be laid out in the order the
|
|||||||
.Nm
|
.Nm
|
||||||
assumes that no tiles were mirrored.
|
assumes that no tiles were mirrored.
|
||||||
.El
|
.El
|
||||||
|
.Sh DIAGNOSTICS
|
||||||
|
Warnings are diagnostic messages that indicate possibly erroneous behavior that does not necessarily compromise the conversion process.
|
||||||
|
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 or meta warning into an error.
|
||||||
|
A warning's name is appended
|
||||||
|
.Pq example: Fl Werror=embedded ,
|
||||||
|
and this warning is implicitly enabled and turned into an error.
|
||||||
|
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
|
||||||
|
.Dq meta
|
||||||
|
warnings, that enable a collection of other warnings.
|
||||||
|
If a specific warning is toggled via a meta flag and a specific one, the more specific one takes priority.
|
||||||
|
The position on the command-line acts as a tie breaker, the last one taking effect.
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It Fl Wall
|
||||||
|
This enables warnings that are likely to indicate an error or undesired behavior, and that can easily be fixed.
|
||||||
|
.It Fl Weverything
|
||||||
|
Enables literally every warning.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
The following warnings are actual warning flags; with each description, the corresponding warning flag is included.
|
||||||
|
Note that each of these flag also has a negation (for example,
|
||||||
|
.Fl Wtrim-nonempty
|
||||||
|
enables the warning that
|
||||||
|
.Fl Wno-trim-nonempty
|
||||||
|
disables; and
|
||||||
|
.Fl Wall
|
||||||
|
enables every warning that
|
||||||
|
.Fl Wno-all
|
||||||
|
disables).
|
||||||
|
Only the non-default flag is listed here.
|
||||||
|
Ignoring the
|
||||||
|
.Dq no-
|
||||||
|
prefix, entries are listed alphabetically.
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It Fl Wembedded
|
||||||
|
Warn when a generated palette is sorted according to the input PNG's embedded palette but
|
||||||
|
.Fl c Cm embedded
|
||||||
|
was not provided.
|
||||||
|
This warning is enabled by
|
||||||
|
.Fl Weverything .
|
||||||
|
.It Fl Wno-obsolete
|
||||||
|
Warn when obsolete features are encountered, which have been deprecated and may later be removed.
|
||||||
|
.It Fl Wtrim-nonempty
|
||||||
|
Warn when
|
||||||
|
.Fl x
|
||||||
|
trims a nonempty tile.
|
||||||
|
An "empty" tile uses entirely color 0 of its palette.
|
||||||
|
This warning is enabled by
|
||||||
|
.Fl Wall .
|
||||||
|
.El
|
||||||
.Sh EXAMPLES
|
.Sh EXAMPLES
|
||||||
The following will only validate the
|
The following will only validate the
|
||||||
.Ql tileset.png
|
.Ql tileset.png
|
||||||
@@ -721,9 +860,8 @@ $ rgbgfx level1.png -i tileset.2bpp -c gbc:tileset.pal -t level1.tilemap -a leve
|
|||||||
$ rgbgfx level2.png -i tileset.2bpp -c gbc:tileset.pal -t level2.tilemap -a level2.attrmap
|
$ rgbgfx level2.png -i tileset.2bpp -c gbc:tileset.pal -t level2.tilemap -a level2.attrmap
|
||||||
.Ed
|
.Ed
|
||||||
.Sh BUGS
|
.Sh BUGS
|
||||||
Please report bugs and mistakes in this man page on
|
Please report bugs or mistakes in this documentation on
|
||||||
.Lk https://github.com/gbdev/rgbds/issues GitHub .
|
.Lk https://github.com/gbdev/rgbds/issues GitHub .
|
||||||
Bug reports and feature requests about RGBDS are also welcome!
|
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr rgbasm 1 ,
|
.Xr rgbasm 1 ,
|
||||||
.Xr rgblink 1 ,
|
.Xr rgblink 1 ,
|
||||||
|
|||||||
169
man/rgblink.1
169
man/rgblink.1
@@ -1,6 +1,6 @@
|
|||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd May 4, 2025
|
.Dd September 1, 2025
|
||||||
.Dt RGBLINK 1
|
.Dt RGBLINK 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -9,6 +9,8 @@
|
|||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm
|
.Nm
|
||||||
.Op Fl dhMtVvwx
|
.Op Fl dhMtVvwx
|
||||||
|
.Op Fl B Ar param
|
||||||
|
.Op Fl \-color Ar when
|
||||||
.Op Fl l Ar linker_script
|
.Op Fl l Ar linker_script
|
||||||
.Op Fl m Ar map_file
|
.Op Fl m Ar map_file
|
||||||
.Op Fl n Ar sym_file
|
.Op Fl n Ar sym_file
|
||||||
@@ -16,6 +18,7 @@
|
|||||||
.Op Fl o Ar out_file
|
.Op Fl o Ar out_file
|
||||||
.Op Fl p Ar pad_value
|
.Op Fl p Ar pad_value
|
||||||
.Op Fl S Ar spec
|
.Op Fl S Ar spec
|
||||||
|
.Op Fl W Ar warning
|
||||||
.Ar
|
.Ar
|
||||||
.Sh DESCRIPTION
|
.Sh DESCRIPTION
|
||||||
The
|
The
|
||||||
@@ -47,7 +50,7 @@ option, which implies
|
|||||||
but also prohibits the use of banked VRAM.
|
but also prohibits the use of banked VRAM.
|
||||||
.Pp
|
.Pp
|
||||||
The input
|
The input
|
||||||
.Ar asmfile
|
.Ar file
|
||||||
can be a path to a file, or
|
can be a path to a file, or
|
||||||
.Cm \-
|
.Cm \-
|
||||||
to read from standard input.
|
to read from standard input.
|
||||||
@@ -62,6 +65,41 @@ is invalid because it could also be
|
|||||||
.Fl \-version .
|
.Fl \-version .
|
||||||
The arguments are as follows:
|
The arguments are as follows:
|
||||||
.Bl -tag -width Ds
|
.Bl -tag -width Ds
|
||||||
|
.It Fl B Ar param , Fl \-backtrace Ar param
|
||||||
|
Configures how location backtraces are printed if warnings or errors occur.
|
||||||
|
This flag may be specified multiple times with different parameters that combine meaningfully.
|
||||||
|
If
|
||||||
|
.Ar param
|
||||||
|
is a positive number, it specifies the maximum backtrace depth, abbreviating deeper ones.
|
||||||
|
Other valid parameter values are the following:
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It Cm 0
|
||||||
|
Do not limit the maximum backtrace depth; this is the default.
|
||||||
|
.It Cm all
|
||||||
|
Force all locations to be printed, even "quiet" ones (see
|
||||||
|
.Dq Excluding locations from backtraces
|
||||||
|
in
|
||||||
|
.Xr rgbasm 5
|
||||||
|
for details).
|
||||||
|
.It Cm no-all
|
||||||
|
Do not print "quieted" locations in backtraces; this is the default.
|
||||||
|
.It Cm collapse
|
||||||
|
Print all locations on one line.
|
||||||
|
.It Cm no-collapse
|
||||||
|
Print one location per line; this is the default.
|
||||||
|
.El
|
||||||
|
.It Fl \-color Ar when
|
||||||
|
Specify when to highlight warning and error messages with color:
|
||||||
|
.Ql always ,
|
||||||
|
.Ql never ,
|
||||||
|
or
|
||||||
|
.Ql auto .
|
||||||
|
.Ql auto
|
||||||
|
determines whether to use colors based on the
|
||||||
|
.Ql Lk https://no-color.org/ NO_COLOR
|
||||||
|
or
|
||||||
|
.Ql Lk https://force-color.org/ FORCE_COLOR
|
||||||
|
environment variables, or whether the output is to a TTY.
|
||||||
.It Fl d , Fl \-dmg
|
.It Fl d , Fl \-dmg
|
||||||
Enable DMG mode.
|
Enable DMG mode.
|
||||||
Prohibit the use of sections that doesn't exist on a DMG, such as VRAM bank 1.
|
Prohibit the use of sections that doesn't exist on a DMG, such as VRAM bank 1.
|
||||||
@@ -113,7 +151,34 @@ Useful for ROMs that fit in 32 KiB.
|
|||||||
.It Fl V , Fl \-version
|
.It Fl V , Fl \-version
|
||||||
Print the version of the program and exit.
|
Print the version of the program and exit.
|
||||||
.It Fl v , Fl \-verbose
|
.It Fl v , Fl \-verbose
|
||||||
Verbose: enable printing more information to standard error.
|
Be verbose.
|
||||||
|
The verbosity level is increased by one each time the flag is specified, with each level including the previous:
|
||||||
|
.Bl -enum -compact
|
||||||
|
.It
|
||||||
|
Print the
|
||||||
|
.Nm
|
||||||
|
configuration before taking actions.
|
||||||
|
.It
|
||||||
|
Print a notice before significant actions.
|
||||||
|
.It
|
||||||
|
Print some of the actions' intermediate results.
|
||||||
|
.It
|
||||||
|
Print some internal debug information.
|
||||||
|
.It
|
||||||
|
Print detailed internal information.
|
||||||
|
.El
|
||||||
|
The verbosity level does not go past 6.
|
||||||
|
.Pp
|
||||||
|
Note that verbose output is only intended to be consumed by humans, and may change without notice between RGBDS releases; relying on those for scripts is not advised.
|
||||||
|
.It Fl W Ar warning , Fl \-warning Ar warning
|
||||||
|
Set warning flag
|
||||||
|
.Ar warning .
|
||||||
|
A warning message will be printed if
|
||||||
|
.Ar warning
|
||||||
|
is an unknown warning flag.
|
||||||
|
See the
|
||||||
|
.Sx DIAGNOSTICS
|
||||||
|
section for a list of warnings.
|
||||||
.It Fl w , Fl \-wramx
|
.It Fl w , Fl \-wramx
|
||||||
Expand the WRAM0 section size from 4 KiB to the full 8 KiB assigned to WRAM.
|
Expand the WRAM0 section size from 4 KiB to the full 8 KiB assigned to WRAM.
|
||||||
WRAMX sections that are fixed to a bank other than 1 become errors, other WRAMX sections are treated as WRAM0.
|
WRAMX sections that are fixed to a bank other than 1 become errors, other WRAMX sections are treated as WRAM0.
|
||||||
@@ -127,8 +192,13 @@ When making a ROM, note that not using this is not a replacement for
|
|||||||
option!
|
option!
|
||||||
.El
|
.El
|
||||||
.Ss Scrambling algorithm
|
.Ss Scrambling algorithm
|
||||||
The default section placement algorithm tries to minimize the number of banks used;
|
The default section placement algorithm tries to place sections into as few banks as possible.
|
||||||
.Dq scrambling
|
(It turns out that section placement is an NP-complete problem known as "bin packing", so
|
||||||
|
.Nm
|
||||||
|
does not attempt to find the optimal solution, but instead uses a "first-fit" heuristic to find a good one in a reasonable amount of time.
|
||||||
|
There are no guarantees about where this algorithm will place sections, apart from the bank, address, and alignment constraints manually specified for the sections.)
|
||||||
|
.Pp
|
||||||
|
.Dq Scrambling
|
||||||
instead places sections into a given pool of banks, trying to minimize the number of sections sharing a given bank.
|
instead places sections into a given pool of banks, trying to minimize the number of sections sharing a given bank.
|
||||||
This is useful to catch broken bank assumptions, such as expecting two different sections to land in the same bank (that is not guaranteed unless both are manually assigned the same bank number).
|
This is useful to catch broken bank assumptions, such as expecting two different sections to land in the same bank (that is not guaranteed unless both are manually assigned the same bank number).
|
||||||
.Pp
|
.Pp
|
||||||
@@ -176,6 +246,93 @@ as
|
|||||||
.Ic WRAMX
|
.Ic WRAMX
|
||||||
sections will be treated as
|
sections will be treated as
|
||||||
.Ic WRAM0 .
|
.Ic WRAM0 .
|
||||||
|
.Sh DIAGNOSTICS
|
||||||
|
Warnings are diagnostic messages that indicate possibly erroneous behavior that does not necessarily compromise the linking process.
|
||||||
|
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 or meta warning into an error.
|
||||||
|
A warning's name is appended
|
||||||
|
.Pq example: Fl Werror=assert ,
|
||||||
|
and this warning is implicitly enabled and turned into an error.
|
||||||
|
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
|
||||||
|
.Dq meta
|
||||||
|
warnings, that enable a collection of other warnings.
|
||||||
|
If a specific warning is toggled via a meta flag and a specific one, the more specific one takes priority.
|
||||||
|
The position on the command-line acts as a tie breaker, the last one taking effect.
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It Fl Wall
|
||||||
|
This enables warnings that are likely to indicate an error or undesired behavior, and that can easily be fixed.
|
||||||
|
.It Fl Weverything
|
||||||
|
Enables literally every warning.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
The following warnings are actual warning flags; with each description, the corresponding warning flag is included.
|
||||||
|
Note that each of these flag also has a negation (for example,
|
||||||
|
.Fl Wobsolete
|
||||||
|
enables the warning that
|
||||||
|
.Fl Wno-obsolete
|
||||||
|
disables; and
|
||||||
|
.Fl Wall
|
||||||
|
enables every warning that
|
||||||
|
.Fl Wno-all
|
||||||
|
disables).
|
||||||
|
Only the non-default flag is listed here.
|
||||||
|
Ignoring the
|
||||||
|
.Dq no-
|
||||||
|
prefix, entries are listed alphabetically.
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It Fl Wno-assert
|
||||||
|
Warn when
|
||||||
|
.Ic WARN Ns No -type
|
||||||
|
assertions fail. (See
|
||||||
|
.Dq Aborting the assembly process
|
||||||
|
in
|
||||||
|
.Xr rgbasm 5
|
||||||
|
for
|
||||||
|
.Ic ASSERT ) .
|
||||||
|
.It Fl Wdiv
|
||||||
|
Warn when dividing the smallest negative integer (-2**31) by -1, which yields itself due to integer overflow.
|
||||||
|
This warning is enabled by
|
||||||
|
.Fl Wall .
|
||||||
|
.It Fl Wno-obsolete
|
||||||
|
Warn when obsolete features are encountered, which have been deprecated and may later be removed.
|
||||||
|
.It Fl Wshift
|
||||||
|
Warn when shifting right a negative value.
|
||||||
|
Use a division by 2**N instead.
|
||||||
|
This warning is enabled by
|
||||||
|
.Fl Wall .
|
||||||
|
.It Fl Wshift-amount
|
||||||
|
Warn when a shift's operand is negative or greater than 32.
|
||||||
|
This warning is enabled by
|
||||||
|
.Fl Wall .
|
||||||
|
.It Fl Wtruncation=
|
||||||
|
Warn when an implicit truncation (for example,
|
||||||
|
.Ic db
|
||||||
|
to an 8-bit value) loses some bits.
|
||||||
|
.Fl Wtruncation=0
|
||||||
|
or
|
||||||
|
.Fl Wno-truncation
|
||||||
|
disables this warning.
|
||||||
|
.Fl Wtruncation=1
|
||||||
|
warns when an N-bit value is 2**N or greater, or less than -2**N.
|
||||||
|
.Fl Wtruncation=2
|
||||||
|
or just
|
||||||
|
.Fl Wtruncation
|
||||||
|
also warns when an N-bit value is less than -2**(N-1), which will not fit in two's complement encoding.
|
||||||
|
.El
|
||||||
.Sh EXAMPLES
|
.Sh EXAMPLES
|
||||||
All you need for a basic ROM is an object file, which can be made into a ROM image like so:
|
All you need for a basic ROM is an object file, which can be made into a ROM image like so:
|
||||||
.Pp
|
.Pp
|
||||||
@@ -194,7 +351,7 @@ Here is a more complete example:
|
|||||||
.Pp
|
.Pp
|
||||||
.Dl $ rgblink -o bin/game.gb -n bin/game.sym -p 0xFF obj/title.o obj/engine.o
|
.Dl $ rgblink -o bin/game.gb -n bin/game.sym -p 0xFF obj/title.o obj/engine.o
|
||||||
.Sh BUGS
|
.Sh BUGS
|
||||||
Please report bugs on
|
Please report bugs or mistakes in this documentation on
|
||||||
.Lk https://github.com/gbdev/rgbds/issues GitHub .
|
.Lk https://github.com/gbdev/rgbds/issues GitHub .
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr rgbasm 1 ,
|
.Xr rgbasm 1 ,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd May 4, 2025
|
.Dd September 1, 2025
|
||||||
.Dt RGBLINK 5
|
.Dt RGBLINK 5
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -24,18 +24,20 @@ They are simply ignored.
|
|||||||
.Pp
|
.Pp
|
||||||
Keywords are composed of letters and digits (but they can't start with a digit); they are all case-insensitive.
|
Keywords are composed of letters and digits (but they can't start with a digit); they are all case-insensitive.
|
||||||
.Pp
|
.Pp
|
||||||
Numbers can be written in decimal format, or in binary using the
|
Numbers can be written in a number of formats.
|
||||||
.Ql %
|
.Bl -column -offset indent "Hexadecimal" "Possible prefixes"
|
||||||
prefix, or in hexadecimal using the
|
.It Sy Format type Ta Sy Possible prefixes Ta Sy Accepted characters
|
||||||
.Ql $
|
.It Decimal Ta none Ta 0123456789
|
||||||
prefix (hexadecimal digits are case-insensitive).
|
.It Hexadecimal Ta Li $ , 0x , 0X Ta 0123456789ABCDEF
|
||||||
Note that unlike
|
.It Octal Ta Li & , 0o , 0O Ta 01234567
|
||||||
.Xr rgbasm 5 ,
|
.It Binary Ta Li % , 0b , 0B Ta 01
|
||||||
an octal
|
.El
|
||||||
.Ql &
|
.Pp
|
||||||
prefix is not supported, nor are
|
Underscores are also accepted in numbers, except at the beginning of one.
|
||||||
.Ql _
|
This can be useful for grouping digits, like
|
||||||
digit separators.
|
.Ql 1_234
|
||||||
|
or
|
||||||
|
.Ql $ff_80 .
|
||||||
.Pp
|
.Pp
|
||||||
Strings begin with a double quote, and end at the next (non-escaped) double quote.
|
Strings begin with a double quote, and end at the next (non-escaped) double quote.
|
||||||
Strings must not contain literal newline characters.
|
Strings must not contain literal newline characters.
|
||||||
@@ -46,8 +48,9 @@ are supported, specifically
|
|||||||
.Ql \e" ,
|
.Ql \e" ,
|
||||||
.Ql \en ,
|
.Ql \en ,
|
||||||
.Ql \er ,
|
.Ql \er ,
|
||||||
|
.Ql \et ,
|
||||||
and
|
and
|
||||||
.Ql \et .
|
.Ql \e0 .
|
||||||
Other backslash escape sequences in
|
Other backslash escape sequences in
|
||||||
.Xr rgbasm 5
|
.Xr rgbasm 5
|
||||||
are only relevant to assembly code and do not apply in linker scripts.
|
are only relevant to assembly code and do not apply in linker scripts.
|
||||||
|
|||||||
@@ -3,16 +3,19 @@
|
|||||||
configure_file(version.cpp _version.cpp ESCAPE_QUOTES)
|
configure_file(version.cpp _version.cpp ESCAPE_QUOTES)
|
||||||
|
|
||||||
set(common_src
|
set(common_src
|
||||||
"error.cpp"
|
|
||||||
"extern/getopt.cpp"
|
"extern/getopt.cpp"
|
||||||
|
"diagnostics.cpp"
|
||||||
|
"style.cpp"
|
||||||
|
"usage.cpp"
|
||||||
|
"util.cpp"
|
||||||
"_version.cpp"
|
"_version.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(BISON 3.0.0 REQUIRED)
|
find_package(BISON 3.0.0 REQUIRED)
|
||||||
set(BISON_FLAGS "-Wall -Dparse.lac=full -Dlr.type=ielr")
|
set(BISON_FLAGS "-Wall -Dlr.type=ielr")
|
||||||
# Set some optimization flags on versions that support them
|
# Set some optimization flags on versions that support them
|
||||||
if(BISON_VERSION VERSION_GREATER_EQUAL "3.5")
|
if(BISON_VERSION VERSION_GREATER_EQUAL "3.5")
|
||||||
set(BISON_FLAGS "${BISON_FLAGS} -Dapi.token.raw=true")
|
set(BISON_FLAGS "${BISON_FLAGS} -Dparse.lac=full -Dapi.token.raw=true")
|
||||||
endif()
|
endif()
|
||||||
if(BISON_VERSION VERSION_GREATER_EQUAL "3.6")
|
if(BISON_VERSION VERSION_GREATER_EQUAL "3.6")
|
||||||
set(BISON_FLAGS "${BISON_FLAGS} -Dparse.error=detailed")
|
set(BISON_FLAGS "${BISON_FLAGS} -Dparse.error=detailed")
|
||||||
@@ -34,6 +37,7 @@ BISON_TARGET(LINKER_SCRIPT_PARSER "link/script.y"
|
|||||||
|
|
||||||
set(rgbasm_src
|
set(rgbasm_src
|
||||||
"${BISON_ASM_PARSER_OUTPUT_SOURCE}"
|
"${BISON_ASM_PARSER_OUTPUT_SOURCE}"
|
||||||
|
"asm/actions.cpp"
|
||||||
"asm/charmap.cpp"
|
"asm/charmap.cpp"
|
||||||
"asm/fixpoint.cpp"
|
"asm/fixpoint.cpp"
|
||||||
"asm/format.cpp"
|
"asm/format.cpp"
|
||||||
@@ -48,31 +52,18 @@ set(rgbasm_src
|
|||||||
"asm/symbol.cpp"
|
"asm/symbol.cpp"
|
||||||
"asm/warning.cpp"
|
"asm/warning.cpp"
|
||||||
"extern/utf8decoder.cpp"
|
"extern/utf8decoder.cpp"
|
||||||
|
"backtrace.cpp"
|
||||||
"linkdefs.cpp"
|
"linkdefs.cpp"
|
||||||
"opmath.cpp"
|
"opmath.cpp"
|
||||||
"util.cpp"
|
"verbosity.cpp"
|
||||||
)
|
|
||||||
|
|
||||||
set(rgbfix_src
|
|
||||||
"fix/main.cpp"
|
|
||||||
)
|
|
||||||
|
|
||||||
set(rgbgfx_src
|
|
||||||
"gfx/main.cpp"
|
|
||||||
"gfx/pal_packing.cpp"
|
|
||||||
"gfx/pal_sorting.cpp"
|
|
||||||
"gfx/pal_spec.cpp"
|
|
||||||
"gfx/process.cpp"
|
|
||||||
"gfx/proto_palette.cpp"
|
|
||||||
"gfx/reverse.cpp"
|
|
||||||
"gfx/rgba.cpp"
|
|
||||||
"extern/getopt.cpp"
|
|
||||||
"error.cpp"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set(rgblink_src
|
set(rgblink_src
|
||||||
"${BISON_LINKER_SCRIPT_PARSER_OUTPUT_SOURCE}"
|
"${BISON_LINKER_SCRIPT_PARSER_OUTPUT_SOURCE}"
|
||||||
"link/assign.cpp"
|
"link/assign.cpp"
|
||||||
|
"link/fstack.cpp"
|
||||||
|
"link/lexer.cpp"
|
||||||
|
"link/layout.cpp"
|
||||||
"link/main.cpp"
|
"link/main.cpp"
|
||||||
"link/object.cpp"
|
"link/object.cpp"
|
||||||
"link/output.cpp"
|
"link/output.cpp"
|
||||||
@@ -80,10 +71,33 @@ set(rgblink_src
|
|||||||
"link/sdas_obj.cpp"
|
"link/sdas_obj.cpp"
|
||||||
"link/section.cpp"
|
"link/section.cpp"
|
||||||
"link/symbol.cpp"
|
"link/symbol.cpp"
|
||||||
|
"link/warning.cpp"
|
||||||
"extern/utf8decoder.cpp"
|
"extern/utf8decoder.cpp"
|
||||||
|
"backtrace.cpp"
|
||||||
"linkdefs.cpp"
|
"linkdefs.cpp"
|
||||||
"opmath.cpp"
|
"opmath.cpp"
|
||||||
"util.cpp"
|
"verbosity.cpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
set(rgbfix_src
|
||||||
|
"fix/fix.cpp"
|
||||||
|
"fix/main.cpp"
|
||||||
|
"fix/mbc.cpp"
|
||||||
|
"fix/warning.cpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
set(rgbgfx_src
|
||||||
|
"gfx/color_set.cpp"
|
||||||
|
"gfx/main.cpp"
|
||||||
|
"gfx/pal_packing.cpp"
|
||||||
|
"gfx/pal_sorting.cpp"
|
||||||
|
"gfx/pal_spec.cpp"
|
||||||
|
"gfx/png.cpp"
|
||||||
|
"gfx/process.cpp"
|
||||||
|
"gfx/reverse.cpp"
|
||||||
|
"gfx/rgba.cpp"
|
||||||
|
"gfx/warning.cpp"
|
||||||
|
"verbosity.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
foreach(PROG "asm" "fix" "gfx" "link")
|
foreach(PROG "asm" "fix" "gfx" "link")
|
||||||
|
|||||||
630
src/asm/actions.cpp
Normal file
630
src/asm/actions.cpp
Normal file
@@ -0,0 +1,630 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include "asm/actions.hpp"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <optional>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <utility>
|
||||||
|
#include <variant>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "extern/utf8decoder.hpp"
|
||||||
|
#include "helpers.hpp"
|
||||||
|
#include "linkdefs.hpp"
|
||||||
|
|
||||||
|
#include "asm/charmap.hpp"
|
||||||
|
#include "asm/format.hpp"
|
||||||
|
#include "asm/fstack.hpp"
|
||||||
|
#include "asm/lexer.hpp"
|
||||||
|
#include "asm/output.hpp"
|
||||||
|
#include "asm/rpn.hpp" // Expression
|
||||||
|
#include "asm/section.hpp"
|
||||||
|
#include "asm/symbol.hpp"
|
||||||
|
#include "asm/warning.hpp"
|
||||||
|
|
||||||
|
void act_If(int32_t condition) {
|
||||||
|
lexer_IncIFDepth();
|
||||||
|
|
||||||
|
if (condition) {
|
||||||
|
lexer_RunIFBlock();
|
||||||
|
} else {
|
||||||
|
lexer_SetMode(LEXER_SKIP_TO_ELIF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void act_Elif(int32_t condition) {
|
||||||
|
if (lexer_GetIFDepth() == 0) {
|
||||||
|
fatal("Found `ELIF` outside of a conditional (not after an `IF`/`ELIF` block)");
|
||||||
|
}
|
||||||
|
if (lexer_RanIFBlock()) {
|
||||||
|
if (lexer_ReachedELSEBlock()) {
|
||||||
|
fatal("Found `ELIF` after an `ELSE` block");
|
||||||
|
}
|
||||||
|
lexer_SetMode(LEXER_SKIP_TO_ENDC);
|
||||||
|
} else if (condition) {
|
||||||
|
lexer_RunIFBlock();
|
||||||
|
} else {
|
||||||
|
lexer_SetMode(LEXER_SKIP_TO_ELIF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void act_Else() {
|
||||||
|
if (lexer_GetIFDepth() == 0) {
|
||||||
|
fatal("Found `ELSE` outside of a conditional (not after an `IF`/`ELIF` block)");
|
||||||
|
}
|
||||||
|
if (lexer_RanIFBlock()) {
|
||||||
|
if (lexer_ReachedELSEBlock()) {
|
||||||
|
fatal("Found `ELSE` after an `ELSE` block");
|
||||||
|
}
|
||||||
|
lexer_SetMode(LEXER_SKIP_TO_ENDC);
|
||||||
|
} else {
|
||||||
|
lexer_RunIFBlock();
|
||||||
|
lexer_ReachELSEBlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void act_Endc() {
|
||||||
|
lexer_DecIFDepth();
|
||||||
|
}
|
||||||
|
|
||||||
|
AlignmentSpec act_Alignment(int32_t alignment, int32_t alignOfs) {
|
||||||
|
AlignmentSpec spec = {0, 0};
|
||||||
|
if (alignment > 16) {
|
||||||
|
error("Alignment must be between 0 and 16, not %u", alignment);
|
||||||
|
} else if (alignOfs <= -(1 << alignment) || alignOfs >= 1 << alignment) {
|
||||||
|
error(
|
||||||
|
"The absolute alignment offset (%" PRIu32 ") must be less than alignment size (%d)",
|
||||||
|
static_cast<uint32_t>(alignOfs < 0 ? -alignOfs : alignOfs),
|
||||||
|
1 << alignment
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
spec.alignment = alignment;
|
||||||
|
spec.alignOfs = alignOfs < 0 ? (1 << alignment) + alignOfs : alignOfs;
|
||||||
|
}
|
||||||
|
return spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void failAssert(AssertionType type, std::string const &message) {
|
||||||
|
switch (type) {
|
||||||
|
case ASSERT_FATAL:
|
||||||
|
if (message.empty()) {
|
||||||
|
fatal("Assertion failed");
|
||||||
|
} else {
|
||||||
|
fatal("Assertion failed: %s", message.c_str());
|
||||||
|
}
|
||||||
|
case ASSERT_ERROR:
|
||||||
|
if (message.empty()) {
|
||||||
|
error("Assertion failed");
|
||||||
|
} else {
|
||||||
|
error("Assertion failed: %s", message.c_str());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ASSERT_WARN:
|
||||||
|
if (message.empty()) {
|
||||||
|
warning(WARNING_ASSERT, "Assertion failed");
|
||||||
|
} else {
|
||||||
|
warning(WARNING_ASSERT, "Assertion failed: %s", message.c_str());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void act_Assert(AssertionType type, Expression const &expr, std::string const &message) {
|
||||||
|
if (!expr.isKnown()) {
|
||||||
|
out_CreateAssert(type, expr, message, sect_GetOutputOffset());
|
||||||
|
} else if (expr.value() == 0) {
|
||||||
|
failAssert(type, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void act_StaticAssert(AssertionType type, int32_t condition, std::string const &message) {
|
||||||
|
if (!condition) {
|
||||||
|
failAssert(type, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> act_ReadFile(std::string const &name, uint32_t maxLen) {
|
||||||
|
FILE *file = nullptr;
|
||||||
|
if (std::optional<std::string> fullPath = fstk_FindFile(name); fullPath) {
|
||||||
|
file = fopen(fullPath->c_str(), "rb");
|
||||||
|
}
|
||||||
|
if (!file) {
|
||||||
|
if (fstk_FileError(name, "READFILE")) {
|
||||||
|
// If `fstk_FileError` returned true due to `-MG`, we should abort due to a
|
||||||
|
// missing file, so return `std::nullopt`, which tells the caller to `YYACCEPT`
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
Defer closeFile{[&] { fclose(file); }};
|
||||||
|
|
||||||
|
size_t readSize = maxLen;
|
||||||
|
if (fseek(file, 0, SEEK_END) == 0) {
|
||||||
|
// If the file is seekable and shorter than the max length,
|
||||||
|
// just read as many bytes as there are
|
||||||
|
if (long fileSize = ftell(file); static_cast<size_t>(fileSize) < readSize) {
|
||||||
|
readSize = fileSize;
|
||||||
|
}
|
||||||
|
fseek(file, 0, SEEK_SET);
|
||||||
|
// LCOV_EXCL_START
|
||||||
|
} else if (errno != ESPIPE) {
|
||||||
|
error(
|
||||||
|
"Error determining size of `READFILE` file \"%s\": %s", name.c_str(), strerror(errno)
|
||||||
|
);
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string contents;
|
||||||
|
contents.resize(readSize);
|
||||||
|
|
||||||
|
if (fread(&contents[0], 1, readSize, file) < readSize || ferror(file)) {
|
||||||
|
// LCOV_EXCL_START
|
||||||
|
error("Error reading `READFILE` file \"%s\": %s", name.c_str(), strerror(errno));
|
||||||
|
return "";
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
|
}
|
||||||
|
|
||||||
|
return contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t act_CharToNum(std::string const &str) {
|
||||||
|
if (std::vector<int32_t> units = charmap_Convert(str); units.size() == 1) {
|
||||||
|
// The string is a single character with a single unit value,
|
||||||
|
// which can be used directly as a numeric character.
|
||||||
|
return static_cast<uint32_t>(units[0]);
|
||||||
|
} else {
|
||||||
|
error("Character literals must be a single charmap unit");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t act_StringToNum(std::string const &str) {
|
||||||
|
warning(WARNING_OBSOLETE, "Treating strings as numbers is deprecated");
|
||||||
|
if (std::vector<int32_t> units = charmap_Convert(str); units.size() == 1) {
|
||||||
|
// The string is a single character with a single unit value,
|
||||||
|
// which can be used directly as a number.
|
||||||
|
return static_cast<uint32_t>(units[0]);
|
||||||
|
} else {
|
||||||
|
error("Strings as numbers must be a single charmap unit");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t adjustNegativeIndex(int32_t idx, size_t len, char const *functionName) {
|
||||||
|
// String functions adjust negative index arguments the same way,
|
||||||
|
// such that position -1 is the last character of a string.
|
||||||
|
if (idx < 0) {
|
||||||
|
idx += len;
|
||||||
|
}
|
||||||
|
if (idx < 0) {
|
||||||
|
warning(WARNING_BUILTIN_ARG, "%s: Index starts at 0", functionName);
|
||||||
|
idx = 0;
|
||||||
|
}
|
||||||
|
return static_cast<uint32_t>(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t adjustNegativePos(int32_t pos, size_t len, char const *functionName) {
|
||||||
|
// STRSUB and CHARSUB adjust negative position arguments the same way,
|
||||||
|
// such that position -1 is the last character of a string.
|
||||||
|
if (pos < 0) {
|
||||||
|
pos += len + 1;
|
||||||
|
}
|
||||||
|
if (pos < 1) {
|
||||||
|
warning(WARNING_BUILTIN_ARG, "%s: Position starts at 1", functionName);
|
||||||
|
pos = 1;
|
||||||
|
}
|
||||||
|
return static_cast<uint32_t>(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t act_CharVal(std::string const &str) {
|
||||||
|
if (size_t len = charmap_CharSize(str); len == 0) {
|
||||||
|
error("CHARVAL: No character mapping for \"%s\"", str.c_str());
|
||||||
|
return 0;
|
||||||
|
} else if (len != 1) {
|
||||||
|
error("CHARVAL: Character mapping for \"%s\" must have a single value", str.c_str());
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return *charmap_CharValue(str, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t act_CharVal(std::string const &str, int32_t negIdx) {
|
||||||
|
if (size_t len = charmap_CharSize(str); len != 0) {
|
||||||
|
uint32_t idx = adjustNegativeIndex(negIdx, len, "CHARVAL");
|
||||||
|
if (std::optional<int32_t> val = charmap_CharValue(str, idx); val.has_value()) {
|
||||||
|
return *val;
|
||||||
|
} else {
|
||||||
|
warning(
|
||||||
|
WARNING_BUILTIN_ARG,
|
||||||
|
"CHARVAL: Index %" PRIu32 " is past the end of the character mapping",
|
||||||
|
idx
|
||||||
|
);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error("CHARVAL: No character mapping for \"%s\"", str.c_str());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t act_StringByte(std::string const &str, int32_t negIdx) {
|
||||||
|
size_t len = str.length();
|
||||||
|
if (uint32_t idx = adjustNegativeIndex(negIdx, len, "STRBYTE"); idx < len) {
|
||||||
|
return static_cast<uint8_t>(str[idx]);
|
||||||
|
} else {
|
||||||
|
warning(
|
||||||
|
WARNING_BUILTIN_ARG, "STRBYTE: Index %" PRIu32 " is past the end of the string", idx
|
||||||
|
);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void errorInvalidUTF8Byte(uint8_t byte, char const *functionName) {
|
||||||
|
error("%s: Invalid UTF-8 byte 0x%02hhX", functionName, byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t act_StringLen(std::string const &str, bool printErrors) {
|
||||||
|
size_t len = 0;
|
||||||
|
uint32_t state = UTF8_ACCEPT;
|
||||||
|
uint32_t codepoint = 0;
|
||||||
|
|
||||||
|
for (char c : str) {
|
||||||
|
uint8_t byte = static_cast<uint8_t>(c);
|
||||||
|
|
||||||
|
switch (decode(&state, &codepoint, byte)) {
|
||||||
|
case UTF8_REJECT:
|
||||||
|
if (printErrors) {
|
||||||
|
errorInvalidUTF8Byte(byte, "STRLEN");
|
||||||
|
}
|
||||||
|
state = UTF8_ACCEPT;
|
||||||
|
// fallthrough
|
||||||
|
case UTF8_ACCEPT:
|
||||||
|
++len;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for partial code point.
|
||||||
|
if (state != UTF8_ACCEPT) {
|
||||||
|
if (printErrors) {
|
||||||
|
error("STRLEN: Incomplete UTF-8 character");
|
||||||
|
}
|
||||||
|
++len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
act_StringSlice(std::string const &str, int32_t negStart, std::optional<int32_t> negStop) {
|
||||||
|
size_t adjustLen = act_StringLen(str, false);
|
||||||
|
uint32_t start = adjustNegativeIndex(negStart, adjustLen, "STRSLICE");
|
||||||
|
uint32_t stop = negStop ? adjustNegativeIndex(*negStop, adjustLen, "STRSLICE") : adjustLen;
|
||||||
|
|
||||||
|
size_t strLen = str.length();
|
||||||
|
size_t index = 0;
|
||||||
|
uint32_t state = UTF8_ACCEPT;
|
||||||
|
uint32_t codepoint = 0;
|
||||||
|
uint32_t curIdx = 0;
|
||||||
|
|
||||||
|
// Advance to starting index in source string.
|
||||||
|
while (index < strLen && curIdx < start) {
|
||||||
|
switch (decode(&state, &codepoint, str[index])) {
|
||||||
|
case UTF8_REJECT:
|
||||||
|
errorInvalidUTF8Byte(str[index], "STRSLICE");
|
||||||
|
state = UTF8_ACCEPT;
|
||||||
|
// fallthrough
|
||||||
|
case UTF8_ACCEPT:
|
||||||
|
++curIdx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// An index 1 past the end of the string is allowed, but will trigger the
|
||||||
|
// "Length too big" warning below if the length is nonzero.
|
||||||
|
if (index >= strLen && start > curIdx) {
|
||||||
|
warning(
|
||||||
|
WARNING_BUILTIN_ARG,
|
||||||
|
"STRSLICE: Start index %" PRIu32 " is past the end of the string",
|
||||||
|
start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t startIndex = index;
|
||||||
|
|
||||||
|
// Advance to ending index in source string.
|
||||||
|
while (index < strLen && curIdx < stop) {
|
||||||
|
switch (decode(&state, &codepoint, str[index])) {
|
||||||
|
case UTF8_REJECT:
|
||||||
|
errorInvalidUTF8Byte(str[index], "STRSLICE");
|
||||||
|
state = UTF8_ACCEPT;
|
||||||
|
// fallthrough
|
||||||
|
case UTF8_ACCEPT:
|
||||||
|
++curIdx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for partial code point.
|
||||||
|
if (state != UTF8_ACCEPT) {
|
||||||
|
error("STRSLICE: Incomplete UTF-8 character");
|
||||||
|
++curIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curIdx < stop) {
|
||||||
|
warning(
|
||||||
|
WARNING_BUILTIN_ARG,
|
||||||
|
"STRSLICE: Stop index %" PRIu32 " is past the end of the string",
|
||||||
|
stop
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return str.substr(startIndex, index - startIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string act_StringSub(std::string const &str, int32_t negPos, std::optional<uint32_t> optLen) {
|
||||||
|
warning(WARNING_OBSOLETE, "`STRSUB` is deprecated; use 0-indexed `STRSLICE` instead");
|
||||||
|
|
||||||
|
size_t adjustLen = act_StringLen(str, false);
|
||||||
|
uint32_t pos = adjustNegativePos(negPos, adjustLen, "STRSUB");
|
||||||
|
uint32_t len = optLen ? *optLen : pos > adjustLen ? 0 : adjustLen + 1 - pos;
|
||||||
|
|
||||||
|
size_t strLen = str.length();
|
||||||
|
size_t index = 0;
|
||||||
|
uint32_t state = UTF8_ACCEPT;
|
||||||
|
uint32_t codepoint = 0;
|
||||||
|
uint32_t curPos = 1;
|
||||||
|
|
||||||
|
// Advance to starting position in source string.
|
||||||
|
while (index < strLen && curPos < pos) {
|
||||||
|
switch (decode(&state, &codepoint, str[index])) {
|
||||||
|
case UTF8_REJECT:
|
||||||
|
errorInvalidUTF8Byte(str[index], "STRSUB");
|
||||||
|
state = UTF8_ACCEPT;
|
||||||
|
// fallthrough
|
||||||
|
case UTF8_ACCEPT:
|
||||||
|
++curPos;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A position 1 past the end of the string is allowed, but will trigger the
|
||||||
|
// "Length too big" warning below if the length is nonzero.
|
||||||
|
if (index >= strLen && pos > curPos) {
|
||||||
|
warning(
|
||||||
|
WARNING_BUILTIN_ARG, "STRSUB: Position %" PRIu32 " is past the end of the string", pos
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t startIndex = index;
|
||||||
|
uint32_t curLen = 0;
|
||||||
|
|
||||||
|
// Compute the result length in bytes.
|
||||||
|
while (index < strLen && curLen < len) {
|
||||||
|
switch (decode(&state, &codepoint, str[index])) {
|
||||||
|
case UTF8_REJECT:
|
||||||
|
errorInvalidUTF8Byte(str[index], "STRSUB");
|
||||||
|
state = UTF8_ACCEPT;
|
||||||
|
// fallthrough
|
||||||
|
case UTF8_ACCEPT:
|
||||||
|
++curLen;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for partial code point.
|
||||||
|
if (state != UTF8_ACCEPT) {
|
||||||
|
error("STRSUB: Incomplete UTF-8 character");
|
||||||
|
++curLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curLen < len) {
|
||||||
|
warning(WARNING_BUILTIN_ARG, "STRSUB: Length too big: %" PRIu32, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return str.substr(startIndex, index - startIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t act_CharLen(std::string const &str) {
|
||||||
|
std::string_view view = str;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
for (len = 0; charmap_ConvertNext(view, nullptr); ++len) {}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string act_StringChar(std::string const &str, int32_t negIdx) {
|
||||||
|
size_t adjustLen = act_CharLen(str);
|
||||||
|
uint32_t idx = adjustNegativeIndex(negIdx, adjustLen, "STRCHAR");
|
||||||
|
|
||||||
|
std::string_view view = str;
|
||||||
|
size_t charLen = 1;
|
||||||
|
|
||||||
|
// Advance to starting index in source string.
|
||||||
|
for (uint32_t curIdx = 0; charLen && curIdx < idx; ++curIdx) {
|
||||||
|
charLen = charmap_ConvertNext(view, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view start = view;
|
||||||
|
|
||||||
|
if (!charmap_ConvertNext(view, nullptr)) {
|
||||||
|
warning(
|
||||||
|
WARNING_BUILTIN_ARG, "STRCHAR: Index %" PRIu32 " is past the end of the string", idx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
start = start.substr(0, start.length() - view.length());
|
||||||
|
return std::string(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string act_CharSub(std::string const &str, int32_t negPos) {
|
||||||
|
warning(WARNING_OBSOLETE, "`CHARSUB` is deprecated; use 0-indexed `STRCHAR` instead");
|
||||||
|
|
||||||
|
size_t adjustLen = act_CharLen(str);
|
||||||
|
uint32_t pos = adjustNegativePos(negPos, adjustLen, "CHARSUB");
|
||||||
|
|
||||||
|
std::string_view view = str;
|
||||||
|
size_t charLen = 1;
|
||||||
|
|
||||||
|
// Advance to starting position in source string.
|
||||||
|
for (uint32_t curPos = 1; charLen && curPos < pos; ++curPos) {
|
||||||
|
charLen = charmap_ConvertNext(view, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view start = view;
|
||||||
|
|
||||||
|
if (!charmap_ConvertNext(view, nullptr)) {
|
||||||
|
warning(
|
||||||
|
WARNING_BUILTIN_ARG, "CHARSUB: Position %" PRIu32 " is past the end of the string", pos
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
start = start.substr(0, start.length() - view.length());
|
||||||
|
return std::string(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t act_CharCmp(std::string_view str1, std::string_view str2) {
|
||||||
|
std::vector<int32_t> seq1, seq2;
|
||||||
|
size_t idx1 = 0, idx2 = 0;
|
||||||
|
for (;;) {
|
||||||
|
if (idx1 >= seq1.size()) {
|
||||||
|
idx1 = 0;
|
||||||
|
seq1.clear();
|
||||||
|
charmap_ConvertNext(str1, &seq1);
|
||||||
|
}
|
||||||
|
if (idx2 >= seq2.size()) {
|
||||||
|
idx2 = 0;
|
||||||
|
seq2.clear();
|
||||||
|
charmap_ConvertNext(str2, &seq2);
|
||||||
|
}
|
||||||
|
if (seq1.empty() != seq2.empty()) {
|
||||||
|
return seq1.empty() ? -1 : 1;
|
||||||
|
} else if (seq1.empty()) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
int32_t value1 = seq1[idx1++], value2 = seq2[idx2++];
|
||||||
|
if (value1 != value2) {
|
||||||
|
return (value1 > value2) - (value1 < value2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
act_StringReplace(std::string_view str, std::string const &old, std::string const &rep) {
|
||||||
|
if (old.empty()) {
|
||||||
|
warning(WARNING_EMPTY_STRRPL, "STRRPL: Cannot replace an empty string");
|
||||||
|
return std::string(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string rpl;
|
||||||
|
|
||||||
|
while (!str.empty()) {
|
||||||
|
auto pos = str.find(old);
|
||||||
|
if (pos == str.npos) {
|
||||||
|
rpl.append(str);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rpl.append(str, 0, pos);
|
||||||
|
rpl.append(rep);
|
||||||
|
str.remove_prefix(pos + old.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
return rpl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string act_StringFormat(
|
||||||
|
std::string const &spec, std::vector<std::variant<uint32_t, std::string>> const &args
|
||||||
|
) {
|
||||||
|
std::string str;
|
||||||
|
size_t argIndex = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; spec[i] != '\0';) {
|
||||||
|
if (int c = spec[i]; c != '%') {
|
||||||
|
str += c;
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (int c = spec[++i]; c == '%') {
|
||||||
|
str += c;
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
} else if (c == '\0') {
|
||||||
|
error("STRFMT: Illegal '%%' at end of format string");
|
||||||
|
str += '%';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormatSpec fmt{};
|
||||||
|
size_t n = fmt.parseSpec(spec.c_str() + i);
|
||||||
|
i += n;
|
||||||
|
|
||||||
|
if (!fmt.isValid()) {
|
||||||
|
error("STRFMT: Invalid format spec for argument %zu", argIndex + 1);
|
||||||
|
str += spec.substr(i - n - 1, n + 1); // include the '%'
|
||||||
|
} else if (argIndex >= args.size()) {
|
||||||
|
// Will warn after formatting is done.
|
||||||
|
str += '%';
|
||||||
|
} else if (std::holds_alternative<uint32_t>(args[argIndex])) {
|
||||||
|
fmt.appendNumber(str, std::get<uint32_t>(args[argIndex]));
|
||||||
|
} else {
|
||||||
|
fmt.appendString(str, std::get<std::string>(args[argIndex]));
|
||||||
|
}
|
||||||
|
|
||||||
|
++argIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argIndex < args.size()) {
|
||||||
|
size_t extra = args.size() - argIndex;
|
||||||
|
error("STRFMT: %zu unformatted argument%s", extra, extra == 1 ? "" : "s");
|
||||||
|
} else if (argIndex > args.size()) {
|
||||||
|
error(
|
||||||
|
"STRFMT: Not enough arguments for format spec (expected %zu, got %zu)",
|
||||||
|
argIndex,
|
||||||
|
args.size()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string act_SectionName(std::string const &symName) {
|
||||||
|
Symbol *sym = sym_FindScopedValidSymbol(symName);
|
||||||
|
if (!sym) {
|
||||||
|
if (sym_IsPurgedScoped(symName)) {
|
||||||
|
fatal("Undefined symbol `%s`; it was purged", symName.c_str());
|
||||||
|
} else {
|
||||||
|
fatal("Undefined symbol `%s`", symName.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Section const *section = sym->getSection();
|
||||||
|
if (!section) {
|
||||||
|
fatal("`%s` does not belong to any section", sym->name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return section->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void act_CompoundAssignment(std::string const &symName, RPNCommand op, int32_t constValue) {
|
||||||
|
Expression oldExpr, constExpr, newExpr;
|
||||||
|
oldExpr.makeSymbol(symName);
|
||||||
|
constExpr.makeNumber(constValue);
|
||||||
|
newExpr.makeBinaryOp(op, std::move(oldExpr), constExpr);
|
||||||
|
|
||||||
|
int32_t newValue = newExpr.getConstVal();
|
||||||
|
sym_AddVar(symName, newValue);
|
||||||
|
}
|
||||||
@@ -2,17 +2,21 @@
|
|||||||
|
|
||||||
#include "asm/charmap.hpp"
|
#include "asm/charmap.hpp"
|
||||||
|
|
||||||
#include <deque>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unordered_map>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "extern/utf8decoder.hpp"
|
#include "extern/utf8decoder.hpp"
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
|
#include "itertools.hpp" // InsertionOrderedMap
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
|
|
||||||
#include "asm/warning.hpp"
|
#include "asm/warning.hpp"
|
||||||
@@ -31,92 +35,85 @@ struct CharmapNode {
|
|||||||
struct Charmap {
|
struct Charmap {
|
||||||
std::string name;
|
std::string name;
|
||||||
std::vector<CharmapNode> nodes; // first node is reserved for the root node
|
std::vector<CharmapNode> nodes; // first node is reserved for the root node
|
||||||
|
};
|
||||||
|
|
||||||
// Traverse the trie depth-first to derive the character mappings in definition order
|
// Traverse the trie depth-first to derive the character mappings in definition order
|
||||||
template<typename F>
|
template<typename F>
|
||||||
bool forEachChar(F callback) const {
|
bool forEachChar(Charmap const &charmap, F callback) {
|
||||||
// clang-format off: nested initializers
|
// clang-format off: nested initializers
|
||||||
for (std::stack<std::pair<size_t, std::string>> prefixes({{0, ""}}); !prefixes.empty();) {
|
for (std::stack<std::pair<size_t, std::string>> prefixes({{0, ""}}); !prefixes.empty();) {
|
||||||
// clang-format on
|
// clang-format on
|
||||||
auto [nodeIdx, mapping] = std::move(prefixes.top());
|
auto [nodeIdx, mapping] = std::move(prefixes.top());
|
||||||
prefixes.pop();
|
prefixes.pop();
|
||||||
CharmapNode const &node = nodes[nodeIdx];
|
CharmapNode const &node = charmap.nodes[nodeIdx];
|
||||||
if (node.isTerminal()) {
|
if (node.isTerminal() && !callback(nodeIdx, mapping)) {
|
||||||
if (!callback(nodeIdx, mapping)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
for (unsigned c = 0; c < std::size(node.next); ++c) {
|
||||||
for (unsigned c = 0; c < std::size(node.next); c++) {
|
|
||||||
if (size_t nextIdx = node.next[c]; nextIdx) {
|
if (size_t nextIdx = node.next[c]; nextIdx) {
|
||||||
prefixes.push({nextIdx, mapping + static_cast<char>(c)});
|
prefixes.push({nextIdx, mapping + static_cast<char>(c)});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
static std::deque<Charmap> charmapList;
|
static InsertionOrderedMap<Charmap> charmaps;
|
||||||
static std::unordered_map<std::string, size_t> charmapMap; // Indexes into `charmapList`
|
|
||||||
|
|
||||||
static Charmap *currentCharmap;
|
static Charmap *currentCharmap;
|
||||||
std::stack<Charmap *> charmapStack;
|
static std::stack<Charmap *> charmapStack;
|
||||||
|
|
||||||
bool charmap_ForEach(
|
bool charmap_ForEach(
|
||||||
void (*mapFunc)(std::string const &),
|
void (*mapFunc)(std::string const &),
|
||||||
void (*charFunc)(std::string const &, std::vector<int32_t>)
|
void (*charFunc)(std::string const &, std::vector<int32_t>)
|
||||||
) {
|
) {
|
||||||
for (Charmap const &charmap : charmapList) {
|
for (Charmap const &charmap : charmaps) {
|
||||||
std::map<size_t, std::string> mappings;
|
std::map<size_t, std::string> mappings;
|
||||||
charmap.forEachChar([&mappings](size_t nodeIdx, std::string const &mapping) {
|
forEachChar(charmap, [&mappings](size_t nodeIdx, std::string const &mapping) {
|
||||||
mappings[nodeIdx] = mapping;
|
mappings[nodeIdx] = mapping;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
mapFunc(charmap.name);
|
mapFunc(charmap.name);
|
||||||
for (auto [nodeIdx, mapping] : mappings) {
|
for (auto const &[nodeIdx, mapping] : mappings) {
|
||||||
charFunc(mapping, charmap.nodes[nodeIdx].value);
|
charFunc(mapping, charmap.nodes[nodeIdx].value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return !charmapList.empty();
|
return !charmaps.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void charmap_New(std::string const &name, std::string const *baseName) {
|
void charmap_New(std::string const &name, std::string const *baseName) {
|
||||||
size_t baseIdx = SIZE_MAX;
|
std::optional<size_t> baseIdx = std::nullopt;
|
||||||
|
|
||||||
if (baseName != nullptr) {
|
if (baseName != nullptr) {
|
||||||
if (auto search = charmapMap.find(*baseName); search == charmapMap.end()) {
|
baseIdx = charmaps.findIndex(*baseName);
|
||||||
error("Base charmap '%s' doesn't exist\n", baseName->c_str());
|
if (!baseIdx) {
|
||||||
} else {
|
error("Undefined base charmap `%s`", baseName->c_str());
|
||||||
baseIdx = search->second;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (charmapMap.find(name) != charmapMap.end()) {
|
if (charmaps.contains(name)) {
|
||||||
error("Charmap '%s' already exists\n", name.c_str());
|
error("Charmap `%s` is already defined", name.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init the new charmap's fields
|
// Init the new charmap's fields
|
||||||
charmapMap[name] = charmapList.size();
|
Charmap &charmap = charmaps.add(name);
|
||||||
Charmap &charmap = charmapList.emplace_back();
|
charmap.name = name;
|
||||||
|
if (baseIdx) {
|
||||||
if (baseIdx != SIZE_MAX) {
|
charmap.nodes = charmaps[*baseIdx].nodes; // Copies `charmaps[*baseIdx].nodes`
|
||||||
charmap.nodes = charmapList[baseIdx].nodes; // Copies `charmapList[baseIdx].nodes`
|
|
||||||
} else {
|
} else {
|
||||||
charmap.nodes.emplace_back(); // Zero-init the root node
|
charmap.nodes.emplace_back(); // Zero-init the root node
|
||||||
}
|
}
|
||||||
|
|
||||||
charmap.name = name;
|
|
||||||
|
|
||||||
currentCharmap = &charmap;
|
currentCharmap = &charmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
void charmap_Set(std::string const &name) {
|
void charmap_Set(std::string const &name) {
|
||||||
if (auto search = charmapMap.find(name); search == charmapMap.end()) {
|
if (auto index = charmaps.findIndex(name); index) {
|
||||||
error("Charmap '%s' doesn't exist\n", name.c_str());
|
currentCharmap = &charmaps[*index];
|
||||||
} else {
|
} else {
|
||||||
currentCharmap = &charmapList[search->second];
|
error("Undefined charmap `%s`", name.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,7 +123,7 @@ void charmap_Push() {
|
|||||||
|
|
||||||
void charmap_Pop() {
|
void charmap_Pop() {
|
||||||
if (charmapStack.empty()) {
|
if (charmapStack.empty()) {
|
||||||
error("No entries in the charmap stack\n");
|
error("No entries in the charmap stack");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,13 +133,13 @@ void charmap_Pop() {
|
|||||||
|
|
||||||
void charmap_CheckStack() {
|
void charmap_CheckStack() {
|
||||||
if (!charmapStack.empty()) {
|
if (!charmapStack.empty()) {
|
||||||
warning(WARNING_UNMATCHED_DIRECTIVE, "`PUSHC` without corresponding `POPC`\n");
|
warning(WARNING_UNMATCHED_DIRECTIVE, "`PUSHC` without corresponding `POPC`");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void charmap_Add(std::string const &mapping, std::vector<int32_t> &&value) {
|
void charmap_Add(std::string const &mapping, std::vector<int32_t> &&value) {
|
||||||
if (mapping.empty()) {
|
if (mapping.empty()) {
|
||||||
error("Cannot map an empty string\n");
|
error("Cannot map an empty string");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +165,7 @@ void charmap_Add(std::string const &mapping, std::vector<int32_t> &&value) {
|
|||||||
CharmapNode &node = charmap.nodes[nodeIdx];
|
CharmapNode &node = charmap.nodes[nodeIdx];
|
||||||
|
|
||||||
if (node.isTerminal()) {
|
if (node.isTerminal()) {
|
||||||
warning(WARNING_CHARMAP_REDEF, "Overriding charmap mapping\n");
|
warning(WARNING_CHARMAP_REDEF, "Overriding charmap mapping");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::swap(node.value, value);
|
std::swap(node.value, value);
|
||||||
@@ -189,7 +186,7 @@ bool charmap_HasChar(std::string const &mapping) {
|
|||||||
return charmap.nodes[nodeIdx].isTerminal();
|
return charmap.nodes[nodeIdx].isTerminal();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t charmap_CharSize(std::string const &mapping) {
|
static CharmapNode const *charmapEntry(std::string const &mapping) {
|
||||||
Charmap const &charmap = *currentCharmap;
|
Charmap const &charmap = *currentCharmap;
|
||||||
size_t nodeIdx = 0;
|
size_t nodeIdx = 0;
|
||||||
|
|
||||||
@@ -197,12 +194,24 @@ size_t charmap_CharSize(std::string const &mapping) {
|
|||||||
nodeIdx = charmap.nodes[nodeIdx].next[static_cast<uint8_t>(c)];
|
nodeIdx = charmap.nodes[nodeIdx].next[static_cast<uint8_t>(c)];
|
||||||
|
|
||||||
if (!nodeIdx) {
|
if (!nodeIdx) {
|
||||||
return 0;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CharmapNode const &node = charmap.nodes[nodeIdx];
|
return &charmap.nodes[nodeIdx];
|
||||||
return node.isTerminal() ? node.value.size() : 0;
|
}
|
||||||
|
|
||||||
|
size_t charmap_CharSize(std::string const &mapping) {
|
||||||
|
CharmapNode const *node = charmapEntry(mapping);
|
||||||
|
return node && node->isTerminal() ? node->value.size() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<int32_t> charmap_CharValue(std::string const &mapping, size_t idx) {
|
||||||
|
if (CharmapNode const *node = charmapEntry(mapping);
|
||||||
|
node && node->isTerminal() && idx < node->value.size()) {
|
||||||
|
return node->value[idx];
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int32_t> charmap_Convert(std::string const &input) {
|
std::vector<int32_t> charmap_Convert(std::string const &input) {
|
||||||
@@ -228,13 +237,13 @@ size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
inputIdx++; // Consume that char
|
++inputIdx; // Consume that char
|
||||||
|
|
||||||
if (charmap.nodes[nodeIdx].isTerminal()) {
|
if (charmap.nodes[nodeIdx].isTerminal()) {
|
||||||
matchIdx = nodeIdx; // This node matches, register it
|
matchIdx = nodeIdx; // This node matches, register it
|
||||||
rewindDistance = 0; // If no longer match is found, rewind here
|
rewindDistance = 0; // If no longer match is found, rewind here
|
||||||
} else {
|
} else {
|
||||||
rewindDistance++;
|
++rewindDistance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,14 +263,15 @@ size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output
|
|||||||
} else if (inputIdx < input.length()) { // No match found, but there is some input left
|
} else if (inputIdx < input.length()) { // No match found, but there is some input left
|
||||||
size_t codepointLen = 0;
|
size_t codepointLen = 0;
|
||||||
// This will write the codepoint's value to `output`, little-endian
|
// This will write the codepoint's value to `output`, little-endian
|
||||||
for (uint32_t state = 0, codepoint = 0; inputIdx + codepointLen < input.length();) {
|
for (uint32_t state = UTF8_ACCEPT, codepoint = 0;
|
||||||
if (decode(&state, &codepoint, input[inputIdx + codepointLen]) == 1) {
|
inputIdx + codepointLen < input.length();) {
|
||||||
error("Input string is not valid UTF-8\n");
|
if (decode(&state, &codepoint, input[inputIdx + codepointLen]) == UTF8_REJECT) {
|
||||||
|
error("Input string is not valid UTF-8");
|
||||||
codepointLen = 1;
|
codepointLen = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
codepointLen++;
|
++codepointLen;
|
||||||
if (state == 0) {
|
if (state == UTF8_ACCEPT) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -274,11 +284,11 @@ size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output
|
|||||||
|
|
||||||
// Warn if this character is not mapped but any others are
|
// Warn if this character is not mapped but any others are
|
||||||
if (int firstChar = input[inputIdx]; charmap.nodes.size() > 1) {
|
if (int firstChar = input[inputIdx]; charmap.nodes.size() > 1) {
|
||||||
warning(WARNING_UNMAPPED_CHAR_1, "Unmapped character %s\n", printChar(firstChar));
|
warning(WARNING_UNMAPPED_CHAR_1, "Unmapped character %s", printChar(firstChar));
|
||||||
} else if (charmap.name != DEFAULT_CHARMAP_NAME) {
|
} else if (charmap.name != DEFAULT_CHARMAP_NAME) {
|
||||||
warning(
|
warning(
|
||||||
WARNING_UNMAPPED_CHAR_2,
|
WARNING_UNMAPPED_CHAR_2,
|
||||||
"Unmapped character %s not in " DEFAULT_CHARMAP_NAME " charmap\n",
|
"Unmapped character %s not in `" DEFAULT_CHARMAP_NAME "` charmap",
|
||||||
printChar(firstChar)
|
printChar(firstChar)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -294,7 +304,7 @@ size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output
|
|||||||
std::string charmap_Reverse(std::vector<int32_t> const &value, bool &unique) {
|
std::string charmap_Reverse(std::vector<int32_t> const &value, bool &unique) {
|
||||||
Charmap const &charmap = *currentCharmap;
|
Charmap const &charmap = *currentCharmap;
|
||||||
std::string revMapping;
|
std::string revMapping;
|
||||||
unique = charmap.forEachChar([&](size_t nodeIdx, std::string const &mapping) {
|
unique = forEachChar(charmap, [&](size_t nodeIdx, std::string const &mapping) {
|
||||||
if (charmap.nodes[nodeIdx].value == value) {
|
if (charmap.nodes[nodeIdx].value == value) {
|
||||||
if (revMapping.empty()) {
|
if (revMapping.empty()) {
|
||||||
revMapping = mapping;
|
revMapping = mapping;
|
||||||
|
|||||||
@@ -5,16 +5,10 @@
|
|||||||
#include "asm/fixpoint.hpp"
|
#include "asm/fixpoint.hpp"
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <numbers>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#ifndef M_PI
|
static constexpr double tau = std::numbers::pi * 2;
|
||||||
#define M_PI 3.14159265358979323846
|
|
||||||
#endif
|
|
||||||
|
|
||||||
uint8_t fixPrecision;
|
|
||||||
|
|
||||||
uint8_t fix_Precision() {
|
|
||||||
return fixPrecision;
|
|
||||||
}
|
|
||||||
|
|
||||||
static double fix2double(int32_t i, int32_t q) {
|
static double fix2double(int32_t i, int32_t q) {
|
||||||
return i / pow(2.0, q);
|
return i / pow(2.0, q);
|
||||||
@@ -31,11 +25,11 @@ static int32_t double2fix(double d, int32_t q) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static double turn2rad(double t) {
|
static double turn2rad(double t) {
|
||||||
return t * (M_PI * 2);
|
return t * tau;
|
||||||
}
|
}
|
||||||
|
|
||||||
static double rad2turn(double r) {
|
static double rad2turn(double r) {
|
||||||
return r / (M_PI * 2);
|
return r / tau;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t fix_Sin(int32_t i, int32_t q) {
|
int32_t fix_Sin(int32_t i, int32_t q) {
|
||||||
|
|||||||
@@ -5,95 +5,76 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "asm/fixpoint.hpp"
|
#include "util.hpp" // isDigit
|
||||||
|
|
||||||
|
#include "asm/main.hpp" // options
|
||||||
#include "asm/warning.hpp"
|
#include "asm/warning.hpp"
|
||||||
|
|
||||||
void FormatSpec::useCharacter(int c) {
|
static size_t parseNumber(char const *spec, size_t &value) {
|
||||||
if (state == FORMAT_INVALID) {
|
size_t i = 0;
|
||||||
return;
|
|
||||||
|
value = 0;
|
||||||
|
for (; isDigit(spec[i]); ++i) {
|
||||||
|
value = value * 10 + (spec[i] - '0');
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (c) {
|
return i;
|
||||||
// sign
|
}
|
||||||
case ' ':
|
|
||||||
case '+':
|
size_t FormatSpec::parseSpec(char const *spec) {
|
||||||
if (state > FORMAT_SIGN) {
|
size_t i = 0;
|
||||||
break;
|
|
||||||
}
|
// <sign>
|
||||||
state = FORMAT_EXACT;
|
if (char c = spec[i]; c == ' ' || c == '+') {
|
||||||
|
++i;
|
||||||
sign = c;
|
sign = c;
|
||||||
return;
|
|
||||||
|
|
||||||
// exact
|
|
||||||
case '#':
|
|
||||||
if (state > FORMAT_EXACT) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
state = FORMAT_ALIGN;
|
|
||||||
|
// <exact>
|
||||||
|
if (spec[i] == '#') {
|
||||||
|
++i;
|
||||||
exact = true;
|
exact = true;
|
||||||
return;
|
|
||||||
|
|
||||||
// align
|
|
||||||
case '-':
|
|
||||||
if (state > FORMAT_ALIGN) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
state = FORMAT_WIDTH;
|
|
||||||
alignLeft = true;
|
|
||||||
return;
|
|
||||||
|
|
||||||
// pad, width, and prec values
|
// <align>
|
||||||
case '0':
|
if (spec[i] == '-') {
|
||||||
if (state < FORMAT_WIDTH) {
|
++i;
|
||||||
|
alignLeft = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// <pad>
|
||||||
|
if (spec[i] == '0') {
|
||||||
|
++i;
|
||||||
padZero = true;
|
padZero = true;
|
||||||
}
|
}
|
||||||
[[fallthrough]];
|
|
||||||
case '1':
|
|
||||||
case '2':
|
|
||||||
case '3':
|
|
||||||
case '4':
|
|
||||||
case '5':
|
|
||||||
case '6':
|
|
||||||
case '7':
|
|
||||||
case '8':
|
|
||||||
case '9':
|
|
||||||
if (state < FORMAT_WIDTH) {
|
|
||||||
state = FORMAT_WIDTH;
|
|
||||||
width = c - '0';
|
|
||||||
} else if (state == FORMAT_WIDTH) {
|
|
||||||
width = width * 10 + (c - '0');
|
|
||||||
} else if (state == FORMAT_FRAC) {
|
|
||||||
fracWidth = fracWidth * 10 + (c - '0');
|
|
||||||
} else if (state == FORMAT_PREC) {
|
|
||||||
precision = precision * 10 + (c - '0');
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
|
|
||||||
// width
|
// <width>
|
||||||
case '.':
|
if (isDigit(spec[i])) {
|
||||||
if (state > FORMAT_WIDTH) {
|
i += parseNumber(&spec[i], width);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
state = FORMAT_FRAC;
|
|
||||||
|
// <frac>
|
||||||
|
if (spec[i] == '.') {
|
||||||
|
++i;
|
||||||
hasFrac = true;
|
hasFrac = true;
|
||||||
return;
|
i += parseNumber(&spec[i], fracWidth);
|
||||||
|
|
||||||
// prec
|
|
||||||
case 'q':
|
|
||||||
if (state > FORMAT_PREC) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
state = FORMAT_PREC;
|
|
||||||
hasPrec = true;
|
|
||||||
return;
|
|
||||||
|
|
||||||
// type
|
// <prec>
|
||||||
|
if (spec[i] == 'q') {
|
||||||
|
++i;
|
||||||
|
hasPrec = true;
|
||||||
|
i += parseNumber(&spec[i], precision);
|
||||||
|
}
|
||||||
|
|
||||||
|
// <type>
|
||||||
|
switch (char c = spec[i]; c) {
|
||||||
case 'd':
|
case 'd':
|
||||||
case 'u':
|
case 'u':
|
||||||
case 'X':
|
case 'X':
|
||||||
@@ -102,26 +83,13 @@ void FormatSpec::useCharacter(int c) {
|
|||||||
case 'o':
|
case 'o':
|
||||||
case 'f':
|
case 'f':
|
||||||
case 's':
|
case 's':
|
||||||
if (state >= FORMAT_DONE) {
|
++i;
|
||||||
break;
|
|
||||||
}
|
|
||||||
state = FORMAT_DONE;
|
|
||||||
valid = true;
|
|
||||||
type = c;
|
type = c;
|
||||||
return;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
state = FORMAT_INVALID;
|
parsed = true;
|
||||||
valid = false;
|
return i;
|
||||||
}
|
|
||||||
|
|
||||||
void FormatSpec::finishCharacters() {
|
|
||||||
if (!isValid()) {
|
|
||||||
state = FORMAT_INVALID;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string escapeString(std::string const &str) {
|
static std::string escapeString(std::string const &str) {
|
||||||
@@ -156,25 +124,25 @@ static std::string escapeString(std::string const &str) {
|
|||||||
|
|
||||||
void FormatSpec::appendString(std::string &str, std::string const &value) const {
|
void FormatSpec::appendString(std::string &str, std::string const &value) const {
|
||||||
int useType = type;
|
int useType = type;
|
||||||
if (isEmpty()) {
|
if (!useType) {
|
||||||
// No format was specified
|
// No format was specified
|
||||||
useType = 's';
|
useType = 's';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sign) {
|
if (sign) {
|
||||||
error("Formatting string with sign flag '%c'\n", sign);
|
error("Formatting string with sign flag '%c'", sign);
|
||||||
}
|
}
|
||||||
if (padZero) {
|
if (padZero) {
|
||||||
error("Formatting string with padding flag '0'\n");
|
error("Formatting string with padding flag '0'");
|
||||||
}
|
}
|
||||||
if (hasFrac) {
|
if (hasFrac) {
|
||||||
error("Formatting string with fractional width\n");
|
error("Formatting string with fractional width");
|
||||||
}
|
}
|
||||||
if (hasPrec) {
|
if (hasPrec) {
|
||||||
error("Formatting string with fractional precision\n");
|
error("Formatting string with fractional precision");
|
||||||
}
|
}
|
||||||
if (useType != 's') {
|
if (useType != 's') {
|
||||||
error("Formatting string as type '%c'\n", useType);
|
error("Formatting string as type '%c'", useType);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string useValue = exact ? escapeString(value) : value;
|
std::string useValue = exact ? escapeString(value) : value;
|
||||||
@@ -195,7 +163,7 @@ void FormatSpec::appendString(std::string &str, std::string const &value) const
|
|||||||
void FormatSpec::appendNumber(std::string &str, uint32_t value) const {
|
void FormatSpec::appendNumber(std::string &str, uint32_t value) const {
|
||||||
int useType = type;
|
int useType = type;
|
||||||
bool useExact = exact;
|
bool useExact = exact;
|
||||||
if (isEmpty()) {
|
if (!useType) {
|
||||||
// No format was specified; default to uppercase $hex
|
// No format was specified; default to uppercase $hex
|
||||||
useType = 'X';
|
useType = 'X';
|
||||||
useExact = true;
|
useExact = true;
|
||||||
@@ -203,16 +171,16 @@ void FormatSpec::appendNumber(std::string &str, uint32_t value) const {
|
|||||||
|
|
||||||
if (useType != 'X' && useType != 'x' && useType != 'b' && useType != 'o' && useType != 'f'
|
if (useType != 'X' && useType != 'x' && useType != 'b' && useType != 'o' && useType != 'f'
|
||||||
&& useExact) {
|
&& useExact) {
|
||||||
error("Formatting type '%c' with exact flag '#'\n", useType);
|
error("Formatting type '%c' with exact flag '#'", useType);
|
||||||
}
|
}
|
||||||
if (useType != 'f' && hasFrac) {
|
if (useType != 'f' && hasFrac) {
|
||||||
error("Formatting type '%c' with fractional width\n", useType);
|
error("Formatting type '%c' with fractional width", useType);
|
||||||
}
|
}
|
||||||
if (useType != 'f' && hasPrec) {
|
if (useType != 'f' && hasPrec) {
|
||||||
error("Formatting type '%c' with fractional precision\n", useType);
|
error("Formatting type '%c' with fractional precision", useType);
|
||||||
}
|
}
|
||||||
if (useType == 's') {
|
if (useType == 's') {
|
||||||
error("Formatting number as type 's'\n");
|
error("Formatting number as type 's'");
|
||||||
}
|
}
|
||||||
|
|
||||||
char signChar = sign; // 0 or ' ' or '+'
|
char signChar = sign; // 0 or ' ' or '+'
|
||||||
@@ -254,15 +222,15 @@ void FormatSpec::appendNumber(std::string &str, uint32_t value) const {
|
|||||||
// Default fractional width (C++'s is 6 for "%f"; here 5 is enough for Q16.16)
|
// Default fractional width (C++'s is 6 for "%f"; here 5 is enough for Q16.16)
|
||||||
size_t useFracWidth = hasFrac ? fracWidth : 5;
|
size_t useFracWidth = hasFrac ? fracWidth : 5;
|
||||||
if (useFracWidth > 255) {
|
if (useFracWidth > 255) {
|
||||||
error("Fractional width %zu too long, limiting to 255\n", useFracWidth);
|
error("Fractional width %zu too long, limiting to 255", useFracWidth);
|
||||||
useFracWidth = 255;
|
useFracWidth = 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t defaultPrec = fix_Precision();
|
size_t defaultPrec = options.fixPrecision;
|
||||||
size_t usePrec = hasPrec ? precision : defaultPrec;
|
size_t usePrec = hasPrec ? precision : defaultPrec;
|
||||||
if (usePrec < 1 || usePrec > 31) {
|
if (usePrec < 1 || usePrec > 31) {
|
||||||
error(
|
error(
|
||||||
"Fixed-point constant precision %zu invalid, defaulting to %zu\n",
|
"Fixed-point constant precision %zu invalid, defaulting to %zu",
|
||||||
usePrec,
|
usePrec,
|
||||||
defaultPrec
|
defaultPrec
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,17 +3,26 @@
|
|||||||
#include "asm/fstack.hpp"
|
#include "asm/fstack.hpp"
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <variant>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "error.hpp"
|
#include "backtrace.hpp"
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "linkdefs.hpp"
|
#include "linkdefs.hpp"
|
||||||
#include "platform.hpp" // S_ISDIR (stat macro)
|
#include "platform.hpp" // strncasecmp
|
||||||
|
#include "verbosity.hpp"
|
||||||
|
|
||||||
#include "asm/lexer.hpp"
|
#include "asm/lexer.hpp"
|
||||||
#include "asm/macro.hpp"
|
#include "asm/macro.hpp"
|
||||||
@@ -41,45 +50,90 @@ struct Context {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static std::stack<Context> contextStack;
|
static std::stack<Context> contextStack;
|
||||||
size_t maxRecursionDepth;
|
|
||||||
|
|
||||||
// The first include path for `fstk_FindFile` to try is none at all
|
// The first include path for `fstk_FindFile` to try is none at all
|
||||||
static std::vector<std::string> includePaths = {""};
|
static std::vector<std::string> includePaths = {""}; // -I
|
||||||
|
static std::deque<std::string> preIncludeNames; // -P
|
||||||
|
static bool failedOnMissingInclude = false;
|
||||||
|
|
||||||
static std::string preIncludeName;
|
static std::string reptChain(FileStackNode const &node) {
|
||||||
|
std::string chain;
|
||||||
std::string const &FileStackNode::dump(uint32_t curLineNo) const {
|
std::vector<uint32_t> const &nodeIters = node.iters();
|
||||||
if (data.holds<std::vector<uint32_t>>()) {
|
|
||||||
assume(parent); // REPT nodes use their parent's name
|
|
||||||
std::string const &lastName = parent->dump(lineNo);
|
|
||||||
fputs(" -> ", stderr);
|
|
||||||
fputs(lastName.c_str(), stderr);
|
|
||||||
std::vector<uint32_t> const &nodeIters = iters();
|
|
||||||
for (uint32_t i = nodeIters.size(); i--;) {
|
for (uint32_t i = nodeIters.size(); i--;) {
|
||||||
fprintf(stderr, "::REPT~%" PRIu32, nodeIters[i]);
|
chain.append("::REPT~");
|
||||||
}
|
chain.append(std::to_string(nodeIters[i]));
|
||||||
fprintf(stderr, "(%" PRIu32 ")", curLineNo);
|
|
||||||
return lastName;
|
|
||||||
} else {
|
|
||||||
if (parent) {
|
|
||||||
parent->dump(lineNo);
|
|
||||||
fputs(" -> ", stderr);
|
|
||||||
}
|
|
||||||
std::string const &nodeName = name();
|
|
||||||
fputs(nodeName.c_str(), stderr);
|
|
||||||
fprintf(stderr, "(%" PRIu32 ")", curLineNo);
|
|
||||||
return nodeName;
|
|
||||||
}
|
}
|
||||||
|
return chain;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_DumpCurrent() {
|
using TraceNode = std::pair<std::string, uint32_t>;
|
||||||
if (lexer_AtTopLevel()) {
|
|
||||||
fputs("at top level", stderr);
|
static std::vector<TraceNode> backtrace(FileStackNode const &node, uint32_t curLineNo) {
|
||||||
return;
|
if (node.isQuiet && !tracing.loud) {
|
||||||
|
if (node.parent) {
|
||||||
|
// Quiet REPT nodes will pass their interior line number up to their parent,
|
||||||
|
// which is more precise than the parent's own line number (since that will be
|
||||||
|
// the line number of the "REPT?" or "FOR?" itself).
|
||||||
|
return backtrace(*node.parent, node.type == NODE_REPT ? curLineNo : node.lineNo);
|
||||||
}
|
}
|
||||||
contextStack.top().fileInfo->dump(lexer_GetLineNo());
|
return {}; // LCOV_EXCL_LINE
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!node.parent) {
|
||||||
|
assume(node.type != NODE_REPT && std::holds_alternative<std::string>(node.data));
|
||||||
|
return {
|
||||||
|
{node.name(), curLineNo}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<TraceNode> traceNodes = backtrace(*node.parent, node.lineNo);
|
||||||
|
if (std::holds_alternative<std::vector<uint32_t>>(node.data)) {
|
||||||
|
assume(!traceNodes.empty()); // REPT nodes use their parent's name
|
||||||
|
traceNodes.emplace_back(traceNodes.back().first + reptChain(node), curLineNo);
|
||||||
|
} else {
|
||||||
|
traceNodes.emplace_back(node.name(), curLineNo);
|
||||||
|
}
|
||||||
|
return traceNodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FileStackNode::printBacktrace(uint32_t curLineNo) const {
|
||||||
|
trace_PrintBacktrace(
|
||||||
|
backtrace(*this, curLineNo),
|
||||||
|
[](TraceNode const &node) { return node.first.c_str(); },
|
||||||
|
[](TraceNode const &node) { return node.second; }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fstk_TraceCurrent() {
|
||||||
|
if (!lexer_AtTopLevel()) {
|
||||||
|
assume(!contextStack.empty());
|
||||||
|
contextStack.top().fileInfo->printBacktrace(lexer_GetLineNo());
|
||||||
|
}
|
||||||
|
lexer_TraceStringExpansions();
|
||||||
|
}
|
||||||
|
|
||||||
|
// LCOV_EXCL_START
|
||||||
|
void fstk_VerboseOutputConfig() {
|
||||||
|
assume(checkVerbosity(VERB_CONFIG));
|
||||||
|
// -I/--include
|
||||||
|
if (includePaths.size() > 1) {
|
||||||
|
fputs("\tInclude file paths:\n", stderr);
|
||||||
|
for (std::string const &path : includePaths) {
|
||||||
|
if (!path.empty()) {
|
||||||
|
fprintf(stderr, "\t - %s\n", path.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// -P/--preinclude
|
||||||
|
if (!preIncludeNames.empty()) {
|
||||||
|
fputs("\tPreincluded files:\n", stderr);
|
||||||
|
for (std::string const &name : preIncludeNames) {
|
||||||
|
fprintf(stderr, "\t - %s\n", name.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
std::shared_ptr<FileStackNode> fstk_GetFileStack() {
|
std::shared_ptr<FileStackNode> fstk_GetFileStack() {
|
||||||
return contextStack.empty() ? nullptr : contextStack.top().fileInfo;
|
return contextStack.empty() ? nullptr : contextStack.top().fileInfo;
|
||||||
}
|
}
|
||||||
@@ -114,25 +168,8 @@ void fstk_AddIncludePath(std::string const &path) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_SetPreIncludeFile(std::string const &path) {
|
void fstk_AddPreIncludeFile(std::string const &path) {
|
||||||
if (!preIncludeName.empty()) {
|
preIncludeNames.emplace_front(path);
|
||||||
warnx("Overriding pre-included filename %s", preIncludeName.c_str());
|
|
||||||
}
|
|
||||||
preIncludeName = path;
|
|
||||||
// LCOV_EXCL_START
|
|
||||||
if (verbose) {
|
|
||||||
printf("Pre-included filename %s\n", preIncludeName.c_str());
|
|
||||||
}
|
|
||||||
// LCOV_EXCL_STOP
|
|
||||||
}
|
|
||||||
|
|
||||||
static void printDep(std::string const &path) {
|
|
||||||
if (dependFile) {
|
|
||||||
fprintf(dependFile, "%s: %s\n", targetFileName.c_str(), path.c_str());
|
|
||||||
if (generatePhonyDeps) {
|
|
||||||
fprintf(dependFile, "%s:\n", path.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isValidFilePath(std::string const &path) {
|
static bool isValidFilePath(std::string const &path) {
|
||||||
@@ -140,6 +177,13 @@ static bool isValidFilePath(std::string const &path) {
|
|||||||
return stat(path.c_str(), &statBuf) == 0 && !S_ISDIR(statBuf.st_mode); // Reject directories
|
return stat(path.c_str(), &statBuf) == 0 && !S_ISDIR(statBuf.st_mode); // Reject directories
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void printDep(std::string const &path) {
|
||||||
|
options.printDep(path);
|
||||||
|
if (options.dependFile && options.generatePhonyDeps && isValidFilePath(path)) {
|
||||||
|
fprintf(options.dependFile, "%s:\n", path.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<std::string> fstk_FindFile(std::string const &path) {
|
std::optional<std::string> fstk_FindFile(std::string const &path) {
|
||||||
for (std::string &incPath : includePaths) {
|
for (std::string &incPath : includePaths) {
|
||||||
if (std::string fullPath = incPath + path; isValidFilePath(fullPath)) {
|
if (std::string fullPath = incPath + path; isValidFilePath(fullPath)) {
|
||||||
@@ -149,7 +193,7 @@ std::optional<std::string> fstk_FindFile(std::string const &path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
errno = ENOENT;
|
errno = ENOENT;
|
||||||
if (generatedMissingIncludes) {
|
if (options.missingIncludeState != INC_ERROR) {
|
||||||
printDep(path);
|
printDep(path);
|
||||||
}
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
@@ -159,9 +203,10 @@ bool yywrap() {
|
|||||||
uint32_t ifDepth = lexer_GetIFDepth();
|
uint32_t ifDepth = lexer_GetIFDepth();
|
||||||
|
|
||||||
if (ifDepth != 0) {
|
if (ifDepth != 0) {
|
||||||
fatalerror(
|
fatal(
|
||||||
"Ended block with %" PRIu32 " unterminated IF construct%s\n",
|
"Ended block with %" PRIu32 " unterminated conditional%s (`IF`/`ELIF`/`ELSE` block%s)",
|
||||||
ifDepth,
|
ifDepth,
|
||||||
|
ifDepth == 1 ? "" : "s",
|
||||||
ifDepth == 1 ? "" : "s"
|
ifDepth == 1 ? "" : "s"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -188,11 +233,11 @@ bool yywrap() {
|
|||||||
|
|
||||||
// This error message will refer to the current iteration
|
// This error message will refer to the current iteration
|
||||||
if (sym->type != SYM_VAR) {
|
if (sym->type != SYM_VAR) {
|
||||||
fatalerror("Failed to update FOR symbol value\n");
|
fatal("Failed to update `FOR` symbol value");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Advance to the next iteration
|
// Advance to the next iteration
|
||||||
fileInfoIters.front()++;
|
++fileInfoIters.front();
|
||||||
// If this wasn't the last iteration, wrap instead of popping
|
// If this wasn't the last iteration, wrap instead of popping
|
||||||
if (fileInfoIters.front() <= context.nbReptIters) {
|
if (fileInfoIters.front() <= context.nbReptIters) {
|
||||||
lexer_RestartRept(context.fileInfo->lineNo);
|
lexer_RestartRept(context.fileInfo->lineNo);
|
||||||
@@ -210,19 +255,19 @@ bool yywrap() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void checkRecursionDepth() {
|
static void checkRecursionDepth() {
|
||||||
if (contextStack.size() > maxRecursionDepth) {
|
if (contextStack.size() > options.maxRecursionDepth) {
|
||||||
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
|
fatal("Recursion limit (%zu) exceeded", options.maxRecursionDepth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool newFileContext(std::string const &filePath, bool updateStateNow) {
|
static void newFileContext(std::string const &filePath, bool isQuiet, bool updateStateNow) {
|
||||||
checkRecursionDepth();
|
checkRecursionDepth();
|
||||||
|
|
||||||
std::shared_ptr<std::string> uniqueIDStr = nullptr;
|
std::shared_ptr<std::string> uniqueIDStr = nullptr;
|
||||||
std::shared_ptr<MacroArgs> macroArgs = nullptr;
|
std::shared_ptr<MacroArgs> macroArgs = nullptr;
|
||||||
|
|
||||||
auto fileInfo =
|
auto fileInfo =
|
||||||
std::make_shared<FileStackNode>(NODE_FILE, filePath == "-" ? "<stdin>" : filePath);
|
std::make_shared<FileStackNode>(NODE_FILE, filePath == "-" ? "<stdin>" : filePath, isQuiet);
|
||||||
if (!contextStack.empty()) {
|
if (!contextStack.empty()) {
|
||||||
Context &oldContext = contextStack.top();
|
Context &oldContext = contextStack.top();
|
||||||
fileInfo->parent = oldContext.fileInfo;
|
fileInfo->parent = oldContext.fileInfo;
|
||||||
@@ -237,10 +282,11 @@ static bool newFileContext(std::string const &filePath, bool updateStateNow) {
|
|||||||
.macroArgs = macroArgs,
|
.macroArgs = macroArgs,
|
||||||
});
|
});
|
||||||
|
|
||||||
return context.lexerState.setFileAsNextState(filePath, updateStateNow);
|
context.lexerState.setFileAsNextState(filePath, updateStateNow);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void newMacroContext(Symbol const ¯o, std::shared_ptr<MacroArgs> macroArgs) {
|
static void
|
||||||
|
newMacroContext(Symbol const ¯o, std::shared_ptr<MacroArgs> macroArgs, bool isQuiet) {
|
||||||
checkRecursionDepth();
|
checkRecursionDepth();
|
||||||
|
|
||||||
Context &oldContext = contextStack.top();
|
Context &oldContext = contextStack.top();
|
||||||
@@ -253,16 +299,12 @@ static void newMacroContext(Symbol const ¯o, std::shared_ptr<MacroArgs> macr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (macro.src->type == NODE_REPT) {
|
if (macro.src->type == NODE_REPT) {
|
||||||
std::vector<uint32_t> const &srcIters = macro.src->iters();
|
fileInfoName.append(reptChain(*macro.src));
|
||||||
for (uint32_t i = srcIters.size(); i--;) {
|
|
||||||
fileInfoName.append("::REPT~");
|
|
||||||
fileInfoName.append(std::to_string(srcIters[i]));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fileInfoName.append("::");
|
fileInfoName.append("::");
|
||||||
fileInfoName.append(macro.name);
|
fileInfoName.append(macro.name);
|
||||||
|
|
||||||
auto fileInfo = std::make_shared<FileStackNode>(NODE_MACRO, fileInfoName);
|
auto fileInfo = std::make_shared<FileStackNode>(NODE_MACRO, fileInfoName, isQuiet);
|
||||||
assume(!contextStack.empty()); // The top level context cannot be a MACRO
|
assume(!contextStack.empty()); // The top level context cannot be a MACRO
|
||||||
fileInfo->parent = oldContext.fileInfo;
|
fileInfo->parent = oldContext.fileInfo;
|
||||||
fileInfo->lineNo = lexer_GetLineNo();
|
fileInfo->lineNo = lexer_GetLineNo();
|
||||||
@@ -276,7 +318,8 @@ static void newMacroContext(Symbol const ¯o, std::shared_ptr<MacroArgs> macr
|
|||||||
context.lexerState.setViewAsNextState("MACRO", macro.getMacro(), macro.fileLine);
|
context.lexerState.setViewAsNextState("MACRO", macro.getMacro(), macro.fileLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Context &newReptContext(int32_t reptLineNo, ContentSpan const &span, uint32_t count) {
|
static Context &
|
||||||
|
newReptContext(int32_t reptLineNo, ContentSpan const &span, uint32_t count, bool isQuiet) {
|
||||||
checkRecursionDepth();
|
checkRecursionDepth();
|
||||||
|
|
||||||
Context &oldContext = contextStack.top();
|
Context &oldContext = contextStack.top();
|
||||||
@@ -287,7 +330,7 @@ static Context &newReptContext(int32_t reptLineNo, ContentSpan const &span, uint
|
|||||||
fileInfoIters.insert(fileInfoIters.end(), RANGE(oldContext.fileInfo->iters()));
|
fileInfoIters.insert(fileInfoIters.end(), RANGE(oldContext.fileInfo->iters()));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto fileInfo = std::make_shared<FileStackNode>(NODE_REPT, fileInfoIters);
|
auto fileInfo = std::make_shared<FileStackNode>(NODE_REPT, fileInfoIters, isQuiet);
|
||||||
assume(!contextStack.empty()); // The top level context cannot be a REPT
|
assume(!contextStack.empty()); // The top level context cannot be a REPT
|
||||||
fileInfo->parent = oldContext.fileInfo;
|
fileInfo->parent = oldContext.fileInfo;
|
||||||
fileInfo->lineNo = reptLineNo;
|
fileInfo->lineNo = reptLineNo;
|
||||||
@@ -305,53 +348,94 @@ static Context &newReptContext(int32_t reptLineNo, ContentSpan const &span, uint
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_RunInclude(std::string const &path, bool preInclude) {
|
bool fstk_FileError(std::string const &path, char const *functionName) {
|
||||||
std::optional<std::string> fullPath = fstk_FindFile(path);
|
if (options.missingIncludeState == INC_ERROR) {
|
||||||
|
error("Error opening `%s` file \"%s\": %s", functionName, path.c_str(), strerror(errno));
|
||||||
if (!fullPath) {
|
|
||||||
if (generatedMissingIncludes && !preInclude) {
|
|
||||||
// LCOV_EXCL_START
|
|
||||||
if (verbose) {
|
|
||||||
printf("Aborting (-MG) on INCLUDE file '%s' (%s)\n", path.c_str(), strerror(errno));
|
|
||||||
}
|
|
||||||
// LCOV_EXCL_STOP
|
|
||||||
failedOnMissingInclude = true;
|
|
||||||
} else {
|
} else {
|
||||||
error("Unable to open included file '%s': %s\n", path.c_str(), strerror(errno));
|
failedOnMissingInclude = true;
|
||||||
|
// LCOV_EXCL_START
|
||||||
|
if (options.missingIncludeState == GEN_EXIT) {
|
||||||
|
verbosePrint(
|
||||||
|
VERB_NOTICE,
|
||||||
|
"Aborting due to '-MG' on `%s` file \"%s\": %s\n",
|
||||||
|
functionName,
|
||||||
|
path.c_str(),
|
||||||
|
strerror(errno)
|
||||||
|
);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return;
|
assume(options.missingIncludeState == GEN_CONTINUE);
|
||||||
}
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
if (!newFileContext(*fullPath, false)) {
|
|
||||||
fatalerror("Failed to set up lexer for file include\n"); // LCOV_EXCL_LINE
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_RunMacro(std::string const ¯oName, std::shared_ptr<MacroArgs> macroArgs) {
|
bool fstk_FailedOnMissingInclude() {
|
||||||
|
return failedOnMissingInclude;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fstk_RunInclude(std::string const &path, bool isQuiet) {
|
||||||
|
if (std::optional<std::string> fullPath = fstk_FindFile(path); fullPath) {
|
||||||
|
newFileContext(*fullPath, isQuiet, false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return fstk_FileError(path, "INCLUDE");
|
||||||
|
}
|
||||||
|
|
||||||
|
static char const *suggestDef(std::shared_ptr<MacroArgs> const macroArgs) {
|
||||||
|
std::shared_ptr<std::string> arg = macroArgs->getArg(1);
|
||||||
|
if (!arg) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
char const *str = arg->c_str();
|
||||||
|
static char const *types[] = {"EQUS", "EQU", "RB", "RW", "RL", "="};
|
||||||
|
for (size_t i = 0; i < std::size(types); ++i) {
|
||||||
|
if (char const *type = types[i]; strncasecmp(str, type, strlen(type)) == 0) {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (strncasecmp(str, "SET", literal_strlen("SET")) == 0) {
|
||||||
|
return "=";
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fstk_RunMacro(
|
||||||
|
std::string const ¯oName, std::shared_ptr<MacroArgs> macroArgs, bool isQuiet
|
||||||
|
) {
|
||||||
Symbol *macro = sym_FindExactSymbol(macroName);
|
Symbol *macro = sym_FindExactSymbol(macroName);
|
||||||
|
|
||||||
if (!macro) {
|
if (!macro) {
|
||||||
if (sym_IsPurgedExact(macroName)) {
|
if (sym_IsPurgedExact(macroName)) {
|
||||||
error("Macro \"%s\" not defined; it was purged\n", macroName.c_str());
|
error("Undefined macro `%s`; it was purged", macroName.c_str());
|
||||||
|
} else if (char const *defType = suggestDef(macroArgs); defType) {
|
||||||
|
error(
|
||||||
|
"Undefined macro `%s` (did you mean \"DEF %s %s ...\"?)",
|
||||||
|
macroName.c_str(),
|
||||||
|
macroName.c_str(),
|
||||||
|
defType
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
error("Macro \"%s\" not defined\n", macroName.c_str());
|
error("Undefined macro `%s`", macroName.c_str());
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (macro->type != SYM_MACRO) {
|
if (macro->type != SYM_MACRO) {
|
||||||
error("\"%s\" is not a macro\n", macroName.c_str());
|
error("`%s` is not a macro", macroName.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
newMacroContext(*macro, macroArgs);
|
newMacroContext(*macro, macroArgs, isQuiet || macro->isQuiet);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_RunRept(uint32_t count, int32_t reptLineNo, ContentSpan const &span) {
|
void fstk_RunRept(uint32_t count, int32_t reptLineNo, ContentSpan const &span, bool isQuiet) {
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
newReptContext(reptLineNo, span, count);
|
newReptContext(reptLineNo, span, count, isQuiet);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_RunFor(
|
void fstk_RunFor(
|
||||||
@@ -360,7 +444,8 @@ void fstk_RunFor(
|
|||||||
int32_t stop,
|
int32_t stop,
|
||||||
int32_t step,
|
int32_t step,
|
||||||
int32_t reptLineNo,
|
int32_t reptLineNo,
|
||||||
ContentSpan const &span
|
ContentSpan const &span,
|
||||||
|
bool isQuiet
|
||||||
) {
|
) {
|
||||||
if (Symbol *sym = sym_AddVar(symName, start); sym->type != SYM_VAR) {
|
if (Symbol *sym = sym_AddVar(symName, start); sym->type != SYM_VAR) {
|
||||||
return;
|
return;
|
||||||
@@ -372,12 +457,12 @@ void fstk_RunFor(
|
|||||||
} else if (step < 0 && stop < start) {
|
} else if (step < 0 && stop < start) {
|
||||||
count = (static_cast<int64_t>(start) - stop - 1) / -static_cast<int64_t>(step) + 1;
|
count = (static_cast<int64_t>(start) - stop - 1) / -static_cast<int64_t>(step) + 1;
|
||||||
} else if (step == 0) {
|
} else if (step == 0) {
|
||||||
error("FOR cannot have a step value of 0\n");
|
error("`FOR` cannot have a step value of 0");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((step > 0 && start > stop) || (step < 0 && start < stop)) {
|
if ((step > 0 && start > stop) || (step < 0 && start < stop)) {
|
||||||
warning(
|
warning(
|
||||||
WARNING_BACKWARDS_FOR, "FOR goes backwards from %d to %d by %d\n", start, stop, step
|
WARNING_BACKWARDS_FOR, "`FOR` goes backwards from %d to %d by %d", start, stop, step
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -385,42 +470,38 @@ void fstk_RunFor(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Context &context = newReptContext(reptLineNo, span, count);
|
Context &context = newReptContext(reptLineNo, span, count, isQuiet);
|
||||||
context.isForLoop = true;
|
context.isForLoop = true;
|
||||||
context.forValue = start;
|
context.forValue = start;
|
||||||
context.forStep = step;
|
context.forStep = step;
|
||||||
context.forName = symName;
|
context.forName = symName;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_StopRept() {
|
|
||||||
contextStack.top().nbReptIters = 0; // Prevent more iterations
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fstk_Break() {
|
bool fstk_Break() {
|
||||||
if (contextStack.top().fileInfo->type != NODE_REPT) {
|
if (contextStack.top().fileInfo->type != NODE_REPT) {
|
||||||
error("BREAK can only be used inside a REPT/FOR block\n");
|
error("`BREAK` can only be used inside a loop (`REPT`/`FOR` block)");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fstk_StopRept();
|
contextStack.top().nbReptIters = 0; // Prevent more iterations
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_NewRecursionDepth(size_t newDepth) {
|
void fstk_NewRecursionDepth(size_t newDepth) {
|
||||||
if (contextStack.size() > newDepth + 1) {
|
if (contextStack.size() > newDepth + 1) {
|
||||||
fatalerror("Recursion limit (%zu) exceeded\n", newDepth);
|
fatal("Recursion limit (%zu) exceeded", newDepth);
|
||||||
}
|
}
|
||||||
maxRecursionDepth = newDepth;
|
options.maxRecursionDepth = newDepth;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_Init(std::string const &mainPath, size_t maxDepth) {
|
void fstk_Init(std::string const &mainPath) {
|
||||||
if (!newFileContext(mainPath, true)) {
|
newFileContext(mainPath, false, true);
|
||||||
fatalerror("Failed to open main file\n");
|
|
||||||
|
for (std::string const &name : preIncludeNames) {
|
||||||
|
if (std::optional<std::string> fullPath = fstk_FindFile(name); fullPath) {
|
||||||
|
newFileContext(*fullPath, false, false);
|
||||||
|
} else {
|
||||||
|
error("Error reading pre-included file \"%s\": %s", name.c_str(), strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
maxRecursionDepth = maxDepth;
|
|
||||||
|
|
||||||
if (!preIncludeName.empty()) {
|
|
||||||
fstk_RunInclude(preIncludeName, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1905
src/asm/lexer.cpp
1905
src/asm/lexer.cpp
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include "asm/macro.hpp"
|
#include "asm/macro.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -29,15 +31,15 @@ std::shared_ptr<std::string> MacroArgs::getAllArgs() const {
|
|||||||
|
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
|
|
||||||
for (uint32_t i = shift; i < nbArgs; i++) {
|
for (uint32_t i = shift; i < nbArgs; ++i) {
|
||||||
len += args[i]->length() + 1; // 1 for comma
|
len += args[i]->length() + 1; // 1 for comma
|
||||||
}
|
}
|
||||||
|
|
||||||
auto str = std::make_shared<std::string>();
|
auto str = std::make_shared<std::string>();
|
||||||
str->reserve(len + 1); // 1 for comma
|
str->reserve(len + 1); // 1 for comma
|
||||||
|
|
||||||
for (uint32_t i = shift; i < nbArgs; i++) {
|
for (uint32_t i = shift; i < nbArgs; ++i) {
|
||||||
auto const &arg = args[i];
|
std::shared_ptr<std::string> const &arg = args[i];
|
||||||
|
|
||||||
str->append(*arg);
|
str->append(*arg);
|
||||||
|
|
||||||
@@ -52,7 +54,7 @@ std::shared_ptr<std::string> MacroArgs::getAllArgs() const {
|
|||||||
|
|
||||||
void MacroArgs::appendArg(std::shared_ptr<std::string> arg) {
|
void MacroArgs::appendArg(std::shared_ptr<std::string> arg) {
|
||||||
if (arg->empty()) {
|
if (arg->empty()) {
|
||||||
warning(WARNING_EMPTY_MACRO_ARG, "Empty macro argument\n");
|
warning(WARNING_EMPTY_MACRO_ARG, "Empty macro argument");
|
||||||
}
|
}
|
||||||
args.push_back(arg);
|
args.push_back(arg);
|
||||||
}
|
}
|
||||||
@@ -60,10 +62,10 @@ void MacroArgs::appendArg(std::shared_ptr<std::string> arg) {
|
|||||||
void MacroArgs::shiftArgs(int32_t count) {
|
void MacroArgs::shiftArgs(int32_t count) {
|
||||||
if (size_t nbArgs = args.size();
|
if (size_t nbArgs = args.size();
|
||||||
count > 0 && (static_cast<uint32_t>(count) > nbArgs || shift > nbArgs - count)) {
|
count > 0 && (static_cast<uint32_t>(count) > nbArgs || shift > nbArgs - count)) {
|
||||||
warning(WARNING_MACRO_SHIFT, "Cannot shift macro arguments past their end\n");
|
warning(WARNING_MACRO_SHIFT, "Cannot shift macro arguments past their end");
|
||||||
shift = nbArgs;
|
shift = nbArgs;
|
||||||
} else if (count < 0 && shift < static_cast<uint32_t>(-count)) {
|
} else if (count < 0 && shift < static_cast<uint32_t>(-count)) {
|
||||||
warning(WARNING_MACRO_SHIFT, "Cannot shift macro arguments past their beginning\n");
|
warning(WARNING_MACRO_SHIFT, "Cannot shift macro arguments past their beginning");
|
||||||
shift = 0;
|
shift = 0;
|
||||||
} else {
|
} else {
|
||||||
shift += count;
|
shift += count;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user