mirror of
https://github.com/gbdev/rgbds.git
synced 2026-08-25 08:14:31 +00:00
Compare commits
331
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b1606c63c | ||
|
|
fa49e238a9 | ||
|
|
3426f685a0 | ||
|
|
c0e2f8340b | ||
|
|
8883251bf9 | ||
|
|
f27a8132d9 | ||
|
|
0af28de791 | ||
|
|
8d2275dc48 | ||
|
|
7e11ab4d8f | ||
|
|
e84bcf83b4 | ||
|
|
57c9bab3a5 | ||
|
|
4ada3e85d8 | ||
|
|
ba4a7f2e6a | ||
|
|
b0948566bb | ||
|
|
5aa7067876 | ||
|
|
dff176791c | ||
|
|
07f5969005 | ||
|
|
d61c0561c1 | ||
|
|
b13b0c8eb8 | ||
|
|
ca2de0f429 | ||
|
|
a821badc3e | ||
|
|
cb34fe18f7 | ||
|
|
1ed22cc518 | ||
|
|
9a15b723f2 | ||
|
|
7e2a3491a5 | ||
|
|
e7855ea1b8 | ||
|
|
19e60d9864 | ||
|
|
c0ff3d92ad | ||
|
|
e4583e7ac2 | ||
|
|
5869e0dc60 | ||
|
|
196d50c1d7 | ||
|
|
8b951bc047 | ||
|
|
59b6b15106 | ||
|
|
304cbb464f | ||
|
|
ea3a8ee171 | ||
|
|
f0419eb183 | ||
|
|
4a6ea35299 | ||
|
|
08354cf573 | ||
|
|
417b5212d4 | ||
|
|
66312f55dd | ||
|
|
21eaa2d2cc | ||
|
|
e9fa1e4e94 | ||
|
|
8ab6190f96 | ||
|
|
22bd8a9bd5 | ||
|
|
96bc58b7f7 | ||
|
|
4857ad1e00 | ||
|
|
3b33c942ae | ||
|
|
5525dd902b | ||
|
|
4350a3252d | ||
|
|
a8442bccc7 | ||
|
|
00006a9d00 | ||
|
|
a383bec96e | ||
|
|
226031c18b | ||
|
|
24a896f60a | ||
|
|
a6f5e3efd1 | ||
|
|
c02b234bb1 | ||
|
|
be4c1161bd | ||
|
|
0e1e5e80f6 | ||
|
|
118f1f7543 | ||
|
|
eaa35ed26d | ||
|
|
7b39bcd412 | ||
|
|
255cd7f7f6 | ||
|
|
b8011947a8 | ||
|
|
39e7e064c9 | ||
|
|
7b97d7ce15 | ||
|
|
c667bac93a | ||
|
|
db96144d49 | ||
|
|
276a1a7952 | ||
|
|
b219e03b38 | ||
|
|
824ebd80f9 | ||
|
|
5aedf31b46 | ||
|
|
d94d0bcc2f | ||
|
|
ed8e3e4d02 | ||
|
|
30fd4514dc | ||
|
|
08e83601f5 | ||
|
|
e606f4e997 | ||
|
|
9008d8391c | ||
|
|
21682e8814 | ||
|
|
f82b0838a1 | ||
|
|
dabd0a08d2 | ||
|
|
c3e10dbb03 | ||
|
|
3883c2a2b8 | ||
|
|
cd0caaaceb | ||
|
|
1e4a060d9e | ||
|
|
fe3b238dea | ||
|
|
46a6966b70 | ||
|
|
be3fc61859 | ||
|
|
0adbb92e50 | ||
|
|
09da4e001a | ||
|
|
3eb8c7e8f2 | ||
|
|
189c679e13 | ||
|
|
5d5000011a | ||
|
|
f81f5fe63c | ||
|
|
6e643406a2 | ||
|
|
de36a11be7 | ||
|
|
799c55b4dd | ||
|
|
ddd8f37508 | ||
|
|
c55dec254d | ||
|
|
0996a2f5ed | ||
|
|
e3c594c250 | ||
|
|
daba94b495 | ||
|
|
2f604bfa9a | ||
|
|
9bbb2247a7 | ||
|
|
9b4df11473 | ||
|
|
4a77c42ed7 | ||
|
|
b12eac356e | ||
|
|
82702d8149 | ||
|
|
1f9cc9651a | ||
|
|
04b9c7fa3d | ||
|
|
5e8a6cde20 | ||
|
|
d59081a918 | ||
|
|
250a94c588 | ||
|
|
37818a1847 | ||
|
|
0e3d1df129 | ||
|
|
586c3073f9 | ||
|
|
e3fc46f933 | ||
|
|
3941a28661 | ||
|
|
a829e6c067 | ||
|
|
b252877b05 | ||
|
|
22a3625144 | ||
|
|
6cba5518e9 | ||
|
|
caf561457d | ||
|
|
fc7d9ad573 | ||
|
|
0b91bf654b | ||
|
|
6b2b6e6000 | ||
|
|
f72a4d53e2 | ||
|
|
998f636495 | ||
|
|
075f132d77 | ||
|
|
31e1d2ec87 | ||
|
|
673c62414f | ||
|
|
007672f080 | ||
|
|
7eaca1577d | ||
|
|
996469ee28 | ||
|
|
55db252a8f | ||
|
|
cfa0adf295 | ||
|
|
ed19806434 | ||
|
|
a0d96a0856 | ||
|
|
4b992bfea5 | ||
|
|
141a7fe22b | ||
|
|
43ffc89bb4 | ||
|
|
72a410c007 | ||
|
|
090768e2c9 | ||
|
|
5f2ee530b8 | ||
|
|
4a173130b9 | ||
|
|
ef2f021892 | ||
|
|
dce14fd4b8 | ||
|
|
728bed39d5 | ||
|
|
48fcd9a0ca | ||
|
|
1871b0a0b8 | ||
|
|
d56dbbb4bf | ||
|
|
728d14879b | ||
|
|
0ed8c2f8ee | ||
|
|
d5186b088b | ||
|
|
fee177d0b9 | ||
|
|
42e3da837c | ||
|
|
358e8fe636 | ||
|
|
c50a849757 | ||
|
|
57111b219a | ||
|
|
be2d028ae6 | ||
|
|
1af8bdda16 | ||
|
|
fa22b78e7d | ||
|
|
f0161b41c8 | ||
|
|
ca23210f18 | ||
|
|
85e044f5be | ||
|
|
60e76b2da4 | ||
|
|
3c7488c131 | ||
|
|
9fd0c0297f | ||
|
|
c1c7e64249 | ||
|
|
a18b2f1049 | ||
|
|
3f144b7713 | ||
|
|
eeb3a73210 | ||
|
|
37d39f91f6 | ||
|
|
d5ce5329ea | ||
|
|
6bcd79b997 | ||
|
|
decc5f7153 | ||
|
|
a3c953ea46 | ||
|
|
d43049c84d | ||
|
|
b6ff54acc3 | ||
|
|
71fc449452 | ||
|
|
59dd1fbbf8 | ||
|
|
2502d130eb | ||
|
|
a78a268cb4 | ||
|
|
0553491107 | ||
|
|
6edac50cc4 | ||
|
|
77dceaf43a | ||
|
|
cfec017fed | ||
|
|
12186fdccc | ||
|
|
71dfab3365 | ||
|
|
20b11039c9 | ||
|
|
eb9e9c0f33 | ||
|
|
ac2a022124 | ||
|
|
1249c4ef58 | ||
|
|
aeedfb35a6 | ||
|
|
63609871f4 | ||
|
|
393eaa9dc1 | ||
|
|
b3c3981a90 | ||
|
|
185a5c5369 | ||
|
|
4ea15ed85a | ||
|
|
b218fd65a0 | ||
|
|
81edc27b6e | ||
|
|
dc52487d31 | ||
|
|
6b994b1737 | ||
|
|
0849eb801e | ||
|
|
e6916360f4 | ||
|
|
a802bcb320 | ||
|
|
846aa975f1 | ||
|
|
b65649ca73 | ||
|
|
62ddabb570 | ||
|
|
d48d6fe5d9 | ||
|
|
ac0f93ffda | ||
|
|
a1712cb6d9 | ||
|
|
2968bee86f | ||
|
|
dec7a74166 | ||
|
|
9c4e53d1c4 | ||
|
|
463edc3c01 | ||
|
|
73941c3249 | ||
|
|
7596ae67b5 | ||
|
|
fc08ab6aee | ||
|
|
c7663423d6 | ||
|
|
1ff73cd5e0 | ||
|
|
65ec1af1e8 | ||
|
|
bc3572ec37 | ||
|
|
78281a4aaa | ||
|
|
6e51ab6f55 | ||
|
|
52aaf7e3b3 | ||
|
|
09db4f25dd | ||
|
|
fed5db7743 | ||
|
|
c6dbec7830 | ||
|
|
017b0119e6 | ||
|
|
0e6e91d048 | ||
|
|
df0b557638 | ||
|
|
123201ecc6 | ||
|
|
422e8eec20 | ||
|
|
49a4da2235 | ||
|
|
4577ff16bc | ||
|
|
b430d5a8b8 | ||
|
|
a5be1d886e | ||
|
|
c3b47abcc8 | ||
|
|
2cfb2b2533 | ||
|
|
187e1a55b8 | ||
|
|
143d503c6c | ||
|
|
6e13779b8b | ||
|
|
ace2f3e613 | ||
|
|
8d1a60d6b5 | ||
|
|
11ac22d8f5 | ||
|
|
812e1f6d4b | ||
|
|
8dc052475b | ||
|
|
133eaa4e92 | ||
|
|
57f1894fbd | ||
|
|
021fe01eb0 | ||
|
|
9b4b4a581b | ||
|
|
bcff14b596 | ||
|
|
11f6278d95 | ||
|
|
ede9405daf | ||
|
|
25bf0e9e2c | ||
|
|
167a7ee80c | ||
|
|
4ce4162da4 | ||
|
|
18b074711e | ||
|
|
44b6a2c453 | ||
|
|
3fc3130364 | ||
|
|
5090395ca7 | ||
|
|
efd4373a56 | ||
|
|
92a31ff7df | ||
|
|
8025c2ae66 | ||
|
|
f7831ce8da | ||
|
|
b736fa22ff | ||
|
|
0692d1d28c | ||
|
|
4b69d5cc8a | ||
|
|
52aa518e20 | ||
|
|
919a5dba89 | ||
|
|
d9932b9031 | ||
|
|
66e521e9e5 | ||
|
|
b8e425b41d | ||
|
|
6b0658fb59 | ||
|
|
91fdfcd179 | ||
|
|
5abebcf61f | ||
|
|
145473d9bd | ||
|
|
2fb64816a9 | ||
|
|
7cf330e3f9 | ||
|
|
2e66515096 | ||
|
|
c93f377bfb | ||
|
|
d6d1844d60 | ||
|
|
fd38a3dbc4 | ||
|
|
60d58723aa | ||
|
|
a9b6cda77b | ||
|
|
091452d591 | ||
|
|
df1d909853 | ||
|
|
fb4e08ce71 | ||
|
|
7244b9d17e | ||
|
|
5c4ca36c5a | ||
|
|
292c297518 | ||
|
|
a5bb431d47 | ||
|
|
10e1c9d187 | ||
|
|
d8722b6705 | ||
|
|
2d0ac7eeb0 | ||
|
|
d5033a2bd9 | ||
|
|
45fff0fd58 | ||
|
|
c963c7b1c7 | ||
|
|
63c7835e71 | ||
|
|
35af3185eb | ||
|
|
db9d9c7653 | ||
|
|
9e5a9107f2 | ||
|
|
275fcf8fcf | ||
|
|
bc37b0ece6 | ||
|
|
e63ce24e48 | ||
|
|
d524884799 | ||
|
|
34e1f8dcc5 | ||
|
|
cd64de9323 | ||
|
|
a01b805e4b | ||
|
|
a139cbc744 | ||
|
|
06e5386d93 | ||
|
|
37f57a752f | ||
|
|
670bdf2095 | ||
|
|
6270f654a1 | ||
|
|
676db46c01 | ||
|
|
8979b54281 | ||
|
|
a6f03530ed | ||
|
|
b56b56a695 | ||
|
|
dc5fc9f327 | ||
|
|
3d35457eeb | ||
|
|
aea39ec8c8 | ||
|
|
9293068370 | ||
|
|
d054f07703 | ||
|
|
65b0b49720 | ||
|
|
48d58ba095 | ||
|
|
af0505b4cd | ||
|
|
7c1e1fe515 | ||
|
|
d053025070 | ||
|
|
ade898d815 | ||
|
|
e738b90c69 | ||
|
|
a91d26192d |
+10
-1
@@ -1,5 +1,6 @@
|
||||
[*]
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
indent_size = tab
|
||||
tab_width = 4
|
||||
@@ -7,3 +8,11 @@ charset = utf-8
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
end_of_line = lf
|
||||
|
||||
[{CMakeLists.txt,*.cmake}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.yml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# This file contains a list of commits that are probably not what you
|
||||
# are looking for in a `git blame`, such as mass reformatting or renaming.
|
||||
#
|
||||
# You can set this file as a default ignore file for `git blame` by running
|
||||
# the following command:
|
||||
#
|
||||
# $ git config blame.ignoreRevsFile .git-blame-ignore-revs
|
||||
|
||||
# 2024-03-04: Run `clang-format` on everything
|
||||
e74073e4805fa3cce90a99946fc9994ae2970f9b
|
||||
|
||||
# 2009-06-11: Run `indent' on the whole tree
|
||||
b6c749ffbd30138de7beef13fb9265372a461a96
|
||||
|
||||
# 2009-06-11: Switch line terminators from CRLF to LF
|
||||
94fad97aedcd8bd85c53a505db28390e3f82577b
|
||||
|
||||
# 2009-06-11: Use spaces instead of tabs after CPP directives
|
||||
0d9739535e313b0a2ad19c89b9c85fc7b75237a3
|
||||
@@ -1,6 +1,7 @@
|
||||
# Shell scripts need Unix line endings (see https://github.com/gbdev/rgbds/issues/841)
|
||||
*.sh text eol=lf
|
||||
*.bash text eol=lf
|
||||
*.cfg text eol=lf
|
||||
|
||||
# Flags also need Unix line endings (see https://github.com/gbdev/rgbds/issues/955)
|
||||
*.flags text eol=lf
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
name: Update to RGBDS
|
||||
about: Suggest a feature or report a bug in RGBDS.
|
||||
---
|
||||
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: GBDev Discord
|
||||
url: https://discord.gg/RjJKA8wrD4
|
||||
about: If you have questions about the RGBDS code or its organization, join our Discord server!
|
||||
@@ -0,0 +1,19 @@
|
||||
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "quarterly" # We do not need *immediate* bumps, only frequent enough to avoid falling out of support range.
|
||||
cooldown: # Avoid bumping deps immediately, to give time for security audits to be conducted
|
||||
default-days: 15
|
||||
include: [ "*" ]
|
||||
groups:
|
||||
actions:
|
||||
patterns: [ "*" ]
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
update-types:
|
||||
- "version-update:semver-minor"
|
||||
- "version-update:semver-patch"
|
||||
labels: [ builds ] # Since those are CI-related updates...
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
pngver=1.6.53
|
||||
|
||||
## Grab sources and check them
|
||||
|
||||
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.
|
||||
if [ "$(sha2 -q -256 libpng-$pngver.tar.xz)" != 1d3fb8ccc2932d04aa3663e22ef5ef490244370f4e568d7850165068778d98d4 ]; then
|
||||
sha2 -256 libpng-$pngver.tar.xz
|
||||
echo Checksum mismatch! Aborting. >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
## Extract sources and patch them
|
||||
|
||||
tar -xvf libpng-$pngver.tar.xz
|
||||
|
||||
## Start building!
|
||||
|
||||
mkdir -p build
|
||||
cd build
|
||||
../libpng-$pngver/configure --disable-shared --enable-static \
|
||||
CFLAGS="-O3 -flto -DNDEBUG -mmacosx-version-min=10.9 -arch x86_64 -arch arm64 -fno-exceptions"
|
||||
make -kj
|
||||
make install prefix="$PWD/../libpng-staging"
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
bash --version
|
||||
zsh --version
|
||||
|
||||
find contrib -type f \( -name '*.sh' -o -name '*.bash' \) -exec bash --norc -n {} \;
|
||||
find contrib/zsh_compl -type f -name '_rgb*' -exec zsh -n {} \;
|
||||
@@ -3,17 +3,17 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
declare -A FILES
|
||||
while read -r -d '' file; do
|
||||
FILES["$file"]="true"
|
||||
while IFS= read -r -d '' file; do
|
||||
FILES["$file"]=true
|
||||
done < <(git diff --name-only -z "$1" HEAD)
|
||||
|
||||
edited () {
|
||||
${FILES["$1"]:-"false"}
|
||||
${FILES["$1"]:-false}
|
||||
}
|
||||
|
||||
dependency () {
|
||||
if edited "$1" && ! edited "$2"; then
|
||||
echo "'$1' was modified, but not '$2'! $3" | xargs
|
||||
echo "'$1' was modified, but not '$2'! $3"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -79,15 +79,15 @@ dependency src/gfx/main.cpp contrib/zsh_compl/_rgbgfx \
|
||||
dependency src/gfx/main.cpp contrib/bash_compl/_rgbgfx.bash \
|
||||
"Did the rgbgfx CLI change?"
|
||||
|
||||
dependency test/fetch-test-deps.sh CONTRIBUTING.md \
|
||||
dependency test/external/fetch-repos.sh docs/CONTRIBUTING.md \
|
||||
"Did the test protocol change?"
|
||||
dependency test/run-tests.sh CONTRIBUTING.md \
|
||||
dependency test/run-tests.sh docs/CONTRIBUTING.md \
|
||||
"Did the test protocol change?"
|
||||
dependency test/asm/test.sh CONTRIBUTING.md \
|
||||
dependency test/asm/test.sh docs/CONTRIBUTING.md \
|
||||
"Did the RGBASM test protocol change?"
|
||||
dependency test/link/test.sh CONTRIBUTING.md \
|
||||
dependency test/link/test.sh docs/CONTRIBUTING.md \
|
||||
"Did the RGBLINK test protocol change?"
|
||||
dependency test/fix/test.sh CONTRIBUTING.md \
|
||||
dependency test/fix/test.sh docs/CONTRIBUTING.md \
|
||||
"Did the RGBFIX test protocol change?"
|
||||
dependency test/gfx/test.sh CONTRIBUTING.md \
|
||||
dependency test/gfx/test.sh docs/CONTRIBUTING.md \
|
||||
"Did the RGBGFX test protocol change?"
|
||||
@@ -1,10 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
clang-format --version
|
||||
|
||||
find . -type f \( -iname '*.hpp' -o -iname '*.cpp' \) -exec clang-format -i {} +
|
||||
find . -type f -iname '*.[ch]pp' -exec clang-format -i {} +
|
||||
|
||||
if ! git diff-index --quiet HEAD --; then
|
||||
echo 'Unformatted files:'
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
tag=$1
|
||||
[ $# -eq 1 ]
|
||||
|
||||
flags=(--draft --fail-on-no-commits --verify-tag)
|
||||
if [[ "$tag" = v*-rc* ]]; then
|
||||
flags+=(--prerelease)
|
||||
fi
|
||||
|
||||
files=(
|
||||
win64/rgbds-win64.zip
|
||||
win32/rgbds-win32.zip
|
||||
macos/rgbds-macos.zip
|
||||
linux/rgbds-linux-x86_64.tar.xz
|
||||
rgbds-source.tar.gz
|
||||
)
|
||||
|
||||
set -x
|
||||
gh release create "${flags[@]}" --notes-file - "$tag" "${files[@]}" <<"EOF"
|
||||
Please ensure that the packages below work properly.
|
||||
Once that's done, replace this text with the changelog, un-draft the release, and update the `release` branch.
|
||||
By the way, if you forgot to update `include/version.hpp`, RGBASM's version test is going to fail in the tag's regression testing! (Use `git push --delete origin <tag>` to delete it)
|
||||
EOF
|
||||
@@ -1,23 +0,0 @@
|
||||
function getlibrary ([string] $URI, [string] $filename, [string] $hash, [string] $destdir) {
|
||||
$wc = New-Object Net.WebClient
|
||||
[string] $downloadhash = $null
|
||||
try {
|
||||
$wc.DownloadFile($URI, $filename)
|
||||
$downloadhash = $(Get-FileHash $filename -Algorithm SHA256).Hash
|
||||
} catch {
|
||||
Write-Host "${filename}: failed to download"
|
||||
exit 1
|
||||
}
|
||||
if ($hash -ne $downloadhash) {
|
||||
Write-Host "${filename}: SHA256 mismatch ($downloadhash)"
|
||||
exit 1
|
||||
}
|
||||
Expand-Archive -DestinationPath $destdir $filename
|
||||
}
|
||||
|
||||
getlibrary 'https://www.zlib.net/zlib131.zip' 'zlib.zip' '72af66d44fcc14c22013b46b814d5d2514673dda3d115e64b690c1ad636e7b17' .
|
||||
getlibrary 'https://github.com/pnggroup/libpng/archive/refs/tags/v1.6.53.zip' 'libpng.zip' '9fb99118ec4523d9a9dab652ce7c2472ec76f6ccd69d1aba3ab873bb8cf84b98' .
|
||||
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 libpng-1.6.53 libpng
|
||||
Executable
+101
@@ -0,0 +1,101 @@
|
||||
#!/bin/sh
|
||||
# This script requires `sh` instead of `bash` because the latter is not always installed on FreeBSD.
|
||||
set -eu
|
||||
|
||||
# Python and the Pillow library are dependencies for libbet, a repo built by our external tests.
|
||||
# Other dependencies are for building rgbds itself.
|
||||
|
||||
case $# in
|
||||
1) OS="$1"; TOOLSET= ;;
|
||||
2) OS="$1"; TOOLSET="$2" ;;
|
||||
*) echo >&2 "Usage: $0 <os> [<toolset>]" && exit 1 ;;
|
||||
esac
|
||||
|
||||
case "${OS%%-*}" in
|
||||
ubuntu|debian)
|
||||
pkgs=bison
|
||||
case "$TOOLSET" in
|
||||
mingw32)
|
||||
pkgs="$pkgs libz-mingw-w64-dev g++-mingw-w64-i686-win32"
|
||||
TOOLSET=
|
||||
;;
|
||||
mingw64)
|
||||
pkgs="$pkgs libz-mingw-w64-dev g++-mingw-w64-x86-64-win32"
|
||||
TOOLSET=
|
||||
;;
|
||||
g++-10 | lcov)
|
||||
pkgs="$pkgs libpng-dev pkgconf python3-pil $TOOLSET"
|
||||
TOOLSET=
|
||||
;;
|
||||
'' | g++ | clang++)
|
||||
pkgs="$pkgs libpng-dev pkgconf python3-pil"
|
||||
TOOLSET=
|
||||
;;
|
||||
esac
|
||||
sudo apt-get update -qq
|
||||
# shellcheck disable=SC2086 # (This word splitting is intentional.)
|
||||
sudo apt-get install -yq $pkgs
|
||||
;;
|
||||
macos)
|
||||
pkgs="bison make pillow"
|
||||
case "$TOOLSET" in
|
||||
lld)
|
||||
pkgs="$pkgs $TOOLSET"
|
||||
TOOLSET=
|
||||
;;
|
||||
clang++)
|
||||
TOOLSET=
|
||||
;;
|
||||
esac
|
||||
# macOS bundles GNU Make 3.81, which doesn't support synced output.
|
||||
# We leave it as the default in `PATH`, to test that our Makefile works with it.
|
||||
# However, CMake automatically uses Homebrew's `gmake`, so our CI has synced output.
|
||||
# shellcheck disable=SC2086 # (This word splitting is intentional.)
|
||||
brew install $pkgs
|
||||
# Export `bison` to allow using the version we install from Homebrew,
|
||||
# instead of the outdated one preinstalled on macOS (which doesn't even support `-Wall`...).
|
||||
export PATH="$(brew --prefix)/opt/bison/bin:$PATH"
|
||||
printf 'PATH=%s\n' "$PATH" >>"$GITHUB_ENV" # Make it available to later CI steps too.
|
||||
;;
|
||||
freebsd)
|
||||
pkg install -y bash bison cmake git png
|
||||
;;
|
||||
windows)
|
||||
# GitHub Actions' hosted runners ship CMake 3.x, but versions prior to 4.0.0 ignore `CPACK_PACKAGE_FILE_NAME`.
|
||||
choco install -y winflexbison3 cmake
|
||||
# The version-printing code below will invoke `bison`, not the Windows-specific name `win_bison`.
|
||||
bison() { win_bison "$@"; } # An alias doesn't work, so we use a function instead.
|
||||
py -3 -m pip install pillow
|
||||
;;
|
||||
windowsmingw)
|
||||
py -3 -m pip install pillow
|
||||
;;
|
||||
*)
|
||||
echo "Cannot install deps for OS '$1'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -n "$TOOLSET" ]; then
|
||||
printf >&2 'Unknown toolset `%s` for OS `%s`\n' "$TOOLSET" "$OS"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# We do not build RGBDS *in* MinGW on Windows, so skip printing build tool info.
|
||||
if [ "$OS" = "windowsmingw" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Print some system info, for easier debugging.
|
||||
# https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-commands#grouping-log-lines
|
||||
|
||||
echo ::group::PATH
|
||||
echo "PATH=($PATH)" | sed 's/:/\n /g'
|
||||
echo ::endgroup::
|
||||
|
||||
for prog in bison make cmake; do
|
||||
printf '::group::' # No line terminator, the next command's first line becomes the group's title.
|
||||
$prog --version
|
||||
type $prog
|
||||
echo ::endgroup::
|
||||
done
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
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 -m 644 rgbasm.1 rgblink.1 rgbfix.1 rgbgfx.1 /usr/local/share/man/man1/
|
||||
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/
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
case "${1%-*}" in
|
||||
ubuntu)
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get install -yq bison libpng-dev pkg-config
|
||||
;;
|
||||
macos)
|
||||
brew install bison sha2 md5sha1sum
|
||||
# Export `bison` to allow using the version we install from Homebrew,
|
||||
# instead of the outdated one preinstalled on macOS (which doesn't even support `-Wall`...)
|
||||
export PATH="/opt/homebrew/opt/bison/bin:$PATH"
|
||||
printf 'PATH=%s\n' "$PATH" >>"$GITHUB_ENV" # Make it available to later CI steps too
|
||||
;;
|
||||
*)
|
||||
echo "WARNING: Cannot install deps for OS '$1'"
|
||||
;;
|
||||
esac
|
||||
|
||||
bison --version
|
||||
make --version
|
||||
cmake --version
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
pngver=1.6.53
|
||||
arch="$1"
|
||||
|
||||
## Grab sources and check them
|
||||
|
||||
wget http://downloads.sourceforge.net/project/libpng/libpng16/$pngver/libpng-$pngver.tar.xz
|
||||
echo 1d3fb8ccc2932d04aa3663e22ef5ef490244370f4e568d7850165068778d98d4 libpng-$pngver.tar.xz | sha256sum -c -
|
||||
|
||||
## Extract sources and patch them
|
||||
|
||||
tar -xf libpng-$pngver.tar.xz
|
||||
|
||||
## Start building!
|
||||
|
||||
mkdir -p build
|
||||
cd build
|
||||
../libpng-$pngver/configure \
|
||||
--host="$arch" --target="$arch" \
|
||||
--prefix="/usr/$arch" \
|
||||
--enable-shared --disable-static \
|
||||
CPPFLAGS="-D_FORTIFY_SOURCE=2" \
|
||||
CFLAGS="-O2 -pipe -fno-plt -fno-exceptions --param=ssp-buffer-size=4" \
|
||||
LDFLAGS="-Wl,-O1,--sort-common,--as-needed -fstack-protector"
|
||||
make -kj
|
||||
sudo make install
|
||||
@@ -0,0 +1,4 @@
|
||||
@rem From https://github.com/microsoft/vswhere/wiki/Start-Developer-Command-Prompt#using-batch
|
||||
for /f "delims=" %%p in ('vswhere.exe -prerelease -latest -property installationPath') do set vspath=%%p
|
||||
@rem From https://learn.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=msvc-170#developer_command_file_locations
|
||||
call "%vspath%\VC\Auxiliary\Build\vcvars%1.bat"
|
||||
@@ -1,18 +0,0 @@
|
||||
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=
|
||||
@@ -6,6 +6,12 @@ on:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
# Force colored output (see https://bixense.com/clicolors/ and https://force-color.org/)
|
||||
TERM: xterm-256color
|
||||
CLICOLOR: 1
|
||||
CLICOLOR_FORCE: 1
|
||||
|
||||
jobs:
|
||||
publish-docker-image:
|
||||
if: github.repository_owner == 'gbdev'
|
||||
@@ -14,10 +20,10 @@ jobs:
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
@@ -43,7 +49,7 @@ jobs:
|
||||
|
||||
- name: Delete untagged container images
|
||||
if: github.repository_owner == 'gbdev'
|
||||
uses: Chizkiyahu/delete-untagged-ghcr-action@v5
|
||||
uses: Chizkiyahu/delete-untagged-ghcr-action@v6
|
||||
with:
|
||||
# Requires a personal access token with delete:packages permissions
|
||||
token: ${{ secrets.PAT_TOKEN }}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
name: Contrib script syntax checking
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
env:
|
||||
# Force colored output (see https://bixense.com/clicolors/ and https://force-color.org/)
|
||||
TERM: xterm-256color
|
||||
CLICOLOR: 1
|
||||
CLICOLOR_FORCE: 1
|
||||
|
||||
jobs:
|
||||
check-contrib:
|
||||
runs-on: ubuntu-slim
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v7
|
||||
- name: Install zsh
|
||||
run: |
|
||||
sudo apt install -Uyq zsh
|
||||
- name: Check contrib scripts
|
||||
run: |
|
||||
.github/scripts/check-contrib.sh
|
||||
@@ -0,0 +1,27 @@
|
||||
name: Diff completeness check
|
||||
on: pull_request
|
||||
|
||||
env:
|
||||
# Force colored output (see https://bixense.com/clicolors/ and https://force-color.org/)
|
||||
TERM: xterm-256color
|
||||
CLICOLOR: 1
|
||||
CLICOLOR_FORCE: 1
|
||||
|
||||
jobs:
|
||||
check-diff:
|
||||
runs-on: ubuntu-slim
|
||||
steps:
|
||||
- name: Clone repo
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Check diff
|
||||
shell: bash # Bash is the default, but specifying it explicitly enables `-o pipefail`.
|
||||
run: |
|
||||
MERGE_BASE=$(gh api "/repos/{owner}/{repo}/compare/$BASE_SHA...$HEAD_SHA" --jq '.merge_base_commit.sha')
|
||||
git fetch origin "$MERGE_BASE"
|
||||
.github/scripts/check-diff.bash "$MERGE_BASE" | sed -E 's/^/::warning::/'
|
||||
env:
|
||||
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
@@ -0,0 +1,21 @@
|
||||
name: Code format checking
|
||||
on: pull_request
|
||||
|
||||
env:
|
||||
# Force colored output (see https://bixense.com/clicolors/ and https://force-color.org/)
|
||||
TERM: xterm-256color
|
||||
CLICOLOR: 1
|
||||
CLICOLOR_FORCE: 1
|
||||
|
||||
jobs:
|
||||
check-format:
|
||||
runs-on: ubuntu-slim
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v7
|
||||
- name: Install clang-format
|
||||
run: |
|
||||
sudo apt install -Uyq clang-format
|
||||
- name: Check format
|
||||
run: |
|
||||
.github/scripts/check-format.sh
|
||||
@@ -1,17 +0,0 @@
|
||||
name: Diff completeness check
|
||||
on: pull_request
|
||||
|
||||
jobs:
|
||||
checkdiff:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up repo
|
||||
run: |
|
||||
git clone -b "${{ github.event.pull_request.head.ref }}" "${{ github.event.pull_request.head.repo.clone_url }}" rgbds
|
||||
cd rgbds
|
||||
git remote add upstream "${{ github.event.pull_request.base.repo.clone_url }}"
|
||||
git fetch upstream
|
||||
- name: Check diff
|
||||
working-directory: rgbds
|
||||
run: |
|
||||
make checkdiff "BASE_REF=${{ github.event.pull_request.base.sha }}" Q= | tee log
|
||||
@@ -1,12 +0,0 @@
|
||||
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
|
||||
@@ -3,28 +3,26 @@ on:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
env:
|
||||
# Force colored output (see https://bixense.com/clicolors/ and https://force-color.org/)
|
||||
TERM: xterm-256color
|
||||
CLICOLOR: 1
|
||||
CLICOLOR_FORCE: 1
|
||||
|
||||
jobs:
|
||||
coverage:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v7
|
||||
- name: Install deps
|
||||
shell: bash
|
||||
run: |
|
||||
./.github/scripts/install_deps.sh ubuntu
|
||||
- name: Install LCOV
|
||||
run: |
|
||||
sudo apt-get install lcov
|
||||
- name: Install test dependency dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
test/fetch-test-deps.sh --get-deps ubuntu
|
||||
.github/scripts/install-deps.sh ubuntu-latest lcov
|
||||
- name: Generate coverage report
|
||||
run: |
|
||||
contrib/coverage.bash ubuntu-ci
|
||||
contrib/coverage.bash --os ubuntu-latest --jobs "$(getconf _NPROCESSORS_ONLN)"
|
||||
- name: Upload coverage report
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: coverage-report
|
||||
# Workaround for keeping the top-level coverage/ directory
|
||||
@@ -32,3 +30,4 @@ jobs:
|
||||
path: |
|
||||
coverage
|
||||
dummy-file-to-keep-directory-structure.txt
|
||||
if-no-files-found: error
|
||||
|
||||
@@ -4,162 +4,122 @@ on:
|
||||
tags:
|
||||
- v[0-9]*
|
||||
|
||||
env:
|
||||
# Force colored output (see https://bixense.com/clicolors/ and https://force-color.org/)
|
||||
TERM: xterm-256color
|
||||
CLICOLOR: 1
|
||||
CLICOLOR_FORCE: 1
|
||||
|
||||
CMAKE_COLOR_DIAGNOSTICS: ON # Tells CMake-generated build systems to have colored output.
|
||||
CMAKE_BUILD_TYPE: Release # `cmake -S` now implies `-DCMAKE_BUILD_TYPE=Release`.
|
||||
CMAKE_CONFIG_TYPE: Release # `cmake --build` now implies `--config Release`.
|
||||
|
||||
# We instruct CMake to download and build third-party projects in the same place as in `testing.yml`
|
||||
# to prevent divergence between testing and release workflows.
|
||||
DEPS_ROOT_DIR: ~/_deps
|
||||
|
||||
# We use Ninja as the generator for CMake below because it provides synchronous output by default,
|
||||
# and only shows full command lines when they fail.
|
||||
|
||||
jobs:
|
||||
windows:
|
||||
runs-on: windows-2022
|
||||
strategy:
|
||||
matrix:
|
||||
bits: [32, 64]
|
||||
include:
|
||||
- bits: 32
|
||||
arch: x86
|
||||
platform: Win32
|
||||
- bits: 64
|
||||
arch: x86_x64
|
||||
platform: x64
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Get version from tag
|
||||
shell: bash
|
||||
run: | # Turn "vX.Y.Z" into "X.Y.Z"
|
||||
VERSION="${{ github.ref_name }}"
|
||||
echo "version=${VERSION#v}" >> $GITHUB_ENV
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v7
|
||||
- name: Install deps
|
||||
run: .github/scripts/get_win_deps.ps1
|
||||
- name: Check libraries cache
|
||||
id: cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
zbuild
|
||||
pngbuild
|
||||
key: ${{ matrix.arch }}-${{ hashFiles('zlib/**', 'libpng/**') }}
|
||||
- name: Build zlib
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: | # BUILD_SHARED_LIBS causes the output DLL to be correctly called `zlib1.dll`
|
||||
cmake -S zlib -B zbuild -A ${{ matrix.platform }} -Wno-dev -DCMAKE_INSTALL_PREFIX=install_dir -DBUILD_SHARED_LIBS=ON
|
||||
cmake --build zbuild --config Release -j
|
||||
- name: Install zlib
|
||||
run: |
|
||||
cmake --install zbuild
|
||||
- name: Build libpng
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
cmake -S libpng -B pngbuild -A ${{ matrix.platform }} -Wno-dev -DCMAKE_INSTALL_PREFIX=install_dir -DPNG_SHARED=ON -DPNG_STATIC=OFF -DPNG_TESTS=OFF
|
||||
cmake --build pngbuild --config Release -j
|
||||
- name: Install libpng
|
||||
run: |
|
||||
cmake --install pngbuild
|
||||
bash .github/scripts/install-deps.sh windows
|
||||
- name: Build Windows binaries
|
||||
shell: bash
|
||||
shell: cmd
|
||||
run: |
|
||||
cmake -S . -B build -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build build --config Release -j --verbose
|
||||
cmake --install build --verbose --prefix install_dir --strip
|
||||
call .github\scripts\msvc-env.bat ${{ matrix.bits }}
|
||||
cmake -B build -G Ninja -DFETCHCONTENT_BASE_DIR="${{ env.DEPS_ROOT_DIR }}" -DCMAKE_C_FLAGS="/nologo"
|
||||
cmake --build build -- -k 0
|
||||
- name: Package binaries
|
||||
working-directory: build
|
||||
run: |
|
||||
Compress-Archive -LiteralPath @("install_dir/bin/rgbasm.exe", "install_dir/bin/rgblink.exe", "install_dir/bin/rgbfix.exe", "install_dir/bin/rgbgfx.exe", "install_dir/bin/zlib1.dll", "install_dir/bin/libpng16.dll") "rgbds-win${{ matrix.bits }}.zip"
|
||||
cpack -DCPACK_PACKAGE_FILE_NAME=rgbds-win${{ matrix.bits }} -G ZIP --verbose
|
||||
- name: Upload Windows binaries
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: win${{ matrix.bits }}
|
||||
path: rgbds-win${{ matrix.bits }}.zip
|
||||
path: build/rgbds-win${{ matrix.bits }}.zip
|
||||
if-no-files-found: error
|
||||
|
||||
macos:
|
||||
runs-on: macos-14
|
||||
runs-on: macos-26
|
||||
steps:
|
||||
- name: Get version from tag
|
||||
shell: bash
|
||||
run: | # Turn "refs/tags/vX.Y.Z" into "X.Y.Z"
|
||||
VERSION="${{ github.ref_name }}"
|
||||
echo "version=${VERSION#v}" >> $GITHUB_ENV
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v7
|
||||
- name: Install deps
|
||||
shell: bash
|
||||
run: |
|
||||
./.github/scripts/install_deps.sh macos
|
||||
- name: Build libpng
|
||||
run: |
|
||||
./.github/scripts/build_libpng.sh
|
||||
# We force linking libpng statically; the other libs are provided by macOS itself
|
||||
./.github/scripts/install-deps.sh macos lld
|
||||
- name: Build binaries
|
||||
run: |
|
||||
make -kj CXXFLAGS="-O3 -flto -DNDEBUG -mmacosx-version-min=10.9 -arch x86_64 -arch arm64" PNGCFLAGS="-I libpng-staging/include" PNGLDLIBS="libpng-staging/lib/libpng.a -lz" Q=
|
||||
cmake -B build -G Ninja --preset macos-static -DFETCHCONTENT_BASE_DIR="${{ env.DEPS_ROOT_DIR }}"
|
||||
cmake --build build -- -k 0
|
||||
strip rgb{asm,link,fix,gfx}
|
||||
env:
|
||||
LDFLAGS: -fuse-ld=lld # cmake/macos-static.cmake comments explain why we use lld.
|
||||
- name: Create install script
|
||||
run: |
|
||||
make install.sh
|
||||
- name: Package binaries
|
||||
run: |
|
||||
zip --junk-paths rgbds-macos.zip rgb{asm,link,fix,gfx} man/* .github/scripts/install.sh
|
||||
zip --junk-paths rgbds-macos.zip rgb{asm,link,fix,gfx} man/* install.sh
|
||||
- name: Upload macOS binaries
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: macos
|
||||
path: rgbds-macos.zip
|
||||
if-no-files-found: error
|
||||
|
||||
linux:
|
||||
runs-on: ubuntu-22.04 # Oldest supported, for best glibc compatibility.
|
||||
steps:
|
||||
- name: Get version from tag
|
||||
shell: bash
|
||||
run: | # Turn "refs/tags/vX.Y.Z" into "X.Y.Z"
|
||||
VERSION="${{ github.ref_name }}"
|
||||
echo "version=${VERSION#v}" >> $GITHUB_ENV
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v7
|
||||
- name: Install deps
|
||||
shell: bash
|
||||
run: |
|
||||
./.github/scripts/install_deps.sh ubuntu-22.04
|
||||
./.github/scripts/install-deps.sh ubuntu-22.04
|
||||
- name: Build binaries
|
||||
run: |
|
||||
make -kj WARNFLAGS="-Wall -Wextra -pedantic -static" PKG_CONFIG="pkg-config --static" Q=
|
||||
make -k -j "$(getconf _NPROCESSORS_ONLN)" WARNFLAGS="-Wall -Wextra -pedantic -static" PKG_CONFIG="pkg-config --static" Q=
|
||||
strip rgb{asm,link,fix,gfx}
|
||||
- name: Create install script
|
||||
run: |
|
||||
make install.sh Q=
|
||||
- name: Package binaries
|
||||
run: |
|
||||
tar caf rgbds-linux-x86_64.tar.xz --transform='s#.*/##' rgb{asm,link,fix,gfx} man/* .github/scripts/install.sh
|
||||
tar caf rgbds-linux-x86_64.tar.xz --transform='s#.*/##' rgb{asm,link,fix,gfx} man/* install.sh
|
||||
- name: Upload Linux binaries
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: linux
|
||||
path: rgbds-linux-x86_64.tar.xz
|
||||
if-no-files-found: error
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-slim
|
||||
needs: [windows, macos, linux]
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Get version from tag
|
||||
shell: bash
|
||||
run: | # Turn "refs/tags/vX.Y.Z" into "X.Y.Z"
|
||||
VERSION="${{ github.ref_name }}"
|
||||
echo "version=${VERSION#v}" >> $GITHUB_ENV
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v7
|
||||
- name: Package sources
|
||||
run: |
|
||||
make dist Q=
|
||||
ls
|
||||
- name: Download Linux binaries
|
||||
uses: actions/download-artifact@v4
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
body: |
|
||||
Please ensure that the packages below work properly.
|
||||
Once that's done, replace this text with the changelog, un-draft the release, and update the `release` branch.
|
||||
By the way, if you forgot to update `include/version.hpp`, RGBASM's version test is going to fail in the tag's regression testing! (Use `git push --delete origin <tag>` to delete it)
|
||||
draft: true # Don't publish the release quite yet...
|
||||
prerelease: ${{ contains(github.ref, '-rc') }}
|
||||
files: |
|
||||
win32/rgbds-win32.zip
|
||||
win64/rgbds-win64.zip
|
||||
macos/rgbds-macos.zip
|
||||
linux/rgbds-linux-x86_64.tar.xz
|
||||
rgbds-source.tar.gz
|
||||
fail_on_unmatched_files: true
|
||||
- name: Download all binary packages
|
||||
uses: actions/download-artifact@v8
|
||||
- name: Draft the release
|
||||
run: |
|
||||
.github/scripts/draft-release.sh "${ref#refs/tags/}"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ref: ${{ github.ref_name }}
|
||||
GH_TOKEN: ${{ github.token }} # Required by the `gh` command.
|
||||
|
||||
@@ -4,24 +4,32 @@ on:
|
||||
types:
|
||||
- released # This avoids triggering on pre-releases
|
||||
|
||||
env:
|
||||
# Force colored output (see https://bixense.com/clicolors/ and https://force-color.org/)
|
||||
TERM: xterm-256color
|
||||
CLICOLOR: 1
|
||||
CLICOLOR_FORCE: 1
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.repository_owner == 'gbdev'
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-slim
|
||||
steps:
|
||||
- name: Checkout rgbds@release
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
path: rgbds
|
||||
- name: Checkout rgbds-www@master
|
||||
uses: actions/checkout@v4
|
||||
sparse-checkout: man # Avoid checking out other files, we don't use them here.
|
||||
- name: Checkout rgbds-www
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
repository: ${{ github.repository_owner }}/rgbds-www
|
||||
path: rgbds-www
|
||||
repository: ${{ github.repository_owner }}/rgbds-www
|
||||
ref: master # Need to check out the branch to be able to commit on and push it.
|
||||
ssh-key: !!str ${{ secrets.SSH_KEY_SECRET }}
|
||||
- name: Install groff and mandoc
|
||||
run: |
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get install -yq groff mandoc
|
||||
sudo apt install -Uyq groff mandoc -o APT::Color=yes -o Acquire::Queue-Mode=access
|
||||
- name: Update pages
|
||||
working-directory: rgbds/man
|
||||
run: | # The ref appears to be in the format "refs/tags/<version>", so strip that
|
||||
@@ -29,19 +37,12 @@ jobs:
|
||||
../../rgbds-www/maintainer/new_release.sh ${GITHUB_REF##*/}
|
||||
- name: Push new pages
|
||||
working-directory: rgbds-www
|
||||
env:
|
||||
GIT_AUTHOR_NAME: !!str ${{ github.actor }}
|
||||
GIT_AUTHOR_EMAIL: !!str ${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com
|
||||
GIT_COMMITTER_NAME: github-actions[bot]
|
||||
GIT_COMMITTER_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com
|
||||
run: |
|
||||
mkdir -p -m 700 ~/.ssh
|
||||
cat > ~/.ssh/id_ed25519 <<<"${{ secrets.SSH_KEY_SECRET }}"
|
||||
chmod 0600 ~/.ssh/id_ed25519
|
||||
eval $(ssh-agent -s)
|
||||
ssh-add ~/.ssh/id_ed25519
|
||||
git config --global user.name "GitHub Action"
|
||||
git config --global user.email "[email protected]"
|
||||
git add -A
|
||||
git commit -m "Create RGBDS ${GITHUB_REF##*/} documentation"
|
||||
if git remote | grep -q origin; then
|
||||
git remote set-url origin [email protected]:${{ github.repository_owner }}/rgbds-www.git
|
||||
else
|
||||
git remote add origin [email protected]:${{ github.repository_owner }}/rgbds-www.git
|
||||
fi
|
||||
git push origin master
|
||||
git push
|
||||
|
||||
+197
-180
@@ -3,57 +3,102 @@ on:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
env:
|
||||
# Force colored output (see https://bixense.com/clicolors/ and https://force-color.org/)
|
||||
TERM: xterm-256color
|
||||
CLICOLOR: 1
|
||||
CLICOLOR_FORCE: 1
|
||||
|
||||
CMAKE_COLOR_DIAGNOSTICS: ON # Tells CMake-generated build systems to have colored output.
|
||||
CMAKE_CONFIG_TYPE: Debug # `cmake --build` now implies `--config Debug`.
|
||||
|
||||
CTEST_PARALLEL_LEVEL: "" # `ctest` now implies `--parallel` (default number of logical CPUs).
|
||||
CTEST_NO_TESTS_ACTION: error # Make CTest fail if it cannot find any tests. (That should never happen.)
|
||||
CTEST_OUTPUT_ON_FAILURE: ON # CTest reports test program output on failure.
|
||||
|
||||
GIT_CONFIG_COUNT: 1
|
||||
GIT_CONFIG_KEY_0: color.ui
|
||||
GIT_CONFIG_VALUE_0: always # Colorful output is helpful even though CI is not considered to be a terminal.
|
||||
|
||||
# We instruct CMake to download and build third-party projects outside of our source tree,
|
||||
# otherwise they can trigger `-Werror=dev` (from the `develop` preset).
|
||||
DEPS_ROOT_DIR: ~/_deps # Note that this needs to be used in a position where Bash will trigger tilde expansion!
|
||||
|
||||
# We use bash syntax across OSes for consistency unless otherwise specified
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
# We use Ninja as the generator for CMake below because it provides synchronous output by default,
|
||||
# and only shows full command lines when they fail.
|
||||
|
||||
jobs:
|
||||
unix:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-22.04, macos-14]
|
||||
os: [ubuntu-22.04, macos-15-intel, macos-26]
|
||||
cxx: [g++, clang++]
|
||||
buildsys: [make, cmake]
|
||||
exclude:
|
||||
# Don't use `g++` on macOS; it's just an alias to `clang++`.
|
||||
- os: macos-14
|
||||
cxx: g++
|
||||
exclude: # Don't use `g++` on macOS; it's just an alias to `clang++`.
|
||||
- { os: macos-15-intel, cxx: g++ }
|
||||
- { os: macos-26, cxx: g++ }
|
||||
include: # Use `g++-10`, the earliest GCC version we support, on the earliest Ubuntu runner.
|
||||
- { os: ubuntu-22.04, cxx: g++-10, buildsys: make }
|
||||
- { os: ubuntu-22.04, cxx: g++-10, buildsys: cmake }
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v7
|
||||
- name: Install deps
|
||||
shell: bash
|
||||
run: |
|
||||
./.github/scripts/install_deps.sh ${{ matrix.os }}
|
||||
- name: Build & install using Make
|
||||
.github/scripts/install-deps.sh ${{ matrix.os }} ${{ matrix.cxx }}
|
||||
- name: Build using Make
|
||||
if: matrix.buildsys == 'make'
|
||||
run: |
|
||||
make develop -kj Q= CXX=${{ matrix.cxx }}
|
||||
sudo make install -j Q=
|
||||
- name: Build & install using CMake
|
||||
make develop -k -j "$(getconf _NPROCESSORS_ONLN)" Q= CXX=${{ matrix.cxx }}
|
||||
- name: Install using Make
|
||||
if: matrix.buildsys == 'make'
|
||||
run: |
|
||||
sudo make install Q=
|
||||
type rgbasm rgblink rgbfix rgbgfx
|
||||
man -w 1 rgbasm rgblink rgbfix rgbgfx
|
||||
- name: Build using CMake
|
||||
if: matrix.buildsys == 'cmake'
|
||||
# Since GitHub's runners are basically kitchen sinks,
|
||||
# the Mono framework exposes a libpng 1.4.x header, breaking everything.
|
||||
# Searching frameworks last makes Homebrew's libpng be discovered first, which works.
|
||||
# Note that since this is specific to our CI environment, the workaround is
|
||||
# better applied here than in our CMakeLists, where it could affect and break someone else.
|
||||
run: |
|
||||
CXX=${{ matrix.cxx }} cmake -B build -G Ninja --preset develop -DCMAKE_FIND_FRAMEWORK=LAST
|
||||
cmake --build build -- -k 0
|
||||
- name: Install using CMake
|
||||
if: matrix.buildsys == 'cmake'
|
||||
run: |
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} -DSANITIZERS=ON -DMORE_WARNINGS=ON
|
||||
cmake --build build -j --verbose
|
||||
sudo cmake --install build --verbose
|
||||
type rgbasm rgblink rgbfix rgbgfx
|
||||
man -w 1 rgbasm rgblink rgbfix rgbgfx
|
||||
- name: Package binaries
|
||||
run: |
|
||||
mkdir bins
|
||||
cp rgb{asm,link,fix,gfx} bins
|
||||
- name: Upload binaries
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: rgbds-canary-${{ matrix.os }}-${{ matrix.cxx }}-${{ matrix.buildsys }}
|
||||
path: bins
|
||||
if-no-files-found: error
|
||||
- name: Compute test dependency cache params
|
||||
id: test-deps-cache-params
|
||||
shell: bash
|
||||
run: |
|
||||
paths=$(test/fetch-test-deps.sh --get-paths)
|
||||
hash=$(test/fetch-test-deps.sh --get-hash)
|
||||
paths=$(test/external/fetch-repos.sh --get-paths)
|
||||
hash=$(test/external/fetch-repos.sh --get-hash)
|
||||
tee -a <<<"paths=\"${paths//,/\\n}\"" $GITHUB_OUTPUT
|
||||
tee -a <<<"hash=${hash%-}" $GITHUB_OUTPUT
|
||||
- name: Check test dependency repositories cache
|
||||
id: test-deps-cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
||||
key: ${{ matrix.os }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
||||
@@ -61,211 +106,178 @@ jobs:
|
||||
if: steps.test-deps-cache.outputs.cache-hit != 'true'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
test/fetch-test-deps.sh
|
||||
- name: Install test dependency dependencies
|
||||
shell: bash
|
||||
test/external/fetch-repos.sh
|
||||
- name: Run tests using our script
|
||||
if: matrix.buildsys == 'make'
|
||||
run: |
|
||||
test/fetch-test-deps.sh --get-deps ${{ matrix.os }}
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
CXX=${{ matrix.cxx }} test/run-tests.sh --os ${{ matrix.os }} --jobs "$(getconf _NPROCESSORS_ONLN)"
|
||||
- name: Run tests using CTest
|
||||
if: matrix.buildsys == 'cmake'
|
||||
run: |
|
||||
CXX=${{ matrix.cxx }} test/run-tests.sh --os ${{ matrix.os }}
|
||||
ctest --test-dir build --schedule-random
|
||||
|
||||
macos-static:
|
||||
runs-on: macos-14
|
||||
runs-on: macos-26
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v7
|
||||
- name: Install deps
|
||||
shell: bash
|
||||
run: |
|
||||
./.github/scripts/install_deps.sh macos
|
||||
- name: Build libpng
|
||||
.github/scripts/install-deps.sh macos lld
|
||||
- name: Cache library deps
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ env.DEPS_ROOT_DIR }}/*-tmp/
|
||||
key: dep-srcs-${{ hashFiles('cmake/deps.cmake') }}
|
||||
enableCrossOsArchive: true
|
||||
- name: Build
|
||||
run: |
|
||||
./.github/scripts/build_libpng.sh
|
||||
- name: Build & install
|
||||
cmake -B build -G Ninja --preset macos-static -DFETCHCONTENT_BASE_DIR="${{ env.DEPS_ROOT_DIR }}"
|
||||
cmake --build build -- -k 0
|
||||
env:
|
||||
LDFLAGS: -fuse-ld=lld # cmake/macos-static.cmake comments explain why we use lld.
|
||||
- name: Install
|
||||
run: |
|
||||
make -kj CXXFLAGS="-O3 -flto -DNDEBUG -mmacosx-version-min=10.9 -arch x86_64 -arch arm64" PNGCFLAGS="-I libpng-staging/include" PNGLDLIBS="libpng-staging/lib/libpng.a -lz" Q=
|
||||
sudo cmake --install build --verbose
|
||||
type rgbasm rgblink rgbfix rgbgfx
|
||||
man -w 1 rgbasm rgblink rgbfix rgbgfx
|
||||
- name: Package binaries
|
||||
run: |
|
||||
mkdir bins
|
||||
cp rgb{asm,link,fix,gfx} bins
|
||||
- name: Upload binaries
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: rgbds-canary-macos-static
|
||||
path: bins
|
||||
if-no-files-found: error
|
||||
- name: Compute test dependency cache params
|
||||
id: test-deps-cache-params
|
||||
shell: bash
|
||||
run: |
|
||||
paths=$(test/fetch-test-deps.sh --get-paths)
|
||||
hash=$(test/fetch-test-deps.sh --get-hash)
|
||||
paths=$(test/external/fetch-repos.sh --get-paths)
|
||||
hash=$(test/external/fetch-repos.sh --get-hash)
|
||||
tee -a <<<"paths=\"${paths//,/\\n}\"" $GITHUB_OUTPUT
|
||||
tee -a <<<"hash=${hash%-}" $GITHUB_OUTPUT
|
||||
- name: Check test dependency repositories cache
|
||||
id: test-deps-cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
||||
key: ${{ matrix.os }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
||||
key: macos-26-${{ steps.test-deps-cache-params.outputs.hash }}
|
||||
- name: Fetch test dependency repositories
|
||||
if: steps.test-deps-cache.outputs.cache-hit != 'true'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
test/fetch-test-deps.sh
|
||||
- name: Install test dependency dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
test/fetch-test-deps.sh --get-deps macos
|
||||
test/external/fetch-repos.sh
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
run: |
|
||||
test/run-tests.sh --os macos
|
||||
ctest --test-dir build --schedule-random
|
||||
|
||||
windows:
|
||||
strategy:
|
||||
matrix:
|
||||
bits: [32, 64]
|
||||
os: [windows-2022, windows-2025]
|
||||
include:
|
||||
- bits: 32
|
||||
arch: x86
|
||||
platform: Win32
|
||||
- bits: 64
|
||||
arch: x86_x64
|
||||
platform: x64
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v7
|
||||
- name: Install deps
|
||||
run: .github/scripts/get_win_deps.ps1
|
||||
- name: Check libraries cache
|
||||
id: cache
|
||||
uses: actions/cache@v4
|
||||
run: |
|
||||
bash .github/scripts/install-deps.sh ${{ matrix.os }}
|
||||
- name: Cache library deps
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
zbuild
|
||||
pngbuild
|
||||
key: ${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles('zlib/**', 'libpng/**') }}
|
||||
- name: Build zlib
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: | # BUILD_SHARED_LIBS causes the output DLL to be correctly called `zlib1.dll`
|
||||
cmake -S zlib -B zbuild -A ${{ matrix.platform }} -Wno-dev -DCMAKE_INSTALL_PREFIX=install_dir -DBUILD_SHARED_LIBS=ON
|
||||
cmake --build zbuild --config Release -j
|
||||
- name: Install zlib
|
||||
path: ${{ env.DEPS_ROOT_DIR }}/*-tmp/
|
||||
key: dep-srcs-${{ hashFiles('cmake/deps.cmake') }}
|
||||
enableCrossOsArchive: true
|
||||
- name: Build
|
||||
shell: cmd
|
||||
run: | # ASan seems to be broken on Windows, so we disable it.
|
||||
call .github\scripts\msvc-env.bat ${{ matrix.bits }}
|
||||
cmake -B build -G Ninja --preset develop -DSANITIZERS=OFF ^
|
||||
-DFETCHCONTENT_BASE_DIR="${{ env.DEPS_ROOT_DIR }}" -DCMAKE_C_FLAGS="/nologo"
|
||||
cmake --build build -- -k 0
|
||||
- name: Install
|
||||
shell: cmd
|
||||
run: |
|
||||
cmake --install zbuild
|
||||
- name: Build libpng
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
cmake -S libpng -B pngbuild -A ${{ matrix.platform }} -Wno-dev -DCMAKE_INSTALL_PREFIX=install_dir -DPNG_SHARED=ON -DPNG_STATIC=OFF -DPNG_TESTS=OFF
|
||||
cmake --build pngbuild --config Release -j
|
||||
- name: Install libpng
|
||||
run: |
|
||||
cmake --install pngbuild
|
||||
- name: Build Windows binaries
|
||||
shell: bash
|
||||
run: |
|
||||
cmake -S . -B build -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build build --config Release -j --verbose
|
||||
cmake --install build --verbose --prefix install_dir
|
||||
cmake --install build --verbose
|
||||
where rgbasm rgblink rgbfix rgbgfx
|
||||
- name: Package binaries
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: |
|
||||
mkdir bins
|
||||
cp install_dir/bin/{rgbasm.exe,rgblink.exe,rgbfix.exe,rgbgfx.exe,zlib1.dll,libpng16.dll} bins
|
||||
cpack -DCPACK_PACKAGE_FILE_NAME=rgbds-win${{ matrix.bits }} -G ZIP --verbose
|
||||
- name: Upload Windows binaries
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: rgbds-canary-w${{ matrix.bits }}-${{ matrix.os }}
|
||||
path: bins
|
||||
path: build/rgbds-win${{ matrix.bits }}.zip
|
||||
if-no-files-found: error
|
||||
- name: Compute test dependency cache params
|
||||
id: test-deps-cache-params
|
||||
shell: bash
|
||||
run: |
|
||||
paths=$(test/fetch-test-deps.sh --get-paths)
|
||||
hash=$(test/fetch-test-deps.sh --get-hash)
|
||||
paths=$(test/external/fetch-repos.sh --get-paths)
|
||||
hash=$(test/external/fetch-repos.sh --get-hash)
|
||||
tee -a <<<"paths=\"${paths//,/\\n}\"" $GITHUB_OUTPUT
|
||||
tee -a <<<"hash=${hash%-}" $GITHUB_OUTPUT
|
||||
- name: Check test dependency repositories cache
|
||||
id: test-deps-cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
||||
key: ${{ matrix.os }}-${{ matrix.bits }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
||||
- name: Fetch test dependency repositories
|
||||
if: steps.test-deps-cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
continue-on-error: true
|
||||
run: |
|
||||
test/fetch-test-deps.sh
|
||||
- name: Install test dependency dependencies
|
||||
shell: bash
|
||||
test/external/fetch-repos.sh
|
||||
- name: Run tests using CTest
|
||||
run: |
|
||||
test/fetch-test-deps.sh --get-deps ${{ matrix.os }}
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
run: |
|
||||
cp bins/* .
|
||||
cp bins/*.dll test/gfx
|
||||
test/run-tests.sh --os ${{ matrix.os }}
|
||||
ctest --test-dir build --schedule-random
|
||||
|
||||
windows-mingw-build:
|
||||
strategy:
|
||||
matrix:
|
||||
bits: [32, 64]
|
||||
include:
|
||||
- bits: 32
|
||||
arch: i686
|
||||
triplet: i686-w64-mingw32
|
||||
- bits: 64
|
||||
arch: x86-64
|
||||
triplet: x86_64-w64-mingw32
|
||||
fail-fast: false
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
DIST_DIR: win${{ matrix.bits }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v7
|
||||
- name: Install deps
|
||||
shell: bash
|
||||
run: |
|
||||
./.github/scripts/install_deps.sh ubuntu
|
||||
- name: Install MinGW
|
||||
run: | # dpkg-dev is apparently required for pkg-config for cross-building
|
||||
sudo apt-get install g++-mingw-w64-${{ matrix.arch }}-win32 mingw-w64-tools libz-mingw-w64-dev dpkg-dev
|
||||
- name: Install libpng dev headers for MinGW
|
||||
run: |
|
||||
./.github/scripts/mingw-w64-libpng-dev.sh ${{ matrix.triplet }}
|
||||
.github/scripts/install-deps.sh ubuntu mingw${{ matrix.bits }}
|
||||
- name: Cache library deps
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ env.DEPS_ROOT_DIR }}/*-tmp/
|
||||
key: dep-srcs-${{ hashFiles('cmake/deps.cmake') }}
|
||||
enableCrossOsArchive: true
|
||||
- name: Cross-build Windows binaries
|
||||
run: | # MinGW does not support `--preset develop` sanitizers ASan or UBSan.
|
||||
dll_search_dir=$(printf '%s\n' /usr/lib/gcc/*-w64-mingw32/*-win32 | tee -a /dev/stderr)
|
||||
cmake -B build -G Ninja --preset develop -DSANITIZERS=OFF --toolchain cmake/toolchain-mingw${{ matrix.bits }}.cmake \
|
||||
-DFETCHCONTENT_BASE_DIR="${{ env.DEPS_ROOT_DIR }}" -DDLL_SEARCH_DIRS="$dll_search_dir"
|
||||
cmake --build build -- -k 0
|
||||
- name: Package Windows binaries
|
||||
run: |
|
||||
make mingw${{ matrix.bits }} -kj Q=
|
||||
- name: Package binaries
|
||||
run: | # DLL dependencies can be figured out using e.g. Dependency Walker or objdump -p
|
||||
mkdir bins
|
||||
mv -v rgb{asm,link,fix,gfx}.exe bins/
|
||||
cp -v /usr/${{ matrix.triplet }}/lib/zlib1.dll bins
|
||||
cp -v /usr/${{ matrix.triplet }}/bin/libpng16-16.dll bins
|
||||
cp -v /usr/lib/gcc/${{ matrix.triplet }}/10-win32/lib{ssp-0,stdc++-6}.dll bins
|
||||
[ "${{ matrix.bits }}" -ne 32 ] || cp -v /usr/lib/gcc/${{ matrix.triplet }}/10-win32/libgcc_s_dw2-1.dll bins
|
||||
cmake --install build --prefix . --verbose --component binaries
|
||||
cmake --install build --prefix . --verbose --component shared-libs
|
||||
- name: Upload Windows binaries
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: rgbds-canary-mingw-win${{ matrix.bits }}
|
||||
path: bins
|
||||
path: bin
|
||||
if-no-files-found: error
|
||||
- name: Upload Windows test binaries
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: testing-programs-mingw-win${{ matrix.bits }}
|
||||
path: |
|
||||
test/gfx/randtilegen.exe
|
||||
test/gfx/rgbgfx_test.exe
|
||||
if-no-files-found: error
|
||||
|
||||
windows-mingw-testing:
|
||||
needs: windows-mingw-build
|
||||
@@ -277,50 +289,45 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v7
|
||||
- name: Install test deps
|
||||
run: |
|
||||
.github/scripts/install-deps.sh windowsmingw
|
||||
- name: Retrieve binaries
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: rgbds-canary-mingw-win${{ matrix.bits }}
|
||||
path: bins
|
||||
- name: Retrieve test binaries
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: testing-programs-mingw-win${{ matrix.bits }}
|
||||
path: test/gfx
|
||||
- name: Extract binaries
|
||||
shell: bash
|
||||
run: |
|
||||
cp bins/* .
|
||||
cp bins/*.dll test/gfx
|
||||
- name: Compute test dependency cache params
|
||||
id: test-deps-cache-params
|
||||
shell: bash
|
||||
run: |
|
||||
paths=$(test/fetch-test-deps.sh --get-paths)
|
||||
hash=$(test/fetch-test-deps.sh --get-hash)
|
||||
paths=$(test/external/fetch-repos.sh --get-paths)
|
||||
hash=$(test/external/fetch-repos.sh --get-hash)
|
||||
tee -a <<<"paths=\"${paths//,/\\n}\"" $GITHUB_OUTPUT
|
||||
tee -a <<<"hash=${hash%-}" $GITHUB_OUTPUT
|
||||
- name: Check test dependency repositories cache
|
||||
id: test-deps-cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
||||
key: mingw-${{ matrix.bits }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
||||
- name: Fetch test dependency repositories
|
||||
if: steps.test-deps-cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
continue-on-error: true
|
||||
run: |
|
||||
test/fetch-test-deps.sh
|
||||
- name: Install test dependency dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
test/fetch-test-deps.sh --get-deps ${{ matrix.os }}
|
||||
test/external/fetch-repos.sh
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
run: |
|
||||
test/run-tests.sh --os ${{ matrix.os }}
|
||||
test/run-tests.sh --os ${{ matrix.os }} --jobs "$(getconf _NPROCESSORS_ONLN)"
|
||||
|
||||
cygwin:
|
||||
strategy:
|
||||
@@ -334,14 +341,18 @@ jobs:
|
||||
fail-fast: false
|
||||
runs-on: windows-2022
|
||||
timeout-minutes: 30
|
||||
defaults:
|
||||
run:
|
||||
shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -o igncr '{0}'
|
||||
steps:
|
||||
- name: Save Windows git location for the PATH
|
||||
shell: pwsh
|
||||
run: | # The `cygwin/cygwin-install-action` step will override the PATH with Cygwin's binaries
|
||||
"ORIGINAL_GIT_DIR=$(Split-Path (Get-Command git).Source)" >> $env:GITHUB_ENV
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
uses: actions/checkout@v7
|
||||
- name: Setup Cygwin
|
||||
uses: cygwin/cygwin-install-action@v4
|
||||
uses: cygwin/cygwin-install-action@v6
|
||||
with:
|
||||
platform: ${{ matrix.arch }}
|
||||
packages: >-
|
||||
@@ -351,39 +362,45 @@ jobs:
|
||||
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}'
|
||||
- name: Build
|
||||
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}'
|
||||
make -k -j "$(getconf _NPROCESSORS_ONLN)" Q=
|
||||
- name: Install
|
||||
run: |
|
||||
test/run-tests.sh --only-internal
|
||||
make install Q=
|
||||
type rgbasm rgblink rgbfix rgbgfx
|
||||
man -w 1 rgbasm rgblink rgbfix rgbgfx
|
||||
- name: Run tests
|
||||
run: |
|
||||
test/run-tests.sh --os cygwin --only-internal --jobs "$(getconf _NPROCESSORS_ONLN)"
|
||||
- name: Use Windows git location in the PATH
|
||||
shell: pwsh
|
||||
run: | # Prevents the `actions/checkout` post-job cleanup from using Cygwin's git binary
|
||||
"$env:ORIGINAL_GIT_DIR" >> $env:GITHUB_PATH
|
||||
|
||||
freebsd:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
uses: actions/checkout@v7
|
||||
- name: Build & test using CMake on FreeBSD
|
||||
uses: vmactions/freebsd-vm@v1
|
||||
with:
|
||||
release: "14.3"
|
||||
envs: >-
|
||||
TERM CLICOLOR CLICOLOR_FORCE
|
||||
CMAKE_COLOR_DIAGNOSTICS CMAKE_CONFIG_TYPE
|
||||
CTEST_PARALLEL_LEVEL
|
||||
CTEST_NO_TESTS_ACTION CTEST_OUTPUT_ON_FAILURE
|
||||
GIT_CONFIG_COUNT GIT_CONFIG_KEY_0 GIT_CONFIG_VALUE_0
|
||||
release: "15.1"
|
||||
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 -DOS=bsd
|
||||
cmake --build build -j4 --verbose
|
||||
.github/scripts/install-deps.sh freebsd
|
||||
run: | # Leak detection is not supported on FreeBSD, so disable it.
|
||||
cmake -B build --preset develop
|
||||
cmake --build build --verbose -- -k -j "$(getconf _NPROCESSORS_ONLN)"
|
||||
ASAN_OPTIONS=detect_leaks=0 ctest --test-dir build --schedule-random --label-exclude external
|
||||
cmake --install build --verbose
|
||||
cmake --build build --target test
|
||||
type rgbasm rgblink rgbfix rgbgfx
|
||||
man -w 1 rgbasm rgblink rgbfix rgbgfx
|
||||
|
||||
@@ -4,57 +4,47 @@ on:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- man/gbz80.7
|
||||
- man/rgbds.5
|
||||
- man/rgbds.7
|
||||
- man/rgbasm.1
|
||||
- man/rgbasm.5
|
||||
- man/rgbasm-old.5
|
||||
- man/rgblink.1
|
||||
- man/rgblink.5
|
||||
- man/rgbfix.1
|
||||
- man/rgbgfx.1
|
||||
- man/*
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
# Force colored output (see https://bixense.com/clicolors/ and https://force-color.org/)
|
||||
TERM: xterm-256color
|
||||
CLICOLOR: 1
|
||||
CLICOLOR_FORCE: 1
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.repository_owner == 'gbdev'
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-slim
|
||||
steps:
|
||||
- name: Checkout rgbds@master
|
||||
uses: actions/checkout@v4
|
||||
- name: Checkout rgbds
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
repository: gbdev/rgbds
|
||||
ref: master
|
||||
path: rgbds
|
||||
- name: Checkout rgbds-www@master
|
||||
uses: actions/checkout@v4
|
||||
sparse-checkout: man # Avoid checking out other files, we don't use them here.
|
||||
- name: Checkout rgbds-www
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
repository: gbdev/rgbds-www
|
||||
ref: master
|
||||
path: rgbds-www
|
||||
repository: ${{ github.repository_owner }}/rgbds-www
|
||||
ref: master # Need to check out the branch to be able to commit on and push it.
|
||||
ssh-key: !!str ${{ secrets.SSH_KEY_SECRET }}
|
||||
- name: Install groff and mandoc
|
||||
run: |
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get install -yq groff mandoc
|
||||
sudo apt install -Uyq groff mandoc -o APT::Color=yes -o Acquire::Queue-Mode=access
|
||||
- name: Update pages
|
||||
working-directory: rgbds/man
|
||||
run: |
|
||||
../../rgbds-www/maintainer/man_to_html.sh master *
|
||||
- name: Push new pages
|
||||
working-directory: rgbds-www
|
||||
env:
|
||||
GIT_AUTHOR_NAME: !!str ${{ github.actor }}
|
||||
GIT_AUTHOR_EMAIL: !!str ${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com
|
||||
GIT_COMMITTER_NAME: github-actions[bot]
|
||||
GIT_COMMITTER_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com
|
||||
run: |
|
||||
mkdir -p -m 700 ~/.ssh
|
||||
echo "${{ secrets.SSH_KEY_SECRET }}" > ~/.ssh/id_ed25519
|
||||
chmod 0600 ~/.ssh/id_ed25519
|
||||
eval $(ssh-agent -s)
|
||||
ssh-add ~/.ssh/id_ed25519
|
||||
git config --global user.name "GitHub Action"
|
||||
git config --global user.email "[email protected]"
|
||||
git add -A
|
||||
git commit -m "Update RGBDS master documentation"
|
||||
if git remote | grep -q origin; then
|
||||
git remote set-url origin [email protected]:gbdev/rgbds-www.git
|
||||
else
|
||||
git remote add origin [email protected]:gbdev/rgbds-www.git
|
||||
fi
|
||||
git push origin master
|
||||
git push
|
||||
|
||||
+4
-1
@@ -2,6 +2,7 @@
|
||||
/rgblink
|
||||
/rgbfix
|
||||
/rgbgfx
|
||||
/install.sh
|
||||
/rgbshim.sh
|
||||
/coverage/
|
||||
*.o
|
||||
@@ -10,9 +11,11 @@
|
||||
*.gcno
|
||||
*.gcda
|
||||
*.gcov
|
||||
/compile_flags.txt
|
||||
CMakeCache.txt
|
||||
CMakeFiles/
|
||||
cmake_install.cmake
|
||||
build/
|
||||
CMakeUserPresets.json
|
||||
build*/
|
||||
*.dSYM/
|
||||
callgrind.out.*
|
||||
|
||||
+190
-93
@@ -1,122 +1,199 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
# 3.9 required for LTO checks
|
||||
# 3.17 optional for CMAKE_CTEST_ARGUMENTS
|
||||
cmake_minimum_required(VERSION 3.9..3.17 FATAL_ERROR)
|
||||
# - 3.24 is required for `FetchContent`'s `find_package` integration.
|
||||
# Older versions *may* work, but we can't test them; compat patches are welcome.
|
||||
# - 3.17 is required for `CHECK_*` messages to display properly, but is not essential.
|
||||
# - 3.9 is required for LTO checks.
|
||||
cmake_minimum_required(VERSION 3.24...4.3 FATAL_ERROR)
|
||||
|
||||
# Read the project version from the header (the canonical source of truth).
|
||||
file(STRINGS "include/version.hpp" version_defines REGEX "^[ \t]*#define[ \t]+PACKAGE_VERSION_")
|
||||
foreach(line IN LISTS version_defines)
|
||||
# We want the `CMAKE_MATCH_n` variables, so we just need to run *some* regex op.
|
||||
string(REGEX MATCH "PACKAGE_(VERSION_[^ \t]+)[ \t]+([0-9]+)" dummy "${line}")
|
||||
set("${CMAKE_MATCH_1}" "${CMAKE_MATCH_2}")
|
||||
endforeach()
|
||||
|
||||
project(rgbds
|
||||
LANGUAGES CXX)
|
||||
|
||||
include(CTest)
|
||||
|
||||
# get real path of source and binary directories
|
||||
get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH)
|
||||
get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH)
|
||||
|
||||
# reject in-source builds, may conflict with Makefile
|
||||
if(srcdir STREQUAL bindir)
|
||||
message("RGBDS should not be built in the source directory.")
|
||||
message("Instead, create a separate build directory and specify to CMake the path to the source directory.")
|
||||
message(FATAL_ERROR "Terminating configuration")
|
||||
VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}"
|
||||
LANGUAGES CXX
|
||||
DESCRIPTION "Game Boy assembly toolchain"
|
||||
HOMEPAGE_URL "https://rgbds.gbdev.io")
|
||||
if(DEFINED VERSION_RC)
|
||||
string(APPEND CMAKE_PROJECT_VERSION "-rc${VERSION_RC}")
|
||||
endif()
|
||||
|
||||
option(SANITIZERS "Build with sanitizers enabled" OFF) # Ignored on MSVC
|
||||
option(MORE_WARNINGS "Turn on more warnings" OFF) # Ignored on MSVC
|
||||
# Reject in-source builds, as they may conflict with the Makefile.
|
||||
get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH)
|
||||
get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH)
|
||||
if(srcdir STREQUAL bindir)
|
||||
message(FATAL_ERROR "RGBDS should not be built in the source directory.
|
||||
Instead, create a separate build directory and specify to CMake the path to the source directory.")
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
# MSVC's own standard library triggers warning C5105,
|
||||
# "macro expansion producing 'defined' has undefined behavior".
|
||||
# Warning C5030 is about unknown attributes (`[[gnu::ATTR]]`), none of ours being load-bearing.
|
||||
# Warning C4996 is about using POSIX names, which we want to do for portability.
|
||||
# We also opt into the C++20-conformant preprocessor.
|
||||
add_compile_options(/MP /wd5105 /wd5030 /wd4996 /Zc:preprocessor)
|
||||
add_definitions(/D_CRT_SECURE_NO_WARNINGS)
|
||||
include(CTest) # Note: CTest only functions properly if included from the top-level CMakeLists.
|
||||
include(GNUInstallDirs)
|
||||
include(CMakeDependentOption)
|
||||
|
||||
if(SANITIZERS)
|
||||
set(SAN_FLAGS /fsanitize=address)
|
||||
add_compile_options(${SAN_FLAGS})
|
||||
add_link_options(${SAN_FLAGS})
|
||||
endif()
|
||||
else()
|
||||
add_compile_options(-Wall -pedantic)
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
# C++20 allows macros to take zero variadic arguments, but Clang (aka AppleClang on macOS)
|
||||
# does not recognize this yet.
|
||||
add_compile_options(-Wno-gnu-zero-variadic-macro-arguments)
|
||||
endif()
|
||||
if(SANITIZERS)
|
||||
## Compiler switches that should apply to our deps too.
|
||||
|
||||
option(SANITIZERS "Build with sanitizers enabled" OFF)
|
||||
cmake_dependent_option(MORE_WARNINGS "Turn on more warnings" OFF "NOT MSVC" OFF)
|
||||
|
||||
if(NOT DEFINED CMAKE_MSVC_RUNTIME_LIBRARY) # Allow this to be overridden from the CLI easily.
|
||||
# We link against the Microsoft C++ runtime libs statically, except the debug-mode one.
|
||||
# This is because Microsoft's license terms (through some indirection) allow us to redistribute
|
||||
# the runtime lib code *except* the debug-mode libs'.
|
||||
# Since we want to remain able to not think too hard about debug executables, we link against
|
||||
# the debug libs dynamically, shifting the burden of obtaining them onto the recipient.
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:DebugDLL>")
|
||||
endif()
|
||||
|
||||
if(SANITIZERS)
|
||||
if(MSVC)
|
||||
message(STATUS "ASan enabled")
|
||||
add_compile_options(/fsanitize=address) # Note that this shouldn't be passed to the linker.
|
||||
else() # We assume a GNU-like compiler.
|
||||
message(STATUS "ASan and UBSan enabled")
|
||||
set(SAN_FLAGS -fsanitize=address -fsanitize=undefined
|
||||
-fsanitize=float-divide-by-zero)
|
||||
add_compile_options(${SAN_FLAGS})
|
||||
add_link_options(${SAN_FLAGS})
|
||||
add_definitions(-D_GLIBCXX_ASSERTIONS -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG)
|
||||
# A non-zero optimization level is desired in debug mode, but allow overriding it nonetheless
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-g -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls ${CMAKE_CXX_FLAGS_DEBUG}"
|
||||
CACHE STRING "" FORCE)
|
||||
# A non-zero optimization level is desired even in debug mode (especially for Clang),
|
||||
# and the two codegen flags improve the sanitizers' backtraces, but we want the user to
|
||||
# be able to override these easily so we put them first.
|
||||
string(PREPEND CMAKE_CXX_FLAGS_DEBUG "-Og -fno-omit-frame-pointer -fno-optimize-sibling-calls ")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message(CHECK_START "Checking if LTO is supported")
|
||||
include(CheckIPOSupported)
|
||||
check_ipo_supported(RESULT enable_lto)
|
||||
if(enable_lto)
|
||||
message(CHECK_PASS "yes (enabled only in release modes)")
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO ON)
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL ON)
|
||||
else()
|
||||
message(CHECK_FAIL "no")
|
||||
endif()
|
||||
|
||||
# Use versioning consistent with Makefile:
|
||||
# the git revision is used but uses the fallback in an archive.
|
||||
|
||||
message(CHECK_START "Determining RGBDS version from Git history")
|
||||
list(APPEND CMAKE_MESSAGE_INDENT " ")
|
||||
set(GIT_REV "") # This fallback is important!
|
||||
find_package(Git)
|
||||
list(POP_BACK CMAKE_MESSAGE_INDENT)
|
||||
if(NOT Git_FOUND)
|
||||
message(CHECK_FAIL "Git not found")
|
||||
else()
|
||||
execute_process(COMMAND "${GIT_EXECUTABLE}" --git-dir=.git -c safe.directory='*'
|
||||
describe --tags --dirty --always --match "v[0-9]*"
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE GIT_REV OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_VARIABLE git_err ERROR_STRIP_TRAILING_WHITESPACE
|
||||
RESULT_VARIABLE result)
|
||||
if(NOT result EQUAL 0)
|
||||
# Note that this happens e.g. when building from a tarball, so it shouldn't fail the build!
|
||||
message(CHECK_FAIL "error ${result} from Git:")
|
||||
list(APPEND CMAKE_MESSAGE_INDENT " ")
|
||||
message("${git_err}")
|
||||
list(POP_BACK CMAKE_MESSAGE_INDENT)
|
||||
else()
|
||||
message(CHECK_PASS "${GIT_REV}")
|
||||
if(NOT "${GIT_REV}" MATCHES "^v[0-9]+\\.[0-9]+\\.[0-9]+(-rc[0-9]+)?")
|
||||
# Can't find an ancestor tag! (That passes `--match`, anyway.)
|
||||
message(WARNING "No `v*` Git tag reachable; falling back")
|
||||
elseif(NOT CMAKE_MATCH_0 STREQUAL "v${CMAKE_PROJECT_VERSION}")
|
||||
message(SEND_ERROR "\
|
||||
Version mismatch! Git says ${CMAKE_MATCH_0},
|
||||
version.hpp says v${CMAKE_PROJECT_VERSION}!")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
## Dependencies.
|
||||
|
||||
include(FetchContent)
|
||||
include(cmake/deps.cmake)
|
||||
|
||||
# Be sure to make zlib available before libpng, otherwise libpng
|
||||
# will attempt to `find_package(ZLIB)` without going through `FetchContent`.
|
||||
if(NOT DEFINED ZLIB_BUILD_TESTING) # Unless overridden (e.g. in the cache), we don't care about zlib's tests.
|
||||
set(ZLIB_BUILD_TESTING OFF)
|
||||
endif()
|
||||
FetchContent_MakeAvailable(ZLIB)
|
||||
if(NOT DEFINED ZLIB_INCLUDE_DIRS)
|
||||
set(ZLIB_INCLUDE_DIRS "${zlib_BINARY_DIR};${zlib_SOURCE_DIR}") # libpng's `genout` script relies on this variable to be set.
|
||||
endif()
|
||||
# Statically building zlib (from source) generates a target named differently.
|
||||
if(NOT TARGET ZLIB::ZLIB AND TARGET zlibstatic)
|
||||
add_library(ZLIB::ZLIB ALIAS zlibstatic)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED PNG_TESTS) # Unless overridden (e.g. in the cache), we don't care about libpng's tests.
|
||||
set(PNG_TESTS OFF)
|
||||
endif()
|
||||
FetchContent_MakeAvailable(PNG)
|
||||
if(NOT TARGET PNG::PNG)
|
||||
if(PNG_SHARED)
|
||||
add_library(PNG::PNG ALIAS png_shared)
|
||||
else()
|
||||
add_library(PNG::PNG ALIAS png_static)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
## The actual stuff.
|
||||
# Any compiler options that shouldn't apply to our dependencies go here.
|
||||
|
||||
include_directories("include")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
|
||||
if(MSVC)
|
||||
add_compile_options(
|
||||
/wd5030 # Warning C5030 is about unknown attributes (`[[gnu::ATTR]]`), none of ours being load-bearing.
|
||||
/wd4996 # Warning C4996 is about using POSIX names, which we want to do for portability.
|
||||
/Zc:preprocessor # Opt into the C++20-conformant preprocessor.
|
||||
)
|
||||
add_definitions(/D_CRT_SECURE_NO_WARNINGS)
|
||||
else()
|
||||
add_compile_options(-Wall -pedantic -Wno-unknown-warning-option -fno-exceptions -fno-rtti)
|
||||
|
||||
if(MORE_WARNINGS)
|
||||
add_compile_options(-Werror -Wextra
|
||||
add_compile_options(-Wextra
|
||||
-Walloc-zero -Wcast-align -Wcast-qual -Wduplicated-branches -Wduplicated-cond
|
||||
-Wfloat-equal -Wlogical-op -Wnull-dereference -Wold-style-cast -Wshift-overflow=2
|
||||
-Wstringop-overflow=4 -Wtrampolines -Wundef -Wuninitialized -Wunused -Wshadow
|
||||
-Wformat=2 -Wformat-overflow=2 -Wformat-truncation=1
|
||||
-Wno-format-nonliteral -Wno-strict-overflow
|
||||
-Wno-unused-but-set-variable # bison's `yynerrs_` is incremented but unused
|
||||
-Wno-type-limits -Wno-tautological-constant-out-of-range-compare
|
||||
-Wvla # MSVC does not support VLAs
|
||||
-Wno-unknown-warning-option) # Clang shouldn't diagnose unknown warnings
|
||||
-Wvla) # MSVC does not support VLAs
|
||||
endif()
|
||||
|
||||
add_compile_options(-Wno-unused-but-set-variable # bison's `yynerrs_` is incremented but unused
|
||||
# C++20 allows macros to take zero variadic arguments.
|
||||
# Some versions of Clang don't recognize this, and treat them as a GNU extension.
|
||||
-Wno-gnu-zero-variadic-macro-arguments)
|
||||
|
||||
if(MINGW)
|
||||
# MinGW warns about format specifiers like `%z` and `%j`, which are missing on Windows XP and earlier.
|
||||
# We rely on runtimes more modern than that, so we can ignore those warnings.
|
||||
# If this is a problem for you: use Microsoft's equivalents, or define `__USE_MINGW_ANSI_STDIO=1`.
|
||||
# https://sourceforge.net/p/mingw-w64/wiki2/gnu%20printf/
|
||||
add_compile_options(-Wno-error=format -Wno-error=format-extra-args)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Use versioning consistent with Makefile
|
||||
# the git revision is used but uses the fallback in an archive
|
||||
|
||||
find_program(GIT git)
|
||||
if(GIT)
|
||||
execute_process(COMMAND ${GIT} --git-dir=.git -c safe.directory='*' describe --tags --dirty --always
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_REV OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET)
|
||||
message(STATUS "RGBDS version: ${GIT_REV}")
|
||||
else(GIT)
|
||||
message(STATUS "Cannot determine RGBDS version (Git not installed), falling back")
|
||||
endif(GIT)
|
||||
|
||||
find_package(PkgConfig)
|
||||
if(MSVC OR NOT PKG_CONFIG_FOUND)
|
||||
# fallback to find_package
|
||||
# cmake's FindPNG is very fragile; it breaks when multiple versions are installed
|
||||
# this is most evident on macOS but can occur on Linux too
|
||||
find_package(PNG REQUIRED)
|
||||
else()
|
||||
pkg_check_modules(LIBPNG REQUIRED libpng)
|
||||
endif()
|
||||
|
||||
include_directories("${PROJECT_SOURCE_DIR}/include")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
|
||||
add_subdirectory(src)
|
||||
set(CMAKE_CTEST_ARGUMENTS "--verbose")
|
||||
add_subdirectory(test)
|
||||
|
||||
# By default, build in Release mode; Debug mode must be explicitly requested
|
||||
# (You may want to augment it with the options above)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
message(CHECK_START "Checking if LTO is supported")
|
||||
include(CheckIPOSupported)
|
||||
check_ipo_supported(RESULT enable_lto)
|
||||
if(enable_lto)
|
||||
message(CHECK_PASS "yes")
|
||||
set_property(TARGET rgbasm rgblink rgbfix rgbgfx PROPERTY INTERPROCEDURAL_OPTIMIZATION ON)
|
||||
else()
|
||||
message(CHECK_FAIL "no")
|
||||
endif()
|
||||
if(BUILD_TESTING) # This option is defined implicitly by `include(CTest)`.
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
set(MANDIR "share/man")
|
||||
set(man1 "man/rgbasm.1"
|
||||
"man/rgbfix.1"
|
||||
"man/rgbgfx.1"
|
||||
@@ -129,6 +206,26 @@ set(man7 "man/gbz80.7"
|
||||
"man/rgbds.7")
|
||||
|
||||
foreach(SECTION "man1" "man5" "man7")
|
||||
set(DEST "${MANDIR}/${SECTION}")
|
||||
install(FILES ${${SECTION}} DESTINATION ${DEST})
|
||||
install(FILES ${${SECTION}} DESTINATION "${CMAKE_INSTALL_MANDIR}/${SECTION}" COMPONENT man)
|
||||
endforeach()
|
||||
|
||||
## Packaging.
|
||||
|
||||
# We only specify here the package-agnostic options;
|
||||
# the rest is rather convention from our side, and thus more appropriate for presets or CLI flags.
|
||||
## CPACK_PACKAGE_NAME: copied from `project()`
|
||||
set(CPACK_PACKAGE_VENDOR "GBDev")
|
||||
set(CPACK_PACKAGE_VERSION "${CMAKE_PROJECT_VERSION}") # The individual components are defined implicitly.
|
||||
set(CPACK_PACKAGE_DESCRIPTION "An assembly toolchain for the Nintendo Game Boy and Game Boy Color") # Same as our repo's description.
|
||||
## CPACK_PACKAGE_DESCRIPTION_SUMMARY: copied from `project()`
|
||||
set(CPACK_PACKAGE_HOMEPAGE_URL "https://rgbds.gbdev.io")
|
||||
## CPACK_PACKAGE_FILE_NAME: should be provided at runtime (`cpack -P`)
|
||||
set(CPACK_PACKAGE_CHECKSUM SHA256)
|
||||
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
|
||||
set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/README.md")
|
||||
set(CPACK_STRIP_FILES ON) # Only applies to binary packages, not sources.
|
||||
set(CPACK_VERBATIM_VARIABLES ON)
|
||||
set(CPACK_THREADS 0) # Use all available CPU cores.
|
||||
set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY OFF)
|
||||
|
||||
include(CPack)
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"version": 3,
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "develop",
|
||||
"description": "Debugging config for maintainers",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"CMAKE_EXPORT_COMPILE_COMMANDS": true,
|
||||
"CMAKE_COMPILE_WARNING_AS_ERROR": true,
|
||||
"CMAKE_LINK_WARNING_AS_ERROR": true,
|
||||
"MORE_WARNINGS": true,
|
||||
"SANITIZERS": true
|
||||
},
|
||||
"warnings": { "uninitialized": true },
|
||||
"errors": { "dev": true }
|
||||
},
|
||||
{
|
||||
"name": "develop-msbuild32",
|
||||
"inherits": ["develop", "msbuild32"]
|
||||
},
|
||||
{
|
||||
"name": "develop-msbuild64",
|
||||
"inherits": ["develop", "msbuild64"]
|
||||
},
|
||||
|
||||
{
|
||||
"name": "msbuild32",
|
||||
"description": "Flags for building 32-bit executables with MSBuild",
|
||||
"architecture": "Win32"
|
||||
},
|
||||
{
|
||||
"name": "msbuild64",
|
||||
"description": "Flags for building 64-bit executables with MSBuild",
|
||||
"architecture": "x64"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "macos-static",
|
||||
"description": "Flags for building an executable compatible with old macOS",
|
||||
"cacheVariables": {
|
||||
"CMAKE_PROJECT_TOP_LEVEL_INCLUDES": "cmake/macos-static.cmake"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+12
-8
@@ -1,21 +1,25 @@
|
||||
FROM debian:12-slim
|
||||
FROM debian:13-slim
|
||||
LABEL org.opencontainers.image.source=https://github.com/gbdev/rgbds
|
||||
ARG version=1.0.1
|
||||
ARG version=1.0.2
|
||||
WORKDIR /rgbds
|
||||
|
||||
COPY . .
|
||||
|
||||
# Install dependencies
|
||||
RUN apt-get update && \
|
||||
apt-get install sudo make cmake gcc build-essential -y
|
||||
RUN ./.github/scripts/install-deps.sh debian
|
||||
|
||||
# Install dependencies and compile RGBDS
|
||||
RUN ./.github/scripts/install_deps.sh ubuntu-22.04
|
||||
RUN make -j CXXFLAGS="-O3 -flto -DNDEBUG -static" PKG_CONFIG="pkg-config --static" Q=
|
||||
# Build RGBDS
|
||||
RUN make -j "$(getconf _NPROCESSORS_ONLN)" CXXFLAGS="-O3 -flto -DNDEBUG -static" PKG_CONFIG="pkg-config --static" Q=
|
||||
|
||||
# Create an archive with the compiled executables and all the necessary to install it,
|
||||
# Create the install script
|
||||
RUN make install.sh Q=
|
||||
|
||||
# Create an archive with the compiled executables, man pages, and install script,
|
||||
# so it can be copied outside of the container and installed/used in another system
|
||||
RUN tar caf rgbds-linux-x86_64.tar.xz --transform='s#.*/##' rgbasm rgblink rgbfix rgbgfx man/* .github/scripts/install.sh
|
||||
RUN tar caf rgbds-linux-x86_64.tar.xz --transform='s#.*/##' rgbasm rgblink rgbfix rgbgfx man/* install.sh
|
||||
|
||||
# Install RGBDS on the container so all the executables will be available in the PATH
|
||||
RUN cp man/* .
|
||||
RUN ./.github/scripts/install.sh
|
||||
RUN make install Q=
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .cpp .y .o
|
||||
|
||||
.PHONY: all clean install checkdiff develop debug profile coverage format tidy iwyu mingw32 mingw64 wine-shim dist
|
||||
.PHONY: all clean install develop debug profile coverage format tidy iwyu wine-shim dist
|
||||
|
||||
# User-defined variables
|
||||
|
||||
@@ -15,6 +15,7 @@ SUFFIX :=
|
||||
STRIP := -s
|
||||
BINMODE := 755
|
||||
MANMODE := 644
|
||||
MANSRC := man/
|
||||
|
||||
# Other variables
|
||||
|
||||
@@ -26,25 +27,25 @@ PNGLDLIBS := `${PKG_CONFIG} --libs-only-l libpng`
|
||||
# Note: if this comes up empty, `version.cpp` will automatically fall back to last release number
|
||||
VERSION_STRING := `git --git-dir=.git -c safe.directory='*' describe --tags --dirty --always 2>/dev/null`
|
||||
|
||||
WARNFLAGS := -Wall -pedantic -Wno-unknown-warning-option -Wno-gnu-zero-variadic-macro-arguments
|
||||
WARNFLAGS := -Wall -pedantic -Wno-unknown-warning-option \
|
||||
-Wno-gnu-zero-variadic-macro-arguments -Wno-unused-but-set-variable
|
||||
|
||||
# Overridable CXXFLAGS
|
||||
CXXFLAGS ?= -O3 -flto -DNDEBUG
|
||||
# Non-overridable CXXFLAGS
|
||||
REALCXXFLAGS := ${CXXFLAGS} ${WARNFLAGS} -std=c++20 -I include -fno-exceptions -fno-rtti
|
||||
REQUIREDCXXFLAGS := -std=c++20 -I include -fno-exceptions -fno-rtti
|
||||
REALCXXFLAGS := ${CXXFLAGS} ${WARNFLAGS} ${REQUIREDCXXFLAGS}
|
||||
# Overridable LDFLAGS
|
||||
LDFLAGS ?=
|
||||
# Non-overridable LDFLAGS
|
||||
REALLDFLAGS := ${LDFLAGS} ${WARNFLAGS} -DBUILD_VERSION_STRING=\"${VERSION_STRING}\"
|
||||
REQUIREDLDFLAGS := -DBUILD_VERSION_STRING=\"${VERSION_STRING}\"
|
||||
REALLDFLAGS := ${LDFLAGS} ${WARNFLAGS} ${REQUIREDLDFLAGS}
|
||||
|
||||
# Wrapper around bison that passes flags depending on what the version supports
|
||||
BISON := src/bison.sh
|
||||
|
||||
RM := rm -rf
|
||||
|
||||
# Used for checking pull requests
|
||||
BASE_REF := origin/master
|
||||
|
||||
# Rules to build the RGBDS binaries
|
||||
|
||||
all: rgbasm rgblink rgbfix rgbgfx
|
||||
@@ -64,6 +65,7 @@ rgbasm_obj := \
|
||||
src/asm/fixpoint.o \
|
||||
src/asm/format.o \
|
||||
src/asm/fstack.o \
|
||||
src/asm/intern.o \
|
||||
src/asm/lexer.o \
|
||||
src/asm/macro.o \
|
||||
src/asm/main.o \
|
||||
@@ -182,6 +184,11 @@ src/gfx/rgba.o: src/gfx/rgba.cpp
|
||||
.cpp.o:
|
||||
$Q${CXX} ${REALCXXFLAGS} -c -o $@ $<
|
||||
|
||||
install.sh:
|
||||
$Qecho '#!/usr/bin/env bash' > $@
|
||||
$Q${MAKE} -s -n install MANSRC= >> $@
|
||||
$Qchmod +x $@
|
||||
|
||||
# Target used to remove all files generated by other Makefile targets
|
||||
clean:
|
||||
$Q${RM} rgbasm rgbasm.exe
|
||||
@@ -190,7 +197,7 @@ clean:
|
||||
$Q${RM} rgbgfx rgbgfx.exe
|
||||
$Qfind src/ -name "*.o" -exec rm {} \;
|
||||
$Qfind . -type f \( -name "*.gcno" -o -name "*.gcda" -o -name "*.gcov" \) -exec rm {} \;
|
||||
$Q${RM} rgbshim.sh
|
||||
$Q${RM} install.sh rgbshim.sh
|
||||
$Q${RM} src/asm/parser.cpp src/asm/parser.hpp src/asm/stack.hh
|
||||
$Q${RM} src/link/script.cpp src/link/script.hpp src/link/stack.hh
|
||||
$Q${RM} test/gfx/randtilegen test/gfx/rgbgfx_test
|
||||
@@ -202,13 +209,9 @@ install: all
|
||||
$Qinstall ${STRIP} -m ${BINMODE} rgblink ${DESTDIR}${bindir}/rgblink${SUFFIX}
|
||||
$Qinstall ${STRIP} -m ${BINMODE} rgbfix ${DESTDIR}${bindir}/rgbfix${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/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/
|
||||
|
||||
# Target used to check for suspiciously missing changed files.
|
||||
checkdiff:
|
||||
$Qcontrib/checkdiff.bash `git merge-base HEAD ${BASE_REF}`
|
||||
$Qinstall -m ${MANMODE} ${MANSRC}rgbasm.1 ${MANSRC}rgblink.1 ${MANSRC}rgbfix.1 ${MANSRC}rgbgfx.1 ${DESTDIR}${mandir}/man1/
|
||||
$Qinstall -m ${MANMODE} ${MANSRC}rgbds.5 ${MANSRC}rgbasm.5 ${MANSRC}rgbasm-old.5 ${MANSRC}rgblink.5 ${DESTDIR}${mandir}/man5/
|
||||
$Qinstall -m ${MANMODE} ${MANSRC}rgbds.7 ${MANSRC}gbz80.7 ${DESTDIR}${mandir}/man7/
|
||||
|
||||
# Target used in development to prevent adding new issues to the source code.
|
||||
# All warnings are treated as errors to block the compilation and make the
|
||||
@@ -232,23 +235,27 @@ debug:
|
||||
CXXFLAGS="-ggdb3 -O0 -fno-omit-frame-pointer -fno-optimize-sibling-calls"
|
||||
|
||||
# Target used in development to profile with callgrind.
|
||||
# Use `valgrind --tool=callgrind --dump-instr=yes --simulate-cache=yes --collect-jumps=yes ./rgbasm ...`.
|
||||
profile:
|
||||
$Qenv ${MAKE} \
|
||||
CXXFLAGS="-ggdb3 -O3 -fno-omit-frame-pointer -fno-optimize-sibling-calls"
|
||||
|
||||
# Target used in development to inspect code coverage with gcov.
|
||||
# Use `./contrib/coverage.bash`.
|
||||
coverage:
|
||||
$Qenv ${MAKE} \
|
||||
CXXFLAGS="-ggdb3 -Og --coverage -fno-omit-frame-pointer -fno-optimize-sibling-calls"
|
||||
|
||||
# Target used in development to format source code with clang-format.
|
||||
format:
|
||||
$Qclang-format -i $$(git ls-files '*.hpp' '*.cpp')
|
||||
$Qclang-format -i $$(git ls-files '*.[hc]pp')
|
||||
|
||||
# Target used in development to check code with clang-tidy.
|
||||
# Requires Bison-generated header files to exist.
|
||||
tidy: src/asm/parser.hpp src/link/script.hpp
|
||||
$Qclang-tidy -p . $$(git ls-files '*.hpp' '*.cpp')
|
||||
tidy: src/asm/parser.hpp src/link/script.hpp compile_flags.txt
|
||||
$Qclang-tidy -p . $$(git ls-files '*.[hc]pp')
|
||||
compile_flags.txt:
|
||||
$Qprintf '%s\n' ${REQUIREDCXXFLAGS} ${REQUIREDLDFLAGS} >$@
|
||||
|
||||
# Target used in development to remove unused `#include` headers.
|
||||
iwyu:
|
||||
@@ -256,22 +263,7 @@ iwyu:
|
||||
CXX="include-what-you-use" \
|
||||
REALCXXFLAGS="-std=c++20 -I include"
|
||||
|
||||
# Targets for the project maintainer to easily create Windows exes.
|
||||
# This is not for Windows users!
|
||||
# If you're building on Windows with Cygwin or MinGW, just follow the Unix
|
||||
# install instructions instead.
|
||||
|
||||
mingw32:
|
||||
$Q${MAKE} all test/gfx/randtilegen test/gfx/rgbgfx_test \
|
||||
CXX=i686-w64-mingw32-g++ \
|
||||
CXXFLAGS="-O3 -flto -DNDEBUG -static-libgcc -static-libstdc++" \
|
||||
PKG_CONFIG="PKG_CONFIG_SYSROOT_DIR=/usr/i686-w64-mingw32 pkg-config"
|
||||
|
||||
mingw64:
|
||||
$Q${MAKE} all test/gfx/randtilegen test/gfx/rgbgfx_test \
|
||||
CXX=x86_64-w64-mingw32-g++ \
|
||||
PKG_CONFIG="PKG_CONFIG_SYSROOT_DIR=/usr/x86_64-w64-mingw32 pkg-config"
|
||||
|
||||
# Target used in development to conveniently invoke RGBDS binaries with Wine.
|
||||
wine-shim:
|
||||
$Qecho '#!/usr/bin/env bash' > rgbshim.sh
|
||||
$Qecho 'WINEDEBUG=-all wine $$0.exe "$${@:1}"' >> rgbshim.sh
|
||||
@@ -283,7 +275,6 @@ wine-shim:
|
||||
|
||||
# Target for the project maintainer to produce distributable release tarballs
|
||||
# of the source code.
|
||||
|
||||
dist:
|
||||
$Qgit ls-files | sed s~^~$${PWD##*/}/~ \
|
||||
| tar -czf rgbds-source.tar.gz -C .. -T -
|
||||
|
||||
@@ -1,33 +1,39 @@
|
||||
# RGBDS
|
||||
|
||||
RGBDS (Rednex Game Boy Development System) is a free assembler/linker package
|
||||
for the Game Boy and Game Boy Color. It consists of:
|
||||
[RGBDS](https://github.com/gbdev/rgbds) (Rednex Game Boy Development System) is
|
||||
a free assembler/linker package for the Game Boy and Game Boy Color.
|
||||
|
||||
- RGBASM (assembler)
|
||||
- RGBLINK (linker)
|
||||
- RGBFIX (checksum/header fixer)
|
||||
- RGBGFX (PNG-to-Game Boy graphics converter)
|
||||
It consists of four tools:
|
||||
|
||||
This is a fork of the original RGBDS which aims to make the programs more like
|
||||
other UNIX tools.
|
||||
- **RGBASM:** assembler
|
||||
- **RGBLINK:** linker
|
||||
- **RGBFIX:** ROM checksum/header fixer
|
||||
- **RGBGFX:** PNG-to-Game Boy graphics converter
|
||||
|
||||
This toolchain is maintained [on GitHub](https://github.com/gbdev/rgbds).
|
||||
## Documentation
|
||||
|
||||
The documentation of this toolchain can be [viewed online](https://rgbds.gbdev.io/docs/),
|
||||
including its [basic usage and development history](https://rgbds.gbdev.io/docs/rgbds.7).
|
||||
It is generated from the man pages found in this repository.
|
||||
The source code of the website itself is on GitHub as well under the repository
|
||||
[rgbds-www](https://github.com/gbdev/rgbds-www).
|
||||
The [full documentation](https://rgbds.gbdev.io/docs/) is available online,
|
||||
including the [development history](https://rgbds.gbdev.io/docs/rgbds.7) and
|
||||
[version history](https://rgbds.gbdev.io/versions), generated from the man pages
|
||||
in this repository.
|
||||
|
||||
If you want to contribute or maintain RGBDS, read [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
If you have questions regarding the code, its organization, etc. you can find the maintainers
|
||||
[on the GBDev community channels](https://gbdev.io/chat) or via mail at `rgbds at gbdev dot io`.
|
||||
The documentation website's own source code is available in the
|
||||
[rgbds-www](https://github.com/gbdev/rgbds-www) repository.
|
||||
|
||||
## Installing RGBDS
|
||||
## Community
|
||||
|
||||
The [installation procedure](https://rgbds.gbdev.io/install) is available
|
||||
online for various platforms. [Building from source](https://rgbds.gbdev.io/install/source)
|
||||
is possible using `make` or `cmake`; follow the link for more detailed instructions.
|
||||
If you have questions about the RGBDS code or its organization, you can contact
|
||||
the maintainers on the [GBDev Discord server](https://discord.gg/RjJKA8wrD4) or
|
||||
[other community channels](https://gbdev.io/chat), or via email at
|
||||
`rgbds at gbdev dot io`.
|
||||
|
||||
If you want to help maintain RGBDS, please read [the contribution guide](/docs/CONTRIBUTING.md).
|
||||
|
||||
## Installing
|
||||
|
||||
The [platform-specific installation instructions](https://rgbds.gbdev.io/install/)
|
||||
are available online. To [build from source](https://rgbds.gbdev.io/install/source)
|
||||
using `make` or `cmake`, briefly:
|
||||
|
||||
```sh
|
||||
make
|
||||
@@ -40,8 +46,8 @@ cmake --build build
|
||||
cmake --install build
|
||||
```
|
||||
|
||||
Two parameters available when building are a prefix (e.g. to put the executables in a directory)
|
||||
and a suffix (e.g. to append the version number or commit ID).
|
||||
Both build systems support a prefix (e.g. to install into a specific directory)
|
||||
and a suffix (e.g. to append the version number or commit ID):
|
||||
|
||||
```sh
|
||||
make
|
||||
@@ -54,4 +60,4 @@ cmake --build build
|
||||
cmake --install build --prefix install_dir
|
||||
```
|
||||
|
||||
(If you set a `SUFFIX`, it should include the `.exe` extension on Windows.)
|
||||
On Windows, any `SUFFIX` should include the `.exe` extension.
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# This file declares the dependencies we use, using `FetchContent`.
|
||||
# https://cmake.org/cmake.help/latest/guide/using-dependencies/index.html#downloading-and-building-from-source-with-fetchcontent
|
||||
# These are kept in a separate file so that it can be hashed as a key for our CI's `actions/cache`.
|
||||
|
||||
FetchContent_Declare(PNG
|
||||
URL https://download.sourceforge.net/libpng/libpng-1.6.58.tar.xz
|
||||
URL_HASH SHA256=28eb403f51f0f7405249132cecfe82ea5c0ef97f1b32c5a65828814ae0d34775
|
||||
EXCLUDE_FROM_ALL # We only install the runtime dependencies, and do so separately.
|
||||
FIND_PACKAGE_ARGS 1.5.4)
|
||||
|
||||
set(PNG_TESTS OFF CACHE INTERNAL "") # We do not need libpng's tests or tools
|
||||
set(PNG_TOOLS OFF CACHE INTERNAL "") # (and they can even cause compile errors!)
|
||||
set(PNG_SHARED OFF CACHE INTERNAL "")
|
||||
set(PNG_STATIC ON CACHE INTERNAL "")
|
||||
|
||||
FetchContent_Declare(ZLIB
|
||||
URL https://www.zlib.net/zlib-1.3.2.tar.xz
|
||||
URL_HASH SHA256=d7a0654783a4da529d1bb793b7ad9c3318020af77667bcae35f95d0e42a792f3
|
||||
EXCLUDE_FROM_ALL # We only install the runtime dependencies, and do so separately.
|
||||
# libpng documents requiring "zlib 1.0.4 or later (1.2.13 or later recommended for performance and security reasons)".
|
||||
# We thus enforce 1.0.4, but note that the libpng source code mentions that "it may work with versions as old as zlib 0.95".
|
||||
FIND_PACKAGE_ARGS 1.0.4)
|
||||
|
||||
set(ZLIB_BUILD_SHARED OFF CACHE INTERNAL "")
|
||||
set(ZLIB_BUILD_STATIC ON CACHE INTERNAL "")
|
||||
@@ -0,0 +1,33 @@
|
||||
# This file is meant to be included at the project level,
|
||||
# in order to generate executables compatible with old macOS versions.
|
||||
# See our `macos-static` CMake preset for how it's meant to be used.
|
||||
|
||||
# Note that targeting old enough versions of Mac OS X on recent enough versions of macOS
|
||||
# triggers some poorly-tested code paths within Apple's linker, which then crashes.
|
||||
# This can be worked around by using LLVM's LLD linker and passing `-fuse-ld=lld` when linking.
|
||||
|
||||
# The `-mmacosx-version-min=10.4` flag ensures that the binary only uses APIs available on Mac OS X 10.4 Tiger.
|
||||
# The `-arch` flags build a "fat binary" that works on both Apple architectures:
|
||||
# older Intel x64 Macs and newer ARM "Apple Silicon" ones.
|
||||
set(secret_sauce -mmacosx-version-min=10.4 "SHELL:-arch x86_64" "SHELL:-arch arm64") # Avoid `-arch` being dedup'd.
|
||||
add_compile_options(${secret_sauce})
|
||||
add_link_options(${secret_sauce})
|
||||
set(PNG_HARDWARE_OPTIMIZATIONS OFF) # These do not play well with a dual-arch build.
|
||||
|
||||
# Mac OS X has always provided zlib, so we can safely link dynamically against it.
|
||||
# However, libpng is *not* provided by default, so we link it statically, which requires downloading and building it from source.
|
||||
set(PNG_SHARED OFF)
|
||||
set(PNG_STATIC ON)
|
||||
# If libpng is already available (e.g. via Homebrew), we ignore that and still build our own.
|
||||
set(FETCHCONTENT_TRY_FIND_PACKAGE_MODE NEVER)
|
||||
# But we still want to attempt linking against the system's zlib.
|
||||
function(rgbds_provide_dependency method dep_name)
|
||||
if(dep_name STREQUAL "ZLIB")
|
||||
find_package(ZLIB)
|
||||
if(ZLIB_FOUND)
|
||||
FetchContent_SetPopulated(ZLIB)
|
||||
endif()
|
||||
endif()
|
||||
endfunction(rgbds_provide_dependency)
|
||||
cmake_language(SET_DEPENDENCY_PROVIDER rgbds_provide_dependency
|
||||
SUPPORTED_METHODS FETCHCONTENT_MAKEAVAILABLE_SERIAL)
|
||||
@@ -0,0 +1,22 @@
|
||||
# From https://www.mingw-w64.org/build-systems/cmake/
|
||||
|
||||
set(CMAKE_SYSTEM_NAME Windows)
|
||||
set(CMAKE_SYSTEM_PROCESSOR i686)
|
||||
|
||||
# Specify the cross-compiler.
|
||||
set(CMAKE_C_COMPILER i686-w64-mingw32-gcc)
|
||||
set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++)
|
||||
set(CMAKE_RC_COMPILER i686-w64-mingw32-windres)
|
||||
|
||||
# Specify the target environment.
|
||||
set(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32)
|
||||
|
||||
# Search for programs in the build host directories.
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
# Search for libraries and headers in the target directories.
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
|
||||
# CMake determines how to examine dependencies based on the *host* system, leading to
|
||||
# a "file unknown error" unless the target platform is explicitly specified.
|
||||
set(CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM "windows+pe")
|
||||
@@ -0,0 +1,22 @@
|
||||
# From https://www.mingw-w64.org/build-systems/cmake/
|
||||
|
||||
set(CMAKE_SYSTEM_NAME Windows)
|
||||
set(CMAKE_SYSTEM_PROCESSOR x86_64)
|
||||
|
||||
# Specify the cross-compiler.
|
||||
set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
|
||||
set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
|
||||
set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
|
||||
|
||||
# Specify the target environment.
|
||||
set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32)
|
||||
|
||||
# Search for programs in the build host directories.
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
# Search for libraries and headers in the target directories.
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
|
||||
# CMake determines how to examine dependencies based on the *host* system, leading to
|
||||
# a "file unknown error" unless the target platform is explicitly specified.
|
||||
set(CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM "windows+pe")
|
||||
@@ -1,6 +0,0 @@
|
||||
-std=c++20
|
||||
-I
|
||||
include
|
||||
-fno-exceptions
|
||||
-fno-rtti
|
||||
-fno-caret-diagnostics
|
||||
@@ -142,6 +142,7 @@ _rgblink_completions() {
|
||||
mapfile -t COMPREPLY < <(compgen -W "
|
||||
assert
|
||||
div
|
||||
large-constant
|
||||
obsolete
|
||||
shift
|
||||
shift-amount
|
||||
|
||||
+56
-16
@@ -1,17 +1,55 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
set -eu
|
||||
|
||||
usage() {
|
||||
cat <<"EOF"
|
||||
Generates LCOV code coverage report for RGBDS.
|
||||
Options:
|
||||
-h, --help show this help message
|
||||
-o, --open open the HTML report in the preferred application
|
||||
--jobs <n> build RGBDS and external codebases with `make -j<n>`
|
||||
--os <os> skip tests known to fail on <os> (e.g. `macos-14`)
|
||||
EOF
|
||||
}
|
||||
|
||||
# Parse options in pure Bash because macOS `getopt` is stuck
|
||||
# in what util-linux `getopt` calls `GETOPT_COMPATIBLE` mode
|
||||
make_args=()
|
||||
runtests_args=()
|
||||
open_report=false
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
-o|--open)
|
||||
open_report=true
|
||||
;;
|
||||
--jobs)
|
||||
shift
|
||||
make_args+=(-j "$1")
|
||||
runtests_args+=(--jobs "$1")
|
||||
;;
|
||||
--os)
|
||||
shift
|
||||
runtests_args+=(--os "$1")
|
||||
;;
|
||||
*)
|
||||
echo "$(basename "$0"): unknown option '$1'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# Build RGBDS with gcov support
|
||||
make coverage -j
|
||||
make coverage "${make_args[@]}"
|
||||
|
||||
# Run the tests
|
||||
pushd test
|
||||
./fetch-test-deps.sh
|
||||
if [[ $# -eq 0 ]]; then
|
||||
./run-tests.sh
|
||||
else
|
||||
./run-tests.sh --os "$1"
|
||||
fi
|
||||
external/fetch-repos.sh
|
||||
./run-tests.sh "${runtests_args[@]}"
|
||||
popd
|
||||
|
||||
# Generate coverage logs
|
||||
@@ -24,12 +62,14 @@ lcov -c --no-external -d . -o "$COVERAGE_INFO"
|
||||
lcov -r "$COVERAGE_INFO" src/asm/parser.{hpp,cpp} src/link/script.{hpp,cpp} -o "$COVERAGE_INFO"
|
||||
genhtml --dark-mode --num-spaces 4 -f -s -o coverage/ "$COVERAGE_INFO"
|
||||
|
||||
# Check whether running from coverage.yml workflow
|
||||
if [ "$1" != "ubuntu-ci" ]; then
|
||||
# Open report in web browser
|
||||
if [ "$(uname)" == "Darwin" ]; then
|
||||
open coverage/index.html
|
||||
else
|
||||
xdg-open coverage/index.html
|
||||
fi
|
||||
if "$open_report"; then
|
||||
# Open the report in the preferred web browser
|
||||
if [ "$(uname)" == "Darwin" ]; then
|
||||
open coverage/index.html
|
||||
else
|
||||
xdg-open coverage/index.html
|
||||
fi
|
||||
else
|
||||
# Output the path to the report
|
||||
echo "Generated LCOV report at $PWD/coverage/index.html"
|
||||
fi
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -ne 2 ]]; then
|
||||
|
||||
@@ -9,6 +9,7 @@ _rgblink_warnings() {
|
||||
|
||||
'assert:Warn when WARN-type asserts fail'
|
||||
'div:Warn when dividing the smallest int by -1'
|
||||
'large-constant:Warn on constants too large for a signed 32-bit int'
|
||||
'obsolete:Warn when using deprecated features'
|
||||
'shift:Warn when shifting negative values'
|
||||
'shift-amount:Warn when a shift'\''s operand is negative or \> 32'
|
||||
|
||||
@@ -28,11 +28,15 @@ rgbds/
|
||||
│ │ └── ...
|
||||
│ └── workflows/
|
||||
│ └── ...
|
||||
├── cmake/
|
||||
│ └── ...
|
||||
├── contrib/
|
||||
│ ├── bash_compl/
|
||||
│ ├── zsh_compl/
|
||||
│ │ └── ...
|
||||
│ └── ...
|
||||
├── docs/
|
||||
│ └── ...
|
||||
├── include/
|
||||
│ └── ...
|
||||
├── man/
|
||||
@@ -52,13 +56,15 @@ rgbds/
|
||||
│ ├── bison.sh
|
||||
│ └── ...
|
||||
├── test/
|
||||
│ ├── fetch-test-deps.sh
|
||||
│ ├── run-tests.sh
|
||||
│ ├── external/
|
||||
│ │ ├── fetch-repos.sh
|
||||
│ │ └── ...
|
||||
│ └── ...
|
||||
├── .clang-format
|
||||
├── .clang-tidy
|
||||
├── CMakeLists.txt
|
||||
├── compile_flags.txt
|
||||
├── CMakePresets.json
|
||||
├── Dockerfile
|
||||
└── Makefile
|
||||
```
|
||||
@@ -69,12 +75,20 @@ rgbds/
|
||||
Scripts used by GitHub Actions workflow files.
|
||||
* **`workflows/`:**
|
||||
GitHub Actions CI workflow description files. Used for automated testing, deployment, etc.
|
||||
- **`cmake/`**:
|
||||
Files relevant to our CMake build system that are not required to be somewhere else (e.g. `CMakePresets.json` *has* to be at the root).
|
||||
- **`contrib/`:**
|
||||
Scripts and other resources which may be useful to RGBDS users and developers.
|
||||
* **`bash_compl/`:**
|
||||
Tab completion scripts for use with `bash`. Run them with `source` somewhere in your `.bashrc`, and they should auto-load when you open a shell.
|
||||
* **`zsh_compl/`:**
|
||||
Tab completion scripts for use with `zsh`. Put them somewhere in your `fpath`, and they should auto-load when you open a shell.
|
||||
- **`docs/`:**
|
||||
Documentation related to the development and maintenance of RGBDS itself.
|
||||
(The directory's name, as well as some of its file's names, are
|
||||
[recognized by GitHub](https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/setting-guidelines-for-repository-contributors)
|
||||
and linked to in parts of its UI.)
|
||||
Documentation for RGBDS itself exists as manual pages in `man/`.
|
||||
- **`include/`:**
|
||||
Header files for the respective source files in `src`.
|
||||
- **`man/`:**
|
||||
@@ -97,18 +111,23 @@ rgbds/
|
||||
Script used to run the Bison parser generator with the latest flags that the user's version supports.
|
||||
- **`test/`:**
|
||||
Testing framework used to verify that changes to the code don't break or modify the behavior of RGBDS.
|
||||
* **`fetch-test-deps.sh`:**
|
||||
Script used to fetch dependencies for building external repositories. `fetch-test-deps.sh --help` describes its options.
|
||||
The `test.sh` scripts inside each of the subdirectories are the individual test drivers.
|
||||
* **`run-tests.sh`:**
|
||||
Script used to run tests, including internal test cases and external repositories. `run-tests.sh --help` describes its options.
|
||||
* **`external/`:**
|
||||
Directory for third-party repos making use of RGBDS, which get cloned here and built during testing.
|
||||
- **`fetch-repos.sh`:**
|
||||
Script used to clone the external repositories. `fetch-repos.sh --help` describes its options.
|
||||
- **`*.cfg`:**
|
||||
Each `.cfg` file defines the parameters to clone, build, and verify an external repository.
|
||||
- **`.clang-format`:**
|
||||
Code style for automated C++ formatting with [`clang-format`](https://clang.llvm.org/docs/ClangFormat.html) (for which we define the shortcut `make format`).
|
||||
- **`.clang-tidy`:**
|
||||
Configuration for C++ static analysis with [`clang-tidy`](https://clang.llvm.org/extra/clang-tidy/) (for which we define the shortcut `make tidy`).
|
||||
- **`CMakeLists.txt`:**
|
||||
Defines how to build RGBDS with CMake.
|
||||
- **`compile_flags.txt`:**
|
||||
Compiler flags for `clang-tidy`.
|
||||
- **`CMakePresets.json`:**
|
||||
Defines some [presets](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html) useful for working with our CMake.
|
||||
- **`Dockerfile`:**
|
||||
Defines how to build RGBDS with Docker (which we do in CI to provide a [container image](https://github.com/gbdev/rgbds/pkgs/container/rgbds)).
|
||||
- **`Makefile`:**
|
||||
@@ -165,10 +184,13 @@ These files have been copied ("vendored") from external authors and adapted for
|
||||
- **`fstack.cpp`:**
|
||||
Functions and data related to "fstack" nodes (the contents of top-level or `INCLUDE`d files, macro expansions, or `REPT`/`FOR` loop iterations) and their "contexts" (metadata that is only relevant while a node's content is being lexed and parsed).
|
||||
This file *owns* the `Context`s in its `contextStack` collection. Each of those `Context`s *owns* its `LexerState`, and *refers* to its `FileStackNode`, `uniqueIDStr`, and `macroArgs`. Each `FileStackNode` also *references* its `parent`.
|
||||
- **`intern.cpp`:**
|
||||
[Interned strings](https://en.wikipedia.org/wiki/String_interning), which are used for the names of keywords, symbols (labels, constants, variables, string constants, macros, etc), and charmaps.
|
||||
This file *owns* the strings in its `internedStrings` collection, which its `internedIndexes` collection *references* by view and index.
|
||||
- **`lexer.cpp`:**
|
||||
Functions and data related to [lexing](https://en.wikipedia.org/wiki/Lexical_analysis) assembly source code into tokens, which can then be parsed.
|
||||
This file maintains static `lexerState` and `lexerStateEOL` pointers to `LexerState`s from the `Context`s in `fstack.cpp`.
|
||||
Each `LexerState` *owns* its `content` and its `expansions`' content. Each `Expansion` (the contents of an `{interpolation}` or macro argument) in turn *owns* its `contents`.
|
||||
Each `LexerState` *owns* its `content` and its `expansionStack`'s content. Each `Expansion` (the contents of an `{interpolation}` or macro argument) in turn *owns* its `contents`.
|
||||
The lexer and parser are interdependent: when the parser reaches certain tokens, it changes the lexer's mode, which affects how characters get lexed into tokens. For example, when the parser reaches a macro name, it changes the lexer to "raw" mode, which lexes the rest of the line as a sequence of string arguments to the macro.
|
||||
- **`macro.cpp`:**
|
||||
`MacroArgs` methods related to macro arguments. Each `MacroArgs` *references* its arguments' contents.
|
||||
@@ -186,7 +208,7 @@ These files have been copied ("vendored") from external authors and adapted for
|
||||
Grammar for the RGBASM assembly language, which Bison preprocesses into a [LALR(1) parser](https://en.wikipedia.org/wiki/LALR_parser).
|
||||
The Bison-generated parser calls `yylex` (defined in `lexer.cpp`) to get the next token, and calls `yywrap` (defined in `fstack.cpp`) when the current context is out of tokens and returns `EOF`.
|
||||
- **`rpn.cpp`:**
|
||||
`Expression` methods and data related to "[RPN](https://en.wikipedia.org/wiki/Reverse_Polish_notation)" expressions. When a numeric expression is parsed, if its value cannot be calculated at assembly time, it is built up into a buffer of RPN-encoded operations to do so at link time by RGBLINK. The valid RPN operations are defined in [man/rgbds.5](man/rgbds.5).
|
||||
`Expression` methods and data related to "[RPN](https://en.wikipedia.org/wiki/Reverse_Polish_notation)" expressions. When a numeric expression is parsed, if its value cannot be calculated at assembly time, it is built up into a buffer of RPN-encoded operations to do so at link time by RGBLINK. The valid RPN operations are defined in [man/rgbds.5](/man/rgbds.5).
|
||||
- **`section.cpp`:**
|
||||
Functions and data related to `SECTION`s.
|
||||
This file *owns* the `Section`s in its `sections` collection. It also maintains various static pointers to those sections, including the `currentSection`, `currentLoadSection`, and `sectionStack` (which is affected by `PUSHS` and `POPS` directives). (Note that sections cannot be deleted.)
|
||||
@@ -261,7 +283,7 @@ These files have been copied ("vendored") from external authors and adapted for
|
||||
Functions and data related to outputting ROM files (with `-o/--output`), symbol files (with `-n/--sym`), and map files (with `-m/--map`).
|
||||
This file *references* some `Symbol`s and `Section`s, in collections that keep them sorted by address and name, which allows the symbol and map output to be in order.
|
||||
- **`patch.cpp`:**
|
||||
Functions and data related to "[RPN](https://en.wikipedia.org/wiki/Reverse_Polish_notation)" expression patches read from the object files, including the ones for `ASSERT` conditions. After sections have been assigned specific locations, the RPN patches can have their values calculated and applied to the ROM. The valid RPN operations are defined in [man/rgbds.5](man/rgbds.5).
|
||||
Functions and data related to "[RPN](https://en.wikipedia.org/wiki/Reverse_Polish_notation)" expression patches read from the object files, including the ones for `ASSERT` conditions. After sections have been assigned specific locations, the RPN patches can have their values calculated and applied to the ROM. The valid RPN operations are defined in [man/rgbds.5](/man/rgbds.5).
|
||||
This file *owns* the `Assertion`s in its `assertions` collection, and the `RPNStackEntry`s in its `rpnStack` collection.
|
||||
- **`script.y`:**
|
||||
Grammar for the linker script language, which Bison preprocesses into a [LALR(1) parser](https://en.wikipedia.org/wiki/LALR_parser).
|
||||
@@ -4,7 +4,7 @@ RGBDS was created in the late '90s and has received contributions from several
|
||||
developers since then. It wouldn't have been possible to get to this point
|
||||
without their work, and it is always open to the contributions of other people.
|
||||
|
||||
## Reporting Bugs
|
||||
## Reporting bugs
|
||||
|
||||
Bug reports are essential to improve RGBDS and they are always welcome. If you
|
||||
want to report a bug:
|
||||
@@ -35,6 +35,9 @@ implement it yourself, you can always contribute to RGBDS with your own code.
|
||||
|
||||
## Contributing code
|
||||
|
||||
Thank you! Code contributions are the most appreciated.
|
||||
You can find a description of the project's layout in [`ARCHITECTURE.md`](/ARCHITECTURE.md).
|
||||
|
||||
If you want to contribute with your own code, whether it is to fix a current
|
||||
issue or to add something that nobody had requested, you should first consider
|
||||
if your change is going to be small (and likely to be accepted as-is) or big
|
||||
@@ -62,20 +65,28 @@ years). If you are adding new files, you need to use the
|
||||
target checks for additional warnings. Your patches shouldn't introduce any
|
||||
new warning (but it may be possible to remove some warning checks if it makes
|
||||
the code much easier).
|
||||
|
||||
You can also use `cmake --preset develop` if you prefer.
|
||||
5. Test your changes by running `./run-tests.sh` in the `test` directory.
|
||||
(You must run `./fetch-test-deps.sh` first; if you forget to, the test suite
|
||||
will fail and remind you mid-way.)
|
||||
5. Format your changes according to `clang-format`, which will reformat the
|
||||
coding style according to our standards defined in `.clang-format`.
|
||||
6. Create a pull request against the branch `master`.
|
||||
7. Check the results of the GitHub Actions CI jobs for your pull request. The
|
||||
`./run-tests.sh --help` will print its available options. (You must run
|
||||
`external/fetch-repos.sh` first; if you forget to, the test suite will fail and
|
||||
remind you mid-way.)
|
||||
|
||||
You can also use `ctest --test-dir build` if you prefer.
|
||||
`ctest --test-dir build --print-labels` will print its available
|
||||
[test labels](https://cmake.org/cmake/help/latest/manual/ctest.1.html#label-matching).
|
||||
6. Format your changes according to `clang-format`, which will reformat the
|
||||
coding style according to our standards defined in `.clang-format`. You can
|
||||
use `make format` to format all the C++ files.
|
||||
7. Create a pull request against the branch `master`.
|
||||
8. Check the results of the GitHub Actions CI jobs for your pull request. The
|
||||
"Code format checking" and "Regression testing" jobs should all succeed.
|
||||
The "Diff completeness check" and "Static analysis" jobs should be manually
|
||||
checked, as they may output warnings which do not count as failure errors.
|
||||
The "Code coverage report" provides an
|
||||
[LCOV](https://github.com/linux-test-project/lcov)-generated report which
|
||||
can be downloaded and checked to see if your new code has full test coverage.
|
||||
8. Be prepared to get some comments about your code and to modify it. Tip: Use
|
||||
The "Diff completeness check" job should be manually checked, as it may
|
||||
output warnings which do not count as failure errors. The "Code coverage
|
||||
report" provides an [LCOV](https://github.com/linux-test-project/lcov)-generated
|
||||
report which can be downloaded and checked to see if your new code has full
|
||||
test coverage.
|
||||
9. Be prepared to get some comments about your code and to modify it. Tip: Use
|
||||
`git rebase -i origin/master` to modify chains of commits.
|
||||
|
||||
## Adding a test
|
||||
@@ -131,6 +142,10 @@ linked in various ways depending on the test.
|
||||
|
||||
These simply check that RGBLINK's output matches some expected output.
|
||||
|
||||
If a `.flags` file exists, its first line contains flags to pass to RGBLINK.
|
||||
(There may be more lines, which will be ignored; they can serve as comments to
|
||||
explain what the test is about.)
|
||||
|
||||
A `.out` file **must** exist, and RGBLINK's total output must match that file's
|
||||
contents.
|
||||
|
||||
@@ -212,30 +227,21 @@ If a `.flags` file exists, it will be used as part of the RGBGFX invocation
|
||||
Each `seed*.bin` file corresponds to one test.
|
||||
Each one is a binary RNG file which is passed to the `rgbgfx_test` program.
|
||||
|
||||
### Downstream projects
|
||||
### External projects
|
||||
|
||||
1. Make sure the downstream project supports
|
||||
<code>make <var><target></var> RGBDS=<var><path/to/RGBDS/></var></code>.
|
||||
While the test suite supports any Make target name, only
|
||||
[Make](//gnu.org/software/make) is currently supported, and the Makefile must
|
||||
support a `RGBDS` variable to use a non-system RGBDS directory.
|
||||
|
||||
Also, only projects hosted on GitHub are currently supported.
|
||||
2. Add the project to `test/fetch-test-deps.sh`: add a new `action` line at the
|
||||
bottom, following the existing pattern:
|
||||
|
||||
```sh
|
||||
action <owner> <repo> <date of last commit> <hash of last commit>
|
||||
```
|
||||
|
||||
(The date is used to avoid fetching too much history when cloning the
|
||||
repositories.)
|
||||
3. Add the project to `test/run-tests.sh`: add a new `test_downstream` line at
|
||||
the bottom, following the existing pattern:
|
||||
|
||||
```sh
|
||||
test_downstream <owner> <repo> <makefile target> <build file> <sha1 hash of build file>
|
||||
```
|
||||
Each `.cfg` file corresponds to one test.
|
||||
Each one defines the parameters for a real external project that builds using RGBDS.
|
||||
|
||||
1. Ensure that the downstream project supports a `RGBDS` variable to build with
|
||||
a non-system copy of RGBDS in some directory, i.e.
|
||||
<code>make <var><target></var> RGBDS=<var><path/to/rgbds/></var></code>.
|
||||
Only [GNU Make](https://www.gnu.org/software/make/) is currently supported.
|
||||
2. Create the new `.cfg` file, following the existing ones.
|
||||
|
||||
If an external project does not support `make RGBDS=`, or otherwise fails to
|
||||
build with current RGBDS, then you can edit the project to build correctly, and
|
||||
create a patch with `git diff` to be committed as
|
||||
<code>test/external/patches/<var><project></var>.patch</code>.
|
||||
|
||||
## Container images
|
||||
|
||||
@@ -254,4 +260,12 @@ manually running:
|
||||
# e.g. to build and tag as 'master'
|
||||
docker build . --tag ghcr.io/gbdev/rgbds:master
|
||||
docker push ghcr.io/gbdev/rgbds:master
|
||||
```
|
||||
```
|
||||
|
||||
## Publishing a new release
|
||||
|
||||
Please refer to [`RELEASE.md`](/RELEASE.md).
|
||||
|
||||
## Closing remarks
|
||||
|
||||
Feel free to add yourself to `CREDITS.md`!
|
||||
@@ -6,23 +6,24 @@ GitHub.
|
||||
1. Update the following files, then commit and push.
|
||||
You can use <code>git commit -m "Release <i><version></i>"</code> and `git push origin master`.
|
||||
|
||||
- [include/version.hpp](include/version.hpp): set appropriate values for `PACKAGE_VERSION_MAJOR`,
|
||||
- [include/version.hpp](/include/version.hpp): set appropriate values for `PACKAGE_VERSION_MAJOR`,
|
||||
`PACKAGE_VERSION_MINOR`, `PACKAGE_VERSION_PATCH`, and `PACKAGE_VERSION_RC`.
|
||||
**Only** define `PACKAGE_VERSION_RC` if you are publishing a release candidate!
|
||||
- [Dockerfile](Dockerfile): update `ARG version`.
|
||||
- [test/fetch-test-deps.sh](test/fetch-test-deps.sh): update test dependency commits
|
||||
- [Dockerfile](/Dockerfile): update `ARG version`.
|
||||
- [test/external/\*.cfg](/test/external): update test dependency commits
|
||||
(preferably, use the latest available).
|
||||
- [man/\*](man/): update dates and authors.
|
||||
- [man/\*](/man/): update dates and authors.
|
||||
|
||||
2. Create a Git tag formatted as <code>v<i><MAJOR></i>.<i><MINOR></i>.<i><PATCH></i></code>,
|
||||
or <code>v<i><MAJOR></i>.<i><MINOR></i>.<i><PATCH></i>-rc<i><RC></i></code>
|
||||
for a release candidate. <code><i>MAJOR</i></code>, <code><i>MINOR</i></code>,
|
||||
<code><i>PATCH</i></code>, and <code><i>RC</i></code> should match their values from
|
||||
[include/version.hpp](include/version.hpp). You can use <code>git tag <i><tag></i></code>.
|
||||
[include/version.hpp](/include/version.hpp). You can use <code>git tag <i><tag></i></code>.
|
||||
|
||||
3. Push the tag to GitHub. You can use <code>git push origin <i><tag></i></code>.
|
||||
|
||||
GitHub Actions will run the [create-release-artifacts.yaml](.github/workflows/create-release-artifacts.yaml)
|
||||
GitHub Actions will run the
|
||||
[create-release-artifacts.yml](/.github/workflows/create-release-artifacts.yml)
|
||||
workflow to detect the tag starting with "`v[0-9]`" and automatically do the following:
|
||||
|
||||
1. Build 32-bit and 64-bit RGBDS binaries for Windows with `cmake`.
|
||||
@@ -38,7 +39,7 @@ GitHub.
|
||||
procedure. You can use <code>git push --delete origin <i><tag></i></code> and
|
||||
<code>git tag --delete <i><tag></i></code>.
|
||||
|
||||
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).
|
||||
|
||||
This is not done automatically for prereleases, since we do not normally publish documentation
|
||||
@@ -13,7 +13,8 @@
|
||||
|
||||
#include "linkdefs.hpp" // AssertionType, RPNCommand
|
||||
|
||||
#include "asm/rpn.hpp" // Expression
|
||||
#include "asm/intern.hpp" // InternedStr
|
||||
#include "asm/rpn.hpp" // Expression
|
||||
|
||||
struct AlignmentSpec {
|
||||
uint8_t alignment;
|
||||
@@ -54,8 +55,8 @@ std::string act_StringFormat(
|
||||
std::string const &spec, std::vector<std::variant<uint32_t, std::string>> const &args
|
||||
);
|
||||
|
||||
std::string act_SectionName(std::string const &symName);
|
||||
std::string act_SectionName(InternedStr symName);
|
||||
|
||||
void act_CompoundAssignment(std::string const &symName, RPNCommand op, int32_t constValue);
|
||||
void act_CompoundAssignment(InternedStr symName, RPNCommand op, int32_t constValue);
|
||||
|
||||
#endif // RGBDS_ASM_ACTIONS_HPP
|
||||
|
||||
@@ -10,14 +10,14 @@
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#define DEFAULT_CHARMAP_NAME "main"
|
||||
#include "asm/intern.hpp"
|
||||
|
||||
void charmap_Init();
|
||||
bool charmap_ForEach(
|
||||
void (*mapFunc)(std::string const &),
|
||||
void (*charFunc)(std::string const &, std::vector<int32_t>)
|
||||
void (*mapFunc)(InternedStr), void (*charFunc)(std::string const &, std::vector<int32_t>)
|
||||
);
|
||||
void charmap_New(std::string const &name, std::string const *baseName);
|
||||
void charmap_Set(std::string const &name);
|
||||
void charmap_New(InternedStr name, InternedStr const *baseName);
|
||||
void charmap_Set(InternedStr name);
|
||||
void charmap_Push();
|
||||
void charmap_Pop();
|
||||
void charmap_CheckStack();
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "linkdefs.hpp"
|
||||
|
||||
#include "asm/intern.hpp"
|
||||
#include "asm/lexer.hpp"
|
||||
|
||||
struct FileStackNode {
|
||||
@@ -68,12 +69,10 @@ bool fstk_FailedOnMissingInclude();
|
||||
|
||||
bool yywrap();
|
||||
bool fstk_RunInclude(std::string const &path, bool isQuiet);
|
||||
void fstk_RunMacro(
|
||||
std::string const ¯oName, std::shared_ptr<MacroArgs> macroArgs, bool isQuiet
|
||||
);
|
||||
void fstk_RunMacro(InternedStr macroName, std::shared_ptr<MacroArgs> macroArgs, bool isQuiet);
|
||||
void fstk_RunRept(uint32_t count, int32_t reptLineNo, ContentSpan const &span, bool isQuiet);
|
||||
void fstk_RunFor(
|
||||
std::string const &symName,
|
||||
InternedStr symName,
|
||||
int32_t start,
|
||||
int32_t stop,
|
||||
int32_t step,
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifndef RGBDS_ASM_INTERN_HPP
|
||||
#define RGBDS_ASM_INTERN_HPP
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility> // hash
|
||||
|
||||
class InternedStr {
|
||||
size_t index;
|
||||
|
||||
public:
|
||||
constexpr InternedStr() : index(static_cast<size_t>(-1)) {}
|
||||
explicit constexpr InternedStr(size_t index_) : index(index_) {}
|
||||
|
||||
std::string const &str() const;
|
||||
char const *c_str() const { return str().c_str(); }
|
||||
|
||||
bool operator==(InternedStr const &rhs) const { return index == rhs.index; }
|
||||
|
||||
template<typename T>
|
||||
friend struct std::hash;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct std::hash<InternedStr> {
|
||||
size_t operator()(InternedStr const &str) const { return std::hash<size_t>{}(str.index); }
|
||||
};
|
||||
|
||||
InternedStr intern(std::string_view str);
|
||||
|
||||
#endif // RGBDS_ASM_INTERN_HPP
|
||||
+10
-50
@@ -9,18 +9,9 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "platform.hpp" // SSIZE_MAX
|
||||
|
||||
// This value is a compromise between `LexerState` allocation performance when reading the entire
|
||||
// file works, and buffering performance when it doesn't (e.g. when piping a file into RGBASM).
|
||||
static constexpr size_t LEXER_BUF_SIZE = 64;
|
||||
// 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");
|
||||
// This caps the size of buffer reads, and according to POSIX, passing more than SSIZE_MAX is UB
|
||||
static_assert(LEXER_BUF_SIZE <= SSIZE_MAX, "Lexer buffer size is too large");
|
||||
#include "asm/intern.hpp"
|
||||
|
||||
enum LexerMode {
|
||||
LEXER_NORMAL,
|
||||
@@ -32,7 +23,7 @@ enum LexerMode {
|
||||
};
|
||||
|
||||
struct Expansion {
|
||||
std::optional<std::string> name;
|
||||
std::optional<InternedStr> name;
|
||||
std::shared_ptr<std::string> contents;
|
||||
size_t offset; // Cursor into `contents`
|
||||
|
||||
@@ -45,34 +36,6 @@ struct ContentSpan {
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct ViewedContent {
|
||||
ContentSpan span; // Span of chars
|
||||
size_t offset = 0; // Cursor into `span.ptr`
|
||||
|
||||
ViewedContent(ContentSpan const &span_) : span(span_) {}
|
||||
ViewedContent(std::shared_ptr<char[]> ptr, size_t size) : span({.ptr = ptr, .size = size}) {}
|
||||
|
||||
std::shared_ptr<char[]> makeSharedContentPtr() const {
|
||||
return std::shared_ptr<char[]>(span.ptr, &span.ptr[offset]);
|
||||
}
|
||||
};
|
||||
|
||||
struct BufferedContent {
|
||||
int fd; // File from which to read chars
|
||||
char buf[LEXER_BUF_SIZE] = {}; // Circular buffer of chars
|
||||
size_t offset = 0; // Cursor into `buf`
|
||||
size_t size = 0; // Number of "fresh" chars in `buf`
|
||||
|
||||
BufferedContent(int fd_) : fd(fd_) {}
|
||||
~BufferedContent();
|
||||
|
||||
void advance(); // Increment `offset` circularly, decrement `size`
|
||||
void refill(); // Read from `fd` to fill `buf`
|
||||
|
||||
private:
|
||||
size_t readMore(size_t startIndex, size_t nbChars);
|
||||
};
|
||||
|
||||
struct IfStackEntry {
|
||||
bool ranIfBlock; // Whether an IF/ELIF/ELSE block ran already
|
||||
bool reachedElseBlock; // Whether an ELSE block ran already
|
||||
@@ -87,31 +50,28 @@ struct LexerState {
|
||||
int lastToken;
|
||||
int nextToken;
|
||||
|
||||
std::deque<IfStackEntry> ifStack;
|
||||
std::deque<IfStackEntry> ifStack; // Front is the innermost `IF` block
|
||||
|
||||
bool capturing; // Whether the text being lexed should be captured
|
||||
size_t captureSize; // Amount of text captured
|
||||
std::shared_ptr<std::vector<char>> captureBuf; // Buffer to send the captured text to if set
|
||||
|
||||
bool disableExpansions;
|
||||
size_t expansionScanDistance; // Max distance already scanned for expansions
|
||||
bool expandStrings;
|
||||
std::deque<Expansion> expansions; // Front is the innermost current expansion
|
||||
bool enableExpansions;
|
||||
bool enableStringExpansions;
|
||||
size_t expansionScanDistance; // Max distance already scanned for expansions
|
||||
std::deque<Expansion> expansionStack; // Front is the innermost current expansion
|
||||
|
||||
std::variant<std::monostate, ViewedContent, BufferedContent> content;
|
||||
ContentSpan content; // Span of chars
|
||||
size_t offset = 0; // Cursor into `content.ptr`
|
||||
|
||||
~LexerState();
|
||||
|
||||
int peekChar();
|
||||
int peekCharAhead();
|
||||
|
||||
std::shared_ptr<char[]> makeSharedCaptureBufPtr() const {
|
||||
return std::shared_ptr<char[]>(captureBuf, captureBuf->data());
|
||||
}
|
||||
|
||||
void setAsCurrentState();
|
||||
void setFileAsNextState(std::string const &filePath, bool updateStateNow);
|
||||
void setViewAsNextState(char const *name, ContentSpan const &span, uint32_t lineNo_);
|
||||
void setViewAsNextState(char const *name, ContentSpan const &content_, uint32_t lineNo_);
|
||||
|
||||
void clear(uint32_t lineNo_);
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ struct MacroArgs {
|
||||
std::vector<std::shared_ptr<std::string>> args;
|
||||
|
||||
uint32_t nbArgs() const { return args.size() - shift; }
|
||||
std::shared_ptr<std::string> getArg(int32_t i) const;
|
||||
std::shared_ptr<std::string> getArg(int32_t num) const;
|
||||
std::shared_ptr<std::string> getAllArgs() const;
|
||||
|
||||
void appendArg(std::shared_ptr<std::string> arg);
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
#include "helpers.hpp" // assume
|
||||
#include "util.hpp" // xfclose
|
||||
|
||||
enum MissingInclude {
|
||||
INC_ERROR, // A missing included file is an error that halts assembly
|
||||
GEN_EXIT, // A missing included file is assumed to be generated; exit normally
|
||||
@@ -30,12 +33,13 @@ struct Options {
|
||||
|
||||
~Options() {
|
||||
if (dependFile) {
|
||||
fclose(dependFile);
|
||||
xfclose(dependFile);
|
||||
}
|
||||
}
|
||||
|
||||
void printDep(std::string const &depName) {
|
||||
if (dependFile) {
|
||||
assume(targetFileName.has_value());
|
||||
fprintf(dependFile, "%s: %s\n", targetFileName->c_str(), depName.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
+6
-4
@@ -10,16 +10,18 @@
|
||||
|
||||
#include "linkdefs.hpp"
|
||||
|
||||
#include "asm/intern.hpp"
|
||||
|
||||
struct Symbol;
|
||||
|
||||
struct RPNValue {
|
||||
RPNCommand command; // The RPN_* command ID
|
||||
std::variant<std::monostate, uint8_t, uint32_t, std::string> data; // Data after the ID, if any
|
||||
std::variant<std::monostate, uint8_t, uint32_t, InternedStr> data; // Data after the ID, if any
|
||||
|
||||
RPNValue(RPNCommand cmd);
|
||||
RPNValue(RPNCommand cmd, uint8_t val);
|
||||
RPNValue(RPNCommand cmd, uint32_t val);
|
||||
RPNValue(RPNCommand cmd, std::string const &name);
|
||||
RPNValue(RPNCommand cmd, InternedStr name);
|
||||
|
||||
void appendEncoded(std::vector<uint8_t> &buffer) const;
|
||||
};
|
||||
@@ -40,8 +42,8 @@ struct Expression {
|
||||
bool isDiffConstant(Symbol const *symName) const;
|
||||
|
||||
void makeNumber(uint32_t value);
|
||||
void makeSymbol(std::string const &symName);
|
||||
void makeBankSymbol(std::string const &symName);
|
||||
void makeSymbol(InternedStr symName);
|
||||
void makeBankSymbol(InternedStr symName);
|
||||
void makeBankSection(std::string const §Name);
|
||||
void makeSizeOfSection(std::string const §Name);
|
||||
void makeStartOfSection(std::string const §Name);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "intern.hpp"
|
||||
#include "linkdefs.hpp"
|
||||
|
||||
struct Expression;
|
||||
@@ -107,6 +108,6 @@ void sect_PushSection();
|
||||
void sect_PopSection();
|
||||
void sect_CheckStack();
|
||||
|
||||
std::string sect_PushSectionFragmentLiteral();
|
||||
InternedStr sect_PushSectionFragmentLiteral();
|
||||
|
||||
#endif // RGBDS_ASM_SECTION_HPP
|
||||
|
||||
+20
-21
@@ -10,6 +10,7 @@
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#include "asm/intern.hpp"
|
||||
#include "asm/lexer.hpp"
|
||||
#include "asm/section.hpp"
|
||||
|
||||
@@ -26,7 +27,7 @@ struct Symbol; // Forward declaration for `sym_IsPC`
|
||||
bool sym_IsPC(Symbol const *sym); // Forward declaration for `getSection`
|
||||
|
||||
struct Symbol {
|
||||
std::string name;
|
||||
InternedStr name;
|
||||
SymbolType type;
|
||||
bool isBuiltin;
|
||||
bool isExported; // Not relevant for SYM_MACRO or SYM_EQUS
|
||||
@@ -68,36 +69,34 @@ struct Symbol {
|
||||
uint32_t getConstantValue() const;
|
||||
};
|
||||
|
||||
bool sym_IsDotScope(std::string const &symName);
|
||||
bool sym_IsDotScope(InternedStr symName);
|
||||
|
||||
void sym_ForEach(void (*callback)(Symbol &));
|
||||
|
||||
Symbol *sym_AddLocalLabel(std::string const &symName);
|
||||
Symbol *sym_AddLabel(std::string const &symName);
|
||||
Symbol *sym_AddLocalLabel(InternedStr symName);
|
||||
Symbol *sym_AddLabel(InternedStr symName);
|
||||
Symbol *sym_AddAnonLabel();
|
||||
std::string sym_MakeAnonLabelName(uint32_t ofs, bool neg);
|
||||
void sym_Export(std::string const &symName);
|
||||
Symbol *sym_AddEqu(std::string const &symName, int32_t value);
|
||||
Symbol *sym_RedefEqu(std::string const &symName, int32_t value);
|
||||
Symbol *sym_AddVar(std::string const &symName, int32_t value);
|
||||
InternedStr sym_MakeAnonLabelName(uint32_t ofs, bool neg);
|
||||
void sym_Export(InternedStr symName);
|
||||
Symbol *sym_AddEqu(InternedStr symName, int32_t value);
|
||||
Symbol *sym_RedefEqu(InternedStr symName, int32_t value);
|
||||
Symbol *sym_AddVar(InternedStr symName, int32_t value);
|
||||
int32_t sym_GetRSValue();
|
||||
void sym_SetRSValue(int32_t value);
|
||||
// Find a symbol by exact name, bypassing expansion checks
|
||||
Symbol *sym_FindExactSymbol(std::string const &symName);
|
||||
Symbol *sym_FindExactSymbol(InternedStr symName);
|
||||
// Find a symbol, possibly scoped, by name
|
||||
Symbol *sym_FindScopedSymbol(std::string const &symName);
|
||||
Symbol *sym_FindScopedSymbol(InternedStr symName);
|
||||
// Find a scoped symbol by name; do not return `@` or `_NARG` when they have no value
|
||||
Symbol *sym_FindScopedValidSymbol(std::string const &symName);
|
||||
Symbol *sym_FindScopedValidSymbol(InternedStr symName);
|
||||
Symbol const *sym_GetPC();
|
||||
Symbol *sym_AddMacro(
|
||||
std::string const &symName, int32_t defLineNo, ContentSpan const &span, bool isQuiet
|
||||
);
|
||||
Symbol *sym_Ref(std::string const &symName);
|
||||
Symbol *sym_AddString(std::string const &symName, std::shared_ptr<std::string> value);
|
||||
Symbol *sym_RedefString(std::string const &symName, std::shared_ptr<std::string> value);
|
||||
void sym_Purge(std::string const &symName);
|
||||
bool sym_IsPurgedExact(std::string const &symName);
|
||||
bool sym_IsPurgedScoped(std::string const &symName);
|
||||
Symbol *sym_AddMacro(InternedStr symName, int32_t defLineNo, ContentSpan const &span, bool isQuiet);
|
||||
Symbol *sym_Ref(InternedStr symName);
|
||||
Symbol *sym_AddString(InternedStr symName, std::shared_ptr<std::string> value);
|
||||
Symbol *sym_RedefString(InternedStr symName, std::shared_ptr<std::string> value);
|
||||
void sym_Purge(InternedStr symName);
|
||||
bool sym_IsPurgedExact(InternedStr symName);
|
||||
bool sym_IsPurgedScoped(InternedStr symName);
|
||||
void sym_Init(time_t now);
|
||||
|
||||
// Functions to save and restore the current label scopes.
|
||||
|
||||
@@ -61,13 +61,17 @@ extern Diagnostics<WarningLevel, WarningID> warnings;
|
||||
void warning(WarningID id, char const *fmt, ...);
|
||||
|
||||
// Used for errors that compromise the whole assembly process by affecting the
|
||||
// following code, potencially making the assembler generate errors caused by
|
||||
// following code, potentially making the assembler generate errors caused by
|
||||
// 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,
|
||||
// when it fails to allocate memory).
|
||||
[[gnu::format(printf, 1, 2), noreturn]]
|
||||
void fatal(char const *fmt, ...);
|
||||
|
||||
// Used for fatal errors that handle their own backtrace output.
|
||||
[[noreturn]]
|
||||
void fatalNoTrace(std::function<void()> callback);
|
||||
|
||||
// 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
|
||||
// get a list of all errors at the end, making it easier to fix all of them at
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
|
||||
#include "helpers.hpp" // InvocableR
|
||||
#include "style.hpp"
|
||||
|
||||
#define TRACE_SEPARATOR "<-"
|
||||
@@ -25,8 +26,12 @@ extern Tracing tracing;
|
||||
|
||||
bool trace_ParseTraceDepth(char const *arg);
|
||||
|
||||
template<typename NodeT, typename NameFnT, typename LineNoFnT>
|
||||
void trace_PrintBacktrace(std::vector<NodeT> const &stack, NameFnT getName, LineNoFnT getLineNo) {
|
||||
template<typename NodeT>
|
||||
void trace_PrintBacktrace(
|
||||
std::vector<NodeT> const &stack,
|
||||
InvocableR<char const *, NodeT const &> auto getName,
|
||||
InvocableR<uint32_t, NodeT const &> auto getLineNo
|
||||
) {
|
||||
size_t n = stack.size();
|
||||
if (n == 0) {
|
||||
return; // LCOV_EXCL_LINE
|
||||
|
||||
+21
-21
@@ -1,21 +1,21 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifndef RGBDS_CLI_HPP
|
||||
#define RGBDS_CLI_HPP
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <string>
|
||||
|
||||
#include "extern/getopt.hpp" // option
|
||||
#include "usage.hpp"
|
||||
|
||||
void cli_ParseArgs(
|
||||
int argc,
|
||||
char *argv[],
|
||||
char const *shortOpts,
|
||||
option const *longOpts,
|
||||
void (*parseArg)(int, char *),
|
||||
Usage usage
|
||||
);
|
||||
|
||||
#endif // RGBDS_CLI_HPP
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifndef RGBDS_CLI_HPP
|
||||
#define RGBDS_CLI_HPP
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <string>
|
||||
|
||||
#include "extern/getopt.hpp" // option
|
||||
#include "usage.hpp"
|
||||
|
||||
void cli_ParseArgs(
|
||||
int argc,
|
||||
char *argv[],
|
||||
char const *shortOpts,
|
||||
option const *longOpts,
|
||||
void (*parseArg)(int, char *),
|
||||
Usage usage
|
||||
);
|
||||
|
||||
#endif // RGBDS_CLI_HPP
|
||||
|
||||
+56
-6
@@ -15,9 +15,20 @@
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "itertools.hpp"
|
||||
#include "style.hpp"
|
||||
|
||||
[[gnu::format(printf, 1, 2)]]
|
||||
void warnx(char const *fmt, ...);
|
||||
[[gnu::format(printf, 1, 0)]]
|
||||
void vwarnx(char const *fmt, va_list args);
|
||||
[[gnu::format(printf, 1, 2)]]
|
||||
void errorx(char const *fmt, ...);
|
||||
[[gnu::format(printf, 1, 0)]]
|
||||
void verrorx(char const *fmt, va_list args);
|
||||
[[gnu::format(printf, 1, 2)]]
|
||||
void fatalx(char const *fmt, ...);
|
||||
[[gnu::format(printf, 1, 0)]]
|
||||
void vfatalx(char const *fmt, va_list args);
|
||||
|
||||
enum WarningAbled { WARNING_DEFAULT, WARNING_ENABLED, WARNING_DISABLED };
|
||||
|
||||
@@ -30,7 +41,7 @@ struct WarningState {
|
||||
|
||||
std::pair<WarningState, std::optional<uint32_t>> getInitialWarningState(std::string &flag);
|
||||
|
||||
template<typename LevelEnumT>
|
||||
template<Enum LevelEnumT>
|
||||
struct WarningFlag {
|
||||
char const *name;
|
||||
LevelEnumT level;
|
||||
@@ -38,14 +49,14 @@ struct WarningFlag {
|
||||
|
||||
enum WarningBehavior { DISABLED, ENABLED, ERROR };
|
||||
|
||||
template<typename WarningEnumT>
|
||||
template<Enum WarningEnumT>
|
||||
struct ParamWarning {
|
||||
WarningEnumT firstID;
|
||||
WarningEnumT lastID;
|
||||
uint8_t defaultLevel;
|
||||
};
|
||||
|
||||
template<typename WarningEnumT>
|
||||
template<Enum WarningEnumT>
|
||||
struct DiagnosticsState {
|
||||
WarningState flagStates[WarningEnumT::NB_WARNINGS];
|
||||
WarningState metaStates[WarningEnumT::NB_WARNINGS];
|
||||
@@ -53,7 +64,7 @@ struct DiagnosticsState {
|
||||
bool warningsAreErrors = false;
|
||||
};
|
||||
|
||||
template<typename LevelEnumT, typename WarningEnumT>
|
||||
template<Enum LevelEnumT, Enum WarningEnumT>
|
||||
struct Diagnostics {
|
||||
std::vector<WarningFlag<LevelEnumT>> metaWarnings;
|
||||
std::vector<WarningFlag<LevelEnumT>> warningFlags;
|
||||
@@ -71,7 +82,7 @@ struct Diagnostics {
|
||||
void processWarningFlag(char const *flag);
|
||||
};
|
||||
|
||||
template<typename LevelEnumT, typename WarningEnumT>
|
||||
template<Enum LevelEnumT, Enum WarningEnumT>
|
||||
WarningBehavior Diagnostics<LevelEnumT, WarningEnumT>::getWarningBehavior(WarningEnumT id) const {
|
||||
// Check if warnings are globally disabled
|
||||
if (!state.warningsEnabled) {
|
||||
@@ -120,7 +131,7 @@ WarningBehavior Diagnostics<LevelEnumT, WarningEnumT>::getWarningBehavior(Warnin
|
||||
return WarningBehavior::DISABLED;
|
||||
}
|
||||
|
||||
template<typename LevelEnumT, typename WarningEnumT>
|
||||
template<Enum LevelEnumT, Enum WarningEnumT>
|
||||
void Diagnostics<LevelEnumT, WarningEnumT>::processWarningFlag(char const *flag) {
|
||||
std::string rootFlag = flag;
|
||||
|
||||
@@ -208,4 +219,43 @@ void Diagnostics<LevelEnumT, WarningEnumT>::processWarningFlag(char const *flag)
|
||||
warnx("Unknown warning flag \"%s\"", rootFlag.c_str());
|
||||
}
|
||||
|
||||
template<Enum LevelEnumT, Enum WarningEnumT>
|
||||
[[gnu::format(printf, 3, 0)]]
|
||||
WarningBehavior printDiagnostic(
|
||||
Diagnostics<LevelEnumT, WarningEnumT> const &warnings,
|
||||
WarningEnumT id,
|
||||
char const *fmt,
|
||||
va_list args
|
||||
) {
|
||||
char const *flag = warnings.warningFlags[id].name;
|
||||
WarningBehavior behavior = warnings.getWarningBehavior(id);
|
||||
|
||||
switch (behavior) {
|
||||
case WarningBehavior::DISABLED:
|
||||
break;
|
||||
|
||||
case WarningBehavior::ENABLED:
|
||||
style_Set(stderr, STYLE_YELLOW, true);
|
||||
fputs("warning: ", stderr);
|
||||
style_Reset(stderr);
|
||||
vfprintf(stderr, fmt, args);
|
||||
style_Set(stderr, STYLE_YELLOW, true);
|
||||
fprintf(stderr, " [-W%s]\n", flag);
|
||||
style_Reset(stderr);
|
||||
break;
|
||||
|
||||
case WarningBehavior::ERROR:
|
||||
style_Set(stderr, STYLE_RED, true);
|
||||
fputs("error: ", stderr);
|
||||
style_Reset(stderr);
|
||||
vfprintf(stderr, fmt, args);
|
||||
style_Set(stderr, STYLE_RED, true);
|
||||
fprintf(stderr, " [-Werror=%s]\n", flag);
|
||||
style_Reset(stderr);
|
||||
break;
|
||||
}
|
||||
|
||||
return behavior;
|
||||
}
|
||||
|
||||
#endif // RGBDS_DIAGNOSTICS_HPP
|
||||
|
||||
Vendored
+6
-1
@@ -8,6 +8,11 @@
|
||||
#define UTF8_ACCEPT 0
|
||||
#define UTF8_REJECT 12
|
||||
|
||||
uint32_t decode(uint32_t *state, uint32_t *codep, uint8_t byte);
|
||||
struct Utf8Decoder {
|
||||
uint32_t state = UTF8_ACCEPT;
|
||||
uint32_t codepoint = 0;
|
||||
|
||||
uint32_t update(uint8_t byte);
|
||||
};
|
||||
|
||||
#endif // RGBDS_EXTERN_UTF8DECODER_HPP
|
||||
|
||||
@@ -41,15 +41,7 @@ public:
|
||||
return std::holds_alternative<std::filebuf>(_file) ? std::get<std::filebuf>(_file)
|
||||
: *std::get<std::streambuf *>(_file);
|
||||
}
|
||||
std::streambuf const &operator*() const {
|
||||
// The non-`const` version does not perform any modifications, so it's okay.
|
||||
return **const_cast<File *>(this);
|
||||
}
|
||||
std::streambuf *operator->() { return &**this; }
|
||||
std::streambuf const *operator->() const {
|
||||
// See the `operator*` equivalent.
|
||||
return const_cast<File *>(this)->operator->();
|
||||
}
|
||||
|
||||
char const *c_str(std::string const &path) const {
|
||||
return std::holds_alternative<std::filebuf>(_file) ? path.c_str()
|
||||
|
||||
@@ -12,7 +12,7 @@ public:
|
||||
static constexpr size_t capacity = 4;
|
||||
|
||||
private:
|
||||
// Up to 4 colors, sorted, and where SIZE_MAX means the slot is empty
|
||||
// Up to 4 colors, sorted, and where UINT16_MAX means the slot is empty
|
||||
// (OK because it's not a valid color index)
|
||||
// Sorting is done on the raw numerical values to lessen `compare`'s complexity
|
||||
std::array<uint16_t, capacity> _colorIndices{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX};
|
||||
@@ -21,11 +21,7 @@ public:
|
||||
// Adds the specified color to the set, or **silently drops it** if the set is full.
|
||||
void add(uint16_t color);
|
||||
|
||||
enum ComparisonResult {
|
||||
NEITHER,
|
||||
WE_BIGGER,
|
||||
THEY_BIGGER = -1,
|
||||
};
|
||||
enum ComparisonResult { INCOMPARABLE, SUBSET_OR_EQUAL, STRICT_SUPERSET };
|
||||
ComparisonResult compare(ColorSet const &other) const;
|
||||
|
||||
size_t size() const;
|
||||
|
||||
+23
-23
@@ -1,23 +1,23 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifndef RGBDS_GFX_FLIP_HPP
|
||||
#define RGBDS_GFX_FLIP_HPP
|
||||
|
||||
#include <array>
|
||||
#include <stdint.h>
|
||||
|
||||
// Flipping tends to happen fairly often, so take a bite out of dcache to speed it up
|
||||
static std::array<uint16_t, 256> flipTable = ([]() constexpr {
|
||||
std::array<uint16_t, 256> table{};
|
||||
for (uint16_t i = 0; i < table.size(); ++i) {
|
||||
// To flip all the bits, we'll flip both nibbles, then each nibble half, etc.
|
||||
uint16_t byte = i;
|
||||
byte = (byte & 0b0000'1111) << 4 | (byte & 0b1111'0000) >> 4;
|
||||
byte = (byte & 0b0011'0011) << 2 | (byte & 0b1100'1100) >> 2;
|
||||
byte = (byte & 0b0101'0101) << 1 | (byte & 0b1010'1010) >> 1;
|
||||
table[i] = byte;
|
||||
}
|
||||
return table;
|
||||
})();
|
||||
|
||||
#endif // RGBDS_GFX_FLIP_HPP
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifndef RGBDS_GFX_FLIP_HPP
|
||||
#define RGBDS_GFX_FLIP_HPP
|
||||
|
||||
#include <array>
|
||||
#include <stdint.h>
|
||||
|
||||
// Flipping tends to happen fairly often, so take a bite out of dcache to speed it up
|
||||
static std::array<uint16_t, 256> flipTable = ([]() constexpr {
|
||||
std::array<uint16_t, 256> table{};
|
||||
for (uint16_t i = 0; i < table.size(); ++i) {
|
||||
// To flip all the bits, we'll flip both nibbles, then each nibble half, etc.
|
||||
uint16_t byte = i;
|
||||
byte = (byte & 0b0000'1111) << 4 | (byte & 0b1111'0000) >> 4;
|
||||
byte = (byte & 0b0011'0011) << 2 | (byte & 0b1100'1100) >> 2;
|
||||
byte = (byte & 0b0101'0101) << 1 | (byte & 0b1010'1010) >> 1;
|
||||
table[i] = byte;
|
||||
}
|
||||
return table;
|
||||
})();
|
||||
|
||||
#endif // RGBDS_GFX_FLIP_HPP
|
||||
|
||||
+27
-27
@@ -1,27 +1,27 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifndef RGBDS_GFX_PALETTE_HPP
|
||||
#define RGBDS_GFX_PALETTE_HPP
|
||||
|
||||
#include <array>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct Palette {
|
||||
// An array of 4 GBC-native (RGB555) colors
|
||||
std::array<uint16_t, 4> colors{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX};
|
||||
|
||||
void addColor(uint16_t color);
|
||||
uint8_t indexOf(uint16_t color) const;
|
||||
uint16_t &operator[](size_t index) { return colors[index]; }
|
||||
uint16_t const &operator[](size_t index) const { return colors[index]; }
|
||||
|
||||
decltype(colors)::iterator begin();
|
||||
decltype(colors)::iterator end();
|
||||
decltype(colors)::const_iterator begin() const;
|
||||
decltype(colors)::const_iterator end() const;
|
||||
|
||||
uint8_t size() const;
|
||||
};
|
||||
|
||||
#endif // RGBDS_GFX_PALETTE_HPP
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifndef RGBDS_GFX_PALETTE_HPP
|
||||
#define RGBDS_GFX_PALETTE_HPP
|
||||
|
||||
#include <array>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct Palette {
|
||||
// An array of 4 GBC-native (RGB555) colors
|
||||
std::array<uint16_t, 4> colors{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX};
|
||||
|
||||
void addColor(uint16_t color);
|
||||
uint8_t indexOf(uint16_t color) const;
|
||||
uint16_t &operator[](size_t index) { return colors[index]; }
|
||||
uint16_t const &operator[](size_t index) const { return colors[index]; }
|
||||
|
||||
decltype(colors)::iterator begin();
|
||||
decltype(colors)::iterator end();
|
||||
decltype(colors)::const_iterator begin() const;
|
||||
decltype(colors)::const_iterator end() const;
|
||||
|
||||
uint8_t size() const;
|
||||
};
|
||||
|
||||
#endif // RGBDS_GFX_PALETTE_HPP
|
||||
|
||||
+25
-7
@@ -4,6 +4,9 @@
|
||||
#define RGBDS_GFX_RGBA_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
#include "helpers.hpp" // literal_strlen
|
||||
|
||||
struct Rgba {
|
||||
uint8_t red;
|
||||
@@ -17,6 +20,10 @@ struct Rgba {
|
||||
explicit constexpr Rgba(uint32_t rgba = 0)
|
||||
: red(rgba >> 24), green(rgba >> 16), blue(rgba >> 8), alpha(rgba) {}
|
||||
|
||||
// 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 (1 << 15) exactly.
|
||||
static constexpr uint16_t transparent = 0b1'00000'00000'00000;
|
||||
|
||||
static constexpr Rgba fromCGBColor(uint16_t color) {
|
||||
constexpr auto _5to8 = [](uint8_t channel) -> uint8_t {
|
||||
channel &= 0b11111; // For caller's convenience
|
||||
@@ -26,12 +33,12 @@ struct Rgba {
|
||||
_5to8(color),
|
||||
_5to8(color >> 5),
|
||||
_5to8(color >> 10),
|
||||
static_cast<uint8_t>(color & 0x8000 ? 0x00 : 0xFF),
|
||||
static_cast<uint8_t>(color & transparent ? 0x00 : 0xFF),
|
||||
};
|
||||
}
|
||||
|
||||
// Returns this RGBA as a 32-bit number that can be printed in hex (`%08x`) to yield its CSS
|
||||
// representation
|
||||
// Returns this RGBA as a 32-bit number that can be printed in hex (`#%08x`)
|
||||
// to yield its CSS representation (`#rrggbbaa`).
|
||||
uint32_t toCSS() const {
|
||||
constexpr auto shl = [](uint8_t val, unsigned shift) {
|
||||
return static_cast<uint32_t>(val) << shift;
|
||||
@@ -41,10 +48,6 @@ struct Rgba {
|
||||
|
||||
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.
|
||||
static constexpr uint16_t transparent = 0b1'00000'00000'00000;
|
||||
|
||||
static constexpr uint8_t transparency_threshold = 0x10;
|
||||
bool isTransparent() const { return alpha < transparency_threshold; }
|
||||
static constexpr uint8_t opacity_threshold = 0xF0;
|
||||
@@ -56,4 +59,19 @@ struct Rgba {
|
||||
uint8_t grayIndex() const;
|
||||
};
|
||||
|
||||
// Returns a CGB color as a rendered string "GB:rr,gg,bb" or "transparent".
|
||||
std::string toCGB(uint16_t color);
|
||||
|
||||
std::string listCGBColors(auto const &list) {
|
||||
std::string buf;
|
||||
for (uint16_t color : list) {
|
||||
buf += toCGB(color) + "; ";
|
||||
}
|
||||
static constexpr size_t n = literal_strlen("; ");
|
||||
if (buf.size() >= n) {
|
||||
buf.erase(buf.size() - n, n);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
#endif // RGBDS_GFX_RGBA_HPP
|
||||
|
||||
+24
-1
@@ -3,6 +3,8 @@
|
||||
#ifndef RGBDS_HELPERS_HPP
|
||||
#define RGBDS_HELPERS_HPP
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
// Ideally we'd use `std::unreachable`, but it has insufficient compiler support
|
||||
#ifdef __GNUC__ // GCC or compatible
|
||||
#ifdef NDEBUG
|
||||
@@ -99,14 +101,35 @@ static inline int clz(unsigned int x) {
|
||||
#define RANGE(s) std::begin(s), std::end(s)
|
||||
#define RRANGE(s) std::rbegin(s), std::rend(s)
|
||||
|
||||
// Macros to print-format a `std::string_view` (like <inttype.h> macros)
|
||||
#define PRI_SV ".*s"
|
||||
#define PRI_SV_ARG(sv) static_cast<int>((sv).length()), (sv).data()
|
||||
|
||||
// MSVC does not inline `strlen()` or `.length()` of a constant string
|
||||
template<int SizeOfString>
|
||||
requires(SizeOfString > 0)
|
||||
static constexpr int literal_strlen(char const (&)[SizeOfString]) {
|
||||
return SizeOfString - 1; // Don't count the ending '\0'
|
||||
}
|
||||
|
||||
// Concept for template parameters that must be an `enum` type
|
||||
template<typename EnumT>
|
||||
concept Enum = std::is_enum_v<EnumT>;
|
||||
|
||||
// Concept for template parameters that must be a function returning `void`
|
||||
template<typename ProcedureFnT, typename... Args>
|
||||
concept Procedure = std::is_void_v<std::invoke_result_t<ProcedureFnT, Args...>>;
|
||||
|
||||
// Concept for template parameters that must be a function returning a particular type
|
||||
template<typename FnT, typename ReturnT, typename... Args>
|
||||
concept InvocableR = std::is_invocable_r_v<ReturnT, FnT, Args...>;
|
||||
|
||||
// Concept for template parameters that must match a particular type modulo cv-qualifiers
|
||||
template<typename T, typename UnqualifiedT>
|
||||
concept QualifiedEquivalent = std::is_same_v<std::remove_cvref_t<T>, UnqualifiedT>;
|
||||
|
||||
// For ad-hoc RAII in place of a `defer` statement or cross-platform `__attribute__((cleanup))`
|
||||
template<typename DeferredFnT>
|
||||
template<Procedure DeferredFnT>
|
||||
struct Defer {
|
||||
DeferredFnT deferred;
|
||||
Defer(DeferredFnT func) : deferred(func) {}
|
||||
|
||||
+15
-13
@@ -12,6 +12,8 @@
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "helpers.hpp" // Enum
|
||||
|
||||
// A wrapper around iterables to reverse their iteration order; used in `for`-each loops.
|
||||
template<typename IterableT>
|
||||
struct ReversedIterable {
|
||||
@@ -33,18 +35,18 @@ ReversedIterable<IterableT> reversed(IterableT &&_iterable) {
|
||||
return {_iterable};
|
||||
}
|
||||
|
||||
// A map from `std::string` keys to `ItemT` items, iterable in the order the items were inserted.
|
||||
template<typename ItemT>
|
||||
// A map from `KeyT` keys to `ItemT` items, iterable in the order the items were inserted.
|
||||
template<typename KeyT, typename ItemT>
|
||||
class InsertionOrderedMap {
|
||||
std::deque<ItemT> list;
|
||||
std::unordered_map<std::string, size_t> map; // Indexes into `list`
|
||||
std::deque<ItemT> list; // `deque` does not invalidate item references
|
||||
std::unordered_map<KeyT, size_t> map; // Indexes into `list`
|
||||
|
||||
public:
|
||||
size_t size() const { return list.size(); }
|
||||
|
||||
bool empty() const { return list.empty(); }
|
||||
|
||||
bool contains(std::string const &name) const { return map.find(name) != map.end(); }
|
||||
bool contains(KeyT const &key) const { return map.find(key) != map.end(); }
|
||||
|
||||
ItemT &operator[](size_t i) { return list[i]; }
|
||||
|
||||
@@ -53,13 +55,13 @@ public:
|
||||
typename decltype(list)::const_iterator begin() const { return list.begin(); }
|
||||
typename decltype(list)::const_iterator end() const { return list.end(); }
|
||||
|
||||
ItemT &add(std::string const &name) {
|
||||
map[name] = list.size();
|
||||
ItemT &add(KeyT const &key) {
|
||||
map[key] = list.size();
|
||||
return list.emplace_back();
|
||||
}
|
||||
|
||||
ItemT &add(std::string const &name, ItemT &&value) {
|
||||
map[name] = list.size();
|
||||
ItemT &add(KeyT const &key, ItemT &&value) {
|
||||
map[key] = list.size();
|
||||
list.emplace_back(std::move(value));
|
||||
return list.back();
|
||||
}
|
||||
@@ -69,8 +71,8 @@ public:
|
||||
return list.emplace_back();
|
||||
}
|
||||
|
||||
std::optional<size_t> findIndex(std::string const &name) const {
|
||||
if (auto search = map.find(name); search != map.end()) {
|
||||
std::optional<size_t> findIndex(KeyT const &key) const {
|
||||
if (auto search = map.find(key); search != map.end()) {
|
||||
return search->second;
|
||||
}
|
||||
return std::nullopt;
|
||||
@@ -78,7 +80,7 @@ public:
|
||||
};
|
||||
|
||||
// An iterable of `enum` values in the half-open range [start, stop).
|
||||
template<typename EnumT>
|
||||
template<Enum EnumT>
|
||||
class EnumSeq {
|
||||
EnumT _start;
|
||||
EnumT _stop;
|
||||
@@ -169,7 +171,7 @@ template<typename IterableT>
|
||||
using ZipHolder = std::conditional_t<
|
||||
std::is_lvalue_reference_v<IterableT>,
|
||||
IterableT,
|
||||
std::remove_cv_t<std::remove_reference_t<IterableT>>>;
|
||||
std::remove_cvref_t<IterableT>>;
|
||||
|
||||
// Iterates over N containers at once, yielding tuples of N items at a time.
|
||||
// Does the same number of iterations as the first container's iterator!
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "helpers.hpp" // QualifiedEquivalent
|
||||
#include "linkdefs.hpp"
|
||||
|
||||
struct FileStackNode;
|
||||
@@ -52,7 +53,7 @@ struct Section {
|
||||
|
||||
private:
|
||||
// Template class for both const and non-const iterators over the "pieces" of this section
|
||||
template<typename SectionT>
|
||||
template<QualifiedEquivalent<Section> SectionT>
|
||||
class PiecesIterable {
|
||||
SectionT *_firstPiece;
|
||||
|
||||
|
||||
@@ -22,11 +22,12 @@ enum WarningLevel {
|
||||
};
|
||||
|
||||
enum WarningID {
|
||||
WARNING_ASSERT, // Assertions
|
||||
WARNING_DIV, // Undefined division behavior
|
||||
WARNING_OBSOLETE, // Obsolete/deprecated things
|
||||
WARNING_SHIFT, // Undefined `SHIFT` behavior
|
||||
WARNING_SHIFT_AMOUNT, // Strange `SHIFT` amount
|
||||
WARNING_ASSERT, // Assertions
|
||||
WARNING_DIV, // Undefined division behavior
|
||||
WARNING_LARGE_CONSTANT, // Constants too large
|
||||
WARNING_OBSOLETE, // Obsolete/deprecated things
|
||||
WARNING_SHIFT, // Undefined `SHIFT` behavior
|
||||
WARNING_SHIFT_AMOUNT, // Strange `SHIFT` amount
|
||||
|
||||
NB_PLAIN_WARNINGS,
|
||||
|
||||
@@ -47,6 +48,8 @@ void warning(FileStackNode const *src, uint32_t lineNo, WarningID id, char const
|
||||
void warning(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...);
|
||||
[[gnu::format(printf, 1, 2)]]
|
||||
void warning(char const *fmt, ...);
|
||||
[[gnu::format(printf, 2, 3)]]
|
||||
void scriptWarning(WarningID id, char const *fmt, ...);
|
||||
|
||||
[[gnu::format(printf, 3, 4)]]
|
||||
void error(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...);
|
||||
|
||||
+48
-11
@@ -8,7 +8,8 @@
|
||||
#include <optional>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "helpers.hpp"
|
||||
@@ -21,6 +22,13 @@ enum NumberBase {
|
||||
BASE_16 = 16,
|
||||
};
|
||||
|
||||
// File-closing functions that will not close standard streams
|
||||
int xfclose(FILE *file);
|
||||
int xclose(int fd);
|
||||
|
||||
// Measure file size with `fseek` and `ftell` idiom
|
||||
long seekSize(FILE *file);
|
||||
|
||||
// Locale-independent character class functions
|
||||
bool isNewline(int c);
|
||||
bool isBlankSpace(int c);
|
||||
@@ -29,10 +37,6 @@ bool isPrintable(int c);
|
||||
bool isUpper(int c);
|
||||
bool isLower(int c);
|
||||
bool isLetter(int c);
|
||||
bool isDigit(int c);
|
||||
bool isBinDigit(int c);
|
||||
bool isOctDigit(int c);
|
||||
bool isHexDigit(int c);
|
||||
bool isAlphanumeric(int c);
|
||||
|
||||
// Locale-independent character transform functions
|
||||
@@ -42,30 +46,63 @@ char toUpper(char c);
|
||||
bool startsIdentifier(int c);
|
||||
bool continuesIdentifier(int c);
|
||||
|
||||
uint8_t parseHexDigit(int c);
|
||||
template<uint32_t Base>
|
||||
inline constexpr bool ValidBaseV = Base > 0 && Base <= 36;
|
||||
|
||||
template<uint32_t Base>
|
||||
requires ValidBaseV<Base>
|
||||
bool isDigit(int c) {
|
||||
if constexpr (Base <= 10) {
|
||||
return c >= '0' && c < static_cast<int>('0' + Base);
|
||||
} else {
|
||||
return isDigit<10>(c) || (c >= 'A' && c < static_cast<int>('A' + Base - 10))
|
||||
|| (c >= 'a' && c < static_cast<int>('a' + Base - 10));
|
||||
}
|
||||
}
|
||||
|
||||
template<uint32_t Base>
|
||||
requires ValidBaseV<Base>
|
||||
uint8_t parseDigit(int c) {
|
||||
assume(isDigit<Base>(c));
|
||||
if constexpr (Base <= 10) {
|
||||
return c - '0';
|
||||
} else {
|
||||
// Check digit ranges from greatest to least ('a'-'z', then 'A'-'Z', then '0'-'9')
|
||||
if (c >= 'a') {
|
||||
return c - 'a' + 10;
|
||||
} else if (c >= 'A') {
|
||||
return c - 'A' + 10;
|
||||
} else {
|
||||
return parseDigit<10>(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<uint64_t> parseNumber(char const *&str, NumberBase base = BASE_AUTO);
|
||||
std::optional<uint64_t> parseWholeNumber(char const *str, NumberBase base = BASE_AUTO);
|
||||
|
||||
char const *printChar(int c);
|
||||
|
||||
struct Uppercase {
|
||||
using is_transparent = void;
|
||||
|
||||
// FNV-1a hash of an uppercased string
|
||||
constexpr size_t operator()(std::string const &str) const {
|
||||
return std::accumulate(RANGE(str), 0x811C9DC5, [](size_t hash, char c) {
|
||||
constexpr size_t operator()(std::string_view str) const {
|
||||
return std::accumulate(RANGE(str), size_t(0x811C9DC5), [](size_t hash, char c) {
|
||||
return (hash ^ toUpper(c)) * 16777619;
|
||||
});
|
||||
}
|
||||
|
||||
// Compare two strings without case-sensitivity (by converting to uppercase)
|
||||
constexpr bool operator()(std::string const &str1, std::string const &str2) const {
|
||||
constexpr bool operator()(std::string_view str1, std::string_view str2) const {
|
||||
return std::equal(RANGE(str1), RANGE(str2), [](char c1, char c2) {
|
||||
return toUpper(c1) == toUpper(c2);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// An unordered map from case-insensitive `std::string` keys to `ItemT` items
|
||||
// An unordered map from case-insensitive `std::string_view` keys to `ItemT` items
|
||||
template<typename ItemT>
|
||||
using UpperMap = std::unordered_map<std::string, ItemT, Uppercase, Uppercase>;
|
||||
using UpperMap = std::unordered_map<std::string_view, ItemT, Uppercase, Uppercase>;
|
||||
|
||||
#endif // RGBDS_UTIL_HPP
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "helpers.hpp" // Procedure
|
||||
#include "style.hpp"
|
||||
|
||||
// This macro does not evaluate its arguments unless the condition is true.
|
||||
#define verbosePrint(level, ...) \
|
||||
do { \
|
||||
@@ -30,6 +33,14 @@ bool checkVerbosity(Verbosity level);
|
||||
[[gnu::format(printf, 1, 2)]]
|
||||
void printVerbosely(char const *fmt, ...);
|
||||
|
||||
void verboseDo(Verbosity level, Procedure<> auto callback) {
|
||||
if (checkVerbosity(level)) {
|
||||
style_Set(stderr, STYLE_MAGENTA, false);
|
||||
callback();
|
||||
style_Reset(stderr);
|
||||
}
|
||||
}
|
||||
|
||||
void printVVVVVVerbosity();
|
||||
|
||||
#endif // RGBDS_VERBOSITY_HPP
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
|
||||
#define PACKAGE_VERSION_MAJOR 1
|
||||
#define PACKAGE_VERSION_MINOR 0
|
||||
#define PACKAGE_VERSION_PATCH 1
|
||||
#define PACKAGE_VERSION_PATCH 2
|
||||
// #define PACKAGE_VERSION_RC 1
|
||||
|
||||
char const *get_package_version_string();
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd January 1, 2026
|
||||
.Dd July 20, 2026
|
||||
.Dt GBZ80 7
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
+44
-1
@@ -2,7 +2,7 @@
|
||||
.\"
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd January 1, 2026
|
||||
.Dd July 20, 2026
|
||||
.Dt RGBASM-OLD 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -140,12 +140,55 @@ Deprecated in 0.9.0, removed in 1.0.0.
|
||||
Instead, use a multi-value
|
||||
.Ic CHARMAP ,
|
||||
or explicitly combine the values of individual characters.
|
||||
.Pp
|
||||
For example, instead of a multi-character string like:
|
||||
.Bd -literal -offset indent
|
||||
CHARMAP "X", $12
|
||||
CHARMAP "Y", $34
|
||||
LD BC, "XY"
|
||||
.Ed
|
||||
.Pp
|
||||
Either use a multi-value
|
||||
.Ic CHARMAP :
|
||||
.Bd -literal -offset indent
|
||||
CHARMAP "X", $12
|
||||
CHARMAP "Y", $34
|
||||
CHARMAP "<XY>", 'X', 'Y'
|
||||
LD BC, CHARVAL("<XY>", 0) << 8 | CHARVAL("<XY>", 1)
|
||||
.Ed
|
||||
.Pp
|
||||
Or combine the individual characters:
|
||||
.Bd -literal -offset indent
|
||||
CHARMAP "X", $12
|
||||
CHARMAP "Y", $34
|
||||
LD BC, 'X' << 8 | 'Y'
|
||||
.Ed
|
||||
.Pp
|
||||
Or use a single-value character:
|
||||
.Bd -literal -offset indent
|
||||
CHARMAP "X", $12
|
||||
CHARMAP "Y", $34
|
||||
CHARMAP "<XY>", 'X' << 8 | 'Y'
|
||||
LD BC, '<XY>'
|
||||
.Ed
|
||||
.Ss Treating strings as numbers
|
||||
Deprecated in 1.0.0.
|
||||
.Pp
|
||||
Instead, use character constants or the
|
||||
.Ic CHARVAL
|
||||
function.
|
||||
.Pp
|
||||
For example, instead of a string literal like
|
||||
.Ql LD A, \&"R" ,
|
||||
use a numeric character literal
|
||||
.Ql LD A, 'R' .
|
||||
.Pp
|
||||
And instead of a string expression like
|
||||
.Ql CP STRUPR("\e1") ,
|
||||
use a numeric
|
||||
.Ic CHARVAL
|
||||
expression
|
||||
.Ql CP CHARVAL(STRUPR("\e1")) .
|
||||
.Ss rgbgfx -f/--fix and -F/--fix-and-save
|
||||
Removed in 0.6.0.
|
||||
.Pp
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd January 1, 2026
|
||||
.Dd July 20, 2026
|
||||
.Dt RGBASM 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
+10
-8
@@ -2,7 +2,7 @@
|
||||
.\"
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd January 1, 2026
|
||||
.Dd July 20, 2026
|
||||
.Dt RGBASM 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -745,9 +745,14 @@ The result may be constant if
|
||||
is able to compute it.
|
||||
.It Fn SECTION symbol Ta Returns the name of the section that
|
||||
.Ar symbol
|
||||
is in.
|
||||
is defined in.
|
||||
.Ar symbol
|
||||
must have been defined already.
|
||||
If
|
||||
.Ar arg
|
||||
is the symbol
|
||||
.Ic @ ,
|
||||
this function returns the name of the current section.
|
||||
.It Fn SIZEOF arg Ta If
|
||||
.Ar arg
|
||||
is a string, this function returns the size of the section named
|
||||
@@ -1737,9 +1742,6 @@ error: Undefined symbol "LabelA"
|
||||
at c.asm(2)
|
||||
Linking failed with 1 error
|
||||
.Ed
|
||||
.Pp
|
||||
Note also that only exported symbols will appear in symbol and map files produced by
|
||||
.Xr rgblink 1 .
|
||||
.Ss Purging symbols
|
||||
.Ic PURGE
|
||||
allows you to completely remove a symbol from the symbol table, as if it had never been defined.
|
||||
@@ -2571,9 +2573,9 @@ The stack's number of entries is limited only by the amount of memory in your ma
|
||||
.Ic PUSHO
|
||||
can also take a comma-separated list of options, to push the current set and apply the argument set at the same time:
|
||||
.Bd -literal -offset indent
|
||||
PUSHO b.X, g.oOX
|
||||
DB %..XXXX..
|
||||
DW `..ooOOXX
|
||||
PUSHO b.#, g.oO#
|
||||
DB %..####..
|
||||
DW `..ooOO##
|
||||
POPO
|
||||
.Ed
|
||||
.Ss Excluding locations from backtraces
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd January 1, 2026
|
||||
.Dd July 20, 2026
|
||||
.Dt RGBDS 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd January 1, 2026
|
||||
.Dd July 20, 2026
|
||||
.Dt RGBDS 7
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd January 1, 2026
|
||||
.Dd July 20, 2026
|
||||
.Dt RGBFIX 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
+2
-1
@@ -2,7 +2,7 @@
|
||||
.\"
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd January 1, 2026
|
||||
.Dd July 20, 2026
|
||||
.Dt RGBGFX 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -13,6 +13,7 @@
|
||||
.Op Fl CmhOuVwXYZ
|
||||
.Op Fl v Op Fl v No ...
|
||||
.Op Fl a Ar attrmap | Fl A
|
||||
.Op Fl B Ar color
|
||||
.Op Fl b Ar base_ids
|
||||
.Op Fl c Ar pal_spec
|
||||
.Op Fl \-color Ar when
|
||||
|
||||
+14
-7
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd January 1, 2026
|
||||
.Dd July 20, 2026
|
||||
.Dt RGBLINK 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -257,22 +257,27 @@ an at-file, it only disables option processing within that at-file, and processi
|
||||
The default section placement algorithm tries to place sections into as few banks as possible.
|
||||
(It turns out that section placement is an NP-complete problem known as "bin packing", so
|
||||
.Nm
|
||||
does not attempt to find the optimal solution, but instead uses a "first-fit" heuristic to find a good one in a reasonable amount of time.
|
||||
There are no guarantees about where this algorithm will place sections, apart from the bank, address, and alignment constraints manually specified for the sections.)
|
||||
does not attempt to find the optimal solution, but instead uses a "first-fit" heuristic to find a good one in a reasonable amount of time.)
|
||||
There are no guarantees about where this algorithm will place sections, apart from the bank, address, and alignment constraints manually specified for the sections.
|
||||
.Pp
|
||||
.Dq Scrambling
|
||||
instead places sections into a given pool of banks, trying to minimize the number of sections sharing a given bank.
|
||||
This is useful to catch broken bank assumptions, such as expecting two different sections to land in the same bank (that is not guaranteed unless both are manually assigned the same bank number).
|
||||
There are still no guarantees about where this algorithm will place sections, apart from the bank pool size and any manually specified section constraints.
|
||||
.Pp
|
||||
A scrambling spec is a comma-separated list of region specs.
|
||||
A trailing comma is allowed, as well as whitespace between all specs and their components.
|
||||
Each region spec has the following form:
|
||||
.D1 Ar region Ns Op = Ns Ar size
|
||||
.Ar region
|
||||
specifies the section type, and
|
||||
.Ar size
|
||||
specifies the number of banks in the pool for that section type.
|
||||
.Ar region
|
||||
must be one of the following (case-insensitive), while
|
||||
.Ar size
|
||||
must be a positive decimal integer between 1 and the corresponding maximum.
|
||||
Certain regions allow omitting the size, in which case it defaults to its max value.
|
||||
Certain regions allow omitting the size, in which case it defaults to its maximum value.
|
||||
.Bl -column "Region name" "Max value" "Size optional"
|
||||
Region name Ta Max size Ta Size optional
|
||||
.Cm romx Ta 65535 Ta \&No
|
||||
@@ -285,14 +290,14 @@ A
|
||||
of 0 disables scrambling for that region.
|
||||
.Pp
|
||||
For example,
|
||||
.Ql romx=64,wramx=4
|
||||
.Ql romx=64,wramx,sram=4
|
||||
will scramble
|
||||
.Ic ROMX
|
||||
sections among ROM banks 1 to 64,
|
||||
.Ic WRAMX
|
||||
sections among RAM banks 1 to 4, and will not scramble
|
||||
sections among RAM banks 1 to 7, and
|
||||
.Ic SRAM
|
||||
sections.
|
||||
sections among SRAM banks 0 to 3.
|
||||
.Pp
|
||||
Later region specs override earlier ones; for example,
|
||||
.Ql romx=42, Romx=0
|
||||
@@ -369,6 +374,8 @@ for
|
||||
Warn when dividing the smallest negative integer (-2**31) by -1, which yields itself due to integer overflow.
|
||||
This warning is enabled by
|
||||
.Fl Wall .
|
||||
.It Fl Wno-large-constant
|
||||
Warn when a constant too large to fit in a signed 32-bit integer is encountered.
|
||||
.It Fl Wno-obsolete
|
||||
Warn when obsolete features are encountered, which have been deprecated and may later be removed.
|
||||
.It Fl Wshift
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd January 1, 2026
|
||||
.Dd July 20, 2026
|
||||
.Dt RGBLINK 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Generated by CMake
|
||||
/.version.cpp
|
||||
+78
-54
@@ -1,48 +1,44 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
configure_file(version.cpp _version.cpp ESCAPE_QUOTES)
|
||||
|
||||
set(common_src
|
||||
add_library(common OBJECT
|
||||
"extern/getopt.cpp"
|
||||
"cli.cpp"
|
||||
"diagnostics.cpp"
|
||||
"style.cpp"
|
||||
"usage.cpp"
|
||||
"util.cpp"
|
||||
"_version.cpp"
|
||||
)
|
||||
"version.cpp"
|
||||
)
|
||||
target_compile_definitions(common PRIVATE "BUILD_VERSION_STRING=\"${GIT_REV}\"")
|
||||
|
||||
find_package(BISON 3.0.0 REQUIRED)
|
||||
# BISON 4.0 deprecates passing this BISON_FLAGS string to `bison_target`'s `COMPILE_FLAGS`,
|
||||
# in favor of passing a list to `OPTIONS`, but that would require dropping support for CMake <4.0.
|
||||
set(BISON_FLAGS "-Wall -Dlr.type=ielr")
|
||||
# Set some optimization flags on versions that support them
|
||||
# Set some flags on versions that support them.
|
||||
if(BISON_VERSION VERSION_GREATER_EQUAL "3.5")
|
||||
set(BISON_FLAGS "${BISON_FLAGS} -Dparse.lac=full -Dapi.token.raw=true")
|
||||
set(BISON_FLAGS "${BISON_FLAGS} -Dparse.lac=full -Dapi.token.raw=true -Wdangling-alias")
|
||||
endif()
|
||||
if(BISON_VERSION VERSION_GREATER_EQUAL "3.6")
|
||||
set(BISON_FLAGS "${BISON_FLAGS} -Dparse.error=detailed")
|
||||
else()
|
||||
set(BISON_FLAGS "${BISON_FLAGS} -Dparse.error=verbose")
|
||||
endif()
|
||||
if(BISON_VERSION VERSION_GREATER_EQUAL "3.7")
|
||||
set(BISON_FLAGS "${BISON_FLAGS} -Wcounterexamples")
|
||||
endif()
|
||||
|
||||
BISON_TARGET(ASM_PARSER "asm/parser.y"
|
||||
"${PROJECT_SOURCE_DIR}/src/asm/parser.cpp"
|
||||
COMPILE_FLAGS "${BISON_FLAGS}"
|
||||
DEFINES_FILE "${PROJECT_SOURCE_DIR}/src/asm/parser.hpp"
|
||||
)
|
||||
|
||||
BISON_TARGET(LINKER_SCRIPT_PARSER "link/script.y"
|
||||
"${PROJECT_SOURCE_DIR}/src/link/script.cpp"
|
||||
COMPILE_FLAGS "${BISON_FLAGS}"
|
||||
DEFINES_FILE "${PROJECT_SOURCE_DIR}/src/link/script.hpp"
|
||||
)
|
||||
|
||||
set(rgbasm_src
|
||||
"${BISON_ASM_PARSER_OUTPUT_SOURCE}"
|
||||
bison_target(asm_parser "asm/parser.y"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/parser.cpp"
|
||||
COMPILE_FLAGS "${BISON_FLAGS}")
|
||||
add_executable(rgbasm $<TARGET_OBJECTS:common>
|
||||
"${BISON_asm_parser_OUTPUT_SOURCE}"
|
||||
"asm/actions.cpp"
|
||||
"asm/charmap.cpp"
|
||||
"asm/fixpoint.cpp"
|
||||
"asm/format.cpp"
|
||||
"asm/fstack.cpp"
|
||||
"asm/intern.cpp"
|
||||
"asm/lexer.cpp"
|
||||
"asm/macro.cpp"
|
||||
"asm/main.cpp"
|
||||
@@ -57,10 +53,15 @@ set(rgbasm_src
|
||||
"linkdefs.cpp"
|
||||
"opmath.cpp"
|
||||
"verbosity.cpp"
|
||||
)
|
||||
)
|
||||
cmake_path(GET BISON_asm_parser_OUTPUT_HEADER PARENT_PATH parser_header_dir)
|
||||
target_include_directories(rgbasm PRIVATE "${parser_header_dir}")
|
||||
|
||||
set(rgblink_src
|
||||
"${BISON_LINKER_SCRIPT_PARSER_OUTPUT_SOURCE}"
|
||||
bison_target(linker_script_parser "link/script.y"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/script.cpp"
|
||||
COMPILE_FLAGS "${BISON_FLAGS}")
|
||||
add_executable(rgblink $<TARGET_OBJECTS:common>
|
||||
"${BISON_linker_script_parser_OUTPUT_SOURCE}"
|
||||
"link/assign.cpp"
|
||||
"link/fstack.cpp"
|
||||
"link/lexer.cpp"
|
||||
@@ -78,16 +79,18 @@ set(rgblink_src
|
||||
"linkdefs.cpp"
|
||||
"opmath.cpp"
|
||||
"verbosity.cpp"
|
||||
)
|
||||
)
|
||||
cmake_path(GET BISON_linker_script_parser_OUTPUT_HEADER PARENT_PATH parser_header_dir)
|
||||
target_include_directories(rgblink PRIVATE "${parser_header_dir}")
|
||||
|
||||
set(rgbfix_src
|
||||
add_executable(rgbfix $<TARGET_OBJECTS:common>
|
||||
"fix/fix.cpp"
|
||||
"fix/main.cpp"
|
||||
"fix/mbc.cpp"
|
||||
"fix/warning.cpp"
|
||||
)
|
||||
)
|
||||
|
||||
set(rgbgfx_src
|
||||
add_executable(rgbgfx $<TARGET_OBJECTS:common>
|
||||
"gfx/color_set.cpp"
|
||||
"gfx/main.cpp"
|
||||
"gfx/pal_packing.cpp"
|
||||
@@ -100,32 +103,53 @@ set(rgbgfx_src
|
||||
"gfx/rgba.cpp"
|
||||
"gfx/warning.cpp"
|
||||
"verbosity.cpp"
|
||||
)
|
||||
)
|
||||
|
||||
foreach(PROG "asm" "fix" "gfx" "link")
|
||||
add_executable(rgb${PROG}
|
||||
${rgb${PROG}_src}
|
||||
${common_src}
|
||||
)
|
||||
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()
|
||||
install(TARGETS rgbasm rgblink rgbfix rgbgfx RUNTIME COMPONENT binaries)
|
||||
# Tests expect the binaries to end up in-source; this is more acceptable than the entire artifact dir.
|
||||
set_target_properties(rgbasm rgblink rgbfix rgbgfx PROPERTIES
|
||||
# The generator expression (even if a no-op) stops muti-config generators using a of "per-configuration subdirectory".
|
||||
RUNTIME_OUTPUT_DIRECTORY $<1:${CMAKE_CURRENT_SOURCE_DIR}/..>)
|
||||
target_link_libraries(rgbgfx PRIVATE PNG::PNG)
|
||||
# Copy the DLLs in the output directory so the program can be run for testing without having to `install`.
|
||||
# From https://cmake.org/cmake/help/v4.3/manual/cmake-generator-expressions.7.html#genex:TARGET_RUNTIME_DLLS.
|
||||
add_custom_command(TARGET rgbgfx POST_BUILD COMMENT "Copying rgbgfx's DLLs"
|
||||
# It would be nice to symlink instead, but that only supports one file at a time.
|
||||
COMMAND ${CMAKE_COMMAND} -E copy -t $<TARGET_FILE_DIR:rgbgfx> $<TARGET_RUNTIME_DLLS:rgbgfx>
|
||||
COMMAND_EXPAND_LISTS VERBATIM)
|
||||
|
||||
if(LIBPNG_FOUND) # pkg-config
|
||||
target_include_directories(rgbgfx PRIVATE ${LIBPNG_INCLUDE_DIRS})
|
||||
target_link_directories(rgbgfx PRIVATE ${LIBPNG_LIBRARY_DIRS})
|
||||
target_link_libraries(rgbgfx PRIVATE ${LIBPNG_LIBRARIES})
|
||||
else()
|
||||
target_compile_definitions(rgbgfx PRIVATE ${PNG_DEFINITIONS})
|
||||
target_include_directories(rgbgfx PRIVATE ${PNG_INCLUDE_DIRS})
|
||||
target_link_libraries(rgbgfx PRIVATE ${PNG_LIBRARIES})
|
||||
endif()
|
||||
|
||||
include(CheckLibraryExists)
|
||||
check_library_exists("m" "sin" "" HAS_LIBM)
|
||||
if(HAS_LIBM)
|
||||
target_link_libraries(rgbasm PRIVATE "m")
|
||||
if(WIN32)
|
||||
# CMake 3.27 is required for `$<INSTALL_PREFIX>`, but is only needed when cross-compiling for MinGW.
|
||||
if(CMAKE_VERSION VERSION_LESS "3.27")
|
||||
message(WARNING "CMake 3.27 or higher is required for `\$<INSTALL_PREFIX>` inside `install(CODE)`")
|
||||
endif()
|
||||
# On Windows, you don't link against DLLs directly, but against an import library for that DLL.
|
||||
# This leads to, sometimes, the DLL being stored in a directory far away from the import library itself!
|
||||
set(DLL_SEARCH_DIRS CACHE PATH "List of directories in which DLLs will be searched")
|
||||
cmake_path(CONVERT "${DLL_SEARCH_DIRS}" TO_CMAKE_PATH_LIST search_dirs NORMALIZE)
|
||||
# TODO: we've only tested this with MinGW, but it may not work with other uses of the variable.
|
||||
# Anyone who knows how to handle this better, feel free to submit a patch!
|
||||
if(DEFINED CMAKE_FIND_ROOT_PATH)
|
||||
list(APPEND search_dirs "${CMAKE_FIND_ROOT_PATH}/bin")
|
||||
endif()
|
||||
# Escape the search paths in a form that makes them suitable for splatting into the CMake install script.
|
||||
list(TRANSFORM search_dirs REPLACE "([\\$\"])" "\\\\1") # Neutralize escapes and variable refs...
|
||||
list(TRANSFORM search_dirs REPLACE "^(.+)\$" "\"\\1\"") # ...and quote each argument.
|
||||
list(JOIN search_dirs " " search_dirs_splat)
|
||||
# This is a modified version of the install code generated by
|
||||
# `install(TARGETS rgbgfx RUNTIME_DEPENDENCIES)`.
|
||||
# Unfortunately, that `install(TARGETS)` is outright not supported when cross-compiling,
|
||||
# and working around that has CMake fail to resolve the DLL locations with MinGW,
|
||||
# due to not specifying any `DIRECTORIES` and the system search path being ineffective.
|
||||
install(CODE "\
|
||||
file(GET_RUNTIME_DEPENDENCIES
|
||||
RESOLVED_DEPENDENCIES_VAR rgbgfx_deps
|
||||
EXECUTABLES \"$<TARGET_FILE:rgbgfx>\"
|
||||
DIRECTORIES ${search_dirs_splat} \"$<TARGET_FILE_DIR:ZLIB::ZLIB>\" \"$<TARGET_FILE_DIR:PNG::PNG>\"
|
||||
PRE_EXCLUDE_REGEXES \"^kernel32\\\\.dll$\" \"^msvcrt.?.?\\\\.dll\" \"^api-ms-win-.*\\\\.dll$\")
|
||||
foreach(rgbgfx_dep IN LISTS rgbgfx_deps)
|
||||
file(INSTALL DESTINATION \"$<INSTALL_PREFIX>/${CMAKE_INSTALL_BINDIR}\" TYPE SHARED_LIBRARY FILES \${rgbgfx_dep}
|
||||
FOLLOW_SYMLINK_CHAIN)
|
||||
endforeach()"
|
||||
COMPONENT shared-libs EXCLUDE_FROM_ALL) # Most platforms install those separately.
|
||||
endif()
|
||||
|
||||
+28
-30
@@ -18,10 +18,12 @@
|
||||
#include "extern/utf8decoder.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "linkdefs.hpp"
|
||||
#include "util.hpp" // xfclose, seekSize
|
||||
|
||||
#include "asm/charmap.hpp"
|
||||
#include "asm/format.hpp"
|
||||
#include "asm/fstack.hpp"
|
||||
#include "asm/intern.hpp"
|
||||
#include "asm/lexer.hpp"
|
||||
#include "asm/output.hpp"
|
||||
#include "asm/rpn.hpp" // Expression
|
||||
@@ -146,16 +148,15 @@ std::optional<std::string> act_ReadFile(std::string const &name, uint32_t maxLen
|
||||
}
|
||||
return "";
|
||||
}
|
||||
Defer closeFile{[&] { fclose(file); }};
|
||||
Defer closeFile{[&] { xfclose(file); }};
|
||||
|
||||
size_t readSize = maxLen;
|
||||
if (fseek(file, 0, SEEK_END) == 0) {
|
||||
if (long fileSize = seekSize(file); fileSize != -1) {
|
||||
// If the file is seekable and shorter than the max length,
|
||||
// just read as many bytes as there are
|
||||
if (long fileSize = ftell(file); static_cast<size_t>(fileSize) < readSize) {
|
||||
if (static_cast<size_t>(fileSize) < readSize) {
|
||||
readSize = fileSize;
|
||||
}
|
||||
fseek(file, 0, SEEK_SET);
|
||||
// LCOV_EXCL_START
|
||||
} else if (errno != ESPIPE) {
|
||||
error(
|
||||
@@ -278,19 +279,18 @@ static void errorInvalidUTF8Byte(uint8_t byte, char const *functionName) {
|
||||
|
||||
size_t act_StringLen(std::string const &str, bool printErrors) {
|
||||
size_t len = 0;
|
||||
uint32_t state = UTF8_ACCEPT;
|
||||
uint32_t codepoint = 0;
|
||||
Utf8Decoder decoder;
|
||||
|
||||
for (char c : str) {
|
||||
uint8_t byte = static_cast<uint8_t>(c);
|
||||
|
||||
switch (decode(&state, &codepoint, byte)) {
|
||||
switch (decoder.update(byte)) {
|
||||
case UTF8_REJECT:
|
||||
if (printErrors) {
|
||||
errorInvalidUTF8Byte(byte, "STRLEN");
|
||||
}
|
||||
state = UTF8_ACCEPT;
|
||||
// fallthrough
|
||||
decoder.state = UTF8_ACCEPT;
|
||||
[[fallthrough]];
|
||||
case UTF8_ACCEPT:
|
||||
++len;
|
||||
break;
|
||||
@@ -298,7 +298,7 @@ size_t act_StringLen(std::string const &str, bool printErrors) {
|
||||
}
|
||||
|
||||
// Check for partial code point.
|
||||
if (state != UTF8_ACCEPT) {
|
||||
if (decoder.state != UTF8_ACCEPT) {
|
||||
if (printErrors) {
|
||||
error("STRLEN: Incomplete UTF-8 character");
|
||||
}
|
||||
@@ -316,17 +316,16 @@ std::string
|
||||
|
||||
size_t strLen = str.length();
|
||||
size_t index = 0;
|
||||
uint32_t state = UTF8_ACCEPT;
|
||||
uint32_t codepoint = 0;
|
||||
Utf8Decoder decoder;
|
||||
uint32_t curIdx = 0;
|
||||
|
||||
// Advance to starting index in source string.
|
||||
while (index < strLen && curIdx < start) {
|
||||
switch (decode(&state, &codepoint, str[index])) {
|
||||
switch (decoder.update(str[index])) {
|
||||
case UTF8_REJECT:
|
||||
errorInvalidUTF8Byte(str[index], "STRSLICE");
|
||||
state = UTF8_ACCEPT;
|
||||
// fallthrough
|
||||
decoder.state = UTF8_ACCEPT;
|
||||
[[fallthrough]];
|
||||
case UTF8_ACCEPT:
|
||||
++curIdx;
|
||||
break;
|
||||
@@ -348,11 +347,11 @@ std::string
|
||||
|
||||
// Advance to ending index in source string.
|
||||
while (index < strLen && curIdx < stop) {
|
||||
switch (decode(&state, &codepoint, str[index])) {
|
||||
switch (decoder.update(str[index])) {
|
||||
case UTF8_REJECT:
|
||||
errorInvalidUTF8Byte(str[index], "STRSLICE");
|
||||
state = UTF8_ACCEPT;
|
||||
// fallthrough
|
||||
decoder.state = UTF8_ACCEPT;
|
||||
[[fallthrough]];
|
||||
case UTF8_ACCEPT:
|
||||
++curIdx;
|
||||
break;
|
||||
@@ -361,7 +360,7 @@ std::string
|
||||
}
|
||||
|
||||
// Check for partial code point.
|
||||
if (state != UTF8_ACCEPT) {
|
||||
if (decoder.state != UTF8_ACCEPT) {
|
||||
error("STRSLICE: Incomplete UTF-8 character");
|
||||
++curIdx;
|
||||
}
|
||||
@@ -386,17 +385,16 @@ std::string act_StringSub(std::string const &str, int32_t negPos, std::optional<
|
||||
|
||||
size_t strLen = str.length();
|
||||
size_t index = 0;
|
||||
uint32_t state = UTF8_ACCEPT;
|
||||
uint32_t codepoint = 0;
|
||||
Utf8Decoder decoder;
|
||||
uint32_t curPos = 1;
|
||||
|
||||
// Advance to starting position in source string.
|
||||
while (index < strLen && curPos < pos) {
|
||||
switch (decode(&state, &codepoint, str[index])) {
|
||||
switch (decoder.update(str[index])) {
|
||||
case UTF8_REJECT:
|
||||
errorInvalidUTF8Byte(str[index], "STRSUB");
|
||||
state = UTF8_ACCEPT;
|
||||
// fallthrough
|
||||
decoder.state = UTF8_ACCEPT;
|
||||
[[fallthrough]];
|
||||
case UTF8_ACCEPT:
|
||||
++curPos;
|
||||
break;
|
||||
@@ -417,11 +415,11 @@ std::string act_StringSub(std::string const &str, int32_t negPos, std::optional<
|
||||
|
||||
// Compute the result length in bytes.
|
||||
while (index < strLen && curLen < len) {
|
||||
switch (decode(&state, &codepoint, str[index])) {
|
||||
switch (decoder.update(str[index])) {
|
||||
case UTF8_REJECT:
|
||||
errorInvalidUTF8Byte(str[index], "STRSUB");
|
||||
state = UTF8_ACCEPT;
|
||||
// fallthrough
|
||||
decoder.state = UTF8_ACCEPT;
|
||||
[[fallthrough]];
|
||||
case UTF8_ACCEPT:
|
||||
++curLen;
|
||||
break;
|
||||
@@ -430,7 +428,7 @@ std::string act_StringSub(std::string const &str, int32_t negPos, std::optional<
|
||||
}
|
||||
|
||||
// Check for partial code point.
|
||||
if (state != UTF8_ACCEPT) {
|
||||
if (decoder.state != UTF8_ACCEPT) {
|
||||
error("STRSUB: Incomplete UTF-8 character");
|
||||
++curLen;
|
||||
}
|
||||
@@ -607,7 +605,7 @@ std::string act_StringFormat(
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string act_SectionName(std::string const &symName) {
|
||||
std::string act_SectionName(InternedStr symName) {
|
||||
Symbol *sym = sym_FindScopedValidSymbol(symName);
|
||||
if (!sym) {
|
||||
if (sym_IsPurgedScoped(symName)) {
|
||||
@@ -625,7 +623,7 @@ std::string act_SectionName(std::string const &symName) {
|
||||
return section->name;
|
||||
}
|
||||
|
||||
void act_CompoundAssignment(std::string const &symName, RPNCommand op, int32_t constValue) {
|
||||
void act_CompoundAssignment(InternedStr symName, RPNCommand op, int32_t constValue) {
|
||||
Expression oldExpr, constExpr, newExpr;
|
||||
oldExpr.makeSymbol(symName);
|
||||
constExpr.makeNumber(constValue);
|
||||
|
||||
+70
-60
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "asm/charmap.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <concepts> // predicate
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <stack>
|
||||
@@ -19,27 +21,57 @@
|
||||
#include "itertools.hpp" // InsertionOrderedMap
|
||||
#include "util.hpp"
|
||||
|
||||
#include "asm/intern.hpp"
|
||||
#include "asm/warning.hpp"
|
||||
|
||||
// Charmaps are stored using a structure known as "trie".
|
||||
// Essentially a tree, where each nodes stores a single character's worth of info:
|
||||
// whether there exists a mapping that ends at the current character,
|
||||
static InternedStr mainCharmapName;
|
||||
|
||||
static bool compareNode(std::pair<char, size_t> edge, char c) {
|
||||
return edge.first < c;
|
||||
}
|
||||
|
||||
struct CharmapNode {
|
||||
std::vector<int32_t> value; // The mapped value, if there exists a mapping that ends here
|
||||
// The mapped value, if there exists a mapping that ends here; empty for non-terminal nodes.
|
||||
std::vector<int32_t> value;
|
||||
// Trie edges, pairing each next character with an index into the parent `Charmap`'s `nodes`.
|
||||
// Sorted by character. Indexes must be nonzero.
|
||||
// These MUST be indexes and not pointers, because pointers get invalidated by reallocation!
|
||||
size_t next[256]; // Indexes of where to go next, 0 = nowhere
|
||||
std::vector<std::pair<char, size_t>> children;
|
||||
|
||||
bool isTerminal() const { return !value.empty(); }
|
||||
|
||||
size_t nextIndex(char c) const {
|
||||
if (auto pos = std::lower_bound(RANGE(children), c, compareNode);
|
||||
pos != children.end() && pos->first == c) {
|
||||
assume(pos->second != 0);
|
||||
return pos->second;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct Charmap {
|
||||
std::string name;
|
||||
std::vector<CharmapNode> nodes; // first node is reserved for the root node
|
||||
InternedStr name;
|
||||
std::vector<CharmapNode> nodes; // Trie of mappings (first node is reserved for the root node)
|
||||
|
||||
size_t nextIndexOrAdd(size_t nodeIdx, char c) {
|
||||
std::vector<std::pair<char, size_t>> &children = nodes[nodeIdx].children;
|
||||
if (auto pos = std::lower_bound(RANGE(children), c, compareNode);
|
||||
pos != children.end() && pos->first == c) {
|
||||
assume(pos->second != 0);
|
||||
return pos->second;
|
||||
} else {
|
||||
auto nextIdx = children.emplace(pos, c, nodes.size());
|
||||
nodes.emplace_back();
|
||||
return nextIdx->second;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Traverse the trie depth-first to derive the character mappings in definition order
|
||||
template<typename CallbackFnT>
|
||||
bool forEachChar(Charmap const &charmap, CallbackFnT callback) {
|
||||
bool forEachChar(
|
||||
Charmap const &charmap, std::predicate<size_t, std::string const &> auto callback
|
||||
) {
|
||||
// clang-format off: nested initializers
|
||||
for (std::stack<std::pair<size_t, std::string>> prefixes({{0, ""}}); !prefixes.empty();) {
|
||||
// clang-format on
|
||||
@@ -49,23 +81,26 @@ bool forEachChar(Charmap const &charmap, CallbackFnT callback) {
|
||||
if (node.isTerminal() && !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)});
|
||||
}
|
||||
for (auto const &[c, nextIdx] : node.children) {
|
||||
assume(nextIdx);
|
||||
prefixes.push({nextIdx, mapping + c});
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static InsertionOrderedMap<Charmap> charmaps;
|
||||
static InsertionOrderedMap<InternedStr, Charmap> charmaps;
|
||||
|
||||
static Charmap *currentCharmap;
|
||||
static std::stack<Charmap *> charmapStack;
|
||||
|
||||
void charmap_Init() {
|
||||
mainCharmapName = intern("main");
|
||||
charmap_New(mainCharmapName, nullptr);
|
||||
}
|
||||
|
||||
bool charmap_ForEach(
|
||||
void (*mapFunc)(std::string const &),
|
||||
void (*charFunc)(std::string const &, std::vector<int32_t>)
|
||||
void (*mapFunc)(InternedStr), void (*charFunc)(std::string const &, std::vector<int32_t>)
|
||||
) {
|
||||
for (Charmap const &charmap : charmaps) {
|
||||
std::map<size_t, std::string> mappings;
|
||||
@@ -82,7 +117,7 @@ bool charmap_ForEach(
|
||||
return !charmaps.empty();
|
||||
}
|
||||
|
||||
void charmap_New(std::string const &name, std::string const *baseName) {
|
||||
void charmap_New(InternedStr name, InternedStr const *baseName) {
|
||||
std::optional<size_t> baseIdx = std::nullopt;
|
||||
|
||||
if (baseName != nullptr) {
|
||||
@@ -109,7 +144,7 @@ void charmap_New(std::string const &name, std::string const *baseName) {
|
||||
currentCharmap = &charmap;
|
||||
}
|
||||
|
||||
void charmap_Set(std::string const &name) {
|
||||
void charmap_Set(InternedStr name) {
|
||||
if (auto index = charmaps.findIndex(name); index) {
|
||||
currentCharmap = &charmaps[*index];
|
||||
} else {
|
||||
@@ -144,55 +179,25 @@ void charmap_Add(std::string const &mapping, std::vector<int32_t> &&value) {
|
||||
}
|
||||
|
||||
Charmap &charmap = *currentCharmap;
|
||||
|
||||
size_t nodeIdx = 0;
|
||||
|
||||
for (char c : mapping) {
|
||||
size_t &nextIdxRef = charmap.nodes[nodeIdx].next[static_cast<uint8_t>(c)];
|
||||
size_t nextIdx = nextIdxRef;
|
||||
|
||||
if (!nextIdx) {
|
||||
// Switch to and zero-init the new node
|
||||
nextIdxRef = charmap.nodes.size();
|
||||
nextIdx = nextIdxRef;
|
||||
// This may reallocate `charmap.nodes` and invalidate `nextIdxRef`,
|
||||
// which is why we keep the actual value in `nextIdx`
|
||||
charmap.nodes.emplace_back();
|
||||
}
|
||||
|
||||
nodeIdx = nextIdx;
|
||||
nodeIdx = charmap.nextIndexOrAdd(nodeIdx, c);
|
||||
}
|
||||
|
||||
CharmapNode &node = charmap.nodes[nodeIdx];
|
||||
|
||||
if (node.isTerminal()) {
|
||||
warning(WARNING_CHARMAP_REDEF, "Overriding charmap mapping");
|
||||
}
|
||||
|
||||
std::swap(node.value, value);
|
||||
}
|
||||
|
||||
bool charmap_HasChar(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 false;
|
||||
}
|
||||
}
|
||||
|
||||
return charmap.nodes[nodeIdx].isTerminal();
|
||||
}
|
||||
|
||||
static CharmapNode const *charmapEntry(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)];
|
||||
|
||||
nodeIdx = charmap.nodes[nodeIdx].nextIndex(c);
|
||||
if (!nodeIdx) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -201,6 +206,11 @@ static CharmapNode const *charmapEntry(std::string const &mapping) {
|
||||
return &charmap.nodes[nodeIdx];
|
||||
}
|
||||
|
||||
bool charmap_HasChar(std::string const &mapping) {
|
||||
CharmapNode const *node = charmapEntry(mapping);
|
||||
return node && node->isTerminal();
|
||||
}
|
||||
|
||||
size_t charmap_CharSize(std::string const &mapping) {
|
||||
CharmapNode const *node = charmapEntry(mapping);
|
||||
return node && node->isTerminal() ? node->value.size() : 0;
|
||||
@@ -231,7 +241,7 @@ size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output
|
||||
size_t inputIdx = 0;
|
||||
|
||||
for (size_t nodeIdx = 0; inputIdx < input.length();) {
|
||||
nodeIdx = charmap.nodes[nodeIdx].next[static_cast<uint8_t>(input[inputIdx])];
|
||||
nodeIdx = charmap.nodes[nodeIdx].nextIndex(input[inputIdx]);
|
||||
|
||||
if (!nodeIdx) {
|
||||
break;
|
||||
@@ -263,15 +273,14 @@ size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output
|
||||
} else if (inputIdx < input.length()) { // No match found, but there is some input left
|
||||
size_t codepointLen = 0;
|
||||
// This will write the codepoint's value to `output`, little-endian
|
||||
for (uint32_t state = UTF8_ACCEPT, codepoint = 0;
|
||||
inputIdx + codepointLen < input.length();) {
|
||||
if (decode(&state, &codepoint, input[inputIdx + codepointLen]) == UTF8_REJECT) {
|
||||
for (Utf8Decoder decoder; inputIdx + codepointLen < input.length();) {
|
||||
if (decoder.update(input[inputIdx + codepointLen]) == UTF8_REJECT) {
|
||||
error("Input string is not valid UTF-8");
|
||||
codepointLen = 1;
|
||||
break;
|
||||
}
|
||||
++codepointLen;
|
||||
if (state == UTF8_ACCEPT) {
|
||||
if (decoder.state == UTF8_ACCEPT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -285,11 +294,12 @@ size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output
|
||||
// Warn if this character is not mapped but any others are
|
||||
if (int firstChar = input[inputIdx]; charmap.nodes.size() > 1) {
|
||||
warning(WARNING_UNMAPPED_CHAR_1, "Unmapped character %s", printChar(firstChar));
|
||||
} else if (charmap.name != DEFAULT_CHARMAP_NAME) {
|
||||
} else if (charmap.name != mainCharmapName) {
|
||||
warning(
|
||||
WARNING_UNMAPPED_CHAR_2,
|
||||
"Unmapped character %s not in `" DEFAULT_CHARMAP_NAME "` charmap",
|
||||
printChar(firstChar)
|
||||
"Unmapped character %s not in `%s` charmap",
|
||||
printChar(firstChar),
|
||||
mainCharmapName.c_str()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <numbers>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "helpers.hpp" // assume
|
||||
|
||||
static constexpr double tau = std::numbers::pi * 2;
|
||||
|
||||
static double fix2double(int32_t i, int32_t q) {
|
||||
@@ -21,7 +23,8 @@ static int32_t double2fix(double d, int32_t q) {
|
||||
if (isinf(d)) {
|
||||
return d < 0 ? INT32_MIN : INT32_MAX;
|
||||
}
|
||||
return static_cast<int32_t>(round(d * pow(2.0, q)));
|
||||
double v = round(d * pow(2.0, q));
|
||||
return v < INT32_MIN ? INT32_MIN : v > INT32_MAX ? INT32_MAX : static_cast<int32_t>(v);
|
||||
}
|
||||
|
||||
static double turn2rad(double t) {
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ size_t FormatSpec::parseSpec(char const *spec) {
|
||||
padZero = true;
|
||||
}
|
||||
// <width>
|
||||
if (isDigit(spec[i])) {
|
||||
if (isDigit<10>(spec[i])) {
|
||||
width = parseSpecNumber();
|
||||
}
|
||||
// <frac>
|
||||
|
||||
+13
-14
@@ -25,6 +25,7 @@
|
||||
#include "platform.hpp" // strncasecmp
|
||||
#include "verbosity.hpp"
|
||||
|
||||
#include "asm/intern.hpp"
|
||||
#include "asm/lexer.hpp"
|
||||
#include "asm/macro.hpp"
|
||||
#include "asm/main.hpp"
|
||||
@@ -47,14 +48,14 @@ struct Context {
|
||||
bool isForLoop = false;
|
||||
int32_t forValue = 0;
|
||||
int32_t forStep = 0;
|
||||
std::string forName{};
|
||||
InternedStr forName{};
|
||||
};
|
||||
|
||||
static std::stack<Context> contextStack;
|
||||
|
||||
// The first include path for `fstk_FindFile` to try is none at all
|
||||
static std::vector<std::string> includePaths = {""}; // -I
|
||||
static std::deque<std::string> preIncludeNames; // -P
|
||||
static std::deque<std::string> preIncludeStack; // -P
|
||||
static bool failedOnMissingInclude = false;
|
||||
|
||||
void FileStackNode::printBacktrace(uint32_t curLineNo) const {
|
||||
@@ -124,9 +125,9 @@ void fstk_VerboseOutputConfig() {
|
||||
}
|
||||
}
|
||||
// -P/--preinclude
|
||||
if (!preIncludeNames.empty()) {
|
||||
if (!preIncludeStack.empty()) {
|
||||
fputs("\tPreincluded files:\n", stderr);
|
||||
for (std::string const &name : preIncludeNames) {
|
||||
for (std::string const &name : preIncludeStack) {
|
||||
fprintf(stderr, "\t - %s\n", name.c_str());
|
||||
}
|
||||
}
|
||||
@@ -168,7 +169,7 @@ void fstk_AddIncludePath(std::string const &path) {
|
||||
}
|
||||
|
||||
void fstk_AddPreIncludeFile(std::string const &path) {
|
||||
preIncludeNames.emplace_front(path);
|
||||
preIncludeStack.emplace_front(path);
|
||||
}
|
||||
|
||||
static bool isValidFilePath(std::string const &path) {
|
||||
@@ -309,7 +310,7 @@ static void
|
||||
}
|
||||
}
|
||||
fileInfoName.append(NODE_SEPARATOR);
|
||||
fileInfoName.append(macro.name);
|
||||
fileInfoName.append(macro.name.str());
|
||||
|
||||
auto fileInfo = std::make_shared<FileStackNode>(NODE_MACRO, fileInfoName, isQuiet);
|
||||
assume(!contextStack.empty()); // The top level context cannot be a MACRO
|
||||
@@ -389,9 +390,7 @@ bool fstk_RunInclude(std::string const &path, bool isQuiet) {
|
||||
return fstk_FileError(path, "`INCLUDE`");
|
||||
}
|
||||
|
||||
void fstk_RunMacro(
|
||||
std::string const ¯oName, std::shared_ptr<MacroArgs> macroArgs, bool isQuiet
|
||||
) {
|
||||
void fstk_RunMacro(InternedStr macroName, std::shared_ptr<MacroArgs> macroArgs, bool isQuiet) {
|
||||
auto makeSuggestion = [¯oName, ¯oArgs]() -> std::optional<std::string> {
|
||||
std::shared_ptr<std::string> arg = macroArgs->getArg(1);
|
||||
if (!arg) {
|
||||
@@ -402,14 +401,14 @@ void fstk_RunMacro(
|
||||
static char const *types[] = {"EQUS", "EQU", "RB", "RW", "RL", "="};
|
||||
for (char const *type : types) {
|
||||
if (strncasecmp(str, type, strlen(type)) == 0) {
|
||||
return "\"DEF "s + macroName + " " + type + " ...\"";
|
||||
return "\"DEF "s + macroName.str() + " " + type + " ...\"";
|
||||
}
|
||||
}
|
||||
if (strncasecmp(str, "SET", literal_strlen("SET")) == 0) {
|
||||
return "\"DEF "s + macroName + " = ...\"";
|
||||
return "\"DEF "s + macroName.str() + " = ...\"";
|
||||
}
|
||||
if (str[0] == ':') {
|
||||
return "a label \""s + macroName + (str[1] == ':' ? "::" : ":") + "\"";
|
||||
return "a label \""s + macroName.str() + (str[1] == ':' ? "::" : ":") + "\"";
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
@@ -439,7 +438,7 @@ void fstk_RunRept(uint32_t count, int32_t reptLineNo, ContentSpan const &span, b
|
||||
}
|
||||
|
||||
void fstk_RunFor(
|
||||
std::string const &symName,
|
||||
InternedStr symName,
|
||||
int32_t start,
|
||||
int32_t stop,
|
||||
int32_t step,
|
||||
@@ -497,7 +496,7 @@ void fstk_NewRecursionDepth(size_t newDepth) {
|
||||
bool fstk_Init(std::string const &mainPath) {
|
||||
newFileContext(mainPath, false, true);
|
||||
|
||||
for (std::string const &name : preIncludeNames) {
|
||||
for (std::string const &name : preIncludeStack) {
|
||||
if (std::optional<std::string> fullPath = fstk_FindFile(name); fullPath) {
|
||||
newFileContext(*fullPath, false, false);
|
||||
} else if (fstk_FileError(name, "pre-included")) {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "asm/intern.hpp"
|
||||
|
||||
#include <deque>
|
||||
#include <functional> // equal_to
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "helpers.hpp" // assume
|
||||
#include "verbosity.hpp"
|
||||
|
||||
// Avoid `std::string` allocations when looking up heterogeneous values in `internedIndexes`
|
||||
struct StringHash {
|
||||
using is_transparent = void;
|
||||
|
||||
size_t operator()(std::string_view str) const { return std::hash<std::string_view>{}(str); }
|
||||
};
|
||||
|
||||
// Use a `deque` not a `vector` to prevent reallocation so `internedIndexes` keys stay valid
|
||||
static std::deque<std::string> internedStrings;
|
||||
// Keys are views of values in `internedStrings`; values are their corresponding indexes
|
||||
static std::unordered_map<std::string_view, size_t, StringHash, std::equal_to<>> internedIndexes;
|
||||
|
||||
std::string const &InternedStr::str() const {
|
||||
assume(index != static_cast<size_t>(-1));
|
||||
return internedStrings[index];
|
||||
}
|
||||
|
||||
InternedStr intern(std::string_view str) {
|
||||
if (auto search = internedIndexes.find(str); search != internedIndexes.end()) {
|
||||
return InternedStr(search->second);
|
||||
}
|
||||
|
||||
size_t index = internedStrings.size();
|
||||
std::string &interned = internedStrings.emplace_back(str);
|
||||
internedIndexes.emplace(interned, index);
|
||||
|
||||
verbosePrint(VERB_TRACE, "Interned string \"%s\"\n", interned.c_str());
|
||||
|
||||
return InternedStr(index);
|
||||
}
|
||||
+513
-461
File diff suppressed because it is too large
Load Diff
+17
-9
@@ -8,18 +8,26 @@
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include "helpers.hpp" // assume
|
||||
|
||||
#include "asm/warning.hpp"
|
||||
|
||||
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(int32_t num) const {
|
||||
assume(num != 0);
|
||||
if (num > 0) {
|
||||
// Macro arguments adjust 1-based indexes by the shift amount.
|
||||
if (size_t i = num - 1 + shift; i < args.size()) {
|
||||
return args[i];
|
||||
}
|
||||
} else {
|
||||
// Bracketed macro arguments adjust negative indexes such that -1 is the last argument.
|
||||
if (num == INT32_MIN || static_cast<size_t>(-num) > args.size()) {
|
||||
return nullptr;
|
||||
} else if (size_t i = args.size() - static_cast<size_t>(-num); i >= shift) {
|
||||
return args[i];
|
||||
}
|
||||
}
|
||||
|
||||
int32_t realIndex = i + shift - 1;
|
||||
|
||||
return realIndex < 0 || static_cast<uint32_t>(realIndex) >= args.size() ? nullptr
|
||||
: args[realIndex];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<std::string> MacroArgs::getAllArgs() const {
|
||||
|
||||
+12
-19
@@ -24,7 +24,7 @@
|
||||
#include "helpers.hpp"
|
||||
#include "parser.hpp" // Generated from parser.y
|
||||
#include "platform.hpp"
|
||||
#include "style.hpp"
|
||||
#include "style.hpp" // style_Parse
|
||||
#include "usage.hpp"
|
||||
#include "util.hpp" // UpperMap
|
||||
#include "verbosity.hpp"
|
||||
@@ -190,9 +190,9 @@ static void parseArg(int ch, char *arg) {
|
||||
char *equals = strchr(arg, '=');
|
||||
if (equals) {
|
||||
*equals = '\0';
|
||||
sym_AddString(arg, std::make_shared<std::string>(equals + 1));
|
||||
sym_AddString(intern(arg), std::make_shared<std::string>(equals + 1));
|
||||
} else {
|
||||
sym_AddString(arg, std::make_shared<std::string>("1"));
|
||||
sym_AddString(intern(arg), std::make_shared<std::string>("1"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -267,6 +267,7 @@ static void parseArg(int ch, char *arg) {
|
||||
}
|
||||
|
||||
case 'r':
|
||||
errno = 0;
|
||||
if (std::optional<uint64_t> maxDepth = parseWholeNumber(arg); !maxDepth) {
|
||||
fatal("Invalid argument for option '-r'");
|
||||
} else if (errno == ERANGE) {
|
||||
@@ -289,7 +290,7 @@ static void parseArg(int ch, char *arg) {
|
||||
if (localOptions.stateFileSpecs.find(name) != localOptions.stateFileSpecs.end()) {
|
||||
warnx("Overriding state file \"%s\"", name);
|
||||
}
|
||||
localOptions.stateFileSpecs.emplace(name, std::move(features));
|
||||
localOptions.stateFileSpecs[name] = std::move(features);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -314,8 +315,6 @@ static void parseArg(int ch, char *arg) {
|
||||
case 'X':
|
||||
if (std::optional<uint64_t> maxErrors = parseWholeNumber(arg); !maxErrors) {
|
||||
fatal("Invalid argument for option '-X'");
|
||||
} else if (*maxErrors > UINT64_MAX) {
|
||||
fatal("Argument for option '-X' must be between 0 and %" PRIu64, UINT64_MAX);
|
||||
} else {
|
||||
options.maxErrors = *maxErrors;
|
||||
}
|
||||
@@ -374,12 +373,6 @@ static void parseArg(int ch, char *arg) {
|
||||
|
||||
// LCOV_EXCL_START
|
||||
static void verboseOutputConfig() {
|
||||
if (!checkVerbosity(VERB_CONFIG)) {
|
||||
return;
|
||||
}
|
||||
|
||||
style_Set(stderr, STYLE_MAGENTA, false);
|
||||
|
||||
usage.printVersion(true);
|
||||
|
||||
printVVVVVVerbosity();
|
||||
@@ -491,8 +484,6 @@ static void verboseOutputConfig() {
|
||||
}
|
||||
}
|
||||
fputs("Ready for assembly\n", stderr);
|
||||
|
||||
style_Reset(stderr);
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
@@ -501,9 +492,11 @@ int main(int argc, char *argv[]) {
|
||||
// https://reproducible-builds.org/docs/source-date-epoch/
|
||||
time_t now = time(nullptr);
|
||||
if (char const *sourceDateEpoch = getenv("SOURCE_DATE_EPOCH"); sourceDateEpoch) {
|
||||
// Use `strtoul`, not `parseWholeNumber`, because SOURCE_DATE_EPOCH does
|
||||
// not conventionally support our custom base prefixes
|
||||
now = static_cast<time_t>(strtoul(sourceDateEpoch, nullptr, 0));
|
||||
if (std::optional<uint64_t> epoch = parseWholeNumber(sourceDateEpoch, BASE_10); epoch) {
|
||||
now = static_cast<time_t>(*epoch);
|
||||
} else {
|
||||
warnx("Ignoring invalid `SOURCE_DATE_EPOCH` value \"%s\"", sourceDateEpoch);
|
||||
}
|
||||
}
|
||||
sym_Init(now);
|
||||
|
||||
@@ -518,7 +511,7 @@ int main(int argc, char *argv[]) {
|
||||
options.targetFileName = options.objectFileName;
|
||||
}
|
||||
|
||||
verboseOutputConfig();
|
||||
verboseDo(VERB_CONFIG, verboseOutputConfig);
|
||||
|
||||
if (!localOptions.inputFileName) {
|
||||
usage.printAndExit("No input file specified (pass \"-\" to read from standard input)");
|
||||
@@ -556,7 +549,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
options.printDep(*localOptions.inputFileName);
|
||||
|
||||
charmap_New(DEFAULT_CHARMAP_NAME, nullptr);
|
||||
charmap_Init();
|
||||
|
||||
// Init lexer and file stack, and parse (`yy::parser` is auto-generated from `parser.y`)
|
||||
if (yy::parser parser; fstk_Init(*localOptions.inputFileName) && parser.parse() != 0) {
|
||||
|
||||
@@ -105,6 +105,7 @@ void opt_Parse(char const *s) {
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
errno = 0;
|
||||
if (std::optional<uint64_t> maxRecursionDepth = parseWholeNumber(s); !maxRecursionDepth) {
|
||||
error("Invalid argument for option 'r'");
|
||||
} else if (errno == ERANGE) {
|
||||
|
||||
+6
-4
@@ -17,9 +17,11 @@
|
||||
#include "helpers.hpp" // assume, Defer
|
||||
#include "linkdefs.hpp"
|
||||
#include "platform.hpp"
|
||||
#include "util.hpp" // xfclose
|
||||
|
||||
#include "asm/charmap.hpp"
|
||||
#include "asm/fstack.hpp"
|
||||
#include "asm/intern.hpp"
|
||||
#include "asm/lexer.hpp"
|
||||
#include "asm/main.hpp"
|
||||
#include "asm/rpn.hpp"
|
||||
@@ -107,7 +109,7 @@ static void writeSection(Section const §, FILE *file) {
|
||||
}
|
||||
|
||||
static void writeSymbol(Symbol const &sym, FILE *file) {
|
||||
putString(sym.name, file);
|
||||
putString(sym.name.str(), file);
|
||||
if (!sym.isDefined()) {
|
||||
putc(SYMTYPE_IMPORT, file);
|
||||
} else {
|
||||
@@ -209,7 +211,7 @@ void out_WriteObject() {
|
||||
fatal("Failed to open object file \"%s\": %s", objectFileName, strerror(errno));
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
Defer closeFile{[&] { fclose(file); }};
|
||||
Defer closeFile{[&] { xfclose(file); }};
|
||||
|
||||
// Also write symbols that weren't written above
|
||||
sym_ForEach(out_RegisterSymbol);
|
||||
@@ -338,7 +340,7 @@ static bool dumpCharmaps(FILE *file) {
|
||||
|
||||
// Characters are ordered by charmap, then by definition order
|
||||
return charmap_ForEach(
|
||||
[](std::string const &name) { fprintf(charmapFile, "newcharmap %s\n", name.c_str()); },
|
||||
[](InternedStr name) { fprintf(charmapFile, "newcharmap %s\n", name.c_str()); },
|
||||
[](std::string const &mapping, std::vector<int32_t> value) {
|
||||
fputs("charmap \"", charmapFile);
|
||||
dumpString(mapping, charmapFile);
|
||||
@@ -388,7 +390,7 @@ void out_WriteState(std::string name, std::vector<StateFeature> const &features)
|
||||
fatal("Failed to open state file \"%s\": %s", name.c_str(), strerror(errno));
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
Defer closeFile{[&] { fclose(file); }};
|
||||
Defer closeFile{[&] { xfclose(file); }};
|
||||
|
||||
static char const *dumpHeadings[NB_STATE_FEATURES] = {
|
||||
"Numeric constants",
|
||||
|
||||
+40
-38
@@ -32,6 +32,7 @@
|
||||
|
||||
%code {
|
||||
#include <algorithm>
|
||||
#include <concepts> // invocable
|
||||
#include <inttypes.h>
|
||||
#include <optional>
|
||||
#include <stdio.h>
|
||||
@@ -57,9 +58,10 @@
|
||||
|
||||
yy::parser::symbol_type yylex(); // Provided by lexer.cpp
|
||||
|
||||
template<typename NumCallbackFnT, typename StrCallbackFnT>
|
||||
static auto handleSymbolByType(
|
||||
std::string const &symName, NumCallbackFnT numCallback, StrCallbackFnT strCallback
|
||||
InternedStr symName,
|
||||
std::invocable<Expression const &> auto numCallback,
|
||||
std::invocable<std::string const &> auto strCallback
|
||||
) {
|
||||
if (Symbol *sym = sym_FindScopedSymbol(symName); sym && sym->type == SYM_EQUS) {
|
||||
return strCallback(*sym->getEqus());
|
||||
@@ -322,11 +324,11 @@
|
||||
%token <int32_t> NUMBER "number"
|
||||
%token <std::string> STRING "string"
|
||||
%token <std::string> CHARACTER "character"
|
||||
%token <std::string> SYMBOL "symbol"
|
||||
%token <std::string> LABEL "label"
|
||||
%token <std::string> LOCAL "local label"
|
||||
%token <std::string> ANON "anonymous label"
|
||||
%token <std::string> QMACRO "quiet macro"
|
||||
%token <InternedStr> SYMBOL "symbol"
|
||||
%token <InternedStr> LABEL "label"
|
||||
%token <InternedStr> LOCAL "local label"
|
||||
%token <InternedStr> ANON "anonymous label"
|
||||
%token <InternedStr> QMACRO "quiet macro"
|
||||
|
||||
/******************** Data types ********************/
|
||||
|
||||
@@ -353,23 +355,23 @@
|
||||
%type <std::string> string_literal
|
||||
%type <std::string> strcat_args
|
||||
// Strings used for identifiers
|
||||
%type <std::string> def_id
|
||||
%type <std::string> redef_id
|
||||
%type <std::string> def_numeric
|
||||
%type <std::string> def_equ
|
||||
%type <std::string> redef_equ
|
||||
%type <std::string> def_set
|
||||
%type <std::string> def_rb
|
||||
%type <std::string> def_rw
|
||||
%type <std::string> def_rl
|
||||
%type <std::string> def_equs
|
||||
%type <std::string> redef_equs
|
||||
%type <std::string> scoped_sym
|
||||
%type <InternedStr> def_id
|
||||
%type <InternedStr> redef_id
|
||||
%type <InternedStr> def_numeric
|
||||
%type <InternedStr> def_equ
|
||||
%type <InternedStr> redef_equ
|
||||
%type <InternedStr> def_set
|
||||
%type <InternedStr> def_rb
|
||||
%type <InternedStr> def_rw
|
||||
%type <InternedStr> def_rl
|
||||
%type <InternedStr> def_equs
|
||||
%type <InternedStr> redef_equs
|
||||
%type <InternedStr> scoped_sym
|
||||
// `scoped_sym_no_anon` exists because anonymous labels usually count as "scoped symbols", but some
|
||||
// contexts treat anonymous labels and other labels/symbols differently, e.g. `purge` or `export`.
|
||||
%type <std::string> scoped_sym_no_anon
|
||||
%type <std::string> fragment_literal
|
||||
%type <std::string> fragment_literal_name
|
||||
%type <InternedStr> scoped_sym_no_anon
|
||||
%type <InternedStr> fragment_literal
|
||||
%type <InternedStr> fragment_literal_name
|
||||
|
||||
// SM83 instruction parameters
|
||||
%type <int32_t> reg_r
|
||||
@@ -397,7 +399,7 @@
|
||||
%type <std::vector<Expression>> ds_args
|
||||
%type <ForArgs> for_args
|
||||
%type <std::shared_ptr<MacroArgs>> macro_args
|
||||
%type <std::vector<std::string>> purge_args
|
||||
%type <std::vector<InternedStr>> purge_args
|
||||
%type <SectionSpec> sect_attrs
|
||||
%type <SectionModifier> sect_mod
|
||||
%type <SectionType> sect_type
|
||||
@@ -505,7 +507,7 @@ def_id:
|
||||
lexer_ToggleStringExpansion(false);
|
||||
} SYMBOL {
|
||||
lexer_ToggleStringExpansion(true);
|
||||
$$ = std::move($3);
|
||||
$$ = $3;
|
||||
}
|
||||
;
|
||||
|
||||
@@ -514,7 +516,7 @@ redef_id:
|
||||
lexer_ToggleStringExpansion(false);
|
||||
} SYMBOL {
|
||||
lexer_ToggleStringExpansion(true);
|
||||
$$ = std::move($3);
|
||||
$$ = $3;
|
||||
}
|
||||
;
|
||||
|
||||
@@ -910,40 +912,40 @@ endu:
|
||||
|
||||
def_equ:
|
||||
def_id POP_EQU iconst {
|
||||
$$ = std::move($1);
|
||||
$$ = $1;
|
||||
sym_AddEqu($$, $3);
|
||||
}
|
||||
;
|
||||
|
||||
redef_equ:
|
||||
redef_id POP_EQU iconst {
|
||||
$$ = std::move($1);
|
||||
$$ = $1;
|
||||
sym_RedefEqu($$, $3);
|
||||
}
|
||||
;
|
||||
|
||||
def_set:
|
||||
def_id POP_EQUAL iconst {
|
||||
$$ = std::move($1);
|
||||
$$ = $1;
|
||||
sym_AddVar($$, $3);
|
||||
}
|
||||
| redef_id POP_EQUAL iconst {
|
||||
$$ = std::move($1);
|
||||
$$ = $1;
|
||||
sym_AddVar($$, $3);
|
||||
}
|
||||
| def_id compound_eq iconst {
|
||||
$$ = std::move($1);
|
||||
$$ = $1;
|
||||
act_CompoundAssignment($$, $2, $3);
|
||||
}
|
||||
| redef_id compound_eq iconst {
|
||||
$$ = std::move($1);
|
||||
$$ = $1;
|
||||
act_CompoundAssignment($$, $2, $3);
|
||||
}
|
||||
;
|
||||
|
||||
def_rb:
|
||||
def_id POP_RB rs_uconst {
|
||||
$$ = std::move($1);
|
||||
$$ = $1;
|
||||
uint32_t rs = sym_GetRSValue();
|
||||
sym_AddEqu($$, rs);
|
||||
sym_SetRSValue(rs + $3);
|
||||
@@ -952,7 +954,7 @@ def_rb:
|
||||
|
||||
def_rw:
|
||||
def_id POP_RW rs_uconst {
|
||||
$$ = std::move($1);
|
||||
$$ = $1;
|
||||
uint32_t rs = sym_GetRSValue();
|
||||
sym_AddEqu($$, rs);
|
||||
sym_SetRSValue(rs + 2 * $3);
|
||||
@@ -961,7 +963,7 @@ def_rw:
|
||||
|
||||
def_rl:
|
||||
def_id SM83_RL rs_uconst {
|
||||
$$ = std::move($1);
|
||||
$$ = $1;
|
||||
uint32_t rs = sym_GetRSValue();
|
||||
sym_AddEqu($$, rs);
|
||||
sym_SetRSValue(rs + 4 * $3);
|
||||
@@ -970,14 +972,14 @@ def_rl:
|
||||
|
||||
def_equs:
|
||||
def_id POP_EQUS string {
|
||||
$$ = std::move($1);
|
||||
$$ = $1;
|
||||
sym_AddString($$, std::make_shared<std::string>($3));
|
||||
}
|
||||
;
|
||||
|
||||
redef_equs:
|
||||
redef_id POP_EQUS string {
|
||||
$$ = std::move($1);
|
||||
$$ = $1;
|
||||
sym_RedefString($$, std::make_shared<std::string>($3));
|
||||
}
|
||||
;
|
||||
@@ -986,7 +988,7 @@ purge:
|
||||
POP_PURGE {
|
||||
lexer_ToggleStringExpansion(false);
|
||||
} purge_args trailing_comma {
|
||||
for (std::string &arg : $3) {
|
||||
for (InternedStr arg : $3) {
|
||||
sym_Purge(arg);
|
||||
}
|
||||
lexer_ToggleStringExpansion(true);
|
||||
@@ -1252,7 +1254,7 @@ reloc_16bit:
|
||||
fragment_literal:
|
||||
LBRACKS fragment_literal_name asm_file RBRACKS {
|
||||
sect_PopSection();
|
||||
$$ = std::move($2);
|
||||
$$ = $2;
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
+22
-23
@@ -37,7 +37,7 @@ Symbol const *Expression::symbolOf() const {
|
||||
if (rpn.size() != 1 || rpn[0].command != RPN_SYM) {
|
||||
return nullptr;
|
||||
}
|
||||
return sym_FindScopedSymbol(std::get<std::string>(rpn[0].data));
|
||||
return sym_FindScopedSymbol(std::get<InternedStr>(rpn[0].data));
|
||||
}
|
||||
|
||||
bool Expression::isDiffConstant(Symbol const *sym) const {
|
||||
@@ -58,7 +58,7 @@ void Expression::makeNumber(uint32_t value) {
|
||||
data = static_cast<int32_t>(value);
|
||||
}
|
||||
|
||||
void Expression::makeSymbol(std::string const &symName) {
|
||||
void Expression::makeSymbol(InternedStr symName) {
|
||||
assume(rpn.empty());
|
||||
if (Symbol *sym = sym_FindScopedSymbol(symName); sym_IsPC(sym) && !sect_GetSymbolSection()) {
|
||||
error("PC has no value outside of a section");
|
||||
@@ -69,8 +69,8 @@ void Expression::makeSymbol(std::string const &symName) {
|
||||
} else if (!sym || !sym->isConstant()) {
|
||||
data = sym_IsPC(sym) ? "PC is not constant at assembly time"
|
||||
: (sym && sym->isDefined()
|
||||
? "`"s + symName + "` is not constant at assembly time"
|
||||
: "undefined symbol `"s + symName + "`")
|
||||
? "`"s + symName.str() + "` is not constant at assembly time"
|
||||
: "undefined symbol `"s + symName.str() + "`")
|
||||
+ (sym_IsPurgedScoped(symName) ? "; it was purged" : "");
|
||||
sym = sym_Ref(symName);
|
||||
rpn.emplace_back(RPN_SYM, sym->name);
|
||||
@@ -79,7 +79,7 @@ void Expression::makeSymbol(std::string const &symName) {
|
||||
}
|
||||
}
|
||||
|
||||
void Expression::makeBankSymbol(std::string const &symName) {
|
||||
void Expression::makeBankSymbol(InternedStr symName) {
|
||||
assume(rpn.empty());
|
||||
if (Symbol const *sym = sym_FindScopedSymbol(symName); sym_IsPC(sym)) {
|
||||
// The @ symbol is treated differently.
|
||||
@@ -103,8 +103,8 @@ void Expression::makeBankSymbol(std::string const &symName) {
|
||||
data = static_cast<int32_t>(sym->getSection()->bank);
|
||||
} else {
|
||||
data = sym_IsPurgedScoped(symName)
|
||||
? "`"s + symName + "`'s bank is not known; it was purged"
|
||||
: "`"s + symName + "`'s bank is not known";
|
||||
? "`"s + symName.str() + "`'s bank is not known; it was purged"
|
||||
: "`"s + symName.str() + "`'s bank is not known";
|
||||
rpn.emplace_back(RPN_BANK_SYM, sym->name);
|
||||
}
|
||||
}
|
||||
@@ -116,7 +116,7 @@ void Expression::makeBankSection(std::string const §Name) {
|
||||
data = static_cast<int32_t>(sect->bank);
|
||||
} else {
|
||||
data = "Section \""s + sectName + "\"'s bank is not known";
|
||||
rpn.emplace_back(RPN_BANK_SECT, sectName);
|
||||
rpn.emplace_back(RPN_BANK_SECT, intern(sectName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ void Expression::makeSizeOfSection(std::string const §Name) {
|
||||
data = static_cast<int32_t>(sect->size);
|
||||
} else {
|
||||
data = "Section \""s + sectName + "\"'s size is not known";
|
||||
rpn.emplace_back(RPN_SIZEOF_SECT, sectName);
|
||||
rpn.emplace_back(RPN_SIZEOF_SECT, intern(sectName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ void Expression::makeStartOfSection(std::string const §Name) {
|
||||
data = static_cast<int32_t>(sect->org);
|
||||
} else {
|
||||
data = "Section \""s + sectName + "\"'s start is not known";
|
||||
rpn.emplace_back(RPN_STARTOF_SECT, sectName);
|
||||
rpn.emplace_back(RPN_STARTOF_SECT, intern(sectName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,9 +227,9 @@ static int32_t tryConstMask(Expression const &lhs, Expression const &rhs) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
assume(sym.isNumeric());
|
||||
assume(sym.type == SYM_LABEL);
|
||||
// We can now safely use `expr.value()` and `sym.getSection()`
|
||||
|
||||
// We can now safely use `expr.value()`
|
||||
int32_t mask = expr.value();
|
||||
|
||||
// The mask must not cover any unknown bits
|
||||
@@ -375,8 +375,7 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
|
||||
case RPN_DIV:
|
||||
if (rval == 0) {
|
||||
fatal("Division by zero");
|
||||
}
|
||||
if (lval == INT32_MIN && rval == -1) {
|
||||
} else if (lval == INT32_MIN && rval == -1) {
|
||||
warning(
|
||||
WARNING_DIV,
|
||||
"Division of %" PRId32 " by -1 yields %" PRId32,
|
||||
@@ -391,8 +390,8 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
|
||||
case RPN_MOD:
|
||||
if (rval == 0) {
|
||||
fatal("Modulo by zero");
|
||||
}
|
||||
if (lval == INT32_MIN && rval == -1) {
|
||||
} else if (lval == INT32_MIN && rval == -1) {
|
||||
warning(WARNING_DIV, "Modulo of %" PRId32 " by -1 yields 0", INT32_MIN);
|
||||
data = 0;
|
||||
} else {
|
||||
data = op_modulo(lval, rval);
|
||||
@@ -486,8 +485,8 @@ void Expression::checkNBit(uint8_t n) const {
|
||||
}
|
||||
|
||||
bool checkNBit(int32_t v, uint8_t n, char const *name) {
|
||||
assume(n != 0); // That doesn't make sense
|
||||
assume(n < CHAR_BIT * sizeof(int)); // Otherwise `1 << n` is UB
|
||||
assume(n != 0); // That doesn't make sense
|
||||
assume(n < CHAR_BIT * sizeof(int) - 1); // Otherwise `1 << n` is UB
|
||||
|
||||
if (v < -(1 << n) || v >= 1 << n) {
|
||||
warning(
|
||||
@@ -550,7 +549,7 @@ RPNValue::RPNValue(RPNCommand cmd, uint32_t val) : command(cmd), data(val) {
|
||||
assume(cmd == RPN_CONST);
|
||||
}
|
||||
|
||||
RPNValue::RPNValue(RPNCommand cmd, std::string const &name) : command(cmd), data(name) {
|
||||
RPNValue::RPNValue(RPNCommand cmd, InternedStr name) : command(cmd), data(name) {
|
||||
assume(
|
||||
cmd == RPN_SYM || cmd == RPN_BANK_SYM || cmd == RPN_BANK_SECT || cmd == RPN_SIZEOF_SECT
|
||||
|| cmd == RPN_STARTOF_SECT
|
||||
@@ -576,9 +575,9 @@ void RPNValue::appendEncoded(std::vector<uint8_t> &buffer) const {
|
||||
case RPN_SYM:
|
||||
case RPN_BANK_SYM: {
|
||||
// The command ID is followed by a four-byte symbol ID
|
||||
assume(std::holds_alternative<std::string>(data));
|
||||
assume(std::holds_alternative<InternedStr>(data));
|
||||
// The symbol name is always written expanded
|
||||
Symbol *sym = sym_FindExactSymbol(std::get<std::string>(data));
|
||||
Symbol *sym = sym_FindExactSymbol(std::get<InternedStr>(data));
|
||||
out_RegisterSymbol(*sym); // Ensure that `sym->ID` is set
|
||||
buffer.push_back(sym->ID & 0xFF);
|
||||
buffer.push_back(sym->ID >> 8);
|
||||
@@ -591,8 +590,8 @@ void RPNValue::appendEncoded(std::vector<uint8_t> &buffer) const {
|
||||
case RPN_SIZEOF_SECT:
|
||||
case RPN_STARTOF_SECT: {
|
||||
// The command ID is followed by a NUL-terminated section name string
|
||||
assume(std::holds_alternative<std::string>(data));
|
||||
std::string const &name = std::get<std::string>(data);
|
||||
assume(std::holds_alternative<InternedStr>(data));
|
||||
std::string const &name = std::get<InternedStr>(data).str();
|
||||
buffer.reserve(buffer.size() + name.length() + 1);
|
||||
buffer.insert(buffer.end(), RANGE(name));
|
||||
buffer.push_back('\0');
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user