mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-21 02:32:06 +00:00
Compare commits
173 Commits
v0.9.0-rc1
...
v0.9.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1715f85d50 | ||
|
|
c2de0a991a | ||
|
|
2e6e1ccf06 | ||
|
|
081f48404c | ||
|
|
bdac0ce053 | ||
|
|
122d91509f | ||
|
|
7c6f778ae7 | ||
|
|
8cf6c5423a | ||
|
|
56f7222230 | ||
|
|
e45b9625ca | ||
|
|
e0a8eb8aff | ||
|
|
2a5b9b5f98 | ||
|
|
a72843748f | ||
|
|
762e2311d2 | ||
|
|
0b7cda9e0c | ||
|
|
df83bc31d2 | ||
|
|
bc8d99d915 | ||
|
|
c841672059 | ||
|
|
75b605797d | ||
|
|
00d0ae840d | ||
|
|
2cdbb145da | ||
|
|
d8192560b0 | ||
|
|
9b395f3bf1 | ||
|
|
0150eb4bf3 | ||
|
|
632342b254 | ||
|
|
c9060c7f2d | ||
|
|
993879a2ed | ||
|
|
62309d5c87 | ||
|
|
b2e865ee2a | ||
|
|
3feb75f84f | ||
|
|
ad4d9da4cf | ||
|
|
1489854932 | ||
|
|
2aef09c8d9 | ||
|
|
48412e9c56 | ||
|
|
177a3abfac | ||
|
|
4c916b8da8 | ||
|
|
d9d381cb62 | ||
|
|
fbde24ee17 | ||
|
|
91310c9eb6 | ||
|
|
81ea4ee920 | ||
|
|
29ece2940d | ||
|
|
03452c6d4f | ||
|
|
b35e9d86fb | ||
|
|
e20347e38c | ||
|
|
f61019dd68 | ||
|
|
c19ddc80f0 | ||
|
|
a59867cd78 | ||
|
|
375adc6804 | ||
|
|
44caffe04a | ||
|
|
d54619a453 | ||
|
|
e49291b7cf | ||
|
|
34a9c8e083 | ||
|
|
c4b456b166 | ||
|
|
79401cce8b | ||
|
|
4e44958d26 | ||
|
|
cae31005f8 | ||
|
|
25c9f8f383 | ||
|
|
01c9106b59 | ||
|
|
192fc808c8 | ||
|
|
9c8e327ae2 | ||
|
|
9ebd2a7e8e | ||
|
|
b8b60207f5 | ||
|
|
c5e59f40fd | ||
|
|
a354af3d08 | ||
|
|
20c18256ed | ||
|
|
890528812e | ||
|
|
84f59e14ed | ||
|
|
91d7ce5e09 | ||
|
|
d9654b752f | ||
|
|
157826bf82 | ||
|
|
a5e36f924f | ||
|
|
82f7bdb480 | ||
|
|
056190413e | ||
|
|
c2db23aef0 | ||
|
|
2426068409 | ||
|
|
147a5c9bf3 | ||
|
|
6ae3f040b8 | ||
|
|
e561f63db3 | ||
|
|
af9de812ec | ||
|
|
edc9e07a2d | ||
|
|
382ad17969 | ||
|
|
fac5e35d24 | ||
|
|
a85d6b3b57 | ||
|
|
f23a14afc7 | ||
|
|
f63167dd0f | ||
|
|
0ee4ba95b3 | ||
|
|
727c1f5b50 | ||
|
|
d829fd2ffe | ||
|
|
b13c0f2f8e | ||
|
|
d9773424e4 | ||
|
|
4e2464a69d | ||
|
|
a5f12f66bb | ||
|
|
73ad431b8d | ||
|
|
d88feee1c0 | ||
|
|
5963dc9e0e | ||
|
|
8363f25d47 | ||
|
|
72b2a4d7c0 | ||
|
|
06daf2a9b5 | ||
|
|
ad95d2e06f | ||
|
|
5197e6b79f | ||
|
|
b99ce3845e | ||
|
|
d63955eccd | ||
|
|
2c4fc4cbe8 | ||
|
|
7d3c31b6d8 | ||
|
|
151f83db6d | ||
|
|
22838ce2d8 | ||
|
|
b058bb6e15 | ||
|
|
36b04b5dea | ||
|
|
a7296ecb31 | ||
|
|
92917ceb2f | ||
|
|
c1c5b10082 | ||
|
|
f44de0c7ae | ||
|
|
b18cfe6bdb | ||
|
|
a8ec9228d4 | ||
|
|
c1b85554a8 | ||
|
|
b877c81c32 | ||
|
|
e66da4c8c7 | ||
|
|
573e044b30 | ||
|
|
ceb43c7aa4 | ||
|
|
eae1ecb77e | ||
|
|
c4de6c402b | ||
|
|
0b147c9386 | ||
|
|
6982c8a116 | ||
|
|
d5f39c8dce | ||
|
|
a5d18d62df | ||
|
|
a27f704c25 | ||
|
|
9216485bca | ||
|
|
c33acb905b | ||
|
|
81c3521610 | ||
|
|
e0ee9dc3ad | ||
|
|
cb546f0cd8 | ||
|
|
a60186db2f | ||
|
|
d9f87a5721 | ||
|
|
a7fdb2c3d3 | ||
|
|
5efd303b7f | ||
|
|
0d3980d039 | ||
|
|
ab6244d81c | ||
|
|
7fcf4ba60f | ||
|
|
f048cbbb11 | ||
|
|
4c495c31d9 | ||
|
|
90286ccbbc | ||
|
|
b33aa31944 | ||
|
|
dd6c741143 | ||
|
|
3b3263273c | ||
|
|
bc5a71ff88 | ||
|
|
e623aeb85d | ||
|
|
a2ff653a83 | ||
|
|
a13723c523 | ||
|
|
cf85146353 | ||
|
|
a9e49a09fd | ||
|
|
cbe44fed9b | ||
|
|
c439b8e27f | ||
|
|
86bf289452 | ||
|
|
e1ac7f389d | ||
|
|
d5159f66be | ||
|
|
c7a029a051 | ||
|
|
d5ded84501 | ||
|
|
4cd0dd5314 | ||
|
|
9783671399 | ||
|
|
8037b9e10a | ||
|
|
7c74653aa1 | ||
|
|
22767e36e2 | ||
|
|
6b89938da7 | ||
|
|
15919e550f | ||
|
|
f93587c805 | ||
|
|
a870f7de10 | ||
|
|
6b72067387 | ||
|
|
84c01f064f | ||
|
|
5d3e96662e | ||
|
|
91580043e0 | ||
|
|
3e28e92622 | ||
|
|
d494f73825 | ||
|
|
b03a5b13b7 |
@@ -24,6 +24,7 @@ AttributeMacros:
|
|||||||
BinPackArguments: false
|
BinPackArguments: false
|
||||||
BinPackParameters: false
|
BinPackParameters: false
|
||||||
BitFieldColonSpacing: Both
|
BitFieldColonSpacing: Both
|
||||||
|
BreakAfterAttributes: Always
|
||||||
BreakBeforeBinaryOperators: NonAssignment
|
BreakBeforeBinaryOperators: NonAssignment
|
||||||
BreakBeforeBraces: Attach
|
BreakBeforeBraces: Attach
|
||||||
BreakBeforeConceptDeclarations: true
|
BreakBeforeConceptDeclarations: true
|
||||||
@@ -54,12 +55,14 @@ IncludeCategories:
|
|||||||
IndentAccessModifiers: false
|
IndentAccessModifiers: false
|
||||||
IndentCaseBlocks: false
|
IndentCaseBlocks: false
|
||||||
IndentCaseLabels: false
|
IndentCaseLabels: false
|
||||||
IndentExternBlock: NoIndent
|
IndentExternBlock: Indent
|
||||||
IndentGotoLabels: false
|
IndentGotoLabels: false
|
||||||
IndentPPDirectives: BeforeHash
|
IndentPPDirectives: BeforeHash
|
||||||
IndentRequires: true
|
IndentRequires: true
|
||||||
IndentWidth: 4
|
IndentWidth: 4
|
||||||
IndentWrappedFunctionNames: true
|
IndentWrappedFunctionNames: true
|
||||||
|
InsertBraces: true
|
||||||
|
InsertNewlineAtEOF: true
|
||||||
# Only support for Javascript as of clang-format 14...
|
# Only support for Javascript as of clang-format 14...
|
||||||
# InsertTrailingCommas: Wrapped
|
# InsertTrailingCommas: Wrapped
|
||||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||||
@@ -71,6 +74,8 @@ PPIndentWidth: -1
|
|||||||
PointerAlignment: Right
|
PointerAlignment: Right
|
||||||
QualifierAlignment: Right
|
QualifierAlignment: Right
|
||||||
ReflowComments: true
|
ReflowComments: true
|
||||||
|
RemoveParentheses: ReturnStatement
|
||||||
|
RemoveSemicolon: true
|
||||||
SortIncludes: CaseSensitive
|
SortIncludes: CaseSensitive
|
||||||
SortUsingDeclarations: true
|
SortUsingDeclarations: true
|
||||||
SpaceAfterCStyleCast: false
|
SpaceAfterCStyleCast: 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.43
|
pngver=1.6.45
|
||||||
|
|
||||||
## 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)" != 6a5ca0652392a2d7c9db2ae5b40210843c0bbc081cbd410825ab00cc59f14a6c ]; then
|
if [ "$(sha2 -q -256 libpng-$pngver.tar.xz)" != 926485350139ffb51ef69760db35f78846c805fef3d59bfdcb2fba704663f370 ]; 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.43.zip' 'libpng.zip' '5e18474a26814ae479e02ca6432da32d19dc6e615551d140c954a68d63b3f192' .
|
getlibrary 'https://github.com/glennrp/libpng/archive/refs/tags/v1.6.45.zip' 'libpng.zip' '1b3d94b2f1d137db1bf1842cb9f03df179772a517f7b86e26351742190632785' .
|
||||||
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.43 libpng
|
Move-Item libpng-1.6.45 libpng
|
||||||
|
|||||||
2
.github/scripts/install.sh
vendored
2
.github/scripts/install.sh
vendored
@@ -3,5 +3,5 @@
|
|||||||
install -d /usr/local/bin/ /usr/local/share/man/man1/ /usr/local/share/man/man5/ /usr/local/share/man/man7/
|
install -d /usr/local/bin/ /usr/local/share/man/man1/ /usr/local/share/man/man5/ /usr/local/share/man/man7/
|
||||||
install -s -m 755 rgbasm rgblink rgbfix rgbgfx /usr/local/bin/
|
install -s -m 755 rgbasm rgblink rgbfix rgbgfx /usr/local/bin/
|
||||||
install -m 644 rgbasm.1 rgblink.1 rgbfix.1 rgbgfx.1 /usr/local/share/man/man1/
|
install -m 644 rgbasm.1 rgblink.1 rgbfix.1 rgbgfx.1 /usr/local/share/man/man1/
|
||||||
install -m 644 rgbds.5 rgbasm.5 rgblink.5 /usr/local/share/man/man5/
|
install -m 644 rgbds.5 rgbasm.5 rgblink.5 rgbasm-old.5 /usr/local/share/man/man5/
|
||||||
install -m 644 rgbds.7 gbz80.7 /usr/local/share/man/man7/
|
install -m 644 rgbds.7 gbz80.7 /usr/local/share/man/man7/
|
||||||
|
|||||||
2
.github/scripts/mingw-w64-libpng-dev.sh
vendored
2
.github/scripts/mingw-w64-libpng-dev.sh
vendored
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
pngver=1.6.43
|
pngver=1.6.45
|
||||||
arch="$1"
|
arch="$1"
|
||||||
|
|
||||||
## Grab sources and check them
|
## Grab sources and check them
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
d6bd2a3f43f17020918a4c1bd81c1a78111b6f759af9c1d3c754f704a1bf0429 libpng-1.6.43-apng.patch.gz
|
f7caa3b55f003ce23d6a087b1c2a643262647bfd1a1b31afdc9b18eabf1bbc7e libpng-1.6.45-apng.patch.gz
|
||||||
6a5ca0652392a2d7c9db2ae5b40210843c0bbc081cbd410825ab00cc59f14a6c libpng-1.6.43.tar.xz
|
926485350139ffb51ef69760db35f78846c805fef3d59bfdcb2fba704663f370 libpng-1.6.45.tar.xz
|
||||||
|
|||||||
18
.github/workflows/analysis.yml
vendored
Normal file
18
.github/workflows/analysis.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
name: Static analysis
|
||||||
|
on:
|
||||||
|
- push
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analysis:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Install deps
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
./.github/scripts/install_deps.sh ubuntu-latest
|
||||||
|
- name: Static analysis
|
||||||
|
run: | # Silence warnings with too many false positives (https://stackoverflow.com/a/73913076)
|
||||||
|
make -kj CXX=g++-14 CXXFLAGS="-fanalyzer -fanalyzer-verbosity=0 -Wno-analyzer-use-of-uninitialized-value -DNDEBUG" Q=
|
||||||
51
.github/workflows/build-container.yml
vendored
Normal file
51
.github/workflows/build-container.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
name: Build container image
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish-docker-image:
|
||||||
|
if: github.repository_owner == 'gbdev'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build and push the master container image
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
run: |
|
||||||
|
COMMIT_HASH=$(git rev-parse --short HEAD)
|
||||||
|
sed -i "2i LABEL org.opencontainers.image.description=\"RGBDS container image, containing the git version master:$COMMIT_HASH\"" Dockerfile
|
||||||
|
docker build . --tag ghcr.io/gbdev/rgbds:master
|
||||||
|
docker push ghcr.io/gbdev/rgbds:master
|
||||||
|
|
||||||
|
- name: Build and push the version-tagged container image
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
run: |
|
||||||
|
TAG_NAME=${GITHUB_REF#refs/tags/}
|
||||||
|
sed -i "2i LABEL org.opencontainers.image.description=\"RGBDS container image for the release version $TAG_NAME\"" Dockerfile
|
||||||
|
docker build . --tag ghcr.io/gbdev/rgbds:$TAG_NAME
|
||||||
|
docker push ghcr.io/gbdev/rgbds:$TAG_NAME
|
||||||
|
|
||||||
|
- name: Delete untagged container images
|
||||||
|
if: github.repository_owner == 'gbdev'
|
||||||
|
uses: Chizkiyahu/delete-untagged-ghcr-action@v5
|
||||||
|
with:
|
||||||
|
# Requires a personal access token with delete:packages permissions
|
||||||
|
token: ${{ secrets.PAT_TOKEN }}
|
||||||
|
package_name: 'rgbds'
|
||||||
|
untagged_only: true
|
||||||
|
except_untagged_multiplatform: true
|
||||||
|
owner_type: 'org'
|
||||||
4
.github/workflows/checkdiff.yml
vendored
4
.github/workflows/checkdiff.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: "Code coverage checking"
|
name: Code coverage checking
|
||||||
on: pull_request
|
on: pull_request
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -11,7 +11,7 @@ jobs:
|
|||||||
cd rgbds
|
cd rgbds
|
||||||
git remote add upstream "${{ github.event.pull_request.base.repo.clone_url }}"
|
git remote add upstream "${{ github.event.pull_request.base.repo.clone_url }}"
|
||||||
git fetch upstream
|
git fetch upstream
|
||||||
- name: Checkdiff
|
- name: Check diff
|
||||||
working-directory: rgbds
|
working-directory: rgbds
|
||||||
run: |
|
run: |
|
||||||
make checkdiff "BASE_REF=${{ github.event.pull_request.base.sha }}" Q= | tee log
|
make checkdiff "BASE_REF=${{ github.event.pull_request.base.sha }}" Q= | tee log
|
||||||
|
|||||||
12
.github/workflows/checkformat.yml
vendored
Normal file
12
.github/workflows/checkformat.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
name: Code format checking
|
||||||
|
on: pull_request
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
checkformat:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Check format
|
||||||
|
run: |
|
||||||
|
./contrib/checkformat.bash
|
||||||
30
.github/workflows/create-release-artifacts.yml
vendored
30
.github/workflows/create-release-artifacts.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: "Create release artifacts"
|
name: Create release artifacts
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
@@ -24,30 +24,32 @@ jobs:
|
|||||||
run: | # Turn "vX.Y.Z" into "X.Y.Z"
|
run: | # Turn "vX.Y.Z" into "X.Y.Z"
|
||||||
VERSION="${{ github.ref_name }}"
|
VERSION="${{ github.ref_name }}"
|
||||||
echo "version=${VERSION#v}" >> $GITHUB_ENV
|
echo "version=${VERSION#v}" >> $GITHUB_ENV
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: .github/scripts/get_win_deps.ps1
|
run: .github/scripts/get_win_deps.ps1
|
||||||
- uses: actions/cache@v4
|
- name: Check libraries cache
|
||||||
id: cache
|
id: cache
|
||||||
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
zbuild
|
zbuild
|
||||||
pngbuild
|
pngbuild
|
||||||
key: ${{ matrix.arch }}-${{ hashFiles('zlib/**', 'libpng/**') }}
|
key: ${{ matrix.arch }}-${{ hashFiles('zlib/**', 'libpng/**') }}
|
||||||
- name: Build zlib
|
- name: Build zlib
|
||||||
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
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
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
|
||||||
- name: Install zlib
|
- name: Install zlib
|
||||||
run: |
|
run: |
|
||||||
cmake --install zbuild
|
cmake --install zbuild
|
||||||
- name: Build libpng
|
- name: Build libpng
|
||||||
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
cmake -S libpng -B pngbuild -A ${{ matrix.platform }} -Wno-dev -DCMAKE_INSTALL_PREFIX=install_dir -DPNG_SHARED=ON -DPNG_STATIC=OFF -DPNG_TESTS=OFF
|
cmake -S libpng -B pngbuild -A ${{ matrix.platform }} -Wno-dev -DCMAKE_INSTALL_PREFIX=install_dir -DPNG_SHARED=ON -DPNG_STATIC=OFF -DPNG_TESTS=OFF
|
||||||
cmake --build pngbuild --config Release -j
|
cmake --build pngbuild --config Release -j
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
|
||||||
- name: Install libpng
|
- name: Install libpng
|
||||||
run: |
|
run: |
|
||||||
cmake --install pngbuild
|
cmake --install pngbuild
|
||||||
@@ -74,7 +76,8 @@ jobs:
|
|||||||
run: | # Turn "refs/tags/vX.Y.Z" into "X.Y.Z"
|
run: | # Turn "refs/tags/vX.Y.Z" into "X.Y.Z"
|
||||||
VERSION="${{ github.ref_name }}"
|
VERSION="${{ github.ref_name }}"
|
||||||
echo "version=${VERSION#v}" >> $GITHUB_ENV
|
echo "version=${VERSION#v}" >> $GITHUB_ENV
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@@ -97,18 +100,19 @@ jobs:
|
|||||||
path: rgbds-${{ env.version }}-macos.zip
|
path: rgbds-${{ env.version }}-macos.zip
|
||||||
|
|
||||||
linux:
|
linux:
|
||||||
runs-on: ubuntu-20.04 # Oldest supported, for best glibc compatibility.
|
runs-on: ubuntu-22.04 # Oldest supported, for best glibc compatibility.
|
||||||
steps:
|
steps:
|
||||||
- name: Get version from tag
|
- name: Get version from tag
|
||||||
shell: bash
|
shell: bash
|
||||||
run: | # Turn "refs/tags/vX.Y.Z" into "X.Y.Z"
|
run: | # Turn "refs/tags/vX.Y.Z" into "X.Y.Z"
|
||||||
VERSION="${{ github.ref_name }}"
|
VERSION="${{ github.ref_name }}"
|
||||||
echo "version=${VERSION#v}" >> $GITHUB_ENV
|
echo "version=${VERSION#v}" >> $GITHUB_ENV
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
./.github/scripts/install_deps.sh ubuntu-20.04
|
./.github/scripts/install_deps.sh ubuntu-22.04
|
||||||
- name: Build binaries
|
- name: Build binaries
|
||||||
run: |
|
run: |
|
||||||
make -kj WARNFLAGS="-Wall -Wextra -pedantic -static" PKG_CONFIG="pkg-config --static" Q=
|
make -kj WARNFLAGS="-Wall -Wextra -pedantic -static" PKG_CONFIG="pkg-config --static" Q=
|
||||||
@@ -133,17 +137,19 @@ jobs:
|
|||||||
run: | # Turn "refs/tags/vX.Y.Z" into "X.Y.Z"
|
run: | # Turn "refs/tags/vX.Y.Z" into "X.Y.Z"
|
||||||
VERSION="${{ github.ref_name }}"
|
VERSION="${{ github.ref_name }}"
|
||||||
echo "version=${VERSION#v}" >> $GITHUB_ENV
|
echo "version=${VERSION#v}" >> $GITHUB_ENV
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
- name: Package sources
|
- name: Package sources
|
||||||
run: |
|
run: |
|
||||||
make dist Q=
|
make dist Q=
|
||||||
ls
|
ls
|
||||||
- uses: actions/download-artifact@v4
|
- name: Download Linux binaries
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
- name: Release
|
- name: Release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
body: |
|
body: |
|
||||||
Please ensure that the four packages below work properly.
|
Please ensure that the packages below work properly.
|
||||||
Once that's done, replace this text with the changelog, un-draft the release, and update the `release` branch.
|
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 gonna fail in the tag's regression testing! (Use `git push --delete origin <tag>` to delete it)
|
||||||
draft: true # Don't publish the release quite yet...
|
draft: true # Don't publish the release quite yet...
|
||||||
|
|||||||
4
.github/workflows/create-release-docs.yml
vendored
4
.github/workflows/create-release-docs.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: "Create release docs"
|
name: Create release docs
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types:
|
types:
|
||||||
@@ -7,7 +7,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: github.repository_owner == 'gbdev'
|
if: github.repository_owner == 'gbdev'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout rgbds@release
|
- name: Checkout rgbds@release
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|||||||
111
.github/workflows/testing.yml
vendored
111
.github/workflows/testing.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: "Regression testing"
|
name: Regression testing
|
||||||
on:
|
on:
|
||||||
- push
|
- push
|
||||||
- pull_request
|
- pull_request
|
||||||
@@ -7,7 +7,7 @@ jobs:
|
|||||||
unix:
|
unix:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-20.04, ubuntu-22.04, macos-14]
|
os: [ubuntu-22.04, macos-14]
|
||||||
cxx: [g++, clang++]
|
cxx: [g++, clang++]
|
||||||
buildsys: [make, cmake]
|
buildsys: [make, cmake]
|
||||||
exclude:
|
exclude:
|
||||||
@@ -17,7 +17,8 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@@ -32,9 +33,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} -DSANITIZERS=ON -DMORE_WARNINGS=ON
|
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} -DSANITIZERS=ON -DMORE_WARNINGS=ON
|
||||||
cmake --build build -j --verbose
|
cmake --build build -j --verbose
|
||||||
cp build/src/rgb{asm,link,fix,gfx} .
|
|
||||||
sudo cmake --install build --verbose
|
sudo cmake --install build --verbose
|
||||||
cmake --install build --verbose --component "Test support programs"
|
|
||||||
- name: Package binaries
|
- name: Package binaries
|
||||||
run: |
|
run: |
|
||||||
mkdir bins
|
mkdir bins
|
||||||
@@ -58,8 +57,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
||||||
key: ${{ matrix.os }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
key: ${{ matrix.os }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
||||||
- if: steps.test-deps-cache.outputs.cache-hit != 'true'
|
- name: Fetch test dependency repositories
|
||||||
name: Fetch test dependency repositories
|
if: steps.test-deps-cache.outputs.cache-hit != 'true'
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
test/fetch-test-deps.sh
|
test/fetch-test-deps.sh
|
||||||
@@ -75,7 +74,8 @@ jobs:
|
|||||||
macos-static:
|
macos-static:
|
||||||
runs-on: macos-14
|
runs-on: macos-14
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@@ -109,8 +109,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
||||||
key: ${{ matrix.os }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
key: ${{ matrix.os }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
||||||
- if: steps.test-deps-cache.outputs.cache-hit != 'true'
|
- name: Fetch test dependency repositories
|
||||||
name: Fetch test dependency repositories
|
if: steps.test-deps-cache.outputs.cache-hit != 'true'
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
test/fetch-test-deps.sh
|
test/fetch-test-deps.sh
|
||||||
@@ -138,30 +138,32 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: .github/scripts/get_win_deps.ps1
|
run: .github/scripts/get_win_deps.ps1
|
||||||
- uses: actions/cache@v4
|
- name: Check libraries cache
|
||||||
id: cache
|
id: cache
|
||||||
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
zbuild
|
zbuild
|
||||||
pngbuild
|
pngbuild
|
||||||
key: ${{ matrix.arch }}-${{ hashFiles('zlib/**', 'libpng/**') }}
|
key: ${{ matrix.arch }}-${{ hashFiles('zlib/**', 'libpng/**') }}
|
||||||
- name: Build zlib
|
- name: Build zlib
|
||||||
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
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
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
|
||||||
- name: Install zlib
|
- name: Install zlib
|
||||||
run: |
|
run: |
|
||||||
cmake --install zbuild
|
cmake --install zbuild
|
||||||
- name: Build libpng
|
- name: Build libpng
|
||||||
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
cmake -S libpng -B pngbuild -A ${{ matrix.platform }} -Wno-dev -DCMAKE_INSTALL_PREFIX=install_dir -DPNG_SHARED=ON -DPNG_STATIC=OFF -DPNG_TESTS=OFF
|
cmake -S libpng -B pngbuild -A ${{ matrix.platform }} -Wno-dev -DCMAKE_INSTALL_PREFIX=install_dir -DPNG_SHARED=ON -DPNG_STATIC=OFF -DPNG_TESTS=OFF
|
||||||
cmake --build pngbuild --config Release -j
|
cmake --build pngbuild --config Release -j
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
|
||||||
- name: Install libpng
|
- name: Install libpng
|
||||||
run: |
|
run: |
|
||||||
cmake --install pngbuild
|
cmake --install pngbuild
|
||||||
@@ -171,7 +173,6 @@ jobs:
|
|||||||
cmake -S . -B build -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DCMAKE_BUILD_TYPE=Release
|
cmake -S . -B build -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DCMAKE_BUILD_TYPE=Release
|
||||||
cmake --build build --config Release -j --verbose
|
cmake --build build --config Release -j --verbose
|
||||||
cmake --install build --verbose --prefix install_dir
|
cmake --install build --verbose --prefix install_dir
|
||||||
cmake --install build --verbose --component "Test support programs"
|
|
||||||
- name: Package binaries
|
- name: Package binaries
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@@ -196,8 +197,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
||||||
key: ${{ matrix.os }}-${{ matrix.bits }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
key: ${{ matrix.os }}-${{ matrix.bits }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
||||||
- if: steps.test-deps-cache.outputs.cache-hit != 'true'
|
- name: Fetch test dependency repositories
|
||||||
name: Fetch test dependency repositories
|
if: steps.test-deps-cache.outputs.cache-hit != 'true'
|
||||||
shell: bash
|
shell: bash
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
@@ -229,7 +230,8 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
DIST_DIR: win${{ matrix.bits }}
|
DIST_DIR: win${{ matrix.bits }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@@ -273,7 +275,8 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
- name: Retrieve binaries
|
- name: Retrieve binaries
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -303,8 +306,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
||||||
key: mingw-${{ matrix.bits }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
key: mingw-${{ matrix.bits }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
||||||
- if: steps.test-deps-cache.outputs.cache-hit != 'true'
|
- name: Fetch test dependency repositories
|
||||||
name: Fetch test dependency repositories
|
if: steps.test-deps-cache.outputs.cache-hit != 'true'
|
||||||
shell: bash
|
shell: bash
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
@@ -317,3 +320,69 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
test/run-tests.sh
|
test/run-tests.sh
|
||||||
|
|
||||||
|
cygwin:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
bits: [32, 64]
|
||||||
|
include:
|
||||||
|
- bits: 32
|
||||||
|
arch: x86
|
||||||
|
- bits: 64
|
||||||
|
arch: x86_64
|
||||||
|
fail-fast: false
|
||||||
|
runs-on: windows-2019
|
||||||
|
timeout-minutes: 30
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
fetch-tags: true
|
||||||
|
- name: Setup Cygwin
|
||||||
|
uses: cygwin/cygwin-install-action@v4
|
||||||
|
with:
|
||||||
|
platform: ${{ matrix.arch }}
|
||||||
|
packages: >-
|
||||||
|
bison
|
||||||
|
gcc-g++
|
||||||
|
git
|
||||||
|
libpng-devel
|
||||||
|
make
|
||||||
|
pkg-config
|
||||||
|
- name: Build & install using Make
|
||||||
|
shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -o igncr '{0}'
|
||||||
|
run: | # Cygwin does not support `make develop` sanitizers ASan or UBSan
|
||||||
|
make -kj Q=
|
||||||
|
make install -j Q=
|
||||||
|
- name: Run tests
|
||||||
|
shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -o igncr '{0}'
|
||||||
|
run: |
|
||||||
|
test/run-tests.sh --only-internal
|
||||||
|
|
||||||
|
freebsd:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 30
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
fetch-tags: true
|
||||||
|
- name: Build & test using CMake on FreeBSD
|
||||||
|
uses: vmactions/freebsd-vm@v1
|
||||||
|
with:
|
||||||
|
release: "15.0"
|
||||||
|
usesh: true
|
||||||
|
prepare: |
|
||||||
|
pkg install -y \
|
||||||
|
bash \
|
||||||
|
bison \
|
||||||
|
cmake \
|
||||||
|
git \
|
||||||
|
png
|
||||||
|
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 --build build -j4 --verbose
|
||||||
|
cmake --install build --verbose
|
||||||
|
cmake --build build --target test
|
||||||
|
|||||||
5
.github/workflows/update-master-docs.yml
vendored
5
.github/workflows/update-master-docs.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: "Update master docs"
|
name: Update master docs
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
@@ -9,6 +9,7 @@ on:
|
|||||||
- man/rgbds.7
|
- man/rgbds.7
|
||||||
- man/rgbasm.1
|
- man/rgbasm.1
|
||||||
- man/rgbasm.5
|
- man/rgbasm.5
|
||||||
|
- man/rgbasm-old.5
|
||||||
- man/rgblink.1
|
- man/rgblink.1
|
||||||
- man/rgblink.5
|
- man/rgblink.5
|
||||||
- man/rgbfix.1
|
- man/rgbfix.1
|
||||||
@@ -17,7 +18,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: github.repository_owner == 'gbdev'
|
if: github.repository_owner == 'gbdev'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout rgbds@master
|
- name: Checkout rgbds@master
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -13,4 +13,6 @@
|
|||||||
CMakeCache.txt
|
CMakeCache.txt
|
||||||
CMakeFiles/
|
CMakeFiles/
|
||||||
cmake_install.cmake
|
cmake_install.cmake
|
||||||
|
build/
|
||||||
|
*.dSYM/
|
||||||
callgrind.out.*
|
callgrind.out.*
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
# 3.9 required for LTO checks
|
# 3.9 required for LTO checks
|
||||||
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
|
# 3.17 optional for CMAKE_CTEST_ARGUMENTS
|
||||||
|
cmake_minimum_required(VERSION 3.9..3.17 FATAL_ERROR)
|
||||||
|
|
||||||
project(rgbds
|
project(rgbds
|
||||||
LANGUAGES CXX)
|
LANGUAGES CXX)
|
||||||
|
|
||||||
|
include(CTest)
|
||||||
|
|
||||||
# get real path of source and binary directories
|
# get real path of source and binary directories
|
||||||
get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH)
|
get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH)
|
||||||
get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH)
|
get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH)
|
||||||
@@ -41,27 +44,22 @@ else()
|
|||||||
# does not recognize this yet.
|
# does not recognize this yet.
|
||||||
add_compile_options(-Wno-gnu-zero-variadic-macro-arguments)
|
add_compile_options(-Wno-gnu-zero-variadic-macro-arguments)
|
||||||
endif()
|
endif()
|
||||||
add_definitions(-D_POSIX_C_SOURCE=200809L)
|
|
||||||
if(SANITIZERS)
|
if(SANITIZERS)
|
||||||
set(SAN_FLAGS -fsanitize=shift -fsanitize=integer-divide-by-zero
|
set(SAN_FLAGS -fsanitize=address -fsanitize=undefined
|
||||||
-fsanitize=unreachable -fsanitize=vla-bound
|
-fsanitize=float-divide-by-zero)
|
||||||
-fsanitize=signed-integer-overflow -fsanitize=bounds
|
|
||||||
-fsanitize=object-size -fsanitize=bool -fsanitize=enum
|
|
||||||
-fsanitize=alignment -fsanitize=null -fsanitize=address)
|
|
||||||
add_compile_options(${SAN_FLAGS})
|
add_compile_options(${SAN_FLAGS})
|
||||||
add_link_options(${SAN_FLAGS})
|
add_link_options(${SAN_FLAGS})
|
||||||
add_definitions(-D_GLIBCXX_ASSERTIONS)
|
add_definitions(-D_GLIBCXX_ASSERTIONS)
|
||||||
# A non-zero optimization level is desired in debug mode, but allow overriding it nonetheless
|
# A non-zero optimization level is desired in debug mode, but allow overriding it nonetheless
|
||||||
# TODO: this overrides anything previously set... that's a bit sloppy!
|
set(CMAKE_CXX_FLAGS_DEBUG "-g -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls ${CMAKE_CXX_FLAGS_DEBUG}"
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "-g -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls" CACHE STRING "" FORCE)
|
CACHE STRING "" FORCE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(MORE_WARNINGS)
|
if(MORE_WARNINGS)
|
||||||
add_compile_options(-Werror -Wextra
|
add_compile_options(-Werror -Wextra
|
||||||
-Walloc-zero -Wcast-align -Wcast-qual -Wduplicated-branches -Wduplicated-cond
|
-Walloc-zero -Wcast-align -Wcast-qual -Wduplicated-branches -Wduplicated-cond
|
||||||
-Wfloat-equal -Wlogical-op -Wnull-dereference -Wshift-overflow=2
|
-Wfloat-equal -Wlogical-op -Wnull-dereference -Wold-style-cast -Wshift-overflow=2
|
||||||
-Wstringop-overflow=4 -Wundef -Wuninitialized -Wunused
|
-Wstringop-overflow=4 -Wtrampolines -Wundef -Wuninitialized -Wunused -Wshadow
|
||||||
-Wshadow # TODO: -Wshadow=compatible-local?
|
|
||||||
-Wformat=2 -Wformat-overflow=2 -Wformat-truncation=1
|
-Wformat=2 -Wformat-overflow=2 -Wformat-truncation=1
|
||||||
-Wno-format-nonliteral -Wno-strict-overflow
|
-Wno-format-nonliteral -Wno-strict-overflow
|
||||||
-Wno-unused-but-set-variable # bison's `yynerrs_` is incremented but unused
|
-Wno-unused-but-set-variable # bison's `yynerrs_` is incremented but unused
|
||||||
@@ -76,7 +74,7 @@ endif()
|
|||||||
|
|
||||||
find_program(GIT git)
|
find_program(GIT git)
|
||||||
if(GIT)
|
if(GIT)
|
||||||
execute_process(COMMAND ${GIT} --git-dir=.git describe --tags --dirty --always
|
execute_process(COMMAND ${GIT} --git-dir=.git -c safe.directory='*' describe --tags --dirty --always
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
OUTPUT_VARIABLE GIT_REV OUTPUT_STRIP_TRAILING_WHITESPACE
|
OUTPUT_VARIABLE GIT_REV OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
ERROR_QUIET)
|
ERROR_QUIET)
|
||||||
@@ -88,6 +86,8 @@ endif(GIT)
|
|||||||
find_package(PkgConfig)
|
find_package(PkgConfig)
|
||||||
if(MSVC OR NOT PKG_CONFIG_FOUND)
|
if(MSVC OR NOT PKG_CONFIG_FOUND)
|
||||||
# fallback to find_package
|
# fallback to find_package
|
||||||
|
# cmake's FindPNG is very fragile; it breaks when multiple versions are installed
|
||||||
|
# this is most evident on macOS but can occur on Linux too
|
||||||
find_package(PNG REQUIRED)
|
find_package(PNG REQUIRED)
|
||||||
else()
|
else()
|
||||||
pkg_check_modules(LIBPNG REQUIRED libpng)
|
pkg_check_modules(LIBPNG REQUIRED libpng)
|
||||||
@@ -99,6 +99,7 @@ set(CMAKE_CXX_STANDARD 20)
|
|||||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
set(CMAKE_CTEST_ARGUMENTS "--verbose")
|
||||||
add_subdirectory(test)
|
add_subdirectory(test)
|
||||||
|
|
||||||
# By default, build in Release mode; Debug mode must be explicitly requested
|
# By default, build in Release mode; Debug mode must be explicitly requested
|
||||||
@@ -121,6 +122,7 @@ set(man1 "man/rgbasm.1"
|
|||||||
"man/rgbgfx.1"
|
"man/rgbgfx.1"
|
||||||
"man/rgblink.1")
|
"man/rgblink.1")
|
||||||
set(man5 "man/rgbasm.5"
|
set(man5 "man/rgbasm.5"
|
||||||
|
"man/rgbasm-old.5"
|
||||||
"man/rgblink.5"
|
"man/rgblink.5"
|
||||||
"man/rgbds.5")
|
"man/rgbds.5")
|
||||||
set(man7 "man/gbz80.7"
|
set(man7 "man/gbz80.7"
|
||||||
|
|||||||
@@ -174,3 +174,17 @@ Each one is a binary RNG file which is passed to the `rgbgfx_test` program.
|
|||||||
```sh
|
```sh
|
||||||
test_downstream <owner> <repo> <makefile target> <build file> <sha1 hash of build file>
|
test_downstream <owner> <repo> <makefile target> <build file> <sha1 hash of build file>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Container images
|
||||||
|
|
||||||
|
The CI will [take care](https://github.com/gbdev/rgbds/blob/master/.github/workflows/build-container.yml) of updating the [rgbds container](https://github.com/gbdev/rgbds/pkgs/container/rgbds) image tagged `master`.
|
||||||
|
|
||||||
|
When a git tag is pushed, the image is also tagged with that tag.
|
||||||
|
|
||||||
|
The image can be built locally and pushed to the GitHub container registry by manually running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# e.g. to build and tag as 'master'
|
||||||
|
docker build . --tag ghcr.io/gbdev/rgbds:master
|
||||||
|
docker push ghcr.io/gbdev/rgbds:master
|
||||||
|
```
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
FROM debian:11-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.0-rc1
|
ARG version=0.9.2
|
||||||
WORKDIR /rgbds
|
WORKDIR /rgbds
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
@@ -8,7 +8,7 @@ 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
|
||||||
|
|
||||||
RUN ./.github/scripts/install_deps.sh ubuntu-20.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
|
RUN tar caf rgbds-${version}-linux-x86_64.tar.xz --transform='s#.*/##' rgbasm rgblink rgbfix rgbgfx man/* .github/scripts/install.sh
|
||||||
|
|||||||
61
Makefile
61
Makefile
@@ -7,46 +7,43 @@
|
|||||||
|
|
||||||
# User-defined variables
|
# User-defined variables
|
||||||
|
|
||||||
Q := @
|
Q := @
|
||||||
PREFIX := /usr/local
|
PREFIX := /usr/local
|
||||||
bindir := ${PREFIX}/bin
|
bindir := ${PREFIX}/bin
|
||||||
mandir := ${PREFIX}/share/man
|
mandir := ${PREFIX}/share/man
|
||||||
SUFFIX :=
|
SUFFIX :=
|
||||||
STRIP := -s
|
STRIP := -s
|
||||||
BINMODE := 755
|
BINMODE := 755
|
||||||
MANMODE := 644
|
MANMODE := 644
|
||||||
|
|
||||||
# Other variables
|
# Other variables
|
||||||
|
|
||||||
PKG_CONFIG := pkg-config
|
PKG_CONFIG := pkg-config
|
||||||
PNGCFLAGS := `${PKG_CONFIG} --cflags libpng`
|
PNGCFLAGS := `${PKG_CONFIG} --cflags libpng`
|
||||||
PNGLDFLAGS := `${PKG_CONFIG} --libs-only-L libpng`
|
PNGLDFLAGS := `${PKG_CONFIG} --libs-only-L libpng`
|
||||||
PNGLDLIBS := `${PKG_CONFIG} --libs-only-l libpng`
|
PNGLDLIBS := `${PKG_CONFIG} --libs-only-l libpng`
|
||||||
|
|
||||||
# Note: if this comes up empty, `version.cpp` will automatically fall back to last release number
|
# Note: if this comes up empty, `version.cpp` will automatically fall back to last release number
|
||||||
VERSION_STRING := `git --git-dir=.git describe --tags --dirty --always 2>/dev/null`
|
VERSION_STRING := `git --git-dir=.git -c safe.directory='*' describe --tags --dirty --always 2>/dev/null`
|
||||||
|
|
||||||
WARNFLAGS := -Wall -pedantic -Wno-unknown-warning-option \
|
WARNFLAGS := -Wall -pedantic -Wno-unknown-warning-option -Wno-gnu-zero-variadic-macro-arguments
|
||||||
-Wno-gnu-zero-variadic-macro-arguments
|
|
||||||
|
|
||||||
# Overridable CXXFLAGS
|
# Overridable CXXFLAGS
|
||||||
CXXFLAGS ?= -O3 -flto -DNDEBUG
|
CXXFLAGS ?= -O3 -flto -DNDEBUG
|
||||||
# Non-overridable CXXFLAGS
|
# Non-overridable CXXFLAGS
|
||||||
REALCXXFLAGS := ${CXXFLAGS} ${WARNFLAGS} -std=c++2a -I include \
|
REALCXXFLAGS := ${CXXFLAGS} ${WARNFLAGS} -std=c++2a -I include -fno-exceptions -fno-rtti
|
||||||
-D_POSIX_C_SOURCE=200809L -fno-exceptions -fno-rtti
|
|
||||||
# Overridable LDFLAGS
|
# Overridable LDFLAGS
|
||||||
LDFLAGS ?=
|
LDFLAGS ?=
|
||||||
# Non-overridable LDFLAGS
|
# Non-overridable LDFLAGS
|
||||||
REALLDFLAGS := ${LDFLAGS} ${WARNFLAGS} \
|
REALLDFLAGS := ${LDFLAGS} ${WARNFLAGS} -DBUILD_VERSION_STRING=\"${VERSION_STRING}\"
|
||||||
-DBUILD_VERSION_STRING=\"${VERSION_STRING}\"
|
|
||||||
|
|
||||||
# Wrapper around bison that passes flags depending on what the version supports
|
# Wrapper around bison that passes flags depending on what the version supports
|
||||||
BISON := src/bison.sh
|
BISON := src/bison.sh
|
||||||
|
|
||||||
RM := rm -rf
|
RM := rm -rf
|
||||||
|
|
||||||
# Used for checking pull requests
|
# Used for checking pull requests
|
||||||
BASE_REF := origin/master
|
BASE_REF := origin/master
|
||||||
|
|
||||||
# Rules to build the RGBDS binaries
|
# Rules to build the RGBDS binaries
|
||||||
|
|
||||||
@@ -188,7 +185,7 @@ install: all
|
|||||||
$Qinstall ${STRIP} -m ${BINMODE} rgbfix ${DESTDIR}${bindir}/rgbfix${SUFFIX}
|
$Qinstall ${STRIP} -m ${BINMODE} rgbfix ${DESTDIR}${bindir}/rgbfix${SUFFIX}
|
||||||
$Qinstall ${STRIP} -m ${BINMODE} rgbgfx ${DESTDIR}${bindir}/rgbgfx${SUFFIX}
|
$Qinstall ${STRIP} -m ${BINMODE} rgbgfx ${DESTDIR}${bindir}/rgbgfx${SUFFIX}
|
||||||
$Qinstall -m ${MANMODE} man/rgbasm.1 man/rgblink.1 man/rgbfix.1 man/rgbgfx.1 ${DESTDIR}${mandir}/man1/
|
$Qinstall -m ${MANMODE} man/rgbasm.1 man/rgblink.1 man/rgbfix.1 man/rgbgfx.1 ${DESTDIR}${mandir}/man1/
|
||||||
$Qinstall -m ${MANMODE} man/rgbds.5 man/rgbasm.5 man/rgblink.5 ${DESTDIR}${mandir}/man5/
|
$Qinstall -m ${MANMODE} man/rgbds.5 man/rgbasm.5 man/rgbasm-old.5 man/rgblink.5 ${DESTDIR}${mandir}/man5/
|
||||||
$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.
|
||||||
@@ -204,17 +201,13 @@ checkdiff:
|
|||||||
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 \
|
||||||
-Wfloat-equal -Wlogical-op -Wnull-dereference -Wshift-overflow=2 \
|
-Wfloat-equal -Wlogical-op -Wnull-dereference -Wold-style-cast -Wshift-overflow=2 \
|
||||||
-Wstringop-overflow=4 -Wundef -Wuninitialized -Wunused -Wshadow \
|
-Wstringop-overflow=4 -Wtrampolines -Wundef -Wuninitialized -Wunused -Wshadow \
|
||||||
-Wformat=2 -Wformat-overflow=2 -Wformat-truncation=1 \
|
-Wformat=2 -Wformat-overflow=2 -Wformat-truncation=1 \
|
||||||
-Wno-format-nonliteral -Wno-strict-overflow -Wno-unused-but-set-variable \
|
-Wno-format-nonliteral -Wno-strict-overflow -Wno-unused-but-set-variable \
|
||||||
-Wno-type-limits -Wno-tautological-constant-out-of-range-compare -Wvla \
|
-Wno-type-limits -Wno-tautological-constant-out-of-range-compare -Wvla \
|
||||||
-D_GLIBCXX_ASSERTIONS \
|
-D_GLIBCXX_ASSERTIONS -fsanitize=address -fsanitize=undefined \
|
||||||
-fsanitize=shift -fsanitize=integer-divide-by-zero \
|
-fsanitize=float-divide-by-zero" \
|
||||||
-fsanitize=unreachable -fsanitize=vla-bound \
|
|
||||||
-fsanitize=signed-integer-overflow -fsanitize=bounds \
|
|
||||||
-fsanitize=object-size -fsanitize=bool -fsanitize=enum \
|
|
||||||
-fsanitize=alignment -fsanitize=null -fsanitize=address" \
|
|
||||||
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.
|
# This target is used during development in order to more easily debug with gdb.
|
||||||
@@ -272,4 +265,4 @@ wine-shim:
|
|||||||
|
|
||||||
dist:
|
dist:
|
||||||
$Qgit ls-files | sed s~^~$${PWD##*/}/~ \
|
$Qgit ls-files | sed s~^~$${PWD##*/}/~ \
|
||||||
| tar -czf rgbds-`git describe --tags | cut -c 2-`.tar.gz -C .. -T -
|
| tar -czf rgbds-`git -c safe.directory='*' describe --tags | cut -c 2-`.tar.gz -C .. -T -
|
||||||
|
|||||||
@@ -137,9 +137,12 @@ The RGBDS source code file structure is as follows:
|
|||||||
this version as [rgbds-linux](https://github.com/vegard/rgbds-linux).
|
this version as [rgbds-linux](https://github.com/vegard/rgbds-linux).
|
||||||
- 2010-01-12: Anthony J. Bentley [forks](https://github.com/bentley) Nossum's
|
- 2010-01-12: Anthony J. Bentley [forks](https://github.com/bentley) Nossum's
|
||||||
repository. The fork becomes the reference implementation of RGBDS.
|
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),
|
- 2015-01-18: stag019 begins implementing [RGBGFX](https://github.com/stag019/rgbgfx),
|
||||||
a PNG‐to‐Game Boy graphics converter, for eventual integration into RGBDS.
|
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)
|
- 2016-09-05: RGBGFX is
|
||||||
|
[integrated](https://github.com/gbdev/rgbds/commit/c3c31138ddbd8680d4e67957e387f2816798a71b)
|
||||||
into Bentley's repository.
|
into Bentley's repository.
|
||||||
- 2017-02-23: Bentley's repository is moved to the [rednex](https://github.com/rednex)
|
- 2017-02-23: Bentley's repository is moved to the [rednex](https://github.com/rednex)
|
||||||
organization.
|
organization.
|
||||||
|
|||||||
39
RELEASE.md
39
RELEASE.md
@@ -3,13 +3,16 @@
|
|||||||
This describes for the maintainers of RGBDS how to publish a new release on
|
This describes for the maintainers of RGBDS how to publish a new release on
|
||||||
GitHub.
|
GitHub.
|
||||||
|
|
||||||
1. Update, commit, and push [include/version.hpp](include/version.hpp) with
|
1. Update the following files, then commit and push.
|
||||||
values for `PACKAGE_VERSION_MAJOR`, `PACKAGE_VERSION_MINOR`,
|
You can use <code>git commit -m "Release <i><version></i>"</code> and `git push origin master`.
|
||||||
`PACKAGE_VERSION_PATCH`, and `PACKAGE_VERSION_RC`, as well as
|
|
||||||
[Dockerfile](Dockerfile) with a value for `ARG version`. Only define
|
- [include/version.hpp](include/version.hpp): set appropriate values for `PACKAGE_VERSION_MAJOR`,
|
||||||
`PACKAGE_VERSION_RC` if you are publishing a release candidate! You can
|
`PACKAGE_VERSION_MINOR`, `PACKAGE_VERSION_PATCH`, and `PACKAGE_VERSION_RC`.
|
||||||
use <code>git commit -m "Release <i><version></i>"</code> and
|
**Only** define `PACKAGE_VERSION_RC` if you are publishing a release candidate!
|
||||||
`git push origin master`.
|
- [Dockerfile](Dockerfile): update `ARG version`.
|
||||||
|
- [test/fetch-test-deps.sh](test/fetch-test-deps.sh): update test dependency commits
|
||||||
|
(preferably, use the latest available).
|
||||||
|
- [man/\*](man/): update dates and authors.
|
||||||
|
|
||||||
2. Create a Git tag formatted as <code>v<i><MAJOR></i>.<i><MINOR></i>.<i><PATCH></i></code>,
|
2. Create a Git tag formatted as <code>v<i><MAJOR></i>.<i><MINOR></i>.<i><PATCH></i></code>,
|
||||||
or <code>v<i><MAJOR></i>.<i><MINOR></i>.<i><PATCH></i>-rc<i><RC></i></code>
|
or <code>v<i><MAJOR></i>.<i><MINOR></i>.<i><PATCH></i>-rc<i><RC></i></code>
|
||||||
@@ -38,7 +41,9 @@ GitHub.
|
|||||||
4. GitHub Actions will run the [create-release-docs.yml](.github/workflows/create-release-docs.yml)
|
4. GitHub Actions will run the [create-release-docs.yml](.github/workflows/create-release-docs.yml)
|
||||||
workflow to add the release documentation to [rgbds-www](https://github.com/gbdev/rgbds-www).
|
workflow to add the release documentation to [rgbds-www](https://github.com/gbdev/rgbds-www).
|
||||||
|
|
||||||
This is not done automatically for prereleases; if you want to do this manually,
|
This is not done automatically for prereleases, since we do not normally publish documentation
|
||||||
|
for them. If you want to manually publish prerelease documentation, such as for an April Fools
|
||||||
|
joke prerelease,
|
||||||
|
|
||||||
1. Clone [rgbds-www](https://github.com/gbdev/rgbds-www). You can use
|
1. Clone [rgbds-www](https://github.com/gbdev/rgbds-www). You can use
|
||||||
`git clone https://github.com/gbdev/rgbds-www.git`.
|
`git clone https://github.com/gbdev/rgbds-www.git`.
|
||||||
@@ -52,8 +57,8 @@ GitHub.
|
|||||||
|
|
||||||
If you do not have `groff` installed, you can change
|
If you do not have `groff` installed, you can change
|
||||||
`groff -Tpdf -mdoc -wall` to `mandoc -Tpdf -I os=Linux` in
|
`groff -Tpdf -mdoc -wall` to `mandoc -Tpdf -I os=Linux` in
|
||||||
[.github/actions/get-pages.sh](.github/actions/get-pages.sh) and it
|
[maintainer/man_to_html.sh](https://github.com/gbdev/rgbds-www/blob/master/maintainer/man_to_html.sh)
|
||||||
will suffice.
|
and it will suffice.
|
||||||
|
|
||||||
4. Commit and push the documentation. You can use <code>git commit -m
|
4. Commit and push the documentation. You can use <code>git commit -m
|
||||||
"Create RGBDS <i><tag></i> documentation"</code> and `git push origin master`
|
"Create RGBDS <i><tag></i> documentation"</code> and `git push origin master`
|
||||||
@@ -64,3 +69,17 @@ 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 release`.
|
||||||
|
|
||||||
|
8. Update the following related projects.
|
||||||
|
|
||||||
|
1. [rgbds-www](https://github.com/gbdev/rgbds-www): update
|
||||||
|
[src/pages/versions.mdx](https://github.com/gbdev/rgbds-www/blob/master/src/pages/versions.mdx)
|
||||||
|
to list the new release.
|
||||||
|
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)
|
||||||
|
if necessary) to use the new release, and
|
||||||
|
[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):
|
||||||
|
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.
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ _rgbasm_completions() {
|
|||||||
# 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"
|
[V]="version:normal"
|
||||||
|
[h]="help:normal"
|
||||||
[E]="export-all:normal"
|
[E]="export-all:normal"
|
||||||
[v]="verbose:normal"
|
[v]="verbose:normal"
|
||||||
[w]=":normal"
|
[w]=":normal"
|
||||||
@@ -192,6 +193,8 @@ _rgbasm_completions() {
|
|||||||
shift-amount
|
shift-amount
|
||||||
truncation
|
truncation
|
||||||
unmapped-char
|
unmapped-char
|
||||||
|
unmatched-directive
|
||||||
|
unterminated-load
|
||||||
user
|
user
|
||||||
all
|
all
|
||||||
extra
|
extra
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ _rgbfix_completions() {
|
|||||||
# 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"
|
[V]="version:normal"
|
||||||
|
[h]="help:normal"
|
||||||
[j]="non-japanese:normal"
|
[j]="non-japanese:normal"
|
||||||
[s]="sgb-compatible:normal"
|
[s]="sgb-compatible:normal"
|
||||||
[v]="validate:normal"
|
[v]="validate:normal"
|
||||||
@@ -20,6 +21,7 @@ _rgbfix_completions() {
|
|||||||
[l]="old-licensee:unk"
|
[l]="old-licensee:unk"
|
||||||
[m]="mbc-type:mbc"
|
[m]="mbc-type:mbc"
|
||||||
[n]="rom-version:unk"
|
[n]="rom-version:unk"
|
||||||
|
[o]="output:glob-*.gb *.gbc *.sgb"
|
||||||
[p]="pad-value:unk"
|
[p]="pad-value:unk"
|
||||||
[r]="ram-size:unk"
|
[r]="ram-size:unk"
|
||||||
[t]="title:unk"
|
[t]="title:unk"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ _rgbgfx_completions() {
|
|||||||
# 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"
|
[V]="version:normal"
|
||||||
|
[h]="help:normal"
|
||||||
[C]="color-curve:normal"
|
[C]="color-curve:normal"
|
||||||
[m]="mirror-tiles:normal"
|
[m]="mirror-tiles:normal"
|
||||||
[O]="group-outputs:normal"
|
[O]="group-outputs:normal"
|
||||||
@@ -18,6 +19,7 @@ _rgbgfx_completions() {
|
|||||||
[Z]="columns:normal"
|
[Z]="columns:normal"
|
||||||
[a]="attr-map:glob-*.attrmap"
|
[a]="attr-map:glob-*.attrmap"
|
||||||
[A]="auto-attr-map:normal"
|
[A]="auto-attr-map:normal"
|
||||||
|
[B]="background-color:unk"
|
||||||
[b]="base-tiles:unk"
|
[b]="base-tiles:unk"
|
||||||
[c]="colors:unk"
|
[c]="colors:unk"
|
||||||
[d]="depth:unk"
|
[d]="depth:unk"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ _rgblink_completions() {
|
|||||||
# 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"
|
[V]="version:normal"
|
||||||
|
[h]="help:normal"
|
||||||
[d]="dmg:normal"
|
[d]="dmg:normal"
|
||||||
[t]="tiny:normal"
|
[t]="tiny:normal"
|
||||||
[v]="verbose:normal"
|
[v]="verbose:normal"
|
||||||
|
|||||||
15
contrib/checkformat.bash
Executable file
15
contrib/checkformat.bash
Executable file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
clang-format --version
|
||||||
|
|
||||||
|
find . -type f \( -iname '*.hpp' -o -iname '*.cpp' \) -exec clang-format -i {} +
|
||||||
|
|
||||||
|
if ! git diff-index --quiet HEAD --; then
|
||||||
|
echo 'Unformatted files:'
|
||||||
|
git diff-index --name-only HEAD --
|
||||||
|
echo
|
||||||
|
git diff HEAD --
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
@@ -32,14 +32,15 @@ diff <(xxd "$1") <(xxd "$2") | while read -r LINE; do
|
|||||||
# Ignore comment lines, only pick matching bank
|
# Ignore comment lines, only pick matching bank
|
||||||
# (The bank regex ignores comments already, make `cut` and `tr` process less lines)
|
# (The bank regex ignores comments already, make `cut` and `tr` process less lines)
|
||||||
grep -Ei "$(printf "^%02x:" $BANK)" "$SYMFILE" |
|
grep -Ei "$(printf "^%02x:" $BANK)" "$SYMFILE" |
|
||||||
|
sed "s/$(printf "^%02x:" $BANK)/0x/g" |
|
||||||
cut -d ';' -f 1 |
|
cut -d ';' -f 1 |
|
||||||
tr -d "\r" |
|
tr -d "\r" |
|
||||||
|
sort -g |
|
||||||
while read -r SYMADDR SYM; do
|
while read -r SYMADDR SYM; do
|
||||||
SYMADDR=$((0x${SYMADDR#*:}))
|
SYMADDR=$(($SYMADDR))
|
||||||
if [[ $SYMADDR -le $ADDR ]]; then
|
if [[ $SYMADDR -le $ADDR ]]; then
|
||||||
printf " (%s+0x%x)\n" "$SYM" $((ADDR - SYMADDR))
|
printf " (%s+0x%x)\n" "$SYM" $((ADDR - SYMADDR))
|
||||||
fi
|
fi
|
||||||
# TODO: assumes sorted sym files
|
|
||||||
done | tail -n 1
|
done | tail -n 1
|
||||||
fi)
|
fi)
|
||||||
printf "%02x:%04x %s\n" $BANK $ADDR "$EXTRA"
|
printf "%02x:%04x %s\n" $BANK $ADDR "$EXTRA"
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ _rgbasm_warnings() {
|
|||||||
'shift-amount:Warn when a shift'\''s operand it negative or \> 32'
|
'shift-amount:Warn when a shift'\''s operand it 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'
|
||||||
|
'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 `no-` and `error=` somehow?
|
||||||
@@ -34,8 +36,9 @@ _rgbasm_warnings() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
local args=(
|
local args=(
|
||||||
# Arguments are listed here in the same order as in the manual, except for the version
|
# Arguments are listed here in the same order as in the manual, except for the version and help
|
||||||
'(- : * options)'{-V,--version}'[Print version number]'
|
'(- : * options)'{-V,--version}'[Print version number 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}'[Print additional messages regarding progression]'
|
||||||
|
|||||||
@@ -35,8 +35,9 @@ _mbc_names() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
local args=(
|
local args=(
|
||||||
# Arguments are listed here in the same order as in the manual, except for the version
|
# Arguments are listed here in the same order as in the manual, except for the version and help
|
||||||
'(- : * options)'{-V,--version}'[Print version number]'
|
'(- : * options)'{-V,--version}'[Print version number and exit]'
|
||||||
|
'(- : * options)'{-h,--help}'[Print help text and exit]'
|
||||||
|
|
||||||
'(-C --color-only -c --color-compatible)'{-C,--color-only}'[Mark ROM as GBC-only]'
|
'(-C --color-only -c --color-compatible)'{-C,--color-only}'[Mark ROM as GBC-only]'
|
||||||
'(-C --color-only -c --color-compatible)'{-c,--color-compatible}'[Mark ROM as GBC-compatible]'
|
'(-C --color-only -c --color-compatible)'{-c,--color-compatible}'[Mark ROM as GBC-compatible]'
|
||||||
@@ -52,6 +53,7 @@ local args=(
|
|||||||
'(-L --logo)'{-L,--logo}'+[Set custom logo]:1bpp image:'
|
'(-L --logo)'{-L,--logo}'+[Set custom logo]:1bpp image:'
|
||||||
'(-m --mbc-type)'{-m,--mbc-type}"+[Set MBC flags]:mbc name:_mbc_names"
|
'(-m --mbc-type)'{-m,--mbc-type}"+[Set MBC flags]:mbc name:_mbc_names"
|
||||||
'(-n --rom-version)'{-n,--rom-version}'+[Set ROM version]:rom version byte:'
|
'(-n --rom-version)'{-n,--rom-version}'+[Set ROM version]:rom version byte:'
|
||||||
|
'(-o --output)'{-o,--output}"+[Output file]:output file:_files -g '*.{gb,sgb,gbc}'"
|
||||||
'(-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:'
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ _depths() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
local args=(
|
local args=(
|
||||||
# Arguments are listed here in the same order as in the manual, except for the version
|
# Arguments are listed here in the same order as in the manual, except for the version and help
|
||||||
'(- : * options)'{-V,--version}'[Print version number]'
|
'(- : * options)'{-V,--version}'[Print version number and exit]'
|
||||||
|
'(- : * options)'{-h,--help}'[Print help text and exit]'
|
||||||
|
|
||||||
'(-a --attr-map -A --auto-attr-map)'{-A,--auto-attr-map}'[Shortcut for -a <file>.attrmap]'
|
'(-a --attr-map -A --auto-attr-map)'{-A,--auto-attr-map}'[Shortcut for -a <file>.attrmap]'
|
||||||
'(-C --color-curve)'{-C,--color-curve}'[Generate palettes using GBC color curve]'
|
'(-C --color-curve)'{-C,--color-curve}'[Generate palettes using GBC color curve]'
|
||||||
@@ -27,6 +28,7 @@ local args=(
|
|||||||
'(-Z --columns)'{-Z,--columns}'[Read the image in column-major order]'
|
'(-Z --columns)'{-Z,--columns}'[Read the image in column-major order]'
|
||||||
|
|
||||||
'(-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 --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:'
|
||||||
'(-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'
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
#compdef rgblink
|
#compdef rgblink
|
||||||
|
|
||||||
local args=(
|
local args=(
|
||||||
# Arguments are listed here in the same order as in the manual, except for the version
|
# Arguments are listed here in the same order as in the manual, except for the version and help
|
||||||
'(- : * options)'{-V,--version}'[Print version number]'
|
'(- : * options)'{-V,--version}'[Print version number and exit]'
|
||||||
|
'(- : * options)'{-h,--help}'[Print help text and exit]'
|
||||||
|
|
||||||
'(-d --dmg)'{-d,--dmg}'[Enable DMG mode (-w + no VRAM banking)]'
|
'(-d --dmg)'{-d,--dmg}'[Enable DMG mode (-w + no VRAM banking)]'
|
||||||
'(-t --tiny)'{-t,--tiny}'[Enable tiny mode, disabling ROM banking]'
|
'(-t --tiny)'{-t,--tiny}'[Enable tiny mode, disabling ROM banking]'
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_ASM_CHARMAP_HPP
|
#ifndef RGBDS_ASM_CHARMAP_HPP
|
||||||
#define RGBDS_ASM_CHARMAP_HPP
|
#define RGBDS_ASM_CHARMAP_HPP
|
||||||
@@ -18,9 +18,12 @@ void charmap_New(std::string const &name, std::string const *baseName);
|
|||||||
void charmap_Set(std::string const &name);
|
void charmap_Set(std::string const &name);
|
||||||
void charmap_Push();
|
void charmap_Push();
|
||||||
void charmap_Pop();
|
void charmap_Pop();
|
||||||
|
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 &input);
|
bool charmap_HasChar(std::string const &mapping);
|
||||||
|
size_t charmap_CharSize(std::string const &mapping);
|
||||||
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);
|
||||||
|
|
||||||
#endif // RGBDS_ASM_CHARMAP_HPP
|
#endif // RGBDS_ASM_CHARMAP_HPP
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_ASM_FIXPOINT_HPP
|
#ifndef RGBDS_ASM_FIXPOINT_HPP
|
||||||
#define RGBDS_ASM_FIXPOINT_HPP
|
#define RGBDS_ASM_FIXPOINT_HPP
|
||||||
@@ -8,7 +8,6 @@
|
|||||||
extern uint8_t fixPrecision;
|
extern uint8_t fixPrecision;
|
||||||
|
|
||||||
uint8_t fix_Precision();
|
uint8_t fix_Precision();
|
||||||
double fix_PrecisionFactor();
|
|
||||||
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);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_ASM_FORMAT_HPP
|
#ifndef RGBDS_ASM_FORMAT_HPP
|
||||||
#define RGBDS_ASM_FORMAT_HPP
|
#define RGBDS_ASM_FORMAT_HPP
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
// Contains some assembler-wide defines and externs
|
// Contains some assembler-wide defines and externs
|
||||||
|
|
||||||
@@ -30,8 +30,8 @@ struct FileStackNode {
|
|||||||
// Meaningless at the root level, but gets written to the object file anyway, so init it
|
// Meaningless at the root level, but gets written to the object file anyway, so init it
|
||||||
uint32_t lineNo = 0;
|
uint32_t lineNo = 0;
|
||||||
|
|
||||||
// Set only if referenced: ID within the object file, -1 if not output yet
|
// Set only if referenced: ID within the object file, `UINT32_MAX` if not output yet
|
||||||
uint32_t ID = -1;
|
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 data.get<std::vector<uint32_t>>(); }
|
||||||
@@ -41,15 +41,11 @@ struct FileStackNode {
|
|||||||
std::string const &name() const { return data.get<std::string>(); }
|
std::string const &name() const { return data.get<std::string>(); }
|
||||||
|
|
||||||
FileStackNode(FileStackNodeType type_, Either<std::vector<uint32_t>, std::string> data_)
|
FileStackNode(FileStackNodeType type_, Either<std::vector<uint32_t>, std::string> data_)
|
||||||
: type(type_), data(data_){};
|
: type(type_), data(data_) {}
|
||||||
|
|
||||||
std::string const &dump(uint32_t curLineNo) const;
|
std::string const &dump(uint32_t curLineNo) const;
|
||||||
|
|
||||||
// If true, entering this context generates a new unique ID.
|
|
||||||
bool generatesUniqueID() const { return type == NODE_REPT || type == NODE_MACRO; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define DEFAULT_MAX_DEPTH 64
|
|
||||||
extern size_t maxRecursionDepth;
|
extern size_t maxRecursionDepth;
|
||||||
|
|
||||||
struct MacroArgs;
|
struct MacroArgs;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_ASM_LEXER_HPP
|
#ifndef RGBDS_ASM_LEXER_HPP
|
||||||
#define RGBDS_ASM_LEXER_HPP
|
#define RGBDS_ASM_LEXER_HPP
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
// This value is a compromise between `LexerState` allocation performance when `mmap` works, and
|
// This value is a compromise between `LexerState` allocation performance when `mmap` works, and
|
||||||
// buffering performance when it doesn't/can't (e.g. when piping a file into RGBASM).
|
// buffering performance when it doesn't/can't (e.g. when piping a file into RGBASM).
|
||||||
#define 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");
|
||||||
// This caps the size of buffer reads, and according to POSIX, passing more than SSIZE_MAX is UB
|
// This caps the size of buffer reads, and according to POSIX, passing more than SSIZE_MAX is UB
|
||||||
@@ -83,7 +83,6 @@ struct LexerState {
|
|||||||
LexerMode mode;
|
LexerMode mode;
|
||||||
bool atLineStart;
|
bool atLineStart;
|
||||||
uint32_t lineNo;
|
uint32_t lineNo;
|
||||||
uint32_t colNo;
|
|
||||||
int lastToken;
|
int lastToken;
|
||||||
|
|
||||||
std::deque<IfStackEntry> ifStack;
|
std::deque<IfStackEntry> ifStack;
|
||||||
@@ -147,7 +146,6 @@ void lexer_ReachELSEBlock();
|
|||||||
|
|
||||||
void lexer_CheckRecursionDepth();
|
void lexer_CheckRecursionDepth();
|
||||||
uint32_t lexer_GetLineNo();
|
uint32_t lexer_GetLineNo();
|
||||||
uint32_t lexer_GetColNo();
|
|
||||||
void lexer_DumpStringExpansions();
|
void lexer_DumpStringExpansions();
|
||||||
|
|
||||||
struct Capture {
|
struct Capture {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_ASM_MACRO_HPP
|
#ifndef RGBDS_ASM_MACRO_HPP
|
||||||
#define RGBDS_ASM_MACRO_HPP
|
#define RGBDS_ASM_MACRO_HPP
|
||||||
@@ -9,11 +9,11 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
struct MacroArgs {
|
struct MacroArgs {
|
||||||
unsigned int shift;
|
uint32_t shift;
|
||||||
std::vector<std::shared_ptr<std::string>> args;
|
std::vector<std::shared_ptr<std::string>> args;
|
||||||
|
|
||||||
uint32_t nbArgs() const { return args.size() - shift; }
|
uint32_t nbArgs() const { return args.size() - shift; }
|
||||||
std::shared_ptr<std::string> getArg(uint32_t i) const;
|
std::shared_ptr<std::string> getArg(int32_t i) const;
|
||||||
std::shared_ptr<std::string> getAllArgs() const;
|
std::shared_ptr<std::string> getAllArgs() const;
|
||||||
|
|
||||||
void appendArg(std::shared_ptr<std::string> arg);
|
void appendArg(std::shared_ptr<std::string> arg);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_ASM_MAIN_HPP
|
#ifndef RGBDS_ASM_MAIN_HPP
|
||||||
#define RGBDS_ASM_MAIN_HPP
|
#define RGBDS_ASM_MAIN_HPP
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_ASM_OPT_HPP
|
#ifndef RGBDS_ASM_OPT_HPP
|
||||||
#define RGBDS_ASM_OPT_HPP
|
#define RGBDS_ASM_OPT_HPP
|
||||||
@@ -14,5 +14,6 @@ void opt_Parse(char const *option);
|
|||||||
|
|
||||||
void opt_Push();
|
void opt_Push();
|
||||||
void opt_Pop();
|
void opt_Pop();
|
||||||
|
void opt_CheckStack();
|
||||||
|
|
||||||
#endif // RGBDS_ASM_OPT_HPP
|
#endif // RGBDS_ASM_OPT_HPP
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_ASM_OUTPUT_HPP
|
#ifndef RGBDS_ASM_OUTPUT_HPP
|
||||||
#define RGBDS_ASM_OUTPUT_HPP
|
#define RGBDS_ASM_OUTPUT_HPP
|
||||||
@@ -14,14 +14,7 @@
|
|||||||
struct Expression;
|
struct Expression;
|
||||||
struct FileStackNode;
|
struct FileStackNode;
|
||||||
|
|
||||||
enum StateFeature {
|
enum StateFeature { STATE_EQU, STATE_VAR, STATE_EQUS, STATE_CHAR, STATE_MACRO, NB_STATE_FEATURES };
|
||||||
STATE_EQU,
|
|
||||||
STATE_VAR,
|
|
||||||
STATE_EQUS,
|
|
||||||
STATE_CHAR,
|
|
||||||
STATE_MACRO,
|
|
||||||
NB_STATE_FEATURES
|
|
||||||
};
|
|
||||||
|
|
||||||
extern std::string objectFileName;
|
extern std::string objectFileName;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_ASM_RPN_HPP
|
#ifndef RGBDS_ASM_RPN_HPP
|
||||||
#define RGBDS_ASM_RPN_HPP
|
#define RGBDS_ASM_RPN_HPP
|
||||||
@@ -22,21 +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
|
||||||
|
|
||||||
Expression() = default;
|
bool isKnown() const { return data.holds<int32_t>(); }
|
||||||
Expression(Expression &&) = default;
|
int32_t value() const { return data.get<int32_t>(); }
|
||||||
#ifdef _MSC_VER
|
|
||||||
// MSVC and WinFlexBison won't build without this...
|
|
||||||
Expression(Expression const &) = default;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Expression &operator=(Expression &&) = default;
|
|
||||||
|
|
||||||
bool isKnown() const {
|
|
||||||
return data.holds<int32_t>();
|
|
||||||
}
|
|
||||||
int32_t value() const {
|
|
||||||
return data.get<int32_t>();
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t getConstVal() const;
|
int32_t getConstVal() const;
|
||||||
Symbol const *symbolOf() const;
|
Symbol const *symbolOf() const;
|
||||||
@@ -53,8 +40,9 @@ 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);
|
||||||
|
|
||||||
void makeCheckHRAM();
|
bool makeCheckHRAM();
|
||||||
void makeCheckRST();
|
void makeCheckRST();
|
||||||
|
void makeCheckBitIndex(uint8_t mask);
|
||||||
|
|
||||||
void checkNBit(uint8_t n) const;
|
void checkNBit(uint8_t n) const;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_ASM_SECTION_HPP
|
#ifndef RGBDS_ASM_SECTION_HPP
|
||||||
#define RGBDS_ASM_SECTION_HPP
|
#define RGBDS_ASM_SECTION_HPP
|
||||||
@@ -70,7 +70,8 @@ void sect_SetLoadSection(
|
|||||||
SectionSpec const &attrs,
|
SectionSpec const &attrs,
|
||||||
SectionModifier mod
|
SectionModifier mod
|
||||||
);
|
);
|
||||||
void sect_EndLoadSection();
|
void sect_EndLoadSection(char const *cause);
|
||||||
|
void sect_CheckLoadClosed();
|
||||||
|
|
||||||
Section *sect_GetSymbolSection();
|
Section *sect_GetSymbolSection();
|
||||||
uint32_t sect_GetSymbolOffset();
|
uint32_t sect_GetSymbolOffset();
|
||||||
@@ -90,16 +91,17 @@ void sect_ByteString(std::vector<int32_t> const &string);
|
|||||||
void sect_WordString(std::vector<int32_t> const &string);
|
void sect_WordString(std::vector<int32_t> const &string);
|
||||||
void sect_LongString(std::vector<int32_t> const &string);
|
void sect_LongString(std::vector<int32_t> const &string);
|
||||||
void sect_Skip(uint32_t skip, bool ds);
|
void sect_Skip(uint32_t skip, bool ds);
|
||||||
void sect_RelByte(Expression &expr, uint32_t pcShift);
|
void sect_RelByte(Expression const &expr, uint32_t pcShift);
|
||||||
void sect_RelBytes(uint32_t n, std::vector<Expression> &exprs);
|
void sect_RelBytes(uint32_t n, std::vector<Expression> const &exprs);
|
||||||
void sect_RelWord(Expression &expr, uint32_t pcShift);
|
void sect_RelWord(Expression const &expr, uint32_t pcShift);
|
||||||
void sect_RelLong(Expression &expr, uint32_t pcShift);
|
void sect_RelLong(Expression const &expr, uint32_t pcShift);
|
||||||
void sect_PCRelByte(Expression &expr, uint32_t pcShift);
|
void sect_PCRelByte(Expression const &expr, uint32_t pcShift);
|
||||||
void sect_BinaryFile(std::string const &name, int32_t startPos);
|
void sect_BinaryFile(std::string const &name, int32_t startPos);
|
||||||
void sect_BinaryFileSlice(std::string const &name, int32_t startPos, int32_t length);
|
void sect_BinaryFileSlice(std::string const &name, int32_t startPos, int32_t length);
|
||||||
|
|
||||||
void sect_EndSection();
|
void sect_EndSection();
|
||||||
void sect_PushSection();
|
void sect_PushSection();
|
||||||
void sect_PopSection();
|
void sect_PopSection();
|
||||||
|
void sect_CheckStack();
|
||||||
|
|
||||||
#endif // RGBDS_ASM_SECTION_HPP
|
#endif // RGBDS_ASM_SECTION_HPP
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_ASM_SYMBOL_HPP
|
#ifndef RGBDS_ASM_SYMBOL_HPP
|
||||||
#define RGBDS_ASM_SYMBOL_HPP
|
#define RGBDS_ASM_SYMBOL_HPP
|
||||||
@@ -45,7 +45,7 @@ struct Symbol {
|
|||||||
>
|
>
|
||||||
data;
|
data;
|
||||||
|
|
||||||
uint32_t ID; // ID of the symbol in the object file (-1 if none)
|
uint32_t ID; // ID of the symbol in the object file (`UINT32_MAX` if none)
|
||||||
uint32_t defIndex; // Ordering of the symbol in the state file
|
uint32_t defIndex; // Ordering of the symbol in the state file
|
||||||
|
|
||||||
bool isDefined() const { return type != SYM_REF; }
|
bool isDefined() const { return type != SYM_REF; }
|
||||||
@@ -55,7 +55,7 @@ struct Symbol {
|
|||||||
bool isConstant() const {
|
bool isConstant() const {
|
||||||
if (type == SYM_LABEL) {
|
if (type == SYM_LABEL) {
|
||||||
Section const *sect = getSection();
|
Section const *sect = getSection();
|
||||||
return sect && sect->org != (uint32_t)-1;
|
return sect && sect->org != UINT32_MAX;
|
||||||
}
|
}
|
||||||
return type == SYM_EQU || type == SYM_VAR;
|
return type == SYM_EQU || type == SYM_VAR;
|
||||||
}
|
}
|
||||||
@@ -82,7 +82,6 @@ Symbol *sym_RedefEqu(std::string const &symName, int32_t value);
|
|||||||
Symbol *sym_AddVar(std::string const &symName, int32_t value);
|
Symbol *sym_AddVar(std::string const &symName, int32_t value);
|
||||||
int32_t sym_GetRSValue();
|
int32_t sym_GetRSValue();
|
||||||
void sym_SetRSValue(int32_t value);
|
void sym_SetRSValue(int32_t value);
|
||||||
uint32_t sym_GetConstantValue(std::string const &symName);
|
|
||||||
// Find a symbol by exact name, bypassing expansion checks
|
// Find a symbol by exact name, bypassing expansion checks
|
||||||
Symbol *sym_FindExactSymbol(std::string const &symName);
|
Symbol *sym_FindExactSymbol(std::string const &symName);
|
||||||
// Find a symbol, possibly scoped, by name
|
// Find a symbol, possibly scoped, by name
|
||||||
|
|||||||
@@ -1,35 +1,34 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#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;
|
extern unsigned int nbErrors, maxErrors;
|
||||||
|
|
||||||
enum WarningState { WARNING_DEFAULT, WARNING_DISABLED, WARNING_ENABLED, WARNING_ERROR };
|
|
||||||
|
|
||||||
enum WarningID {
|
enum WarningID {
|
||||||
WARNING_ASSERT, // Assertions
|
WARNING_ASSERT, // Assertions
|
||||||
WARNING_BACKWARDS_FOR, // `for` loop with backwards range
|
WARNING_BACKWARDS_FOR, // `FOR` loop with backwards range
|
||||||
WARNING_BUILTIN_ARG, // Invalid args to builtins
|
WARNING_BUILTIN_ARG, // Invalid args to builtins
|
||||||
WARNING_CHARMAP_REDEF, // Charmap entry re-definition
|
WARNING_CHARMAP_REDEF, // Charmap entry re-definition
|
||||||
WARNING_DIV, // Division undefined behavior
|
WARNING_DIV, // Undefined division behavior
|
||||||
WARNING_EMPTY_DATA_DIRECTIVE, // `db`, `dw` or `dl` directive without data in ROM
|
WARNING_EMPTY_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_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
|
||||||
WARNING_OBSOLETE, // Obsolete things
|
WARNING_OBSOLETE, // Obsolete/deprecated things
|
||||||
WARNING_SHIFT, // Shifting undefined behavior
|
WARNING_SHIFT, // Undefined `SHIFT` behavior
|
||||||
WARNING_SHIFT_AMOUNT, // Strange shift amount
|
WARNING_SHIFT_AMOUNT, // Strange `SHIFT` amount
|
||||||
WARNING_USER, // User warnings
|
WARNING_UNMATCHED_DIRECTIVE, // `PUSH[C|O|S]` without `POP[C|O|S]`
|
||||||
|
WARNING_UNTERMINATED_LOAD, // `LOAD` without `ENDL`
|
||||||
|
WARNING_USER, // User-defined `WARN`ings
|
||||||
|
|
||||||
NB_PLAIN_WARNINGS,
|
NB_PLAIN_WARNINGS,
|
||||||
|
|
||||||
// Warnings past this point are "parametric" warnings, only mapping to a single flag
|
// Warnings past this point are "parametric" warnings, only mapping to a single flag
|
||||||
#define PARAM_WARNINGS_START NB_PLAIN_WARNINGS
|
|
||||||
// Treating string as number may lose some bits
|
// Treating string as number may lose some bits
|
||||||
WARNING_NUMERIC_STRING_1 = PARAM_WARNINGS_START,
|
WARNING_NUMERIC_STRING_1 = NB_PLAIN_WARNINGS,
|
||||||
WARNING_NUMERIC_STRING_2,
|
WARNING_NUMERIC_STRING_2,
|
||||||
// Purging an exported symbol or label
|
// Purging an exported symbol or label
|
||||||
WARNING_PURGE_1,
|
WARNING_PURGE_1,
|
||||||
@@ -41,45 +40,46 @@ enum WarningID {
|
|||||||
WARNING_UNMAPPED_CHAR_1,
|
WARNING_UNMAPPED_CHAR_1,
|
||||||
WARNING_UNMAPPED_CHAR_2,
|
WARNING_UNMAPPED_CHAR_2,
|
||||||
|
|
||||||
NB_PLAIN_AND_PARAM_WARNINGS,
|
|
||||||
#define NB_PARAM_WARNINGS (NB_PLAIN_AND_PARAM_WARNINGS - PARAM_WARNINGS_START)
|
|
||||||
|
|
||||||
// Warnings past this point are "meta" warnings
|
|
||||||
#define META_WARNINGS_START NB_PLAIN_AND_PARAM_WARNINGS
|
|
||||||
WARNING_ALL = META_WARNINGS_START,
|
|
||||||
WARNING_EXTRA,
|
|
||||||
WARNING_EVERYTHING,
|
|
||||||
|
|
||||||
NB_WARNINGS,
|
NB_WARNINGS,
|
||||||
#define NB_META_WARNINGS (NB_WARNINGS - META_WARNINGS_START)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern WarningState warningStates[NB_PLAIN_AND_PARAM_WARNINGS];
|
enum WarningAbled { WARNING_DEFAULT, WARNING_ENABLED, WARNING_DISABLED };
|
||||||
|
|
||||||
|
struct WarningState {
|
||||||
|
WarningAbled state;
|
||||||
|
WarningAbled error;
|
||||||
|
|
||||||
|
void update(WarningState other);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Diagnostics {
|
||||||
|
WarningState flagStates[NB_WARNINGS];
|
||||||
|
WarningState metaStates[NB_WARNINGS];
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Diagnostics warningStates;
|
||||||
extern bool warningsAreErrors;
|
extern bool warningsAreErrors;
|
||||||
|
|
||||||
void processWarningFlag(char const *flag);
|
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.
|
[[gnu::format(printf, 2, 3)]]
|
||||||
*/
|
void warning(WarningID id, char const *fmt, ...);
|
||||||
[[gnu::format(printf, 2, 3)]] void warning(WarningID id, char const *fmt, ...);
|
|
||||||
|
|
||||||
/*
|
// Used for errors that compromise the whole assembly process by affecting the
|
||||||
* Used for errors that compromise the whole assembly process by affecting the
|
// following code, potencially making the assembler generate errors caused by
|
||||||
* following code, potencially making the assembler generate errors caused by
|
// the first one and unrelated to the code that the assembler complains about.
|
||||||
* the first one and unrelated to the code that the assembler complains about.
|
// 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]]
|
||||||
*/
|
void fatalerror(char const *fmt, ...);
|
||||||
[[gnu::format(printf, 1, 2), noreturn]] void fatalerror(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
|
// get a list of all errors at the end, making it easier to fix all of them at
|
||||||
* get a list of all errors at the end, making it easier to fix all of them at
|
// once.
|
||||||
* once.
|
[[gnu::format(printf, 1, 2)]]
|
||||||
*/
|
void error(char const *fmt, ...);
|
||||||
[[gnu::format(printf, 1, 2)]] void error(char const *fmt, ...);
|
|
||||||
|
|
||||||
#endif // RGBDS_ASM_WARNING_HPP
|
#endif // RGBDS_ASM_WARNING_HPP
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_DEFAULT_INIT_ALLOC_HPP
|
#ifndef RGBDS_DEFAULT_INIT_ALLOC_HPP
|
||||||
#define RGBDS_DEFAULT_INIT_ALLOC_HPP
|
#define RGBDS_DEFAULT_INIT_ALLOC_HPP
|
||||||
@@ -6,14 +6,11 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
/*
|
// Allocator adaptor that interposes construct() calls to convert value-initialization
|
||||||
* 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
|
||||||
* (which is what you get with e.g. `vector::resize`) into default-initialization (which does not
|
// zero out non-class types).
|
||||||
* zero out non-class types).
|
// From
|
||||||
* From
|
// https://stackoverflow.com/questions/21028299/is-this-behavior-of-vectorresizesize-type-n-under-c11-and-boost-container/21028912#21028912
|
||||||
* 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>>
|
template<typename T, typename A = std::allocator<T>>
|
||||||
class default_init_allocator : public A {
|
class default_init_allocator : public A {
|
||||||
using a_t = std::allocator_traits<A>;
|
using a_t = std::allocator_traits<A>;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_EITHER_HPP
|
#ifndef RGBDS_EITHER_HPP
|
||||||
#define RGBDS_EITHER_HPP
|
#define RGBDS_EITHER_HPP
|
||||||
@@ -43,11 +43,11 @@ private:
|
|||||||
// Generic field accessors; for internal use only.
|
// Generic field accessors; for internal use only.
|
||||||
template<typename T>
|
template<typename T>
|
||||||
auto &field() {
|
auto &field() {
|
||||||
return pick((T *)nullptr);
|
return pick(static_cast<T *>(nullptr));
|
||||||
}
|
}
|
||||||
template<typename T>
|
template<typename T>
|
||||||
auto const &field() const {
|
auto const &field() const {
|
||||||
return pick((T *)nullptr);
|
return pick(static_cast<T *>(nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -103,7 +103,7 @@ public:
|
|||||||
} else if (other._tag == other._t2.tag_value) {
|
} else if (other._tag == other._t2.tag_value) {
|
||||||
*this = other._t2.value;
|
*this = other._t2.value;
|
||||||
} else {
|
} else {
|
||||||
_tag = nulltag;
|
_tag = nulltag; // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_ERROR_HPP
|
#ifndef RGBDS_ERROR_HPP
|
||||||
#define RGBDS_ERROR_HPP
|
#define RGBDS_ERROR_HPP
|
||||||
|
|
||||||
extern "C" {
|
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)]] void warn(char const *fmt...);
|
[[gnu::format(printf, 1, 2), noreturn]]
|
||||||
[[gnu::format(printf, 1, 2)]] void warnx(char const *fmt, ...);
|
void err(char const *fmt, ...);
|
||||||
|
[[gnu::format(printf, 1, 2), noreturn]]
|
||||||
[[gnu::format(printf, 1, 2), noreturn]] void err(char const *fmt, ...);
|
void errx(char const *fmt, ...);
|
||||||
[[gnu::format(printf, 1, 2), noreturn]] void errx(char const *fmt, ...);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // RGBDS_ERROR_HPP
|
#endif // RGBDS_ERROR_HPP
|
||||||
|
|||||||
16
include/extern/getopt.hpp
vendored
16
include/extern/getopt.hpp
vendored
@@ -1,11 +1,15 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
/* This implementation was taken from musl and modified for RGBDS */
|
// This implementation was taken from musl and modified for RGBDS
|
||||||
|
|
||||||
#ifndef RGBDS_EXTERN_GETOPT_HPP
|
#ifndef RGBDS_EXTERN_GETOPT_HPP
|
||||||
#define RGBDS_EXTERN_GETOPT_HPP
|
#define RGBDS_EXTERN_GETOPT_HPP
|
||||||
|
|
||||||
extern "C" {
|
// clang-format off: vertically align values
|
||||||
|
static constexpr int no_argument = 0;
|
||||||
|
static constexpr int required_argument = 1;
|
||||||
|
static constexpr int optional_argument = 2;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
extern char *musl_optarg;
|
extern char *musl_optarg;
|
||||||
extern int musl_optind, musl_opterr, musl_optopt, musl_optreset;
|
extern int musl_optind, musl_opterr, musl_optopt, musl_optreset;
|
||||||
@@ -21,10 +25,4 @@ int musl_getopt_long_only(
|
|||||||
int argc, char **argv, char const *optstring, option const *longopts, int *idx
|
int argc, char **argv, char const *optstring, option const *longopts, int *idx
|
||||||
);
|
);
|
||||||
|
|
||||||
#define no_argument 0
|
|
||||||
#define required_argument 1
|
|
||||||
#define optional_argument 2
|
|
||||||
|
|
||||||
} // extern "C"
|
|
||||||
|
|
||||||
#endif // RGBDS_EXTERN_GETOPT_HPP
|
#endif // RGBDS_EXTERN_GETOPT_HPP
|
||||||
|
|||||||
2
include/extern/utf8decoder.hpp
vendored
2
include/extern/utf8decoder.hpp
vendored
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_EXTERN_UTF8DECODER_HPP
|
#ifndef RGBDS_EXTERN_UTF8DECODER_HPP
|
||||||
#define RGBDS_EXTERN_UTF8DECODER_HPP
|
#define RGBDS_EXTERN_UTF8DECODER_HPP
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_FILE_HPP
|
#ifndef RGBDS_FILE_HPP
|
||||||
#define RGBDS_FILE_HPP
|
#define RGBDS_FILE_HPP
|
||||||
@@ -18,17 +18,13 @@
|
|||||||
#include "gfx/main.hpp"
|
#include "gfx/main.hpp"
|
||||||
|
|
||||||
class File {
|
class File {
|
||||||
// Construct a `std::streambuf *` by default, since it's probably lighter than a `filebuf`.
|
|
||||||
Either<std::streambuf *, std::filebuf> _file;
|
Either<std::streambuf *, std::filebuf> _file;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
File() {}
|
File() : _file(nullptr) {}
|
||||||
~File() { close(); }
|
|
||||||
|
|
||||||
/**
|
// This should only be called once, and before doing any `->` operations.
|
||||||
* This should only be called once, and before doing any `->` operations.
|
// 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>();
|
_file.emplace<std::filebuf>();
|
||||||
@@ -63,20 +59,6 @@ public:
|
|||||||
return const_cast<File *>(this)->operator->();
|
return const_cast<File *>(this)->operator->();
|
||||||
}
|
}
|
||||||
|
|
||||||
File *close() {
|
|
||||||
if (_file.holds<std::filebuf>()) {
|
|
||||||
// This is called by the destructor, and an explicit `close` shouldn't close twice.
|
|
||||||
std::filebuf fileBuf = std::move(_file.get<std::filebuf>());
|
|
||||||
_file.emplace<std::streambuf *>(nullptr);
|
|
||||||
if (fileBuf.close() != nullptr) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
} else if (_file.get<std::streambuf *>() != nullptr) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 _file.holds<std::filebuf>() ? path.c_str()
|
||||||
: _file.get<std::streambuf *>() == std::cin.rdbuf() ? "<stdin>"
|
: _file.get<std::streambuf *>() == std::cin.rdbuf() ? "<stdin>"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_GFX_MAIN_HPP
|
#ifndef RGBDS_GFX_MAIN_HPP
|
||||||
#define RGBDS_GFX_MAIN_HPP
|
#define RGBDS_GFX_MAIN_HPP
|
||||||
@@ -10,6 +10,8 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "helpers.hpp"
|
||||||
|
|
||||||
#include "gfx/rgba.hpp"
|
#include "gfx/rgba.hpp"
|
||||||
|
|
||||||
struct Options {
|
struct Options {
|
||||||
@@ -21,6 +23,7 @@ struct Options {
|
|||||||
uint8_t verbosity = 0; // -v
|
uint8_t verbosity = 0; // -v
|
||||||
|
|
||||||
std::string attrmap{}; // -a, -A
|
std::string attrmap{}; // -a, -A
|
||||||
|
std::optional<Rgba> bgColor{}; // -B
|
||||||
std::array<uint8_t, 2> baseTileIDs{0, 0}; // -b
|
std::array<uint8_t, 2> baseTileIDs{0, 0}; // -b
|
||||||
enum {
|
enum {
|
||||||
NO_SPEC,
|
NO_SPEC,
|
||||||
@@ -48,14 +51,17 @@ struct Options {
|
|||||||
|
|
||||||
std::string input{}; // positional arg
|
std::string input{}; // positional arg
|
||||||
|
|
||||||
static constexpr uint8_t VERB_NONE = 0; // Normal, no extra output
|
// clang-format off: vertically align values
|
||||||
static constexpr uint8_t VERB_CFG = 1; // Print configuration after parsing options
|
static constexpr uint8_t VERB_NONE = 0; // Normal, no extra output
|
||||||
static constexpr uint8_t VERB_LOG_ACT = 2; // Log actions before doing them
|
static constexpr uint8_t VERB_CFG = 1; // Print configuration after parsing options
|
||||||
static constexpr uint8_t VERB_INTERM = 3; // Print some intermediate results
|
static constexpr uint8_t VERB_LOG_ACT = 2; // Log actions before doing them
|
||||||
static constexpr uint8_t VERB_DEBUG = 4; // Internals are logged
|
static constexpr uint8_t VERB_INTERM = 3; // Print some intermediate results
|
||||||
static constexpr uint8_t VERB_UNMAPPED = 5; // Unused so far
|
static constexpr uint8_t VERB_DEBUG = 4; // Internals are logged
|
||||||
static constexpr uint8_t VERB_VVVVVV = 6; // What, can't I have a little fun?
|
static constexpr uint8_t VERB_TRACE = 5; // Step-by-step algorithm details
|
||||||
[[gnu::format(printf, 3, 4)]] void verbosePrint(uint8_t level, char const *fmt, ...) const;
|
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; }
|
||||||
@@ -63,32 +69,24 @@ struct Options {
|
|||||||
|
|
||||||
extern Options options;
|
extern Options options;
|
||||||
|
|
||||||
/*
|
// Prints the error count, and exits with failure
|
||||||
* Prints the error count, and exits with failure
|
[[noreturn]]
|
||||||
*/
|
void giveUp();
|
||||||
[[noreturn]] void giveUp();
|
// If any error has been emitted thus far, calls `giveUp()`.
|
||||||
/*
|
|
||||||
* If any error has been emitted thus far, calls `giveUp()`.
|
|
||||||
*/
|
|
||||||
void requireZeroErrors();
|
void requireZeroErrors();
|
||||||
/*
|
// Prints a warning, and does not change the error count
|
||||||
* Prints a warning, and does not change the error count
|
[[gnu::format(printf, 1, 2)]]
|
||||||
*/
|
void warning(char const *fmt, ...);
|
||||||
[[gnu::format(printf, 1, 2)]] void warning(char const *fmt, ...);
|
// Prints an error, and increments the error count
|
||||||
/*
|
[[gnu::format(printf, 1, 2)]]
|
||||||
* Prints an error, and increments the error count
|
void error(char const *fmt, ...);
|
||||||
*/
|
// Prints an error, and increments the error count
|
||||||
[[gnu::format(printf, 1, 2)]] void error(char const *fmt, ...);
|
// Does not take format arguments so `format_` and `-Wformat-security` won't complain about
|
||||||
/*
|
// calling `errorMessage(msg)`.
|
||||||
* 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);
|
void errorMessage(char const *msg);
|
||||||
/*
|
// Prints a fatal error, increments the error count, and gives up
|
||||||
* Prints a fatal error, increments the error count, and gives up
|
[[gnu::format(printf, 1, 2), noreturn]]
|
||||||
*/
|
void fatal(char const *fmt, ...);
|
||||||
[[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
|
||||||
@@ -107,20 +105,41 @@ struct Palette {
|
|||||||
uint8_t size() const;
|
uint8_t size() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail {
|
// Flipping tends to happen fairly often, so take a bite out of dcache to speed it up
|
||||||
template<typename T, T... i>
|
static constexpr auto flipTable = ([]() constexpr {
|
||||||
static constexpr auto flipTable(std::integer_sequence<T, i...>) {
|
std::array<uint16_t, 256> table{};
|
||||||
return std::array{[](uint8_t byte) {
|
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;
|
||||||
byte = (byte & 0b0000'1111) << 4 | (byte & 0b1111'0000) >> 4;
|
byte = (byte & 0b0000'1111) << 4 | (byte & 0b1111'0000) >> 4;
|
||||||
byte = (byte & 0b0011'0011) << 2 | (byte & 0b1100'1100) >> 2;
|
byte = (byte & 0b0011'0011) << 2 | (byte & 0b1100'1100) >> 2;
|
||||||
byte = (byte & 0b0101'0101) << 1 | (byte & 0b1010'1010) >> 1;
|
byte = (byte & 0b0101'0101) << 1 | (byte & 0b1010'1010) >> 1;
|
||||||
return byte;
|
table[i] = byte;
|
||||||
}(i)...};
|
}
|
||||||
}
|
return table;
|
||||||
} // namespace detail
|
})();
|
||||||
|
|
||||||
// Flipping tends to happen fairly often, so take a bite out of dcache to speed it up
|
// Parsing helpers.
|
||||||
static constexpr auto flipTable = detail::flipTable(std::make_integer_sequence<uint16_t, 256>());
|
|
||||||
|
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
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_GFX_PAL_PACKING_HPP
|
#ifndef RGBDS_GFX_PAL_PACKING_HPP
|
||||||
#define RGBDS_GFX_PAL_PACKING_HPP
|
#define RGBDS_GFX_PAL_PACKING_HPP
|
||||||
@@ -6,19 +6,13 @@
|
|||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "defaultinitalloc.hpp"
|
#include "defaultinitvec.hpp"
|
||||||
|
|
||||||
struct Palette;
|
struct Palette;
|
||||||
class ProtoPalette;
|
class ProtoPalette;
|
||||||
|
|
||||||
namespace packing {
|
// Returns which palette each proto-palette maps to, and how many palettes are necessary
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns which palette each proto-palette maps to, and how many palettes are necessary
|
|
||||||
*/
|
|
||||||
std::tuple<DefaultInitVec<size_t>, size_t>
|
std::tuple<DefaultInitVec<size_t>, size_t>
|
||||||
overloadAndRemove(std::vector<ProtoPalette> const &protoPalettes);
|
overloadAndRemove(std::vector<ProtoPalette> const &protoPalettes);
|
||||||
|
|
||||||
} // namespace packing
|
|
||||||
|
|
||||||
#endif // RGBDS_GFX_PAL_PACKING_HPP
|
#endif // RGBDS_GFX_PAL_PACKING_HPP
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_GFX_PAL_SORTING_HPP
|
#ifndef RGBDS_GFX_PAL_SORTING_HPP
|
||||||
#define RGBDS_GFX_PAL_SORTING_HPP
|
#define RGBDS_GFX_PAL_SORTING_HPP
|
||||||
@@ -10,22 +10,22 @@
|
|||||||
|
|
||||||
#include "gfx/rgba.hpp"
|
#include "gfx/rgba.hpp"
|
||||||
|
|
||||||
|
// Allow a slot for every possible CGB color, plus one for transparency
|
||||||
|
// 32 (1 << 5) per channel, times 3 RGB channels = 32768 CGB colors
|
||||||
|
static constexpr size_t NB_COLOR_SLOTS = (1 << (5 * 3)) + 1;
|
||||||
|
|
||||||
struct Palette;
|
struct Palette;
|
||||||
|
|
||||||
namespace sorting {
|
void sortIndexed(
|
||||||
|
|
||||||
void indexed(
|
|
||||||
std::vector<Palette> &palettes,
|
std::vector<Palette> &palettes,
|
||||||
int palSize,
|
int palSize,
|
||||||
png_color const *palRGB,
|
png_color const *palRGB,
|
||||||
int palAlphaSize,
|
int palAlphaSize,
|
||||||
png_byte *palAlpha
|
png_byte *palAlpha
|
||||||
);
|
);
|
||||||
void grayscale(
|
void sortGrayscale(
|
||||||
std::vector<Palette> &palettes, std::array<std::optional<Rgba>, 0x8001> const &colors
|
std::vector<Palette> &palettes, std::array<std::optional<Rgba>, NB_COLOR_SLOTS> const &colors
|
||||||
);
|
);
|
||||||
void rgb(std::vector<Palette> &palettes);
|
void sortRgb(std::vector<Palette> &palettes);
|
||||||
|
|
||||||
} // namespace sorting
|
|
||||||
|
|
||||||
#endif // RGBDS_GFX_PAL_SORTING_HPP
|
#endif // RGBDS_GFX_PAL_SORTING_HPP
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_GFX_PAL_SPEC_HPP
|
#ifndef RGBDS_GFX_PAL_SPEC_HPP
|
||||||
#define RGBDS_GFX_PAL_SPEC_HPP
|
#define RGBDS_GFX_PAL_SPEC_HPP
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_GFX_PROCESS_HPP
|
#ifndef RGBDS_GFX_PROCESS_HPP
|
||||||
#define RGBDS_GFX_PROCESS_HPP
|
#define RGBDS_GFX_PROCESS_HPP
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_GFX_PROTO_PALETTE_HPP
|
#ifndef RGBDS_GFX_PROTO_PALETTE_HPP
|
||||||
#define RGBDS_GFX_PROTO_PALETTE_HPP
|
#define RGBDS_GFX_PROTO_PALETTE_HPP
|
||||||
@@ -18,12 +18,8 @@ private:
|
|||||||
std::array<uint16_t, capacity> _colorIndices{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX};
|
std::array<uint16_t, capacity> _colorIndices{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/*
|
// Adds the specified color to the set, or **silently drops it** if the set is full.
|
||||||
* Adds the specified color to the set, or **silently drops it** if the set is full.
|
void add(uint16_t color);
|
||||||
*
|
|
||||||
* Returns whether the color was unique.
|
|
||||||
*/
|
|
||||||
bool add(uint16_t color);
|
|
||||||
|
|
||||||
enum ComparisonResult {
|
enum ComparisonResult {
|
||||||
NEITHER,
|
NEITHER,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_GFX_REVERSE_HPP
|
#ifndef RGBDS_GFX_REVERSE_HPP
|
||||||
#define RGBDS_GFX_REVERSE_HPP
|
#define RGBDS_GFX_REVERSE_HPP
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_GFX_RGBA_HPP
|
#ifndef RGBDS_GFX_RGBA_HPP
|
||||||
#define RGBDS_GFX_RGBA_HPP
|
#define RGBDS_GFX_RGBA_HPP
|
||||||
@@ -13,9 +13,7 @@ struct Rgba {
|
|||||||
|
|
||||||
constexpr Rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
constexpr Rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||||
: red(r), green(g), blue(b), alpha(a) {}
|
: red(r), green(g), blue(b), alpha(a) {}
|
||||||
/*
|
// Constructs the color from a "packed" RGBA representation (0xRRGGBBAA)
|
||||||
* Constructs the color from a "packed" RGBA representation (0xRRGGBBAA)
|
|
||||||
*/
|
|
||||||
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) {}
|
||||||
|
|
||||||
@@ -28,34 +26,28 @@ struct Rgba {
|
|||||||
_5to8(cgbColor),
|
_5to8(cgbColor),
|
||||||
_5to8(cgbColor >> 5),
|
_5to8(cgbColor >> 5),
|
||||||
_5to8(cgbColor >> 10),
|
_5to8(cgbColor >> 10),
|
||||||
(uint8_t)(cgbColor & 0x8000 ? 0x00 : 0xFF),
|
static_cast<uint8_t>(cgbColor & 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; };
|
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);
|
||||||
}
|
}
|
||||||
friend bool operator==(Rgba const &lhs, Rgba const &rhs) { return lhs.toCSS() == rhs.toCSS(); }
|
|
||||||
friend bool operator!=(Rgba const &lhs, Rgba const &rhs) { return lhs.toCSS() != rhs.toCSS(); }
|
|
||||||
|
|
||||||
/*
|
bool operator==(Rgba const &rhs) const { return toCSS() == rhs.toCSS(); }
|
||||||
* CGB colors are RGB555, so we use bit 15 to signify that the color is transparent instead
|
|
||||||
* Since the rest of the bits don't matter then, we return 0x8000 exactly.
|
// CGB colors are RGB555, so we use bit 15 to signify that the color is transparent instead
|
||||||
*/
|
// Since the rest of the bits don't matter then, we return 0x8000 exactly.
|
||||||
static constexpr uint16_t transparent = 0b1'00000'00000'00000;
|
static constexpr uint16_t transparent = 0b1'00000'00000'00000;
|
||||||
|
|
||||||
static constexpr uint8_t transparency_threshold = 0x10;
|
static constexpr uint8_t transparency_threshold = 0x10;
|
||||||
bool isTransparent() const { return alpha < transparency_threshold; }
|
bool isTransparent() const { return alpha < transparency_threshold; }
|
||||||
static constexpr uint8_t opacity_threshold = 0xF0;
|
static constexpr uint8_t opacity_threshold = 0xF0;
|
||||||
bool isOpaque() const { return alpha >= opacity_threshold; }
|
bool isOpaque() const { return alpha >= opacity_threshold; }
|
||||||
/*
|
// Computes the equivalent CGB color, respects the color curve depending on options
|
||||||
* Computes the equivalent CGB color, respects the color curve depending on options
|
|
||||||
*/
|
|
||||||
uint16_t cgbColor() const;
|
uint16_t cgbColor() const;
|
||||||
|
|
||||||
bool isGray() const { return red == green && green == blue; }
|
bool isGray() const { return red == green && green == blue; }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_HELPERS_HPP
|
#ifndef RGBDS_HELPERS_HPP
|
||||||
#define RGBDS_HELPERS_HPP
|
#define RGBDS_HELPERS_HPP
|
||||||
@@ -14,7 +14,8 @@
|
|||||||
#else
|
#else
|
||||||
// This seems to generate similar code to __builtin_unreachable, despite different semantics
|
// This seems to generate similar code to __builtin_unreachable, despite different semantics
|
||||||
// Note that executing this is undefined behavior (declared [[noreturn]], but does return)
|
// Note that executing this is undefined behavior (declared [[noreturn]], but does return)
|
||||||
[[noreturn]] static inline void unreachable_() {
|
[[noreturn]]
|
||||||
|
static inline void unreachable_() {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -26,8 +27,9 @@
|
|||||||
// `[[gnu::assume()]]` for GCC or compatible also has insufficient support (GCC 13+ only)
|
// `[[gnu::assume()]]` for GCC or compatible also has insufficient support (GCC 13+ only)
|
||||||
#define assume(x) \
|
#define assume(x) \
|
||||||
do { \
|
do { \
|
||||||
if (!(x)) \
|
if (!(x)) { \
|
||||||
unreachable_(); \
|
unreachable_(); \
|
||||||
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
@@ -93,15 +95,14 @@ static inline int clz(unsigned int x) {
|
|||||||
#define CAT(x, y) x##y
|
#define CAT(x, y) x##y
|
||||||
#define EXPAND_AND_CAT(x, y) CAT(x, y)
|
#define EXPAND_AND_CAT(x, y) CAT(x, y)
|
||||||
|
|
||||||
// Obtaining the size of an array; `arr` must be an expression, not a type!
|
|
||||||
// (Having two instances of `arr` is OK because the contents of `sizeof` are not evaluated.)
|
|
||||||
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof *(arr))
|
|
||||||
|
|
||||||
// For lack of <ranges>, this adds some more brevity
|
// For lack of <ranges>, this adds some more brevity
|
||||||
#define RANGE(s) std::begin(s), std::end(s)
|
#define RANGE(s) std::begin(s), std::end(s)
|
||||||
|
|
||||||
// MSVC does not inline `strlen()` or `.length()` of a constant string, so we use `sizeof`
|
// MSVC does not inline `strlen()` or `.length()` of a constant string
|
||||||
#define QUOTEDSTRLEN(s) (sizeof(s) - 1)
|
template<int N>
|
||||||
|
static constexpr int literal_strlen(char const (&)[N]) {
|
||||||
|
return N - 1;
|
||||||
|
}
|
||||||
|
|
||||||
// For ad-hoc RAII in place of a `defer` statement or cross-platform `__attribute__((cleanup))`
|
// For ad-hoc RAII in place of a `defer` statement or cross-platform `__attribute__((cleanup))`
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_ITERTOOLS_HPP
|
#ifndef RGBDS_ITERTOOLS_HPP
|
||||||
#define RGBDS_ITERTOOLS_HPP
|
#define RGBDS_ITERTOOLS_HPP
|
||||||
@@ -6,52 +6,45 @@
|
|||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
class EnumSeqIterator {
|
|
||||||
T _value;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit EnumSeqIterator(T value) : _value(value) {}
|
|
||||||
|
|
||||||
EnumSeqIterator &operator++() {
|
|
||||||
_value = (T)(_value + 1);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto operator*() const { return _value; }
|
|
||||||
|
|
||||||
friend auto operator==(EnumSeqIterator const &lhs, EnumSeqIterator const &rhs) {
|
|
||||||
return lhs._value == rhs._value;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend auto operator!=(EnumSeqIterator const &lhs, EnumSeqIterator const &rhs) {
|
|
||||||
return lhs._value != rhs._value;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class EnumSeq {
|
class EnumSeq {
|
||||||
T _start;
|
T _start;
|
||||||
T _stop;
|
T _stop;
|
||||||
|
|
||||||
|
class Iterator {
|
||||||
|
T _value;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Iterator(T value) : _value(value) {}
|
||||||
|
|
||||||
|
Iterator &operator++() {
|
||||||
|
_value = static_cast<T>(_value + 1);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operator*() const { return _value; }
|
||||||
|
|
||||||
|
bool operator==(Iterator const &rhs) const { return _value == rhs._value; }
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit EnumSeq(T stop) : _start((T)0), _stop(stop) {}
|
explicit EnumSeq(T stop) : _start(static_cast<T>(0)), _stop(stop) {}
|
||||||
explicit EnumSeq(T start, T stop) : _start(start), _stop(stop) {}
|
explicit EnumSeq(T start, T stop) : _start(start), _stop(stop) {}
|
||||||
|
|
||||||
EnumSeqIterator<T> begin() { return EnumSeqIterator(_start); }
|
Iterator begin() { return Iterator(_start); }
|
||||||
EnumSeqIterator<T> end() { return EnumSeqIterator(_stop); }
|
Iterator end() { return Iterator(_stop); }
|
||||||
};
|
};
|
||||||
|
|
||||||
// This is not a fully generic implementation; its current use cases only require for-loop behavior.
|
// This is not a fully generic implementation; its current use cases only require for-loop behavior.
|
||||||
// We also assume that all iterators have the same length.
|
// We also assume that all iterators have the same length.
|
||||||
template<typename... Iters>
|
template<typename... Ts>
|
||||||
class Zip {
|
class ZipIterator {
|
||||||
std::tuple<Iters...> _iters;
|
std::tuple<Ts...> _iters;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Zip(std::tuple<Iters...> &&iters) : _iters(iters) {}
|
explicit ZipIterator(std::tuple<Ts...> &&iters) : _iters(iters) {}
|
||||||
|
|
||||||
Zip &operator++() {
|
ZipIterator &operator++() {
|
||||||
std::apply([](auto &&...it) { (++it, ...); }, _iters);
|
std::apply([](auto &&...it) { (++it, ...); }, _iters);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@@ -62,26 +55,20 @@ public:
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
friend auto operator==(Zip const &lhs, Zip const &rhs) {
|
bool operator==(ZipIterator const &rhs) const {
|
||||||
return std::get<0>(lhs._iters) == std::get<0>(rhs._iters);
|
return std::get<0>(_iters) == std::get<0>(rhs._iters);
|
||||||
}
|
|
||||||
|
|
||||||
friend auto operator!=(Zip const &lhs, Zip const &rhs) {
|
|
||||||
return std::get<0>(lhs._iters) != std::get<0>(rhs._iters);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail {
|
template<typename... Ts>
|
||||||
template<typename... Containers>
|
|
||||||
class ZipContainer {
|
class ZipContainer {
|
||||||
std::tuple<Containers...> _containers;
|
std::tuple<Ts...> _containers;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ZipContainer(Containers &&...containers)
|
explicit ZipContainer(Ts &&...containers) : _containers(std::forward<Ts>(containers)...) {}
|
||||||
: _containers(std::forward<Containers>(containers)...) {}
|
|
||||||
|
|
||||||
auto begin() {
|
auto begin() {
|
||||||
return Zip(std::apply(
|
return ZipIterator(std::apply(
|
||||||
[](auto &&...containers) {
|
[](auto &&...containers) {
|
||||||
using std::begin;
|
using std::begin;
|
||||||
return std::make_tuple(begin(containers)...);
|
return std::make_tuple(begin(containers)...);
|
||||||
@@ -91,7 +78,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto end() {
|
auto end() {
|
||||||
return Zip(std::apply(
|
return ZipIterator(std::apply(
|
||||||
[](auto &&...containers) {
|
[](auto &&...containers) {
|
||||||
using std::end;
|
using std::end;
|
||||||
return std::make_tuple(end(containers)...);
|
return std::make_tuple(end(containers)...);
|
||||||
@@ -105,12 +92,11 @@ public:
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
using Holder = std::
|
using Holder = std::
|
||||||
conditional_t<std::is_lvalue_reference_v<T>, T, std::remove_cv_t<std::remove_reference_t<T>>>;
|
conditional_t<std::is_lvalue_reference_v<T>, T, std::remove_cv_t<std::remove_reference_t<T>>>;
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
// Does the same number of iterations as the first container's iterator!
|
// Does the same number of iterations as the first container's iterator!
|
||||||
template<typename... Containers>
|
template<typename... Ts>
|
||||||
static constexpr auto zip(Containers &&...cs) {
|
static constexpr auto zip(Ts &&...cs) {
|
||||||
return detail::ZipContainer<detail::Holder<Containers>...>(std::forward<Containers>(cs)...);
|
return ZipContainer<Holder<Ts>...>(std::forward<Ts>(cs)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // RGBDS_ITERTOOLS_HPP
|
#endif // RGBDS_ITERTOOLS_HPP
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_LINK_ASSIGN_HPP
|
#ifndef RGBDS_LINK_ASSIGN_HPP
|
||||||
#define RGBDS_LINK_ASSIGN_HPP
|
#define RGBDS_LINK_ASSIGN_HPP
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_LINK_MAIN_HPP
|
#ifndef RGBDS_LINK_MAIN_HPP
|
||||||
#define RGBDS_LINK_MAIN_HPP
|
#define RGBDS_LINK_MAIN_HPP
|
||||||
@@ -32,8 +32,9 @@ extern bool disablePadding;
|
|||||||
// Helper macro for printing verbose-mode messages
|
// Helper macro for printing verbose-mode messages
|
||||||
#define verbosePrint(...) \
|
#define verbosePrint(...) \
|
||||||
do { \
|
do { \
|
||||||
if (beVerbose) \
|
if (beVerbose) { \
|
||||||
fprintf(stderr, __VA_ARGS__); \
|
fprintf(stderr, __VA_ARGS__); \
|
||||||
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
struct FileStackNode {
|
struct FileStackNode {
|
||||||
@@ -58,11 +59,11 @@ struct FileStackNode {
|
|||||||
std::string const &dump(uint32_t curLineNo) const;
|
std::string const &dump(uint32_t curLineNo) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
[[gnu::format(printf, 3, 4)]] void
|
[[gnu::format(printf, 3, 4)]]
|
||||||
warning(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...);
|
void warning(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...);
|
||||||
[[gnu::format(printf, 3, 4)]] void
|
[[gnu::format(printf, 3, 4)]]
|
||||||
error(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...);
|
void error(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...);
|
||||||
[[gnu::format(printf, 3, 4), noreturn]] void
|
[[gnu::format(printf, 3, 4), noreturn]]
|
||||||
fatal(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...);
|
void fatal(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...);
|
||||||
|
|
||||||
#endif // RGBDS_LINK_MAIN_HPP
|
#endif // RGBDS_LINK_MAIN_HPP
|
||||||
|
|||||||
@@ -1,19 +1,12 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_LINK_OBJECT_HPP
|
#ifndef RGBDS_LINK_OBJECT_HPP
|
||||||
#define RGBDS_LINK_OBJECT_HPP
|
#define RGBDS_LINK_OBJECT_HPP
|
||||||
|
|
||||||
/*
|
// Read an object (.o) file, and add its info to the data structures.
|
||||||
* Read an object (.o) file, and add its info to the data structures.
|
void obj_ReadFile(char const *fileName, unsigned int fileID);
|
||||||
* @param fileName A path to the object file to be read
|
|
||||||
* @param i The ID of the file
|
|
||||||
*/
|
|
||||||
void obj_ReadFile(char const *fileName, unsigned int i);
|
|
||||||
|
|
||||||
/*
|
// Sets up object file reading
|
||||||
* Sets up object file reading
|
|
||||||
* @param nbFiles The number of object files that will be read
|
|
||||||
*/
|
|
||||||
void obj_Setup(unsigned int nbFiles);
|
void obj_Setup(unsigned int nbFiles);
|
||||||
|
|
||||||
#endif // RGBDS_LINK_OBJECT_HPP
|
#endif // RGBDS_LINK_OBJECT_HPP
|
||||||
|
|||||||
@@ -1,26 +1,17 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_LINK_OUTPUT_HPP
|
#ifndef RGBDS_LINK_OUTPUT_HPP
|
||||||
#define RGBDS_LINK_OUTPUT_HPP
|
#define RGBDS_LINK_OUTPUT_HPP
|
||||||
|
|
||||||
struct Section;
|
struct Section;
|
||||||
|
|
||||||
/*
|
// Registers a section for output.
|
||||||
* Registers a section for output.
|
|
||||||
* @param section The section to add
|
|
||||||
*/
|
|
||||||
void out_AddSection(Section const §ion);
|
void out_AddSection(Section const §ion);
|
||||||
|
|
||||||
/*
|
// Finds an assigned section overlapping another one.
|
||||||
* Finds an assigned section overlapping another one.
|
|
||||||
* @param section The section that is being overlapped
|
|
||||||
* @return A section overlapping it
|
|
||||||
*/
|
|
||||||
Section const *out_OverlappingSection(Section const §ion);
|
Section const *out_OverlappingSection(Section const §ion);
|
||||||
|
|
||||||
/*
|
// Writes all output (bin, sym, map) files.
|
||||||
* Writes all output (bin, sym, map) files.
|
|
||||||
*/
|
|
||||||
void out_WriteFiles();
|
void out_WriteFiles();
|
||||||
|
|
||||||
#endif // RGBDS_LINK_OUTPUT_HPP
|
#endif // RGBDS_LINK_OUTPUT_HPP
|
||||||
|
|||||||
@@ -1,17 +1,12 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_LINK_PATCH_HPP
|
#ifndef RGBDS_LINK_PATCH_HPP
|
||||||
#define RGBDS_LINK_PATCH_HPP
|
#define RGBDS_LINK_PATCH_HPP
|
||||||
|
|
||||||
/*
|
// Checks all assertions
|
||||||
* Checks all assertions
|
|
||||||
* @return true if assertion failed
|
|
||||||
*/
|
|
||||||
void patch_CheckAssertions();
|
void patch_CheckAssertions();
|
||||||
|
|
||||||
/*
|
// Applies all SECTIONs' patches to them
|
||||||
* Applies all SECTIONs' patches to them
|
|
||||||
*/
|
|
||||||
void patch_ApplyPatches();
|
void patch_ApplyPatches();
|
||||||
|
|
||||||
#endif // RGBDS_LINK_PATCH_HPP
|
#endif // RGBDS_LINK_PATCH_HPP
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_LINK_SDAS_OBJ_HPP
|
#ifndef RGBDS_LINK_SDAS_OBJ_HPP
|
||||||
#define RGBDS_LINK_SDAS_OBJ_HPP
|
#define RGBDS_LINK_SDAS_OBJ_HPP
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_LINK_SECTION_HPP
|
#ifndef RGBDS_LINK_SECTION_HPP
|
||||||
#define RGBDS_LINK_SECTION_HPP
|
#define RGBDS_LINK_SECTION_HPP
|
||||||
@@ -65,29 +65,17 @@ struct Assertion {
|
|||||||
|
|
||||||
extern std::deque<Assertion> assertions;
|
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.
|
|
||||||
* @param callback The function to call for each structure.
|
|
||||||
*/
|
|
||||||
void sect_ForEach(void (*callback)(Section &));
|
void sect_ForEach(void (*callback)(Section &));
|
||||||
|
|
||||||
/*
|
// Registers a section to be processed.
|
||||||
* Registers a section to be processed.
|
|
||||||
* @param section The section to register.
|
|
||||||
*/
|
|
||||||
void sect_AddSection(std::unique_ptr<Section> &§ion);
|
void sect_AddSection(std::unique_ptr<Section> &§ion);
|
||||||
|
|
||||||
/*
|
// Finds a section by its name.
|
||||||
* Finds a section by its name.
|
|
||||||
* @param name The name of the section to look for
|
|
||||||
* @return A pointer to the section, or `nullptr` if it wasn't found
|
|
||||||
*/
|
|
||||||
Section *sect_GetSection(std::string const &name);
|
Section *sect_GetSection(std::string const &name);
|
||||||
|
|
||||||
/*
|
// Checks if all sections meet reasonable criteria, such as max size
|
||||||
* Checks if all sections meet reasonable criteria, such as max size
|
|
||||||
*/
|
|
||||||
void sect_DoSanityChecks();
|
void sect_DoSanityChecks();
|
||||||
|
|
||||||
#endif // RGBDS_LINK_SECTION_HPP
|
#endif // RGBDS_LINK_SECTION_HPP
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_LINK_SYMBOL_HPP
|
#ifndef RGBDS_LINK_SYMBOL_HPP
|
||||||
#define RGBDS_LINK_SYMBOL_HPP
|
#define RGBDS_LINK_SYMBOL_HPP
|
||||||
@@ -41,11 +41,7 @@ void sym_ForEach(void (*callback)(Symbol &));
|
|||||||
|
|
||||||
void sym_AddSymbol(Symbol &symbol);
|
void sym_AddSymbol(Symbol &symbol);
|
||||||
|
|
||||||
/*
|
// Finds a symbol in all the defined symbols.
|
||||||
* Finds a symbol in all the defined symbols.
|
|
||||||
* @param name The name of the symbol to look for
|
|
||||||
* @return A pointer to the symbol, or `nullptr` if not found.
|
|
||||||
*/
|
|
||||||
Symbol *sym_GetSymbol(std::string const &name);
|
Symbol *sym_GetSymbol(std::string const &name);
|
||||||
|
|
||||||
void sym_DumpLocalAliasedSymbols(std::string const &name);
|
void sym_DumpLocalAliasedSymbols(std::string const &name);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_LINKDEFS_HPP
|
#ifndef RGBDS_LINKDEFS_HPP
|
||||||
#define RGBDS_LINKDEFS_HPP
|
#define RGBDS_LINKDEFS_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 11U
|
#define RGBDS_OBJECT_REV 12U
|
||||||
|
|
||||||
enum AssertionType { ASSERT_WARN, ASSERT_ERROR, ASSERT_FATAL };
|
enum AssertionType { ASSERT_WARN, ASSERT_ERROR, ASSERT_FATAL };
|
||||||
|
|
||||||
@@ -52,6 +52,7 @@ enum RPNCommand {
|
|||||||
|
|
||||||
RPN_HRAM = 0x60,
|
RPN_HRAM = 0x60,
|
||||||
RPN_RST = 0x61,
|
RPN_RST = 0x61,
|
||||||
|
RPN_BIT_INDEX = 0x62,
|
||||||
|
|
||||||
RPN_HIGH = 0x70,
|
RPN_HIGH = 0x70,
|
||||||
RPN_LOW = 0x71,
|
RPN_LOW = 0x71,
|
||||||
@@ -92,29 +93,19 @@ extern struct SectionTypeInfo {
|
|||||||
uint32_t lastBank;
|
uint32_t lastBank;
|
||||||
} sectionTypeInfo[SECTTYPE_INVALID];
|
} sectionTypeInfo[SECTTYPE_INVALID];
|
||||||
|
|
||||||
/*
|
// 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.
|
|
||||||
* @param type The section's type
|
|
||||||
* @return `true` if the section's definition includes data
|
|
||||||
*/
|
|
||||||
static inline bool sect_HasData(SectionType type) {
|
static inline bool sect_HasData(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
|
||||||
* Computes a memory region's end address (last byte), eg. 0x7FFF
|
|
||||||
* @return The address of the last byte in that memory region
|
|
||||||
*/
|
|
||||||
static inline uint16_t endaddr(SectionType type) {
|
static inline uint16_t endaddr(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
|
||||||
* Computes a memory region's number of banks
|
|
||||||
* @return The number of banks, 1 for regions without banking
|
|
||||||
*/
|
|
||||||
static inline uint32_t nbbanks(SectionType type) {
|
static inline uint32_t nbbanks(SectionType type) {
|
||||||
return sectionTypeInfo[type].lastBank - sectionTypeInfo[type].firstBank + 1;
|
return sectionTypeInfo[type].lastBank - sectionTypeInfo[type].firstBank + 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_OP_MATH_HPP
|
#ifndef RGBDS_OP_MATH_HPP
|
||||||
#define RGBDS_OP_MATH_HPP
|
#define RGBDS_OP_MATH_HPP
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
// platform-specific hacks
|
// platform-specific hacks
|
||||||
|
|
||||||
@@ -56,4 +56,9 @@
|
|||||||
#define setmode(fd, mode) (0)
|
#define setmode(fd, mode) (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// MingGW and Cygwin need POSIX functions which are not standard C explicitly enabled
|
||||||
|
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // RGBDS_PLATFORM_HPP
|
#endif // RGBDS_PLATFORM_HPP
|
||||||
|
|||||||
@@ -1,17 +1,8 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_UTIL_HPP
|
#ifndef RGBDS_UTIL_HPP
|
||||||
#define RGBDS_UTIL_HPP
|
#define RGBDS_UTIL_HPP
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
char const *printChar(int c);
|
char const *printChar(int c);
|
||||||
|
|
||||||
/*
|
|
||||||
* @return The number of bytes read, or 0 if invalid data was found
|
|
||||||
*/
|
|
||||||
size_t readUTF8Char(std::vector<int32_t> *dest, char const *src);
|
|
||||||
|
|
||||||
#endif // RGBDS_UTIL_HPP
|
#endif // RGBDS_UTIL_HPP
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef RGBDS_VERSION_HPP
|
#ifndef RGBDS_VERSION_HPP
|
||||||
#define RGBDS_VERSION_HPP
|
#define RGBDS_VERSION_HPP
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
#define PACKAGE_VERSION_MAJOR 0
|
#define PACKAGE_VERSION_MAJOR 0
|
||||||
#define PACKAGE_VERSION_MINOR 9
|
#define PACKAGE_VERSION_MINOR 9
|
||||||
#define PACKAGE_VERSION_PATCH 0
|
#define PACKAGE_VERSION_PATCH 2
|
||||||
#define PACKAGE_VERSION_RC 1
|
|
||||||
|
|
||||||
char const *get_package_version_string();
|
char const *get_package_version_string();
|
||||||
}
|
|
||||||
|
|
||||||
#endif // RGBDS_VERSION_H
|
#endif // RGBDS_VERSION_H
|
||||||
|
|||||||
607
man/gbz80.7
607
man/gbz80.7
File diff suppressed because it is too large
Load Diff
445
man/rgbasm-old.5
Normal file
445
man/rgbasm-old.5
Normal file
@@ -0,0 +1,445 @@
|
|||||||
|
'\" e
|
||||||
|
.\"
|
||||||
|
.\" SPDX-License-Identifier: MIT
|
||||||
|
.\"
|
||||||
|
.Dd May 4, 2025
|
||||||
|
.Dt RGBASM-OLD 5
|
||||||
|
.Os
|
||||||
|
.Sh NAME
|
||||||
|
.Nm rgbasm-old
|
||||||
|
.Nd obsolete language documentation
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
This is the list of features that have been removed from the
|
||||||
|
.Xr rgbasm 5
|
||||||
|
assembly language over its decades of evolution, along with their modern alternatives.
|
||||||
|
Its goal is to be a reference for backwards incompatibility, when upgrading an old assembly codebase to work with the latest RGBDS release.
|
||||||
|
It does
|
||||||
|
.Em not
|
||||||
|
attempt to list every syntax bug that was ever fixed (with some notable exceptions), nor new reserved keywords that may conflict with old identifiers.
|
||||||
|
.Sh REMOVED
|
||||||
|
These are features which have been completely removed, without any direct alternatives.
|
||||||
|
Usually these features were limiting the addition of other features, or had awkward limits on their own intended effects.
|
||||||
|
.Ss Automatic LD to LDH conversion (rgbasm -l)
|
||||||
|
Deprecated in 0.7.0, removed in 0.8.0.
|
||||||
|
.Pp
|
||||||
|
.Xr rgbasm 1
|
||||||
|
used to automatically treat
|
||||||
|
.Ql LD
|
||||||
|
as
|
||||||
|
.Ql LDH
|
||||||
|
if the address was known to be in the
|
||||||
|
.Ad $FF00-$FFFF
|
||||||
|
range, with the
|
||||||
|
.Fl L
|
||||||
|
flag to opt out.
|
||||||
|
.Xr rgbasm 1
|
||||||
|
0.6.0 added a
|
||||||
|
.Fl l
|
||||||
|
flag to opt in instead.
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Ql LDH ,
|
||||||
|
and remove the
|
||||||
|
.Fl L
|
||||||
|
and
|
||||||
|
.Fl l
|
||||||
|
flags from
|
||||||
|
.Xr rgbasm 1 .
|
||||||
|
.Ss Automatic NOP after HALT (rgbasm -H)
|
||||||
|
Deprecated in 0.7.0, removed in 0.8.0.
|
||||||
|
.Pp
|
||||||
|
.Xr rgbasm 1
|
||||||
|
used to automatically insert a
|
||||||
|
.Ql NOP
|
||||||
|
after
|
||||||
|
.Ql HALT ,
|
||||||
|
with the
|
||||||
|
.Fl h
|
||||||
|
flag to opt out.
|
||||||
|
.Xr rgbasm 1
|
||||||
|
0.6.0 added a
|
||||||
|
.Fl H
|
||||||
|
flag to opt in instead.
|
||||||
|
.Pp
|
||||||
|
Instead, use an explicit
|
||||||
|
.Ql NOP
|
||||||
|
after
|
||||||
|
.Ql HALT ,
|
||||||
|
and remove the
|
||||||
|
.Fl h
|
||||||
|
and
|
||||||
|
.Fl H
|
||||||
|
flags from
|
||||||
|
.Xr rgbasm 1 .
|
||||||
|
.Ss Nested macro definitions
|
||||||
|
Removed in 0.4.2.
|
||||||
|
.Pp
|
||||||
|
Instead, put the nested macro definition inside a quoted string (making sure that none of its lines start with
|
||||||
|
.Ic ENDM ) ,
|
||||||
|
then interpolate that string.
|
||||||
|
For example:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
MACRO outer
|
||||||
|
DEF definition EQUS """
|
||||||
|
MACRO inner
|
||||||
|
println (\e1) - (\e\e1)
|
||||||
|
\enENDM"""
|
||||||
|
{definition}
|
||||||
|
PURGE definition
|
||||||
|
ENDM
|
||||||
|
outer 10
|
||||||
|
inner 3 ; prints 7
|
||||||
|
.Ed
|
||||||
|
.Ss Negative DS
|
||||||
|
Removed in 0.3.2.
|
||||||
|
.Pp
|
||||||
|
This was used to "rewind" the value of
|
||||||
|
.Ic @
|
||||||
|
in RAM sections, allowing labeled space allocations to overlap.
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Ic UNION .
|
||||||
|
.Ss __FILE__ and __LINE__
|
||||||
|
Deprecated in 0.6.0, removed in 0.7.0.
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Ic WARN
|
||||||
|
or
|
||||||
|
.Ic FAIL
|
||||||
|
to print a complete trace of filenames and line numbers.
|
||||||
|
.Ss _PI
|
||||||
|
Deprecated in 0.5.0, removed in 0.6.0.
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Ql 3.141592653 .
|
||||||
|
.Ss Treating multi-character strings as numbers
|
||||||
|
Deprecated in 0.9.0.
|
||||||
|
.Pp
|
||||||
|
Instead, use a multi-value
|
||||||
|
.Ic CHARMAP ,
|
||||||
|
or explicitly combine the values of individual characters.
|
||||||
|
.Ss rgbgfx -f/--fix and -F/--fix-and-save
|
||||||
|
Removed in 0.6.0.
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Ql rgbgfx -c/--colors
|
||||||
|
to explicitly specify a color palette.
|
||||||
|
If using
|
||||||
|
.Ql -c embedded ,
|
||||||
|
arrange the PNG's indexed palette in a separate graphics editor.
|
||||||
|
.Ss rgbgfx -D/--debug
|
||||||
|
Removed in 0.6.0.
|
||||||
|
.Sh REPLACED
|
||||||
|
These are features whose syntax has been changed without affecting functionality.
|
||||||
|
They can generally be updated with a single search-and-replace.
|
||||||
|
.Ss Defining constants and variables without DEF
|
||||||
|
Deprecated in 0.7.0, removed in 0.8.0.
|
||||||
|
.Pp
|
||||||
|
.Ic EQU , EQUS , = , RB , RW ,
|
||||||
|
and
|
||||||
|
.Ic RL
|
||||||
|
definitions used to just start with the symbol name, but had to be typed in column 1.
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Ic DEF
|
||||||
|
before constant and variable definitions.
|
||||||
|
Note that
|
||||||
|
.Ic EQUS
|
||||||
|
expansion does not occur for the symbol name, so you have to use explicit
|
||||||
|
.Ql {interpolation} .
|
||||||
|
.Ss Defining macros like labels
|
||||||
|
Deprecated in 0.6.0, removed in 0.7.0.
|
||||||
|
.Pp
|
||||||
|
Macros used to be defined as
|
||||||
|
.Ql name: MACRO .
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Ql MACRO name .
|
||||||
|
Note that
|
||||||
|
.Ic EQUS
|
||||||
|
expansion does not occur for the macro name, so you have to use explicit
|
||||||
|
.Ql {interpolation} .
|
||||||
|
.Ss Defining variables with SET
|
||||||
|
Deprecated in 0.5.2, removed in 0.6.0.
|
||||||
|
.Pp
|
||||||
|
Variables used to be defined as
|
||||||
|
.Ql name SET value .
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Ql DEF name = value .
|
||||||
|
.Ss Global labels without colons
|
||||||
|
Deprecated in 0.4.0, removed in 0.5.0.
|
||||||
|
.Pp
|
||||||
|
Labels used to be definable with just a name, but had to be typed in column 1.
|
||||||
|
.Pp
|
||||||
|
Instead, use explicit colons; for example,
|
||||||
|
.Ql Label:
|
||||||
|
or exported
|
||||||
|
.Ql Label:: .
|
||||||
|
.Ss '\e,' in strings within macro arguments
|
||||||
|
Deprecated in 0.5.0, removed in 0.7.0.
|
||||||
|
.Pp
|
||||||
|
Macro arguments now handle quoted strings and parenthesized expressions as single arguments, so commas inside them are not argument separators and do not need escaping.
|
||||||
|
.Pp
|
||||||
|
Instead, just use commas without backslashes.
|
||||||
|
.Ss '*' comments
|
||||||
|
Deprecated in 0.4.1, removed in 0.5.0.
|
||||||
|
.Pp
|
||||||
|
These comments had to have the
|
||||||
|
.Ql *
|
||||||
|
typed in column 1.
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Ql \&;
|
||||||
|
comments.
|
||||||
|
.Ss PRINTT, PRINTI, PRINTV, and PRINTF
|
||||||
|
Deprecated in 0.5.0, removed in 0.6.0.
|
||||||
|
.Pp
|
||||||
|
These directives were each specific to one type of value.
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Ic PRINT
|
||||||
|
and
|
||||||
|
.Ic PRINTLN ,
|
||||||
|
with
|
||||||
|
.Ic STRFMT
|
||||||
|
or
|
||||||
|
.Ql {interpolation}
|
||||||
|
for type-specific formatting.
|
||||||
|
.Ss IMPORT and XREF
|
||||||
|
Removed in 0.4.0.
|
||||||
|
.Pp
|
||||||
|
Symbols are now automatically resolved if they were exported from elsewhere.
|
||||||
|
.Pp
|
||||||
|
Instead, just remove these directives.
|
||||||
|
.Ss GLOBAL and XDEF
|
||||||
|
Deprecated in 0.4.2, removed in 0.5.0.
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Ic EXPORT .
|
||||||
|
.Ss HOME, CODE, DATA, and BSS
|
||||||
|
Deprecated in 0.3.0, removed in 0.4.0.
|
||||||
|
.Pp
|
||||||
|
Instead of
|
||||||
|
.Ic HOME ,
|
||||||
|
use
|
||||||
|
.Ic ROM0 ;
|
||||||
|
instead of
|
||||||
|
.Ic CODE
|
||||||
|
and
|
||||||
|
.Ic DATA ,
|
||||||
|
use
|
||||||
|
.Ic ROMX ;
|
||||||
|
instead of
|
||||||
|
.Ic BSS ,
|
||||||
|
use
|
||||||
|
.Ic WRAM0 .
|
||||||
|
.Ss JP [HL]
|
||||||
|
Deprecated in 0.3.0, removed in 0.4.0.
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Ql JP HL .
|
||||||
|
.Ss LDI A, HL and LDD A, HL
|
||||||
|
Deprecated in 0.3.0, removed in 0.4.0.
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Ql LDI A, [HL]
|
||||||
|
and
|
||||||
|
.Ql LDD A, [HL]
|
||||||
|
(or
|
||||||
|
.Ql LD A, [HLI]
|
||||||
|
and
|
||||||
|
.Ql LD A, [HLD] ;
|
||||||
|
or
|
||||||
|
.Ql LD A, [HL+]
|
||||||
|
and
|
||||||
|
.Ql LD A, [HL-] ) .
|
||||||
|
.Ss LDIO
|
||||||
|
Deprecated in 0.9.0.
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Ql LDH .
|
||||||
|
.Ss LD [C], A and LD A, [C]
|
||||||
|
Deprecated in 0.9.0.
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Ql LDH [C], A
|
||||||
|
and
|
||||||
|
.Ql LDH A, [C] .
|
||||||
|
.Pp
|
||||||
|
Note that
|
||||||
|
.Ql LD [$FF00+C], A
|
||||||
|
and
|
||||||
|
.Ql LD A, [$FF00+C]
|
||||||
|
were also deprecated in 0.9.0, but were
|
||||||
|
.Em undeprecated
|
||||||
|
in 0.9.1.
|
||||||
|
.Ss LDH [n8], A and LDH A, [n8]
|
||||||
|
Deprecated in 0.9.0.
|
||||||
|
.Pp
|
||||||
|
.Ql LDH
|
||||||
|
used to treat "addresses" from
|
||||||
|
.Ad $00
|
||||||
|
to
|
||||||
|
.Ad $FF
|
||||||
|
as if they were the low byte of an address from
|
||||||
|
.Ad $FF00
|
||||||
|
to
|
||||||
|
.Ad $FFFF .
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Ql LDH [n16], A
|
||||||
|
and
|
||||||
|
.Ql LDH A, [n16] .
|
||||||
|
.Ss LD HL, [SP + e8]
|
||||||
|
Deprecated in 0.3.0, removed in 0.4.0.
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Ql LD HL, SP + e8 .
|
||||||
|
.Ss LDHL SP, e8
|
||||||
|
Supported in ASMotor, removed in RGBDS.
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Ql LD HL, SP + e8 .
|
||||||
|
.Ss rgbasm -i
|
||||||
|
Deprecated in 0.6.0, removed in 0.8.0.
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Fl I
|
||||||
|
or
|
||||||
|
.Fl -include .
|
||||||
|
.Ss rgbgfx -h
|
||||||
|
Removed in 0.6.0.
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Fl Z
|
||||||
|
or
|
||||||
|
.Fl -columns .
|
||||||
|
.Ss rgbgfx --output-*
|
||||||
|
Deprecated in 0.7.0, removed in 0.8.0.
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Fl -auto-* .
|
||||||
|
.Sh CHANGED
|
||||||
|
These are breaking changes that did not alter syntax, and so could not practically be deprecated.
|
||||||
|
.Ss Trigonometry function units
|
||||||
|
Changed in 0.6.0.
|
||||||
|
.Pp
|
||||||
|
Instead of dividing a circle into 65536.0 "binary degrees", it is now divided into 1.0 "turns".
|
||||||
|
.Pp
|
||||||
|
For example, previously we had:
|
||||||
|
.EQ
|
||||||
|
delim $$
|
||||||
|
.EN
|
||||||
|
.Bl -bullet -offset indent
|
||||||
|
.It
|
||||||
|
.Ql SIN(0.25) == 0.00002 ,
|
||||||
|
because 0.25 binary degrees = $0.25 / 65536.0$ turns = $0.000004 tau$ radians = $0.000008 pi$ radians, and $sin ( 0.000008 pi ) = 0.00002$
|
||||||
|
.It
|
||||||
|
.Ql SIN(16384.0) == 1.0 ,
|
||||||
|
because 16384.0 binary degrees = $16384.0 / 65536.0$ turns = $0.25 tau$ radians = $pi / 2$ radians, and $sin ( pi / 2 ) = 1$
|
||||||
|
.It
|
||||||
|
.Ql ASIN(1.0) == 16384.0
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
Instead, now we have:
|
||||||
|
.Bl -bullet -offset indent
|
||||||
|
.It
|
||||||
|
.Ql SIN(0.25) == 1.0 ,
|
||||||
|
because $0.25$ turns = $0.25 tau$ radians = $pi / 2$ radians, and $sin ( pi / 2 ) = 1$
|
||||||
|
.It
|
||||||
|
.Ql SIN(16384.0) == 0.0 ,
|
||||||
|
because $16384$ turns = $16384 tau$ radians = $32768 pi$ radians, and $sin ( 32768 pi ) = 0$
|
||||||
|
.It
|
||||||
|
.Ql ASIN(1.0) == 0.25
|
||||||
|
.El
|
||||||
|
.EQ
|
||||||
|
delim off
|
||||||
|
.EN
|
||||||
|
.Ss ** operator associativity
|
||||||
|
Changed in 0.9.0.
|
||||||
|
.Pp
|
||||||
|
Instead of being left-associative,
|
||||||
|
.Ql **
|
||||||
|
is now right-associative.
|
||||||
|
.Pp
|
||||||
|
Previously we had
|
||||||
|
.Ql p ** q ** r == (p ** q) ** r .
|
||||||
|
.Pp
|
||||||
|
Instead, now we have
|
||||||
|
.Ql p ** q ** r == p ** (q ** r) .
|
||||||
|
.Sh BUGS
|
||||||
|
These are misfeatures that may have been possible by mistake.
|
||||||
|
They do not get deprecated, just fixed.
|
||||||
|
.Ss Space between exported labels' colons
|
||||||
|
Fixed in 0.7.0.
|
||||||
|
.Pp
|
||||||
|
Labels with two colons used to ignore a space between them; for example,
|
||||||
|
.Ql Label:\ : .
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Ql Label:: .
|
||||||
|
.Ss Space between label and colon
|
||||||
|
Fixed in 0.9.0.
|
||||||
|
.Pp
|
||||||
|
Space between a label and its colon(s) used to be ignored; for example,
|
||||||
|
.Ql Label\ :
|
||||||
|
and
|
||||||
|
.Ql Label\ :: .
|
||||||
|
Now they are treated as invocations of the
|
||||||
|
.Ql Label
|
||||||
|
macro with
|
||||||
|
.Ql \&:
|
||||||
|
and
|
||||||
|
.Ql ::
|
||||||
|
as arguments.
|
||||||
|
.Pp
|
||||||
|
Instead, use
|
||||||
|
.Ql Label:
|
||||||
|
and
|
||||||
|
.Ql Label:: .
|
||||||
|
.Ss ADD r16 with implicit first HL operand
|
||||||
|
Fixed in 0.5.0.
|
||||||
|
.Pp
|
||||||
|
For example,
|
||||||
|
.Ql ADD BC
|
||||||
|
used to be treated as
|
||||||
|
.Ql ADD HL, BC ,
|
||||||
|
and likewise for
|
||||||
|
.Ql DE ,
|
||||||
|
.Ql HL ,
|
||||||
|
and
|
||||||
|
.Ql SP .
|
||||||
|
.Pp
|
||||||
|
Instead, use an explicit first
|
||||||
|
.Ql HL
|
||||||
|
operand.
|
||||||
|
.Ss = instead of SET
|
||||||
|
Fixed in 0.4.0.
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Ic =
|
||||||
|
operator used to be an alias for the
|
||||||
|
.Ic SET
|
||||||
|
keyword, which included using
|
||||||
|
.Ic =
|
||||||
|
for the
|
||||||
|
.Ic SET
|
||||||
|
.Em instruction .
|
||||||
|
.Pp
|
||||||
|
Instead, just use
|
||||||
|
.Ic SET
|
||||||
|
for the instruction.
|
||||||
|
.Sh SEE ALSO
|
||||||
|
.Xr rgbasm 1 ,
|
||||||
|
.Xr gbz80 7 ,
|
||||||
|
.Xr rgbds 5 ,
|
||||||
|
.Xr rgbds 7
|
||||||
|
.Sh HISTORY
|
||||||
|
.Xr rgbasm 1
|
||||||
|
was originally written by
|
||||||
|
.An Carsten S\(/orensen
|
||||||
|
as part of the ASMotor package, and was later repackaged in RGBDS by
|
||||||
|
.An Justin Lloyd .
|
||||||
|
It is now maintained by a number of contributors at
|
||||||
|
.Lk https://github.com/gbdev/rgbds .
|
||||||
52
man/rgbasm.1
52
man/rgbasm.1
@@ -1,6 +1,6 @@
|
|||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd September 18, 2024
|
.Dd May 4, 2025
|
||||||
.Dt RGBASM 1
|
.Dt RGBASM 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
.Nd Game Boy assembler
|
.Nd Game Boy assembler
|
||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm
|
.Nm
|
||||||
.Op Fl EVvw
|
.Op Fl EhVvw
|
||||||
.Op Fl b Ar chars
|
.Op Fl b Ar chars
|
||||||
.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
|
||||||
@@ -67,6 +67,8 @@ 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.
|
Change the four characters used for gfx constants.
|
||||||
The defaults are 0123.
|
The defaults are 0123.
|
||||||
|
.It Fl h , Fl \-help
|
||||||
|
Print help text for the program and exit.
|
||||||
.It Fl I Ar path , Fl \-include Ar path
|
.It Fl I Ar path , Fl \-include Ar path
|
||||||
Add a new
|
Add a new
|
||||||
.Dq include path ;
|
.Dq include path ;
|
||||||
@@ -200,7 +202,7 @@ section for a list of warnings.
|
|||||||
Disable all warning output, even when turned into errors.
|
Disable all warning output, even when turned into errors.
|
||||||
.It Fl X Ar max_errors , Fl \-max-errors Ar max_errors
|
.It Fl X Ar max_errors , Fl \-max-errors Ar max_errors
|
||||||
If more than this number of errors (not warnings) occur, then abort the assembly process;
|
If more than this number of errors (not warnings) occur, then abort the assembly process;
|
||||||
.Fl X 0
|
.Fl X Ar 0
|
||||||
disables this behavior.
|
disables this behavior.
|
||||||
The default is 100 if
|
The default is 100 if
|
||||||
.Nm
|
.Nm
|
||||||
@@ -212,13 +214,19 @@ The following options alter the way warnings are processed.
|
|||||||
.Bl -tag -width Ds
|
.Bl -tag -width Ds
|
||||||
.It Fl Werror
|
.It Fl Werror
|
||||||
Make all warnings into errors.
|
Make all warnings into errors.
|
||||||
|
This can be negated as
|
||||||
|
.Fl Wno-error
|
||||||
|
to prevent turning all warnings into errors.
|
||||||
.It Fl Werror=
|
.It Fl Werror=
|
||||||
Make the specified warning into an error.
|
Make the specified warning or meta warning into an error.
|
||||||
A warning's name is appended
|
A warning's name is appended
|
||||||
.Pq example: Fl Werror=obsolete ,
|
.Pq example: Fl Werror=obsolete ,
|
||||||
and this warning is implicitly enabled and turned into an error.
|
and this warning is implicitly enabled and turned into an error.
|
||||||
This is an error if used with a meta warning, such as
|
This can be negated as
|
||||||
.Fl Werror=all .
|
.Fl Wno-error=
|
||||||
|
to prevent turning a specified warning into an error, even if
|
||||||
|
.Fl Werror
|
||||||
|
is in effect.
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
The following warnings are
|
The following warnings are
|
||||||
@@ -240,6 +248,10 @@ Note that each of these flag 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
|
||||||
|
disables; and
|
||||||
|
.Fl Wall
|
||||||
|
enables every warning that
|
||||||
|
.Fl Wno-all
|
||||||
disables).
|
disables).
|
||||||
Only the non-default flag is listed here.
|
Only the non-default flag is listed here.
|
||||||
Ignoring the
|
Ignoring the
|
||||||
@@ -291,6 +303,13 @@ This warning is enabled by
|
|||||||
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
|
||||||
.Fl Wextra .
|
.Fl Wextra .
|
||||||
|
.It Fl Wno-nested-comment
|
||||||
|
Warn when the block comment start sequence
|
||||||
|
.Ql /*
|
||||||
|
is found inside of a block comment.
|
||||||
|
Block comments cannot be nested, so the first
|
||||||
|
.Ql */
|
||||||
|
will end the whole comment.
|
||||||
.It Fl Wno-obsolete
|
.It Fl Wno-obsolete
|
||||||
Warn when obsolete constructs such as the
|
Warn when obsolete constructs such as the
|
||||||
.Ic _PI
|
.Ic _PI
|
||||||
@@ -344,7 +363,7 @@ also warns when an N-bit value is less than -2**(N-1), which will not fit in two
|
|||||||
Warn when a character goes through charmap conversion but has no defined mapping.
|
Warn when a character goes through charmap conversion but has no defined mapping.
|
||||||
.Fl Wunmapped-char=0
|
.Fl Wunmapped-char=0
|
||||||
or
|
or
|
||||||
.Fl Wunmapped-char
|
.Fl Wno-unmapped-char
|
||||||
disables this warning.
|
disables this warning.
|
||||||
.Fl Wunmapped-char=1
|
.Fl Wunmapped-char=1
|
||||||
or just
|
or just
|
||||||
@@ -353,6 +372,24 @@ only warns if the active charmap is not empty.
|
|||||||
.Fl Wunmapped-char=2
|
.Fl Wunmapped-char=2
|
||||||
warns if the active charmap is empty, and/or is not the default charmap
|
warns if the active charmap is empty, and/or is not the default charmap
|
||||||
.Sq main .
|
.Sq main .
|
||||||
|
.It Fl Wunmatched-directive
|
||||||
|
Warn when a
|
||||||
|
.Ic PUSHC , PUSHO ,
|
||||||
|
or
|
||||||
|
.Ic PUSHS
|
||||||
|
directive does not have a corresponding
|
||||||
|
.Ic POPC , POPO ,
|
||||||
|
or
|
||||||
|
.Ic POPS .
|
||||||
|
This warning is enabled by
|
||||||
|
.Fl Wextra .
|
||||||
|
.It Fl Wunterminated-load
|
||||||
|
Warn when a
|
||||||
|
.Ic LOAD
|
||||||
|
block is not terminated by an
|
||||||
|
.Ic ENDL .
|
||||||
|
This warning is enabled by
|
||||||
|
.Fl Wextra .
|
||||||
.It Fl Wno-user
|
.It Fl Wno-user
|
||||||
Warn when the
|
Warn when the
|
||||||
.Ic WARN
|
.Ic WARN
|
||||||
@@ -392,6 +429,7 @@ Please report bugs on
|
|||||||
.Xr rgbfix 1 ,
|
.Xr rgbfix 1 ,
|
||||||
.Xr rgbgfx 1 ,
|
.Xr rgbgfx 1 ,
|
||||||
.Xr gbz80 7 ,
|
.Xr gbz80 7 ,
|
||||||
|
.Xr rgbasm-old 5 ,
|
||||||
.Xr rgbds 5 ,
|
.Xr rgbds 5 ,
|
||||||
.Xr rgbds 7
|
.Xr rgbds 7
|
||||||
.Sh HISTORY
|
.Sh HISTORY
|
||||||
|
|||||||
299
man/rgbasm.5
299
man/rgbasm.5
@@ -2,7 +2,7 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd September 18, 2024
|
.Dd May 4, 2025
|
||||||
.Dt RGBASM 5
|
.Dt RGBASM 5
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -41,7 +41,6 @@ or
|
|||||||
Labels tie a name to a specific location within a section (see
|
Labels tie a name to a specific location within a section (see
|
||||||
.Sx Labels
|
.Sx Labels
|
||||||
below).
|
below).
|
||||||
They must come first in the line.
|
|
||||||
.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 can be separated by double colons
|
||||||
@@ -254,7 +253,7 @@ Although, for these examples,
|
|||||||
.Ic STRFMT
|
.Ic STRFMT
|
||||||
would be more appropriate; see
|
would be more appropriate; see
|
||||||
.Sx String expressions
|
.Sx String expressions
|
||||||
further below.
|
below.
|
||||||
.Sh EXPRESSIONS
|
.Sh EXPRESSIONS
|
||||||
An expression can be composed of many things.
|
An expression can be composed of many things.
|
||||||
Numeric expressions are always evaluated using signed 32-bit math.
|
Numeric expressions are always evaluated using signed 32-bit math.
|
||||||
@@ -268,21 +267,21 @@ This is generally always the case, unless a label is involved, as explained in t
|
|||||||
section.
|
section.
|
||||||
However, some operators can be constant even with non-constant operands, as explained in
|
However, some operators can be constant even with non-constant operands, as explained in
|
||||||
.Sx Operators
|
.Sx Operators
|
||||||
further below.
|
below.
|
||||||
.Pp
|
.Pp
|
||||||
The instructions in the macro-language generally require constant expressions.
|
The instructions in the macro-language generally require constant expressions.
|
||||||
.Ss Numeric formats
|
.Ss Numeric formats
|
||||||
There are a number of numeric formats.
|
There are a number of numeric formats.
|
||||||
.Bl -column -offset indent "Precise fixed-point" "Prefix"
|
.Bl -column -offset indent "Precise fixed-point" "Possible prefixes"
|
||||||
.It Sy Format type Ta Sy Prefix Ta Sy Accepted characters
|
.It Sy Format type Ta Sy Possible prefixes Ta Sy Accepted characters
|
||||||
.It Hexadecimal Ta $ Ta 0123456789ABCDEF
|
|
||||||
.It Decimal Ta none Ta 0123456789
|
.It Decimal Ta none Ta 0123456789
|
||||||
.It Octal Ta & Ta 01234567
|
.It Hexadecimal Ta Li $ , 0x , 0X Ta 0123456789ABCDEF
|
||||||
.It Binary Ta % Ta 01
|
.It Octal Ta Li & , 0o , 0O Ta 01234567
|
||||||
|
.It Binary Ta Li % , 0b , 0B Ta 01
|
||||||
.It Fixed-point Ta none Ta 01234.56789
|
.It 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 \(dqABYZ\(dq
|
||||||
.It Game Boy graphics Ta \` Ta 0123
|
.It Game Boy graphics Ta Li \` Ta 0123
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
Underscores are also accepted in numbers, except at the beginning of one.
|
Underscores are also accepted in numbers, except at the beginning of one.
|
||||||
@@ -310,26 +309,35 @@ is equivalent to
|
|||||||
.Pp
|
.Pp
|
||||||
You can also use symbols, which are implicitly replaced with their value.
|
You can also use symbols, which are implicitly replaced with their value.
|
||||||
.Ss Operators
|
.Ss Operators
|
||||||
A great number of operators you can use in expressions are available (listed from highest to lowest precedence):
|
You can use these operators in numeric expressions (listed from highest to lowest precedence):
|
||||||
.Bl -column -offset indent "!= == <= >= < >"
|
.Bl -column -offset indent "!= == <= >= < >"
|
||||||
.It Sy Operator Ta Sy Meaning
|
.It Sy Operator Ta Sy Meaning
|
||||||
.It Li \&( \&) Ta Precedence override
|
.It Li \&( \&) Ta Grouping
|
||||||
.It Li FUNC() Ta Built-in function call
|
.It Li FUNC() Ta Built-in function call
|
||||||
.It Li ** Ta Exponent
|
.It Li ** Ta Exponentiation
|
||||||
.It Li ~ + - Ta Unary complement/plus/minus
|
.It Li + - ~ \&! Ta Unary plus, minus (negation), complement (bitwise negation), and Boolean negation
|
||||||
.It Li * / % Ta Multiply/divide/modulo
|
.It Li * / % Ta Multiplication, division, and modulo (remainder)
|
||||||
.It Li << Ta Shift left
|
.It Li << >> >>> Ta Bit shifts (left, sign-extended right, zero-extended right)
|
||||||
.It Li >> Ta Signed shift right (sign-extension)
|
.It Li & \&| ^ Ta Bitwise AND/OR/XOR
|
||||||
.It Li >>> Ta Unsigned shift right (zero-extension)
|
.It Li + - Ta Addition and subtraction
|
||||||
.It Li & \&| ^ Ta Binary and/or/xor
|
.It Li == != < > <= >= Ta Comparisons
|
||||||
.It Li + - Ta Add/subtract
|
.It Li && Ta Boolean AND
|
||||||
.It Li != == <= >= < > Ta Comparison
|
.It Li || Ta Boolean OR
|
||||||
.It Li && || Ta Boolean and/or
|
|
||||||
.It Li \&! Ta Unary not
|
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
|
.Sq **
|
||||||
|
raises a number to a non-negative power. It is the only
|
||||||
|
.Em right-associative
|
||||||
|
operator, meaning that
|
||||||
|
.Ql p ** q ** r
|
||||||
|
is equal to
|
||||||
|
.Ql p ** (q ** r) ,
|
||||||
|
not
|
||||||
|
.Ql (p ** q) ** r .
|
||||||
|
All other binary operators are left-associative.
|
||||||
|
.Pp
|
||||||
.Sq ~
|
.Sq ~
|
||||||
complements a value by inverting all its bits.
|
complements a value by inverting all 32 of its bits.
|
||||||
.Pp
|
.Pp
|
||||||
.Sq %
|
.Sq %
|
||||||
is used to get the remainder of the corresponding division, so that
|
is used to get the remainder of the corresponding division, so that
|
||||||
@@ -379,8 +387,8 @@ Even a non-constant operand with any non-zero bits will return 0.
|
|||||||
Besides operators, there are also some functions which have more specialized uses.
|
Besides operators, there are also some functions which have more specialized uses.
|
||||||
.Bl -column "BITWIDTH(n)"
|
.Bl -column "BITWIDTH(n)"
|
||||||
.It Sy Name Ta Sy Operation
|
.It Sy Name Ta Sy Operation
|
||||||
.It Fn HIGH n Ta Equivalent to Ql Ar n No & $FF .
|
.It Fn HIGH n Ta Equivalent to Ql Po Ns Ar n No & $FF00 Pc >> 8 .
|
||||||
.It Fn LOW n Ta Equivalent to Ql Po Ns Ar n No & $FF00 Pc >> 8 .
|
.It Fn LOW n Ta Equivalent to Ql Ar n No & $FF .
|
||||||
.EQ
|
.EQ
|
||||||
delim $$
|
delim $$
|
||||||
.EN
|
.EN
|
||||||
@@ -556,22 +564,17 @@ is equivalent to the regular string
|
|||||||
(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
|
||||||
The following functions operate on string expressions.
|
The following functions operate on string expressions, and return strings themselves.
|
||||||
Most of them return a string, however some of these functions actually return an integer and can be used as part of an integer expression!
|
.Bl -column "STRSLICE(str, start, stop)"
|
||||||
.Bl -column "STRSUB(str, pos, len)"
|
|
||||||
.It Sy Name Ta Sy Operation
|
.It Sy Name Ta Sy Operation
|
||||||
.It Fn STRLEN str Ta Returns the number of characters in Ar str .
|
|
||||||
.It Fn STRCAT strs... Ta Concatenates Ar strs .
|
.It Fn STRCAT strs... Ta Concatenates Ar strs .
|
||||||
.It Fn STRCMP str1 str2 Ta Returns -1 if Ar str1 No is alphabetically lower than Ar str2 No , zero if they match, 1 if Ar str1 No is greater than Ar str2 .
|
|
||||||
.It Fn STRIN str1 str2 Ta Returns the first position of Ar str2 No in Ar str1 No or zero if it's not present Pq first character is position 1 .
|
|
||||||
.It Fn STRRIN str1 str2 Ta Returns the last position of Ar str2 No in Ar str1 No or zero if it's not present Pq first character is position 1 .
|
|
||||||
.It Fn STRSUB str pos len Ta Returns a substring from Ar str No starting at Ar pos No (first character is position 1, last is position -1) and Ar len No characters long. If Ar len No is not specified the substring continues to the end of Ar str .
|
|
||||||
.It Fn STRUPR str Ta Returns Ar str No with all ASCII letters
|
.It Fn STRUPR str Ta Returns Ar str No with all ASCII letters
|
||||||
.Pq Ql a-z
|
.Pq Ql a-z
|
||||||
in uppercase.
|
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 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
|
||||||
@@ -581,9 +584,35 @@ pattern replaced by interpolating the format
|
|||||||
with its corresponding argument in
|
with its corresponding argument in
|
||||||
.Ar args
|
.Ar args
|
||||||
.Pq So %% Sc is replaced by the So % Sc character .
|
.Pq So %% Sc is replaced by the So % Sc character .
|
||||||
.It Fn INCHARMAP str Ta Returns 1 if Ar str No has an entry in the current charmap, and 0 otherwise .
|
.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.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
The following functions operate on string expressions, but return integers.
|
||||||
|
.Bl -column "STRRFIND(str, sub)"
|
||||||
|
.It Sy Name Ta Sy Operation
|
||||||
|
.It Fn STRLEN str Ta Returns the number of characters in Ar str .
|
||||||
|
.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 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 CHARLEN str Ta Returns the number of charmap entries in Ar str No with the current charmap .
|
.It Fn CHARLEN str Ta Returns the number of charmap entries in Ar str No with the current charmap .
|
||||||
.It Fn CHARSUB str pos Ta Returns the substring for the charmap entry at Ar pos No in Ar str No (first character is position 1, last is position -1) 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 CHARSIZE char Ta Returns how many values are in the charmap entry for Ar char No with the current charmap.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
Note that the first character of a string is at index 0, and the last is at index -1.
|
||||||
|
.Pp
|
||||||
|
The following legacy functions are similar to other functions that operate on string expressions, but for historical reasons, they count characters starting from
|
||||||
|
.Em position 1 ,
|
||||||
|
not from index 0!
|
||||||
|
(Position -1 still counts from the last character.)
|
||||||
|
.Bl -column "STRSUB(str, pos, len)"
|
||||||
|
.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
|
.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.
|
||||||
@@ -904,7 +933,7 @@ SECTION "LOAD example", ROMX
|
|||||||
CopyCode:
|
CopyCode:
|
||||||
ld de, RAMCode
|
ld de, RAMCode
|
||||||
ld hl, RAMLocation
|
ld hl, RAMLocation
|
||||||
ld c, RAMLocation.end - RAMLocation
|
ld c, RAMCode.end - RAMCode
|
||||||
\&.loop
|
\&.loop
|
||||||
ld a, [de]
|
ld a, [de]
|
||||||
inc de
|
inc de
|
||||||
@@ -928,8 +957,8 @@ RAMLocation:
|
|||||||
|
|
||||||
\&.string
|
\&.string
|
||||||
db "Hello World!\e0"
|
db "Hello World!\e0"
|
||||||
\&.end
|
|
||||||
ENDL
|
ENDL
|
||||||
|
\&.end
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
A
|
A
|
||||||
@@ -939,7 +968,9 @@ block feels similar to a
|
|||||||
declaration because it creates a new one.
|
declaration because it creates a new one.
|
||||||
All data and code generated within such a block is placed in the current section like usual, but all labels are created as if they were placed in this newly-created section.
|
All data and code generated within such a block is placed in the current section like usual, but all labels are created as if they were placed in this newly-created section.
|
||||||
.Pp
|
.Pp
|
||||||
In the example above, all of the code and data will end up in the "LOAD example" section.
|
In the example above, all of the code and data will end up in the
|
||||||
|
.Dq LOAD example
|
||||||
|
section.
|
||||||
You will notice the
|
You will notice the
|
||||||
.Sq RAMCode
|
.Sq RAMCode
|
||||||
and
|
and
|
||||||
@@ -951,15 +982,30 @@ You cannot nest
|
|||||||
.Ic LOAD
|
.Ic LOAD
|
||||||
blocks, nor can you change or stop the current section within them.
|
blocks, nor can you change or stop the current section within them.
|
||||||
.Pp
|
.Pp
|
||||||
|
The current
|
||||||
|
.Ic LOAD
|
||||||
|
block can be ended by using
|
||||||
|
.Ic ENDL .
|
||||||
|
This directive is only necessary if you want to resume writing code in its containing ROM section.
|
||||||
|
Any of
|
||||||
|
.Ic LOAD , SECTION , ENDSECTION ,
|
||||||
|
or
|
||||||
|
.Ic POPS
|
||||||
|
will end the current
|
||||||
|
.Ic LOAD
|
||||||
|
block before performing its own function.
|
||||||
|
.Pp
|
||||||
.Ic LOAD
|
.Ic LOAD
|
||||||
blocks can use the
|
blocks can use the
|
||||||
.Ic UNION
|
.Ic UNION
|
||||||
or
|
or
|
||||||
.Ic FRAGMENT
|
.Ic FRAGMENT
|
||||||
modifiers, as described below.
|
modifiers as described in
|
||||||
|
.Sx Unionized sections
|
||||||
|
below.
|
||||||
.Ss Unionized sections
|
.Ss Unionized sections
|
||||||
When you're tight on RAM, you may want to define overlapping static memory allocations, as explained in the
|
When you're tight on RAM, you may want to define overlapping static memory allocations, as explained in the
|
||||||
.Sx Unions
|
.Sx Allocating overlapping spaces in RAM
|
||||||
section.
|
section.
|
||||||
However, a
|
However, a
|
||||||
.Ic UNION
|
.Ic UNION
|
||||||
@@ -1000,7 +1046,7 @@ or
|
|||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
Different declarations of the same unionized section are not appended, but instead overlaid on top of each other, just like
|
Different declarations of the same unionized section are not appended, but instead overlaid on top of each other, just like
|
||||||
.Sx Unions .
|
.Sx Allocating overlapping spaces in RAM .
|
||||||
Similarly, the size of an unionized section is the largest of all its declarations.
|
Similarly, the size of an unionized section is the largest of all its declarations.
|
||||||
.Ss Section fragments
|
.Ss Section fragments
|
||||||
Section fragments are sections with a small twist: when several of the same name are encountered, they are concatenated instead of producing an error.
|
Section fragments are sections with a small twist: when several of the same name are encountered, they are concatenated instead of producing an error.
|
||||||
@@ -1027,7 +1073,7 @@ and
|
|||||||
.Ic WRAMX
|
.Ic WRAMX
|
||||||
types are still considered different.
|
types are still considered different.
|
||||||
.It
|
.It
|
||||||
Different constraints (alignment, bank, etc.) can be specified for each unionized section declaration, but they must all be compatible.
|
Different constraints (alignment, bank, etc.) can be specified for each section fragment declaration, but they must all be compatible.
|
||||||
For example, alignment must be compatible with any fixed address, all specified banks must be the same, etc.
|
For example, alignment must be compatible with any fixed address, all specified banks must be the same, etc.
|
||||||
.It
|
.It
|
||||||
A section fragment may not be unionized; after all, that wouldn't make much sense.
|
A section fragment may not be unionized; after all, that wouldn't make much sense.
|
||||||
@@ -1079,7 +1125,9 @@ Additionally, label names can contain up to a single dot
|
|||||||
.Ql \&. ,
|
.Ql \&. ,
|
||||||
which may not be the first character.
|
which may not be the first character.
|
||||||
.Pp
|
.Pp
|
||||||
A symbol cannot have the same name as a reserved keyword, unless it is prefixed by a hash
|
A symbol cannot have the same name as a reserved keyword, unless its name is a
|
||||||
|
.Dq raw identifier
|
||||||
|
prefixed by a hash
|
||||||
.Sq # .
|
.Sq # .
|
||||||
For example,
|
For example,
|
||||||
.Ql #load
|
.Ql #load
|
||||||
@@ -1123,7 +1171,7 @@ otherwise, it is said to be
|
|||||||
.Dq exported ,
|
.Dq exported ,
|
||||||
explained in
|
explained in
|
||||||
.Sx Exporting and importing symbols
|
.Sx Exporting and importing symbols
|
||||||
further below).
|
below).
|
||||||
More than one dot in label names is not allowed.
|
More than one dot in label names is not allowed.
|
||||||
.Pp
|
.Pp
|
||||||
For convenience, local labels can use a shorthand syntax: when a symbol name starting with a dot is found (for example, inside an expression, or when declaring a label), then the current
|
For convenience, local labels can use a shorthand syntax: when a symbol name starting with a dot is found (for example, inside an expression, or when declaring a label), then the current
|
||||||
@@ -1254,7 +1302,7 @@ it at the same time.
|
|||||||
below).
|
below).
|
||||||
.Ss Numeric constants
|
.Ss Numeric constants
|
||||||
.Ic EQU
|
.Ic EQU
|
||||||
is used to define immutable numeric symbols.
|
is used to define numeric constant symbols.
|
||||||
Unlike
|
Unlike
|
||||||
.Sq =
|
.Sq =
|
||||||
above, constants defined this way cannot be redefined.
|
above, constants defined this way cannot be redefined.
|
||||||
@@ -1362,6 +1410,8 @@ This expansion is disabled in a few contexts:
|
|||||||
and
|
and
|
||||||
.Ql MACRO name
|
.Ql MACRO name
|
||||||
will not expand string constants in their names.
|
will not expand string constants in their names.
|
||||||
|
Expansion is also disabled if the string constant's name is a raw identifier prefixed by a hash
|
||||||
|
.Sq # .
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
DEF COUNTREG EQUS "[hl+]"
|
DEF COUNTREG EQUS "[hl+]"
|
||||||
ld a, COUNTREG
|
ld a, COUNTREG
|
||||||
@@ -1553,47 +1603,6 @@ environment variable if that is defined as a UNIX timestamp.
|
|||||||
Refer to the spec at
|
Refer to the spec at
|
||||||
.Lk https://reproducible-builds.org/docs/source-date-epoch/ reproducible-builds.org .
|
.Lk https://reproducible-builds.org/docs/source-date-epoch/ reproducible-builds.org .
|
||||||
.Sh DEFINING DATA
|
.Sh DEFINING DATA
|
||||||
.Ss Statically allocating space in RAM
|
|
||||||
.Ic DS
|
|
||||||
statically allocates a number of empty bytes.
|
|
||||||
This is the preferred method of allocating space in a RAM section.
|
|
||||||
You can also use
|
|
||||||
.Ic DB , DW
|
|
||||||
and
|
|
||||||
.Ic DL
|
|
||||||
without any arguments instead (see
|
|
||||||
.Sx Defining constant data in ROM
|
|
||||||
below).
|
|
||||||
.Bd -literal -offset indent
|
|
||||||
DS 42 ;\ Allocates 42 bytes
|
|
||||||
.Ed
|
|
||||||
.Pp
|
|
||||||
Empty space in RAM sections will not be initialized.
|
|
||||||
In ROM sections, it will be filled with the value passed to the
|
|
||||||
.Fl p
|
|
||||||
command-line option, except when using overlays with
|
|
||||||
.Fl O .
|
|
||||||
.Pp
|
|
||||||
Instead of an exact number of bytes, you can specify
|
|
||||||
.Ic ALIGN Ns Bq Ar align , offset
|
|
||||||
to allocate however many bytes are required to align the subsequent data.
|
|
||||||
Thus,
|
|
||||||
.Sq Ic DS ALIGN Ns Bo Ar align , offset Bc , No ...
|
|
||||||
is equivalent to
|
|
||||||
.Sq Ic DS Ar n , No ...
|
|
||||||
followed by
|
|
||||||
.Sq Ic ALIGN Ns Bq Ar align , offset ,
|
|
||||||
where
|
|
||||||
.Ar n
|
|
||||||
is the minimum value needed to satisfy the
|
|
||||||
.Ic ALIGN
|
|
||||||
constraint (see
|
|
||||||
.Sx Requesting alignment
|
|
||||||
below).
|
|
||||||
Note that
|
|
||||||
.Ic ALIGN Ns Bq Ar align
|
|
||||||
is a shorthand for
|
|
||||||
.Ic ALIGN Ns Bq Ar align , No 0 .
|
|
||||||
.Ss Defining constant data in ROM
|
.Ss Defining constant data in ROM
|
||||||
.Ic DB
|
.Ic DB
|
||||||
defines a list of bytes that will be stored in the final image.
|
defines a list of bytes that will be stored in the final image.
|
||||||
@@ -1660,7 +1669,7 @@ can be used in a
|
|||||||
/
|
/
|
||||||
.Ic SRAM
|
.Ic SRAM
|
||||||
section.
|
section.
|
||||||
.Ss Including binary files
|
.Ss Including binary data files
|
||||||
You probably have some graphics, level data, etc. you'd like to include.
|
You probably have some graphics, level data, etc. you'd like to include.
|
||||||
Use
|
Use
|
||||||
.Ic INCBIN
|
.Ic INCBIN
|
||||||
@@ -1684,7 +1693,48 @@ INCBIN "data.bin", 78, 256
|
|||||||
.Pp
|
.Pp
|
||||||
The length argument is optional.
|
The length argument is optional.
|
||||||
If only the start position is specified, the bytes from the start position until the end of the file will be included.
|
If only the start position is specified, the bytes from the start position until the end of the file will be included.
|
||||||
.Ss Unions
|
.Ss Statically allocating space in RAM
|
||||||
|
.Ic DS
|
||||||
|
statically allocates a number of empty bytes.
|
||||||
|
This is the preferred method of allocating space in a RAM section.
|
||||||
|
You can also use
|
||||||
|
.Ic DB , DW
|
||||||
|
and
|
||||||
|
.Ic DL
|
||||||
|
without any arguments instead (see
|
||||||
|
.Sx Defining constant data in ROM
|
||||||
|
below).
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
DS 42 ;\ Allocates 42 bytes
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
Empty space in RAM sections will not be initialized.
|
||||||
|
In ROM sections, it will be filled with the value passed to the
|
||||||
|
.Fl p
|
||||||
|
command-line option, except when using overlays with
|
||||||
|
.Fl O .
|
||||||
|
.Pp
|
||||||
|
Instead of an exact number of bytes, you can specify
|
||||||
|
.Ic ALIGN Ns Bq Ar align , offset
|
||||||
|
to allocate however many bytes are required to align the subsequent data.
|
||||||
|
Thus,
|
||||||
|
.Sq Ic DS ALIGN Ns Bo Ar align , offset Bc , No ...
|
||||||
|
is equivalent to
|
||||||
|
.Sq Ic DS Ar n , No ...
|
||||||
|
followed by
|
||||||
|
.Sq Ic ALIGN Ns Bq Ar align , offset ,
|
||||||
|
where
|
||||||
|
.Ar n
|
||||||
|
is the minimum value needed to satisfy the
|
||||||
|
.Ic ALIGN
|
||||||
|
constraint (see
|
||||||
|
.Sx Requesting alignment
|
||||||
|
below).
|
||||||
|
Note that
|
||||||
|
.Ic ALIGN Ns Bq Ar align
|
||||||
|
is a shorthand for
|
||||||
|
.Ic ALIGN Ns Bq Ar align , No 0 .
|
||||||
|
.Ss Allocating overlapping spaces in RAM
|
||||||
Unions allow multiple static memory allocations to overlap, like unions in C.
|
Unions allow multiple static memory allocations to overlap, like unions in C.
|
||||||
This does not increase the amount of memory available, but allows re-using the same memory region for different purposes.
|
This does not increase the amount of memory available, but allows re-using the same memory region for different purposes.
|
||||||
.Pp
|
.Pp
|
||||||
@@ -1745,6 +1795,37 @@ Unions may be used in any section, but they may only contain space-allocating di
|
|||||||
.Ic DS
|
.Ic DS
|
||||||
(see
|
(see
|
||||||
.Sx Statically allocating space in RAM ) .
|
.Sx Statically allocating space in RAM ) .
|
||||||
|
.Ss Requesting alignment
|
||||||
|
While
|
||||||
|
.Ic ALIGN
|
||||||
|
as presented in
|
||||||
|
.Sx SECTIONS
|
||||||
|
is often useful as-is, sometimes you instead want a particular piece of data (or code) in the middle of the section to be aligned.
|
||||||
|
This is made easier through the use of mid-section
|
||||||
|
.Ic ALIGN Ar align , offset .
|
||||||
|
It will retroactively alter the section's attributes to ensure that the location the
|
||||||
|
.Ic ALIGN
|
||||||
|
directive is at, has its
|
||||||
|
.Ar align
|
||||||
|
lower bits equal to
|
||||||
|
.Ar offset .
|
||||||
|
.Pp
|
||||||
|
If the constraint cannot be met (for example because the section is fixed at an incompatible address), an error is produced.
|
||||||
|
Note that
|
||||||
|
.Ic ALIGN Ar align
|
||||||
|
is a shorthand for
|
||||||
|
.Ic ALIGN Ar align , No 0 .
|
||||||
|
.Pp
|
||||||
|
There may be times when you don't just want to specify an alignment constraint at the current location, but also skip ahead until the constraint can be satisfied.
|
||||||
|
In that case, you can use
|
||||||
|
.Ic DS ALIGN Ns Bq Ar align , offset
|
||||||
|
to allocate however many bytes are required to align the subsequent data.
|
||||||
|
.Pp
|
||||||
|
If the constraint cannot be met by skipping any amount of space, an error is produced.
|
||||||
|
Note that
|
||||||
|
.Ic ALIGN Ns Bq Ar align
|
||||||
|
is a shorthand for
|
||||||
|
.Ic ALIGN Ns Bq Ar align , No 0 .
|
||||||
.Sh THE MACRO LANGUAGE
|
.Sh THE MACRO LANGUAGE
|
||||||
.Ss Invoking macros
|
.Ss Invoking macros
|
||||||
A macro is invoked by using its name at the beginning of a line, like a directive, followed by any comma-separated arguments.
|
A macro is invoked by using its name at the beginning of a line, like a directive, followed by any comma-separated arguments.
|
||||||
@@ -1796,9 +1877,11 @@ being the second, and so on. Since there are only nine digits, you can only use
|
|||||||
To use the rest, you put the argument number in angle brackets, like
|
To use the rest, you put the argument number in angle brackets, like
|
||||||
.Ic \e<10> .
|
.Ic \e<10> .
|
||||||
.Pp
|
.Pp
|
||||||
This bracketed syntax supports decimal numbers and numeric constant symbols.
|
This bracketed syntax supports decimal numbers and numeric symbols, where negative values count from the last argument.
|
||||||
For example,
|
For example,
|
||||||
.Ql \e<_NARG>
|
.Ql \e<_NARG>
|
||||||
|
or
|
||||||
|
.Ql \e<-1>
|
||||||
will get the last argument.
|
will get the last argument.
|
||||||
.Pp
|
.Pp
|
||||||
Other macro arguments and symbol interpolations will also be expanded inside the angle brackets.
|
Other macro arguments and symbol interpolations will also be expanded inside the angle brackets.
|
||||||
@@ -2283,9 +2366,9 @@ POPO
|
|||||||
.Pp
|
.Pp
|
||||||
.Ic OPT
|
.Ic OPT
|
||||||
can modify the options
|
can modify the options
|
||||||
.Cm b , g , p , Q ,
|
.Cm b , g , p , Q , r ,
|
||||||
and
|
and
|
||||||
.Cm r .
|
.Cm W .
|
||||||
.Pp
|
.Pp
|
||||||
.Ic POPO
|
.Ic POPO
|
||||||
and
|
and
|
||||||
@@ -2306,37 +2389,6 @@ PUSHO b.X, g.oOX
|
|||||||
DW `..ooOOXX
|
DW `..ooOOXX
|
||||||
POPO
|
POPO
|
||||||
.Ed
|
.Ed
|
||||||
.Ss Requesting alignment
|
|
||||||
While
|
|
||||||
.Ic ALIGN
|
|
||||||
as presented in
|
|
||||||
.Sx SECTIONS
|
|
||||||
is often useful as-is, sometimes you instead want a particular piece of data (or code) in the middle of the section to be aligned.
|
|
||||||
This is made easier through the use of mid-section
|
|
||||||
.Ic ALIGN Ar align , offset .
|
|
||||||
It will alter the section's attributes to ensure that the location the
|
|
||||||
.Ic ALIGN
|
|
||||||
directive is at, has its
|
|
||||||
.Ar align
|
|
||||||
lower bits equal to
|
|
||||||
.Ar offset .
|
|
||||||
.Pp
|
|
||||||
If the constraint cannot be met (for example because the section is fixed at an incompatible address), an error is produced.
|
|
||||||
Note that
|
|
||||||
.Ic ALIGN Ar align
|
|
||||||
is a shorthand for
|
|
||||||
.Ic ALIGN Ar align , No 0 .
|
|
||||||
.Pp
|
|
||||||
There may be times when you don't just want to specify an alignment constraint at the current location, but also skip ahead until the constraint can be satisfied.
|
|
||||||
In that case, you can use
|
|
||||||
.Ic DS ALIGN Ns Bq Ar align , offset
|
|
||||||
to allocate however many bytes are required to align the subsequent data.
|
|
||||||
.Pp
|
|
||||||
If the constraint cannot be met by skipping any amount of space, an error is produced.
|
|
||||||
Note that
|
|
||||||
.Ic ALIGN Ns Bq Ar align
|
|
||||||
is a shorthand for
|
|
||||||
.Ic ALIGN Ns Bq Ar align , No 0 .
|
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr rgbasm 1 ,
|
.Xr rgbasm 1 ,
|
||||||
.Xr rgblink 1 ,
|
.Xr rgblink 1 ,
|
||||||
@@ -2344,6 +2396,7 @@ is a shorthand for
|
|||||||
.Xr rgbfix 1 ,
|
.Xr rgbfix 1 ,
|
||||||
.Xr rgbgfx 1 ,
|
.Xr rgbgfx 1 ,
|
||||||
.Xr gbz80 7 ,
|
.Xr gbz80 7 ,
|
||||||
|
.Xr rgbasm-old 5 ,
|
||||||
.Xr rgbds 5 ,
|
.Xr rgbds 5 ,
|
||||||
.Xr rgbds 7
|
.Xr rgbds 7
|
||||||
.Sh HISTORY
|
.Sh HISTORY
|
||||||
|
|||||||
21
man/rgbds.5
21
man/rgbds.5
@@ -1,6 +1,6 @@
|
|||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd September 18, 2024
|
.Dd May 4, 2025
|
||||||
.Dt RGBDS 5
|
.Dt RGBDS 5
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -254,7 +254,7 @@ Size of the
|
|||||||
below.
|
below.
|
||||||
.It Cm BYTE Ar RPNExpr Ns Bq RPNSize
|
.It Cm BYTE Ar RPNExpr Ns Bq RPNSize
|
||||||
The patch's value, encoded as a RPN expression
|
The patch's value, encoded as a RPN expression
|
||||||
.Pq see Sx RPN EXPRESSIONS .
|
.Pq see Sx RPN expressions .
|
||||||
.El
|
.El
|
||||||
.It Cm ENDR
|
.It Cm ENDR
|
||||||
.El
|
.El
|
||||||
@@ -294,14 +294,14 @@ Size of the
|
|||||||
below.
|
below.
|
||||||
.It Cm BYTE Ar RPNExpr Ns Bq RPNSize
|
.It Cm BYTE Ar RPNExpr Ns Bq RPNSize
|
||||||
The patch's value, encoded as a RPN expression
|
The patch's value, encoded as a RPN expression
|
||||||
.Pq see Sx RPN EXPRESSIONS .
|
.Pq see Sx RPN expressions .
|
||||||
.It Cm STRING Ar Message
|
.It Cm STRING Ar Message
|
||||||
The message displayed if the expression evaluates to a non-zero value.
|
The message displayed if the expression evaluates to a non-zero value.
|
||||||
If empty, a generic message is displayed instead.
|
If empty, a generic message is displayed instead.
|
||||||
.El
|
.El
|
||||||
.It Cm ENDR
|
.It Cm ENDR
|
||||||
.El
|
.El
|
||||||
.Ss RPN EXPRESSIONS
|
.Ss RPN expressions
|
||||||
Expressions in the object file are stored as RPN, or
|
Expressions in the object file are stored as RPN, or
|
||||||
.Dq Reverse Polish Notation ,
|
.Dq Reverse Polish Notation ,
|
||||||
which is a notation that allows computing arbitrary expressions with just a simple stack.
|
which is a notation that allows computing arbitrary expressions with just a simple stack.
|
||||||
@@ -388,10 +388,19 @@ The value is then ANDed with $00FF
|
|||||||
check.
|
check.
|
||||||
Checks if the value is a valid
|
Checks if the value is a valid
|
||||||
.Ql rst
|
.Ql rst
|
||||||
.Pq see Do RST vec Dc in Xr gbz80 7
|
vector
|
||||||
vector, that is one of $00, $08, $10, $18, $20, $28, $30, or $38.
|
.Pq see Do RST vec Dc in Xr gbz80 7 ,
|
||||||
|
that is, one of $00, $08, $10, $18, $20, $28, $30, or $38.
|
||||||
The value is then ORed with $C7
|
The value is then ORed with $C7
|
||||||
.Pq Ql \&| $C7 .
|
.Pq Ql \&| $C7 .
|
||||||
|
.It Li $62 Ta Ql bit/res/set
|
||||||
|
check; followed by the instruction's
|
||||||
|
.Cm BYTE
|
||||||
|
mask.
|
||||||
|
Checks if the value is a valid bit index
|
||||||
|
.Pq see e.g. Do BIT u3, r8 Dc in Xr gbz80 7 ,
|
||||||
|
that is, from 0 to 7.
|
||||||
|
The value is then ORed with the instruction's mask.
|
||||||
.It Li $80 Ta Integer literal; followed by the
|
.It Li $80 Ta Integer literal; followed by the
|
||||||
.Cm LONG
|
.Cm LONG
|
||||||
integer.
|
integer.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd September 18, 2024
|
.Dd May 4, 2025
|
||||||
.Dt RGBDS 7
|
.Dt RGBDS 7
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -51,6 +51,10 @@ adapts the code to be more UNIX-like and releases this version as rgbds-linux.
|
|||||||
forks Nossum's repository.
|
forks Nossum's repository.
|
||||||
The fork becomes the reference implementation of RGBDS.
|
The fork becomes the reference implementation of RGBDS.
|
||||||
.It
|
.It
|
||||||
|
2010-09-25: S\(/orensen continues development of
|
||||||
|
.Lk https://github.com/asmotor/asmotor ASMotor
|
||||||
|
to this day.
|
||||||
|
.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.
|
||||||
|
|||||||
10
man/rgbfix.1
10
man/rgbfix.1
@@ -1,6 +1,6 @@
|
|||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd September 18, 2024
|
.Dd May 4, 2025
|
||||||
.Dt RGBFIX 1
|
.Dt RGBFIX 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
.Nd Game Boy header utility and checksum fixer
|
.Nd Game Boy header utility and checksum fixer
|
||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm
|
.Nm
|
||||||
.Op Fl jOsVv
|
.Op Fl hjOsVv
|
||||||
.Op Fl C | c
|
.Op Fl C | c
|
||||||
.Op Fl f Ar fix_spec
|
.Op Fl f Ar fix_spec
|
||||||
.Op Fl i Ar game_id
|
.Op Fl i Ar game_id
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
.Op Fl l Ar licensee_id
|
.Op Fl l Ar licensee_id
|
||||||
.Op Fl m Ar mbc_type
|
.Op Fl m Ar mbc_type
|
||||||
.Op Fl n Ar rom_version
|
.Op Fl n Ar rom_version
|
||||||
|
.Op Fl o Ar out_file
|
||||||
.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
|
||||||
@@ -91,6 +92,8 @@ Fix the global checksum
|
|||||||
.It Cm G
|
.It Cm G
|
||||||
Trash the global checksum.
|
Trash the global checksum.
|
||||||
.El
|
.El
|
||||||
|
.It Fl h , Fl \-help
|
||||||
|
Print help text for the program and exit.
|
||||||
.It Fl i Ar game_id , Fl \-game-id Ar game_id
|
.It Fl i Ar game_id , Fl \-game-id Ar game_id
|
||||||
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
|
||||||
@@ -132,6 +135,9 @@ Set the ROM version
|
|||||||
to a given value from 0 to 0xFF.
|
to a given value from 0 to 0xFF.
|
||||||
.It Fl O , Fl \-overwrite
|
.It Fl O , Fl \-overwrite
|
||||||
Allow overwriting different non-zero bytes in the header without a warning being emitted.
|
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
|
||||||
|
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.
|
||||||
.It Fl p Ar pad_value , Fl \-pad-value Ar pad_value
|
.It Fl p Ar pad_value , Fl \-pad-value Ar pad_value
|
||||||
Pad the ROM image to a valid size with a given pad value from 0 to 255 (0xFF).
|
Pad the ROM image to a valid size with a given pad value from 0 to 255 (0xFF).
|
||||||
.Nm
|
.Nm
|
||||||
|
|||||||
34
man/rgbgfx.1
34
man/rgbgfx.1
@@ -2,7 +2,7 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd September 18, 2024
|
.Dd May 4, 2025
|
||||||
.Dt RGBGFX 1
|
.Dt RGBGFX 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
.Nd Game Boy graphics converter
|
.Nd Game Boy graphics converter
|
||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm
|
.Nm
|
||||||
.Op Fl CmOuVZ
|
.Op Fl CmhOuVXYZ
|
||||||
.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
|
||||||
@@ -103,6 +103,19 @@ and has the same size.
|
|||||||
Same as
|
Same as
|
||||||
.Fl a Ar base_path Ns .attrmap
|
.Fl a Ar base_path Ns .attrmap
|
||||||
.Pq see Sx Automatic output paths .
|
.Pq see Sx Automatic output paths .
|
||||||
|
.It Fl B Ar color , Fl \-background-color Ar color
|
||||||
|
Set a background color to be omitted from output.
|
||||||
|
Colors are accepted in
|
||||||
|
.Ql #rgb
|
||||||
|
or
|
||||||
|
.Ql #rrggbb
|
||||||
|
format, or as
|
||||||
|
.Ql transparent .
|
||||||
|
Input tiles which are entirely the specified background color are ignored and will not be output in tile data file.
|
||||||
|
The tilemap, atrribute map, or palette map files
|
||||||
|
.Em will
|
||||||
|
use placeholder values where background tiles were.
|
||||||
|
If a background color is specified, it cannot be used within tiles which are not ignored.
|
||||||
.It Fl b Ar base_ids , Fl \-base-tiles Ar base_ids
|
.It Fl b Ar base_ids , Fl \-base-tiles Ar base_ids
|
||||||
Set the base IDs for tile map output.
|
Set the base IDs for tile map output.
|
||||||
.Ar base_ids
|
.Ar base_ids
|
||||||
@@ -126,7 +139,7 @@ begins with a hash character
|
|||||||
.Ql # ,
|
.Ql # ,
|
||||||
it is treated as an inline palette specification.
|
it is treated as an inline palette specification.
|
||||||
It should contain a comma-separated list of hexadecimal colors, each beginning with a hash.
|
It should contain a comma-separated list of hexadecimal colors, each beginning with a hash.
|
||||||
Colors are accepted either as
|
Colors are accepted in
|
||||||
.Ql #rgb
|
.Ql #rgb
|
||||||
or
|
or
|
||||||
.Ql #rrggbb
|
.Ql #rrggbb
|
||||||
@@ -165,6 +178,8 @@ for a list of formats and their descriptions.
|
|||||||
.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).
|
||||||
|
.It Fl h , Fl \-help
|
||||||
|
Print help text for the program and exit.
|
||||||
.It Fl i Ar input_tiles , Fl \-input-tileset Ar input_tiles
|
.It Fl i Ar input_tiles , Fl \-input-tileset Ar input_tiles
|
||||||
Use the specified input tiles in addition to having
|
Use the specified input tiles in addition to having
|
||||||
.Nm
|
.Nm
|
||||||
@@ -229,9 +244,8 @@ The second number pair specifies how many tiles to process horizontally and vert
|
|||||||
.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 m , Fl \-mirror-tiles
|
.It Fl m , Fl \-mirror-tiles
|
||||||
Deduplicate tiles that are 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.
|
||||||
Tiles are checked for horizontal, vertical, and horizontal-vertical mirroring.
|
|
||||||
Useful with a tile map and attribute map together (see
|
Useful with a tile map and attribute map together (see
|
||||||
.Fl a
|
.Fl a
|
||||||
and
|
and
|
||||||
@@ -239,6 +253,8 @@ and
|
|||||||
to keep track of the duplicated tiles and the dimension(s) mirrored.
|
to keep track of the duplicated tiles and the dimension(s) mirrored.
|
||||||
Implies
|
Implies
|
||||||
.Fl u .
|
.Fl u .
|
||||||
|
Equivalent to
|
||||||
|
.Fl XY .
|
||||||
.It Fl N Ar nb_tiles , Fl \-nb-tiles Ar nb_tiles
|
.It Fl N Ar nb_tiles , Fl \-nb-tiles Ar nb_tiles
|
||||||
Set a maximum number of tiles that can be placed in each VRAM bank.
|
Set a maximum number of tiles that can be placed in each VRAM bank.
|
||||||
.Ar nb_tiles
|
.Ar nb_tiles
|
||||||
@@ -353,6 +369,10 @@ Some internal debug printing is enabled.
|
|||||||
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 X , Fl \-mirror-x
|
||||||
|
Deduplicate tiles that are horizontally symmetrical mirror images of each other across the X axis.
|
||||||
|
Implies
|
||||||
|
.Fl u .
|
||||||
.It Fl x Ar quantity , Fl \-trim-end Ar quantity
|
.It Fl x Ar quantity , Fl \-trim-end Ar quantity
|
||||||
Do not output the last
|
Do not output the last
|
||||||
.Ar quantity
|
.Ar quantity
|
||||||
@@ -373,6 +393,10 @@ was enabled, so you probably don't want to use this option in combination with
|
|||||||
Note also that the tiles that don't get output will not count towards
|
Note also that the tiles that don't get output will not count towards
|
||||||
.Fl N Ap s
|
.Fl N Ap s
|
||||||
limit.
|
limit.
|
||||||
|
.It Fl Y , Fl \-mirror-y
|
||||||
|
Deduplicate tiles that are vertically symmetrical mirror images of each other across the Y axis.
|
||||||
|
Implies
|
||||||
|
.Fl u .
|
||||||
.It Fl Z , Fl \-columns
|
.It Fl Z , Fl \-columns
|
||||||
Read squares from the PNG in column-major order (column by column), instead of the default row-major order (line by line).
|
Read squares from the PNG in column-major order (column by column), instead of the default row-major order (line by line).
|
||||||
This primarily affects tile map and attribute map output, although it may also change generated tile data and palettes.
|
This primarily affects tile map and attribute map output, although it may also change generated tile data and palettes.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd September 18, 2024
|
.Dd May 4, 2025
|
||||||
.Dt RGBLINK 1
|
.Dt RGBLINK 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
.Nd Game Boy linker
|
.Nd Game Boy linker
|
||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm
|
.Nm
|
||||||
.Op Fl dMtVvwx
|
.Op Fl dhMtVvwx
|
||||||
.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
|
||||||
@@ -67,6 +67,8 @@ 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.
|
||||||
This option automatically enables
|
This option automatically enables
|
||||||
.Fl w .
|
.Fl w .
|
||||||
|
.It Fl h , Fl \-help
|
||||||
|
Print help text for the program and exit.
|
||||||
.It Fl l Ar linker_script , Fl \-linkerscript Ar linker_script
|
.It Fl l Ar linker_script , Fl \-linkerscript Ar linker_script
|
||||||
Specify a linker script file that tells the linker how sections must be placed in the ROM.
|
Specify a linker script file that tells the linker how sections must be placed in the ROM.
|
||||||
The attributes assigned in the linker script must be consistent with any assigned in the code.
|
The attributes assigned in the linker script must be consistent with any assigned in the code.
|
||||||
@@ -119,8 +121,8 @@ WRAMX sections that are fixed to a bank other than 1 become errors, other WRAMX
|
|||||||
Disables padding the end of the final file.
|
Disables padding the end of the final file.
|
||||||
This option automatically enables
|
This option automatically enables
|
||||||
.Fl t .
|
.Fl t .
|
||||||
You can use this when not not making a ROM.
|
You can use this to make binary files that are not a ROM.
|
||||||
When making a ROM, be careful that not using this is not a replacement for
|
When making a ROM, note that not using this is not a replacement for
|
||||||
.Xr rgbfix 1 Ap s Fl p
|
.Xr rgbfix 1 Ap s Fl p
|
||||||
option!
|
option!
|
||||||
.El
|
.El
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd September 18, 2024
|
.Dd May 4, 2025
|
||||||
.Dt RGBLINK 5
|
.Dt RGBLINK 5
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -120,7 +120,7 @@ causes all sections between it and the next
|
|||||||
.Ic ORG
|
.Ic ORG
|
||||||
or bank specification to be placed at addresses automatically determined by
|
or bank specification to be placed at addresses automatically determined by
|
||||||
.Nm .
|
.Nm .
|
||||||
.Pq It is, however, compatible with Ic ALIGN No below.
|
.Pq \&It is, however, compatible with Ic ALIGN No below.
|
||||||
.Pp
|
.Pp
|
||||||
.Ql Ic ALIGN Ar addr , Ar offset
|
.Ql Ic ALIGN Ar addr , Ar offset
|
||||||
increases the
|
increases the
|
||||||
|
|||||||
@@ -91,10 +91,11 @@ foreach(PROG "asm" "fix" "gfx" "link")
|
|||||||
${rgb${PROG}_src}
|
${rgb${PROG}_src}
|
||||||
${common_src}
|
${common_src}
|
||||||
)
|
)
|
||||||
if(SUFFIX)
|
|
||||||
set_target_properties(rgb${PROG} PROPERTIES SUFFIX ${SUFFIX})
|
|
||||||
endif()
|
|
||||||
install(TARGETS rgb${PROG} RUNTIME DESTINATION bin)
|
install(TARGETS rgb${PROG} RUNTIME DESTINATION bin)
|
||||||
|
# Required to run tests
|
||||||
|
set_target_properties(rgb${PROG} PROPERTIES
|
||||||
|
# hack for MSVC: no-op generator expression to stop generation of "per-configuration subdirectory"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY $<1:${CMAKE_SOURCE_DIR}>)
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
if(LIBPNG_FOUND) # pkg-config
|
if(LIBPNG_FOUND) # pkg-config
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#include "asm/charmap.hpp"
|
#include "asm/charmap.hpp"
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "extern/utf8decoder.hpp"
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
|
|
||||||
@@ -30,6 +31,29 @@ 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
|
||||||
|
template<typename F>
|
||||||
|
bool forEachChar(F callback) const {
|
||||||
|
// clang-format off: nested initializers
|
||||||
|
for (std::stack<std::pair<size_t, std::string>> prefixes({{0, ""}}); !prefixes.empty();) {
|
||||||
|
// clang-format on
|
||||||
|
auto [nodeIdx, mapping] = std::move(prefixes.top());
|
||||||
|
prefixes.pop();
|
||||||
|
CharmapNode const &node = nodes[nodeIdx];
|
||||||
|
if (node.isTerminal()) {
|
||||||
|
if (!callback(nodeIdx, mapping)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (unsigned c = 0; c < std::size(node.next); c++) {
|
||||||
|
if (size_t nextIdx = node.next[c]; nextIdx) {
|
||||||
|
prefixes.push({nextIdx, mapping + static_cast<char>(c)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::deque<Charmap> charmapList;
|
static std::deque<Charmap> charmapList;
|
||||||
@@ -43,34 +67,29 @@ bool charmap_ForEach(
|
|||||||
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 : charmapList) {
|
||||||
// Traverse the trie depth-first to derive the character mappings in definition order
|
|
||||||
std::map<size_t, std::string> mappings;
|
std::map<size_t, std::string> mappings;
|
||||||
for (std::stack<std::pair<size_t, std::string>> prefixes({{0, ""}}); !prefixes.empty();) {
|
charmap.forEachChar([&mappings](size_t nodeIdx, std::string const &mapping) {
|
||||||
auto [nodeIdx, mapping] = std::move(prefixes.top());
|
mappings[nodeIdx] = mapping;
|
||||||
prefixes.pop();
|
return true;
|
||||||
CharmapNode const &node = charmap.nodes[nodeIdx];
|
});
|
||||||
if (node.isTerminal())
|
|
||||||
mappings[nodeIdx] = mapping;
|
|
||||||
for (unsigned c = 0; c < 256; c++) {
|
|
||||||
if (size_t nextIdx = node.next[c]; nextIdx)
|
|
||||||
prefixes.push({nextIdx, mapping + (char)c});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mapFunc(charmap.name);
|
mapFunc(charmap.name);
|
||||||
for (auto [nodeIdx, mapping] : mappings)
|
for (auto [nodeIdx, mapping] : mappings) {
|
||||||
charFunc(mapping, charmap.nodes[nodeIdx].value);
|
charFunc(mapping, charmap.nodes[nodeIdx].value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return !charmapList.empty();
|
return !charmapList.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_t)-1;
|
size_t baseIdx = SIZE_MAX;
|
||||||
|
|
||||||
if (baseName != nullptr) {
|
if (baseName != nullptr) {
|
||||||
if (auto search = charmapMap.find(*baseName); search == charmapMap.end())
|
if (auto search = charmapMap.find(*baseName); search == charmapMap.end()) {
|
||||||
error("Base charmap '%s' doesn't exist\n", baseName->c_str());
|
error("Base charmap '%s' doesn't exist\n", baseName->c_str());
|
||||||
else
|
} else {
|
||||||
baseIdx = search->second;
|
baseIdx = search->second;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (charmapMap.find(name) != charmapMap.end()) {
|
if (charmapMap.find(name) != charmapMap.end()) {
|
||||||
@@ -82,10 +101,11 @@ void charmap_New(std::string const &name, std::string const *baseName) {
|
|||||||
charmapMap[name] = charmapList.size();
|
charmapMap[name] = charmapList.size();
|
||||||
Charmap &charmap = charmapList.emplace_back();
|
Charmap &charmap = charmapList.emplace_back();
|
||||||
|
|
||||||
if (baseIdx != (size_t)-1)
|
if (baseIdx != SIZE_MAX) {
|
||||||
charmap.nodes = charmapList[baseIdx].nodes; // Copies `charmapList[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;
|
charmap.name = name;
|
||||||
|
|
||||||
@@ -93,10 +113,11 @@ void charmap_New(std::string const &name, std::string const *baseName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 search = charmapMap.find(name); search == charmapMap.end()) {
|
||||||
error("Charmap '%s' doesn't exist\n", name.c_str());
|
error("Charmap '%s' doesn't exist\n", name.c_str());
|
||||||
else
|
} else {
|
||||||
currentCharmap = &charmapList[search->second];
|
currentCharmap = &charmapList[search->second];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void charmap_Push() {
|
void charmap_Push() {
|
||||||
@@ -113,6 +134,12 @@ void charmap_Pop() {
|
|||||||
charmapStack.pop();
|
charmapStack.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void charmap_CheckStack() {
|
||||||
|
if (!charmapStack.empty()) {
|
||||||
|
warning(WARNING_UNMATCHED_DIRECTIVE, "`PUSHC` without corresponding `POPC`\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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\n");
|
||||||
@@ -123,7 +150,7 @@ void charmap_Add(std::string const &mapping, std::vector<int32_t> &&value) {
|
|||||||
size_t nodeIdx = 0;
|
size_t nodeIdx = 0;
|
||||||
|
|
||||||
for (char c : mapping) {
|
for (char c : mapping) {
|
||||||
size_t &nextIdxRef = charmap.nodes[nodeIdx].next[(uint8_t)c];
|
size_t &nextIdxRef = charmap.nodes[nodeIdx].next[static_cast<uint8_t>(c)];
|
||||||
size_t nextIdx = nextIdxRef;
|
size_t nextIdx = nextIdxRef;
|
||||||
|
|
||||||
if (!nextIdx) {
|
if (!nextIdx) {
|
||||||
@@ -140,30 +167,47 @@ 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\n");
|
||||||
|
}
|
||||||
|
|
||||||
std::swap(node.value, value);
|
std::swap(node.value, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool charmap_HasChar(std::string const &input) {
|
bool charmap_HasChar(std::string const &mapping) {
|
||||||
Charmap const &charmap = *currentCharmap;
|
Charmap const &charmap = *currentCharmap;
|
||||||
size_t nodeIdx = 0;
|
size_t nodeIdx = 0;
|
||||||
|
|
||||||
for (char c : input) {
|
for (char c : mapping) {
|
||||||
nodeIdx = charmap.nodes[nodeIdx].next[(uint8_t)c];
|
nodeIdx = charmap.nodes[nodeIdx].next[static_cast<uint8_t>(c)];
|
||||||
|
|
||||||
if (!nodeIdx)
|
if (!nodeIdx) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return charmap.nodes[nodeIdx].isTerminal();
|
return charmap.nodes[nodeIdx].isTerminal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t charmap_CharSize(std::string const &mapping) {
|
||||||
|
Charmap const &charmap = *currentCharmap;
|
||||||
|
size_t nodeIdx = 0;
|
||||||
|
|
||||||
|
for (char c : mapping) {
|
||||||
|
nodeIdx = charmap.nodes[nodeIdx].next[static_cast<uint8_t>(c)];
|
||||||
|
|
||||||
|
if (!nodeIdx) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CharmapNode const &node = charmap.nodes[nodeIdx];
|
||||||
|
return node.isTerminal() ? node.value.size() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<int32_t> charmap_Convert(std::string const &input) {
|
std::vector<int32_t> charmap_Convert(std::string const &input) {
|
||||||
std::vector<int32_t> output;
|
std::vector<int32_t> output;
|
||||||
for (std::string_view inputView = input; charmap_ConvertNext(inputView, &output);)
|
for (std::string_view inputView = input; charmap_ConvertNext(inputView, &output);) {}
|
||||||
;
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,10 +222,11 @@ size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output
|
|||||||
size_t inputIdx = 0;
|
size_t inputIdx = 0;
|
||||||
|
|
||||||
for (size_t nodeIdx = 0; inputIdx < input.length();) {
|
for (size_t nodeIdx = 0; inputIdx < input.length();) {
|
||||||
nodeIdx = charmap.nodes[nodeIdx].next[(uint8_t)input[inputIdx]];
|
nodeIdx = charmap.nodes[nodeIdx].next[static_cast<uint8_t>(input[inputIdx])];
|
||||||
|
|
||||||
if (!nodeIdx)
|
if (!nodeIdx) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
inputIdx++; // Consume that char
|
inputIdx++; // Consume that char
|
||||||
|
|
||||||
@@ -201,27 +246,42 @@ size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output
|
|||||||
if (matchIdx) { // A match was found, use it
|
if (matchIdx) { // A match was found, use it
|
||||||
std::vector<int32_t> const &value = charmap.nodes[matchIdx].value;
|
std::vector<int32_t> const &value = charmap.nodes[matchIdx].value;
|
||||||
|
|
||||||
if (output)
|
if (output) {
|
||||||
output->insert(output->end(), RANGE(value));
|
output->insert(output->end(), RANGE(value));
|
||||||
|
}
|
||||||
|
|
||||||
matchLen = value.size();
|
matchLen = value.size();
|
||||||
} 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
|
||||||
int firstChar = input[inputIdx];
|
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
|
||||||
size_t codepointLen = readUTF8Char(output, input.data() + inputIdx);
|
for (uint32_t state = 0, codepoint = 0; inputIdx + codepointLen < input.length();) {
|
||||||
|
if (decode(&state, &codepoint, input[inputIdx + codepointLen]) == 1) {
|
||||||
|
error("Input string is not valid UTF-8\n");
|
||||||
|
codepointLen = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
codepointLen++;
|
||||||
|
if (state == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (codepointLen == 0)
|
if (output) {
|
||||||
error("Input string is not valid UTF-8\n");
|
output->insert(
|
||||||
|
output->end(), input.data() + inputIdx, input.data() + inputIdx + codepointLen
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Warn if this character is not mapped but any others are
|
// Warn if this character is not mapped but any others are
|
||||||
if (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\n", 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\n",
|
||||||
printChar(firstChar)
|
printChar(firstChar)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
inputIdx += codepointLen;
|
inputIdx += codepointLen;
|
||||||
matchLen = codepointLen;
|
matchLen = codepointLen;
|
||||||
@@ -230,3 +290,20 @@ size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output
|
|||||||
input = input.substr(inputIdx);
|
input = input.substr(inputIdx);
|
||||||
return matchLen;
|
return matchLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string charmap_Reverse(std::vector<int32_t> const &value, bool &unique) {
|
||||||
|
Charmap const &charmap = *currentCharmap;
|
||||||
|
std::string revMapping;
|
||||||
|
unique = charmap.forEachChar([&](size_t nodeIdx, std::string const &mapping) {
|
||||||
|
if (charmap.nodes[nodeIdx].value == value) {
|
||||||
|
if (revMapping.empty()) {
|
||||||
|
revMapping = mapping;
|
||||||
|
} else {
|
||||||
|
revMapping.clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return revMapping;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
// Fixed-point math routines
|
// Fixed-point math routines
|
||||||
|
|
||||||
@@ -16,20 +16,18 @@ uint8_t fix_Precision() {
|
|||||||
return fixPrecision;
|
return fixPrecision;
|
||||||
}
|
}
|
||||||
|
|
||||||
double fix_PrecisionFactor() {
|
|
||||||
return pow(2.0, 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t double2fix(double d, int32_t q) {
|
static int32_t double2fix(double d, int32_t q) {
|
||||||
if (isnan(d))
|
if (isnan(d)) {
|
||||||
return 0;
|
return 0;
|
||||||
if (isinf(d))
|
}
|
||||||
|
if (isinf(d)) {
|
||||||
return d < 0 ? INT32_MIN : INT32_MAX;
|
return d < 0 ? INT32_MIN : INT32_MAX;
|
||||||
return (int32_t)round(d * pow(2.0, q));
|
}
|
||||||
|
return static_cast<int32_t>(round(d * pow(2.0, q)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static double turn2rad(double t) {
|
static double turn2rad(double t) {
|
||||||
@@ -73,7 +71,12 @@ int32_t fix_Mul(int32_t i, int32_t j, int32_t q) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int32_t fix_Div(int32_t i, int32_t j, int32_t q) {
|
int32_t fix_Div(int32_t i, int32_t j, int32_t q) {
|
||||||
return double2fix(fix2double(i, q) / fix2double(j, q), q);
|
double dividend = fix2double(i, q);
|
||||||
|
double divisor = fix2double(j, q);
|
||||||
|
if (fpclassify(divisor) == FP_ZERO) {
|
||||||
|
return dividend < 0 ? INT32_MIN : dividend > 0 ? INT32_MAX : 0;
|
||||||
|
}
|
||||||
|
return double2fix(dividend / divisor, q);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t fix_Mod(int32_t i, int32_t j, int32_t q) {
|
int32_t fix_Mod(int32_t i, int32_t j, int32_t q) {
|
||||||
@@ -85,7 +88,11 @@ int32_t fix_Pow(int32_t i, int32_t j, int32_t q) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int32_t fix_Log(int32_t i, int32_t j, int32_t q) {
|
int32_t fix_Log(int32_t i, int32_t j, int32_t q) {
|
||||||
return double2fix(log(fix2double(i, q)) / log(fix2double(j, q)), q);
|
double divisor = log(fix2double(j, q));
|
||||||
|
if (fpclassify(divisor) == FP_ZERO) {
|
||||||
|
return INT32_MAX;
|
||||||
|
}
|
||||||
|
return double2fix(log(fix2double(i, q)) / divisor, q);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t fix_Round(int32_t i, int32_t q) {
|
int32_t fix_Round(int32_t i, int32_t q) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#include "asm/format.hpp"
|
#include "asm/format.hpp"
|
||||||
|
|
||||||
@@ -13,39 +13,44 @@
|
|||||||
#include "asm/warning.hpp"
|
#include "asm/warning.hpp"
|
||||||
|
|
||||||
void FormatSpec::useCharacter(int c) {
|
void FormatSpec::useCharacter(int c) {
|
||||||
if (state == FORMAT_INVALID)
|
if (state == FORMAT_INVALID) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
// sign
|
// sign
|
||||||
case ' ':
|
case ' ':
|
||||||
case '+':
|
case '+':
|
||||||
if (state > FORMAT_SIGN)
|
if (state > FORMAT_SIGN) {
|
||||||
goto invalid;
|
break;
|
||||||
|
}
|
||||||
state = FORMAT_EXACT;
|
state = FORMAT_EXACT;
|
||||||
sign = c;
|
sign = c;
|
||||||
break;
|
return;
|
||||||
|
|
||||||
// exact
|
// exact
|
||||||
case '#':
|
case '#':
|
||||||
if (state > FORMAT_EXACT)
|
if (state > FORMAT_EXACT) {
|
||||||
goto invalid;
|
break;
|
||||||
|
}
|
||||||
state = FORMAT_ALIGN;
|
state = FORMAT_ALIGN;
|
||||||
exact = true;
|
exact = true;
|
||||||
break;
|
return;
|
||||||
|
|
||||||
// align
|
// align
|
||||||
case '-':
|
case '-':
|
||||||
if (state > FORMAT_ALIGN)
|
if (state > FORMAT_ALIGN) {
|
||||||
goto invalid;
|
break;
|
||||||
|
}
|
||||||
state = FORMAT_WIDTH;
|
state = FORMAT_WIDTH;
|
||||||
alignLeft = true;
|
alignLeft = true;
|
||||||
break;
|
return;
|
||||||
|
|
||||||
// pad, width, and prec values
|
// pad, width, and prec values
|
||||||
case '0':
|
case '0':
|
||||||
if (state < FORMAT_WIDTH)
|
if (state < FORMAT_WIDTH) {
|
||||||
padZero = true;
|
padZero = true;
|
||||||
|
}
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case '1':
|
case '1':
|
||||||
case '2':
|
case '2':
|
||||||
@@ -66,25 +71,27 @@ void FormatSpec::useCharacter(int c) {
|
|||||||
} else if (state == FORMAT_PREC) {
|
} else if (state == FORMAT_PREC) {
|
||||||
precision = precision * 10 + (c - '0');
|
precision = precision * 10 + (c - '0');
|
||||||
} else {
|
} else {
|
||||||
goto invalid;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
return;
|
||||||
|
|
||||||
// width
|
// width
|
||||||
case '.':
|
case '.':
|
||||||
if (state > FORMAT_WIDTH)
|
if (state > FORMAT_WIDTH) {
|
||||||
goto invalid;
|
break;
|
||||||
|
}
|
||||||
state = FORMAT_FRAC;
|
state = FORMAT_FRAC;
|
||||||
hasFrac = true;
|
hasFrac = true;
|
||||||
break;
|
return;
|
||||||
|
|
||||||
// prec
|
// prec
|
||||||
case 'q':
|
case 'q':
|
||||||
if (state > FORMAT_PREC)
|
if (state > FORMAT_PREC) {
|
||||||
goto invalid;
|
break;
|
||||||
|
}
|
||||||
state = FORMAT_PREC;
|
state = FORMAT_PREC;
|
||||||
hasPrec = true;
|
hasPrec = true;
|
||||||
break;
|
return;
|
||||||
|
|
||||||
// type
|
// type
|
||||||
case 'd':
|
case 'd':
|
||||||
@@ -95,23 +102,26 @@ void FormatSpec::useCharacter(int c) {
|
|||||||
case 'o':
|
case 'o':
|
||||||
case 'f':
|
case 'f':
|
||||||
case 's':
|
case 's':
|
||||||
if (state >= FORMAT_DONE)
|
if (state >= FORMAT_DONE) {
|
||||||
goto invalid;
|
break;
|
||||||
|
}
|
||||||
state = FORMAT_DONE;
|
state = FORMAT_DONE;
|
||||||
valid = true;
|
valid = true;
|
||||||
type = c;
|
type = c;
|
||||||
break;
|
return;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
invalid:
|
break;
|
||||||
state = FORMAT_INVALID;
|
|
||||||
valid = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state = FORMAT_INVALID;
|
||||||
|
valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormatSpec::finishCharacters() {
|
void FormatSpec::finishCharacters() {
|
||||||
if (!isValid())
|
if (!isValid()) {
|
||||||
state = FORMAT_INVALID;
|
state = FORMAT_INVALID;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string escapeString(std::string const &str) {
|
static std::string escapeString(std::string const &str) {
|
||||||
@@ -151,16 +161,21 @@ void FormatSpec::appendString(std::string &str, std::string const &value) const
|
|||||||
useType = 's';
|
useType = 's';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sign)
|
if (sign) {
|
||||||
error("Formatting string with sign flag '%c'\n", sign);
|
error("Formatting string with sign flag '%c'\n", sign);
|
||||||
if (padZero)
|
}
|
||||||
|
if (padZero) {
|
||||||
error("Formatting string with padding flag '0'\n");
|
error("Formatting string with padding flag '0'\n");
|
||||||
if (hasFrac)
|
}
|
||||||
|
if (hasFrac) {
|
||||||
error("Formatting string with fractional width\n");
|
error("Formatting string with fractional width\n");
|
||||||
if (hasPrec)
|
}
|
||||||
|
if (hasPrec) {
|
||||||
error("Formatting string with fractional precision\n");
|
error("Formatting string with fractional precision\n");
|
||||||
if (useType != 's')
|
}
|
||||||
|
if (useType != 's') {
|
||||||
error("Formatting string as type '%c'\n", useType);
|
error("Formatting string as type '%c'\n", useType);
|
||||||
|
}
|
||||||
|
|
||||||
std::string useValue = exact ? escapeString(value) : value;
|
std::string useValue = exact ? escapeString(value) : value;
|
||||||
size_t valueLen = useValue.length();
|
size_t valueLen = useValue.length();
|
||||||
@@ -186,22 +201,28 @@ void FormatSpec::appendNumber(std::string &str, uint32_t value) const {
|
|||||||
useExact = true;
|
useExact = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useType != 'X' && useType != 'x' && useType != 'b' && useType != 'o' && useType != 'f' && useExact)
|
if (useType != 'X' && useType != 'x' && useType != 'b' && useType != 'o' && useType != 'f'
|
||||||
|
&& useExact) {
|
||||||
error("Formatting type '%c' with exact flag '#'\n", useType);
|
error("Formatting type '%c' with exact flag '#'\n", 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\n", 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\n", useType);
|
||||||
if (useType == 's')
|
}
|
||||||
|
if (useType == 's') {
|
||||||
error("Formatting number as type 's'\n");
|
error("Formatting number as type 's'\n");
|
||||||
|
}
|
||||||
|
|
||||||
char signChar = sign; // 0 or ' ' or '+'
|
char signChar = sign; // 0 or ' ' or '+'
|
||||||
|
|
||||||
if (useType == 'd' || useType == 'f') {
|
if (useType == 'd' || useType == 'f') {
|
||||||
if (int32_t v = value; v < 0) {
|
if (int32_t v = value; v < 0) {
|
||||||
signChar = '-';
|
signChar = '-';
|
||||||
if (v != INT32_MIN)
|
if (v != INT32_MIN) {
|
||||||
value = -v;
|
value = -v;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,15 +270,17 @@ void FormatSpec::appendNumber(std::string &str, uint32_t value) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
double fval = fabs(value / pow(2.0, usePrec));
|
double fval = fabs(value / pow(2.0, usePrec));
|
||||||
if (useExact)
|
if (int fracWidthArg = static_cast<int>(useFracWidth); useExact) {
|
||||||
snprintf(valueBuf, sizeof(valueBuf), "%.*fq%zu", (int)useFracWidth, fval, usePrec);
|
snprintf(valueBuf, sizeof(valueBuf), "%.*fq%zu", fracWidthArg, fval, usePrec);
|
||||||
else
|
} else {
|
||||||
snprintf(valueBuf, sizeof(valueBuf), "%.*f", (int)useFracWidth, fval);
|
snprintf(valueBuf, sizeof(valueBuf), "%.*f", fracWidthArg, fval);
|
||||||
|
}
|
||||||
} else if (useType == 'd') {
|
} else if (useType == 'd') {
|
||||||
// Decimal numbers may be formatted with a '-' sign by `snprintf`, so `abs` prevents that,
|
// Decimal numbers may be formatted with a '-' sign by `snprintf`, so `abs` prevents that,
|
||||||
// with a special case for `INT32_MIN` since `labs(INT32_MIN)` is UB. The sign will be
|
// with a special case for `INT32_MIN` since `labs(INT32_MIN)` is UB. The sign will be
|
||||||
// printed later from `signChar`.
|
// printed later from `signChar`.
|
||||||
uint32_t uval = value != (uint32_t)INT32_MIN ? labs((int32_t)value) : value;
|
uint32_t uval =
|
||||||
|
value != static_cast<uint32_t>(INT32_MIN) ? labs(static_cast<int32_t>(value)) : value;
|
||||||
snprintf(valueBuf, sizeof(valueBuf), "%" PRIu32, uval);
|
snprintf(valueBuf, sizeof(valueBuf), "%" PRIu32, uval);
|
||||||
} else {
|
} else {
|
||||||
char const *spec = useType == 'u' ? "%" PRIu32
|
char const *spec = useType == 'u' ? "%" PRIu32
|
||||||
@@ -276,27 +299,33 @@ void FormatSpec::appendNumber(std::string &str, uint32_t value) const {
|
|||||||
|
|
||||||
str.reserve(str.length() + totalLen);
|
str.reserve(str.length() + totalLen);
|
||||||
if (alignLeft) {
|
if (alignLeft) {
|
||||||
if (signChar)
|
if (signChar) {
|
||||||
str += signChar;
|
str += signChar;
|
||||||
if (prefixChar)
|
}
|
||||||
|
if (prefixChar) {
|
||||||
str += prefixChar;
|
str += prefixChar;
|
||||||
|
}
|
||||||
str.append(valueBuf);
|
str.append(valueBuf);
|
||||||
str.append(padLen, ' ');
|
str.append(padLen, ' ');
|
||||||
} else {
|
} else {
|
||||||
if (padZero) {
|
if (padZero) {
|
||||||
// sign, then prefix, then zero padding
|
// sign, then prefix, then zero padding
|
||||||
if (signChar)
|
if (signChar) {
|
||||||
str += signChar;
|
str += signChar;
|
||||||
if (prefixChar)
|
}
|
||||||
|
if (prefixChar) {
|
||||||
str += prefixChar;
|
str += prefixChar;
|
||||||
|
}
|
||||||
str.append(padLen, '0');
|
str.append(padLen, '0');
|
||||||
} else {
|
} else {
|
||||||
// space padding, then sign, then prefix
|
// space padding, then sign, then prefix
|
||||||
str.append(padLen, ' ');
|
str.append(padLen, ' ');
|
||||||
if (signChar)
|
if (signChar) {
|
||||||
str += signChar;
|
str += signChar;
|
||||||
if (prefixChar)
|
}
|
||||||
|
if (prefixChar) {
|
||||||
str += prefixChar;
|
str += prefixChar;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
str.append(valueBuf);
|
str.append(valueBuf);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#include "asm/fstack.hpp"
|
#include "asm/fstack.hpp"
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@@ -90,8 +90,9 @@ std::shared_ptr<std::string> fstk_GetUniqueIDStr() {
|
|||||||
std::shared_ptr<std::string> &str = contextStack.top().uniqueIDStr;
|
std::shared_ptr<std::string> &str = contextStack.top().uniqueIDStr;
|
||||||
|
|
||||||
// If a unique ID is allowed but has not been generated yet, generate one now.
|
// If a unique ID is allowed but has not been generated yet, generate one now.
|
||||||
if (str && str->empty())
|
if (str && str->empty()) {
|
||||||
*str = "_u"s + std::to_string(nextUniqueID++);
|
*str = "_u"s + std::to_string(nextUniqueID++);
|
||||||
|
}
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
@@ -103,27 +104,34 @@ MacroArgs *fstk_GetCurrentMacroArgs() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void fstk_AddIncludePath(std::string const &path) {
|
void fstk_AddIncludePath(std::string const &path) {
|
||||||
if (path.empty())
|
if (path.empty()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string &includePath = includePaths.emplace_back(path);
|
std::string &includePath = includePaths.emplace_back(path);
|
||||||
if (includePath.back() != '/')
|
if (includePath.back() != '/') {
|
||||||
includePath += '/';
|
includePath += '/';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_SetPreIncludeFile(std::string const &path) {
|
void fstk_SetPreIncludeFile(std::string const &path) {
|
||||||
if (!preIncludeName.empty())
|
if (!preIncludeName.empty()) {
|
||||||
warnx("Overriding pre-included filename %s", preIncludeName.c_str());
|
warnx("Overriding pre-included filename %s", preIncludeName.c_str());
|
||||||
|
}
|
||||||
preIncludeName = path;
|
preIncludeName = path;
|
||||||
if (verbose)
|
// LCOV_EXCL_START
|
||||||
|
if (verbose) {
|
||||||
printf("Pre-included filename %s\n", preIncludeName.c_str());
|
printf("Pre-included filename %s\n", preIncludeName.c_str());
|
||||||
|
}
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
}
|
}
|
||||||
|
|
||||||
static void printDep(std::string const &path) {
|
static void printDep(std::string const &path) {
|
||||||
if (dependFile) {
|
if (dependFile) {
|
||||||
fprintf(dependFile, "%s: %s\n", targetFileName.c_str(), path.c_str());
|
fprintf(dependFile, "%s: %s\n", targetFileName.c_str(), path.c_str());
|
||||||
if (generatePhonyDeps)
|
if (generatePhonyDeps) {
|
||||||
fprintf(dependFile, "%s:\n", path.c_str());
|
fprintf(dependFile, "%s:\n", path.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,20 +149,22 @@ std::optional<std::string> fstk_FindFile(std::string const &path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
errno = ENOENT;
|
errno = ENOENT;
|
||||||
if (generatedMissingIncludes)
|
if (generatedMissingIncludes) {
|
||||||
printDep(path);
|
printDep(path);
|
||||||
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool yywrap() {
|
bool yywrap() {
|
||||||
uint32_t ifDepth = lexer_GetIFDepth();
|
uint32_t ifDepth = lexer_GetIFDepth();
|
||||||
|
|
||||||
if (ifDepth != 0)
|
if (ifDepth != 0) {
|
||||||
fatalerror(
|
fatalerror(
|
||||||
"Ended block with %" PRIu32 " unterminated IF construct%s\n",
|
"Ended block with %" PRIu32 " unterminated IF construct%s\n",
|
||||||
ifDepth,
|
ifDepth,
|
||||||
ifDepth == 1 ? "" : "s"
|
ifDepth == 1 ? "" : "s"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (Context &context = contextStack.top(); context.fileInfo->type == NODE_REPT) {
|
if (Context &context = contextStack.top(); context.fileInfo->type == NODE_REPT) {
|
||||||
// The context is a REPT or FOR block, which may loop
|
// The context is a REPT or FOR block, which may loop
|
||||||
@@ -162,7 +172,7 @@ bool yywrap() {
|
|||||||
// If the node is referenced outside this context, we can't edit it, so duplicate it
|
// If the node is referenced outside this context, we can't edit it, so duplicate it
|
||||||
if (context.fileInfo.use_count() > 1) {
|
if (context.fileInfo.use_count() > 1) {
|
||||||
context.fileInfo = std::make_shared<FileStackNode>(*context.fileInfo);
|
context.fileInfo = std::make_shared<FileStackNode>(*context.fileInfo);
|
||||||
context.fileInfo->ID = -1; // The copy is not yet registered
|
context.fileInfo->ID = UINT32_MAX; // The copy is not yet registered
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint32_t> &fileInfoIters = context.fileInfo->iters();
|
std::vector<uint32_t> &fileInfoIters = context.fileInfo->iters();
|
||||||
@@ -170,13 +180,16 @@ bool yywrap() {
|
|||||||
// If this is a FOR, update the symbol value
|
// If this is a FOR, update the symbol value
|
||||||
if (context.isForLoop && fileInfoIters.front() <= context.nbReptIters) {
|
if (context.isForLoop && fileInfoIters.front() <= context.nbReptIters) {
|
||||||
// Avoid arithmetic overflow runtime error
|
// Avoid arithmetic overflow runtime error
|
||||||
uint32_t forValue = (uint32_t)context.forValue + (uint32_t)context.forStep;
|
uint32_t forValue =
|
||||||
context.forValue = forValue <= INT32_MAX ? forValue : -(int32_t)~forValue - 1;
|
static_cast<uint32_t>(context.forValue) + static_cast<uint32_t>(context.forStep);
|
||||||
|
context.forValue =
|
||||||
|
forValue <= INT32_MAX ? forValue : -static_cast<int32_t>(~forValue) - 1;
|
||||||
Symbol *sym = sym_AddVar(context.forName, context.forValue);
|
Symbol *sym = sym_AddVar(context.forName, context.forValue);
|
||||||
|
|
||||||
// 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");
|
fatalerror("Failed to update FOR symbol value\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Advance to the next iteration
|
// Advance to the next iteration
|
||||||
fileInfoIters.front()++;
|
fileInfoIters.front()++;
|
||||||
@@ -197,8 +210,9 @@ bool yywrap() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void checkRecursionDepth() {
|
static void checkRecursionDepth() {
|
||||||
if (contextStack.size() > maxRecursionDepth)
|
if (contextStack.size() > maxRecursionDepth) {
|
||||||
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
|
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool newFileContext(std::string const &filePath, bool updateStateNow) {
|
static bool newFileContext(std::string const &filePath, bool updateStateNow) {
|
||||||
@@ -208,7 +222,7 @@ static bool newFileContext(std::string const &filePath, bool updateStateNow) {
|
|||||||
std::shared_ptr<MacroArgs> macroArgs = nullptr;
|
std::shared_ptr<MacroArgs> macroArgs = nullptr;
|
||||||
|
|
||||||
auto fileInfo =
|
auto fileInfo =
|
||||||
std::make_shared<FileStackNode>(NODE_MACRO, filePath == "-" ? "<stdin>" : filePath);
|
std::make_shared<FileStackNode>(NODE_FILE, filePath == "-" ? "<stdin>" : filePath);
|
||||||
if (!contextStack.empty()) {
|
if (!contextStack.empty()) {
|
||||||
Context &oldContext = contextStack.top();
|
Context &oldContext = contextStack.top();
|
||||||
fileInfo->parent = oldContext.fileInfo;
|
fileInfo->parent = oldContext.fileInfo;
|
||||||
@@ -296,8 +310,11 @@ void fstk_RunInclude(std::string const &path, bool preInclude) {
|
|||||||
|
|
||||||
if (!fullPath) {
|
if (!fullPath) {
|
||||||
if (generatedMissingIncludes && !preInclude) {
|
if (generatedMissingIncludes && !preInclude) {
|
||||||
if (verbose)
|
// LCOV_EXCL_START
|
||||||
|
if (verbose) {
|
||||||
printf("Aborting (-MG) on INCLUDE file '%s' (%s)\n", path.c_str(), strerror(errno));
|
printf("Aborting (-MG) on INCLUDE file '%s' (%s)\n", path.c_str(), strerror(errno));
|
||||||
|
}
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
failedOnMissingInclude = true;
|
failedOnMissingInclude = true;
|
||||||
} else {
|
} else {
|
||||||
error("Unable to open included file '%s': %s\n", path.c_str(), strerror(errno));
|
error("Unable to open included file '%s': %s\n", path.c_str(), strerror(errno));
|
||||||
@@ -305,18 +322,20 @@ void fstk_RunInclude(std::string const &path, bool preInclude) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!newFileContext(*fullPath, false))
|
if (!newFileContext(*fullPath, false)) {
|
||||||
fatalerror("Failed to set up lexer for file include\n");
|
fatalerror("Failed to set up lexer for file include\n"); // LCOV_EXCL_LINE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_RunMacro(std::string const ¯oName, std::shared_ptr<MacroArgs> macroArgs) {
|
void fstk_RunMacro(std::string const ¯oName, std::shared_ptr<MacroArgs> macroArgs) {
|
||||||
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("Macro \"%s\" not defined; it was purged\n", macroName.c_str());
|
||||||
else
|
} else {
|
||||||
error("Macro \"%s\" not defined\n", macroName.c_str());
|
error("Macro \"%s\" not defined\n", macroName.c_str());
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (macro->type != SYM_MACRO) {
|
if (macro->type != SYM_MACRO) {
|
||||||
@@ -328,8 +347,9 @@ void fstk_RunMacro(std::string const ¯oName, std::shared_ptr<MacroArgs> macr
|
|||||||
}
|
}
|
||||||
|
|
||||||
void fstk_RunRept(uint32_t count, int32_t reptLineNo, ContentSpan const &span) {
|
void fstk_RunRept(uint32_t count, int32_t reptLineNo, ContentSpan const &span) {
|
||||||
if (count == 0)
|
if (count == 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
newReptContext(reptLineNo, span, count);
|
newReptContext(reptLineNo, span, count);
|
||||||
}
|
}
|
||||||
@@ -342,24 +362,28 @@ void fstk_RunFor(
|
|||||||
int32_t reptLineNo,
|
int32_t reptLineNo,
|
||||||
ContentSpan const &span
|
ContentSpan const &span
|
||||||
) {
|
) {
|
||||||
if (Symbol *sym = sym_AddVar(symName, start); sym->type != SYM_VAR)
|
if (Symbol *sym = sym_AddVar(symName, start); sym->type != SYM_VAR) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
if (step > 0 && start < stop)
|
if (step > 0 && start < stop) {
|
||||||
count = ((int64_t)stop - start - 1) / step + 1;
|
count = (static_cast<int64_t>(stop) - start - 1) / step + 1;
|
||||||
else if (step < 0 && stop < start)
|
} else if (step < 0 && stop < start) {
|
||||||
count = ((int64_t)start - stop - 1) / -(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\n");
|
||||||
|
}
|
||||||
|
|
||||||
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\n", start, stop, step
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (count == 0)
|
if (count == 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Context &context = newReptContext(reptLineNo, span, count);
|
Context &context = newReptContext(reptLineNo, span, count);
|
||||||
context.isForLoop = true;
|
context.isForLoop = true;
|
||||||
@@ -383,17 +407,20 @@ bool fstk_Break() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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);
|
fatalerror("Recursion limit (%zu) exceeded\n", newDepth);
|
||||||
|
}
|
||||||
maxRecursionDepth = newDepth;
|
maxRecursionDepth = newDepth;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_Init(std::string const &mainPath, size_t maxDepth) {
|
void fstk_Init(std::string const &mainPath, size_t maxDepth) {
|
||||||
if (!newFileContext(mainPath, true))
|
if (!newFileContext(mainPath, true)) {
|
||||||
fatalerror("Failed to open main file\n");
|
fatalerror("Failed to open main file\n");
|
||||||
|
}
|
||||||
|
|
||||||
maxRecursionDepth = maxDepth;
|
maxRecursionDepth = maxDepth;
|
||||||
|
|
||||||
if (!preIncludeName.empty())
|
if (!preIncludeName.empty()) {
|
||||||
fstk_RunInclude(preIncludeName, true);
|
fstk_RunInclude(preIncludeName, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#include "asm/macro.hpp"
|
#include "asm/macro.hpp"
|
||||||
|
|
||||||
@@ -6,28 +6,32 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "helpers.hpp"
|
|
||||||
|
|
||||||
#include "asm/warning.hpp"
|
#include "asm/warning.hpp"
|
||||||
|
|
||||||
#define MAXMACROARGS 99999
|
std::shared_ptr<std::string> MacroArgs::getArg(int32_t i) const {
|
||||||
|
// Bracketed macro arguments adjust negative indexes such that -1 is the last argument.
|
||||||
|
if (i < 0) {
|
||||||
|
i += args.size() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<std::string> MacroArgs::getArg(uint32_t i) const {
|
int32_t realIndex = i + shift - 1;
|
||||||
uint32_t realIndex = i + shift - 1;
|
|
||||||
|
|
||||||
return realIndex >= args.size() ? nullptr : args[realIndex];
|
return realIndex < 0 || static_cast<uint32_t>(realIndex) >= args.size() ? nullptr
|
||||||
|
: args[realIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<std::string> MacroArgs::getAllArgs() const {
|
std::shared_ptr<std::string> MacroArgs::getAllArgs() const {
|
||||||
size_t nbArgs = args.size();
|
size_t nbArgs = args.size();
|
||||||
|
|
||||||
if (shift >= nbArgs)
|
if (shift >= nbArgs) {
|
||||||
return std::make_shared<std::string>("");
|
return std::make_shared<std::string>("");
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
@@ -38,27 +42,27 @@ std::shared_ptr<std::string> MacroArgs::getAllArgs() const {
|
|||||||
str->append(*arg);
|
str->append(*arg);
|
||||||
|
|
||||||
// Commas go between args and after a last empty arg
|
// Commas go between args and after a last empty arg
|
||||||
if (i < nbArgs - 1 || arg->empty())
|
if (i < nbArgs - 1 || arg->empty()) {
|
||||||
str->push_back(','); // no space after comma
|
str->push_back(','); // no space after comma
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
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\n");
|
||||||
if (args.size() == MAXMACROARGS)
|
}
|
||||||
error("A maximum of " EXPAND_AND_STR(MAXMACROARGS) " arguments is allowed\n");
|
|
||||||
args.push_back(arg);
|
args.push_back(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 && ((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\n");
|
||||||
shift = nbArgs;
|
shift = nbArgs;
|
||||||
} else if (count < 0 && shift < (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\n");
|
||||||
shift = 0;
|
shift = 0;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
149
src/asm/main.cpp
149
src/asm/main.cpp
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#include "asm/main.hpp"
|
#include "asm/main.hpp"
|
||||||
|
|
||||||
@@ -37,30 +37,30 @@ static std::string make_escape(std::string &str) {
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
// All dollars needs to be doubled
|
// All dollars needs to be doubled
|
||||||
size_t nextPos = str.find("$", pos);
|
size_t nextPos = str.find("$", pos);
|
||||||
if (nextPos == std::string::npos)
|
if (nextPos == std::string::npos) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
escaped.append(str, pos, nextPos - pos);
|
escaped.append(str, pos, nextPos - pos);
|
||||||
escaped.append("$$");
|
escaped.append("$$");
|
||||||
pos = nextPos + QUOTEDSTRLEN("$");
|
pos = nextPos + literal_strlen("$");
|
||||||
}
|
}
|
||||||
escaped.append(str, pos, str.length() - pos);
|
escaped.append(str, pos, str.length() - pos);
|
||||||
return escaped;
|
return escaped;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Short options
|
// Short options
|
||||||
static char const *optstring = "b:D:Eg:I:M:o:P:p:Q:r:s:VvW:wX:";
|
static char const *optstring = "b:D:Eg:hI:M:o:P:p:Q:r:s:VvW:wX:";
|
||||||
|
|
||||||
// Variables for the long-only options
|
// Variables for the long-only options
|
||||||
static int depType; // Variants of `-M`
|
static int depType; // Variants of `-M`
|
||||||
|
|
||||||
// Equivalent long options
|
// Equivalent long options
|
||||||
// Please keep in the same order as short opts
|
// Please keep in the same order as short opts.
|
||||||
//
|
|
||||||
// Also, make sure long opts don't create ambiguity:
|
// Also, make sure long opts don't create ambiguity:
|
||||||
// A long opt's name should start with the same letter as its short opt,
|
// A long opt's name should start with the same letter as its short opt,
|
||||||
// except if it doesn't create any ambiguity (`verbose` versus `version`).
|
// except if it doesn't create any ambiguity (`verbose` versus `version`).
|
||||||
// This is because long opt matching, even to a single char, is prioritized
|
// This is because long opt matching, even to a single char, is prioritized
|
||||||
// over short opt matching
|
// over short opt matching.
|
||||||
static option const longopts[] = {
|
static option const longopts[] = {
|
||||||
{"binary-digits", required_argument, nullptr, 'b'},
|
{"binary-digits", required_argument, nullptr, 'b'},
|
||||||
{"define", required_argument, nullptr, 'D'},
|
{"define", required_argument, nullptr, 'D'},
|
||||||
@@ -69,6 +69,7 @@ static option const longopts[] = {
|
|||||||
{"include", required_argument, nullptr, 'I'},
|
{"include", required_argument, nullptr, 'I'},
|
||||||
{"dependfile", required_argument, nullptr, 'M'},
|
{"dependfile", required_argument, nullptr, 'M'},
|
||||||
{"MG", no_argument, &depType, 'G'},
|
{"MG", no_argument, &depType, 'G'},
|
||||||
|
{"help", no_argument, nullptr, 'h'},
|
||||||
{"MP", no_argument, &depType, 'P'},
|
{"MP", no_argument, &depType, 'P'},
|
||||||
{"MT", required_argument, &depType, 'T'},
|
{"MT", required_argument, &depType, 'T'},
|
||||||
{"warning", required_argument, nullptr, 'W'},
|
{"warning", required_argument, nullptr, 'W'},
|
||||||
@@ -86,9 +87,10 @@ static option const longopts[] = {
|
|||||||
{nullptr, no_argument, nullptr, 0 }
|
{nullptr, no_argument, nullptr, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// LCOV_EXCL_START
|
||||||
static void printUsage() {
|
static void printUsage() {
|
||||||
fputs(
|
fputs(
|
||||||
"Usage: rgbasm [-EVvw] [-b chars] [-D name[=value]] [-g chars] [-I path]\n"
|
"Usage: rgbasm [-EhVvw] [-b chars] [-D name[=value]] [-g chars] [-I path]\n"
|
||||||
" [-M depend_file] [-MG] [-MP] [-MT target_file] [-MQ target_file]\n"
|
" [-M depend_file] [-MG] [-MP] [-MT target_file] [-MQ target_file]\n"
|
||||||
" [-o out_file] [-P include_file] [-p pad_value] [-Q precision]\n"
|
" [-o out_file] [-P include_file] [-p pad_value] [-Q precision]\n"
|
||||||
" [-r depth] [-s features:state_file] [-W warning] [-X max_errors]\n"
|
" [-r depth] [-s features:state_file] [-W warning] [-X max_errors]\n"
|
||||||
@@ -106,17 +108,20 @@ static void printUsage() {
|
|||||||
stderr
|
stderr
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
time_t now = time(nullptr);
|
time_t now = time(nullptr);
|
||||||
// Support SOURCE_DATE_EPOCH for reproducible builds
|
// Support SOURCE_DATE_EPOCH for reproducible builds
|
||||||
// https://reproducible-builds.org/docs/source-date-epoch/
|
// https://reproducible-builds.org/docs/source-date-epoch/
|
||||||
if (char const *sourceDateEpoch = getenv("SOURCE_DATE_EPOCH"); sourceDateEpoch)
|
if (char const *sourceDateEpoch = getenv("SOURCE_DATE_EPOCH"); sourceDateEpoch) {
|
||||||
now = (time_t)strtoul(sourceDateEpoch, nullptr, 0);
|
now = static_cast<time_t>(strtoul(sourceDateEpoch, nullptr, 0));
|
||||||
|
}
|
||||||
|
|
||||||
Defer closeDependFile{[&] {
|
Defer closeDependFile{[&] {
|
||||||
if (dependFile)
|
if (dependFile) {
|
||||||
fclose(dependFile);
|
fclose(dependFile);
|
||||||
|
}
|
||||||
}};
|
}};
|
||||||
|
|
||||||
// Perform some init for below
|
// Perform some init for below
|
||||||
@@ -128,23 +133,25 @@ int main(int argc, char *argv[]) {
|
|||||||
opt_P(0);
|
opt_P(0);
|
||||||
opt_Q(16);
|
opt_Q(16);
|
||||||
sym_SetExportAll(false);
|
sym_SetExportAll(false);
|
||||||
uint32_t maxDepth = DEFAULT_MAX_DEPTH;
|
uint32_t maxDepth = 64;
|
||||||
char const *dependFileName = nullptr;
|
char const *dependFileName = nullptr;
|
||||||
std::unordered_map<std::string, std::vector<StateFeature>> stateFileSpecs;
|
std::unordered_map<std::string, std::vector<StateFeature>> stateFileSpecs;
|
||||||
std::string newTarget;
|
std::string newTarget;
|
||||||
// Maximum of 100 errors only applies if rgbasm is printing errors to a terminal.
|
// Maximum of 100 errors only applies if rgbasm is printing errors to a terminal.
|
||||||
if (isatty(STDERR_FILENO))
|
if (isatty(STDERR_FILENO)) {
|
||||||
maxErrors = 100;
|
maxErrors = 100;
|
||||||
|
}
|
||||||
|
|
||||||
for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) {
|
for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) {
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
char *endptr;
|
char *endptr;
|
||||||
|
|
||||||
case 'b':
|
case 'b':
|
||||||
if (strlen(musl_optarg) == 2)
|
if (strlen(musl_optarg) == 2) {
|
||||||
opt_B(musl_optarg);
|
opt_B(musl_optarg);
|
||||||
else
|
} else {
|
||||||
errx("Must specify exactly 2 characters for option 'b'");
|
errx("Must specify exactly 2 characters for option 'b'");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
char *equals;
|
char *equals;
|
||||||
@@ -163,19 +170,27 @@ int main(int argc, char *argv[]) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'g':
|
case 'g':
|
||||||
if (strlen(musl_optarg) == 4)
|
if (strlen(musl_optarg) == 4) {
|
||||||
opt_G(musl_optarg);
|
opt_G(musl_optarg);
|
||||||
else
|
} else {
|
||||||
errx("Must specify exactly 4 characters for option 'g'");
|
errx("Must specify exactly 4 characters for option 'g'");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'h':
|
||||||
|
// LCOV_EXCL_START
|
||||||
|
printUsage();
|
||||||
|
exit(0);
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
case 'I':
|
case 'I':
|
||||||
fstk_AddIncludePath(musl_optarg);
|
fstk_AddIncludePath(musl_optarg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'M':
|
case 'M':
|
||||||
if (dependFile)
|
if (dependFile) {
|
||||||
warnx("Overriding dependfile %s", dependFileName);
|
warnx("Overriding dependfile %s", dependFileName);
|
||||||
|
}
|
||||||
if (strcmp("-", musl_optarg)) {
|
if (strcmp("-", musl_optarg)) {
|
||||||
dependFile = fopen(musl_optarg, "w");
|
dependFile = fopen(musl_optarg, "w");
|
||||||
dependFileName = musl_optarg;
|
dependFileName = musl_optarg;
|
||||||
@@ -183,8 +198,9 @@ int main(int argc, char *argv[]) {
|
|||||||
dependFile = stdout;
|
dependFile = stdout;
|
||||||
dependFileName = "<stdout>";
|
dependFileName = "<stdout>";
|
||||||
}
|
}
|
||||||
if (dependFile == nullptr)
|
if (dependFile == nullptr) {
|
||||||
err("Failed to open dependfile \"%s\"", dependFileName);
|
err("Failed to open dependfile \"%s\"", dependFileName); // LCOV_EXCL_LINE
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'o':
|
case 'o':
|
||||||
@@ -199,11 +215,13 @@ int main(int argc, char *argv[]) {
|
|||||||
case 'p':
|
case 'p':
|
||||||
padByte = strtoul(musl_optarg, &endptr, 0);
|
padByte = strtoul(musl_optarg, &endptr, 0);
|
||||||
|
|
||||||
if (musl_optarg[0] == '\0' || *endptr != '\0')
|
if (musl_optarg[0] == '\0' || *endptr != '\0') {
|
||||||
errx("Invalid argument for option 'p'");
|
errx("Invalid argument for option 'p'");
|
||||||
|
}
|
||||||
|
|
||||||
if (padByte > 0xFF)
|
if (padByte > 0xFF) {
|
||||||
errx("Argument for option 'p' must be between 0 and 0xFF");
|
errx("Argument for option 'p' must be between 0 and 0xFF");
|
||||||
|
}
|
||||||
|
|
||||||
opt_P(padByte);
|
opt_P(padByte);
|
||||||
break;
|
break;
|
||||||
@@ -212,15 +230,18 @@ int main(int argc, char *argv[]) {
|
|||||||
char const *precisionArg;
|
char const *precisionArg;
|
||||||
case 'Q':
|
case 'Q':
|
||||||
precisionArg = musl_optarg;
|
precisionArg = musl_optarg;
|
||||||
if (precisionArg[0] == '.')
|
if (precisionArg[0] == '.') {
|
||||||
precisionArg++;
|
precisionArg++;
|
||||||
|
}
|
||||||
precision = strtoul(precisionArg, &endptr, 0);
|
precision = strtoul(precisionArg, &endptr, 0);
|
||||||
|
|
||||||
if (musl_optarg[0] == '\0' || *endptr != '\0')
|
if (musl_optarg[0] == '\0' || *endptr != '\0') {
|
||||||
errx("Invalid argument for option 'Q'");
|
errx("Invalid argument for option 'Q'");
|
||||||
|
}
|
||||||
|
|
||||||
if (precision < 1 || precision > 31)
|
if (precision < 1 || precision > 31) {
|
||||||
errx("Argument for option 'Q' must be between 1 and 31");
|
errx("Argument for option 'Q' must be between 1 and 31");
|
||||||
|
}
|
||||||
|
|
||||||
opt_Q(precision);
|
opt_Q(precision);
|
||||||
break;
|
break;
|
||||||
@@ -228,35 +249,41 @@ int main(int argc, char *argv[]) {
|
|||||||
case 'r':
|
case 'r':
|
||||||
maxDepth = strtoul(musl_optarg, &endptr, 0);
|
maxDepth = strtoul(musl_optarg, &endptr, 0);
|
||||||
|
|
||||||
if (musl_optarg[0] == '\0' || *endptr != '\0')
|
if (musl_optarg[0] == '\0' || *endptr != '\0') {
|
||||||
errx("Invalid argument for option 'r'");
|
errx("Invalid argument for option 'r'");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 's': {
|
case 's': {
|
||||||
// Split "<features>:<name>" so `musl_optarg` is "<features>" and `name` is "<name>"
|
// Split "<features>:<name>" so `musl_optarg` is "<features>" and `name` is "<name>"
|
||||||
char *name = strchr(musl_optarg, ':');
|
char *name = strchr(musl_optarg, ':');
|
||||||
if (!name)
|
if (!name) {
|
||||||
errx("Invalid argument for option 's'");
|
errx("Invalid argument for option 's'");
|
||||||
|
}
|
||||||
*name++ = '\0';
|
*name++ = '\0';
|
||||||
|
|
||||||
std::vector<StateFeature> features;
|
std::vector<StateFeature> features;
|
||||||
for (char *feature = musl_optarg; feature;) {
|
for (char *feature = musl_optarg; feature;) {
|
||||||
// Split "<feature>,<rest>" so `feature` is "<feature>" and `next` is "<rest>"
|
// Split "<feature>,<rest>" so `feature` is "<feature>" and `next` is "<rest>"
|
||||||
char *next = strchr(feature, ',');
|
char *next = strchr(feature, ',');
|
||||||
if (next)
|
if (next) {
|
||||||
*next++ = '\0';
|
*next++ = '\0';
|
||||||
|
}
|
||||||
// Trim whitespace from the beginning of `feature`...
|
// Trim whitespace from the beginning of `feature`...
|
||||||
feature += strspn(feature, " \t");
|
feature += strspn(feature, " \t");
|
||||||
// ...and from the end
|
// ...and from the end
|
||||||
if (char *end = strpbrk(feature, " \t"); end)
|
if (char *end = strpbrk(feature, " \t"); end) {
|
||||||
*end = '\0';
|
*end = '\0';
|
||||||
|
}
|
||||||
// A feature must be specified
|
// A feature must be specified
|
||||||
if (*feature == '\0')
|
if (*feature == '\0') {
|
||||||
errx("Empty feature for option 's'");
|
errx("Empty feature for option 's'");
|
||||||
|
}
|
||||||
// Parse the `feature` and update the `features` list
|
// Parse the `feature` and update the `features` list
|
||||||
if (!strcasecmp(feature, "all")) {
|
if (!strcasecmp(feature, "all")) {
|
||||||
if (!features.empty())
|
if (!features.empty()) {
|
||||||
warnx("Redundant feature before \"%s\" for option 's'", feature);
|
warnx("Redundant feature before \"%s\" for option 's'", feature);
|
||||||
|
}
|
||||||
features.assign({STATE_EQU, STATE_VAR, STATE_EQUS, STATE_CHAR, STATE_MACRO});
|
features.assign({STATE_EQU, STATE_VAR, STATE_EQUS, STATE_CHAR, STATE_MACRO});
|
||||||
} else {
|
} else {
|
||||||
StateFeature value = !strcasecmp(feature, "equ") ? STATE_EQU
|
StateFeature value = !strcasecmp(feature, "equ") ? STATE_EQU
|
||||||
@@ -276,10 +303,14 @@ int main(int argc, char *argv[]) {
|
|||||||
feature = next;
|
feature = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stateFileSpecs.find(name) != stateFileSpecs.end())
|
if (stateFileSpecs.find(name) != stateFileSpecs.end()) {
|
||||||
warnx("Overriding state filename %s", name);
|
warnx("Overriding state filename %s", name);
|
||||||
if (verbose)
|
}
|
||||||
|
// LCOV_EXCL_START
|
||||||
|
if (verbose) {
|
||||||
printf("State filename %s\n", name);
|
printf("State filename %s\n", name);
|
||||||
|
}
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
stateFileSpecs.emplace(name, std::move(features));
|
stateFileSpecs.emplace(name, std::move(features));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -289,11 +320,13 @@ int main(int argc, char *argv[]) {
|
|||||||
exit(0);
|
exit(0);
|
||||||
|
|
||||||
case 'v':
|
case 'v':
|
||||||
|
// LCOV_EXCL_START
|
||||||
verbose = true;
|
verbose = true;
|
||||||
break;
|
break;
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
case 'W':
|
case 'W':
|
||||||
processWarningFlag(musl_optarg);
|
opt_W(musl_optarg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'w':
|
case 'w':
|
||||||
@@ -304,11 +337,13 @@ int main(int argc, char *argv[]) {
|
|||||||
case 'X':
|
case 'X':
|
||||||
maxValue = strtoul(musl_optarg, &endptr, 0);
|
maxValue = strtoul(musl_optarg, &endptr, 0);
|
||||||
|
|
||||||
if (musl_optarg[0] == '\0' || *endptr != '\0')
|
if (musl_optarg[0] == '\0' || *endptr != '\0') {
|
||||||
errx("Invalid argument for option 'X'");
|
errx("Invalid argument for option 'X'");
|
||||||
|
}
|
||||||
|
|
||||||
if (maxValue > UINT_MAX)
|
if (maxValue > UINT_MAX) {
|
||||||
errx("Argument for option 'X' must be between 0 and %u", UINT_MAX);
|
errx("Argument for option 'X' must be between 0 and %u", UINT_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
maxErrors = maxValue;
|
maxErrors = maxValue;
|
||||||
break;
|
break;
|
||||||
@@ -327,10 +362,12 @@ int main(int argc, char *argv[]) {
|
|||||||
case 'Q':
|
case 'Q':
|
||||||
case 'T':
|
case 'T':
|
||||||
newTarget = musl_optarg;
|
newTarget = musl_optarg;
|
||||||
if (depType == 'Q')
|
if (depType == 'Q') {
|
||||||
newTarget = make_escape(newTarget);
|
newTarget = make_escape(newTarget);
|
||||||
if (!targetFileName.empty())
|
}
|
||||||
|
if (!targetFileName.empty()) {
|
||||||
targetFileName += ' ';
|
targetFileName += ' ';
|
||||||
|
}
|
||||||
targetFileName += newTarget;
|
targetFileName += newTarget;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -338,13 +375,16 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
// Unrecognized options
|
// Unrecognized options
|
||||||
default:
|
default:
|
||||||
|
// LCOV_EXCL_START
|
||||||
printUsage();
|
printUsage();
|
||||||
exit(1);
|
exit(1);
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetFileName.empty() && !objectFileName.empty())
|
if (targetFileName.empty() && !objectFileName.empty()) {
|
||||||
targetFileName = objectFileName;
|
targetFileName = objectFileName;
|
||||||
|
}
|
||||||
|
|
||||||
if (argc == musl_optind) {
|
if (argc == musl_optind) {
|
||||||
fputs(
|
fputs(
|
||||||
@@ -360,13 +400,15 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
std::string mainFileName = argv[musl_optind];
|
std::string mainFileName = argv[musl_optind];
|
||||||
|
|
||||||
if (verbose)
|
if (verbose) {
|
||||||
printf("Assembling %s\n", mainFileName.c_str());
|
printf("Assembling %s\n", mainFileName.c_str()); // LCOV_EXCL_LINE
|
||||||
|
}
|
||||||
|
|
||||||
if (dependFile) {
|
if (dependFile) {
|
||||||
if (targetFileName.empty())
|
if (targetFileName.empty()) {
|
||||||
errx("Dependency files can only be created if a target file is specified with either "
|
errx("Dependency files can only be created if a target file is specified with either "
|
||||||
"-o, -MQ or -MT");
|
"-o, -MQ or -MT");
|
||||||
|
}
|
||||||
|
|
||||||
fprintf(dependFile, "%s: %s\n", targetFileName.c_str(), mainFileName.c_str());
|
fprintf(dependFile, "%s: %s\n", targetFileName.c_str(), mainFileName.c_str());
|
||||||
}
|
}
|
||||||
@@ -377,23 +419,34 @@ int main(int argc, char *argv[]) {
|
|||||||
fstk_Init(mainFileName, maxDepth);
|
fstk_Init(mainFileName, maxDepth);
|
||||||
|
|
||||||
// Perform parse (`yy::parser` is auto-generated from `parser.y`)
|
// Perform parse (`yy::parser` is auto-generated from `parser.y`)
|
||||||
if (yy::parser parser; parser.parse() != 0 && nbErrors == 0)
|
if (yy::parser parser; parser.parse() != 0 && nbErrors == 0) {
|
||||||
nbErrors = 1;
|
nbErrors = 1;
|
||||||
|
}
|
||||||
|
|
||||||
sect_CheckUnionClosed();
|
if (!failedOnMissingInclude) {
|
||||||
sect_CheckSizes();
|
sect_CheckUnionClosed();
|
||||||
|
sect_CheckLoadClosed();
|
||||||
|
sect_CheckSizes();
|
||||||
|
|
||||||
if (nbErrors != 0)
|
charmap_CheckStack();
|
||||||
|
opt_CheckStack();
|
||||||
|
sect_CheckStack();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nbErrors != 0) {
|
||||||
errx("Assembly aborted (%u error%s)!", nbErrors, nbErrors == 1 ? "" : "s");
|
errx("Assembly aborted (%u error%s)!", nbErrors, nbErrors == 1 ? "" : "s");
|
||||||
|
}
|
||||||
|
|
||||||
// If parse aborted due to missing an include, and `-MG` was given, exit normally
|
// If parse aborted due to missing an include, and `-MG` was given, exit normally
|
||||||
if (failedOnMissingInclude)
|
if (failedOnMissingInclude) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
out_WriteObject();
|
out_WriteObject();
|
||||||
|
|
||||||
for (auto [name, features] : stateFileSpecs)
|
for (auto [name, features] : stateFileSpecs) {
|
||||||
out_WriteState(name, features);
|
out_WriteState(name, features);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@@ -13,8 +13,6 @@
|
|||||||
#include "asm/section.hpp"
|
#include "asm/section.hpp"
|
||||||
#include "asm/warning.hpp"
|
#include "asm/warning.hpp"
|
||||||
|
|
||||||
static constexpr size_t numWarningStates = sizeof(warningStates);
|
|
||||||
|
|
||||||
struct OptStackEntry {
|
struct OptStackEntry {
|
||||||
char binary[2];
|
char binary[2];
|
||||||
char gbgfx[4];
|
char gbgfx[4];
|
||||||
@@ -22,7 +20,7 @@ struct OptStackEntry {
|
|||||||
uint8_t fillByte;
|
uint8_t fillByte;
|
||||||
bool warningsAreErrors;
|
bool warningsAreErrors;
|
||||||
size_t maxRecursionDepth;
|
size_t maxRecursionDepth;
|
||||||
WarningState warningStates[numWarningStates];
|
Diagnostics warningStates;
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::stack<OptStackEntry> stack;
|
static std::stack<OptStackEntry> stack;
|
||||||
@@ -55,17 +53,19 @@ void opt_W(char const *flag) {
|
|||||||
void opt_Parse(char const *s) {
|
void opt_Parse(char const *s) {
|
||||||
switch (s[0]) {
|
switch (s[0]) {
|
||||||
case 'b':
|
case 'b':
|
||||||
if (strlen(&s[1]) == 2)
|
if (strlen(&s[1]) == 2) {
|
||||||
opt_B(&s[1]);
|
opt_B(&s[1]);
|
||||||
else
|
} else {
|
||||||
error("Must specify exactly 2 characters for option 'b'\n");
|
error("Must specify exactly 2 characters for option 'b'\n");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'g':
|
case 'g':
|
||||||
if (strlen(&s[1]) == 4)
|
if (strlen(&s[1]) == 4) {
|
||||||
opt_G(&s[1]);
|
opt_G(&s[1]);
|
||||||
else
|
} else {
|
||||||
error("Must specify exactly 4 characters for option 'g'\n");
|
error("Must specify exactly 4 characters for option 'g'\n");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'p':
|
case 'p':
|
||||||
@@ -74,12 +74,13 @@ void opt_Parse(char const *s) {
|
|||||||
unsigned int padByte;
|
unsigned int padByte;
|
||||||
|
|
||||||
result = sscanf(&s[1], "%x", &padByte);
|
result = sscanf(&s[1], "%x", &padByte);
|
||||||
if (result != 1)
|
if (result != 1) {
|
||||||
error("Invalid argument for option 'p'\n");
|
error("Invalid argument for option 'p'\n");
|
||||||
else if (padByte > 0xFF)
|
} else if (padByte > 0xFF) {
|
||||||
error("Argument for option 'p' must be between 0 and 0xFF\n");
|
error("Argument for option 'p' must be between 0 and 0xFF\n");
|
||||||
else
|
} else {
|
||||||
opt_P(padByte);
|
opt_P(padByte);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
error("Invalid argument for option 'p'\n");
|
error("Invalid argument for option 'p'\n");
|
||||||
}
|
}
|
||||||
@@ -88,19 +89,21 @@ void opt_Parse(char const *s) {
|
|||||||
char const *precisionArg;
|
char const *precisionArg;
|
||||||
case 'Q':
|
case 'Q':
|
||||||
precisionArg = &s[1];
|
precisionArg = &s[1];
|
||||||
if (precisionArg[0] == '.')
|
if (precisionArg[0] == '.') {
|
||||||
precisionArg++;
|
precisionArg++;
|
||||||
|
}
|
||||||
if (strlen(precisionArg) <= 2) {
|
if (strlen(precisionArg) <= 2) {
|
||||||
int result;
|
int result;
|
||||||
unsigned int precision;
|
unsigned int precision;
|
||||||
|
|
||||||
result = sscanf(precisionArg, "%u", &precision);
|
result = sscanf(precisionArg, "%u", &precision);
|
||||||
if (result != 1)
|
if (result != 1) {
|
||||||
error("Invalid argument for option 'Q'\n");
|
error("Invalid argument for option 'Q'\n");
|
||||||
else if (precision < 1 || precision > 31)
|
} else if (precision < 1 || precision > 31) {
|
||||||
error("Argument for option 'Q' must be between 1 and 31\n");
|
error("Argument for option 'Q' must be between 1 and 31\n");
|
||||||
else
|
} else {
|
||||||
opt_Q(precision);
|
opt_Q(precision);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
error("Invalid argument for option 'Q'\n");
|
error("Invalid argument for option 'Q'\n");
|
||||||
}
|
}
|
||||||
@@ -108,8 +111,9 @@ void opt_Parse(char const *s) {
|
|||||||
|
|
||||||
case 'r': {
|
case 'r': {
|
||||||
++s; // Skip 'r'
|
++s; // Skip 'r'
|
||||||
while (isblank(*s))
|
while (isblank(*s)) {
|
||||||
++s; // Skip leading whitespace
|
++s; // Skip leading whitespace
|
||||||
|
}
|
||||||
|
|
||||||
if (s[0] == '\0') {
|
if (s[0] == '\0') {
|
||||||
error("Missing argument to option 'r'\n");
|
error("Missing argument to option 'r'\n");
|
||||||
@@ -130,10 +134,11 @@ void opt_Parse(char const *s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'W':
|
case 'W':
|
||||||
if (strlen(&s[1]) > 0)
|
if (strlen(&s[1]) > 0) {
|
||||||
opt_W(&s[1]);
|
opt_W(&s[1]);
|
||||||
else
|
} else {
|
||||||
error("Must specify an argument for option 'W'\n");
|
error("Must specify an argument for option 'W'\n");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -160,7 +165,7 @@ void opt_Push() {
|
|||||||
|
|
||||||
// Both of these pulled from warning.hpp
|
// Both of these pulled from warning.hpp
|
||||||
entry.warningsAreErrors = warningsAreErrors;
|
entry.warningsAreErrors = warningsAreErrors;
|
||||||
memcpy(entry.warningStates, warningStates, numWarningStates);
|
entry.warningStates = warningStates;
|
||||||
|
|
||||||
entry.maxRecursionDepth = maxRecursionDepth; // Pulled from fstack.h
|
entry.maxRecursionDepth = maxRecursionDepth; // Pulled from fstack.h
|
||||||
|
|
||||||
@@ -184,5 +189,11 @@ void opt_Pop() {
|
|||||||
|
|
||||||
// opt_W does not apply a whole warning state; it processes one flag string
|
// opt_W does not apply a whole warning state; it processes one flag string
|
||||||
warningsAreErrors = entry.warningsAreErrors;
|
warningsAreErrors = entry.warningsAreErrors;
|
||||||
memcpy(warningStates, entry.warningStates, numWarningStates);
|
warningStates = entry.warningStates;
|
||||||
|
}
|
||||||
|
|
||||||
|
void opt_CheckStack() {
|
||||||
|
if (!stack.empty()) {
|
||||||
|
warning(WARNING_UNMATCHED_DIRECTIVE, "`PUSHO` without corresponding `POPO`\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#include "asm/output.hpp"
|
#include "asm/output.hpp"
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include "error.hpp"
|
#include "error.hpp"
|
||||||
#include "helpers.hpp" // assume, Defer
|
#include "helpers.hpp" // assume, Defer
|
||||||
|
#include "platform.hpp"
|
||||||
|
|
||||||
#include "asm/charmap.hpp"
|
#include "asm/charmap.hpp"
|
||||||
#include "asm/fstack.hpp"
|
#include "asm/fstack.hpp"
|
||||||
@@ -39,10 +40,10 @@ static std::deque<std::shared_ptr<FileStackNode>> fileStackNodes;
|
|||||||
|
|
||||||
static void putLong(uint32_t n, FILE *file) {
|
static void putLong(uint32_t n, FILE *file) {
|
||||||
uint8_t bytes[] = {
|
uint8_t bytes[] = {
|
||||||
(uint8_t)n,
|
static_cast<uint8_t>(n),
|
||||||
(uint8_t)(n >> 8),
|
static_cast<uint8_t>(n >> 8),
|
||||||
(uint8_t)(n >> 16),
|
static_cast<uint8_t>(n >> 16),
|
||||||
(uint8_t)(n >> 24),
|
static_cast<uint8_t>(n >> 24),
|
||||||
};
|
};
|
||||||
fwrite(bytes, 1, sizeof(bytes), file);
|
fwrite(bytes, 1, sizeof(bytes), file);
|
||||||
}
|
}
|
||||||
@@ -54,25 +55,28 @@ static void putString(std::string const &s, FILE *file) {
|
|||||||
|
|
||||||
void out_RegisterNode(std::shared_ptr<FileStackNode> node) {
|
void out_RegisterNode(std::shared_ptr<FileStackNode> node) {
|
||||||
// If node is not already registered, register it (and parents), and give it a unique ID
|
// If node is not already registered, register it (and parents), and give it a unique ID
|
||||||
for (; node && node->ID == (uint32_t)-1; node = node->parent) {
|
for (; node && node->ID == UINT32_MAX; node = node->parent) {
|
||||||
node->ID = fileStackNodes.size();
|
node->ID = fileStackNodes.size();
|
||||||
fileStackNodes.push_front(node);
|
fileStackNodes.push_front(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a section's ID, or -1 if the section is not in the list
|
// Return a section's ID, or UINT32_MAX if the section does not exist
|
||||||
static uint32_t getSectIDIfAny(Section *sect) {
|
static uint32_t getSectIDIfAny(Section *sect) {
|
||||||
if (!sect)
|
if (!sect) {
|
||||||
return (uint32_t)-1;
|
return UINT32_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
if (auto search = sectionMap.find(sect->name); search != sectionMap.end())
|
if (auto search = sectionMap.find(sect->name); search != sectionMap.end()) {
|
||||||
return (uint32_t)(sectionMap.size() - search->second - 1);
|
return static_cast<uint32_t>(search->second);
|
||||||
|
}
|
||||||
|
|
||||||
fatalerror("Unknown section '%s'\n", sect->name.c_str());
|
// Every section that exists should be in `sectionMap`
|
||||||
|
fatalerror("Unknown section '%s'\n", sect->name.c_str()); // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writePatch(Patch const &patch, FILE *file) {
|
static void writePatch(Patch const &patch, FILE *file) {
|
||||||
assume(patch.src->ID != (uint32_t)-1);
|
assume(patch.src->ID != UINT32_MAX);
|
||||||
|
|
||||||
putLong(patch.src->ID, file);
|
putLong(patch.src->ID, file);
|
||||||
putLong(patch.lineNo, file);
|
putLong(patch.lineNo, file);
|
||||||
@@ -85,7 +89,7 @@ static void writePatch(Patch const &patch, FILE *file) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void writeSection(Section const §, FILE *file) {
|
static void writeSection(Section const §, FILE *file) {
|
||||||
assume(sect.src->ID != (uint32_t)-1);
|
assume(sect.src->ID != UINT32_MAX);
|
||||||
|
|
||||||
putString(sect.name, file);
|
putString(sect.name, file);
|
||||||
|
|
||||||
@@ -108,8 +112,9 @@ static void writeSection(Section const §, FILE *file) {
|
|||||||
fwrite(sect.data.data(), 1, sect.size, file);
|
fwrite(sect.data.data(), 1, sect.size, file);
|
||||||
putLong(sect.patches.size(), file);
|
putLong(sect.patches.size(), file);
|
||||||
|
|
||||||
for (Patch const &patch : sect.patches)
|
for (Patch const &patch : sect.patches) {
|
||||||
writePatch(patch, file);
|
writePatch(patch, file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +123,7 @@ static void writeSymbol(Symbol const &sym, FILE *file) {
|
|||||||
if (!sym.isDefined()) {
|
if (!sym.isDefined()) {
|
||||||
putc(SYMTYPE_IMPORT, file);
|
putc(SYMTYPE_IMPORT, file);
|
||||||
} else {
|
} else {
|
||||||
assume(sym.src->ID != (uint32_t)-1);
|
assume(sym.src->ID != UINT32_MAX);
|
||||||
|
|
||||||
putc(sym.isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, file);
|
putc(sym.isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, file);
|
||||||
putLong(sym.src->ID, file);
|
putLong(sym.src->ID, file);
|
||||||
@@ -130,7 +135,7 @@ static void writeSymbol(Symbol const &sym, FILE *file) {
|
|||||||
|
|
||||||
static void registerUnregisteredSymbol(Symbol &sym) {
|
static void registerUnregisteredSymbol(Symbol &sym) {
|
||||||
// Check for `sym.src`, to skip any built-in symbol from rgbasm
|
// Check for `sym.src`, to skip any built-in symbol from rgbasm
|
||||||
if (sym.src && sym.ID == (uint32_t)-1 && !sym_IsPC(&sym)) {
|
if (sym.src && sym.ID == UINT32_MAX && !sym_IsPC(&sym)) {
|
||||||
sym.ID = objectSymbols.size(); // Set the symbol's ID within the object file
|
sym.ID = objectSymbols.size(); // Set the symbol's ID within the object file
|
||||||
objectSymbols.push_back(&sym);
|
objectSymbols.push_back(&sym);
|
||||||
out_RegisterNode(sym.src);
|
out_RegisterNode(sym.src);
|
||||||
@@ -161,8 +166,9 @@ static void writeRpn(std::vector<uint8_t> &rpnexpr, std::vector<uint8_t> const &
|
|||||||
symName.clear();
|
symName.clear();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
uint8_t c = rpn[offset++];
|
uint8_t c = rpn[offset++];
|
||||||
if (c == 0)
|
if (c == 0) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
symName += c;
|
symName += c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,7 +176,7 @@ static void writeRpn(std::vector<uint8_t> &rpnexpr, std::vector<uint8_t> const &
|
|||||||
sym = sym_FindExactSymbol(symName);
|
sym = sym_FindExactSymbol(symName);
|
||||||
if (sym->isConstant()) {
|
if (sym->isConstant()) {
|
||||||
rpnexpr[rpnptr++] = RPN_CONST;
|
rpnexpr[rpnptr++] = RPN_CONST;
|
||||||
value = sym_GetConstantValue(symName);
|
value = sym->getConstantValue();
|
||||||
} else {
|
} else {
|
||||||
rpnexpr[rpnptr++] = RPN_SYM;
|
rpnexpr[rpnptr++] = RPN_SYM;
|
||||||
registerUnregisteredSymbol(*sym); // Ensure that `sym->ID` is set
|
registerUnregisteredSymbol(*sym); // Ensure that `sym->ID` is set
|
||||||
@@ -187,8 +193,9 @@ static void writeRpn(std::vector<uint8_t> &rpnexpr, std::vector<uint8_t> const &
|
|||||||
symName.clear();
|
symName.clear();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
uint8_t c = rpn[offset++];
|
uint8_t c = rpn[offset++];
|
||||||
if (c == 0)
|
if (c == 0) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
symName += c;
|
symName += c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,13 +288,13 @@ void out_CreateAssert(
|
|||||||
assertion.message = message;
|
assertion.message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writeAssert(Assertion &assert, FILE *file) {
|
static void writeAssert(Assertion const &assert, FILE *file) {
|
||||||
writePatch(assert.patch, file);
|
writePatch(assert.patch, file);
|
||||||
putString(assert.message, file);
|
putString(assert.message, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writeFileStackNode(FileStackNode const &node, FILE *file) {
|
static void writeFileStackNode(FileStackNode const &node, FILE *file) {
|
||||||
putLong(node.parent ? node.parent->ID : (uint32_t)-1, file);
|
putLong(node.parent ? node.parent->ID : UINT32_MAX, file);
|
||||||
putLong(node.lineNo, file);
|
putLong(node.lineNo, file);
|
||||||
putc(node.type, file);
|
putc(node.type, file);
|
||||||
if (node.type != NODE_REPT) {
|
if (node.type != NODE_REPT) {
|
||||||
@@ -297,30 +304,34 @@ static void writeFileStackNode(FileStackNode const &node, FILE *file) {
|
|||||||
|
|
||||||
putLong(nodeIters.size(), file);
|
putLong(nodeIters.size(), file);
|
||||||
// Iters are stored by decreasing depth, so reverse the order for output
|
// Iters are stored by decreasing depth, so reverse the order for output
|
||||||
for (uint32_t i = nodeIters.size(); i--;)
|
for (uint32_t i = nodeIters.size(); i--;) {
|
||||||
putLong(nodeIters[i], file);
|
putLong(nodeIters[i], file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void out_WriteObject() {
|
void out_WriteObject() {
|
||||||
if (objectFileName.empty())
|
if (objectFileName.empty()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
FILE *file;
|
FILE *file;
|
||||||
if (objectFileName != "-") {
|
if (objectFileName != "-") {
|
||||||
file = fopen(objectFileName.c_str(), "wb");
|
file = fopen(objectFileName.c_str(), "wb");
|
||||||
} else {
|
} else {
|
||||||
objectFileName = "<stdout>";
|
objectFileName = "<stdout>";
|
||||||
file = fdopen(STDOUT_FILENO, "wb");
|
(void)setmode(STDOUT_FILENO, O_BINARY);
|
||||||
|
file = stdout;
|
||||||
|
}
|
||||||
|
if (!file) {
|
||||||
|
err("Failed to open object file '%s'", objectFileName.c_str()); // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
if (!file)
|
|
||||||
err("Failed to open object file '%s'", objectFileName.c_str());
|
|
||||||
Defer closeFile{[&] { fclose(file); }};
|
Defer closeFile{[&] { fclose(file); }};
|
||||||
|
|
||||||
// Also write symbols that weren't written above
|
// Also write symbols that weren't written above
|
||||||
sym_ForEach(registerUnregisteredSymbol);
|
sym_ForEach(registerUnregisteredSymbol);
|
||||||
|
|
||||||
fprintf(file, RGBDS_OBJECT_VERSION_STRING);
|
fputs(RGBDS_OBJECT_VERSION_STRING, file);
|
||||||
putLong(RGBDS_OBJECT_REV, file);
|
putLong(RGBDS_OBJECT_REV, file);
|
||||||
|
|
||||||
putLong(objectSymbols.size(), file);
|
putLong(objectSymbols.size(), file);
|
||||||
@@ -333,33 +344,34 @@ void out_WriteObject() {
|
|||||||
writeFileStackNode(node, file);
|
writeFileStackNode(node, file);
|
||||||
|
|
||||||
// The list is supposed to have decrementing IDs
|
// The list is supposed to have decrementing IDs
|
||||||
if (it + 1 != fileStackNodes.end() && it[1]->ID != node.ID - 1)
|
assume(it + 1 == fileStackNodes.end() || it[1]->ID == node.ID - 1);
|
||||||
fatalerror(
|
|
||||||
"Internal error: fstack node #%" PRIu32 " follows #%" PRIu32
|
|
||||||
". Please report this to the developers!\n",
|
|
||||||
it[1]->ID,
|
|
||||||
node.ID
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Symbol const *sym : objectSymbols)
|
for (Symbol const *sym : objectSymbols) {
|
||||||
writeSymbol(*sym, file);
|
writeSymbol(*sym, file);
|
||||||
|
}
|
||||||
|
|
||||||
for (auto it = sectionList.rbegin(); it != sectionList.rend(); it++)
|
for (Section const § : sectionList) {
|
||||||
writeSection(*it, file);
|
writeSection(sect, file);
|
||||||
|
}
|
||||||
|
|
||||||
putLong(assertions.size(), file);
|
putLong(assertions.size(), file);
|
||||||
|
|
||||||
for (Assertion &assert : assertions)
|
for (Assertion const &assert : assertions) {
|
||||||
writeAssert(assert, file);
|
writeAssert(assert, file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void out_SetFileName(std::string const &name) {
|
void out_SetFileName(std::string const &name) {
|
||||||
if (!objectFileName.empty())
|
if (!objectFileName.empty()) {
|
||||||
warnx("Overriding output filename %s", objectFileName.c_str());
|
warnx("Overriding output filename %s", objectFileName.c_str());
|
||||||
|
}
|
||||||
objectFileName = name;
|
objectFileName = name;
|
||||||
if (verbose)
|
// LCOV_EXCL_START
|
||||||
|
if (verbose) {
|
||||||
printf("Output filename %s\n", objectFileName.c_str());
|
printf("Output filename %s\n", objectFileName.c_str());
|
||||||
|
}
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dumpString(std::string const &escape, FILE *file) {
|
static void dumpString(std::string const &escape, FILE *file) {
|
||||||
@@ -395,8 +407,9 @@ static bool dumpEquConstants(FILE *file) {
|
|||||||
equConstants.clear();
|
equConstants.clear();
|
||||||
|
|
||||||
sym_ForEach([](Symbol &sym) {
|
sym_ForEach([](Symbol &sym) {
|
||||||
if (!sym.isBuiltin && sym.type == SYM_EQU)
|
if (!sym.isBuiltin && sym.type == SYM_EQU) {
|
||||||
equConstants.push_back(&sym);
|
equConstants.push_back(&sym);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// Constants are ordered by file, then by definition order
|
// Constants are ordered by file, then by definition order
|
||||||
std::sort(RANGE(equConstants), [](Symbol *sym1, Symbol *sym2) -> bool {
|
std::sort(RANGE(equConstants), [](Symbol *sym1, Symbol *sym2) -> bool {
|
||||||
@@ -416,8 +429,9 @@ static bool dumpVariables(FILE *file) {
|
|||||||
variables.clear();
|
variables.clear();
|
||||||
|
|
||||||
sym_ForEach([](Symbol &sym) {
|
sym_ForEach([](Symbol &sym) {
|
||||||
if (!sym.isBuiltin && sym.type == SYM_VAR)
|
if (!sym.isBuiltin && sym.type == SYM_VAR) {
|
||||||
variables.push_back(&sym);
|
variables.push_back(&sym);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// Variables are ordered by file, then by definition order
|
// Variables are ordered by file, then by definition order
|
||||||
std::sort(RANGE(variables), [](Symbol *sym1, Symbol *sym2) -> bool {
|
std::sort(RANGE(variables), [](Symbol *sym1, Symbol *sym2) -> bool {
|
||||||
@@ -437,8 +451,9 @@ static bool dumpEqusConstants(FILE *file) {
|
|||||||
equsConstants.clear();
|
equsConstants.clear();
|
||||||
|
|
||||||
sym_ForEach([](Symbol &sym) {
|
sym_ForEach([](Symbol &sym) {
|
||||||
if (!sym.isBuiltin && sym.type == SYM_EQUS)
|
if (!sym.isBuiltin && sym.type == SYM_EQUS) {
|
||||||
equsConstants.push_back(&sym);
|
equsConstants.push_back(&sym);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// Constants are ordered by file, then by definition order
|
// Constants are ordered by file, then by definition order
|
||||||
std::sort(RANGE(equsConstants), [](Symbol *sym1, Symbol *sym2) -> bool {
|
std::sort(RANGE(equsConstants), [](Symbol *sym1, Symbol *sym2) -> bool {
|
||||||
@@ -465,8 +480,9 @@ static bool dumpCharmaps(FILE *file) {
|
|||||||
fputs("charmap \"", charmapFile);
|
fputs("charmap \"", charmapFile);
|
||||||
dumpString(mapping, charmapFile);
|
dumpString(mapping, charmapFile);
|
||||||
putc('"', charmapFile);
|
putc('"', charmapFile);
|
||||||
for (int32_t v : value)
|
for (int32_t v : value) {
|
||||||
fprintf(charmapFile, ", $%" PRIx32, v);
|
fprintf(charmapFile, ", $%" PRIx32, v);
|
||||||
|
}
|
||||||
putc('\n', charmapFile);
|
putc('\n', charmapFile);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -477,8 +493,9 @@ static bool dumpMacros(FILE *file) {
|
|||||||
macros.clear();
|
macros.clear();
|
||||||
|
|
||||||
sym_ForEach([](Symbol &sym) {
|
sym_ForEach([](Symbol &sym) {
|
||||||
if (!sym.isBuiltin && sym.type == SYM_MACRO)
|
if (!sym.isBuiltin && sym.type == SYM_MACRO) {
|
||||||
macros.push_back(&sym);
|
macros.push_back(&sym);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// Macros are ordered by file, then by definition order
|
// Macros are ordered by file, then by definition order
|
||||||
std::sort(RANGE(macros), [](Symbol *sym1, Symbol *sym2) -> bool {
|
std::sort(RANGE(macros), [](Symbol *sym1, Symbol *sym2) -> bool {
|
||||||
@@ -503,10 +520,12 @@ void out_WriteState(std::string name, std::vector<StateFeature> const &features)
|
|||||||
file = fopen(name.c_str(), "wb");
|
file = fopen(name.c_str(), "wb");
|
||||||
} else {
|
} else {
|
||||||
name = "<stdout>";
|
name = "<stdout>";
|
||||||
file = fdopen(STDOUT_FILENO, "wb");
|
(void)setmode(STDOUT_FILENO, O_BINARY);
|
||||||
|
file = stdout;
|
||||||
|
}
|
||||||
|
if (!file) {
|
||||||
|
err("Failed to open state file '%s'", name.c_str()); // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
if (!file)
|
|
||||||
err("Failed to open state file '%s'", name.c_str());
|
|
||||||
Defer closeFile{[&] { fclose(file); }};
|
Defer closeFile{[&] { fclose(file); }};
|
||||||
|
|
||||||
static char const *dumpHeadings[NB_STATE_FEATURES] = {
|
static char const *dumpHeadings[NB_STATE_FEATURES] = {
|
||||||
@@ -527,7 +546,8 @@ void out_WriteState(std::string name, std::vector<StateFeature> const &features)
|
|||||||
fputs("; File generated by rgbasm\n", file);
|
fputs("; File generated by rgbasm\n", file);
|
||||||
for (StateFeature feature : features) {
|
for (StateFeature feature : features) {
|
||||||
fprintf(file, "\n; %s\n", dumpHeadings[feature]);
|
fprintf(file, "\n; %s\n", dumpHeadings[feature]);
|
||||||
if (!dumpFuncs[feature](file))
|
if (!dumpFuncs[feature](file)) {
|
||||||
fprintf(file, "; No values\n");
|
fprintf(file, "; No values\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1374
src/asm/parser.y
1374
src/asm/parser.y
File diff suppressed because it is too large
Load Diff
272
src/asm/rpn.cpp
272
src/asm/rpn.cpp
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#include "asm/rpn.hpp"
|
#include "asm/rpn.hpp"
|
||||||
|
|
||||||
@@ -46,17 +46,19 @@ int32_t Expression::getConstVal() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Symbol const *Expression::symbolOf() const {
|
Symbol const *Expression::symbolOf() const {
|
||||||
if (!isSymbol)
|
if (!isSymbol) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return sym_FindScopedSymbol((char const *)&rpn[1]);
|
}
|
||||||
|
return sym_FindScopedSymbol(reinterpret_cast<char const *>(&rpn[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Expression::isDiffConstant(Symbol const *sym) const {
|
bool Expression::isDiffConstant(Symbol const *sym) const {
|
||||||
// Check if both expressions only refer to a single symbol
|
// Check if both expressions only refer to a single symbol
|
||||||
Symbol const *sym1 = symbolOf();
|
Symbol const *sym1 = symbolOf();
|
||||||
|
|
||||||
if (!sym1 || !sym || sym1->type != SYM_LABEL || sym->type != SYM_LABEL)
|
if (!sym1 || !sym || sym1->type != SYM_LABEL || sym->type != SYM_LABEL) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Section const *sect1 = sym1->getSection();
|
Section const *sect1 = sym1->getSection();
|
||||||
Section const *sect2 = sym->getSection();
|
Section const *sect2 = sym->getSection();
|
||||||
@@ -65,7 +67,7 @@ bool Expression::isDiffConstant(Symbol const *sym) const {
|
|||||||
|
|
||||||
void Expression::makeNumber(uint32_t value) {
|
void Expression::makeNumber(uint32_t value) {
|
||||||
clear();
|
clear();
|
||||||
data = (int32_t)value;
|
data = static_cast<int32_t>(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Expression::makeSymbol(std::string const &symName) {
|
void Expression::makeSymbol(std::string const &symName) {
|
||||||
@@ -73,13 +75,16 @@ void Expression::makeSymbol(std::string const &symName) {
|
|||||||
if (Symbol *sym = sym_FindScopedSymbol(symName); sym_IsPC(sym) && !sect_GetSymbolSection()) {
|
if (Symbol *sym = sym_FindScopedSymbol(symName); sym_IsPC(sym) && !sect_GetSymbolSection()) {
|
||||||
error("PC has no value outside of a section\n");
|
error("PC has no value outside of a section\n");
|
||||||
data = 0;
|
data = 0;
|
||||||
|
} else if (sym && !sym->isNumeric() && !sym->isLabel()) {
|
||||||
|
error("'%s' is not a numeric symbol\n", symName.c_str());
|
||||||
|
data = 0;
|
||||||
} else if (!sym || !sym->isConstant()) {
|
} else if (!sym || !sym->isConstant()) {
|
||||||
isSymbol = true;
|
isSymbol = true;
|
||||||
|
|
||||||
data = sym_IsPC(sym) ? "PC is not constant at assembly time"
|
data = sym_IsPC(sym) ? "PC is not constant at assembly time"
|
||||||
: sym_IsPurgedScoped(symName)
|
: sym_IsPurgedScoped(symName)
|
||||||
? "'"s + symName + "' is not constant at assembly time; it was purged"
|
? "'"s + symName + "' is not constant at assembly time; it was purged"
|
||||||
: "'"s + symName + "' is not constant at assembly time";
|
: "'"s + symName + "' is not constant at assembly time";
|
||||||
sym = sym_Ref(symName);
|
sym = sym_Ref(symName);
|
||||||
|
|
||||||
size_t nameLen = sym->name.length() + 1; // Don't forget NUL!
|
size_t nameLen = sym->name.length() + 1; // Don't forget NUL!
|
||||||
@@ -89,7 +94,7 @@ void Expression::makeSymbol(std::string const &symName) {
|
|||||||
*ptr++ = RPN_SYM;
|
*ptr++ = RPN_SYM;
|
||||||
memcpy(ptr, sym->name.c_str(), nameLen);
|
memcpy(ptr, sym->name.c_str(), nameLen);
|
||||||
} else {
|
} else {
|
||||||
data = (int32_t)sym_GetConstantValue(symName);
|
data = static_cast<int32_t>(sym->getConstantValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,12 +105,12 @@ void Expression::makeBankSymbol(std::string const &symName) {
|
|||||||
if (!currentSection) {
|
if (!currentSection) {
|
||||||
error("PC has no bank outside of a section\n");
|
error("PC has no bank outside of a section\n");
|
||||||
data = 1;
|
data = 1;
|
||||||
} else if (currentSection->bank == (uint32_t)-1) {
|
} else if (currentSection->bank == UINT32_MAX) {
|
||||||
data = "Current section's bank is not known";
|
data = "Current section's bank is not known";
|
||||||
|
|
||||||
*reserveSpace(1) = RPN_BANK_SELF;
|
*reserveSpace(1) = RPN_BANK_SELF;
|
||||||
} else {
|
} else {
|
||||||
data = (int32_t)currentSection->bank;
|
data = static_cast<int32_t>(currentSection->bank);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else if (sym && !sym->isLabel()) {
|
} else if (sym && !sym->isLabel()) {
|
||||||
@@ -115,13 +120,13 @@ void Expression::makeBankSymbol(std::string const &symName) {
|
|||||||
sym = sym_Ref(symName);
|
sym = sym_Ref(symName);
|
||||||
assume(sym); // If the symbol didn't exist, it should have been created
|
assume(sym); // If the symbol didn't exist, it should have been created
|
||||||
|
|
||||||
if (sym->getSection() && sym->getSection()->bank != (uint32_t)-1) {
|
if (sym->getSection() && sym->getSection()->bank != UINT32_MAX) {
|
||||||
// Symbol's section is known and bank is fixed
|
// Symbol's section is known and bank is fixed
|
||||||
data = (int32_t)sym->getSection()->bank;
|
data = static_cast<int32_t>(sym->getSection()->bank);
|
||||||
} else {
|
} else {
|
||||||
data = sym_IsPurgedScoped(symName)
|
data = sym_IsPurgedScoped(symName)
|
||||||
? "\""s + symName + "\"'s bank is not known; it was purged"
|
? "\""s + symName + "\"'s bank is not known; it was purged"
|
||||||
: "\""s + symName + "\"'s bank is not known";
|
: "\""s + symName + "\"'s bank is not known";
|
||||||
|
|
||||||
size_t nameLen = sym->name.length() + 1; // Room for NUL!
|
size_t nameLen = sym->name.length() + 1; // Room for NUL!
|
||||||
|
|
||||||
@@ -135,8 +140,8 @@ void Expression::makeBankSymbol(std::string const &symName) {
|
|||||||
|
|
||||||
void Expression::makeBankSection(std::string const §Name) {
|
void Expression::makeBankSection(std::string const §Name) {
|
||||||
clear();
|
clear();
|
||||||
if (Section *sect = sect_FindSectionByName(sectName); sect && sect->bank != (uint32_t)-1) {
|
if (Section *sect = sect_FindSectionByName(sectName); sect && sect->bank != UINT32_MAX) {
|
||||||
data = (int32_t)sect->bank;
|
data = static_cast<int32_t>(sect->bank);
|
||||||
} else {
|
} else {
|
||||||
data = "Section \""s + sectName + "\"'s bank is not known";
|
data = "Section \""s + sectName + "\"'s bank is not known";
|
||||||
|
|
||||||
@@ -151,7 +156,7 @@ void Expression::makeBankSection(std::string const §Name) {
|
|||||||
void Expression::makeSizeOfSection(std::string const §Name) {
|
void Expression::makeSizeOfSection(std::string const §Name) {
|
||||||
clear();
|
clear();
|
||||||
if (Section *sect = sect_FindSectionByName(sectName); sect && sect->isSizeKnown()) {
|
if (Section *sect = sect_FindSectionByName(sectName); sect && sect->isSizeKnown()) {
|
||||||
data = (int32_t)sect->size;
|
data = static_cast<int32_t>(sect->size);
|
||||||
} else {
|
} else {
|
||||||
data = "Section \""s + sectName + "\"'s size is not known";
|
data = "Section \""s + sectName + "\"'s size is not known";
|
||||||
|
|
||||||
@@ -165,8 +170,8 @@ void Expression::makeSizeOfSection(std::string const §Name) {
|
|||||||
|
|
||||||
void Expression::makeStartOfSection(std::string const §Name) {
|
void Expression::makeStartOfSection(std::string const §Name) {
|
||||||
clear();
|
clear();
|
||||||
if (Section *sect = sect_FindSectionByName(sectName); sect && sect->org != (uint32_t)-1) {
|
if (Section *sect = sect_FindSectionByName(sectName); sect && sect->org != UINT32_MAX) {
|
||||||
data = (int32_t)sect->org;
|
data = static_cast<int32_t>(sect->org);
|
||||||
} else {
|
} else {
|
||||||
data = "Section \""s + sectName + "\"'s start is not known";
|
data = "Section \""s + sectName + "\"'s start is not known";
|
||||||
|
|
||||||
@@ -208,71 +213,68 @@ static bool tryConstNonzero(Expression const &lhs, Expression const &rhs) {
|
|||||||
|
|
||||||
static bool tryConstLogNot(Expression const &expr) {
|
static bool tryConstLogNot(Expression const &expr) {
|
||||||
Symbol const *sym = expr.symbolOf();
|
Symbol const *sym = expr.symbolOf();
|
||||||
if (!sym || !sym->getSection() || !sym->isDefined())
|
if (!sym || !sym->getSection() || !sym->isDefined()) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
assume(sym->isNumeric());
|
assume(sym->isNumeric());
|
||||||
|
|
||||||
Section const § = *sym->getSection();
|
Section const § = *sym->getSection();
|
||||||
int32_t unknownBits = (1 << 16) - (1 << sect.align);
|
int32_t unknownBits = (1 << 16) - (1 << sect.align);
|
||||||
|
|
||||||
// `sym->getValue()` attempts to add the section's address, but that's "-1"
|
// `sym->getValue()` attempts to add the section's address, but that's `UINT32_MAX`
|
||||||
// because the section is floating (otherwise we wouldn't be here)
|
// because the section is floating (otherwise we wouldn't be here)
|
||||||
assume(sect.org == (uint32_t)-1);
|
assume(sect.org == UINT32_MAX);
|
||||||
int32_t symbolOfs = sym->getValue() + 1;
|
int32_t symbolOfs = sym->getValue() + 1;
|
||||||
|
|
||||||
int32_t knownBits = (symbolOfs + sect.alignOfs) & ~unknownBits;
|
int32_t knownBits = (symbolOfs + sect.alignOfs) & ~unknownBits;
|
||||||
return knownBits != 0;
|
return knownBits != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Returns a constant LOW() from non-constant argument, or -1 if it cannot be computed.
|
||||||
* Attempts to compute a constant LOW() from non-constant argument
|
// This is possible if the argument is a symbol belonging to an `ALIGN[8]` section.
|
||||||
* This is possible if the argument is a symbol belonging to an `ALIGN[8]` section.
|
|
||||||
*
|
|
||||||
* @return The constant `LOW(expr)` result if it can be computed, or -1 otherwise.
|
|
||||||
*/
|
|
||||||
static int32_t tryConstLow(Expression const &expr) {
|
static int32_t tryConstLow(Expression const &expr) {
|
||||||
Symbol const *sym = expr.symbolOf();
|
Symbol const *sym = expr.symbolOf();
|
||||||
if (!sym || !sym->getSection() || !sym->isDefined())
|
if (!sym || !sym->getSection() || !sym->isDefined()) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
assume(sym->isNumeric());
|
assume(sym->isNumeric());
|
||||||
|
|
||||||
// The low byte must not cover any unknown bits
|
// The low byte must not cover any unknown bits
|
||||||
Section const § = *sym->getSection();
|
Section const § = *sym->getSection();
|
||||||
if (sect.align < 8)
|
if (sect.align < 8) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// `sym->getValue()` attempts to add the section's address, but that's "-1"
|
// `sym->getValue()` attempts to add the section's address, but that's `UINT32_MAX`
|
||||||
// because the section is floating (otherwise we wouldn't be here)
|
// because the section is floating (otherwise we wouldn't be here)
|
||||||
assume(sect.org == (uint32_t)-1);
|
assume(sect.org == UINT32_MAX);
|
||||||
int32_t symbolOfs = sym->getValue() + 1;
|
int32_t symbolOfs = sym->getValue() + 1;
|
||||||
|
|
||||||
return (symbolOfs + sect.alignOfs) & 0xFF;
|
return (symbolOfs + sect.alignOfs) & 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Returns a constant binary AND with one non-constant operand, or -1 if it cannot be computed.
|
||||||
* Attempts to compute a constant binary AND with one non-constant operands
|
// This is possible if one operand is a symbol belonging to an `ALIGN[N]` section, and the other is
|
||||||
* This is possible if one operand is a symbol belonging to an `ALIGN[N]` section, and the other is
|
// a constant that only keeps (some of) the lower N bits.
|
||||||
* a constant that only keeps (some of) the lower N bits.
|
|
||||||
*
|
|
||||||
* @return The constant `lhs & rhs` result if it can be computed, or -1 otherwise.
|
|
||||||
*/
|
|
||||||
static int32_t tryConstMask(Expression const &lhs, Expression const &rhs) {
|
static int32_t tryConstMask(Expression const &lhs, Expression const &rhs) {
|
||||||
Symbol const *lhsSymbol = lhs.symbolOf();
|
Symbol const *lhsSymbol = lhs.symbolOf();
|
||||||
Symbol const *rhsSymbol = lhsSymbol ? nullptr : rhs.symbolOf();
|
Symbol const *rhsSymbol = lhsSymbol ? nullptr : rhs.symbolOf();
|
||||||
bool lhsIsSymbol = lhsSymbol && lhsSymbol->getSection();
|
bool lhsIsSymbol = lhsSymbol && lhsSymbol->getSection();
|
||||||
bool rhsIsSymbol = rhsSymbol && rhsSymbol->getSection();
|
bool rhsIsSymbol = rhsSymbol && rhsSymbol->getSection();
|
||||||
|
|
||||||
if (!lhsIsSymbol && !rhsIsSymbol)
|
if (!lhsIsSymbol && !rhsIsSymbol) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// If the lhs isn't a symbol, try again the other way around
|
// If the lhs isn't a symbol, try again the other way around
|
||||||
Symbol const &sym = lhsIsSymbol ? *lhsSymbol : *rhsSymbol;
|
Symbol const &sym = lhsIsSymbol ? *lhsSymbol : *rhsSymbol;
|
||||||
Expression const &expr = lhsIsSymbol ? rhs : lhs; // Opposite side of `sym`
|
Expression const &expr = lhsIsSymbol ? rhs : lhs; // Opposite side of `sym`
|
||||||
|
|
||||||
if (!sym.isDefined() || !expr.isKnown())
|
if (!sym.isDefined() || !expr.isKnown()) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
assume(sym.isNumeric());
|
assume(sym.isNumeric());
|
||||||
|
|
||||||
@@ -281,12 +283,13 @@ static int32_t tryConstMask(Expression const &lhs, Expression const &rhs) {
|
|||||||
|
|
||||||
// The mask must not cover any unknown bits
|
// The mask must not cover any unknown bits
|
||||||
Section const § = *sym.getSection();
|
Section const § = *sym.getSection();
|
||||||
if (int32_t unknownBits = (1 << 16) - (1 << sect.align); (unknownBits & mask) != 0)
|
if (int32_t unknownBits = (1 << 16) - (1 << sect.align); (unknownBits & mask) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// `sym.getValue()` attempts to add the section's address, but that's "-1"
|
// `sym.getValue()` attempts to add the section's address, but that's `UINT32_MAX`
|
||||||
// because the section is floating (otherwise we wouldn't be here)
|
// because the section is floating (otherwise we wouldn't be here)
|
||||||
assume(sect.org == (uint32_t)-1);
|
assume(sect.org == UINT32_MAX);
|
||||||
int32_t symbolOfs = sym.getValue() + 1;
|
int32_t symbolOfs = sym.getValue() + 1;
|
||||||
|
|
||||||
return (symbolOfs + sect.alignOfs) & mask;
|
return (symbolOfs + sect.alignOfs) & mask;
|
||||||
@@ -298,10 +301,11 @@ void Expression::makeUnaryOp(RPNCommand op, Expression &&src) {
|
|||||||
if (src.isKnown()) {
|
if (src.isKnown()) {
|
||||||
// If the expressions is known, just compute the value
|
// If the expressions is known, just compute the value
|
||||||
int32_t val = src.value();
|
int32_t val = src.value();
|
||||||
|
uint32_t uval = static_cast<uint32_t>(val);
|
||||||
|
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case RPN_NEG:
|
case RPN_NEG:
|
||||||
data = (int32_t) - (uint32_t)val;
|
data = static_cast<int32_t>(-uval);
|
||||||
break;
|
break;
|
||||||
case RPN_NOT:
|
case RPN_NOT:
|
||||||
data = ~val;
|
data = ~val;
|
||||||
@@ -310,51 +314,23 @@ void Expression::makeUnaryOp(RPNCommand op, Expression &&src) {
|
|||||||
data = !val;
|
data = !val;
|
||||||
break;
|
break;
|
||||||
case RPN_HIGH:
|
case RPN_HIGH:
|
||||||
data = (int32_t)((uint32_t)val >> 8 & 0xFF);
|
data = static_cast<int32_t>(uval >> 8 & 0xFF);
|
||||||
break;
|
break;
|
||||||
case RPN_LOW:
|
case RPN_LOW:
|
||||||
data = val & 0xFF;
|
data = val & 0xFF;
|
||||||
break;
|
break;
|
||||||
case RPN_BITWIDTH:
|
case RPN_BITWIDTH:
|
||||||
data = val != 0 ? 32 - clz((uint32_t)val) : 0;
|
data = val != 0 ? 32 - clz(uval) : 0;
|
||||||
break;
|
break;
|
||||||
case RPN_TZCOUNT:
|
case RPN_TZCOUNT:
|
||||||
data = val != 0 ? ctz((uint32_t)val) : 32;
|
data = val != 0 ? ctz(uval) : 32;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
case RPN_LOGOR:
|
// `makeUnaryOp` should never be called with a non-unary operator!
|
||||||
case RPN_LOGAND:
|
// LCOV_EXCL_START
|
||||||
case RPN_LOGEQ:
|
unreachable_();
|
||||||
case RPN_LOGGT:
|
|
||||||
case RPN_LOGLT:
|
|
||||||
case RPN_LOGGE:
|
|
||||||
case RPN_LOGLE:
|
|
||||||
case RPN_LOGNE:
|
|
||||||
case RPN_ADD:
|
|
||||||
case RPN_SUB:
|
|
||||||
case RPN_XOR:
|
|
||||||
case RPN_OR:
|
|
||||||
case RPN_AND:
|
|
||||||
case RPN_SHL:
|
|
||||||
case RPN_SHR:
|
|
||||||
case RPN_USHR:
|
|
||||||
case RPN_MUL:
|
|
||||||
case RPN_DIV:
|
|
||||||
case RPN_MOD:
|
|
||||||
case RPN_EXP:
|
|
||||||
case RPN_BANK_SYM:
|
|
||||||
case RPN_BANK_SECT:
|
|
||||||
case RPN_BANK_SELF:
|
|
||||||
case RPN_SIZEOF_SECT:
|
|
||||||
case RPN_STARTOF_SECT:
|
|
||||||
case RPN_SIZEOF_SECTTYPE:
|
|
||||||
case RPN_STARTOF_SECTTYPE:
|
|
||||||
case RPN_HRAM:
|
|
||||||
case RPN_RST:
|
|
||||||
case RPN_CONST:
|
|
||||||
case RPN_SYM:
|
|
||||||
fatalerror("%d is not an unary operator\n", op);
|
|
||||||
}
|
}
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
} else if (op == RPN_LOGNOT && tryConstLogNot(src)) {
|
} else if (op == RPN_LOGNOT && tryConstLogNot(src)) {
|
||||||
data = 0;
|
data = 0;
|
||||||
} else if (int32_t constVal; op == RPN_LOW && (constVal = tryConstLow(src)) != -1) {
|
} else if (int32_t constVal; op == RPN_LOW && (constVal = tryConstLow(src)) != -1) {
|
||||||
@@ -374,6 +350,7 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
|
|||||||
if (src1.isKnown() && src2.isKnown()) {
|
if (src1.isKnown() && src2.isKnown()) {
|
||||||
// If both expressions are known, just compute the value
|
// If both expressions are known, just compute the value
|
||||||
int32_t lval = src1.value(), rval = src2.value();
|
int32_t lval = src1.value(), rval = src2.value();
|
||||||
|
uint32_t ulval = static_cast<uint32_t>(lval), urval = static_cast<uint32_t>(rval);
|
||||||
|
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case RPN_LOGOR:
|
case RPN_LOGOR:
|
||||||
@@ -401,10 +378,10 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
|
|||||||
data = lval != rval;
|
data = lval != rval;
|
||||||
break;
|
break;
|
||||||
case RPN_ADD:
|
case RPN_ADD:
|
||||||
data = (int32_t)((uint32_t)lval + (uint32_t)rval);
|
data = static_cast<int32_t>(ulval + urval);
|
||||||
break;
|
break;
|
||||||
case RPN_SUB:
|
case RPN_SUB:
|
||||||
data = (int32_t)((uint32_t)lval - (uint32_t)rval);
|
data = static_cast<int32_t>(ulval - urval);
|
||||||
break;
|
break;
|
||||||
case RPN_XOR:
|
case RPN_XOR:
|
||||||
data = lval ^ rval;
|
data = lval ^ rval;
|
||||||
@@ -416,47 +393,55 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
|
|||||||
data = lval & rval;
|
data = lval & rval;
|
||||||
break;
|
break;
|
||||||
case RPN_SHL:
|
case RPN_SHL:
|
||||||
if (rval < 0)
|
if (rval < 0) {
|
||||||
warning(
|
warning(
|
||||||
WARNING_SHIFT_AMOUNT, "Shifting left by negative amount %" PRId32 "\n", rval
|
WARNING_SHIFT_AMOUNT, "Shifting left by negative amount %" PRId32 "\n", rval
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (rval >= 32)
|
if (rval >= 32) {
|
||||||
warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %" PRId32 "\n", rval);
|
warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %" PRId32 "\n", rval);
|
||||||
|
}
|
||||||
|
|
||||||
data = op_shift_left(lval, rval);
|
data = op_shift_left(lval, rval);
|
||||||
break;
|
break;
|
||||||
case RPN_SHR:
|
case RPN_SHR:
|
||||||
if (lval < 0)
|
if (lval < 0) {
|
||||||
warning(WARNING_SHIFT, "Shifting right negative value %" PRId32 "\n", lval);
|
warning(WARNING_SHIFT, "Shifting right negative value %" PRId32 "\n", lval);
|
||||||
|
}
|
||||||
|
|
||||||
if (rval < 0)
|
if (rval < 0) {
|
||||||
warning(
|
warning(
|
||||||
WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32 "\n", rval
|
WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32 "\n", rval
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (rval >= 32)
|
if (rval >= 32) {
|
||||||
warning(WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32 "\n", rval);
|
warning(WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32 "\n", rval);
|
||||||
|
}
|
||||||
|
|
||||||
data = op_shift_right(lval, rval);
|
data = op_shift_right(lval, rval);
|
||||||
break;
|
break;
|
||||||
case RPN_USHR:
|
case RPN_USHR:
|
||||||
if (rval < 0)
|
if (rval < 0) {
|
||||||
warning(
|
warning(
|
||||||
WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32 "\n", rval
|
WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32 "\n", rval
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (rval >= 32)
|
if (rval >= 32) {
|
||||||
warning(WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32 "\n", rval);
|
warning(WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32 "\n", rval);
|
||||||
|
}
|
||||||
|
|
||||||
data = op_shift_right_unsigned(lval, rval);
|
data = op_shift_right_unsigned(lval, rval);
|
||||||
break;
|
break;
|
||||||
case RPN_MUL:
|
case RPN_MUL:
|
||||||
data = (int32_t)((uint32_t)lval * (uint32_t)rval);
|
data = static_cast<int32_t>(ulval * urval);
|
||||||
break;
|
break;
|
||||||
case RPN_DIV:
|
case RPN_DIV:
|
||||||
if (rval == 0)
|
if (rval == 0) {
|
||||||
fatalerror("Division by zero\n");
|
fatalerror("Division by zero\n");
|
||||||
|
}
|
||||||
|
|
||||||
if (lval == INT32_MIN && rval == -1) {
|
if (lval == INT32_MIN && rval == -1) {
|
||||||
warning(
|
warning(
|
||||||
@@ -471,41 +456,29 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RPN_MOD:
|
case RPN_MOD:
|
||||||
if (rval == 0)
|
if (rval == 0) {
|
||||||
fatalerror("Modulo by zero\n");
|
fatalerror("Modulo by zero\n");
|
||||||
|
}
|
||||||
|
|
||||||
if (lval == INT32_MIN && rval == -1)
|
if (lval == INT32_MIN && rval == -1) {
|
||||||
data = 0;
|
data = 0;
|
||||||
else
|
} else {
|
||||||
data = op_modulo(lval, rval);
|
data = op_modulo(lval, rval);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case RPN_EXP:
|
case RPN_EXP:
|
||||||
if (rval < 0)
|
if (rval < 0) {
|
||||||
fatalerror("Exponentiation by negative power\n");
|
fatalerror("Exponentiation by negative power\n");
|
||||||
|
}
|
||||||
|
|
||||||
data = op_exponent(lval, rval);
|
data = op_exponent(lval, rval);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
case RPN_NEG:
|
// `makeBinaryOp` should never be called with a non-binary operator!
|
||||||
case RPN_NOT:
|
// LCOV_EXCL_START
|
||||||
case RPN_LOGNOT:
|
unreachable_();
|
||||||
case RPN_BANK_SYM:
|
|
||||||
case RPN_BANK_SECT:
|
|
||||||
case RPN_BANK_SELF:
|
|
||||||
case RPN_SIZEOF_SECT:
|
|
||||||
case RPN_STARTOF_SECT:
|
|
||||||
case RPN_SIZEOF_SECTTYPE:
|
|
||||||
case RPN_STARTOF_SECTTYPE:
|
|
||||||
case RPN_HRAM:
|
|
||||||
case RPN_RST:
|
|
||||||
case RPN_HIGH:
|
|
||||||
case RPN_LOW:
|
|
||||||
case RPN_BITWIDTH:
|
|
||||||
case RPN_TZCOUNT:
|
|
||||||
case RPN_CONST:
|
|
||||||
case RPN_SYM:
|
|
||||||
fatalerror("%d is not a binary operator\n", op);
|
|
||||||
}
|
}
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
} else if (op == RPN_SUB && src1.isDiffConstant(src2.symbolOf())) {
|
} else if (op == RPN_SUB && src1.isDiffConstant(src2.symbolOf())) {
|
||||||
data = src1.symbolOf()->getValue() - src2.symbolOf()->getValue();
|
data = src1.symbolOf()->getValue() - src2.symbolOf()->getValue();
|
||||||
} else if ((op == RPN_LOGAND || op == RPN_AND) && tryConstZero(src1, src2)) {
|
} else if ((op == RPN_LOGAND || op == RPN_AND) && tryConstZero(src1, src2)) {
|
||||||
@@ -522,10 +495,10 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
|
|||||||
uint32_t lval = src1.value();
|
uint32_t lval = src1.value();
|
||||||
uint8_t bytes[] = {
|
uint8_t bytes[] = {
|
||||||
RPN_CONST,
|
RPN_CONST,
|
||||||
(uint8_t)lval,
|
static_cast<uint8_t>(lval),
|
||||||
(uint8_t)(lval >> 8),
|
static_cast<uint8_t>(lval >> 8),
|
||||||
(uint8_t)(lval >> 16),
|
static_cast<uint8_t>(lval >> 16),
|
||||||
(uint8_t)(lval >> 24),
|
static_cast<uint8_t>(lval >> 24),
|
||||||
};
|
};
|
||||||
rpn.clear();
|
rpn.clear();
|
||||||
rpnPatchSize = 0;
|
rpnPatchSize = 0;
|
||||||
@@ -546,10 +519,10 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
|
|||||||
uint32_t rval = src2.value();
|
uint32_t rval = src2.value();
|
||||||
uint8_t bytes[] = {
|
uint8_t bytes[] = {
|
||||||
RPN_CONST,
|
RPN_CONST,
|
||||||
(uint8_t)rval,
|
static_cast<uint8_t>(rval),
|
||||||
(uint8_t)(rval >> 8),
|
static_cast<uint8_t>(rval >> 8),
|
||||||
(uint8_t)(rval >> 16),
|
static_cast<uint8_t>(rval >> 16),
|
||||||
(uint8_t)(rval >> 24),
|
static_cast<uint8_t>(rval >> 24),
|
||||||
};
|
};
|
||||||
uint8_t *ptr = reserveSpace(sizeof(bytes) + 1, sizeof(bytes) + 1);
|
uint8_t *ptr = reserveSpace(sizeof(bytes) + 1, sizeof(bytes) + 1);
|
||||||
memcpy(ptr, bytes, sizeof(bytes));
|
memcpy(ptr, bytes, sizeof(bytes));
|
||||||
@@ -558,24 +531,29 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
|
|||||||
// Copy the right RPN and append the operator
|
// Copy the right RPN and append the operator
|
||||||
uint32_t rightRpnSize = src2.rpn.size();
|
uint32_t rightRpnSize = src2.rpn.size();
|
||||||
uint8_t *ptr = reserveSpace(rightRpnSize + 1, src2.rpnPatchSize + 1);
|
uint8_t *ptr = reserveSpace(rightRpnSize + 1, src2.rpnPatchSize + 1);
|
||||||
if (rightRpnSize > 0)
|
if (rightRpnSize > 0) {
|
||||||
// If `rightRpnSize == 0`, then `memcpy(ptr, nullptr, rightRpnSize)` would be UB
|
// If `rightRpnSize == 0`, then `memcpy(ptr, nullptr, rightRpnSize)` would be UB
|
||||||
memcpy(ptr, src2.rpn.data(), rightRpnSize);
|
memcpy(ptr, src2.rpn.data(), rightRpnSize);
|
||||||
|
}
|
||||||
ptr[rightRpnSize] = op;
|
ptr[rightRpnSize] = op;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Expression::makeCheckHRAM() {
|
bool Expression::makeCheckHRAM() {
|
||||||
isSymbol = false;
|
isSymbol = false;
|
||||||
if (!isKnown()) {
|
if (!isKnown()) {
|
||||||
*reserveSpace(1) = RPN_HRAM;
|
*reserveSpace(1) = RPN_HRAM;
|
||||||
} else if (int32_t val = value(); val >= 0xFF00 && val <= 0xFFFF) {
|
} else if (int32_t val = value(); val >= 0xFF00 && val <= 0xFFFF) {
|
||||||
// That range is valid, but only keep the lower byte
|
// That range is valid, but only keep the lower byte
|
||||||
data = val & 0xFF;
|
data = val & 0xFF;
|
||||||
} else if (val < 0 || val > 0xFF) {
|
} else if (val >= 0 && val <= 0xFF) {
|
||||||
|
// That range is valid, but deprecated
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
error("Source address $%" PRIx32 " not between $FF00 to $FFFF\n", val);
|
error("Source address $%" PRIx32 " not between $FF00 to $FFFF\n", val);
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Expression::makeCheckRST() {
|
void Expression::makeCheckRST() {
|
||||||
@@ -584,16 +562,28 @@ void Expression::makeCheckRST() {
|
|||||||
} else if (int32_t val = value(); val & ~0x38) {
|
} else if (int32_t val = value(); val & ~0x38) {
|
||||||
// A valid RST address must be masked with 0x38
|
// A valid RST address must be masked with 0x38
|
||||||
error("Invalid address $%" PRIx32 " for RST\n", val);
|
error("Invalid address $%" PRIx32 " for RST\n", val);
|
||||||
} else {
|
}
|
||||||
// The target is in the "0x38" bits, all other bits are set
|
}
|
||||||
data = val | 0xC7;
|
|
||||||
|
void Expression::makeCheckBitIndex(uint8_t mask) {
|
||||||
|
assume((mask & 0xC0) != 0x00); // The high two bits must correspond to BIT, RES, or SET
|
||||||
|
|
||||||
|
if (!isKnown()) {
|
||||||
|
uint8_t *ptr = reserveSpace(2);
|
||||||
|
*ptr++ = RPN_BIT_INDEX;
|
||||||
|
*ptr = mask;
|
||||||
|
} else if (int32_t val = value(); val & ~0x07) {
|
||||||
|
// A valid bit index must be masked with 0x07
|
||||||
|
static char const *instructions[4] = {"instruction", "BIT", "RES", "SET"};
|
||||||
|
error("Invalid bit index %" PRId32 " for %s\n", val, instructions[mask >> 6]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks that an RPN expression's value fits within N bits (signed or unsigned)
|
// Checks that an RPN expression's value fits within N bits (signed or unsigned)
|
||||||
void Expression::checkNBit(uint8_t n) const {
|
void Expression::checkNBit(uint8_t n) const {
|
||||||
if (isKnown())
|
if (isKnown()) {
|
||||||
::checkNBit(value(), n, "Expression");
|
::checkNBit(value(), n, nullptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool checkNBit(int32_t v, uint8_t n, char const *name) {
|
bool checkNBit(int32_t v, uint8_t n, char const *name) {
|
||||||
@@ -601,11 +591,23 @@ bool checkNBit(int32_t v, uint8_t n, char const *name) {
|
|||||||
assume(n < CHAR_BIT * sizeof(int)); // Otherwise `1 << n` is UB
|
assume(n < CHAR_BIT * sizeof(int)); // Otherwise `1 << n` is UB
|
||||||
|
|
||||||
if (v < -(1 << n) || v >= 1 << n) {
|
if (v < -(1 << n) || v >= 1 << n) {
|
||||||
warning(WARNING_TRUNCATION_1, "%s must be %u-bit\n", name, n);
|
warning(
|
||||||
|
WARNING_TRUNCATION_1,
|
||||||
|
n == 8 && !name ? "%s must be %u-bit; use LOW() to force 8-bit\n"
|
||||||
|
: "%s must be %u-bit\n",
|
||||||
|
name ? name : "Expression",
|
||||||
|
n
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (v < -(1 << (n - 1))) {
|
if (v < -(1 << (n - 1))) {
|
||||||
warning(WARNING_TRUNCATION_2, "%s must be %u-bit\n", name, n);
|
warning(
|
||||||
|
WARNING_TRUNCATION_2,
|
||||||
|
n == 8 && !name ? "%s must be %u-bit; use LOW() to force 8-bit\n"
|
||||||
|
: "%s must be %u-bit\n",
|
||||||
|
name ? name : "Expression",
|
||||||
|
n
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#include "asm/section.hpp"
|
#include "asm/section.hpp"
|
||||||
|
|
||||||
@@ -49,9 +49,11 @@ static std::pair<Symbol const *, Symbol const *> currentLoadLabelScopes = {nullp
|
|||||||
int32_t loadOffset; // Offset into the LOAD section's parent (see sect_GetOutputOffset)
|
int32_t loadOffset; // Offset into the LOAD section's parent (see sect_GetOutputOffset)
|
||||||
|
|
||||||
// A quick check to see if we have an initialized section
|
// A quick check to see if we have an initialized section
|
||||||
[[nodiscard]] static bool requireSection() {
|
[[nodiscard]]
|
||||||
if (currentSection)
|
static bool requireSection() {
|
||||||
|
if (currentSection) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
error("Cannot output data outside of a SECTION\n");
|
error("Cannot output data outside of a SECTION\n");
|
||||||
return false;
|
return false;
|
||||||
@@ -59,12 +61,15 @@ int32_t loadOffset; // Offset into the LOAD section's parent (see sect_GetOutput
|
|||||||
|
|
||||||
// A quick check to see if we have an initialized section that can contain
|
// A quick check to see if we have an initialized section that can contain
|
||||||
// this much initialized data
|
// this much initialized data
|
||||||
[[nodiscard]] static bool requireCodeSection() {
|
[[nodiscard]]
|
||||||
if (!requireSection())
|
static bool requireCodeSection() {
|
||||||
|
if (!requireSection()) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (sect_HasData(currentSection->type))
|
if (sect_HasData(currentSection->type)) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
error(
|
error(
|
||||||
"Section '%s' cannot contain code or data (not ROM0 or ROMX)\n",
|
"Section '%s' cannot contain code or data (not ROM0 or ROMX)\n",
|
||||||
@@ -75,14 +80,15 @@ int32_t loadOffset; // Offset into the LOAD section's parent (see sect_GetOutput
|
|||||||
|
|
||||||
void sect_CheckSizes() {
|
void sect_CheckSizes() {
|
||||||
for (Section const § : sectionList) {
|
for (Section const § : sectionList) {
|
||||||
if (uint32_t maxSize = sectionTypeInfo[sect.type].size; sect.size > maxSize)
|
if (uint32_t maxSize = sectionTypeInfo[sect.type].size; sect.size > maxSize) {
|
||||||
error(
|
error(
|
||||||
"Section '%s' grew too big (max size = 0x%" PRIX32 " bytes, reached 0x%" PRIX32
|
"Section '%s' grew too big (max size = 0x%" PRIX32 " bytes, reached 0x%" PRIX32
|
||||||
").\n",
|
")\n",
|
||||||
sect.name.c_str(),
|
sect.name.c_str(),
|
||||||
maxSize,
|
maxSize,
|
||||||
sect.size
|
sect.size
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,33 +112,36 @@ static unsigned int mergeSectUnion(
|
|||||||
|
|
||||||
// Unionized sections only need "compatible" constraints, and they end up with the strictest
|
// Unionized sections only need "compatible" constraints, and they end up with the strictest
|
||||||
// combination of both.
|
// combination of both.
|
||||||
if (sect_HasData(type))
|
if (sect_HasData(type)) {
|
||||||
sectError("Cannot declare ROM sections as UNION\n");
|
sectError("Cannot declare ROM sections as UNION\n");
|
||||||
|
}
|
||||||
|
|
||||||
if (org != (uint32_t)-1) {
|
if (org != UINT32_MAX) {
|
||||||
// If both are fixed, they must be the same
|
// If both are fixed, they must be the same
|
||||||
if (sect.org != (uint32_t)-1 && sect.org != org)
|
if (sect.org != UINT32_MAX && sect.org != org) {
|
||||||
sectError(
|
sectError(
|
||||||
"Section already declared as fixed at different address $%04" PRIx32 "\n", sect.org
|
"Section already declared as fixed at different address $%04" PRIx32 "\n", sect.org
|
||||||
);
|
);
|
||||||
else if (sect.align != 0 && (mask(sect.align) & (org - sect.alignOfs)))
|
} else if (sect.align != 0 && (mask(sect.align) & (org - sect.alignOfs))) {
|
||||||
sectError(
|
sectError(
|
||||||
"Section already declared as aligned to %u bytes (offset %" PRIu16 ")\n",
|
"Section already declared as aligned to %u bytes (offset %" PRIu16 ")\n",
|
||||||
1U << sect.align,
|
1U << sect.align,
|
||||||
sect.alignOfs
|
sect.alignOfs
|
||||||
);
|
);
|
||||||
else
|
} else {
|
||||||
// Otherwise, just override
|
// Otherwise, just override
|
||||||
sect.org = org;
|
sect.org = org;
|
||||||
|
}
|
||||||
|
|
||||||
} else if (alignment != 0) {
|
} else if (alignment != 0) {
|
||||||
// Make sure any fixed address given is compatible
|
// Make sure any fixed address given is compatible
|
||||||
if (sect.org != (uint32_t)-1) {
|
if (sect.org != UINT32_MAX) {
|
||||||
if ((sect.org - alignOffset) & mask(alignment))
|
if ((sect.org - alignOffset) & mask(alignment)) {
|
||||||
sectError(
|
sectError(
|
||||||
"Section already declared as fixed at incompatible address $%04" PRIx32 "\n",
|
"Section already declared as fixed at incompatible address $%04" PRIx32 "\n",
|
||||||
sect.org
|
sect.org
|
||||||
);
|
);
|
||||||
|
}
|
||||||
// Check if alignment offsets are compatible
|
// Check if alignment offsets are compatible
|
||||||
} else if ((alignOffset & mask(sect.align)) != (sect.alignOfs & mask(alignment))) {
|
} else if ((alignOffset & mask(sect.align)) != (sect.alignOfs & mask(alignment))) {
|
||||||
sectError(
|
sectError(
|
||||||
@@ -159,38 +168,41 @@ static unsigned int
|
|||||||
// Fragments only need "compatible" constraints, and they end up with the strictest
|
// Fragments only need "compatible" constraints, and they end up with the strictest
|
||||||
// combination of both.
|
// combination of both.
|
||||||
// The merging is however performed at the *end* of the original section!
|
// The merging is however performed at the *end* of the original section!
|
||||||
if (org != (uint32_t)-1) {
|
if (org != UINT32_MAX) {
|
||||||
uint16_t curOrg = org - sect.size;
|
uint16_t curOrg = org - sect.size;
|
||||||
|
|
||||||
// If both are fixed, they must be the same
|
// If both are fixed, they must be the same
|
||||||
if (sect.org != (uint32_t)-1 && sect.org != curOrg)
|
if (sect.org != UINT32_MAX && sect.org != curOrg) {
|
||||||
sectError(
|
sectError(
|
||||||
"Section already declared as fixed at incompatible address $%04" PRIx32 "\n",
|
"Section already declared as fixed at incompatible address $%04" PRIx32 "\n",
|
||||||
sect.org
|
sect.org
|
||||||
);
|
);
|
||||||
else if (sect.align != 0 && (mask(sect.align) & (curOrg - sect.alignOfs)))
|
} else if (sect.align != 0 && (mask(sect.align) & (curOrg - sect.alignOfs))) {
|
||||||
sectError(
|
sectError(
|
||||||
"Section already declared as aligned to %u bytes (offset %" PRIu16 ")\n",
|
"Section already declared as aligned to %u bytes (offset %" PRIu16 ")\n",
|
||||||
1U << sect.align,
|
1U << sect.align,
|
||||||
sect.alignOfs
|
sect.alignOfs
|
||||||
);
|
);
|
||||||
else
|
} else {
|
||||||
// Otherwise, just override
|
// Otherwise, just override
|
||||||
sect.org = curOrg;
|
sect.org = curOrg;
|
||||||
|
}
|
||||||
|
|
||||||
} else if (alignment != 0) {
|
} else if (alignment != 0) {
|
||||||
int32_t curOfs = (alignOffset - sect.size) % (1U << alignment);
|
int32_t curOfs = (alignOffset - sect.size) % (1U << alignment);
|
||||||
|
|
||||||
if (curOfs < 0)
|
if (curOfs < 0) {
|
||||||
curOfs += 1U << alignment;
|
curOfs += 1U << alignment;
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure any fixed address given is compatible
|
// Make sure any fixed address given is compatible
|
||||||
if (sect.org != (uint32_t)-1) {
|
if (sect.org != UINT32_MAX) {
|
||||||
if ((sect.org - curOfs) & mask(alignment))
|
if ((sect.org - curOfs) & mask(alignment)) {
|
||||||
sectError(
|
sectError(
|
||||||
"Section already declared as fixed at incompatible address $%04" PRIx32 "\n",
|
"Section already declared as fixed at incompatible address $%04" PRIx32 "\n",
|
||||||
sect.org
|
sect.org
|
||||||
);
|
);
|
||||||
|
}
|
||||||
// Check if alignment offsets are compatible
|
// Check if alignment offsets are compatible
|
||||||
} else if ((curOfs & mask(sect.align)) != (sect.alignOfs & mask(alignment))) {
|
} else if ((curOfs & mask(sect.align)) != (sect.alignOfs & mask(alignment))) {
|
||||||
sectError(
|
sectError(
|
||||||
@@ -220,13 +232,14 @@ static void mergeSections(
|
|||||||
) {
|
) {
|
||||||
unsigned int nbSectErrors = 0;
|
unsigned int nbSectErrors = 0;
|
||||||
|
|
||||||
if (type != sect.type)
|
if (type != sect.type) {
|
||||||
sectError(
|
sectError(
|
||||||
"Section already exists but with type %s\n", sectionTypeInfo[sect.type].name.c_str()
|
"Section already exists but with type %s\n", sectionTypeInfo[sect.type].name.c_str()
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (sect.modifier != mod) {
|
if (sect.modifier != mod) {
|
||||||
sectError("Section already declared as %s section\n", sectionModNames[sect.modifier]);
|
sectError("Section already declared as SECTION %s\n", sectionModNames[sect.modifier]);
|
||||||
} else {
|
} else {
|
||||||
switch (mod) {
|
switch (mod) {
|
||||||
case SECTION_UNION:
|
case SECTION_UNION:
|
||||||
@@ -238,11 +251,13 @@ static void mergeSections(
|
|||||||
// Common checks
|
// Common checks
|
||||||
|
|
||||||
// If the section's bank is unspecified, override it
|
// If the section's bank is unspecified, override it
|
||||||
if (sect.bank == (uint32_t)-1)
|
if (sect.bank == UINT32_MAX) {
|
||||||
sect.bank = bank;
|
sect.bank = bank;
|
||||||
|
}
|
||||||
// If both specify a bank, it must be the same one
|
// If both specify a bank, it must be the same one
|
||||||
else if (bank != (uint32_t)-1 && sect.bank != bank)
|
else if (bank != UINT32_MAX && sect.bank != bank) {
|
||||||
sectError("Section already declared with different bank %" PRIu32 "\n", sect.bank);
|
sectError("Section already declared with different bank %" PRIu32 "\n", sect.bank);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SECTION_NORMAL:
|
case SECTION_NORMAL:
|
||||||
@@ -253,13 +268,14 @@ static void mergeSections(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nbSectErrors)
|
if (nbSectErrors) {
|
||||||
fatalerror(
|
fatalerror(
|
||||||
"Cannot create section \"%s\" (%u error%s)\n",
|
"Cannot create section \"%s\" (%u error%s)\n",
|
||||||
sect.name.c_str(),
|
sect.name.c_str(),
|
||||||
nbSectErrors,
|
nbSectErrors,
|
||||||
nbSectErrors == 1 ? "" : "s"
|
nbSectErrors == 1 ? "" : "s"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef sectError
|
#undef sectError
|
||||||
@@ -292,8 +308,9 @@ static Section *createSection(
|
|||||||
out_RegisterNode(sect.src);
|
out_RegisterNode(sect.src);
|
||||||
|
|
||||||
// It is only needed to allocate memory for ROM sections.
|
// It is only needed to allocate memory for ROM sections.
|
||||||
if (sect_HasData(type))
|
if (sect_HasData(type)) {
|
||||||
sect.data.resize(sectionTypeInfo[type].size);
|
sect.data.resize(sectionTypeInfo[type].size);
|
||||||
|
}
|
||||||
|
|
||||||
return §
|
return §
|
||||||
}
|
}
|
||||||
@@ -312,11 +329,12 @@ static Section *getSection(
|
|||||||
|
|
||||||
// First, validate parameters, and normalize them if applicable
|
// First, validate parameters, and normalize them if applicable
|
||||||
|
|
||||||
if (bank != (uint32_t)-1) {
|
if (bank != UINT32_MAX) {
|
||||||
if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM && type != SECTTYPE_SRAM
|
if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM && type != SECTTYPE_SRAM
|
||||||
&& type != SECTTYPE_WRAMX)
|
&& type != SECTTYPE_WRAMX) {
|
||||||
error("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections\n");
|
error("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections\n");
|
||||||
else if (bank < sectionTypeInfo[type].firstBank || bank > sectionTypeInfo[type].lastBank)
|
} else if (bank < sectionTypeInfo[type].firstBank
|
||||||
|
|| bank > sectionTypeInfo[type].lastBank) {
|
||||||
error(
|
error(
|
||||||
"%s bank value $%04" PRIx32 " out of range ($%04" PRIx32 " to $%04" PRIx32 ")\n",
|
"%s bank value $%04" PRIx32 " out of range ($%04" PRIx32 " to $%04" PRIx32 ")\n",
|
||||||
sectionTypeInfo[type].name.c_str(),
|
sectionTypeInfo[type].name.c_str(),
|
||||||
@@ -324,6 +342,7 @@ static Section *getSection(
|
|||||||
sectionTypeInfo[type].firstBank,
|
sectionTypeInfo[type].firstBank,
|
||||||
sectionTypeInfo[type].lastBank
|
sectionTypeInfo[type].lastBank
|
||||||
);
|
);
|
||||||
|
}
|
||||||
} else if (nbbanks(type) == 1) {
|
} else if (nbbanks(type) == 1) {
|
||||||
// If the section type only has a single bank, implicitly force it
|
// If the section type only has a single bank, implicitly force it
|
||||||
bank = sectionTypeInfo[type].firstBank;
|
bank = sectionTypeInfo[type].firstBank;
|
||||||
@@ -338,8 +357,8 @@ static Section *getSection(
|
|||||||
alignOffset = 0;
|
alignOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (org != (uint32_t)-1) {
|
if (org != UINT32_MAX) {
|
||||||
if (org < sectionTypeInfo[type].startAddr || org > endaddr(type))
|
if (org < sectionTypeInfo[type].startAddr || org > endaddr(type)) {
|
||||||
error(
|
error(
|
||||||
"Section \"%s\"'s fixed address $%04" PRIx32 " is outside of range [$%04" PRIx16
|
"Section \"%s\"'s fixed address $%04" PRIx32 " is outside of range [$%04" PRIx16
|
||||||
"; $%04" PRIx16 "]\n",
|
"; $%04" PRIx16 "]\n",
|
||||||
@@ -348,6 +367,7 @@ static Section *getSection(
|
|||||||
sectionTypeInfo[type].startAddr,
|
sectionTypeInfo[type].startAddr,
|
||||||
endaddr(type)
|
endaddr(type)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alignment != 0) {
|
if (alignment != 0) {
|
||||||
@@ -358,9 +378,10 @@ static Section *getSection(
|
|||||||
// It doesn't make sense to have both alignment and org set
|
// It doesn't make sense to have both alignment and org set
|
||||||
uint32_t mask = mask(alignment);
|
uint32_t mask = mask(alignment);
|
||||||
|
|
||||||
if (org != (uint32_t)-1) {
|
if (org != UINT32_MAX) {
|
||||||
if ((org - alignOffset) & mask)
|
if ((org - alignOffset) & mask) {
|
||||||
error("Section \"%s\"'s fixed address doesn't match its alignment\n", name.c_str());
|
error("Section \"%s\"'s fixed address doesn't match its alignment\n", name.c_str());
|
||||||
|
}
|
||||||
alignment = 0; // Ignore it if it's satisfied
|
alignment = 0; // Ignore it if it's satisfied
|
||||||
} else if (sectionTypeInfo[type].startAddr & mask) {
|
} else if (sectionTypeInfo[type].startAddr & mask) {
|
||||||
error(
|
error(
|
||||||
@@ -393,25 +414,29 @@ static Section *getSection(
|
|||||||
|
|
||||||
// Set the current section
|
// Set the current section
|
||||||
static void changeSection() {
|
static void changeSection() {
|
||||||
if (!currentUnionStack.empty())
|
if (!currentUnionStack.empty()) {
|
||||||
fatalerror("Cannot change the section within a UNION\n");
|
fatalerror("Cannot change the section within a UNION\n");
|
||||||
|
}
|
||||||
|
|
||||||
sym_ResetCurrentLabelScopes();
|
sym_ResetCurrentLabelScopes();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Section::isSizeKnown() const {
|
bool Section::isSizeKnown() const {
|
||||||
// SECTION UNION and SECTION FRAGMENT can still grow
|
// SECTION UNION and SECTION FRAGMENT can still grow
|
||||||
if (modifier != SECTION_NORMAL)
|
if (modifier != SECTION_NORMAL) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// The current section (or current load section if within one) is still growing
|
// The current section (or current load section if within one) is still growing
|
||||||
if (this == currentSection || this == currentLoadSection)
|
if (this == currentSection || this == currentLoadSection) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Any section on the stack is still growing
|
// Any section on the stack is still growing
|
||||||
for (SectionStackEntry &entry : sectionStack) {
|
for (SectionStackEntry &entry : sectionStack) {
|
||||||
if (entry.section && entry.section->name == name)
|
if (entry.section && entry.section->name == name) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -425,12 +450,14 @@ void sect_NewSection(
|
|||||||
SectionSpec const &attrs,
|
SectionSpec const &attrs,
|
||||||
SectionModifier mod
|
SectionModifier mod
|
||||||
) {
|
) {
|
||||||
if (currentLoadSection)
|
|
||||||
fatalerror("Cannot change the section within a `LOAD` block\n");
|
|
||||||
|
|
||||||
for (SectionStackEntry &entry : sectionStack) {
|
for (SectionStackEntry &entry : sectionStack) {
|
||||||
if (entry.section && entry.section->name == name)
|
if (entry.section && entry.section->name == name) {
|
||||||
fatalerror("Section '%s' is already on the stack\n", name.c_str());
|
fatalerror("Section '%s' is already on the stack\n", name.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentLoadSection) {
|
||||||
|
sect_EndLoadSection("SECTION");
|
||||||
}
|
}
|
||||||
|
|
||||||
Section *sect = getSection(name, type, org, attrs, mod);
|
Section *sect = getSection(name, type, org, attrs, mod);
|
||||||
@@ -454,11 +481,7 @@ void sect_SetLoadSection(
|
|||||||
// Therefore, any interactions are NOT TESTED, so lift either of those restrictions at
|
// Therefore, any interactions are NOT TESTED, so lift either of those restrictions at
|
||||||
// your own peril! ^^
|
// your own peril! ^^
|
||||||
|
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
|
||||||
|
|
||||||
if (currentLoadSection) {
|
|
||||||
error("`LOAD` blocks cannot be nested\n");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,9 +490,8 @@ void sect_SetLoadSection(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mod == SECTION_FRAGMENT) {
|
if (currentLoadSection) {
|
||||||
error("`LOAD FRAGMENT` is not allowed\n");
|
sect_EndLoadSection("LOAD");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Section *sect = getSection(name, type, org, attrs, mod);
|
Section *sect = getSection(name, type, org, attrs, mod);
|
||||||
@@ -481,7 +503,13 @@ void sect_SetLoadSection(
|
|||||||
currentLoadSection = sect;
|
currentLoadSection = sect;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sect_EndLoadSection() {
|
void sect_EndLoadSection(char const *cause) {
|
||||||
|
if (cause) {
|
||||||
|
warning(
|
||||||
|
WARNING_UNTERMINATED_LOAD, "`LOAD` block without `ENDL` terminated by `%s`\n", cause
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!currentLoadSection) {
|
if (!currentLoadSection) {
|
||||||
error("Found `ENDL` outside of a `LOAD` block\n");
|
error("Found `ENDL` outside of a `LOAD` block\n");
|
||||||
return;
|
return;
|
||||||
@@ -494,6 +522,12 @@ void sect_EndLoadSection() {
|
|||||||
sym_SetCurrentLabelScopes(currentLoadLabelScopes);
|
sym_SetCurrentLabelScopes(currentLoadLabelScopes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sect_CheckLoadClosed() {
|
||||||
|
if (currentLoadSection) {
|
||||||
|
warning(WARNING_UNTERMINATED_LOAD, "`LOAD` block without `ENDL` terminated by EOF\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Section *sect_GetSymbolSection() {
|
Section *sect_GetSymbolSection() {
|
||||||
return currentLoadSection ? currentLoadSection : currentSection;
|
return currentLoadSection ? currentLoadSection : currentSection;
|
||||||
}
|
}
|
||||||
@@ -510,16 +544,18 @@ uint32_t sect_GetOutputOffset() {
|
|||||||
// Returns how many bytes need outputting for the specified alignment and offset to succeed
|
// Returns how many bytes need outputting for the specified alignment and offset to succeed
|
||||||
uint32_t sect_GetAlignBytes(uint8_t alignment, uint16_t offset) {
|
uint32_t sect_GetAlignBytes(uint8_t alignment, uint16_t offset) {
|
||||||
Section *sect = sect_GetSymbolSection();
|
Section *sect = sect_GetSymbolSection();
|
||||||
if (!sect)
|
if (!sect) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool isFixed = sect->org != (uint32_t)-1;
|
bool isFixed = sect->org != UINT32_MAX;
|
||||||
|
|
||||||
// If the section is not aligned, no bytes are needed
|
// If the section is not aligned, no bytes are needed
|
||||||
// (fixed sections count as being maximally aligned for this purpose)
|
// (fixed sections count as being maximally aligned for this purpose)
|
||||||
uint8_t curAlignment = isFixed ? 16 : sect->align;
|
uint8_t curAlignment = isFixed ? 16 : sect->align;
|
||||||
if (curAlignment == 0)
|
if (curAlignment == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// We need `(pcValue + curOffset + return value) % (1 << alignment) == offset`
|
// We need `(pcValue + curOffset + return value) % (1 << alignment) == offset`
|
||||||
uint16_t pcValue = isFixed ? sect->org : sect->alignOfs;
|
uint16_t pcValue = isFixed ? sect->org : sect->alignOfs;
|
||||||
@@ -528,18 +564,20 @@ 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) {
|
||||||
if (!requireSection())
|
if (!requireSection()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Section *sect = sect_GetSymbolSection();
|
Section *sect = sect_GetSymbolSection();
|
||||||
uint32_t alignSize = 1 << alignment; // Size of an aligned "block"
|
uint32_t alignSize = 1 << alignment; // Size of an aligned "block"
|
||||||
|
|
||||||
if (sect->org != (uint32_t)-1) {
|
if (sect->org != UINT32_MAX) {
|
||||||
if ((sect->org + curOffset - offset) % alignSize)
|
if ((sect->org + curOffset - offset) % alignSize) {
|
||||||
error(
|
error(
|
||||||
"Section's fixed address fails required alignment (PC = $%04" PRIx32 ")\n",
|
"Section's fixed address fails required alignment (PC = $%04" PRIx32 ")\n",
|
||||||
sect->org + curOffset
|
sect->org + curOffset
|
||||||
);
|
);
|
||||||
|
}
|
||||||
} else if (sect->align != 0
|
} else if (sect->align != 0
|
||||||
&& (((sect->alignOfs + curOffset) % (1u << sect->align)) - offset) % alignSize) {
|
&& (((sect->alignOfs + curOffset) % (1u << sect->align)) - offset) % alignSize) {
|
||||||
error(
|
error(
|
||||||
@@ -563,18 +601,22 @@ void sect_AlignPC(uint8_t alignment, uint16_t offset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void growSection(uint32_t growth) {
|
static void growSection(uint32_t growth) {
|
||||||
if (growth > 0 && curOffset > UINT32_MAX - growth)
|
if (growth > 0 && curOffset > UINT32_MAX - growth) {
|
||||||
fatalerror("Section size would overflow internal counter\n");
|
fatalerror("Section size would overflow internal counter\n");
|
||||||
|
}
|
||||||
curOffset += growth;
|
curOffset += growth;
|
||||||
if (uint32_t outOffset = sect_GetOutputOffset(); outOffset > currentSection->size)
|
if (uint32_t outOffset = sect_GetOutputOffset(); outOffset > currentSection->size) {
|
||||||
currentSection->size = outOffset;
|
currentSection->size = outOffset;
|
||||||
if (currentLoadSection && curOffset > currentLoadSection->size)
|
}
|
||||||
|
if (currentLoadSection && curOffset > currentLoadSection->size) {
|
||||||
currentLoadSection->size = curOffset;
|
currentLoadSection->size = curOffset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writeByte(uint8_t byte) {
|
static void writeByte(uint8_t byte) {
|
||||||
if (uint32_t index = sect_GetOutputOffset(); index < currentSection->data.size())
|
if (uint32_t index = sect_GetOutputOffset(); index < currentSection->data.size()) {
|
||||||
currentSection->data[index] = byte;
|
currentSection->data[index] = byte;
|
||||||
|
}
|
||||||
growSection(1);
|
growSection(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -616,8 +658,9 @@ static void endUnionMember() {
|
|||||||
UnionStackEntry &member = currentUnionStack.top();
|
UnionStackEntry &member = currentUnionStack.top();
|
||||||
uint32_t memberSize = curOffset - member.start;
|
uint32_t memberSize = curOffset - member.start;
|
||||||
|
|
||||||
if (memberSize > member.size)
|
if (memberSize > member.size) {
|
||||||
member.size = memberSize;
|
member.size = memberSize;
|
||||||
|
}
|
||||||
curOffset = member.start;
|
curOffset = member.start;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -640,64 +683,75 @@ void sect_EndUnion() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void sect_CheckUnionClosed() {
|
void sect_CheckUnionClosed() {
|
||||||
if (!currentUnionStack.empty())
|
if (!currentUnionStack.empty()) {
|
||||||
error("Unterminated UNION construct\n");
|
error("Unterminated UNION construct\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output a constant byte
|
// Output a constant byte
|
||||||
void sect_ConstByte(uint8_t byte) {
|
void sect_ConstByte(uint8_t byte) {
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
writeByte(byte);
|
writeByte(byte);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output a string's character units as bytes
|
// Output a string's character units as bytes
|
||||||
void sect_ByteString(std::vector<int32_t> const &string) {
|
void sect_ByteString(std::vector<int32_t> const &string) {
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (int32_t unit : string) {
|
|
||||||
if (!checkNBit(unit, 8, "All character units"))
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int32_t unit : string)
|
for (int32_t unit : string) {
|
||||||
|
if (!checkNBit(unit, 8, "All character units")) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int32_t unit : string) {
|
||||||
writeByte(static_cast<uint8_t>(unit));
|
writeByte(static_cast<uint8_t>(unit));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output a string's character units as words
|
// Output a string's character units as words
|
||||||
void sect_WordString(std::vector<int32_t> const &string) {
|
void sect_WordString(std::vector<int32_t> const &string) {
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (int32_t unit : string) {
|
|
||||||
if (!checkNBit(unit, 16, "All character units"))
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int32_t unit : string)
|
for (int32_t unit : string) {
|
||||||
|
if (!checkNBit(unit, 16, "All character units")) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int32_t unit : string) {
|
||||||
writeWord(static_cast<uint16_t>(unit));
|
writeWord(static_cast<uint16_t>(unit));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output a string's character units as longs
|
// Output a string's character units as longs
|
||||||
void sect_LongString(std::vector<int32_t> const &string) {
|
void sect_LongString(std::vector<int32_t> const &string) {
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (int32_t unit : string)
|
for (int32_t unit : string) {
|
||||||
writeLong(static_cast<uint32_t>(unit));
|
writeLong(static_cast<uint32_t>(unit));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip this many bytes
|
// Skip this many bytes
|
||||||
void sect_Skip(uint32_t skip, bool ds) {
|
void sect_Skip(uint32_t skip, bool ds) {
|
||||||
if (!requireSection())
|
if (!requireSection()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!sect_HasData(currentSection->type)) {
|
if (!sect_HasData(currentSection->type)) {
|
||||||
growSection(skip);
|
growSection(skip);
|
||||||
} else {
|
} else {
|
||||||
if (!ds)
|
if (!ds) {
|
||||||
warning(
|
warning(
|
||||||
WARNING_EMPTY_DATA_DIRECTIVE,
|
WARNING_EMPTY_DATA_DIRECTIVE,
|
||||||
"%s directive without data in ROM\n",
|
"%s directive without data in ROM\n",
|
||||||
@@ -705,16 +759,19 @@ void sect_Skip(uint32_t skip, bool ds) {
|
|||||||
: (skip == 2) ? "DW"
|
: (skip == 2) ? "DW"
|
||||||
: "DB"
|
: "DB"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
// We know we're in a code SECTION
|
// We know we're in a code SECTION
|
||||||
while (skip--)
|
while (skip--) {
|
||||||
writeByte(fillByte);
|
writeByte(fillByte);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output a byte that can be relocatable or constant
|
// Output a byte that can be relocatable or constant
|
||||||
void sect_RelByte(Expression &expr, uint32_t pcShift) {
|
void sect_RelByte(Expression const &expr, uint32_t pcShift) {
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!expr.isKnown()) {
|
if (!expr.isKnown()) {
|
||||||
createPatch(PATCHTYPE_BYTE, expr, pcShift);
|
createPatch(PATCHTYPE_BYTE, expr, pcShift);
|
||||||
@@ -725,14 +782,13 @@ void sect_RelByte(Expression &expr, uint32_t pcShift) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Output several bytes that can be relocatable or constant
|
// Output several bytes that can be relocatable or constant
|
||||||
void sect_RelBytes(uint32_t n, std::vector<Expression> &exprs) {
|
void sect_RelBytes(uint32_t n, std::vector<Expression> const &exprs) {
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (uint32_t i = 0; i < n; i++) {
|
for (uint32_t i = 0; i < n; i++) {
|
||||||
Expression &expr = exprs[i % exprs.size()];
|
if (Expression const &expr = exprs[i % exprs.size()]; !expr.isKnown()) {
|
||||||
|
|
||||||
if (!expr.isKnown()) {
|
|
||||||
createPatch(PATCHTYPE_BYTE, expr, i);
|
createPatch(PATCHTYPE_BYTE, expr, i);
|
||||||
writeByte(0);
|
writeByte(0);
|
||||||
} else {
|
} else {
|
||||||
@@ -742,9 +798,10 @@ void sect_RelBytes(uint32_t n, std::vector<Expression> &exprs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Output a word that can be relocatable or constant
|
// Output a word that can be relocatable or constant
|
||||||
void sect_RelWord(Expression &expr, uint32_t pcShift) {
|
void sect_RelWord(Expression const &expr, uint32_t pcShift) {
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!expr.isKnown()) {
|
if (!expr.isKnown()) {
|
||||||
createPatch(PATCHTYPE_WORD, expr, pcShift);
|
createPatch(PATCHTYPE_WORD, expr, pcShift);
|
||||||
@@ -755,9 +812,10 @@ void sect_RelWord(Expression &expr, uint32_t pcShift) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Output a long that can be relocatable or constant
|
// Output a long that can be relocatable or constant
|
||||||
void sect_RelLong(Expression &expr, uint32_t pcShift) {
|
void sect_RelLong(Expression const &expr, uint32_t pcShift) {
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!expr.isKnown()) {
|
if (!expr.isKnown()) {
|
||||||
createPatch(PATCHTYPE_LONG, expr, pcShift);
|
createPatch(PATCHTYPE_LONG, expr, pcShift);
|
||||||
@@ -768,9 +826,10 @@ void sect_RelLong(Expression &expr, uint32_t pcShift) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Output a PC-relative byte that can be relocatable or constant
|
// Output a PC-relative byte that can be relocatable or constant
|
||||||
void sect_PCRelByte(Expression &expr, uint32_t pcShift) {
|
void sect_PCRelByte(Expression const &expr, uint32_t pcShift) {
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (Symbol const *pc = sym_GetPC(); !expr.isDiffConstant(pc)) {
|
if (Symbol const *pc = sym_GetPC(); !expr.isDiffConstant(pc)) {
|
||||||
createPatch(PATCHTYPE_JR, expr, pcShift);
|
createPatch(PATCHTYPE_JR, expr, pcShift);
|
||||||
@@ -781,15 +840,16 @@ void sect_PCRelByte(Expression &expr, uint32_t pcShift) {
|
|||||||
int16_t offset;
|
int16_t offset;
|
||||||
|
|
||||||
// Offset is relative to the byte *after* the operand
|
// Offset is relative to the byte *after* the operand
|
||||||
if (sym == pc)
|
if (sym == pc) {
|
||||||
offset = -2; // PC as operand to `jr` is lower than reference PC by 2
|
offset = -2; // PC as operand to `jr` is lower than reference PC by 2
|
||||||
else
|
} else {
|
||||||
offset = sym->getValue() - (pc->getValue() + 1);
|
offset = sym->getValue() - (pc->getValue() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
if (offset < -128 || offset > 127) {
|
if (offset < -128 || offset > 127) {
|
||||||
error(
|
error(
|
||||||
"jr target must be between -128 and 127 bytes away, not %" PRId16
|
"JR target must be between -128 and 127 bytes away, not %" PRId16
|
||||||
"; use jp instead\n",
|
"; use JP instead\n",
|
||||||
offset
|
offset
|
||||||
);
|
);
|
||||||
writeByte(0);
|
writeByte(0);
|
||||||
@@ -805,16 +865,21 @@ void sect_BinaryFile(std::string const &name, int32_t startPos) {
|
|||||||
error("Start position cannot be negative (%" PRId32 ")\n", startPos);
|
error("Start position cannot be negative (%" PRId32 ")\n", startPos);
|
||||||
startPos = 0;
|
startPos = 0;
|
||||||
}
|
}
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
FILE *file = nullptr;
|
FILE *file = nullptr;
|
||||||
if (std::optional<std::string> fullPath = fstk_FindFile(name); fullPath)
|
if (std::optional<std::string> fullPath = fstk_FindFile(name); fullPath) {
|
||||||
file = fopen(fullPath->c_str(), "rb");
|
file = fopen(fullPath->c_str(), "rb");
|
||||||
|
}
|
||||||
if (!file) {
|
if (!file) {
|
||||||
if (generatedMissingIncludes) {
|
if (generatedMissingIncludes) {
|
||||||
if (verbose)
|
// LCOV_EXCL_START
|
||||||
|
if (verbose) {
|
||||||
printf("Aborting (-MG) on INCBIN file '%s' (%s)\n", name.c_str(), strerror(errno));
|
printf("Aborting (-MG) on INCBIN file '%s' (%s)\n", name.c_str(), strerror(errno));
|
||||||
|
}
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
failedOnMissingInclude = true;
|
failedOnMissingInclude = true;
|
||||||
} else {
|
} else {
|
||||||
error("Error opening INCBIN file '%s': %s\n", name.c_str(), strerror(errno));
|
error("Error opening INCBIN file '%s': %s\n", name.c_str(), strerror(errno));
|
||||||
@@ -831,10 +896,11 @@ void sect_BinaryFile(std::string const &name, int32_t startPos) {
|
|||||||
// The file is seekable; skip to the specified start position
|
// The file is seekable; skip to the specified start position
|
||||||
fseek(file, startPos, SEEK_SET);
|
fseek(file, startPos, SEEK_SET);
|
||||||
} else {
|
} else {
|
||||||
if (errno != ESPIPE)
|
if (errno != ESPIPE) {
|
||||||
error(
|
error(
|
||||||
"Error determining size of INCBIN file '%s': %s\n", name.c_str(), strerror(errno)
|
"Error determining size of INCBIN file '%s': %s\n", name.c_str(), strerror(errno)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
// The file isn't seekable, so we'll just skip bytes one at a time
|
// The file isn't seekable, so we'll just skip bytes one at a time
|
||||||
while (startPos--) {
|
while (startPos--) {
|
||||||
if (fgetc(file) == EOF) {
|
if (fgetc(file) == EOF) {
|
||||||
@@ -846,11 +912,13 @@ void sect_BinaryFile(std::string const &name, int32_t startPos) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int byte; (byte = fgetc(file)) != EOF;)
|
for (int byte; (byte = fgetc(file)) != EOF;) {
|
||||||
writeByte(byte);
|
writeByte(byte);
|
||||||
|
}
|
||||||
|
|
||||||
if (ferror(file))
|
if (ferror(file)) {
|
||||||
error("Error reading INCBIN file '%s': %s\n", name.c_str(), strerror(errno));
|
error("Error reading INCBIN file '%s': %s\n", name.c_str(), strerror(errno));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output a slice of a binary file
|
// Output a slice of a binary file
|
||||||
@@ -863,18 +931,24 @@ void sect_BinaryFileSlice(std::string const &name, int32_t startPos, int32_t len
|
|||||||
error("Number of bytes to read cannot be negative (%" PRId32 ")\n", length);
|
error("Number of bytes to read cannot be negative (%" PRId32 ")\n", length);
|
||||||
length = 0;
|
length = 0;
|
||||||
}
|
}
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
return;
|
||||||
if (length == 0) // Don't even bother with 0-byte slices
|
}
|
||||||
|
if (length == 0) { // Don't even bother with 0-byte slices
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
FILE *file = nullptr;
|
FILE *file = nullptr;
|
||||||
if (std::optional<std::string> fullPath = fstk_FindFile(name); fullPath)
|
if (std::optional<std::string> fullPath = fstk_FindFile(name); fullPath) {
|
||||||
file = fopen(fullPath->c_str(), "rb");
|
file = fopen(fullPath->c_str(), "rb");
|
||||||
|
}
|
||||||
if (!file) {
|
if (!file) {
|
||||||
if (generatedMissingIncludes) {
|
if (generatedMissingIncludes) {
|
||||||
if (verbose)
|
// LCOV_EXCL_START
|
||||||
|
if (verbose) {
|
||||||
printf("Aborting (-MG) on INCBIN file '%s' (%s)\n", name.c_str(), strerror(errno));
|
printf("Aborting (-MG) on INCBIN file '%s' (%s)\n", name.c_str(), strerror(errno));
|
||||||
|
}
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
failedOnMissingInclude = true;
|
failedOnMissingInclude = true;
|
||||||
} else {
|
} else {
|
||||||
error("Error opening INCBIN file '%s': %s\n", name.c_str(), strerror(errno));
|
error("Error opening INCBIN file '%s': %s\n", name.c_str(), strerror(errno));
|
||||||
@@ -901,10 +975,11 @@ void sect_BinaryFileSlice(std::string const &name, int32_t startPos, int32_t len
|
|||||||
// The file is seekable; skip to the specified start position
|
// The file is seekable; skip to the specified start position
|
||||||
fseek(file, startPos, SEEK_SET);
|
fseek(file, startPos, SEEK_SET);
|
||||||
} else {
|
} else {
|
||||||
if (errno != ESPIPE)
|
if (errno != ESPIPE) {
|
||||||
error(
|
error(
|
||||||
"Error determining size of INCBIN file '%s': %s\n", name.c_str(), strerror(errno)
|
"Error determining size of INCBIN file '%s': %s\n", name.c_str(), strerror(errno)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
// The file isn't seekable, so we'll just skip bytes one at a time
|
// The file isn't seekable, so we'll just skip bytes one at a time
|
||||||
while (startPos--) {
|
while (startPos--) {
|
||||||
if (fgetc(file) == EOF) {
|
if (fgetc(file) == EOF) {
|
||||||
@@ -950,11 +1025,13 @@ void sect_PushSection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void sect_PopSection() {
|
void sect_PopSection() {
|
||||||
if (sectionStack.empty())
|
if (sectionStack.empty()) {
|
||||||
fatalerror("No entries in the section stack\n");
|
fatalerror("No entries in the section stack\n");
|
||||||
|
}
|
||||||
|
|
||||||
if (currentLoadSection)
|
if (currentLoadSection) {
|
||||||
fatalerror("Cannot change the section within a `LOAD` block\n");
|
sect_EndLoadSection("POPS");
|
||||||
|
}
|
||||||
|
|
||||||
SectionStackEntry entry = sectionStack.front();
|
SectionStackEntry entry = sectionStack.front();
|
||||||
sectionStack.pop_front();
|
sectionStack.pop_front();
|
||||||
@@ -968,15 +1045,24 @@ void sect_PopSection() {
|
|||||||
std::swap(currentUnionStack, entry.unionStack);
|
std::swap(currentUnionStack, entry.unionStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sect_CheckStack() {
|
||||||
|
if (!sectionStack.empty()) {
|
||||||
|
warning(WARNING_UNMATCHED_DIRECTIVE, "`PUSHS` without corresponding `POPS`\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void sect_EndSection() {
|
void sect_EndSection() {
|
||||||
if (!currentSection)
|
if (!currentSection) {
|
||||||
fatalerror("Cannot end the section outside of a SECTION\n");
|
fatalerror("Cannot end the section outside of a SECTION\n");
|
||||||
|
}
|
||||||
|
|
||||||
if (currentLoadSection)
|
if (!currentUnionStack.empty()) {
|
||||||
fatalerror("Cannot end the section within a `LOAD` block\n");
|
|
||||||
|
|
||||||
if (!currentUnionStack.empty())
|
|
||||||
fatalerror("Cannot end the section within a UNION\n");
|
fatalerror("Cannot end the section within a UNION\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentLoadSection) {
|
||||||
|
sect_EndLoadSection("ENDSECTION");
|
||||||
|
}
|
||||||
|
|
||||||
// Reset the section scope
|
// Reset the section scope
|
||||||
currentSection = nullptr;
|
currentSection = nullptr;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#include "asm/symbol.hpp"
|
#include "asm/symbol.hpp"
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ std::unordered_map<std::string, Symbol> symbols;
|
|||||||
std::unordered_set<std::string> purgedSymbols;
|
std::unordered_set<std::string> purgedSymbols;
|
||||||
|
|
||||||
static Symbol const *globalScope = nullptr; // Current section's global label scope
|
static Symbol const *globalScope = nullptr; // Current section's global label scope
|
||||||
static Symbol const *localScope = nullptr; // Current section's local label scope
|
static Symbol const *localScope = nullptr; // Current section's local label scope
|
||||||
static Symbol *PCSymbol;
|
static Symbol *PCSymbol;
|
||||||
static Symbol *NARGSymbol;
|
static Symbol *NARGSymbol;
|
||||||
static Symbol *globalScopeSymbol;
|
static Symbol *globalScopeSymbol;
|
||||||
@@ -40,8 +40,9 @@ bool sym_IsPC(Symbol const *sym) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void sym_ForEach(void (*callback)(Symbol &)) {
|
void sym_ForEach(void (*callback)(Symbol &)) {
|
||||||
for (auto &it : symbols)
|
for (auto &it : symbols) {
|
||||||
callback(it.second);
|
callback(it.second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t NARGCallback() {
|
static int32_t NARGCallback() {
|
||||||
@@ -92,7 +93,7 @@ int32_t Symbol::getOutputValue() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ContentSpan const &Symbol::getMacro() const {
|
ContentSpan const &Symbol::getMacro() const {
|
||||||
assume((std::holds_alternative<ContentSpan>(data)));
|
assume(std::holds_alternative<ContentSpan>(data));
|
||||||
return std::get<ContentSpan>(data);
|
return std::get<ContentSpan>(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,8 +102,9 @@ std::shared_ptr<std::string> Symbol::getEqus() const {
|
|||||||
std::holds_alternative<std::shared_ptr<std::string>>(data)
|
std::holds_alternative<std::shared_ptr<std::string>>(data)
|
||||||
|| std::holds_alternative<std::shared_ptr<std::string> (*)()>(data)
|
|| std::holds_alternative<std::shared_ptr<std::string> (*)()>(data)
|
||||||
);
|
);
|
||||||
if (auto *callback = std::get_if<std::shared_ptr<std::string> (*)()>(&data); callback)
|
if (auto *callback = std::get_if<std::shared_ptr<std::string> (*)()>(&data); callback) {
|
||||||
return (*callback)();
|
return (*callback)();
|
||||||
|
}
|
||||||
return std::get<std::shared_ptr<std::string>>(data);
|
return std::get<std::shared_ptr<std::string>>(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,8 +125,9 @@ static void updateSymbolFilename(Symbol &sym) {
|
|||||||
sym.fileLine = sym.src ? lexer_GetLineNo() : 0;
|
sym.fileLine = sym.src ? lexer_GetLineNo() : 0;
|
||||||
|
|
||||||
// If the old node was registered, ensure the new one is too
|
// If the old node was registered, ensure the new one is too
|
||||||
if (oldSrc && oldSrc->ID != (uint32_t)-1)
|
if (oldSrc && oldSrc->ID != UINT32_MAX) {
|
||||||
out_RegisterNode(sym.src);
|
out_RegisterNode(sym.src);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void alreadyDefinedError(Symbol const &sym, char const *asType) {
|
static void alreadyDefinedError(Symbol const &sym, char const *asType) {
|
||||||
@@ -133,8 +136,9 @@ static void alreadyDefinedError(Symbol const &sym, char const *asType) {
|
|||||||
error("'%s' is reserved for a built-in symbol\n", sym.name.c_str());
|
error("'%s' is reserved for a built-in symbol\n", sym.name.c_str());
|
||||||
} else {
|
} else {
|
||||||
error("'%s' already defined", sym.name.c_str());
|
error("'%s' already defined", sym.name.c_str());
|
||||||
if (asType)
|
if (asType) {
|
||||||
fprintf(stderr, " as %s", asType);
|
fprintf(stderr, " as %s", asType);
|
||||||
|
}
|
||||||
fputs(" at ", stderr);
|
fputs(" at ", stderr);
|
||||||
dumpFilename(sym);
|
dumpFilename(sym);
|
||||||
}
|
}
|
||||||
@@ -169,7 +173,7 @@ static Symbol &createSymbol(std::string const &symName) {
|
|||||||
sym.section = nullptr;
|
sym.section = nullptr;
|
||||||
sym.src = fstk_GetFileStack();
|
sym.src = fstk_GetFileStack();
|
||||||
sym.fileLine = sym.src ? lexer_GetLineNo() : 0;
|
sym.fileLine = sym.src ? lexer_GetLineNo() : 0;
|
||||||
sym.ID = -1;
|
sym.ID = UINT32_MAX;
|
||||||
sym.defIndex = nextDefIndex++;
|
sym.defIndex = nextDefIndex++;
|
||||||
|
|
||||||
return sym;
|
return sym;
|
||||||
@@ -184,28 +188,34 @@ static bool isAutoScoped(std::string const &symName) {
|
|||||||
size_t dotPos = symName.find('.');
|
size_t dotPos = symName.find('.');
|
||||||
|
|
||||||
// If there are no dots, it's not a local label
|
// If there are no dots, it's not a local label
|
||||||
if (dotPos == std::string::npos)
|
if (dotPos == std::string::npos) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Label scopes `.` and `..` are the only nonlocal identifiers that start with a dot
|
// Label scopes `.` and `..` are the only nonlocal identifiers that start with a dot
|
||||||
if (dotPos == 0 && symName.find_first_not_of('.') == symName.npos)
|
if (dotPos == 0 && symName.find_first_not_of('.') == symName.npos) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for nothing after the dot
|
// Check for nothing after the dot
|
||||||
if (dotPos == symName.length() - 1)
|
if (dotPos == symName.length() - 1) {
|
||||||
fatalerror("'%s' is a nonsensical reference to an empty local label\n", symName.c_str());
|
fatalerror("'%s' is a nonsensical reference to an empty local label\n", symName.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
// Check for more than one dot
|
// Check for more than one dot
|
||||||
if (symName.find('.', dotPos + 1) != std::string::npos)
|
if (symName.find('.', dotPos + 1) != std::string::npos) {
|
||||||
fatalerror("'%s' is a nonsensical reference to a nested local label\n", symName.c_str());
|
fatalerror("'%s' is a nonsensical reference to a nested local label\n", symName.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
// Check for already-qualified local label
|
// Check for already-qualified local label
|
||||||
if (dotPos > 0)
|
if (dotPos > 0) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for unqualifiable local label
|
// Check for unqualifiable local label
|
||||||
if (!globalScope)
|
if (!globalScope) {
|
||||||
fatalerror("Unqualified local label '%s' in main scope\n", symName.c_str());
|
fatalerror("Unqualified local label '%s' in main scope\n", symName.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -252,24 +262,28 @@ void sym_Purge(std::string const &symName) {
|
|||||||
Symbol *sym = sym_FindScopedValidSymbol(symName);
|
Symbol *sym = sym_FindScopedValidSymbol(symName);
|
||||||
|
|
||||||
if (!sym) {
|
if (!sym) {
|
||||||
if (sym_IsPurgedScoped(symName))
|
if (sym_IsPurgedScoped(symName)) {
|
||||||
error("'%s' was already purged\n", symName.c_str());
|
error("'%s' was already purged\n", symName.c_str());
|
||||||
else
|
} else {
|
||||||
error("'%s' not defined\n", symName.c_str());
|
error("'%s' not defined\n", symName.c_str());
|
||||||
|
}
|
||||||
} else if (sym->isBuiltin) {
|
} else if (sym->isBuiltin) {
|
||||||
error("Built-in symbol '%s' cannot be purged\n", symName.c_str());
|
error("Built-in symbol '%s' cannot be purged\n", symName.c_str());
|
||||||
} else if (sym->ID != (uint32_t)-1) {
|
} else if (sym->ID != UINT32_MAX) {
|
||||||
error("Symbol \"%s\" is referenced and thus cannot be purged\n", symName.c_str());
|
error("Symbol \"%s\" is referenced and thus cannot be purged\n", symName.c_str());
|
||||||
} else {
|
} else {
|
||||||
if (sym->isExported)
|
if (sym->isExported) {
|
||||||
warning(WARNING_PURGE_1, "Purging an exported symbol \"%s\"\n", symName.c_str());
|
warning(WARNING_PURGE_1, "Purging an exported symbol \"%s\"\n", symName.c_str());
|
||||||
else if (sym->isLabel())
|
} else if (sym->isLabel()) {
|
||||||
warning(WARNING_PURGE_2, "Purging a label \"%s\"\n", symName.c_str());
|
warning(WARNING_PURGE_2, "Purging a label \"%s\"\n", symName.c_str());
|
||||||
|
}
|
||||||
// Do not keep a reference to the label after purging it
|
// Do not keep a reference to the label after purging it
|
||||||
if (sym == globalScope)
|
if (sym == globalScope) {
|
||||||
globalScope = nullptr;
|
globalScope = nullptr;
|
||||||
if (sym == localScope)
|
}
|
||||||
|
if (sym == localScope) {
|
||||||
localScope = nullptr;
|
localScope = nullptr;
|
||||||
|
}
|
||||||
purgedSymbols.emplace(sym->name);
|
purgedSymbols.emplace(sym->name);
|
||||||
symbols.erase(sym->name);
|
symbols.erase(sym->name);
|
||||||
}
|
}
|
||||||
@@ -295,31 +309,22 @@ void sym_SetRSValue(int32_t value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t Symbol::getConstantValue() const {
|
uint32_t Symbol::getConstantValue() const {
|
||||||
if (isConstant())
|
if (isConstant()) {
|
||||||
return getValue();
|
return getValue();
|
||||||
|
}
|
||||||
|
|
||||||
if (sym_IsPC(this)) {
|
if (sym_IsPC(this)) {
|
||||||
if (!getSection())
|
if (!getSection()) {
|
||||||
error("PC has no value outside of a section\n");
|
error("PC has no value outside of a section\n");
|
||||||
else
|
} else {
|
||||||
error("PC does not have a constant value; the current section is not fixed\n");
|
error("PC does not have a constant value; the current section is not fixed\n");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
error("\"%s\" does not have a constant value\n", name.c_str());
|
error("\"%s\" does not have a constant value\n", name.c_str());
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t sym_GetConstantValue(std::string const &symName) {
|
|
||||||
if (Symbol const *sym = sym_FindScopedSymbol(symName); sym)
|
|
||||||
return sym->getConstantValue();
|
|
||||||
|
|
||||||
if (sym_IsPurgedScoped(symName))
|
|
||||||
error("'%s' not defined; it was purged\n", symName.c_str());
|
|
||||||
else
|
|
||||||
error("'%s' not defined\n", symName.c_str());
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<Symbol const *, Symbol const *> sym_GetCurrentLabelScopes() {
|
std::pair<Symbol const *, Symbol const *> sym_GetCurrentLabelScopes() {
|
||||||
return {globalScope, localScope};
|
return {globalScope, localScope};
|
||||||
}
|
}
|
||||||
@@ -361,8 +366,9 @@ static Symbol *createNonrelocSymbol(std::string const &symName, bool numeric) {
|
|||||||
Symbol *sym_AddEqu(std::string const &symName, int32_t value) {
|
Symbol *sym_AddEqu(std::string const &symName, int32_t value) {
|
||||||
Symbol *sym = createNonrelocSymbol(symName, true);
|
Symbol *sym = createNonrelocSymbol(symName, true);
|
||||||
|
|
||||||
if (!sym)
|
if (!sym) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
sym->type = SYM_EQU;
|
sym->type = SYM_EQU;
|
||||||
sym->data = value;
|
sym->data = value;
|
||||||
@@ -373,8 +379,9 @@ Symbol *sym_AddEqu(std::string const &symName, int32_t value) {
|
|||||||
Symbol *sym_RedefEqu(std::string const &symName, int32_t value) {
|
Symbol *sym_RedefEqu(std::string const &symName, int32_t value) {
|
||||||
Symbol *sym = sym_FindExactSymbol(symName);
|
Symbol *sym = sym_FindExactSymbol(symName);
|
||||||
|
|
||||||
if (!sym)
|
if (!sym) {
|
||||||
return sym_AddEqu(symName, value);
|
return sym_AddEqu(symName, value);
|
||||||
|
}
|
||||||
|
|
||||||
if (sym->isDefined() && sym->type != SYM_EQU) {
|
if (sym->isDefined() && sym->type != SYM_EQU) {
|
||||||
alreadyDefinedError(*sym, "non-EQU");
|
alreadyDefinedError(*sym, "non-EQU");
|
||||||
@@ -394,8 +401,9 @@ Symbol *sym_RedefEqu(std::string const &symName, int32_t value) {
|
|||||||
Symbol *sym_AddString(std::string const &symName, std::shared_ptr<std::string> str) {
|
Symbol *sym_AddString(std::string const &symName, std::shared_ptr<std::string> str) {
|
||||||
Symbol *sym = createNonrelocSymbol(symName, false);
|
Symbol *sym = createNonrelocSymbol(symName, false);
|
||||||
|
|
||||||
if (!sym)
|
if (!sym) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
sym->type = SYM_EQUS;
|
sym->type = SYM_EQUS;
|
||||||
sym->data = str;
|
sym->data = str;
|
||||||
@@ -405,8 +413,9 @@ Symbol *sym_AddString(std::string const &symName, std::shared_ptr<std::string> s
|
|||||||
Symbol *sym_RedefString(std::string const &symName, std::shared_ptr<std::string> str) {
|
Symbol *sym_RedefString(std::string const &symName, std::shared_ptr<std::string> str) {
|
||||||
Symbol *sym = sym_FindExactSymbol(symName);
|
Symbol *sym = sym_FindExactSymbol(symName);
|
||||||
|
|
||||||
if (!sym)
|
if (!sym) {
|
||||||
return sym_AddString(symName, str);
|
return sym_AddString(symName, str);
|
||||||
|
}
|
||||||
|
|
||||||
if (sym->type != SYM_EQUS) {
|
if (sym->type != SYM_EQUS) {
|
||||||
if (sym->isDefined()) {
|
if (sym->isDefined()) {
|
||||||
@@ -460,14 +469,16 @@ static Symbol *addLabel(std::string const &symName) {
|
|||||||
}
|
}
|
||||||
// If the symbol already exists as a ref, just "take over" it
|
// If the symbol already exists as a ref, just "take over" it
|
||||||
sym->type = SYM_LABEL;
|
sym->type = SYM_LABEL;
|
||||||
sym->data = (int32_t)sect_GetSymbolOffset();
|
sym->data = static_cast<int32_t>(sect_GetSymbolOffset());
|
||||||
// Don't export anonymous labels
|
// Don't export anonymous labels
|
||||||
if (exportAll && !symName.starts_with('!'))
|
if (exportAll && !symName.starts_with('!')) {
|
||||||
sym->isExported = true;
|
sym->isExported = true;
|
||||||
|
}
|
||||||
sym->section = sect_GetSymbolSection();
|
sym->section = sect_GetSymbolSection();
|
||||||
|
|
||||||
if (sym && !sym->section)
|
if (sym && !sym->section) {
|
||||||
error("Label \"%s\" created outside of a SECTION\n", symName.c_str());
|
error("Label \"%s\" created outside of a SECTION\n", symName.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
return sym;
|
return sym;
|
||||||
}
|
}
|
||||||
@@ -478,8 +489,9 @@ Symbol *sym_AddLocalLabel(std::string const &symName) {
|
|||||||
|
|
||||||
Symbol *sym = addLabel(isAutoScoped(symName) ? globalScope->name + symName : symName);
|
Symbol *sym = addLabel(isAutoScoped(symName) ? globalScope->name + symName : symName);
|
||||||
|
|
||||||
if (sym)
|
if (sym) {
|
||||||
localScope = sym;
|
localScope = sym;
|
||||||
|
}
|
||||||
|
|
||||||
return sym;
|
return sym;
|
||||||
}
|
}
|
||||||
@@ -503,8 +515,10 @@ static uint32_t anonLabelID = 0;
|
|||||||
|
|
||||||
Symbol *sym_AddAnonLabel() {
|
Symbol *sym_AddAnonLabel() {
|
||||||
if (anonLabelID == UINT32_MAX) {
|
if (anonLabelID == UINT32_MAX) {
|
||||||
|
// LCOV_EXCL_START
|
||||||
error("Only %" PRIu32 " anonymous labels can be created!", anonLabelID);
|
error("Only %" PRIu32 " anonymous labels can be created!", anonLabelID);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string anon = sym_MakeAnonLabelName(0, true); // The direction is important!
|
std::string anon = sym_MakeAnonLabelName(0, true); // The direction is important!
|
||||||
@@ -516,7 +530,7 @@ std::string sym_MakeAnonLabelName(uint32_t ofs, bool neg) {
|
|||||||
uint32_t id = 0;
|
uint32_t id = 0;
|
||||||
|
|
||||||
if (neg) {
|
if (neg) {
|
||||||
if (ofs > anonLabelID)
|
if (ofs > anonLabelID) {
|
||||||
error(
|
error(
|
||||||
"Reference to anonymous label %" PRIu32 " before, when only %" PRIu32
|
"Reference to anonymous label %" PRIu32 " before, when only %" PRIu32
|
||||||
" ha%s been created so far\n",
|
" ha%s been created so far\n",
|
||||||
@@ -524,45 +538,52 @@ std::string sym_MakeAnonLabelName(uint32_t ofs, bool neg) {
|
|||||||
anonLabelID,
|
anonLabelID,
|
||||||
anonLabelID == 1 ? "s" : "ve"
|
anonLabelID == 1 ? "s" : "ve"
|
||||||
);
|
);
|
||||||
else
|
} else {
|
||||||
id = anonLabelID - ofs;
|
id = anonLabelID - ofs;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ofs--; // We're referencing symbols that haven't been created yet...
|
ofs--; // We're referencing symbols that haven't been created yet...
|
||||||
if (ofs > UINT32_MAX - anonLabelID)
|
if (ofs > UINT32_MAX - anonLabelID) {
|
||||||
|
// LCOV_EXCL_START
|
||||||
error(
|
error(
|
||||||
"Reference to anonymous label %" PRIu32 " after, when only %" PRIu32
|
"Reference to anonymous label %" PRIu32 " after, when only %" PRIu32
|
||||||
" may still be created\n",
|
" may still be created\n",
|
||||||
ofs + 1,
|
ofs + 1,
|
||||||
UINT32_MAX - anonLabelID
|
UINT32_MAX - anonLabelID
|
||||||
);
|
);
|
||||||
else
|
} else {
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
id = anonLabelID + ofs;
|
id = anonLabelID + ofs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string anon("!");
|
return "!"s + std::to_string(id);
|
||||||
anon += std::to_string(id);
|
|
||||||
return anon;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sym_Export(std::string const &symName) {
|
void sym_Export(std::string const &symName) {
|
||||||
if (symName.starts_with('!')) {
|
if (symName.starts_with('!')) {
|
||||||
|
// LCOV_EXCL_START
|
||||||
|
// The parser does not accept anonymous labels for an `EXPORT` directive
|
||||||
error("Anonymous labels cannot be exported\n");
|
error("Anonymous labels cannot be exported\n");
|
||||||
return;
|
return;
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
}
|
}
|
||||||
|
|
||||||
Symbol *sym = sym_FindScopedSymbol(symName);
|
Symbol *sym = sym_FindScopedSymbol(symName);
|
||||||
|
|
||||||
// If the symbol doesn't exist, create a ref that can be purged
|
// If the symbol doesn't exist, create a ref that can be purged
|
||||||
if (!sym)
|
if (!sym) {
|
||||||
sym = sym_Ref(symName);
|
sym = sym_Ref(symName);
|
||||||
|
}
|
||||||
sym->isExported = true;
|
sym->isExported = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
Symbol *sym = createNonrelocSymbol(symName, false);
|
Symbol *sym = createNonrelocSymbol(symName, false);
|
||||||
|
|
||||||
if (!sym)
|
if (!sym) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
sym->type = SYM_MACRO;
|
sym->type = SYM_MACRO;
|
||||||
sym->data = span;
|
sym->data = span;
|
||||||
@@ -627,11 +648,13 @@ void sym_Init(time_t now) {
|
|||||||
sym_AddEqu("__RGBDS_RC__"s, PACKAGE_VERSION_RC)->isBuiltin = true;
|
sym_AddEqu("__RGBDS_RC__"s, PACKAGE_VERSION_RC)->isBuiltin = true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (now == (time_t)-1) {
|
// LCOV_EXCL_START
|
||||||
|
if (now == static_cast<time_t>(-1)) {
|
||||||
warn("Failed to determine current time");
|
warn("Failed to determine current time");
|
||||||
// Fall back by pretending we are at the Epoch
|
// Fall back by pretending we are at the Epoch
|
||||||
now = 0;
|
now = 0;
|
||||||
}
|
}
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
tm const *time_local = localtime(&now);
|
tm const *time_local = localtime(&now);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#include "asm/warning.hpp"
|
#include "asm/warning.hpp"
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "error.hpp"
|
#include "error.hpp"
|
||||||
#include "helpers.hpp" // QUOTEDSTRLEN
|
#include "helpers.hpp"
|
||||||
#include "itertools.hpp"
|
#include "itertools.hpp"
|
||||||
|
|
||||||
#include "asm/fstack.hpp"
|
#include "asm/fstack.hpp"
|
||||||
@@ -20,276 +20,188 @@
|
|||||||
unsigned int nbErrors = 0;
|
unsigned int nbErrors = 0;
|
||||||
unsigned int maxErrors = 0;
|
unsigned int maxErrors = 0;
|
||||||
|
|
||||||
static WarningState const defaultWarnings[ARRAY_SIZE(warningStates)] = {
|
Diagnostics warningStates;
|
||||||
WARNING_ENABLED, // WARNING_ASSERT
|
bool warningsAreErrors;
|
||||||
WARNING_DISABLED, // WARNING_BACKWARDS_FOR
|
|
||||||
WARNING_DISABLED, // WARNING_BUILTIN_ARG
|
|
||||||
WARNING_DISABLED, // WARNING_CHARMAP_REDEF
|
|
||||||
WARNING_DISABLED, // WARNING_DIV
|
|
||||||
WARNING_DISABLED, // WARNING_EMPTY_DATA_DIRECTIVE
|
|
||||||
WARNING_DISABLED, // WARNING_EMPTY_MACRO_ARG
|
|
||||||
WARNING_DISABLED, // WARNING_EMPTY_STRRPL
|
|
||||||
WARNING_DISABLED, // WARNING_LARGE_CONSTANT
|
|
||||||
WARNING_DISABLED, // WARNING_MACRO_SHIFT
|
|
||||||
WARNING_ENABLED, // WARNING_NESTED_COMMENT
|
|
||||||
WARNING_ENABLED, // WARNING_OBSOLETE
|
|
||||||
WARNING_DISABLED, // WARNING_SHIFT
|
|
||||||
WARNING_DISABLED, // WARNING_SHIFT_AMOUNT
|
|
||||||
WARNING_ENABLED, // WARNING_USER
|
|
||||||
|
|
||||||
WARNING_DISABLED, // WARNING_NUMERIC_STRING_1
|
enum WarningLevel {
|
||||||
WARNING_DISABLED, // WARNING_NUMERIC_STRING_2
|
LEVEL_DEFAULT, // Warnings that are enabled by default
|
||||||
WARNING_ENABLED, // WARNING_TRUNCATION_1
|
LEVEL_ALL, // Warnings that probably indicate an error
|
||||||
WARNING_DISABLED, // WARNING_TRUNCATION_2
|
LEVEL_EXTRA, // Warnings that are less likely to indicate an error
|
||||||
WARNING_ENABLED, // WARNING_PURGE_1
|
LEVEL_EVERYTHING, // Literally every warning
|
||||||
WARNING_DISABLED, // WARNING_PURGE_2
|
|
||||||
WARNING_ENABLED, // WARNING_UNMAPPED_CHAR_1
|
|
||||||
WARNING_DISABLED, // WARNING_UNMAPPED_CHAR_2
|
|
||||||
};
|
};
|
||||||
|
|
||||||
WarningState warningStates[ARRAY_SIZE(warningStates)];
|
struct WarningFlag {
|
||||||
|
char const *name;
|
||||||
|
WarningLevel level;
|
||||||
|
};
|
||||||
|
|
||||||
bool warningsAreErrors; // Set if `-Werror` was specified
|
static WarningFlag const metaWarnings[] = {
|
||||||
|
{"all", LEVEL_ALL },
|
||||||
static WarningState warningState(WarningID id) {
|
{"extra", LEVEL_EXTRA },
|
||||||
// Check if warnings are globally disabled
|
{"everything", LEVEL_EVERYTHING},
|
||||||
if (!warnings)
|
};
|
||||||
return WARNING_DISABLED;
|
|
||||||
|
|
||||||
// Get the actual state
|
|
||||||
WarningState state = warningStates[id];
|
|
||||||
|
|
||||||
if (state == WARNING_DEFAULT)
|
|
||||||
// The state isn't set, grab its default state
|
|
||||||
state = defaultWarnings[id];
|
|
||||||
|
|
||||||
if (warningsAreErrors && state == WARNING_ENABLED)
|
|
||||||
state = WARNING_ERROR;
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char const * const warningFlags[NB_WARNINGS] = {
|
|
||||||
"assert",
|
|
||||||
"backwards-for",
|
|
||||||
"builtin-args",
|
|
||||||
"charmap-redef",
|
|
||||||
"div",
|
|
||||||
"empty-data-directive",
|
|
||||||
"empty-macro-arg",
|
|
||||||
"empty-strrpl",
|
|
||||||
"large-constant",
|
|
||||||
"macro-shift",
|
|
||||||
"nested-comment",
|
|
||||||
"obsolete",
|
|
||||||
"shift",
|
|
||||||
"shift-amount",
|
|
||||||
"user",
|
|
||||||
|
|
||||||
|
static WarningFlag const warningFlags[NB_WARNINGS] = {
|
||||||
|
{"assert", LEVEL_DEFAULT },
|
||||||
|
{"backwards-for", LEVEL_ALL },
|
||||||
|
{"builtin-args", LEVEL_ALL },
|
||||||
|
{"charmap-redef", LEVEL_ALL },
|
||||||
|
{"div", LEVEL_EVERYTHING},
|
||||||
|
{"empty-data-directive", LEVEL_ALL },
|
||||||
|
{"empty-macro-arg", LEVEL_EXTRA },
|
||||||
|
{"empty-strrpl", LEVEL_ALL },
|
||||||
|
{"large-constant", LEVEL_ALL },
|
||||||
|
{"macro-shift", LEVEL_EXTRA },
|
||||||
|
{"nested-comment", LEVEL_DEFAULT },
|
||||||
|
{"obsolete", LEVEL_DEFAULT },
|
||||||
|
{"shift", LEVEL_EVERYTHING},
|
||||||
|
{"shift-amount", LEVEL_EVERYTHING},
|
||||||
|
{"unmatched-directive", LEVEL_EXTRA },
|
||||||
|
{"unterminated-load", LEVEL_EXTRA },
|
||||||
|
{"user", LEVEL_DEFAULT },
|
||||||
// Parametric warnings
|
// Parametric warnings
|
||||||
"numeric-string",
|
{"numeric-string", LEVEL_EVERYTHING},
|
||||||
"numeric-string",
|
{"numeric-string", LEVEL_EVERYTHING},
|
||||||
"purge",
|
{"purge", LEVEL_DEFAULT },
|
||||||
"purge",
|
{"purge", LEVEL_ALL },
|
||||||
"truncation",
|
{"truncation", LEVEL_DEFAULT },
|
||||||
"truncation",
|
{"truncation", LEVEL_EXTRA },
|
||||||
"unmapped-char",
|
{"unmapped-char", LEVEL_DEFAULT },
|
||||||
"unmapped-char",
|
{"unmapped-char", LEVEL_ALL },
|
||||||
|
|
||||||
// Meta warnings
|
|
||||||
"all",
|
|
||||||
"extra",
|
|
||||||
"everything", // Especially useful for testing
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct {
|
static const struct {
|
||||||
char const *name;
|
WarningID firstID;
|
||||||
uint8_t nbLevels;
|
WarningID lastID;
|
||||||
uint8_t defaultLevel;
|
uint8_t defaultLevel;
|
||||||
} paramWarnings[] = {
|
} paramWarnings[] = {
|
||||||
{"numeric-string", 2, 1},
|
{WARNING_NUMERIC_STRING_1, WARNING_NUMERIC_STRING_2, 1},
|
||||||
{"purge", 2, 1},
|
{WARNING_PURGE_1, WARNING_PURGE_2, 1},
|
||||||
{"truncation", 2, 2},
|
{WARNING_TRUNCATION_1, WARNING_TRUNCATION_2, 2},
|
||||||
{"unmapped-char", 2, 1},
|
{WARNING_UNMAPPED_CHAR_1, WARNING_UNMAPPED_CHAR_2, 1},
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool tryProcessParamWarning(char const *flag, uint8_t param, WarningState state) {
|
enum WarningBehavior { DISABLED, ENABLED, ERROR };
|
||||||
WarningID baseID = PARAM_WARNINGS_START;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < ARRAY_SIZE(paramWarnings); i++) {
|
static WarningBehavior getWarningBehavior(WarningID id) {
|
||||||
uint8_t maxParam = paramWarnings[i].nbLevels;
|
// Check if warnings are globally disabled
|
||||||
|
if (!warnings) {
|
||||||
if (!strcmp(flag, paramWarnings[i].name)) { // Match!
|
return WarningBehavior::DISABLED;
|
||||||
if (!strcmp(flag, "numeric-string"))
|
|
||||||
warning(WARNING_OBSOLETE, "Warning flag \"numeric-string\" is deprecated\n");
|
|
||||||
|
|
||||||
// If making the warning an error but param is 0, set to the maximum
|
|
||||||
// This accommodates `-Werror=flag`, but also `-Werror=flag=0`, which is
|
|
||||||
// thus filtered out by the caller.
|
|
||||||
// A param of 0 makes sense for disabling everything, but neither for
|
|
||||||
// enabling nor "erroring". Use the default for those.
|
|
||||||
if (param == 0 && state != WARNING_DISABLED) {
|
|
||||||
param = paramWarnings[i].defaultLevel;
|
|
||||||
} else if (param > maxParam) {
|
|
||||||
if (param != 255) // Don't warn if already capped
|
|
||||||
warnx(
|
|
||||||
"Got parameter %" PRIu8
|
|
||||||
" for warning flag \"%s\", but the maximum is %" PRIu8 "; capping.\n",
|
|
||||||
param,
|
|
||||||
flag,
|
|
||||||
maxParam
|
|
||||||
);
|
|
||||||
param = maxParam;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the first <param> to enabled/error, and disable the rest
|
|
||||||
for (uint8_t ofs = 0; ofs < maxParam; ofs++) {
|
|
||||||
warningStates[baseID + ofs] = ofs < param ? state : WARNING_DISABLED;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
baseID = (WarningID)(baseID + maxParam);
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
// Get the state of this warning flag
|
||||||
|
WarningState const &flagState = warningStates.flagStates[id];
|
||||||
|
WarningState const &metaState = warningStates.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 = 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 == LEVEL_DEFAULT) { // enabled by default
|
||||||
|
return enabledBehavior;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No flag enables this warning, explicitly or implicitly
|
||||||
|
return WarningBehavior::DISABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MetaWarningCommand { META_WARNING_DONE = NB_WARNINGS };
|
void WarningState::update(WarningState other) {
|
||||||
|
if (other.state != WARNING_DEFAULT) {
|
||||||
// Warnings that probably indicate an error
|
state = other.state;
|
||||||
static uint8_t const _wallCommands[] = {
|
}
|
||||||
WARNING_BACKWARDS_FOR,
|
if (other.error != WARNING_DEFAULT) {
|
||||||
WARNING_BUILTIN_ARG,
|
error = other.error;
|
||||||
WARNING_CHARMAP_REDEF,
|
}
|
||||||
WARNING_EMPTY_DATA_DIRECTIVE,
|
}
|
||||||
WARNING_EMPTY_STRRPL,
|
|
||||||
WARNING_LARGE_CONSTANT,
|
|
||||||
WARNING_NESTED_COMMENT,
|
|
||||||
WARNING_OBSOLETE,
|
|
||||||
WARNING_PURGE_1,
|
|
||||||
WARNING_PURGE_2,
|
|
||||||
WARNING_UNMAPPED_CHAR_1,
|
|
||||||
META_WARNING_DONE,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Warnings that are less likely to indicate an error
|
|
||||||
static uint8_t const _wextraCommands[] = {
|
|
||||||
WARNING_EMPTY_MACRO_ARG,
|
|
||||||
WARNING_MACRO_SHIFT,
|
|
||||||
WARNING_NESTED_COMMENT,
|
|
||||||
WARNING_OBSOLETE,
|
|
||||||
WARNING_PURGE_1,
|
|
||||||
WARNING_PURGE_2,
|
|
||||||
WARNING_TRUNCATION_1,
|
|
||||||
WARNING_TRUNCATION_2,
|
|
||||||
WARNING_UNMAPPED_CHAR_1,
|
|
||||||
WARNING_UNMAPPED_CHAR_2,
|
|
||||||
META_WARNING_DONE,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Literally everything. Notably useful for testing
|
|
||||||
static uint8_t const _weverythingCommands[] = {
|
|
||||||
WARNING_BACKWARDS_FOR,
|
|
||||||
WARNING_BUILTIN_ARG,
|
|
||||||
WARNING_DIV,
|
|
||||||
WARNING_EMPTY_DATA_DIRECTIVE,
|
|
||||||
WARNING_EMPTY_MACRO_ARG,
|
|
||||||
WARNING_EMPTY_STRRPL,
|
|
||||||
WARNING_LARGE_CONSTANT,
|
|
||||||
WARNING_MACRO_SHIFT,
|
|
||||||
WARNING_NESTED_COMMENT,
|
|
||||||
WARNING_OBSOLETE,
|
|
||||||
WARNING_PURGE_1,
|
|
||||||
WARNING_PURGE_2,
|
|
||||||
WARNING_SHIFT,
|
|
||||||
WARNING_SHIFT_AMOUNT,
|
|
||||||
WARNING_TRUNCATION_1,
|
|
||||||
WARNING_TRUNCATION_2,
|
|
||||||
WARNING_UNMAPPED_CHAR_1,
|
|
||||||
WARNING_UNMAPPED_CHAR_2,
|
|
||||||
// WARNING_USER,
|
|
||||||
META_WARNING_DONE,
|
|
||||||
};
|
|
||||||
|
|
||||||
static uint8_t const *metaWarningCommands[NB_META_WARNINGS] = {
|
|
||||||
_wallCommands,
|
|
||||||
_wextraCommands,
|
|
||||||
_weverythingCommands,
|
|
||||||
};
|
|
||||||
|
|
||||||
void processWarningFlag(char const *flag) {
|
void processWarningFlag(char const *flag) {
|
||||||
static bool setError = false;
|
std::string rootFlag = flag;
|
||||||
|
|
||||||
// First, try to match against a "meta" warning
|
// Check for `-Werror` or `-Wno-error` to return early
|
||||||
for (WarningID id : EnumSeq(META_WARNINGS_START, NB_WARNINGS)) {
|
if (rootFlag == "error") {
|
||||||
// TODO: improve the matching performance?
|
// `-Werror` promotes warnings to errors
|
||||||
if (!strcmp(flag, warningFlags[id])) {
|
warningsAreErrors = true;
|
||||||
// We got a match!
|
return;
|
||||||
if (setError)
|
} else if (rootFlag == "no-error") {
|
||||||
errx("Cannot make meta warning \"%s\" into an error", flag);
|
// `-Wno-error` disables promotion of warnings to errors
|
||||||
|
warningsAreErrors = false;
|
||||||
for (uint8_t const *ptr = metaWarningCommands[id - META_WARNINGS_START];
|
return;
|
||||||
*ptr != META_WARNING_DONE;
|
|
||||||
ptr++) {
|
|
||||||
// Warning flag, set without override
|
|
||||||
if (warningStates[*ptr] == WARNING_DEFAULT)
|
|
||||||
warningStates[*ptr] = WARNING_ENABLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's not a meta warning, specially check against `-Werror`
|
// Check for prefixes that affect what the flag does
|
||||||
if (!strncmp(flag, "error", QUOTEDSTRLEN("error"))) {
|
WarningState state;
|
||||||
char const *errorFlag = flag + QUOTEDSTRLEN("error");
|
if (rootFlag.starts_with("error=")) {
|
||||||
|
// `-Werror=<flag>` enables the flag as an error
|
||||||
switch (*errorFlag) {
|
state = {.state = WARNING_ENABLED, .error = WARNING_ENABLED};
|
||||||
case '\0':
|
rootFlag.erase(0, literal_strlen("error="));
|
||||||
// `-Werror`
|
} else if (rootFlag.starts_with("no-error=")) {
|
||||||
warningsAreErrors = true;
|
// `-Wno-error=<flag>` prevents the flag from being an error,
|
||||||
return;
|
// without affecting whether it is enabled
|
||||||
|
state = {.state = WARNING_DEFAULT, .error = WARNING_DISABLED};
|
||||||
case '=':
|
rootFlag.erase(0, literal_strlen("no-error="));
|
||||||
// `-Werror=XXX`
|
} else if (rootFlag.starts_with("no-")) {
|
||||||
setError = true;
|
// `-Wno-<flag>` disables the flag
|
||||||
processWarningFlag(errorFlag + 1); // Skip the `=`
|
state = {.state = WARNING_DISABLED, .error = WARNING_DEFAULT};
|
||||||
setError = false;
|
rootFlag.erase(0, literal_strlen("no-"));
|
||||||
return;
|
} else {
|
||||||
|
// `-W<flag>` enables the flag
|
||||||
// Otherwise, allow parsing as another flag
|
state = {.state = WARNING_ENABLED, .error = WARNING_DEFAULT};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Well, it's either a normal warning or a mistake
|
// Check for an `=` parameter to process as a parametric warning
|
||||||
|
// `-Wno-<flag>` and `-Wno-error=<flag>` negation cannot have an `=` parameter, but without a
|
||||||
WarningState state = setError ? WARNING_ERROR
|
// parameter, the 0 value will apply to all levels of a parametric warning
|
||||||
// Not an error, then check if this is a negation
|
uint8_t param = 0;
|
||||||
: strncmp(flag, "no-", QUOTEDSTRLEN("no-")) ? WARNING_ENABLED
|
bool hasParam = false;
|
||||||
: WARNING_DISABLED;
|
if (state.state == WARNING_ENABLED) {
|
||||||
char const *rootFlag = state == WARNING_DISABLED ? flag + QUOTEDSTRLEN("no-") : flag;
|
|
||||||
|
|
||||||
// Is this a "parametric" warning?
|
|
||||||
if (state != WARNING_DISABLED) { // The `no-` form cannot be parametrized
|
|
||||||
// First, check if there is an "equals" sign followed by a decimal number
|
// First, check if there is an "equals" sign followed by a decimal number
|
||||||
char const *equals = strchr(rootFlag, '=');
|
// Ignore an equal sign at the very end of the string
|
||||||
|
if (auto equals = rootFlag.find('=');
|
||||||
|
equals != rootFlag.npos && equals != rootFlag.size() - 1) {
|
||||||
|
hasParam = true;
|
||||||
|
|
||||||
if (equals && equals[1] != '\0') { // Ignore an equal sign at the very end as well
|
|
||||||
// Is the rest of the string a decimal number?
|
// Is the rest of the string a decimal number?
|
||||||
// We want to avoid `strtoul`'s whitespace and sign, so we parse manually
|
// We want to avoid `strtoul`'s whitespace and sign, so we parse manually
|
||||||
uint8_t param = 0;
|
char const *ptr = rootFlag.c_str() + equals + 1;
|
||||||
char const *ptr = equals + 1;
|
|
||||||
bool warned = false;
|
bool warned = false;
|
||||||
|
|
||||||
// The `if`'s condition above ensures that this will run at least once
|
// The `if`'s condition above ensures that this will run at least once
|
||||||
do {
|
do {
|
||||||
// If we don't have a digit, bail
|
// If we don't have a digit, bail
|
||||||
if (*ptr < '0' || *ptr > '9')
|
if (*ptr < '0' || *ptr > '9') {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
// Avoid overflowing!
|
// Avoid overflowing!
|
||||||
if (param > UINT8_MAX - (*ptr - '0')) {
|
if (param > UINT8_MAX - (*ptr - '0')) {
|
||||||
if (!warned)
|
if (!warned) {
|
||||||
warnx("Invalid warning flag \"%s\": capping parameter at 255\n", flag);
|
warnx("Invalid warning flag \"%s\": capping parameter at 255", flag);
|
||||||
|
}
|
||||||
warned = true; // Only warn once, cap always
|
warned = true; // Only warn once, cap always
|
||||||
param = 255;
|
param = 255;
|
||||||
continue;
|
continue;
|
||||||
@@ -299,40 +211,88 @@ void processWarningFlag(char const *flag) {
|
|||||||
ptr++;
|
ptr++;
|
||||||
} while (*ptr);
|
} while (*ptr);
|
||||||
|
|
||||||
// If we managed to the end of the string, check that the warning indeed
|
// If we reached the end of the string, truncate it at the '='
|
||||||
// accepts a parameter
|
|
||||||
if (*ptr == '\0') {
|
if (*ptr == '\0') {
|
||||||
if (setError && param == 0) {
|
rootFlag.resize(equals);
|
||||||
warnx("Ignoring nonsensical warning flag \"%s\"\n", flag);
|
// `-W<flag>=0` is equivalent to `-Wno-<flag>`
|
||||||
return;
|
if (param == 0) {
|
||||||
|
state.state = WARNING_DISABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string truncFlag = rootFlag;
|
|
||||||
|
|
||||||
truncFlag.resize(equals - rootFlag); // Truncate the param at the '='
|
|
||||||
if (tryProcessParamWarning(
|
|
||||||
truncFlag.c_str(), param, param == 0 ? WARNING_DISABLED : state
|
|
||||||
))
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to match the flag against a "normal" flag
|
// Try to match the flag against a parametric warning
|
||||||
for (WarningID id : EnumSeq(NB_PLAIN_WARNINGS)) {
|
// If there was an equals sign, it will have set `param`; if not, `param` will be 0, which
|
||||||
if (!strcmp(rootFlag, warningFlags[id])) {
|
// applies to all levels
|
||||||
// We got a match!
|
for (auto const ¶mWarning : paramWarnings) {
|
||||||
warningStates[id] = state;
|
WarningID baseID = paramWarning.firstID;
|
||||||
|
uint8_t maxParam = paramWarning.lastID - baseID + 1;
|
||||||
|
assume(paramWarning.defaultLevel <= maxParam);
|
||||||
|
|
||||||
|
if (rootFlag == warningFlags[baseID].name) { // Match!
|
||||||
|
if (rootFlag == "numeric-string") {
|
||||||
|
warning(WARNING_OBSOLETE, "Warning flag \"numeric-string\" is deprecated\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If making the warning an error but param is 0, set to the maximum
|
||||||
|
// This accommodates `-Werror=<flag>`, but also `-Werror=<flag>=0`, which is
|
||||||
|
// thus filtered out by the caller.
|
||||||
|
// A param of 0 makes sense for disabling everything, but neither for
|
||||||
|
// enabling nor "erroring". Use the default for those.
|
||||||
|
if (param == 0) {
|
||||||
|
param = paramWarning.defaultLevel;
|
||||||
|
} else if (param > maxParam) {
|
||||||
|
if (param != 255) { // Don't warn if already capped
|
||||||
|
warnx(
|
||||||
|
"Invalid parameter %" PRIu8
|
||||||
|
" for warning flag \"%s\"; capping at maximum %" PRIu8,
|
||||||
|
param,
|
||||||
|
rootFlag.c_str(),
|
||||||
|
maxParam
|
||||||
|
);
|
||||||
|
}
|
||||||
|
param = maxParam;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the first <param> to enabled/error, and disable the rest
|
||||||
|
for (uint8_t ofs = 0; ofs < maxParam; ofs++) {
|
||||||
|
WarningState &warning = warningStates.flagStates[baseID + ofs];
|
||||||
|
if (ofs < param) {
|
||||||
|
warning.update(state);
|
||||||
|
} else {
|
||||||
|
warning.state = WARNING_DISABLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lastly, this might be a "parametric" warning without an equals sign
|
// Try to match against a non-parametric warning, unless there was an equals sign
|
||||||
// If it is, treat the param as 1 if enabling, or 0 if disabling
|
if (!hasParam) {
|
||||||
if (tryProcessParamWarning(rootFlag, 0, state))
|
// Try to match against a "meta" warning
|
||||||
return;
|
for (WarningFlag const &metaWarning : metaWarnings) {
|
||||||
|
if (rootFlag == metaWarning.name) {
|
||||||
|
// Set each of the warning flags that meets this level
|
||||||
|
for (WarningID id : EnumSeq(NB_WARNINGS)) {
|
||||||
|
if (metaWarning.level >= warningFlags[id].level) {
|
||||||
|
warningStates.metaStates[id].update(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
warnx("Unknown warning `%s`", flag);
|
// Try to match the flag against a "normal" flag
|
||||||
|
for (WarningID id : EnumSeq(NB_PLAIN_WARNINGS)) {
|
||||||
|
if (rootFlag == warningFlags[id].name) {
|
||||||
|
warningStates.flagStates[id].update(state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
warnx("Unknown warning flag \"%s\"", flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
void printDiag(
|
void printDiag(
|
||||||
@@ -356,16 +316,18 @@ void error(char const *fmt, ...) {
|
|||||||
|
|
||||||
// This intentionally makes 0 act as "unlimited" (or at least "limited to sizeof(unsigned)")
|
// This intentionally makes 0 act as "unlimited" (or at least "limited to sizeof(unsigned)")
|
||||||
nbErrors++;
|
nbErrors++;
|
||||||
if (nbErrors == maxErrors)
|
if (nbErrors == maxErrors) {
|
||||||
errx(
|
errx(
|
||||||
"The maximum of %u error%s was reached (configure with \"-X/--max-errors\"); assembly "
|
"The maximum of %u error%s was reached (configure with \"-X/--max-errors\"); assembly "
|
||||||
"aborted!",
|
"aborted!",
|
||||||
maxErrors,
|
maxErrors,
|
||||||
maxErrors == 1 ? "" : "s"
|
maxErrors == 1 ? "" : "s"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]] void fatalerror(char const *fmt, ...) {
|
[[noreturn]]
|
||||||
|
void fatalerror(char const *fmt, ...) {
|
||||||
va_list args;
|
va_list args;
|
||||||
|
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
@@ -376,26 +338,22 @@ void error(char const *fmt, ...) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void warning(WarningID id, char const *fmt, ...) {
|
void warning(WarningID id, char const *fmt, ...) {
|
||||||
char const *flag = warningFlags[id];
|
char const *flag = warningFlags[id].name;
|
||||||
va_list args;
|
va_list args;
|
||||||
|
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
|
|
||||||
switch (warningState(id)) {
|
switch (getWarningBehavior(id)) {
|
||||||
case WARNING_DISABLED:
|
case WarningBehavior::DISABLED:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WARNING_ENABLED:
|
case WarningBehavior::ENABLED:
|
||||||
printDiag(fmt, args, "warning", ": [-W%s]", flag);
|
printDiag(fmt, args, "warning", ": [-W%s]", flag);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WARNING_ERROR:
|
case WarningBehavior::ERROR:
|
||||||
printDiag(fmt, args, "error", ": [-Werror=%s]", flag);
|
printDiag(fmt, args, "error", ": [-Werror=%s]", flag);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WARNING_DEFAULT:
|
|
||||||
unreachable_();
|
|
||||||
// Not reached
|
|
||||||
}
|
}
|
||||||
|
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|||||||
@@ -15,14 +15,17 @@ fi
|
|||||||
BISON_FLAGS="-Wall -Dparse.lac=full -Dlr.type=ielr"
|
BISON_FLAGS="-Wall -Dparse.lac=full -Dlr.type=ielr"
|
||||||
|
|
||||||
# Set some optimization flags on versions that support them
|
# Set some optimization flags on versions that support them
|
||||||
if [ "$BISON_MAJOR" -eq 4 ] || [ "$BISON_MAJOR" -eq 3 ] && [ "$BISON_MINOR" -ge 5 ]; then
|
if [ "$BISON_MAJOR" -ge 4 ] || [ "$BISON_MAJOR" -eq 3 ] && [ "$BISON_MINOR" -ge 5 ]; then
|
||||||
BISON_FLAGS="$BISON_FLAGS -Dapi.token.raw=true"
|
BISON_FLAGS="$BISON_FLAGS -Dapi.token.raw=true"
|
||||||
fi
|
fi
|
||||||
if [ "$BISON_MAJOR" -eq 4 ] || [ "$BISON_MAJOR" -eq 3 ] && [ "$BISON_MINOR" -ge 6 ]; then
|
if [ "$BISON_MAJOR" -ge 4 ] || [ "$BISON_MAJOR" -eq 3 ] && [ "$BISON_MINOR" -ge 6 ]; then
|
||||||
BISON_FLAGS="$BISON_FLAGS -Dparse.error=detailed"
|
BISON_FLAGS="$BISON_FLAGS -Dparse.error=detailed"
|
||||||
else
|
else
|
||||||
BISON_FLAGS="$BISON_FLAGS -Dparse.error=verbose"
|
BISON_FLAGS="$BISON_FLAGS -Dparse.error=verbose"
|
||||||
fi
|
fi
|
||||||
|
if [ "$BISON_MAJOR" -ge 4 ] || [ "$BISON_MAJOR" -eq 3 ] && [ "$BISON_MINOR" -ge 7 ]; then
|
||||||
|
BISON_FLAGS="$BISON_FLAGS -Wcounterexamples"
|
||||||
|
fi
|
||||||
|
|
||||||
# Replace the arguments to this script ($@) with the ones in $BISON_FLAGS
|
# Replace the arguments to this script ($@) with the ones in $BISON_FLAGS
|
||||||
eval "set -- $BISON_FLAGS"
|
eval "set -- $BISON_FLAGS"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#include "error.hpp"
|
#include "error.hpp"
|
||||||
|
|
||||||
@@ -22,7 +22,8 @@ static void vwarnx(char const *fmt, va_list ap) {
|
|||||||
putc('\n', stderr);
|
putc('\n', stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]] static void verr(char const *fmt, va_list ap) {
|
[[noreturn]]
|
||||||
|
static void verr(char const *fmt, va_list ap) {
|
||||||
char const *error = strerror(errno);
|
char const *error = strerror(errno);
|
||||||
|
|
||||||
fprintf(stderr, "error: ");
|
fprintf(stderr, "error: ");
|
||||||
@@ -32,7 +33,8 @@ static void vwarnx(char const *fmt, va_list ap) {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]] static void verrx(char const *fmt, va_list ap) {
|
[[noreturn]]
|
||||||
|
static void verrx(char const *fmt, va_list ap) {
|
||||||
fprintf(stderr, "error: ");
|
fprintf(stderr, "error: ");
|
||||||
vfprintf(stderr, fmt, ap);
|
vfprintf(stderr, fmt, ap);
|
||||||
putc('\n', stderr);
|
putc('\n', stderr);
|
||||||
@@ -56,14 +58,16 @@ void warnx(char const *fmt, ...) {
|
|||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]] void err(char const *fmt, ...) {
|
[[noreturn]]
|
||||||
|
void err(char const *fmt, ...) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
verr(fmt, ap);
|
verr(fmt, ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]] void errx(char const *fmt, ...) {
|
[[noreturn]]
|
||||||
|
void errx(char const *fmt, ...) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user