mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-21 10:42:07 +00:00
Compare commits
211 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ede982b50a | ||
|
|
319d775c13 | ||
|
|
44124319a6 | ||
|
|
462fd7539c | ||
|
|
f16e34b804 | ||
|
|
c3ccdc548e | ||
|
|
fd721ca480 | ||
|
|
eac365aef0 | ||
|
|
4de6266442 | ||
|
|
213d985e17 | ||
|
|
de76dcb8fb | ||
|
|
30fb6bde5e | ||
|
|
4f842a1248 | ||
|
|
cc4d455b8a | ||
|
|
0bb5efebfd | ||
|
|
b6bf7ae620 | ||
|
|
dc96cc6d1e | ||
|
|
642daf1a76 | ||
|
|
84edfb3d88 | ||
|
|
7e620bff81 | ||
|
|
0c55703438 | ||
|
|
6c57ad2226 | ||
|
|
9028fb5391 | ||
|
|
12dc49b60a | ||
|
|
7e1d20acdf | ||
|
|
5230104852 | ||
|
|
55be77be69 | ||
|
|
42b3a17356 | ||
|
|
4e1d79081c | ||
|
|
4ce4fdec71 | ||
|
|
05256946ac | ||
|
|
73396166aa | ||
|
|
4c5d5c7085 | ||
|
|
045a9e8b93 | ||
|
|
4419f0d54f | ||
|
|
b07aa00d5c | ||
|
|
e4f5df1306 | ||
|
|
dc62d60e9b | ||
|
|
71d8aeb4c2 | ||
|
|
0836f67d42 | ||
|
|
176a57a1e9 | ||
|
|
0d02355dbf | ||
|
|
6767d11c23 | ||
|
|
2dd9015dc6 | ||
|
|
217c10ddac | ||
|
|
822e4e7c44 | ||
|
|
0b1d01792d | ||
|
|
01637768cf | ||
|
|
6a8ae643d5 | ||
|
|
fd83d46ba0 | ||
|
|
914856342c | ||
|
|
91889fc14a | ||
|
|
7c8ec5a5ed | ||
|
|
effc6788eb | ||
|
|
f9daf27511 | ||
|
|
06f7387466 | ||
|
|
21e50eeff1 | ||
|
|
fd4cec93cd | ||
|
|
92f2055a6c | ||
|
|
be9b1198e9 | ||
|
|
56bea083f9 | ||
|
|
fdfc02ab96 | ||
|
|
fc7f042ad6 | ||
|
|
761c775043 | ||
|
|
b421c983d6 | ||
|
|
3036b58598 | ||
|
|
2eca43cd2d | ||
|
|
c24694233f | ||
|
|
423a7c4899 | ||
|
|
ee9e45b3d4 | ||
|
|
5a65188ca9 | ||
|
|
930080f556 | ||
|
|
8e7afb0ab3 | ||
|
|
138523570e | ||
|
|
82469ac0fd | ||
|
|
96cb5e10ed | ||
|
|
b224cab3e0 | ||
|
|
7381d7b92f | ||
|
|
dbef51ba05 | ||
|
|
c952dd8a6e | ||
|
|
b65ea64a58 | ||
|
|
542b5d18f1 | ||
|
|
71a0a42cfb | ||
|
|
ac011fe69f | ||
|
|
9e3d7a50e6 | ||
|
|
e33c2ad6a2 | ||
|
|
615f1072d9 | ||
|
|
f7b7a97407 | ||
|
|
ece6853e0f | ||
|
|
b7b03ee451 | ||
|
|
f9b48c0cad | ||
|
|
aa76603da9 | ||
|
|
b83b9825f8 | ||
|
|
d641972cde | ||
|
|
4d1333e124 | ||
|
|
35396e6410 | ||
|
|
8d18b39eee | ||
|
|
ae77893021 | ||
|
|
baeb180acd | ||
|
|
fd02ffb7bd | ||
|
|
62ecdce0b0 | ||
|
|
e4f2fad215 | ||
|
|
3f5f9bcaf0 | ||
|
|
08867b3cec | ||
|
|
9081feab51 | ||
|
|
cf992164f7 | ||
|
|
b27b821e7f | ||
|
|
d9ecaabac1 | ||
|
|
cd747d8175 | ||
|
|
df75fd2ec2 | ||
|
|
adcaf4cd46 | ||
|
|
81a77a9b88 | ||
|
|
6e805cd318 | ||
|
|
e11f25024e | ||
|
|
7c895f8a1b | ||
|
|
38bda7e1bb | ||
|
|
149db9a022 | ||
|
|
fed252bc49 | ||
|
|
61b2fd9816 | ||
|
|
5ad7a93750 | ||
|
|
2ec10012b6 | ||
|
|
e56c6cc291 | ||
|
|
4c9a929a14 | ||
|
|
71f8871702 | ||
|
|
6dc4ce6599 | ||
|
|
3a44cc7722 | ||
|
|
4cfed3c98f | ||
|
|
6af57ff026 | ||
|
|
2e3db9d56a | ||
|
|
f8d9fa87ed | ||
|
|
f53ad359a6 | ||
|
|
84de86beb5 | ||
|
|
04e7af2675 | ||
|
|
4f13a336b9 | ||
|
|
03508119e5 | ||
|
|
03e20138d3 | ||
|
|
dfcba36448 | ||
|
|
8e4a9a5c21 | ||
|
|
a1286e6f0e | ||
|
|
c0808246e5 | ||
|
|
ba051e10fb | ||
|
|
9fee0603b1 | ||
|
|
be8ebe6db9 | ||
|
|
548e3dc31c | ||
|
|
9440086d77 | ||
|
|
ec5a1bc71f | ||
|
|
9742fa731c | ||
|
|
91a3c538d9 | ||
|
|
e98485da3f | ||
|
|
6528a955fe | ||
|
|
431f77127e | ||
|
|
2cc58723cb | ||
|
|
268219d74e | ||
|
|
d09ed3e52e | ||
|
|
421d1f5490 | ||
|
|
66784b7122 | ||
|
|
54f2d99ce7 | ||
|
|
557c799ec9 | ||
|
|
d22a667095 | ||
|
|
30085a5342 | ||
|
|
304e6c4279 | ||
|
|
12458aae6f | ||
|
|
7e5d9683b1 | ||
|
|
210a4a957a | ||
|
|
131ad9b315 | ||
|
|
06b4cf57ab | ||
|
|
dbefdc923a | ||
|
|
37e45de9c1 | ||
|
|
5e63527190 | ||
|
|
0cc9026978 | ||
|
|
bb6a5441ed | ||
|
|
0ffda1bf29 | ||
|
|
56b5f77dc8 | ||
|
|
6eb284f99e | ||
|
|
34c2288fd0 | ||
|
|
304bb9f902 | ||
|
|
14be01880d | ||
|
|
12b7cf3cd4 | ||
|
|
0d7914bff7 | ||
|
|
d2285953d7 | ||
|
|
d2801505c3 | ||
|
|
9d62b4b9bb | ||
|
|
b65b723fa6 | ||
|
|
e05321356b | ||
|
|
0778959e98 | ||
|
|
76331d1c4a | ||
|
|
9e378ec5cf | ||
|
|
f57d33b17a | ||
|
|
c389e8dccb | ||
|
|
90c64a94d1 | ||
|
|
df27af6d1c | ||
|
|
c85b48f23e | ||
|
|
5a9f2b7750 | ||
|
|
fcfecc6e82 | ||
|
|
f863a927c1 | ||
|
|
cb4fbdfcd5 | ||
|
|
c6eacde55e | ||
|
|
daf780c7e6 | ||
|
|
613305d234 | ||
|
|
656432301d | ||
|
|
38a80f2816 | ||
|
|
6563133426 | ||
|
|
762474d3ac | ||
|
|
b1adbcc77c | ||
|
|
dda052da20 | ||
|
|
2d6bdbc1b1 | ||
|
|
0600856167 | ||
|
|
ce47b57b03 | ||
|
|
f3c916ab96 | ||
|
|
ca6fa6d1d7 | ||
|
|
fcd37b52b6 |
@@ -83,3 +83,6 @@
|
||||
|
||||
# Parentheses can make the code clearer
|
||||
--ignore UNNECESSARY_PARENTHESES
|
||||
|
||||
# We don't have `fallthrough;` */
|
||||
--ignore PREFER_FALLTHROUGH
|
||||
|
||||
@@ -38,48 +38,11 @@ BEGIN {
|
||||
}
|
||||
}
|
||||
|
||||
/<div class="Nd">/ {
|
||||
# Make the description blurb inline, as with terminal output
|
||||
gsub(/div/, "span")
|
||||
}
|
||||
|
||||
BEGIN {
|
||||
pages["gbz80", 7] = 1
|
||||
pages["rgbds", 5] = 1
|
||||
pages["rgbds", 7] = 1
|
||||
pages["rgbasm", 1] = 1
|
||||
pages["rgbasm", 5] = 1
|
||||
pages["rgblink",1] = 1
|
||||
pages["rgblink",5] = 1
|
||||
pages["rgbfix", 1] = 1
|
||||
pages["rgbgfx", 1] = 1
|
||||
}
|
||||
/<a class="Xr">/ {
|
||||
# Link to other pages in the doc
|
||||
for (i in pages) {
|
||||
split(i, page, SUBSEP)
|
||||
name = page[1]
|
||||
section = page[2]
|
||||
gsub(sprintf("<a class=\"Xr\">%s\\(%d\\)", name, section),
|
||||
sprintf("<a class=\"Xr\" href=\"%s.%d.html\">%s(%d)", name, section, name, section))
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
# Make long opts (defined using `Fl Fl`) into a single tag
|
||||
gsub(/<code class="Fl">-<\/code><code class="Fl">/, "<code class=\"Fl\">-")
|
||||
gsub(/<code class="Fl">-<\/code>\s*<code class="Fl">/, "<code class=\"Fl\">-")
|
||||
}
|
||||
|
||||
{
|
||||
print
|
||||
}
|
||||
|
||||
/<head>/ {
|
||||
# Add viewport size <meta> tag for mobile users
|
||||
print " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"
|
||||
}
|
||||
|
||||
/<link/ {
|
||||
# Inject our own style overrides
|
||||
print(" <link rel=\"stylesheet\" href=\"rgbds.css\" type=\"text/css\" media=\"all\"/>")
|
||||
}
|
||||
100
.github/actions/get-pages.sh
vendored
Executable file
100
.github/actions/get-pages.sh
vendored
Executable file
@@ -0,0 +1,100 @@
|
||||
#!/bin/bash
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $0 [-h] [-r] <rgbds-www> <version>
|
||||
Copy renders from RGBDS repository to rgbds-www documentation
|
||||
Execute from the root folder of the RGBDS repo, checked out at the desired tag
|
||||
<rgbds-www> : Path to the '_documentation' folder in the rgbds-www repository
|
||||
<version> : Version to be copied, such as 'v0.4.1' or 'master'
|
||||
|
||||
-h Display this help message
|
||||
-r Update "latest stable" redirection pages (use for releases, not master)
|
||||
EOF
|
||||
}
|
||||
|
||||
update_redirects=0
|
||||
bad_usage=0
|
||||
while getopts ":hr" opt; do
|
||||
case $opt in
|
||||
r)
|
||||
update_redirects=1
|
||||
;;
|
||||
h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
\?)
|
||||
echo "Unknown option '$OPTARG'"
|
||||
bad_usage=1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
if [ $bad_usage -ne 0 ]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
shift $(($OPTIND - 1))
|
||||
|
||||
|
||||
declare -A PAGES
|
||||
PAGES=(
|
||||
[rgbasm.1.html]=src/asm/rgbasm.1
|
||||
[rgbasm.5.html]=src/asm/rgbasm.5
|
||||
[rgblink.1.html]=src/link/rgblink.1
|
||||
[rgblink.5.html]=src/link/rgblink.5
|
||||
[rgbfix.1.html]=src/fix/rgbfix.1
|
||||
[rgbgfx.1.html]=src/gfx/rgbgfx.1
|
||||
[rgbds.5.html]=src/rgbds.5
|
||||
[rgbds.7.html]=src/rgbds.7
|
||||
[gbz80.7.html]=src/gbz80.7
|
||||
)
|
||||
WWWPATH="/docs"
|
||||
mkdir -p "$1/$2"
|
||||
|
||||
# `mandoc` uses a different format for referring to man pages present in the **current** directory
|
||||
# We want that format for RGBDS man pages, and the other one for the t=rest;
|
||||
# we thus need to copy all pages to a temporary directory, and process them there.
|
||||
|
||||
# Copy all pages to current dir
|
||||
cp "${PAGES[@]}" .
|
||||
|
||||
for page in "${!PAGES[@]}"; do
|
||||
stem="${page%.html}"
|
||||
manpage="${stem%.?}(${stem#*.})"
|
||||
descr="$(awk -v 'FS=.Nd ' '/.Nd/ { print $2; }' "${PAGES[$page]}")"
|
||||
|
||||
cat - >"$1/$2/$page" <<EOF
|
||||
---
|
||||
layout: doc
|
||||
title: $manpage [$2]
|
||||
description: RGBDS $2 — $descr
|
||||
---
|
||||
EOF
|
||||
options=fragment,man='%N.%S;https://linux.die.net/man/%S/%N'
|
||||
if [ $stem = rgbasm.5 ]; then
|
||||
options+=,toc
|
||||
fi
|
||||
mandoc -Thtml -I os=Linux -O$options "${PAGES[$page]##*/}" | .github/actions/doc_postproc.awk >> "$1/$2/$page"
|
||||
if [ $update_redirects -ne 0 ]; then
|
||||
cat - >"$1/$page" <<EOF
|
||||
---
|
||||
redirect_to: $WWWPATH/$2/${page%.html}
|
||||
permalink: $WWWPATH/${page%.html}/
|
||||
title: $manpage [latest stable]
|
||||
description: RGBDS latest stable — $descr
|
||||
---
|
||||
EOF
|
||||
fi
|
||||
done
|
||||
cat - >"$1/$2/index.html" <<EOF
|
||||
---
|
||||
layout: doc_index
|
||||
permalink: /docs/$2/
|
||||
title: RGBDS online manual [$2]
|
||||
description: RGBDS $2 - Online manual
|
||||
---
|
||||
EOF
|
||||
|
||||
# Clean up
|
||||
rm "${PAGES[@]##*/}"
|
||||
16
.github/workflows/checkpatch.yml
vendored
16
.github/workflows/checkpatch.yml
vendored
@@ -5,13 +5,21 @@ jobs:
|
||||
checkpatch:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- 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: Set up checkpatch
|
||||
working-directory: rgbds
|
||||
run: |
|
||||
wget 'https://raw.githubusercontent.com/torvalds/linux/master/scripts/checkpatch.pl'
|
||||
chmod +x checkpatch.pl
|
||||
touch const_structs.checkpatch
|
||||
touch spelling.txt
|
||||
wget 'https://raw.githubusercontent.com/torvalds/linux/master/scripts/const_structs.checkpatch'
|
||||
wget 'https://raw.githubusercontent.com/torvalds/linux/master/scripts/spelling.txt'
|
||||
- name: Checkpatch
|
||||
working-directory: rgbds
|
||||
run: |
|
||||
make checkpatch CHECKPATCH=./checkpatch.pl BASE_REF=${{ github.base_ref }} Q=
|
||||
make checkpatch CHECKPATCH=./checkpatch.pl "BASE_REF=${{ github.event.pull_request.base.sha }}" Q= | tee log
|
||||
if grep -q ERROR: log; then exit 1; else exit 0; fi
|
||||
|
||||
54
.github/workflows/create-release-docs.yml
vendored
Normal file
54
.github/workflows/create-release-docs.yml
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
name: "Create release docs"
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- created
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- name: Checkout rgbds@master
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: gbdev/rgbds
|
||||
ref: master
|
||||
path: rgbds
|
||||
- name: Checkout rgbds-www@master
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: gbdev/rgbds-www
|
||||
ref: master
|
||||
path: rgbds-www
|
||||
- name: Build and install mandoc
|
||||
run: |
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get install -yq zlib1g-dev
|
||||
wget 'http://mandoc.bsd.lv/snapshots/mandoc-1.14.5.tar.gz'
|
||||
tar xf mandoc-1.14.5.tar.gz
|
||||
cd mandoc-1.14.5
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
- name: Update pages
|
||||
working-directory: rgbds
|
||||
run: |
|
||||
./.github/actions/get-pages.sh ../rgbds-www/_documentation ${GITHUB_REF}
|
||||
- name: Push new pages
|
||||
working-directory: rgbds-www
|
||||
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 "community@gbdev.io"
|
||||
git add .
|
||||
git commit -m "Create RGBDS ${GITHUB_REF} documentation"
|
||||
if git remote | grep -q origin; then
|
||||
git remote set-url origin git@github.com:gbdev/rgbds-www.git
|
||||
else
|
||||
git remote add origin git@github.com:gbdev/rgbds-www.git
|
||||
fi
|
||||
git push origin master
|
||||
110
.github/workflows/testing.yml
vendored
110
.github/workflows/testing.yml
vendored
@@ -1,5 +1,7 @@
|
||||
name: "Regression testing"
|
||||
on: push
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
jobs:
|
||||
unix-testing:
|
||||
@@ -7,10 +9,17 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04, macos-10.15]
|
||||
cc: [gcc, clang]
|
||||
buildsys: [make, cmake]
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
cc: gcc
|
||||
target: develop
|
||||
cmakevars: -DSANITIZERS=ON -DMORE_WARNINGS=ON -DCMAKE_BUILD_TYPE=Debug
|
||||
- os: ubuntu-20.04
|
||||
cc: gcc
|
||||
target: develop
|
||||
cmakevars: -DSANITIZERS=ON -DMORE_WARNINGS=ON -DCMAKE_BUILD_TYPE=Debug
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -18,12 +27,18 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
./.github/actions/install_deps.sh ${{ matrix.os }}
|
||||
- name: Build
|
||||
- name: Build & install using Make
|
||||
run: |
|
||||
make ${{ matrix.target }} -j Q= CC=${{ matrix.cc }}
|
||||
- name: Install
|
||||
run: |
|
||||
sudo make install -j Q=
|
||||
if: matrix.buildsys == 'make'
|
||||
- name: Build & install using CMake
|
||||
run: |
|
||||
cmake -S . -B build -DCMAKE_VERBOSE_MAKEFILE=ON ${{ matrix.cmakevars }}
|
||||
cmake --build build
|
||||
cp build/src/rgb{asm,link,fix,gfx} .
|
||||
sudo cmake --install build
|
||||
if: matrix.buildsys == 'cmake'
|
||||
- name: Package binaries
|
||||
run: |
|
||||
mkdir bins
|
||||
@@ -31,14 +46,85 @@ jobs:
|
||||
- name: Upload binaries
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: rgbds-canary-${{ matrix.os }}-${{ matrix.cc }}
|
||||
name: rgbds-canary-${{ matrix.os }}-${{ matrix.cc }}-${{ matrix.buildsys }}
|
||||
path: bins
|
||||
- name: Test
|
||||
shell: bash
|
||||
run: |
|
||||
test/run-tests.sh
|
||||
|
||||
windows-build:
|
||||
windows-testing:
|
||||
strategy:
|
||||
matrix:
|
||||
bits: [32, 64]
|
||||
include:
|
||||
- bits: 32
|
||||
arch: x86
|
||||
platform: Win32
|
||||
- bits: 64
|
||||
arch: x86_x64
|
||||
platform: x64
|
||||
fail-fast: false
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Get zlib, libpng and bison
|
||||
run: | # TODO: use an array
|
||||
$wc = New-Object System.Net.WebClient
|
||||
$wc.DownloadFile('https://www.zlib.net/zlib1211.zip', 'zlib.zip')
|
||||
$hash = (Get-FileHash "zlib.zip" -Algorithm SHA256).Hash
|
||||
if ($hash -ne 'd7510a8ee1918b7d0cad197a089c0a2cd4d6df05fee22389f67f115e738b178d') {
|
||||
Write-Host "zlib SHA256 mismatch! ($hash)"
|
||||
exit 1
|
||||
}
|
||||
$wc.DownloadFile('https://download.sourceforge.net/libpng/lpng1637.zip', 'libpng.zip')
|
||||
$hash = (Get-FileHash "libpng.zip" -Algorithm SHA256).Hash
|
||||
if ($hash -ne '3b4b1cbd0bae6822f749d39b1ccadd6297f05e2b85a83dd2ce6ecd7d09eabdf2') {
|
||||
Write-Host "libpng SHA256 mismatch! ($hash)"
|
||||
exit 1
|
||||
}
|
||||
$wc.DownloadFile('https://github.com/lexxmark/winflexbison/releases/download/v2.5.23/win_flex_bison-2.5.23.zip', 'winflexbison.zip')
|
||||
$hash = (Get-FileHash "winflexbison.zip" -Algorithm SHA256).Hash
|
||||
if ($hash -ne '6AA5C8EA662DA1550020A5804C28BE63FFAA53486DA9F6842E24C379EC422DFC') {
|
||||
Write-Host "bison SHA256 mismatch! ($hash)"
|
||||
}
|
||||
Expand-Archive -DestinationPath . "zlib.zip"
|
||||
Expand-Archive -DestinationPath . "libpng.zip"
|
||||
Expand-Archive -DestinationPath install_dir "winflexbison.zip"
|
||||
Move-Item zlib-1.2.11 zlib
|
||||
Move-Item lpng1637 libpng
|
||||
- name: Build zlib
|
||||
run: | # BUILD_SHARED_LIBS causes the output DLL to be correctly called `zlib1.dll`
|
||||
cmake -S zlib -B zbuild -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DBUILD_SHARED_LIBS=ON
|
||||
cmake --build zbuild --config Release
|
||||
cmake --install zbuild
|
||||
- name: Build libpng
|
||||
run: |
|
||||
cmake -S libpng -B pngbuild -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DPNG_SHARED=ON -DPNG_STATIC=ON -DPNG_TESTS=OFF
|
||||
cmake --build pngbuild --config Release
|
||||
cmake --install pngbuild
|
||||
- name: Build Windows binaries
|
||||
run: |
|
||||
cmake -S . -B build -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build build --config Release
|
||||
cmake --install build
|
||||
- name: Package binaries
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir bins
|
||||
cp install_dir/bin/{rgbasm.exe,rgblink.exe,rgbfix.exe,rgbgfx.exe,zlib1.dll,libpng16.dll} bins
|
||||
- name: Upload Windows binaries
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: rgbds-canary-win${{ matrix.bits }}
|
||||
path: bins
|
||||
- name: Test
|
||||
shell: bash
|
||||
run: |
|
||||
cp bins/* .
|
||||
test/run-tests.sh
|
||||
|
||||
windows-xbuild:
|
||||
strategy:
|
||||
matrix:
|
||||
bits: [32, 64]
|
||||
@@ -50,6 +136,7 @@ jobs:
|
||||
- bits: 64
|
||||
arch: x86-64
|
||||
triplet: x86_64-w64-mingw32
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
DIST_DIR: win${{ matrix.bits }}
|
||||
@@ -65,7 +152,7 @@ jobs:
|
||||
- name: Install libpng dev headers for MinGW
|
||||
run: |
|
||||
sudo ./.github/actions/mingw-w64-libpng-dev.sh ${{ matrix.triplet }}
|
||||
- name: Build Windows binaries
|
||||
- name: Cross-build Windows binaries
|
||||
run: |
|
||||
make mingw${{ matrix.bits }} -j Q=
|
||||
- name: Package binaries
|
||||
@@ -81,21 +168,22 @@ jobs:
|
||||
- name: Upload Windows binaries
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: rgbds-canary-win${{ matrix.bits }}
|
||||
name: rgbds-canary-mingw-win${{ matrix.bits }}
|
||||
path: bins
|
||||
|
||||
windows-testing:
|
||||
needs: windows-build
|
||||
windows-xtesting:
|
||||
needs: windows-xbuild
|
||||
strategy:
|
||||
matrix:
|
||||
bits: [32, 64]
|
||||
fail-fast: false
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Retrieve binaries
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: rgbds-canary-win${{ matrix.bits }}
|
||||
name: rgbds-canary-mingw-win${{ matrix.bits }}
|
||||
path: bins
|
||||
- name: Extract binaries
|
||||
shell: bash
|
||||
|
||||
65
.github/workflows/update-master-docs.yml
vendored
Normal file
65
.github/workflows/update-master-docs.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
name: "Update master docs"
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- .github/actions/get-pages.sh
|
||||
- src/gbz80.7
|
||||
- src/rgbds.5
|
||||
- src/rgbds.7
|
||||
- src/asm/rgbasm.1
|
||||
- src/asm/rgbasm.5
|
||||
- src/link/rgblink.1
|
||||
- src/link/rgblink.5
|
||||
- src/fix/rgbfix.1
|
||||
- src/gfx/rgbgfx.1
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- name: Checkout rgbds@master
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: gbdev/rgbds
|
||||
ref: master
|
||||
path: rgbds
|
||||
- name: Checkout rgbds-www@master
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: gbdev/rgbds-www
|
||||
ref: master
|
||||
path: rgbds-www
|
||||
- name: Build and install mandoc
|
||||
run: |
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get install -yq zlib1g-dev
|
||||
wget 'http://mandoc.bsd.lv/snapshots/mandoc-1.14.5.tar.gz'
|
||||
tar xf mandoc-1.14.5.tar.gz
|
||||
cd mandoc-1.14.5
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
- name: Update pages
|
||||
working-directory: rgbds
|
||||
run: |
|
||||
./.github/actions/get-pages.sh ../rgbds-www/_documentation master
|
||||
- name: Push new pages
|
||||
working-directory: rgbds-www
|
||||
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 "community@gbdev.io"
|
||||
git add .
|
||||
git commit -m "Update RGBDS master documentation"
|
||||
if git remote | grep -q origin; then
|
||||
git remote set-url origin git@github.com:gbdev/rgbds-www.git
|
||||
else
|
||||
git remote add origin git@github.com:gbdev/rgbds-www.git
|
||||
fi
|
||||
git push origin master
|
||||
@@ -6,35 +6,11 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.8)
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
cmake_policy(VERSION 3.0)
|
||||
|
||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||
|
||||
set(RGBDS_VER 0.4.1)
|
||||
set(RGBDS_DESC "Rednex Game Boy Development System")
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS 3.0)
|
||||
project(rgbds C)
|
||||
set(PROJECT_VERSION "${RGBDS_VER}")
|
||||
else()
|
||||
if(CMAKE_VERSION VERSION_LESS 3.9)
|
||||
project(rgbds VERSION "${RGBDS_VER}"
|
||||
LANGUAGES C)
|
||||
else()
|
||||
project(rgbds VERSION "${RGBDS_VER}"
|
||||
DESCRIPTION "${RGBDS_DESC}"
|
||||
LANGUAGES C)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS 3.9)
|
||||
set(PROJECT_DESCRIPTION "${RGBDS_DESC}")
|
||||
endif()
|
||||
|
||||
set(DEFAULT_BUILD_TYPE "Release")
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}")
|
||||
endif()
|
||||
project(rgbds
|
||||
LANGUAGES C)
|
||||
|
||||
# get real path of source and binary directories
|
||||
get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH)
|
||||
@@ -47,25 +23,59 @@ if(srcdir STREQUAL bindir)
|
||||
message(FATAL_ERROR "Terminating configuration")
|
||||
endif()
|
||||
|
||||
find_package(PNG 1.2 REQUIRED)
|
||||
find_package(BISON REQUIRED)
|
||||
find_package(FLEX)
|
||||
|
||||
include_directories("${PROJECT_SOURCE_DIR}/include")
|
||||
|
||||
option(SANITIZERS "Build with sanitizers enabled" OFF)
|
||||
option(MORE_WARNINGS "Turn on more warnings" OFF)
|
||||
option(TRACE_PARSER "Trace parser execution" OFF)
|
||||
option(TRACE_LEXER "Trace lexer execution" OFF)
|
||||
|
||||
if(MSVC)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W1 /MP -D_CRT_SECURE_NO_WARNINGS")
|
||||
add_compile_options(/W1 /MP)
|
||||
add_definitions(/D_CRT_SECURE_NO_WARNINGS)
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -pedantic")
|
||||
add_compile_options(-Wall -pedantic)
|
||||
if(SANITIZERS)
|
||||
set(SAN_FLAGS -fsanitize=shift -fsanitize=integer-divide-by-zero
|
||||
-fsanitize=unreachable -fsanitize=vla-bound
|
||||
-fsanitize=signed-integer-overflow -fsanitize=bounds
|
||||
-fsanitize=object-size -fsanitize=bool -fsanitize=enum
|
||||
-fsanitize=alignment -fsanitize=null)
|
||||
add_compile_options(${SAN_FLAGS})
|
||||
link_libraries(${SAN_FLAGS})
|
||||
endif()
|
||||
|
||||
if(MORE_WARNINGS)
|
||||
add_compile_options(-Werror -Wextra -Wno-type-limits
|
||||
-Wno-sign-compare -Wvla -Wformat -Wformat-security -Wformat-overflow=2
|
||||
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused
|
||||
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5
|
||||
-Wstringop-overflow=4 -Walloc-zero -Wduplicated-cond
|
||||
-Wfloat-equal -Wshadow -Wcast-qual -Wcast-align -Wlogical-op
|
||||
-Wnested-externs -Wno-aggressive-loop-optimizations -Winline
|
||||
-Wundef -Wstrict-prototypes -Wold-style-definition)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS 3.12)
|
||||
add_definitions(-DBUILD_VERSION_STRING="${PROJECT_VERSION}")
|
||||
else()
|
||||
add_compile_definitions(BUILD_VERSION_STRING="${PROJECT_VERSION}")
|
||||
endif()
|
||||
# Use versioning consistent with Makefile
|
||||
# the git revision is used but uses the fallback in an archive
|
||||
|
||||
execute_process(COMMAND git describe --tags --dirty --always
|
||||
OUTPUT_VARIABLE GIT_REV
|
||||
ERROR_QUIET)
|
||||
string(STRIP "${GIT_REV}" GIT_REV)
|
||||
|
||||
add_definitions(-DBUILD_VERSION_STRING="${GIT_REV}")
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_C_STANDARD_REQUIRED True)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
if(TRACE_PARSER)
|
||||
target_compile_definitions(rgbasm PRIVATE -DYYDEBUG)
|
||||
endif()
|
||||
|
||||
if(TRACE_LEXER)
|
||||
target_compile_definitions(rgbasm PRIVATE -DLEXER_DEBUG)
|
||||
endif()
|
||||
|
||||
@@ -8,7 +8,6 @@ FROM alpine:latest
|
||||
RUN apk add --update \
|
||||
build-base \
|
||||
byacc \
|
||||
flex \
|
||||
libpng-dev
|
||||
COPY . /rgbds
|
||||
WORKDIR /rgbds
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
|
||||
Copyright (c) 1997-2020, Carsten Sorensen and RGBDS contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
|
||||
48
Makefile
48
Makefile
@@ -27,22 +27,23 @@ PNGCFLAGS := `${PKG_CONFIG} --cflags libpng`
|
||||
PNGLDFLAGS := `${PKG_CONFIG} --libs-only-L libpng`
|
||||
PNGLDLIBS := `${PKG_CONFIG} --libs-only-l libpng`
|
||||
|
||||
# Note: if this comes up empty, `version.c` will automatically fall back to last release number
|
||||
VERSION_STRING := `git describe --tags --dirty --always 2>/dev/null`
|
||||
|
||||
WARNFLAGS := -Wall
|
||||
|
||||
# Overridable CFLAGS
|
||||
CFLAGS := -O3
|
||||
CFLAGS ?= -O3 -DNDEBUG
|
||||
# Non-overridable CFLAGS
|
||||
REALCFLAGS := ${CFLAGS} ${WARNFLAGS} -std=gnu11 -D_POSIX_C_SOURCE=200809L \
|
||||
-Iinclude
|
||||
# Overridable LDFLAGS
|
||||
LDFLAGS :=
|
||||
LDFLAGS ?=
|
||||
# Non-overridable LDFLAGS
|
||||
REALLDFLAGS := ${LDFLAGS} ${WARNFLAGS} \
|
||||
-DBUILD_VERSION_STRING=\"${VERSION_STRING}\"
|
||||
|
||||
YFLAGS :=
|
||||
YFLAGS ?=
|
||||
|
||||
YACC := yacc
|
||||
RM := rm -rf
|
||||
@@ -52,14 +53,13 @@ RM := rm -rf
|
||||
all: rgbasm rgblink rgbfix rgbgfx
|
||||
|
||||
rgbasm_obj := \
|
||||
src/asm/asmy.o \
|
||||
src/asm/charmap.o \
|
||||
src/asm/fstack.o \
|
||||
src/asm/globlex.o \
|
||||
src/asm/lexer.o \
|
||||
src/asm/macro.o \
|
||||
src/asm/main.o \
|
||||
src/asm/math.o \
|
||||
src/asm/parser.o \
|
||||
src/asm/output.o \
|
||||
src/asm/rpn.o \
|
||||
src/asm/section.o \
|
||||
@@ -72,7 +72,7 @@ rgbasm_obj := \
|
||||
src/hashmap.o \
|
||||
src/linkdefs.o
|
||||
|
||||
src/asm/globlex.o src/asm/lexer.o src/asm/constexpr.o: src/asm/asmy.h
|
||||
src/asm/lexer.o src/asm/main.o: src/asm/parser.h
|
||||
|
||||
rgblink_obj := \
|
||||
src/link/assign.o \
|
||||
@@ -127,8 +127,7 @@ rgbgfx: ${rgbgfx_obj}
|
||||
.c.o:
|
||||
$Q${CC} ${REALCFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
||||
|
||||
# Target used to remove all files generated by other Makefile targets, except
|
||||
# for the html documentation.
|
||||
# Target used to remove all files generated by other Makefile targets
|
||||
|
||||
clean:
|
||||
$Q${RM} rgbasm rgbasm.exe
|
||||
@@ -137,16 +136,7 @@ clean:
|
||||
$Q${RM} rgbgfx rgbgfx.exe
|
||||
$Qfind src/ -name "*.o" -exec rm {} \;
|
||||
$Q${RM} rgbshim.sh
|
||||
$Q${RM} src/asm/asmy.c src/asm/asmy.h
|
||||
|
||||
# Target used to remove all html files generated by the wwwman target
|
||||
|
||||
cleanwwwman:
|
||||
$Q${RM} docs/rgbds.7.html docs/gbz80.7.html docs/rgbds.5.html
|
||||
$Q${RM} docs/rgbasm.1.html docs/rgbasm.5.html
|
||||
$Q${RM} docs/rgblink.1.html docs/rgblink.5.html
|
||||
$Q${RM} docs/rgbfix.1.html
|
||||
$Q${RM} docs/rgbgfx.1.html
|
||||
$Q${RM} src/asm/parser.c src/asm/parser.h
|
||||
|
||||
# Target used to install the binaries and man pages.
|
||||
|
||||
@@ -191,29 +181,13 @@ checkpatch:
|
||||
| ${CHECKPATCH} - || true; \
|
||||
done
|
||||
|
||||
# Target for the project maintainer to easily create web manuals.
|
||||
# It relies on mandoc: http://mdocml.bsd.lv
|
||||
|
||||
MANDOC := -Thtml -Ios=General -Oman=%N.%S.html -Ostyle=mandoc.css
|
||||
|
||||
wwwman:
|
||||
$Qmandoc ${MANDOC} src/rgbds.7 | src/doc_postproc.awk > docs/rgbds.7.html
|
||||
$Qmandoc ${MANDOC} src/gbz80.7 | src/doc_postproc.awk > docs/gbz80.7.html
|
||||
$Qmandoc ${MANDOC} src/rgbds.5 | src/doc_postproc.awk > docs/rgbds.5.html
|
||||
$Qmandoc ${MANDOC} src/asm/rgbasm.1 | src/doc_postproc.awk > docs/rgbasm.1.html
|
||||
$Qmandoc ${MANDOC} src/asm/rgbasm.5 | src/doc_postproc.awk > docs/rgbasm.5.html
|
||||
$Qmandoc ${MANDOC} src/fix/rgbfix.1 | src/doc_postproc.awk > docs/rgbfix.1.html
|
||||
$Qmandoc ${MANDOC} src/link/rgblink.1 | src/doc_postproc.awk > docs/rgblink.1.html
|
||||
$Qmandoc ${MANDOC} src/link/rgblink.5 | src/doc_postproc.awk > docs/rgblink.5.html
|
||||
$Qmandoc ${MANDOC} src/gfx/rgbgfx.1 | src/doc_postproc.awk > docs/rgbgfx.1.html
|
||||
|
||||
# This target is used during development in order to prevent adding new issues
|
||||
# to the source code. All warnings are treated as errors in order to block the
|
||||
# compilation and make the continous integration infrastructure return failure.
|
||||
|
||||
develop:
|
||||
$Qenv $(MAKE) -j WARNFLAGS="-Werror -Wall -Wextra -Wpedantic \
|
||||
-Wno-sign-compare -Wformat -Wformat-security -Wformat-overflow=2 \
|
||||
$Qenv $(MAKE) -j WARNFLAGS="-Werror -Wall -Wextra -Wpedantic -Wno-type-limits \
|
||||
-Wno-sign-compare -Wvla -Wformat -Wformat-security -Wformat-overflow=2 \
|
||||
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused \
|
||||
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5 \
|
||||
-Wstringop-overflow=4 -Walloc-zero -Wduplicated-cond \
|
||||
@@ -224,7 +198,7 @@ develop:
|
||||
-fsanitize=unreachable -fsanitize=vla-bound \
|
||||
-fsanitize=signed-integer-overflow -fsanitize=bounds \
|
||||
-fsanitize=object-size -fsanitize=bool -fsanitize=enum \
|
||||
-fsanitize=alignment -fsanitize=null -DDEVELOP" CFLAGS="-g -O0"
|
||||
-fsanitize=alignment -fsanitize=null" CFLAGS="-ggdb3 -O0"
|
||||
|
||||
# Targets for the project maintainer to easily create Windows exes.
|
||||
# This is not for Windows users!
|
||||
|
||||
187
README.rst
187
README.rst
@@ -15,160 +15,87 @@ other UNIX tools.
|
||||
This toolchain is maintained on `GitHub <https://github.com/rednex/rgbds>`__.
|
||||
|
||||
The documentation of this toolchain can be viewed online
|
||||
`here <https://rednex.github.io/rgbds/>`__, it is generated from the man pages
|
||||
`here <https://rgbds.gbdev.io/docs/>`__, it is generated from the man pages
|
||||
found in this repository.
|
||||
|
||||
1. Installing RGBDS
|
||||
-------------------
|
||||
|
||||
1.1 Windows
|
||||
~~~~~~~~~~~
|
||||
|
||||
Windows builds are available in the releases page on GitHub
|
||||
`here <https://github.com/rednex/rgbds/releases>`__.
|
||||
|
||||
Extract the zip and then add the executable directory to the path. For example:
|
||||
|
||||
::
|
||||
|
||||
path %PATH%;C:\Programs\rgbds-0.3.8-win64\win64
|
||||
|
||||
Alternatively, the RGBDS executables can be simply dropped in the folder of the project they're used in.
|
||||
|
||||
If you require the latest version in development, it should be possible to
|
||||
compile RGBDS with MinGW or Cygwin by following the instructions to build it on
|
||||
UNIX systems.
|
||||
|
||||
1.2 macOS
|
||||
~~~~~~~~~
|
||||
|
||||
You can build RGBDS by following the instructions below. However, if you would
|
||||
prefer not to build RGBDS yourself, you may also install it using
|
||||
`Homebrew <http://brew.sh/>`__.
|
||||
|
||||
To install the latest release, use:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
brew install rgbds
|
||||
|
||||
To install RGBDS with all of the current changes in development (as seen on the
|
||||
``master`` branch on GitHub), use:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
brew install rgbds --HEAD
|
||||
|
||||
1.3 Arch Linux
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
To install RGBDS through the AUR run
|
||||
|
||||
.. code:: sh
|
||||
|
||||
yaourt -S rgbds
|
||||
|
||||
1.4 Other UNIX-like systems
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
No official binaries of RGBDS are distributed for these systems, you must follow
|
||||
the simple instructions below to compile and install it.
|
||||
|
||||
2. Building RGBDS from source
|
||||
-----------------------------
|
||||
|
||||
RGBDS can be built in UNIX-like systems by following the instructions below.
|
||||
|
||||
2.1 Dependencies
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
RGBDS requires yacc, libpng and pkg-config to be installed.
|
||||
|
||||
On macOS, install the latter two with `Homebrew <http://brew.sh/>`__:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
brew install libpng pkg-config
|
||||
|
||||
On other Unixes, use the built-in package manager. For example, on Debian or
|
||||
Ubuntu:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
sudo apt-get install byacc flex pkg-config libpng-dev
|
||||
|
||||
You can test if libpng and pkg-config are installed by running ``pkg-config
|
||||
--cflags libpng``: if the output is a path, then you're good, and if it outputs
|
||||
an error then you need to install them via a package manager.
|
||||
|
||||
2.2 Build process
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
To build the programs, run in your terminal:
|
||||
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.
|
||||
|
||||
.. code:: sh
|
||||
|
||||
make
|
||||
|
||||
Then, to install the compiled programs and manual pages, run (with appropriate
|
||||
privileges, e.g, with ``sudo``):
|
||||
sudo make install
|
||||
|
||||
.. code:: sh
|
||||
|
||||
make install
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build build
|
||||
cmake --install build
|
||||
|
||||
After installation, you can read the manuals with the ``man`` command. E.g.,
|
||||
2. RGBDS Folder Organization
|
||||
----------------------------
|
||||
|
||||
.. code:: sh
|
||||
The RGBDS source code file structure somewhat resembles the following:
|
||||
|
||||
man 7 rgbds
|
||||
::
|
||||
|
||||
There are some variables in the Makefile that can be redefined by the user. The
|
||||
variables described below can affect installation behavior when given on the
|
||||
make command line. For example, to install RGBDS in your home directory instead
|
||||
of systemwide, run the following:
|
||||
.
|
||||
├── .github/
|
||||
│ ├── actions/
|
||||
│ │ └── ...
|
||||
│ └── workflows/
|
||||
│ └── ...
|
||||
├── contrib/
|
||||
│ ├── zsh_compl/
|
||||
│ │ └── ...
|
||||
│ └── ...
|
||||
├── include/
|
||||
│ └── ...
|
||||
├── src/
|
||||
│ ├── asm/
|
||||
│ │ └── ...
|
||||
│ ├── extern/
|
||||
│ │ └── ...
|
||||
│ ├── fix/
|
||||
│ │ └── ...
|
||||
│ ├── gfx/
|
||||
│ │ └── ...
|
||||
│ ├── link/
|
||||
│ │ └── ...
|
||||
│ ├── CMakeLists.txt
|
||||
│ └── ...
|
||||
├── test/
|
||||
│ ├── ...
|
||||
│ └── run-tests.sh
|
||||
├── CMakeLists.txt
|
||||
├── Makefile
|
||||
└── README.rst
|
||||
|
||||
.. code:: sh
|
||||
- ``.github/`` - files and scripts related to the integration of the RGBDS codebase with
|
||||
GitHub.
|
||||
|
||||
make install PREFIX=$HOME
|
||||
- ``contrib/`` - scripts and other resources which may be useful to users and developers of
|
||||
RGBDS.
|
||||
|
||||
To do a verbose build, run:
|
||||
* ``zsh_compl`` contains tab completion scripts for use with zsh. Put them somewhere in your ``fpath``, and they should auto-load.
|
||||
|
||||
.. code:: sh
|
||||
- ``include/`` - header files for each respective C files in `src`.
|
||||
|
||||
make Q=
|
||||
- ``src/`` - source code and manual pages for RGBDS.
|
||||
|
||||
This is the complete list of user-defined variables:
|
||||
* Note that the code unique to each RGBDS tool is stored in its respective subdirectory
|
||||
(rgbasm -> ``src/asm/``, for example). ``src/extern/`` contains code imported from external sources.
|
||||
|
||||
- ``PREFIX``: Location where RGBDS will be installed. Defaults to
|
||||
``/usr/local``.
|
||||
- ``test/`` - testing framework used to verify that changes to the code don't break or modify the behavior of RGBDS.
|
||||
|
||||
- ``bindir``: Location where the binaries will be installed. Defaults to
|
||||
``${PREFIX}/bin``.
|
||||
3. History
|
||||
----------
|
||||
|
||||
- ``mandir``: Location where the manpages will be installed. Defaults to
|
||||
``${PREFIX}/share/man``.
|
||||
|
||||
- ``DESTDIR``: This is prepended to all paths during the installation. It is
|
||||
mainly used for packaging.
|
||||
|
||||
- ``Q``: Whether to quiet the build or not. To make the build more verbose,
|
||||
clear this variable. Defaults to ``@``.
|
||||
|
||||
- ``STRIP``: Whether to strip the installed binaries of debug symbols or not.
|
||||
Defaults to ``-s``.
|
||||
|
||||
- ``BINMODE``: Permissions of the installed binaries. Defaults to ``755``.
|
||||
|
||||
- ``MANMODE``: Permissions of the installed manpages. Defaults to ``644``.
|
||||
|
||||
- ``CHECKPATCH``: Path of the script ``checkpatch.pl`` of the Linux kernel.
|
||||
Defaults to ``../linux/scripts/checkpatch.pl``.
|
||||
|
||||
3 History
|
||||
---------
|
||||
|
||||
- Around 1997, Carsten Sorensen (AKA SurfSmurf) writes ASMotor as a
|
||||
- Around 1997, Carsten Sørensen (AKA SurfSmurf) writes ASMotor as a
|
||||
general-purpose assembler/linker system for DOS/Win32
|
||||
|
||||
- Around 1999, Justin Lloyd (AKA Otaku no Zoku) adapts ASMotor to read and
|
||||
@@ -184,3 +111,5 @@ This is the complete list of user-defined variables:
|
||||
- 2017, Bentley's repository is moved to a neutral name.
|
||||
|
||||
- 2018, codebase relicensed under the MIT license.
|
||||
|
||||
- 2020, repository is moved to the `gbdev <https://github.com/gbdev>`__ organisation. The `rgbds.gbdev.io <https://rgbds.gbdev.io>`__ website serving documentation and downloads is created.
|
||||
|
||||
49
contrib/zsh_compl/_rgbasm
Normal file
49
contrib/zsh_compl/_rgbasm
Normal file
@@ -0,0 +1,49 @@
|
||||
#compdef rgbasm
|
||||
|
||||
_rgbasm_warnings() {
|
||||
local warnings=(
|
||||
'error:Turn all warnings into errors'
|
||||
|
||||
'all:Enable most warning messages'
|
||||
'extra:Enable extra, possibly unwanted, messages'
|
||||
'everything:Enable literally everything'
|
||||
|
||||
'assert:Warn when WARN-type asserts fail'
|
||||
'builtin-args:Report incorrect args to built-in funcs'
|
||||
'div:Warn when dividing the smallest int by -1'
|
||||
'empty-entry:Warn on empty entries in db, dw, dl args'
|
||||
'large-constant:Warn on constants too large for a signed 32-bit int'
|
||||
'long-string:Warn on strings too long'
|
||||
'obsolete:Warn when using deprecated features'
|
||||
'shift:Warn when shifting negative values'
|
||||
'shift-amount:Warn when a shift'\''s operand it negative or \> 32'
|
||||
'truncation:Warn when implicit truncations lose bits'
|
||||
'user:Warn when executing the WARN built-in'
|
||||
)
|
||||
# TODO: handle `no-` and `error=` somehow?
|
||||
_describe warning warnings
|
||||
}
|
||||
|
||||
local args=(
|
||||
# Arguments are listed here in the same order as in the manual, except for the version
|
||||
'(- : * options)'{-V,--version}'[Print version number]'
|
||||
|
||||
'(-E --export-all)'{-E,--export-all}'[Export all symbols]'
|
||||
'(-h --halt-without-nop)'{-h,--halt-without-nop}'[Avoid outputting a `nop` after `halt`]'
|
||||
'(-L ---preserve-ld)'{-L,--preserve-ld}'[Prevent auto-optimizing `ld` into `ldh`]'
|
||||
'(-v --verbose)'{-v,--verbose}'[Print additional messages regarding progression]'
|
||||
-w'[Disable all warnings]'
|
||||
|
||||
'(-b --binary-digits)'{-b,--binary-digits}'+[Change chars for binary constants]:digit spec:'
|
||||
'(-D --define)'{-D,--define}'+[Define a string symbol]:name + value (default 1):'
|
||||
'(-g --gfx-chars)'{-g,--gfx-chars}'+[Change chars for gfx constants]:chars spec:'
|
||||
'(-i --include)'{-i,--include}'+[Add an include directory]:include path:_files -/'
|
||||
'(-M --dependfile)'{-M,--dependfile}"+[List deps in make format]:output file:_files -g '*.{d,mk}"
|
||||
'(-o --output)'{-o,--output}'+[Output file]:output file:_files'
|
||||
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
|
||||
'(-r --recursion-depth)'{-r,--recursion-depth}'+[Set maximum recursion depth]:depth:'
|
||||
'(-W --warning)'{-W,--warning}'+[Toggle warning flags]:warning flag:_rgbasm_warnings'
|
||||
|
||||
'*'":assembly sources:_files -g '*.asm'"
|
||||
)
|
||||
_arguments -s -S : $args
|
||||
25
contrib/zsh_compl/_rgbfix
Normal file
25
contrib/zsh_compl/_rgbfix
Normal file
@@ -0,0 +1,25 @@
|
||||
#compdef rgbfix
|
||||
|
||||
local args=(
|
||||
# Arguments are listed here in the same order as in the manual, except for the version
|
||||
'(- : * options)'{-V,--version}'[Print version number]'
|
||||
|
||||
'(-C --color-only)'{-C,--color-only}'[Mark ROM as GBC-only]'
|
||||
'(-c --color-compatible)'{-c,--color-compatible}'[Mark ROM as GBC-compatible]'
|
||||
'(-j --non-japanese)'{-j,--non-japanese}'[Set the non-Japanese region flag]'
|
||||
'(-s --sgb-compatible)'{-s,--sgb-compatible}'[Set the SGB flag]'
|
||||
'(-f --fix-spec -v --validate)'{-v,--validate}'[Shorthand for -f lhg]'
|
||||
|
||||
'(-f --fix-spec -v --validate)'{-f,--fix-spec}'+[Fix or trash some header values]:fix spec:'
|
||||
'(-i --game-id)'{-i,--game-id}'+[Set game ID string]:4-char game ID:'
|
||||
'(-k --new-licensee)'{-k,--new-licensee}'+[Set new licensee string]:2-char licensee ID:'
|
||||
'(-l --old-licensee)'{-l,--old-licensee}'+[Set old licensee ID]:licensee number:'
|
||||
'(-m --mbc-type)'{-m,--mbc-type}'+[Set MBC flags]:mbc flags byte:'
|
||||
'(-n --rom-version)'{-n,--rom-version}'+[Set ROM version]:rom version byte:'
|
||||
'(-p --pad-value)'{-p,--pad-value}'+[Pad to next valid size using this byte as padding]:padding byte:'
|
||||
'(-r --ram-size)'{-r,--ram-size}'+[Set RAM size]:ram size byte:'
|
||||
'(-t --title)'{-t,--title}'+[Set title string]:11-char title string:'
|
||||
|
||||
'*'":ROM files:_files -g '*.{gb,sgb,gbc}'"
|
||||
)
|
||||
_arguments -s -S : $args
|
||||
37
contrib/zsh_compl/_rgbgfx
Normal file
37
contrib/zsh_compl/_rgbgfx
Normal file
@@ -0,0 +1,37 @@
|
||||
#compdef rgbgfx
|
||||
|
||||
_depths() {
|
||||
local depths=(
|
||||
'1:1bpp'
|
||||
'2:2bpp (native)'
|
||||
)
|
||||
|
||||
_describe 'bit depth' depths
|
||||
}
|
||||
|
||||
local args=(
|
||||
# Arguments are listed here in the same order as in the manual, except for the version
|
||||
'(- : * options)'{-V,--version}'[Print version number]'
|
||||
|
||||
'(-a --attr-map -A --output-attr-map)'{-A,--output-attr-map}'[Shortcut for -a <file>.attrmap]'
|
||||
'(-C --color-curve)'{-C,--color-curve}'[Generate palettes using GBC color curve]'
|
||||
'(-D --debug)'{-D,--debug}'[Enable debug features]'
|
||||
'(-f --fix -F --fix-and-save)'{-f,--fix}'[Fix input PNG into an indexed image]'
|
||||
'(-f --fix -F --fix-and-save)'{-F,--fix-and-save}'[Like -f but also save CLI params within the PNG]'
|
||||
'(-h --horizontal)'{-h,--horizontal}'[Lay out tiles horizontally instead of vertically]'
|
||||
'(-m --mirror-tiles)'{-m,--mirror-tiles}'[Eliminate mirrored tiles from output]'
|
||||
'(-p --palette -P --output-palette)'{-P,--output-palette}'[Shortcut for -p <file>.pal]'
|
||||
'(-t --tilemap -T --output-tilemap)'{-T,--output-tilemap}'[Shortcut for -t <file>.tilemap]'
|
||||
'(-u --unique-tiles)'{-u,--unique-tiles}'[Eliminate redundant tiles]'
|
||||
'(-v --verbose)'{-v,--verbose}'[Enable verbose output]'
|
||||
|
||||
'(-a --attr-map -A --output-attr-map)'{-a,--attr-map}'+[Generate a map of tile attributes (mirroring)]:attrmap file:_files'
|
||||
'(-d --depth)'{-d,--depth}'+[Set bit depth]:bit depth:_depths'
|
||||
'(-o --output)'{-o,--output}'+[Set output file]:output file:_files'
|
||||
'(-p --palette -P --output-palette)'{-p,--palette}"+[Output the image's palette in little-endian native RGB555 format]:palette file:_files"
|
||||
'(-t --tilemap -T --output-tilemap)'{-t,--tilemap}'+[Generate a map of tile indices]:tilemap file:_files'
|
||||
'(-x --trim-end)'{-x,--trim-end}'+[Trim end of output by this many tiles]:tile count:'
|
||||
|
||||
'*'":input png files:_files -g '*.png'"
|
||||
)
|
||||
_arguments -s -S : $args
|
||||
22
contrib/zsh_compl/_rgblink
Normal file
22
contrib/zsh_compl/_rgblink
Normal file
@@ -0,0 +1,22 @@
|
||||
#compdef rgblink
|
||||
|
||||
local args=(
|
||||
# Arguments are listed here in the same order as in the manual, except for the version
|
||||
'(- : * options)'{-V,--version}'[Print version number]'
|
||||
|
||||
'(-d --dmg)'{-d,--dmg}'[Enable DMG mode (-w + no VRAM banking)]'
|
||||
'(-t --tiny)'{-t,--tiny}'[Enable tiny mode, disabling ROM banking]'
|
||||
'(-v --verbose)'{-v,--verbose}'[Enable verbose output]'
|
||||
'(-w --wramx)'{-w,--wramx}'[Disable WRAM banking]'
|
||||
|
||||
'(-l --linkerscript)'{-l,--linkerscript}"+[Use a linker script]:linker script:_files -g '*.link'"
|
||||
'(-m --map)'{-m,--map}"+[Produce a map file]:map file:_files -g '*.map'"
|
||||
'(-n --sym)'(-n,--sym)"+[Produce a symbol file]:sym file:_files -g '*.sym'"
|
||||
'(-O --overlay)'{-O,--overlay}'+[Overlay sections over on top of bin file]:base overlay:_files'
|
||||
'(-o --output)'{-o,--output}"+[Write ROM image to this file]:rom file:_files -g '*.{gb,sgb,gbc}'"
|
||||
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
|
||||
'(-s --smart)'{-s,--smart}'+[!BROKEN! Perform smart linking from this symbol]:symbol name:'
|
||||
|
||||
'*'":object files:_files -g '*.o'"
|
||||
)
|
||||
_arguments -s -S : $args
|
||||
1670
docs/gbz80.7.html
1670
docs/gbz80.7.html
File diff suppressed because it is too large
Load Diff
@@ -1,36 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
|
||||
<title>General Information</title>
|
||||
<link rel="stylesheet" type="text/css" href="mandoc.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>RGBDS — Rednex Game Boy Development System</h1>
|
||||
<h2>Table of Contents</h2>
|
||||
<ol>
|
||||
<li>General information
|
||||
<ul>
|
||||
<li><a href="rgbds.7.html">RGBDS general information</a></li>
|
||||
<li><a href="rgbds.5.html">RGBDS object file format</a></li>
|
||||
</ul>
|
||||
<li>Language description
|
||||
<ul>
|
||||
<li><a href="rgbasm.5.html">RGBASM language description</a></li>
|
||||
<li><a href="rgblink.5.html">RGBLINK linkerscript language description</a></li>
|
||||
<li><a href="gbz80.7.html">GBZ80 CPU instruction set description</a></li>
|
||||
</ul>
|
||||
<li>Command line usage
|
||||
<ul>
|
||||
<li><a href="rgbasm.1.html">RGBASM command-line usage</a></li>
|
||||
<li><a href="rgblink.1.html">RGBLINK command-line usage</a></li>
|
||||
<li><a href="rgbfix.1.html">RGBFIX command-line usage</a></li>
|
||||
<li><a href="rgbgfx.1.html">RGBGFX command-line usage</a></li>
|
||||
</ul>
|
||||
</ol>
|
||||
<h2 id="GitHub Repository">GitHub Repository:</h2>
|
||||
<ul>
|
||||
<li><a href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a></li>
|
||||
</ul>
|
||||
</body>
|
||||
347
docs/mandoc.css
347
docs/mandoc.css
@@ -1,347 +0,0 @@
|
||||
/* $Id: mandoc.css,v 1.45 2019/03/01 10:57:18 schwarze Exp $ */
|
||||
/*
|
||||
* Standard style sheet for mandoc(1) -Thtml and man.cgi(8).
|
||||
*
|
||||
* Written by Ingo Schwarze <schwarze@openbsd.org>.
|
||||
* I place this file into the public domain.
|
||||
* Permission to use, copy, modify, and distribute it for any purpose
|
||||
* with or without fee is hereby granted, without any conditions.
|
||||
*/
|
||||
|
||||
/* Global defaults. */
|
||||
|
||||
html { max-width: 65em; }
|
||||
body { font-family: Helvetica,Arial,sans-serif; }
|
||||
table { margin-top: 0em;
|
||||
margin-bottom: 0em;
|
||||
border-collapse: collapse; }
|
||||
/* Some browsers set border-color in a browser style for tbody,
|
||||
* but not for table, resulting in inconsistent border styling. */
|
||||
tbody { border-color: inherit; }
|
||||
tr { border-color: inherit; }
|
||||
td { vertical-align: top;
|
||||
padding-left: 0.2em;
|
||||
padding-right: 0.2em;
|
||||
border-color: inherit; }
|
||||
ul, ol, dl { margin-top: 0em;
|
||||
margin-bottom: 0em; }
|
||||
li, dt { margin-top: 1em; }
|
||||
|
||||
.permalink { border-bottom: thin dotted;
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
text-decoration: inherit; }
|
||||
* { clear: both }
|
||||
|
||||
/* Search form and search results. */
|
||||
|
||||
fieldset { border: thin solid silver;
|
||||
border-radius: 1em;
|
||||
text-align: center; }
|
||||
input[name=expr] {
|
||||
width: 25%; }
|
||||
|
||||
table.results { margin-top: 1em;
|
||||
margin-left: 2em;
|
||||
font-size: smaller; }
|
||||
|
||||
/* Header and footer lines. */
|
||||
|
||||
table.head { width: 100%;
|
||||
border-bottom: 1px dotted #808080;
|
||||
margin-bottom: 1em;
|
||||
font-size: smaller; }
|
||||
td.head-vol { text-align: center; }
|
||||
td.head-rtitle {
|
||||
text-align: right; }
|
||||
|
||||
table.foot { width: 100%;
|
||||
border-top: 1px dotted #808080;
|
||||
margin-top: 1em;
|
||||
font-size: smaller; }
|
||||
td.foot-os { text-align: right; }
|
||||
|
||||
/* Sections and paragraphs. */
|
||||
|
||||
.manual-text {
|
||||
margin-left: 3.8em; }
|
||||
.Nd { }
|
||||
section.Sh { }
|
||||
h1.Sh { margin-top: 1.2em;
|
||||
margin-bottom: 0.6em;
|
||||
margin-left: -3.2em;
|
||||
font-size: 110%; }
|
||||
section.Ss { }
|
||||
h2.Ss { margin-top: 1.2em;
|
||||
margin-bottom: 0.6em;
|
||||
margin-left: -1.2em;
|
||||
font-size: 105%; }
|
||||
.Pp { margin: 0.6em 0em; }
|
||||
.Sx { }
|
||||
.Xr { }
|
||||
|
||||
/* Displays and lists. */
|
||||
|
||||
.Bd { }
|
||||
.Bd-indent { margin-left: 3.8em; }
|
||||
|
||||
.Bl-bullet { list-style-type: disc;
|
||||
padding-left: 1em; }
|
||||
.Bl-bullet > li { }
|
||||
.Bl-dash { list-style-type: none;
|
||||
padding-left: 0em; }
|
||||
.Bl-dash > li:before {
|
||||
content: "\2014 "; }
|
||||
.Bl-item { list-style-type: none;
|
||||
padding-left: 0em; }
|
||||
.Bl-item > li { }
|
||||
.Bl-compact > li {
|
||||
margin-top: 0em; }
|
||||
|
||||
.Bl-enum { padding-left: 2em; }
|
||||
.Bl-enum > li { }
|
||||
.Bl-compact > li {
|
||||
margin-top: 0em; }
|
||||
|
||||
.Bl-diag { }
|
||||
.Bl-diag > dt {
|
||||
font-style: normal;
|
||||
font-weight: bold; }
|
||||
.Bl-diag > dd {
|
||||
margin-left: 0em; }
|
||||
.Bl-hang { }
|
||||
.Bl-hang > dt { }
|
||||
.Bl-hang > dd {
|
||||
margin-left: 5.5em; }
|
||||
.Bl-inset { }
|
||||
.Bl-inset > dt { }
|
||||
.Bl-inset > dd {
|
||||
margin-left: 0em; }
|
||||
.Bl-ohang { }
|
||||
.Bl-ohang > dt { }
|
||||
.Bl-ohang > dd {
|
||||
margin-left: 0em; }
|
||||
.Bl-tag { margin-top: 0.6em;
|
||||
margin-left: 5.5em; }
|
||||
.Bl-tag > dt {
|
||||
float: left;
|
||||
margin-top: 0em;
|
||||
margin-left: -5.5em;
|
||||
padding-right: 0.5em;
|
||||
vertical-align: top; }
|
||||
.Bl-tag > dd {
|
||||
clear: right;
|
||||
width: 100%;
|
||||
margin-top: 0em;
|
||||
margin-left: 0em;
|
||||
margin-bottom: 0.6em;
|
||||
vertical-align: top;
|
||||
overflow: auto; }
|
||||
.Bl-compact { margin-top: 0em; }
|
||||
.Bl-compact > dd {
|
||||
margin-bottom: 0em; }
|
||||
.Bl-compact > dt {
|
||||
margin-top: 0em; }
|
||||
|
||||
.Bl-column { }
|
||||
.Bl-column > tbody > tr { }
|
||||
.Bl-column > tbody > tr > td {
|
||||
margin-top: 1em; }
|
||||
.Bl-compact > tbody > tr > td {
|
||||
margin-top: 0em; }
|
||||
|
||||
.Rs { font-style: normal;
|
||||
font-weight: normal; }
|
||||
.RsA { }
|
||||
.RsB { font-style: italic;
|
||||
font-weight: normal; }
|
||||
.RsC { }
|
||||
.RsD { }
|
||||
.RsI { font-style: italic;
|
||||
font-weight: normal; }
|
||||
.RsJ { font-style: italic;
|
||||
font-weight: normal; }
|
||||
.RsN { }
|
||||
.RsO { }
|
||||
.RsP { }
|
||||
.RsQ { }
|
||||
.RsR { }
|
||||
.RsT { text-decoration: underline; }
|
||||
.RsU { }
|
||||
.RsV { }
|
||||
|
||||
.eqn { }
|
||||
.tbl td { vertical-align: middle; }
|
||||
|
||||
.HP { margin-left: 3.8em;
|
||||
text-indent: -3.8em; }
|
||||
|
||||
/* Semantic markup for command line utilities. */
|
||||
|
||||
table.Nm { }
|
||||
code.Nm { font-style: normal;
|
||||
font-weight: bold;
|
||||
font-family: inherit; }
|
||||
.Fl { font-style: normal;
|
||||
font-weight: bold;
|
||||
font-family: inherit; }
|
||||
.Cm { font-style: normal;
|
||||
font-weight: bold;
|
||||
font-family: inherit; }
|
||||
.Ar { font-style: italic;
|
||||
font-weight: normal; }
|
||||
.Op { display: inline; }
|
||||
.Ic { font-style: normal;
|
||||
font-weight: bold;
|
||||
font-family: inherit; }
|
||||
.Ev { font-style: normal;
|
||||
font-weight: normal;
|
||||
font-family: monospace; }
|
||||
.Pa { font-style: italic;
|
||||
font-weight: normal; }
|
||||
|
||||
/* Semantic markup for function libraries. */
|
||||
|
||||
.Lb { }
|
||||
code.In { font-style: normal;
|
||||
font-weight: bold;
|
||||
font-family: inherit; }
|
||||
a.In { }
|
||||
.Fd { font-style: normal;
|
||||
font-weight: bold;
|
||||
font-family: inherit; }
|
||||
.Ft { font-style: italic;
|
||||
font-weight: normal; }
|
||||
.Fn { font-style: normal;
|
||||
font-weight: bold;
|
||||
font-family: inherit; }
|
||||
.Fa { font-style: italic;
|
||||
font-weight: normal; }
|
||||
.Vt { font-style: italic;
|
||||
font-weight: normal; }
|
||||
.Va { font-style: italic;
|
||||
font-weight: normal; }
|
||||
.Dv { font-style: normal;
|
||||
font-weight: normal;
|
||||
font-family: monospace; }
|
||||
.Er { font-style: normal;
|
||||
font-weight: normal;
|
||||
font-family: monospace; }
|
||||
|
||||
/* Various semantic markup. */
|
||||
|
||||
.An { }
|
||||
.Lk { }
|
||||
.Mt { }
|
||||
.Cd { font-style: normal;
|
||||
font-weight: bold;
|
||||
font-family: inherit; }
|
||||
.Ad { font-style: italic;
|
||||
font-weight: normal; }
|
||||
.Ms { font-style: normal;
|
||||
font-weight: bold; }
|
||||
.St { }
|
||||
.Ux { }
|
||||
|
||||
/* Physical markup. */
|
||||
|
||||
.Bf { display: inline; }
|
||||
.No { font-style: normal;
|
||||
font-weight: normal; }
|
||||
.Em { font-style: italic;
|
||||
font-weight: normal; }
|
||||
.Sy { font-style: normal;
|
||||
font-weight: bold; }
|
||||
.Li { font-style: normal;
|
||||
font-weight: normal;
|
||||
font-family: monospace; }
|
||||
|
||||
/* Tooltip support. */
|
||||
|
||||
h1.Sh, h2.Ss { position: relative; }
|
||||
.An, .Ar, .Cd, .Cm, .Dv, .Em, .Er, .Ev, .Fa, .Fd, .Fl, .Fn, .Ft,
|
||||
.Ic, code.In, .Lb, .Lk, .Ms, .Mt, .Nd, code.Nm, .Pa, .Rs,
|
||||
.St, .Sx, .Sy, .Va, .Vt, .Xr {
|
||||
display: inline-block;
|
||||
position: relative; }
|
||||
|
||||
.An::before { content: "An"; }
|
||||
.Ar::before { content: "Ar"; }
|
||||
.Cd::before { content: "Cd"; }
|
||||
.Cm::before { content: "Cm"; }
|
||||
.Dv::before { content: "Dv"; }
|
||||
.Em::before { content: "Em"; }
|
||||
.Er::before { content: "Er"; }
|
||||
.Ev::before { content: "Ev"; }
|
||||
.Fa::before { content: "Fa"; }
|
||||
.Fd::before { content: "Fd"; }
|
||||
.Fl::before { content: "Fl"; }
|
||||
.Fn::before { content: "Fn"; }
|
||||
.Ft::before { content: "Ft"; }
|
||||
.Ic::before { content: "Ic"; }
|
||||
code.In::before { content: "In"; }
|
||||
.Lb::before { content: "Lb"; }
|
||||
.Lk::before { content: "Lk"; }
|
||||
.Ms::before { content: "Ms"; }
|
||||
.Mt::before { content: "Mt"; }
|
||||
.Nd::before { content: "Nd"; }
|
||||
code.Nm::before { content: "Nm"; }
|
||||
.Pa::before { content: "Pa"; }
|
||||
.Rs::before { content: "Rs"; }
|
||||
h1.Sh::before { content: "Sh"; }
|
||||
h2.Ss::before { content: "Ss"; }
|
||||
.St::before { content: "St"; }
|
||||
.Sx::before { content: "Sx"; }
|
||||
.Sy::before { content: "Sy"; }
|
||||
.Va::before { content: "Va"; }
|
||||
.Vt::before { content: "Vt"; }
|
||||
.Xr::before { content: "Xr"; }
|
||||
|
||||
.An::before, .Ar::before, .Cd::before, .Cm::before,
|
||||
.Dv::before, .Em::before, .Er::before, .Ev::before,
|
||||
.Fa::before, .Fd::before, .Fl::before, .Fn::before, .Ft::before,
|
||||
.Ic::before, code.In::before, .Lb::before, .Lk::before,
|
||||
.Ms::before, .Mt::before, .Nd::before, code.Nm::before,
|
||||
.Pa::before, .Rs::before,
|
||||
h1.Sh::before, h2.Ss::before, .St::before, .Sx::before, .Sy::before,
|
||||
.Va::before, .Vt::before, .Xr::before {
|
||||
opacity: 0;
|
||||
transition: .15s ease opacity;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
box-shadow: 0 0 .35em #000;
|
||||
padding: .15em .25em;
|
||||
white-space: nowrap;
|
||||
font-family: Helvetica,Arial,sans-serif;
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
color: black;
|
||||
background: #fff; }
|
||||
.An:hover::before, .Ar:hover::before, .Cd:hover::before, .Cm:hover::before,
|
||||
.Dv:hover::before, .Em:hover::before, .Er:hover::before, .Ev:hover::before,
|
||||
.Fa:hover::before, .Fd:hover::before, .Fl:hover::before, .Fn:hover::before,
|
||||
.Ft:hover::before, .Ic:hover::before, code.In:hover::before,
|
||||
.Lb:hover::before, .Lk:hover::before, .Ms:hover::before, .Mt:hover::before,
|
||||
.Nd:hover::before, code.Nm:hover::before, .Pa:hover::before,
|
||||
.Rs:hover::before, h1.Sh:hover::before, h2.Ss:hover::before, .St:hover::before,
|
||||
.Sx:hover::before, .Sy:hover::before, .Va:hover::before, .Vt:hover::before,
|
||||
.Xr:hover::before {
|
||||
opacity: 1;
|
||||
pointer-events: inherit; }
|
||||
|
||||
/* Overrides to avoid excessive margins on small devices. */
|
||||
|
||||
@media (max-width: 37.5em) {
|
||||
.manual-text {
|
||||
margin-left: 0.5em; }
|
||||
h1.Sh, h2.Ss { margin-left: 0em; }
|
||||
.Bd-indent { margin-left: 2em; }
|
||||
.Bl-hang > dd {
|
||||
margin-left: 2em; }
|
||||
.Bl-tag { margin-left: 2em; }
|
||||
.Bl-tag > dt {
|
||||
margin-left: -2em; }
|
||||
.HP { margin-left: 2em;
|
||||
text-indent: -2em; }
|
||||
}
|
||||
@@ -1,295 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- This is an automatically generated file. Do not edit.
|
||||
This file is part of RGBDS.
|
||||
|
||||
Copyright (c) 2010-2019, Anthony J. Bentley and RGBDS contributors.
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
-->
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
|
||||
<link rel="stylesheet" href="rgbds.css" type="text/css" media="all"/>
|
||||
<title>RGBASM(1)</title>
|
||||
</head>
|
||||
<body>
|
||||
<table class="head">
|
||||
<tr>
|
||||
<td class="head-ltitle">RGBASM(1)</td>
|
||||
<td class="head-vol">General Commands Manual</td>
|
||||
<td class="head-rtitle">RGBASM(1)</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="manual-text">
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
|
||||
<code class="Nm">rgbasm</code> —
|
||||
<span class="Nd">Game Boy assembler</span>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="SYNOPSIS"><a class="permalink" href="#SYNOPSIS">SYNOPSIS</a></h1>
|
||||
<table class="Nm">
|
||||
<tr>
|
||||
<td><code class="Nm">rgbasm</code></td>
|
||||
<td>[<code class="Fl"><a href="#E">-E</a><a href="#h">h</a><a href="#L">L</a><a href="#V">V</a><a href="#v">v</a><a href="#w">w</a></code>] [<code class="Fl"><a href="#b">-b</a></code>
|
||||
<var class="Ar">chars</var>] [<code class="Fl"><a href="#D">-D</a></code>
|
||||
<var class="Ar">name</var>[=<var class="Ar">value</var>]]
|
||||
[<code class="Fl"><a href="#g">-g</a></code> <var class="Ar">chars</var>]
|
||||
[<code class="Fl"><a href="#i">-i</a></code> <var class="Ar">path</var>]
|
||||
[<code class="Fl"><a href="#M">-M</a></code> <var class="Ar">depend_file</var>]
|
||||
[<code class="Fl"><a href="#M">-M</a><a href="#G">G</a></code>] [<code class="Fl"><a href="#M">-M</a><a href="#P">P</a></code>]
|
||||
[<code class="Fl"><a href="#M">-M</a><a href="#T">T</a></code> <var class="Ar">target_file</var>]
|
||||
[<code class="Fl"><a href="#M">-M</a><a href="#Q">Q</a></code> <var class="Ar">target_file</var>]
|
||||
[<code class="Fl"><a href="#o">-o</a></code> <var class="Ar">out_file</var>]
|
||||
[<code class="Fl"><a href="#p">-p</a></code> <var class="Ar">pad_value</var>]
|
||||
[<code class="Fl"><a href="#r">-r</a></code> <var class="Ar">recursion_depth</var>]
|
||||
[<code class="Fl"><a href="#W">-W</a></code> <var class="Ar">warning</var>]
|
||||
<var class="Ar">file ...</var></td>
|
||||
</tr>
|
||||
</table>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
|
||||
The <code class="Nm">rgbasm</code> program creates an RGB object file from an
|
||||
assembly source file. The input <var class="Ar">file</var> can be a file path,
|
||||
or <code class="Cm">-</code> denoting <code class="Cm">stdin</code>.
|
||||
<p class="Pp">Note that options can be abbreviated as long as the abbreviation
|
||||
is unambiguous: <code class="Fl">--verb</code> is
|
||||
<code class="Fl">--verbose</code>, but
|
||||
<code class="Fl">--ver</code> is invalid because it
|
||||
could also be <code class="Fl">--version</code>. The
|
||||
arguments are as follows:</p>
|
||||
<dl class="Bl-tag">
|
||||
<dt><a class="permalink" href="#b"><code class="Fl" id="b">-b</code></a>
|
||||
<var class="Ar">chars</var>,
|
||||
<code class="Fl">--binary-digits</code>
|
||||
<var class="Ar">chars</var></dt>
|
||||
<dd>Change the two characters used for binary constants. The defaults are
|
||||
01.</dd>
|
||||
<dt><a class="permalink" href="#D"><code class="Fl" id="D">-D</code></a>
|
||||
<var class="Ar">name</var>[=<var class="Ar">value</var>],
|
||||
<code class="Fl">-</code> <code class="Fl">-define</code>
|
||||
<var class="Ar">name</var>[=<var class="Ar">value</var>]</dt>
|
||||
<dd>Add a string symbol to the compiled source code. This is equivalent to
|
||||
‘<code class="Li"><var class="Ar">name</var> <code class="Ic">EQUS
|
||||
"</code><var class="Ar">value</var>"</code>’ in code, or
|
||||
‘<code class="Li"><var class="Ar">name</var> <code class="Ic">EQUS
|
||||
"1"</code></code>’ if <var class="Ar">value</var> is not
|
||||
specified.</dd>
|
||||
<dt><a class="permalink" href="#E"><code class="Fl" id="E">-E</code></a>,
|
||||
<code class="Fl">--export-all</code></dt>
|
||||
<dd>Export all labels, including unreferenced and local labels.</dd>
|
||||
<dt><a class="permalink" href="#g"><code class="Fl" id="g">-g</code></a>
|
||||
<var class="Ar">chars</var>,
|
||||
<code class="Fl">--gfx-chars</code>
|
||||
<var class="Ar">chars</var></dt>
|
||||
<dd>Change the four characters used for gfx constants. The defaults are
|
||||
0123.</dd>
|
||||
<dt><a class="permalink" href="#h"><code class="Fl" id="h">-h</code></a>,
|
||||
<code class="Fl">--halt-without-nop</code></dt>
|
||||
<dd>By default, <code class="Nm">rgbasm</code> inserts a
|
||||
<code class="Ic">nop</code> instruction immediately after any
|
||||
<code class="Ic">halt</code> instruction. The <code class="Fl">-h</code>
|
||||
option disables this behavior.</dd>
|
||||
<dt><a class="permalink" href="#i"><code class="Fl" id="i">-i</code></a>
|
||||
<var class="Ar">path</var>,
|
||||
<code class="Fl">--include</code>
|
||||
<var class="Ar">path</var></dt>
|
||||
<dd>Add an include path.</dd>
|
||||
<dt><a class="permalink" href="#L"><code class="Fl" id="L">-L</code></a>,
|
||||
<code class="Fl">--preserve-ld</code></dt>
|
||||
<dd>Disable the optimization that turns loads of the form <code class="Ic">LD
|
||||
[$FF00+n8],A</code> into the opcode <code class="Ic">LDH
|
||||
[$FF00+n8],A</code> in order to have full control of the result in the
|
||||
final ROM.</dd>
|
||||
<dt><a class="permalink" href="#M"><code class="Fl" id="M">-M</code></a>
|
||||
<var class="Ar">depend_file</var>,
|
||||
<code class="Fl">--dependfile</code>
|
||||
<var class="Ar">depend_file</var></dt>
|
||||
<dd>Print <a class="Xr">make(1)</a> dependencies to
|
||||
<var class="Ar">depend_file</var>.</dd>
|
||||
<dt><a class="permalink" href="#MG"><code class="Fl" id="MG">-MG</code></a></dt>
|
||||
<dd>To be used in conjunction with <code class="Fl">-M</code>. This makes
|
||||
<code class="Nm">rgbasm</code> assume that missing files are
|
||||
auto-generated: when <code class="Ic">INCLUDE</code> or
|
||||
<code class="Ic">INCBIN</code> is attempted on a non-existent file, it is
|
||||
added as a dependency, then <code class="Nm">rgbasm</code> exits normally
|
||||
instead of erroring out. This feature is used in automatic updating of
|
||||
makefiles.</dd>
|
||||
<dt><a class="permalink" href="#MP"><code class="Fl" id="MP">-MP</code></a></dt>
|
||||
<dd>When enabled, this causes a phony target to be added for each dependency
|
||||
other than the main file. This prevents <a class="Xr">make(1)</a> from
|
||||
erroring out when dependency files are deleted.</dd>
|
||||
<dt><a class="permalink" href="#MT"><code class="Fl" id="MT">-MT</code></a>
|
||||
<var class="Ar">target_file</var></dt>
|
||||
<dd>Add a target to the rules emitted by <code class="Fl">-M</code>. The exact
|
||||
string provided will be written, including spaces and special characters.
|
||||
<div class="Bd Bd-indent"><code class="Li"><code class="Fl">-MT</code>
|
||||
<code class="Fl">-fileA</code> <code class="Fl">-MT</code>
|
||||
<code class="Fl">-fileB</code></code></div>
|
||||
is equivalent to
|
||||
<div class="Bd Bd-indent"><code class="Li"><code class="Fl">-MT</code>
|
||||
<code class="Fl">-'fileA</code>
|
||||
<code class="Fl">-fileB'</code>.</code></div>
|
||||
If neither this nor <code class="Fl">-MQ</code> is specified, the output
|
||||
file name is used.</dd>
|
||||
<dt><a class="permalink" href="#MQ"><code class="Fl" id="MQ">-MQ</code></a>
|
||||
<var class="Ar">target_file</var></dt>
|
||||
<dd>Same as <code class="Fl">-MT</code>, but additionally escapes any special
|
||||
<a class="Xr">make(1)</a> characters, essentially ‘$’.</dd>
|
||||
<dt><a class="permalink" href="#o"><code class="Fl" id="o">-o</code></a>
|
||||
<var class="Ar">out_file</var>,
|
||||
<code class="Fl">--output</code>
|
||||
<var class="Ar">out_file</var></dt>
|
||||
<dd>Write an object file to the given filename.</dd>
|
||||
<dt><a class="permalink" href="#p"><code class="Fl" id="p">-p</code></a>
|
||||
<var class="Ar">pad_value</var>,
|
||||
<code class="Fl">--pad-value</code>
|
||||
<var class="Ar">pad_value</var></dt>
|
||||
<dd>When padding an image, pad with this value. The default is 0x00.</dd>
|
||||
<dt><a class="permalink" href="#r"><code class="Fl" id="r">-r</code></a>
|
||||
<var class="Ar">recursion_depth</var>,
|
||||
<code class="Fl">--recursion-depth</code>
|
||||
<var class="Ar">recursion_depth</var></dt>
|
||||
<dd>Specifies the recursion depth at which RGBASM will assume being in an
|
||||
infinite loop.</dd>
|
||||
<dt><a class="permalink" href="#V"><code class="Fl" id="V">-V</code></a>,
|
||||
<code class="Fl">--version</code></dt>
|
||||
<dd>Print the version of the program and exit.</dd>
|
||||
<dt><a class="permalink" href="#v"><code class="Fl" id="v">-v</code></a>,
|
||||
<code class="Fl">--verbose</code></dt>
|
||||
<dd>Be verbose.</dd>
|
||||
<dt><a class="permalink" href="#W"><code class="Fl" id="W">-W</code></a>
|
||||
<var class="Ar">warning</var>,
|
||||
<code class="Fl">--warning</code>
|
||||
<var class="Ar">warning</var></dt>
|
||||
<dd>Set warning flag <var class="Ar">warning</var>. A warning message will be
|
||||
printed if <var class="Ar">warning</var> is an unknown warning flag. See
|
||||
the <a class="Sx" href="#DIAGNOSTICS">DIAGNOSTICS</a> section for a list
|
||||
of warnings.</dd>
|
||||
<dt><a class="permalink" href="#w"><code class="Fl" id="w">-w</code></a></dt>
|
||||
<dd>Disable all warning output, even when turned into errors.</dd>
|
||||
</dl>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="DIAGNOSTICS"><a class="permalink" href="#DIAGNOSTICS">DIAGNOSTICS</a></h1>
|
||||
Warnings are diagnostic messages that indicate possibly erroneous behavior that
|
||||
does not necessarily compromise the assembling process. The following options
|
||||
alter the way warnings are processed.
|
||||
<dl class="Bl-tag">
|
||||
<dt><a class="permalink" href="#Werror"><code class="Fl" id="Werror">-Werror</code></a></dt>
|
||||
<dd>Make all warnings into errors.</dd>
|
||||
<dt><a class="permalink" href="#Werror="><code class="Fl" id="Werror=">-Werror=</code></a></dt>
|
||||
<dd>Make the specified warning into an error. A warning's name is appended
|
||||
(example: <code class="Fl">-Werror=obsolete</code>), and this warning is
|
||||
implicitly enabled and turned into an error. This is an error if used with
|
||||
a meta warning, such as <code class="Fl">-Werror=all</code>.</dd>
|
||||
</dl>
|
||||
<p class="Pp">The following warnings are “meta” warnings, that
|
||||
enable a collection of other warnings. If a specific warning is toggled via
|
||||
a meta flag and a specific one, the more specific one takes priority. The
|
||||
position on the command-line acts as a tie breaker, the last one taking
|
||||
effect.</p>
|
||||
<dl class="Bl-tag">
|
||||
<dt><a class="permalink" href="#Wall"><code class="Fl" id="Wall">-Wall</code></a></dt>
|
||||
<dd>This enables warnings that are likely to indicate an error or undesired
|
||||
behavior, and that can easily be fixed.</dd>
|
||||
<dt><a class="permalink" href="#Wextra"><code class="Fl" id="Wextra">-Wextra</code></a></dt>
|
||||
<dd>This enables extra warnings that are less likely to pose a problem, but
|
||||
that may still be wanted.</dd>
|
||||
<dt><a class="permalink" href="#Weverything"><code class="Fl" id="Weverything">-Weverything</code></a></dt>
|
||||
<dd>Enables literally every warning.</dd>
|
||||
</dl>
|
||||
<p class="Pp">The following warnings are actual warning flags; with each
|
||||
description, the corresponding warning flag is included. Note that each of
|
||||
these flag also has a negation (for example,
|
||||
<code class="Fl">-Wempty-entry</code> enables the warning that
|
||||
<code class="Fl">-Wno-empty-entry</code> disables). Only the non-default
|
||||
flag is listed here. Ignoring the “no-” prefix, entries are
|
||||
listed alphabetically.</p>
|
||||
<dl class="Bl-tag">
|
||||
<dt><a class="permalink" href="#Wno-assert"><code class="Fl" id="Wno-assert">-Wno-assert</code></a></dt>
|
||||
<dd>Warns when <code class="Ic">WARN</code><span class="No">-type</span>
|
||||
assertions fail. (See “Aborting the assembly process” in
|
||||
<a class="Xr" href="rgbasm.5.html">rgbasm(5)</a> for <code class="Ic">ASSERT</code>).</dd>
|
||||
<dt><a class="permalink" href="#Wbuiltin-args"><code class="Fl" id="Wbuiltin-args">-Wbuiltin-args</code></a></dt>
|
||||
<dd>Warn about incorrect arguments to built-in functions, such as
|
||||
<code class="Fn">STRSUB</code>() with indexes outside of the string's
|
||||
bounds. This warning is enabled by <code class="Fl">-Wall</code>.</dd>
|
||||
<dt><a class="permalink" href="#Wdiv"><code class="Fl" id="Wdiv">-Wdiv</code></a></dt>
|
||||
<dd>Warn when dividing the smallest negative integer by -1, which yields
|
||||
itself due to integer overflow.</dd>
|
||||
<dt><a class="permalink" href="#Wempty-entry"><code class="Fl" id="Wempty-entry">-Wempty-entry</code></a></dt>
|
||||
<dd>Warn when an empty entry is encountered in a <code class="Ic">db</code>,
|
||||
<code class="Ic">dw</code>, <code class="Ic">dl</code> list. This warning
|
||||
is enabled by <code class="Fl">-Wextra</code>.</dd>
|
||||
<dt><a class="permalink" href="#Wlarge-constant"><code class="Fl" id="Wlarge-constant">-Wlarge-constant</code></a></dt>
|
||||
<dd>Warn when a constant too large to fit in a signed 32-bit integer is
|
||||
encountered. This warning is enabled by
|
||||
<code class="Fl">-Wall</code>.</dd>
|
||||
<dt><a class="permalink" href="#Wlong-string"><code class="Fl" id="Wlong-string">-Wlong-string</code></a></dt>
|
||||
<dd>Warn when a string too long to fit in internal buffers is encountered.
|
||||
This warning is enabled by <code class="Fl">-Wall</code>.</dd>
|
||||
<dt><a class="permalink" href="#Wno-obsolete"><code class="Fl" id="Wno-obsolete">-Wno-obsolete</code></a></dt>
|
||||
<dd>Warn when obsolete constructs such as the <code class="Ic">jp [hl]</code>
|
||||
instruction or <code class="Ic">HOME</code> section type are
|
||||
encountered.</dd>
|
||||
<dt><a class="permalink" href="#Wshift"><code class="Fl" id="Wshift">-Wshift</code></a></dt>
|
||||
<dd>Warn when shifting right a negative value. Use a division by 2^N
|
||||
instead.</dd>
|
||||
<dt><a class="permalink" href="#Wshift-amount"><code class="Fl" id="Wshift-amount">-Wshift-amount</code></a></dt>
|
||||
<dd>Warn when a shift's operand is negative or greater than 32.</dd>
|
||||
<dt><a class="permalink" href="#Wno-truncation"><code class="Fl" id="Wno-truncation">-Wno-truncation</code></a></dt>
|
||||
<dd>Warn when an implicit truncation (for example, <code class="Ic">db</code>)
|
||||
loses some bits.</dd>
|
||||
<dt><a class="permalink" href="#Wno-user"><code class="Fl" id="Wno-user">-Wno-user</code></a></dt>
|
||||
<dd>Warn when the <code class="Ic">WARN</code> built-in is executed. (See
|
||||
“Aborting the assembly process” in
|
||||
<a class="Xr" href="rgbasm.5.html">rgbasm(5)</a> for <code class="Ic">WARN</code>).</dd>
|
||||
</dl>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="EXAMPLES"><a class="permalink" href="#EXAMPLES">EXAMPLES</a></h1>
|
||||
You can assemble a source file in two ways.
|
||||
<p class="Pp">Straightforward way:</p>
|
||||
<div class="Bd Bd-indent"><code class="Li">$ rgbasm -o bar.o
|
||||
foo.asm</code></div>
|
||||
<p class="Pp">Pipes way:</p>
|
||||
<div class="Bd Bd-indent"><code class="Li">$ cat foo.asm | rgbasm -o bar.o
|
||||
-</code></div>
|
||||
<div class="Bd Bd-indent"><code class="Li">$ rgbasm -o bar.o - <
|
||||
foo.asm</code></div>
|
||||
<p class="Pp">The resulting object file is not yet a usable ROM image—it
|
||||
must first be run through <a class="Xr" href="rgblink.1.html">rgblink(1)</a> and then
|
||||
<a class="Xr" href="rgbfix.1.html">rgbfix(1)</a>.</p>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="BUGS"><a class="permalink" href="#BUGS">BUGS</a></h1>
|
||||
Please report bugs on
|
||||
<a class="Lk" href="https://github.com/rednex/rgbds/issues">GitHub</a>.
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
|
||||
ALSO</a></h1>
|
||||
<a class="Xr" href="rgbasm.5.html">rgbasm(5)</a>, <a class="Xr" href="rgbfix.1.html">rgbfix(1)</a>,
|
||||
<a class="Xr" href="rgblink.1.html">rgblink(1)</a>, <a class="Xr" href="rgbds.5.html">rgbds(5)</a>,
|
||||
<a class="Xr" href="rgbds.7.html">rgbds(7)</a>, <a class="Xr" href="gbz80.7.html">gbz80(7)</a>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
|
||||
<code class="Nm">rgbasm</code> was originally written by Carsten Sørensen
|
||||
as part of the ASMotor package, and was later packaged in RGBDS by Justin
|
||||
Lloyd. It is now maintained by a number of contributors at
|
||||
<a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.
|
||||
</section>
|
||||
</div>
|
||||
<table class="foot">
|
||||
<tr>
|
||||
<td class="foot-date">July 8, 2019</td>
|
||||
<td class="foot-os">General</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
1751
docs/rgbasm.5.html
1751
docs/rgbasm.5.html
File diff suppressed because it is too large
Load Diff
@@ -1,363 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- This is an automatically generated file. Do not edit.
|
||||
This file is part of RGBDS.
|
||||
|
||||
Copyright (c) 2017-2018, Antonio Nino Diaz and RGBDS contributors.
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
-->
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
|
||||
<link rel="stylesheet" href="rgbds.css" type="text/css" media="all"/>
|
||||
<title>RGBDS(5)</title>
|
||||
</head>
|
||||
<body>
|
||||
<table class="head">
|
||||
<tr>
|
||||
<td class="head-ltitle">RGBDS(5)</td>
|
||||
<td class="head-vol">File Formats Manual</td>
|
||||
<td class="head-rtitle">RGBDS(5)</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="manual-text">
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
|
||||
<code class="Nm">rgbds</code> —
|
||||
<span class="Nd">object file format documentation</span>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
|
||||
This is the description of the object files used by <a class="Xr" href="rgbasm.1.html">rgbasm(1)</a>
|
||||
and <a class="Xr" href="rgblink.1.html">rgblink(1)</a>. <i class="Em">Please note that the
|
||||
specifications may change.</i> This toolchain is in development and new
|
||||
features may require adding more information to the current format, or
|
||||
modifying some fields, which would break compatibility with older versions.
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="FILE_STRUCTURE"><a class="permalink" href="#FILE_STRUCTURE">FILE
|
||||
STRUCTURE</a></h1>
|
||||
The following types are used:
|
||||
<p class="Pp"><var class="Ar">LONG</var> is a 32‐bit integer stored in
|
||||
little‐endian format. <var class="Ar">BYTE</var> is an 8‐bit
|
||||
integer. <var class="Ar">STRING</var> is a 0‐terminated string of
|
||||
<var class="Ar">BYTE</var>.</p>
|
||||
<div class="Bd Pp">
|
||||
<pre>
|
||||
; Header
|
||||
|
||||
BYTE ID[4] ; "RGB9"
|
||||
LONG RevisionNumber ; The format's revision number this file uses
|
||||
LONG NumberOfSymbols ; The number of symbols used in this file
|
||||
LONG NumberOfSections ; The number of sections used in this file
|
||||
|
||||
; Symbols
|
||||
|
||||
REPT NumberOfSymbols ; Number of symbols defined in this object file.
|
||||
|
||||
STRING Name ; The name of this symbol. Local symbols are stored
|
||||
; as "Scope.Symbol".
|
||||
|
||||
BYTE Type ; 0 = LOCAL symbol only used in this file.
|
||||
; 1 = IMPORT this symbol from elsewhere
|
||||
; 2 = EXPORT this symbol to other objects.
|
||||
; Bit 7 is independent from the above value, and
|
||||
; encodes whether the section is unionized
|
||||
|
||||
IF (Type & 0x7F) != 1 ; If symbol is defined in this object file.
|
||||
|
||||
STRING FileName ; File where the symbol is defined.
|
||||
|
||||
LONG LineNum ; Line number in the file where the symbol is defined.
|
||||
|
||||
LONG SectionID ; The section number (of this object file) in which
|
||||
; this symbol is defined. If it doesn't belong to any
|
||||
; specific section (like a constant), this field has
|
||||
; the value -1.
|
||||
|
||||
LONG Value ; The symbols value. It's the offset into that
|
||||
; symbol's section.
|
||||
|
||||
ENDC
|
||||
|
||||
ENDR
|
||||
|
||||
; Sections
|
||||
|
||||
REPT NumberOfSections
|
||||
STRING Name ; Name of the section
|
||||
|
||||
LONG Size ; Size in bytes of this section
|
||||
|
||||
BYTE Type ; 0 = WRAM0
|
||||
; 1 = VRAM
|
||||
; 2 = ROMX
|
||||
; 3 = ROM0
|
||||
; 4 = HRAM
|
||||
; 5 = WRAMX
|
||||
; 6 = SRAM
|
||||
; 7 = OAM
|
||||
|
||||
LONG Org ; Address to fix this section at. -1 if the linker should
|
||||
; decide (floating address).
|
||||
|
||||
LONG Bank ; Bank to load this section into. -1 if the linker should
|
||||
; decide (floating bank). This field is only valid for ROMX,
|
||||
; VRAM, WRAMX and SRAM sections.
|
||||
|
||||
BYTE Align ; Alignment of this section, as N bits. 0 when not specified.
|
||||
|
||||
LONG Ofs ; Offset relative to the alignment specified above.
|
||||
; Must be below 1 << Align.
|
||||
|
||||
IF (Type == ROMX) || (Type == ROM0) ; Sections that can contain data.
|
||||
|
||||
BYTE Data[Size] ; Raw data of the section.
|
||||
|
||||
LONG NumberOfPatches ; Number of patches to apply.
|
||||
|
||||
REPT NumberOfPatches
|
||||
|
||||
STRING SourceFile ; Name of the source file (for printing error
|
||||
; messages).
|
||||
|
||||
LONG Offset ; Offset into the section where patch should
|
||||
; be applied (in bytes).
|
||||
|
||||
LONG PCSectionID ; Index within the file of the section in which
|
||||
; PC is located.
|
||||
; This is usually the same section that the
|
||||
; patch should be applied into, except e.g.
|
||||
; with LOAD blocks.
|
||||
|
||||
LONG PCOffset ; PC's offset into the above section.
|
||||
; Used because the section may be floating, so
|
||||
; PC's value is not known to RGBASM.
|
||||
|
||||
BYTE Type ; 0 = BYTE patch.
|
||||
; 1 = little endian WORD patch.
|
||||
; 2 = little endian LONG patch.
|
||||
; 3 = JR offset value BYTE patch.
|
||||
|
||||
LONG RPNSize ; Size of the buffer with the RPN.
|
||||
; expression.
|
||||
|
||||
BYTE RPN[RPNSize] ; RPN expression. Definition below.
|
||||
|
||||
ENDR
|
||||
|
||||
ENDC
|
||||
|
||||
ENDR
|
||||
|
||||
; Assertions
|
||||
|
||||
LONG NumberOfAssertions
|
||||
|
||||
REPT NumberOfAssertions
|
||||
|
||||
STRING SourceFile ; Name of the source file (for printing the failure).
|
||||
|
||||
LONG Offset ; Offset into the section where the assertion is located.
|
||||
|
||||
LONG SectionID ; Index within the file of the section in which PC is
|
||||
; located, or -1 if defined outside a section.
|
||||
|
||||
LONG PCOffset ; PC's offset into the above section.
|
||||
; Used because the section may be floating, so PC's value
|
||||
; is not known to RGBASM.
|
||||
|
||||
BYTE Type ; 0 = Prints the message but allows linking to continue
|
||||
; 1 = Prints the message and evaluates other assertions,
|
||||
; but linking fails afterwards
|
||||
; 2 = Prints the message and immediately fails linking
|
||||
|
||||
LONG RPNSize ; Size of the RPN expression's buffer.
|
||||
|
||||
BYTE RPN[RPNSize] ; RPN expression, same as patches. Assert fails if == 0.
|
||||
|
||||
STRING Message ; A message displayed when the assert fails. If set to
|
||||
; the empty string, a generic message is printed instead.
|
||||
|
||||
ENDR
|
||||
</pre>
|
||||
</div>
|
||||
<section class="Ss">
|
||||
<h2 class="Ss" id="RPN_DATA"><a class="permalink" href="#RPN_DATA">RPN
|
||||
DATA</a></h2>
|
||||
Expressions in the object file are stored as RPN. This is an expression of the
|
||||
form “2 5 +”. This will first push the value “2”
|
||||
to the stack, then “5”. The “+” operator pops two
|
||||
arguments from the stack, adds them, and then pushes the result on the stack,
|
||||
effectively replacing the two top arguments with their sum. In the RGB format,
|
||||
RPN expressions are stored as <var class="Ar">BYTE</var>s with some bytes
|
||||
being special prefixes for integers and symbols.
|
||||
<table class="Bl-column Bd-indent">
|
||||
<tr>
|
||||
<th>Value</th>
|
||||
<th>Meaning</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$00"><code class="Li" id="$00">$00</code></a></td>
|
||||
<td><a class="permalink" href="#+_operator"><code class="Li" id="+_operator">+
|
||||
operator</code></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$01"><code class="Li" id="$01">$01</code></a></td>
|
||||
<td><a class="permalink" href="#-_operator"><code class="Li" id="-_operator">-
|
||||
operator</code></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$02"><code class="Li" id="$02">$02</code></a></td>
|
||||
<td><a class="permalink" href="#*_operator"><code class="Li" id="*_operator">*
|
||||
operator</code></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$03"><code class="Li" id="$03">$03</code></a></td>
|
||||
<td><a class="permalink" href="#/_operator"><code class="Li" id="/_operator">/
|
||||
operator</code></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$04"><code class="Li" id="$04">$04</code></a></td>
|
||||
<td><a class="permalink" href="#__operator"><code class="Li" id="__operator">%
|
||||
operator</code></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$05"><code class="Li" id="$05">$05</code></a></td>
|
||||
<td><a class="permalink" href="#unary_-"><code class="Li" id="unary_-">unary
|
||||
-</code></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$10"><code class="Li" id="$10">$10</code></a></td>
|
||||
<td>|
|
||||
<a class="permalink" href="#operator"><code class="Li" id="operator">operator</code></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$11"><code class="Li" id="$11">$11</code></a></td>
|
||||
<td><a class="permalink" href="#&_operator"><code class="Li" id="&_operator">&
|
||||
operator</code></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$12"><code class="Li" id="$12">$12</code></a></td>
|
||||
<td><a class="permalink" href="#__operator_2"><code class="Li" id="__operator_2">^
|
||||
operator</code></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$13"><code class="Li" id="$13">$13</code></a></td>
|
||||
<td><a class="permalink" href="#unary_~"><code class="Li" id="unary_~">unary
|
||||
~</code></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$21"><code class="Li" id="$21">$21</code></a></td>
|
||||
<td><a class="permalink" href="#&&_comparison"><code class="Li" id="&&_comparison">&&
|
||||
comparison</code></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$22"><code class="Li" id="$22">$22</code></a></td>
|
||||
<td><a class="permalink" href="#___comparison"><code class="Li" id="___comparison">||
|
||||
comparison</code></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$23"><code class="Li" id="$23">$23</code></a></td>
|
||||
<td><a class="permalink" href="#unary__!"><code class="Li" id="unary__!">unary !</code></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$30"><code class="Li" id="$30">$30</code></a></td>
|
||||
<td><a class="permalink" href="#==_comparison"><code class="Li" id="==_comparison">==
|
||||
comparison</code></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$31"><code class="Li" id="$31">$31</code></a></td>
|
||||
<td><a class="permalink" href="#!=_comparison"><code class="Li" id="!=_comparison">!=
|
||||
comparison</code></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$32"><code class="Li" id="$32">$32</code></a></td>
|
||||
<td><a class="permalink" href="#__comparison"><code class="Li" id="__comparison">>
|
||||
comparison</code></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$33"><code class="Li" id="$33">$33</code></a></td>
|
||||
<td><a class="permalink" href="#__comparison_2"><code class="Li" id="__comparison_2"><
|
||||
comparison</code></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$34"><code class="Li" id="$34">$34</code></a></td>
|
||||
<td><a class="permalink" href="#_=_comparison"><code class="Li" id="_=_comparison">>=
|
||||
comparison</code></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$35"><code class="Li" id="$35">$35</code></a></td>
|
||||
<td><a class="permalink" href="#_=_comparison_2"><code class="Li" id="_=_comparison_2"><=
|
||||
comparison</code></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$40"><code class="Li" id="$40">$40</code></a></td>
|
||||
<td><a class="permalink" href="#___operator"><code class="Li" id="___operator"><<
|
||||
operator</code></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$41"><code class="Li" id="$41">$41</code></a></td>
|
||||
<td><a class="permalink" href="#___operator_2"><code class="Li" id="___operator_2">>>
|
||||
operator</code></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$50"><code class="Li" id="$50">$50</code></a></td>
|
||||
<td><a class="permalink" href="#BANK(symbol)"><code class="Li" id="BANK(symbol)">BANK(symbol)</code></a>,
|
||||
a <var class="Ar">LONG</var> Symbol ID follows.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$51"><code class="Li" id="$51">$51</code></a></td>
|
||||
<td><a class="permalink" href="#BANK(section_name)"><code class="Li" id="BANK(section_name)">BANK(section_name)</code></a>,
|
||||
a null-terminated string follows.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$52"><code class="Li" id="$52">$52</code></a></td>
|
||||
<td><a class="permalink" href="#Current_BANK()"><code class="Li" id="Current_BANK()">Current
|
||||
BANK()</code></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$60"><code class="Li" id="$60">$60</code></a></td>
|
||||
<td><a class="permalink" href="#HRAMCheck"><code class="Li" id="HRAMCheck">HRAMCheck</code></a>.
|
||||
Checks if the value is in HRAM, ANDs it with 0xFF.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$61"><code class="Li" id="$61">$61</code></a></td>
|
||||
<td><a class="permalink" href="#RSTCheck"><code class="Li" id="RSTCheck">RSTCheck</code></a>.
|
||||
Checks if the value is a RST vector, ORs it with 0xC7.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$80"><code class="Li" id="$80">$80</code></a></td>
|
||||
<td><var class="Ar">LONG</var> integer follows.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$81"><code class="Li" id="$81">$81</code></a></td>
|
||||
<td><var class="Ar">LONG</var> symbol ID follows.</td>
|
||||
</tr>
|
||||
</table>
|
||||
</section>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
|
||||
ALSO</a></h1>
|
||||
<a class="Xr" href="rgbasm.1.html">rgbasm(1)</a>, <a class="Xr" href="rgblink.1.html">rgblink(1)</a>,
|
||||
<a class="Xr" href="rgbds.7.html">rgbds(7)</a>, <a class="Xr" href="gbz80.7.html">gbz80(7)</a>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
|
||||
<code class="Nm">rgbds</code> was originally written by Carsten Sørensen
|
||||
as part of the ASMotor package, and was later packaged in RGBDS by Justin
|
||||
Lloyd. It is now maintained by a number of contributors at
|
||||
<a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.
|
||||
</section>
|
||||
</div>
|
||||
<table class="foot">
|
||||
<tr>
|
||||
<td class="foot-date">January 26, 2018</td>
|
||||
<td class="foot-os">General</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,80 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- This is an automatically generated file. Do not edit.
|
||||
This file is part of RGBDS.
|
||||
|
||||
Copyright (c) 2010-2018, Anthony J. Bentley and RGBDS contributors.
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
-->
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
|
||||
<link rel="stylesheet" href="rgbds.css" type="text/css" media="all"/>
|
||||
<title>RGBDS(7)</title>
|
||||
</head>
|
||||
<body>
|
||||
<table class="head">
|
||||
<tr>
|
||||
<td class="head-ltitle">RGBDS(7)</td>
|
||||
<td class="head-vol">Miscellaneous Information Manual</td>
|
||||
<td class="head-rtitle">RGBDS(7)</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="manual-text">
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
|
||||
<code class="Nm">rgbds</code> —
|
||||
<span class="Nd">Rednex Game Boy Development System</span>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="EXAMPLES"><a class="permalink" href="#EXAMPLES">EXAMPLES</a></h1>
|
||||
To get a working ROM image from a single assembly source file:
|
||||
<div class="Bd Pp Bd-indent">
|
||||
<pre>
|
||||
$ rgbasm -o bar.o foo.asm
|
||||
$ rgblink -o baz.gb bar.o
|
||||
$ rgbfix -v -p 0 baz.gb
|
||||
</pre>
|
||||
</div>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
|
||||
ALSO</a></h1>
|
||||
<a class="Xr" href="rgbasm.1.html">rgbasm(1)</a>, <a class="Xr" href="rgbfix.1.html">rgbfix(1)</a>,
|
||||
<a class="Xr" href="rgblink.1.html">rgblink(1)</a>, <a class="Xr" href="rgbds.5.html">rgbds(5)</a>,
|
||||
<a class="Xr" href="gbz80.7.html">gbz80(7)</a>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
|
||||
<dl class="Bl-ohang">
|
||||
<dt></dt>
|
||||
<dd>1997, Carsten Sørensen (AKA SurfSmurf) writes ASMotor as a
|
||||
general-purpose assembler/linker system for DOS/Win32.</dd>
|
||||
<dt></dt>
|
||||
<dd>1999, Justin Lloyd (AKA Otaku no Zoku) adapts ASMotor to read and produce
|
||||
GBZ80 assembly/machine code, and releases this version as RGBDS.</dd>
|
||||
<dt></dt>
|
||||
<dd>2009, Vegard Nossum adapts the code to be more UNIX-like and releases this
|
||||
version as rgbds-linux on GitHub.</dd>
|
||||
<dt></dt>
|
||||
<dd>2010, Anthony J. Bentley forks that repository. The fork becomes the
|
||||
reference implementation of rgbds.</dd>
|
||||
<dt></dt>
|
||||
<dd>2017, Bentley's repository is moved to a neutral name. It is now
|
||||
maintained by a number of contributors at
|
||||
<a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.</dd>
|
||||
<dt></dt>
|
||||
<dd>2018, codebase relicensed under the MIT license.</dd>
|
||||
</dl>
|
||||
</section>
|
||||
</div>
|
||||
<table class="foot">
|
||||
<tr>
|
||||
<td class="foot-date">March 7, 2018</td>
|
||||
<td class="foot-os">General</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,63 +0,0 @@
|
||||
/* Overrides to default mandoc styling for HTML renders of RGBDS man pages */
|
||||
|
||||
html {
|
||||
/* Reduce contrast */
|
||||
background-color: #f8f8f8;
|
||||
color: #222;
|
||||
|
||||
/* Override `mandoc.css`'s sowe can put it on <body> instead */
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
body {
|
||||
/* Center body horizontally (requires <html> to span full width) */
|
||||
margin: 0 auto;
|
||||
/* `mandoc.css`'s default, but it's applied to <html> there */
|
||||
max-width: 65em;
|
||||
|
||||
/* Improve readability */
|
||||
font-size: 16px;
|
||||
line-height: 1.4;
|
||||
text-align: justify;
|
||||
|
||||
/* Prevent text from bumping sides on mobile devices */
|
||||
padding: 10px 20px 10px 10px;
|
||||
}
|
||||
@media print {
|
||||
body {
|
||||
/* Max width doesn't make sense for print */
|
||||
max-width: none;
|
||||
/* Make font slightly smaller for printing */
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
code, pre {
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
pre {
|
||||
/* Avoid horizontal page scrolling on mobile */
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Separate lines in tables */
|
||||
table.Bl-column {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.Bl-column tr:not(:first-child) > td,
|
||||
table.Bl-column tr:not(:first-child) > th {
|
||||
border-top: 1px solid #aaa;
|
||||
}
|
||||
|
||||
table.Bl-column th {
|
||||
/* Apply `.Sy` style to table headers */
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table.Bl-column td,
|
||||
table.Bl-column th {
|
||||
/* Add horizontal spacing between columns */
|
||||
padding: 2px 7px 0;
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- This is an automatically generated file. Do not edit.
|
||||
This file is part of RGBDS.
|
||||
|
||||
Copyright (c) 2010-2017, Anthony J. Bentley and RGBDS contributors.
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
-->
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
|
||||
<link rel="stylesheet" href="rgbds.css" type="text/css" media="all"/>
|
||||
<title>RGBFIX(1)</title>
|
||||
</head>
|
||||
<body>
|
||||
<table class="head">
|
||||
<tr>
|
||||
<td class="head-ltitle">RGBFIX(1)</td>
|
||||
<td class="head-vol">General Commands Manual</td>
|
||||
<td class="head-rtitle">RGBFIX(1)</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="manual-text">
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
|
||||
<code class="Nm">rgbfix</code> —
|
||||
<span class="Nd">Game Boy header utility and checksum fixer</span>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="SYNOPSIS"><a class="permalink" href="#SYNOPSIS">SYNOPSIS</a></h1>
|
||||
<table class="Nm">
|
||||
<tr>
|
||||
<td><code class="Nm">rgbfix</code></td>
|
||||
<td>[<code class="Fl"><a href="#j">-j</a><a href="#s">s</a><a href="#V">V</a><a href="#v">v</a></code>] [<code class="Fl"><a href="#C">-C</a></code> |
|
||||
<code class="Fl"><a href="#c">-c</a></code>] [<code class="Fl"><a href="#f">-f</a></code>
|
||||
<var class="Ar">fix_spec</var>] [<code class="Fl"><a href="#i">-i</a></code>
|
||||
<var class="Ar">game_id</var>] [<code class="Fl"><a href="#k">-k</a></code>
|
||||
<var class="Ar">licensee_str</var>] [<code class="Fl"><a href="#l">-l</a></code>
|
||||
<var class="Ar">licensee_id</var>] [<code class="Fl"><a href="#m">-m</a></code>
|
||||
<var class="Ar">mbc_type</var>] [<code class="Fl"><a href="#n">-n</a></code>
|
||||
<var class="Ar">rom_version</var>] [<code class="Fl"><a href="#p">-p</a></code>
|
||||
<var class="Ar">pad_value</var>] [<code class="Fl"><a href="#r">-r</a></code>
|
||||
<var class="Ar">ram_size</var>] [<code class="Fl"><a href="#t">-t</a></code>
|
||||
<var class="Ar">title_str</var>] <var class="Ar">file</var></td>
|
||||
</tr>
|
||||
</table>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
|
||||
The <code class="Nm">rgbfix</code> program changes headers of Game Boy ROM
|
||||
images. It also performs other correctness operations, such as padding.
|
||||
<p class="Pp">Note that options can be abbreviated as long as the abbreviation
|
||||
is unambiguous: <code class="Fl">--verb</code> is
|
||||
<code class="Fl">--verbose</code>, but
|
||||
<code class="Fl">--ver</code> is invalid because it
|
||||
could also be <code class="Fl">--version</code>. The
|
||||
arguments are as follows:</p>
|
||||
<dl class="Bl-tag">
|
||||
<dt><a class="permalink" href="#C"><code class="Fl" id="C">-C</code></a>,
|
||||
<code class="Fl">--color-only</code></dt>
|
||||
<dd>Set the Game Boy Color–only flag: <span class="Ad">0x143</span> =
|
||||
0xC0. If both this and the <code class="Fl">-c</code> flag are set, this
|
||||
takes precedence.</dd>
|
||||
<dt><a class="permalink" href="#c"><code class="Fl" id="c">-c</code></a>,
|
||||
<code class="Fl">--color-compatible</code></dt>
|
||||
<dd>Set the Game Boy Color–compatible flag:
|
||||
<span class="Ad">0x143</span> = 0x80. If both this and the
|
||||
<code class="Fl">-C</code> flag are set, <code class="Fl">-C</code> takes
|
||||
precedence.</dd>
|
||||
<dt><a class="permalink" href="#f"><code class="Fl" id="f">-f</code></a>
|
||||
<var class="Ar">fix_spec</var>,
|
||||
<code class="Fl">--fix-spec</code>
|
||||
<var class="Ar">fix_spec</var></dt>
|
||||
<dd>Fix certain header values that the Game Boy checks for correctness.
|
||||
Alternatively, intentionally trash these values by writing their binary
|
||||
inverse instead. <var class="Ar">fix_spec</var> is a string containing any
|
||||
combination of the following characters:
|
||||
<p class="Pp"></p>
|
||||
<dl class="Bl-tag Bl-compact">
|
||||
<dt><a class="permalink" href="#l"><code class="Cm" id="l">l</code></a></dt>
|
||||
<dd>Fix the Nintendo logo
|
||||
(<span class="Ad">0x104</span>–<span class="Ad">0x133</span>).</dd>
|
||||
<dt><a class="permalink" href="#L"><code class="Cm" id="L">L</code></a></dt>
|
||||
<dd>Trash the Nintendo logo.</dd>
|
||||
<dt><a class="permalink" href="#h"><code class="Cm" id="h">h</code></a></dt>
|
||||
<dd>Fix the header checksum (<span class="Ad">0x14D</span>).</dd>
|
||||
<dt><a class="permalink" href="#H"><code class="Cm" id="H">H</code></a></dt>
|
||||
<dd>Trash the header checksum.</dd>
|
||||
<dt><a class="permalink" href="#g"><code class="Cm" id="g">g</code></a></dt>
|
||||
<dd>Fix the global checksum
|
||||
(<span class="Ad">0x14E</span>–<span class="Ad">0x14F</span>).</dd>
|
||||
<dt><a class="permalink" href="#G"><code class="Cm" id="G">G</code></a></dt>
|
||||
<dd>Trash the global checksum.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt><a class="permalink" href="#i"><code class="Fl" id="i">-i</code></a>
|
||||
<var class="Ar">game_id</var>,
|
||||
<code class="Fl">--game-id</code>
|
||||
<var class="Ar">game_id</var></dt>
|
||||
<dd>Set the game ID string
|
||||
(<span class="Ad">0x13F</span>–<span class="Ad">0x142</span>) to a
|
||||
given string of exactly 4 characters. If both this and the title are set,
|
||||
the game ID will overwrite the overlapping portion of the title.</dd>
|
||||
<dt><a class="permalink" href="#j"><code class="Fl" id="j">-j</code></a>,
|
||||
<code class="Fl">--non-japanese</code></dt>
|
||||
<dd>Set the non-Japanese region flag: <span class="Ad">0x14A</span> = 1.</dd>
|
||||
<dt><a class="permalink" href="#k"><code class="Fl" id="k">-k</code></a>
|
||||
<var class="Ar">licensee_str</var>,
|
||||
<code class="Fl">--new-licensee</code>
|
||||
<var class="Ar">licensee_str</var></dt>
|
||||
<dd>Set the new licensee string
|
||||
(<span class="Ad">0x144</span>–<span class="Ad">0x145</span>) to a
|
||||
given string, truncated to at most two characters.</dd>
|
||||
<dt><a class="permalink" href="#l_2"><code class="Fl" id="l_2">-l</code></a>
|
||||
<var class="Ar">licensee_id</var>,
|
||||
<code class="Fl">--old-licensee</code>
|
||||
<var class="Ar">licensee_id</var></dt>
|
||||
<dd>Set the old licensee code, <span class="Ad">0x14B</span>, to a given value
|
||||
from 0 to 0xFF. This value is deprecated and should be set to 0x33 in all
|
||||
new software.</dd>
|
||||
<dt><a class="permalink" href="#m"><code class="Fl" id="m">-m</code></a>
|
||||
<var class="Ar">mbc_type</var>,
|
||||
<code class="Fl">--mbc-type</code>
|
||||
<var class="Ar">mbc_type</var></dt>
|
||||
<dd>Set the MBC type, <span class="Ad">0x147</span>, to a given value from 0
|
||||
to 0xFF.</dd>
|
||||
<dt><a class="permalink" href="#n"><code class="Fl" id="n">-n</code></a>
|
||||
<var class="Ar">rom_version</var>,
|
||||
<code class="Fl">--rom-version</code>
|
||||
<var class="Ar">rom_version</var></dt>
|
||||
<dd>Set the ROM version, <span class="Ad">0x14C</span>, to a given value from
|
||||
0 to 0xFF.</dd>
|
||||
<dt><a class="permalink" href="#p"><code class="Fl" id="p">-p</code></a>
|
||||
<var class="Ar">pad_value</var>,
|
||||
<code class="Fl">--pad-value</code>
|
||||
<var class="Ar">pad_value</var></dt>
|
||||
<dd>Pad the image to a valid size with a given pad value from 0 to 0xFF.
|
||||
<code class="Nm">rgbfix</code> will automatically pick a size from 32 KiB,
|
||||
64 KiB, 128 KiB, ..., 8192 KiB. The cartridge size byte
|
||||
(<span class="Ad">0x148</span>) will be changed to reflect this new
|
||||
size.</dd>
|
||||
<dt><a class="permalink" href="#r"><code class="Fl" id="r">-r</code></a>
|
||||
<var class="Ar">ram_size</var>,
|
||||
<code class="Fl">--ram-size</code>
|
||||
<var class="Ar">ram_size</var></dt>
|
||||
<dd>Set the RAM size, <span class="Ad">0x149</span>, to a given value from 0
|
||||
to 0xFF.</dd>
|
||||
<dt><a class="permalink" href="#s"><code class="Fl" id="s">-s</code></a>,
|
||||
<code class="Fl">--sgb-compatible</code></dt>
|
||||
<dd>Set the SGB flag: <span class="Ad">0x146</span> = 3. This flag will be
|
||||
ignored by the SGB unless the old licensee code is 0x33!</dd>
|
||||
<dt><a class="permalink" href="#t"><code class="Fl" id="t">-t</code></a>
|
||||
<var class="Ar">title</var>,
|
||||
<code class="Fl">--title</code>
|
||||
<var class="Ar">title</var></dt>
|
||||
<dd>Set the title string
|
||||
(<span class="Ad">0x134</span>–<span class="Ad">0x143</span>) to a
|
||||
given string, truncated to at most 16 characters. It is recommended to use
|
||||
15 characters instead, to avoid clashing with the CGB flag
|
||||
(<code class="Fl">-c</code> or <code class="Fl">-C</code>). If both this
|
||||
and the game ID are set, the game ID will overwrite the overlapping
|
||||
portion of the title.</dd>
|
||||
<dt><a class="permalink" href="#V"><code class="Fl" id="V">-V</code></a>,
|
||||
<code class="Fl">--version</code></dt>
|
||||
<dd>Print the version of the program and exit.</dd>
|
||||
<dt><a class="permalink" href="#v"><code class="Fl" id="v">-v</code></a>,
|
||||
<code class="Fl">--validate</code></dt>
|
||||
<dd>Equivalent to <code class="Fl">-f</code> <code class="Cm">lhg</code>.</dd>
|
||||
</dl>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="EXAMPLES"><a class="permalink" href="#EXAMPLES">EXAMPLES</a></h1>
|
||||
Most values in the ROM header are only cosmetic. The bare minimum requirements
|
||||
for a workable program are the header checksum, the Nintendo logo, and (if
|
||||
needed) the CGB/SGB flags. It is a good idea to pad the image to a valid size
|
||||
as well (“valid” meaning a power of 2, times 32 KiB).
|
||||
<p class="Pp">The following will make a plain, non-color Game Boy game without
|
||||
checking for a valid size:</p>
|
||||
<p class="Pp"></p>
|
||||
<div class="Bd Bd-indent">$ rgbfix -v foo.gb</div>
|
||||
<p class="Pp">The following will make a SGB-enabled, color-enabled game with a
|
||||
title of “foobar”, and pad it to a valid size. (The Game Boy
|
||||
itself does not use the title, but some emulators or ROM managers do.)</p>
|
||||
<p class="Pp"></p>
|
||||
<div class="Bd Bd-indent">$ rgbfix -vcs -l 0x33 -p 255 -t foobar baz.gb</div>
|
||||
<p class="Pp">The following will duplicate the header (sans global checksum) of
|
||||
the game “Survival Kids”:</p>
|
||||
<p class="Pp"></p>
|
||||
<div class="Bd Bd-indent">$ rgbfix -cjsv -k A4 -l 0x33 -m 0x1B -p 0xFF -r 3 -t
|
||||
SURVIVALKIDAVKE SurvivalKids.gbc</div>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="BUGS"><a class="permalink" href="#BUGS">BUGS</a></h1>
|
||||
Please report bugs on
|
||||
<a class="Lk" href="https://github.com/rednex/rgbds/issues">GitHub</a>.
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
|
||||
ALSO</a></h1>
|
||||
<a class="Xr" href="rgbasm.1.html">rgbasm(1)</a>, <a class="Xr" href="rgblink.1.html">rgblink(1)</a>,
|
||||
<a class="Xr" href="rgbds.7.html">rgbds(7)</a>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
|
||||
<code class="Nm">rgbfix</code> was originally released by Carsten
|
||||
Sørensen as a standalone program called gbfix, and was later packaged
|
||||
in RGBDS by Justin Lloyd. It is now maintained by a number of contributors at
|
||||
<a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.
|
||||
</section>
|
||||
</div>
|
||||
<table class="foot">
|
||||
<tr>
|
||||
<td class="foot-date">December 5, 2019</td>
|
||||
<td class="foot-os">General</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,220 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- This is an automatically generated file. Do not edit.
|
||||
This file is part of RGBDS.
|
||||
|
||||
Copyright (c) 2013-2018, stag019 and RGBDS contributors.
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
-->
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
|
||||
<link rel="stylesheet" href="rgbds.css" type="text/css" media="all"/>
|
||||
<title>RGBGFX(1)</title>
|
||||
</head>
|
||||
<body>
|
||||
<table class="head">
|
||||
<tr>
|
||||
<td class="head-ltitle">RGBGFX(1)</td>
|
||||
<td class="head-vol">General Commands Manual</td>
|
||||
<td class="head-rtitle">RGBGFX(1)</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="manual-text">
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
|
||||
<code class="Nm">rgbgfx</code> —
|
||||
<span class="Nd">Game Boy graphics converter</span>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="SYNOPSIS"><a class="permalink" href="#SYNOPSIS">SYNOPSIS</a></h1>
|
||||
<table class="Nm">
|
||||
<tr>
|
||||
<td><code class="Nm">rgbgfx</code></td>
|
||||
<td>[<code class="Fl"><a href="#C">-C</a><a href="#D">D</a><a href="#h">h</a><a href="#m">m</a><a href="#u">u</a><a href="#V">V</a><a href="#v">v</a></code>] [<code class="Fl"><a href="#f">-f</a></code> |
|
||||
<code class="Fl"><a href="#F">-F</a></code>] [<code class="Fl"><a href="#a">-a</a></code>
|
||||
<var class="Ar">attrmap</var> | <code class="Fl"><a href="#A">-A</a></code>]
|
||||
[<code class="Fl"><a href="#d">-d</a></code> <var class="Ar">depth</var>]
|
||||
[<code class="Fl"><a href="#o">-o</a></code> <var class="Ar">out_file</var>]
|
||||
[<code class="Fl"><a href="#p">-p</a></code> <var class="Ar">pal_file</var> |
|
||||
<code class="Fl"><a href="#P">-P</a></code>] [<code class="Fl"><a href="#t">-t</a></code>
|
||||
<var class="Ar">tilemap</var> | <code class="Fl"><a href="#T">-T</a></code>]
|
||||
[<code class="Fl"><a href="#x">-x</a></code> <var class="Ar">tiles</var>]
|
||||
<var class="Ar">file</var></td>
|
||||
</tr>
|
||||
</table>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
|
||||
The <code class="Nm">rgbgfx</code> program converts PNG images into the Nintendo
|
||||
Game Boy's planar tile format.
|
||||
<p class="Pp">The resulting colors and their palette indices are determined
|
||||
differently depending on the input PNG file:</p>
|
||||
<ul class="Bl-dash">
|
||||
<li>If the file has an embedded palette, that palette's color and order are
|
||||
used.</li>
|
||||
<li>If not, and the image only contains shades of gray, rgbgfx maps them to
|
||||
the indices appropriate for each shade. Any undetermined indices are set
|
||||
to respective default shades of gray. For example: if the bit depth is 2
|
||||
and the image contains light gray and black, they become the second and
|
||||
fourth colors, and the first and third colors get set to default white and
|
||||
dark gray. If the image has multiple shades that map to the same index,
|
||||
the palette is instead determined as if the image had color.</li>
|
||||
<li>If the image has color (or the grayscale method failed), the colors are
|
||||
sorted from lightest to darkest.</li>
|
||||
</ul>
|
||||
<p class="Pp">The input image may not contain more colors than the selected bit
|
||||
depth allows. Transparent pixels are set to palette index 0.</p>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="ARGUMENTS"><a class="permalink" href="#ARGUMENTS">ARGUMENTS</a></h1>
|
||||
Note that options can be abbreviated as long as the abbreviation is unambiguous:
|
||||
<code class="Fl">--verb</code> is
|
||||
<code class="Fl">-</code> <code class="Fl">-verbose</code>, but
|
||||
<code class="Fl">--ver</code> is invalid because it
|
||||
could also be <code class="Fl">--version</code>. The
|
||||
arguments are as follows:
|
||||
<dl class="Bl-tag">
|
||||
<dt><a class="permalink" href="#a"><code class="Fl" id="a">-a</code></a>
|
||||
<var class="Ar">attrmap,</var>
|
||||
<code class="Fl">--attr-map</code>
|
||||
<var class="Ar">attrmap</var></dt>
|
||||
<dd>Generate a file of tile mirroring attributes for OAM or (CGB-only)
|
||||
background tiles. For each tile in the input file, a byte is written
|
||||
representing the dimensions that the associated tile in the output file
|
||||
should be mirrored. Useful in combination with <code class="Fl">-m</code>
|
||||
to keep track the mirror direction of mirrored duplicate tiles.</dd>
|
||||
<dt><a class="permalink" href="#A"><code class="Fl" id="A">-A</code></a>,
|
||||
<code class="Fl">--output-attr-map</code></dt>
|
||||
<dd>Same as <code class="Fl">-a</code>, but the attrmap file output name is
|
||||
made by taking the input filename, removing the file extension, and
|
||||
appending <span class="Pa">.attrmap</span>.</dd>
|
||||
<dt><a class="permalink" href="#C"><code class="Fl" id="C">-C</code></a>,
|
||||
<code class="Fl">--color-curve</code></dt>
|
||||
<dd>Use the color curve of the Game Boy Color when generating palettes.</dd>
|
||||
<dt><a class="permalink" href="#D"><code class="Fl" id="D">-D</code></a>,
|
||||
<code class="Fl">--debug</code></dt>
|
||||
<dd>Debug features are enabled.</dd>
|
||||
<dt><a class="permalink" href="#d"><code class="Fl" id="d">-d</code></a>
|
||||
<var class="Ar">depth</var>,
|
||||
<code class="Fl">--depth</code>
|
||||
<var class="Ar">depth</var></dt>
|
||||
<dd>The bit depth of the output image (either 1 or 2). By default, the bit
|
||||
depth is 2 (two bits per pixel).</dd>
|
||||
<dt><a class="permalink" href="#f"><code class="Fl" id="f">-f</code></a>,
|
||||
<code class="Fl">--fix</code></dt>
|
||||
<dd>Fix the input PNG file to be a correctly indexed image.</dd>
|
||||
<dt><a class="permalink" href="#F"><code class="Fl" id="F">-F</code></a>,
|
||||
<code class="Fl">--fix-and-save</code></dt>
|
||||
<dd>Same as <code class="Fl">-f</code>, but additionally, the supplied command
|
||||
line parameters are saved within the PNG and will be loaded and
|
||||
automatically used next time.</dd>
|
||||
<dt><a class="permalink" href="#h"><code class="Fl" id="h">-h</code></a>,
|
||||
<code class="Fl">--horizontal</code></dt>
|
||||
<dd>Lay out tiles horizontally rather than vertically.</dd>
|
||||
<dt><a class="permalink" href="#m"><code class="Fl" id="m">-m</code></a>,
|
||||
<code class="Fl">--mirror-tiles</code></dt>
|
||||
<dd>Truncate tiles by checking for tiles that are mirrored versions of others
|
||||
and omitting these from the output file. Useful with tilemaps and attrmaps
|
||||
together to keep track of the duplicated tiles and the dimension mirrored.
|
||||
Tiles are checked for horizontal, vertical, and horizontal-vertical
|
||||
mirroring. Implies <code class="Fl">-u</code>.</dd>
|
||||
<dt><a class="permalink" href="#o"><code class="Fl" id="o">-o</code></a>
|
||||
<var class="Ar">out_file</var>,
|
||||
<code class="Fl">--output</code>
|
||||
<var class="Ar">out_file</var></dt>
|
||||
<dd>The name of the output file.</dd>
|
||||
<dt><a class="permalink" href="#p"><code class="Fl" id="p">-p</code></a>
|
||||
<var class="Ar">pal_file</var>,
|
||||
<code class="Fl">--palette</code>
|
||||
<var class="Ar">pal_file</var></dt>
|
||||
<dd>Output the image's palette in standard GBC palette format: bytes (8 bytes
|
||||
for two bits per pixel, 4 bytes for one bit per pixel) containing the
|
||||
RGB15 values in little-endian byte order. If the palette contains too few
|
||||
colors, the remaining entries are set to black.</dd>
|
||||
<dt><a class="permalink" href="#P"><code class="Fl" id="P">-P</code></a>,
|
||||
<code class="Fl">--output-palette</code></dt>
|
||||
<dd>Same as <code class="Fl">-p</code>, but the palette file output name is
|
||||
made by taking the input PNG file's filename, removing the file extension,
|
||||
and appending <span class="Pa">.pal</span>.</dd>
|
||||
<dt><a class="permalink" href="#t"><code class="Fl" id="t">-t</code></a>
|
||||
<var class="Ar">tilemap</var>,
|
||||
<code class="Fl">--tilemap</code>
|
||||
<var class="Ar">tilemap</var></dt>
|
||||
<dd>Generate a file of tile indices. For each tile in the input file, a byte
|
||||
is written representing the index of the associated tile in the output
|
||||
file. Useful in combination with <code class="Fl">-u</code> or
|
||||
<code class="Fl">-m</code> to keep track of duplicate tiles.</dd>
|
||||
<dt><a class="permalink" href="#T"><code class="Fl" id="T">-T</code></a>,
|
||||
<code class="Fl">--output-tilemap</code></dt>
|
||||
<dd>Same as <code class="Fl">-t</code>, but the tilemap file output name is
|
||||
made by taking the input filename, removing the file extension, and
|
||||
appending <span class="Pa">.tilemap</span>.</dd>
|
||||
<dt><a class="permalink" href="#u"><code class="Fl" id="u">-u</code></a>,
|
||||
<code class="Fl">--unique-tiles</code></dt>
|
||||
<dd>Truncate tiles by checking for tiles that are exact duplicates of others
|
||||
and omitting these from the output file. Useful with tilemaps to keep
|
||||
track of the duplicated tiles.</dd>
|
||||
<dt><a class="permalink" href="#V"><code class="Fl" id="V">-V</code></a>,
|
||||
<code class="Fl">--version</code></dt>
|
||||
<dd>Print the version of the program and exit.</dd>
|
||||
<dt><a class="permalink" href="#v"><code class="Fl" id="v">-v</code></a>,
|
||||
<code class="Fl">--verbose</code></dt>
|
||||
<dd>Verbose. Print errors when the command line parameters and the parameters
|
||||
in the PNG file don't match.</dd>
|
||||
<dt><a class="permalink" href="#x"><code class="Fl" id="x">-x</code></a>
|
||||
<var class="Ar">tiles</var>,
|
||||
<code class="Fl">--trim-end</code>
|
||||
<var class="Ar">tiles</var></dt>
|
||||
<dd>Trim the end of the output file by this many tiles.</dd>
|
||||
</dl>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="EXAMPLES"><a class="permalink" href="#EXAMPLES">EXAMPLES</a></h1>
|
||||
The following will take a PNG file with a bit depth of 1, 2, or 8, and output
|
||||
planar 2bpp data:
|
||||
<p class="Pp"></p>
|
||||
<div class="Bd Bd-indent">$ rgbgfx -o out.2bpp in.png</div>
|
||||
<p class="Pp">The following creates a planar 2bpp file with only unique tiles,
|
||||
and its tilemap <span class="Pa">out.tilemap</span>:</p>
|
||||
<p class="Pp"></p>
|
||||
<div class="Bd Bd-indent">$ rgbgfx -T -u -o out.2bpp in.png</div>
|
||||
<p class="Pp">The following creates a planar 2bpp file with only unique tiles
|
||||
<span class="Pa">accounting for tile mirroring</span> and its associated
|
||||
tilemap <span class="Pa">out.tilemap</span> and attrmap
|
||||
<span class="Pa">out.attrmap</span>:</p>
|
||||
<p class="Pp"></p>
|
||||
<div class="Bd Bd-indent">$ rgbgfx -A -T -m -o out.2bpp in.png</div>
|
||||
<p class="Pp">The following will do nothing:</p>
|
||||
<p class="Pp"></p>
|
||||
<div class="Bd Bd-indent">$ rgbgfx in.png</div>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="BUGS"><a class="permalink" href="#BUGS">BUGS</a></h1>
|
||||
Please report bugs on
|
||||
<a class="Lk" href="https://github.com/rednex/rgbds/issues">GitHub</a>.
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
|
||||
ALSO</a></h1>
|
||||
<a class="Xr" href="rgbds.7.html">rgbds(7)</a>, <a class="Xr" href="rgbasm.1.html">rgbasm(1)</a>,
|
||||
<a class="Xr" href="rgblink.1.html">rgblink(1)</a>, <a class="Xr" href="rgbfix.1.html">rgbfix(1)</a>,
|
||||
<a class="Xr" href="gbz80.7.html">gbz80(7)</a>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
|
||||
<code class="Nm">rgbgfx</code> was created by <span class="An">stag019</span> to
|
||||
be included in RGBDS. It is now maintained by a number of contributors at
|
||||
<a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.
|
||||
</section>
|
||||
</div>
|
||||
<table class="foot">
|
||||
<tr>
|
||||
<td class="foot-date">December 5, 2019</td>
|
||||
<td class="foot-os">General</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,192 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- This is an automatically generated file. Do not edit.
|
||||
This file is part of RGBDS.
|
||||
|
||||
Copyright (c) 2010-2019, Anthony J. Bentley and RGBDS contributors.
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
-->
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
|
||||
<link rel="stylesheet" href="rgbds.css" type="text/css" media="all"/>
|
||||
<title>RGBLINK(1)</title>
|
||||
</head>
|
||||
<body>
|
||||
<table class="head">
|
||||
<tr>
|
||||
<td class="head-ltitle">RGBLINK(1)</td>
|
||||
<td class="head-vol">General Commands Manual</td>
|
||||
<td class="head-rtitle">RGBLINK(1)</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="manual-text">
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
|
||||
<code class="Nm">rgblink</code> —
|
||||
<span class="Nd">Game Boy linker</span>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="SYNOPSIS"><a class="permalink" href="#SYNOPSIS">SYNOPSIS</a></h1>
|
||||
<table class="Nm">
|
||||
<tr>
|
||||
<td><code class="Nm">rgblink</code></td>
|
||||
<td>[<code class="Fl"><a href="#d">-d</a><a href="#t">t</a><a href="#V">V</a><a href="#v">v</a><a href="#w">w</a><a href="#x">x</a></code>] [<code class="Fl"><a href="#l">-l</a></code>
|
||||
<var class="Ar">linker_script</var>] [<code class="Fl"><a href="#m">-m</a></code>
|
||||
<var class="Ar">map_file</var>] [<code class="Fl"><a href="#n">-n</a></code>
|
||||
<var class="Ar">sym_file</var>] [<code class="Fl"><a href="#O">-O</a></code>
|
||||
<var class="Ar">overlay_file</var>] [<code class="Fl"><a href="#o">-o</a></code>
|
||||
<var class="Ar">out_file</var>] [<code class="Fl"><a href="#p">-p</a></code>
|
||||
<var class="Ar">pad_value</var>] [<code class="Fl"><a href="#s">-s</a></code>
|
||||
<var class="Ar">symbol</var>] <var class="Ar">file ...</var></td>
|
||||
</tr>
|
||||
</table>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
|
||||
The <code class="Nm">rgblink</code> program links RGB object files, typically
|
||||
created by <a class="Xr" href="rgbasm.1.html">rgbasm(1)</a>, into a single Game Boy ROM file. The
|
||||
format is documented in <a class="Xr" href="rgbds.5.html">rgbds(5)</a>.
|
||||
<p class="Pp">ROM0 sections are placed in the first 16 KiB of the output ROM,
|
||||
and ROMX sections are placed in any 16 KiB “bank” except the
|
||||
first. If your ROM will only be 32 KiB, you can use the
|
||||
<code class="Fl">-t</code> option to change this.</p>
|
||||
<p class="Pp">Similarly, WRAM0 sections are placed in the first 4 KiB of WRAM
|
||||
(“bank 0”), and WRAMX sections are placed in any bank of the
|
||||
last 4 KiB. If your ROM doesn't use banked WRAM, you can use the
|
||||
<code class="Fl">-w</code> option to change this.</p>
|
||||
<p class="Pp">Also, if your ROM is designed for a monochrome Game Boy, you can
|
||||
make sure that you don't use any incompatible section by using the
|
||||
<code class="Fl">-d</code> option, which implies <code class="Fl">-w</code>
|
||||
but also prohibits the use of banked VRAM.</p>
|
||||
<p class="Pp">Note that options can be abbreviated as long as the abbreviation
|
||||
is unambiguous: <code class="Fl">--verb</code> is
|
||||
<code class="Fl">--verbose</code>, but
|
||||
<code class="Fl">--ver</code> is invalid because it
|
||||
could also be <code class="Fl">--version</code>. The
|
||||
arguments are as follows:</p>
|
||||
<dl class="Bl-tag">
|
||||
<dt><a class="permalink" href="#d"><code class="Fl" id="d">-d</code></a>,
|
||||
<code class="Fl">--dmg</code></dt>
|
||||
<dd>Enable DMG mode. Prohibit the use of sections that doesn't exist on a DMG,
|
||||
such as VRAM bank 1. This option automatically enables
|
||||
<code class="Fl">-w</code>.</dd>
|
||||
<dt><a class="permalink" href="#l"><code class="Fl" id="l">-l</code></a>
|
||||
<var class="Ar">linker_script,</var>
|
||||
<code class="Fl">--linkerscript</code>
|
||||
<var class="Ar">linker_script</var></dt>
|
||||
<dd>Specify a linker script file that tells the linker how sections must be
|
||||
placed in the ROM. The attributes assigned in the linker script must be
|
||||
consistent with any assigned in the code. See <a class="Xr" href="rgblink.5.html">rgblink(5)</a>
|
||||
for more information about the linker script format.</dd>
|
||||
<dt><a class="permalink" href="#m"><code class="Fl" id="m">-m</code></a>
|
||||
<var class="Ar">map_file</var>,
|
||||
<code class="Fl">--map</code>
|
||||
<var class="Ar">map_file</var></dt>
|
||||
<dd>Write a map file to the given filename, listing how sections and symbols
|
||||
were assigned.</dd>
|
||||
<dt><a class="permalink" href="#n"><code class="Fl" id="n">-n</code></a>
|
||||
<var class="Ar">sym_file</var>,
|
||||
<code class="Fl">--sym</code>
|
||||
<var class="Ar">sym_file</var></dt>
|
||||
<dd>Write a symbol file to the given filename, listing the address of all
|
||||
exported symbols. Several external programs can use this information, for
|
||||
example to help debugging ROMs.</dd>
|
||||
<dt><a class="permalink" href="#O"><code class="Fl" id="O">-O</code></a>
|
||||
<var class="Ar">overlay_file</var>,
|
||||
<code class="Fl">--overlay</code>
|
||||
<var class="Ar">overlay_file</var></dt>
|
||||
<dd>If specified, sections will be overlaid "on top" of the provided
|
||||
ROM image. In that case, all sections must be fixed. This may be used to
|
||||
patch an existing binary.</dd>
|
||||
<dt><a class="permalink" href="#o"><code class="Fl" id="o">-o</code></a>
|
||||
<var class="Ar">out_file</var>,
|
||||
<code class="Fl">--output</code>
|
||||
<var class="Ar">out_file</var></dt>
|
||||
<dd>Write the ROM image to the given file.</dd>
|
||||
<dt><a class="permalink" href="#p"><code class="Fl" id="p">-p</code></a>
|
||||
<var class="Ar">pad_value</var>,
|
||||
<code class="Fl">--pad</code>
|
||||
<var class="Ar">pad_value</var></dt>
|
||||
<dd>When inserting padding between sections, pad with this value. Has no
|
||||
effect if <code class="Fl">-O</code> is specified. The default is 0.</dd>
|
||||
<dt><a class="permalink" href="#s"><code class="Fl" id="s">-s</code></a>
|
||||
<var class="Ar">symbol</var>,
|
||||
<code class="Fl">--smart</code>
|
||||
<var class="Ar">symbol</var></dt>
|
||||
<dd>This option is ignored. It was supposed to perform smart linking but fell
|
||||
into disrepair, and so has been removed. It will be reimplemented at some
|
||||
point.</dd>
|
||||
<dt><a class="permalink" href="#t"><code class="Fl" id="t">-t</code></a>,
|
||||
<code class="Fl">--tiny</code></dt>
|
||||
<dd>Expand the ROM0 section size from 16 KiB to the full 32 KiB assigned to
|
||||
ROM. ROMX sections that are fixed to a bank other than 1 become errors,
|
||||
other ROMX sections are treated as ROM0. Useful for ROMs that fit in 32
|
||||
KiB.</dd>
|
||||
<dt><a class="permalink" href="#V"><code class="Fl" id="V">-V</code></a>,
|
||||
<code class="Fl">--version</code></dt>
|
||||
<dd>Print the version of the program and exit.</dd>
|
||||
<dt><a class="permalink" href="#v"><code class="Fl" id="v">-v</code></a>,
|
||||
<code class="Fl">--verbose</code></dt>
|
||||
<dd>Verbose: enable printing more information to standard error.</dd>
|
||||
<dt><a class="permalink" href="#w"><code class="Fl" id="w">-w</code></a>,
|
||||
<code class="Fl">--wramx</code></dt>
|
||||
<dd>Expand the WRAM0 section size from 4 KiB to the full 8 KiB assigned to
|
||||
WRAM. WRAMX sections that are fixed to a bank other than 1 become errors,
|
||||
other WRAMX sections are treated as WRAM0.</dd>
|
||||
<dt><a class="permalink" href="#x"><code class="Fl" id="x">-x</code></a>,
|
||||
<code class="Fl">--nopad</code></dt>
|
||||
<dd>Disables padding the end of the final file. This option automatically
|
||||
enables <code class="Fl">-t</code>. You can use this when not not making a
|
||||
ROM. When making a ROM, be careful that not using this is not a
|
||||
replacement for <a class="Xr" href="rgbfix.1.html">rgbfix(1)</a>'s <code class="Fl">-p</code>
|
||||
option!</dd>
|
||||
</dl>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="EXAMPLES"><a class="permalink" href="#EXAMPLES">EXAMPLES</a></h1>
|
||||
All you need for a basic ROM is an object file, which can be made into a ROM
|
||||
image like so:
|
||||
<p class="Pp"></p>
|
||||
<div class="Bd Bd-indent">$ rgblink -o bar.gb foo.o</div>
|
||||
<p class="Pp">The resulting <var class="Ar">bar.gb</var> will not have correct
|
||||
checksums (unless you put them in the assembly source). You should use
|
||||
<a class="Xr" href="rgbfix.1.html">rgbfix(1)</a> to fix these so that the program will actually
|
||||
run in a Game Boy:</p>
|
||||
<p class="Pp"></p>
|
||||
<div class="Bd Bd-indent"><code class="Li">$ rgbfix -v bar.gb</code></div>
|
||||
<p class="Pp">Here is a more complete example:</p>
|
||||
<p class="Pp"></p>
|
||||
<div class="Bd Bd-indent"><code class="Li">$ rgblink -o bin/game.gb -n
|
||||
bin/game.sym -p 0xFF obj/title.o obj/engine.o</code></div>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="BUGS"><a class="permalink" href="#BUGS">BUGS</a></h1>
|
||||
Please report bugs on
|
||||
<a class="Lk" href="https://github.com/rednex/rgbds/issues">GitHub</a>.
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
|
||||
ALSO</a></h1>
|
||||
<a class="Xr" href="rgbasm.1.html">rgbasm(1)</a>, <a class="Xr" href="rgblink.5.html">rgblink(5)</a>,
|
||||
<a class="Xr" href="rgbfix.1.html">rgbfix(1)</a>, <a class="Xr" href="rgbds.5.html">rgbds(5)</a>,
|
||||
<a class="Xr" href="rgbds.7.html">rgbds(7)</a>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
|
||||
<code class="Nm">rgblink</code> was originally written by Carsten
|
||||
Sørensen as part of the ASMotor package, and was later packaged in
|
||||
RGBDS by Justin Lloyd. It is now maintained by a number of contributors at
|
||||
<a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.
|
||||
</section>
|
||||
</div>
|
||||
<table class="foot">
|
||||
<tr>
|
||||
<td class="foot-date">November 26, 2019</td>
|
||||
<td class="foot-os">General</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,111 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- This is an automatically generated file. Do not edit.
|
||||
This file is part of RGBDS.
|
||||
|
||||
Copyright (c) 2017-2018, Antonio Nino Diaz and RGBDS contributors.
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
-->
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
|
||||
<link rel="stylesheet" href="rgbds.css" type="text/css" media="all"/>
|
||||
<title>RGBLINK(5)</title>
|
||||
</head>
|
||||
<body>
|
||||
<table class="head">
|
||||
<tr>
|
||||
<td class="head-ltitle">RGBLINK(5)</td>
|
||||
<td class="head-vol">File Formats Manual</td>
|
||||
<td class="head-rtitle">RGBLINK(5)</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="manual-text">
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
|
||||
<code class="Nm">rgblink</code> —
|
||||
<span class="Nd">linker script file format</span>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
|
||||
The linker script is an external file that allows the user to specify the order
|
||||
of sections at link time and in a centralized manner.
|
||||
<p class="Pp">A linker script consists on a series of banks followed by a list
|
||||
of sections and, optionally, commands. They can be lowercase or uppercase,
|
||||
it is ignored. Any line can contain a comment starting with
|
||||
‘<code class="Li">;</code>’ that ends at the end of the
|
||||
line:</p>
|
||||
<div class="Bd Pp Bd-indent">
|
||||
<pre>
|
||||
ROMX $F ; This is a comment
|
||||
"Functions to read array"
|
||||
ALIGN 8
|
||||
"Array aligned to 256 bytes"
|
||||
|
||||
WRAMX 2
|
||||
"Some variables"
|
||||
</pre>
|
||||
</div>
|
||||
<p class="Pp">Numbers can be in decimal or hexadecimal format (the prefix is
|
||||
‘<code class="Li">$</code>’). It is an error if any section
|
||||
name or command is found before setting a bank.</p>
|
||||
<p class="Pp">Files can be included by using the <code class="Ic">INCLUDE</code>
|
||||
keyword, followed by a string with the path of the file that has to be
|
||||
included.</p>
|
||||
<p class="Pp">The possible bank types are: <code class="Cm">ROM0</code>,
|
||||
<code class="Cm">ROMX</code>, <code class="Cm">VRAM</code>,
|
||||
<code class="Cm">SRAM</code>, <code class="Cm">WRAM0</code>,
|
||||
<code class="Cm">WRAMX</code>, <code class="Cm">OAM</code> and
|
||||
<code class="Cm">HRAM</code>. Unless there is a single bank, which can occur
|
||||
with types <code class="Cm">ROMX</code>, <code class="Cm">VRAM</code>,
|
||||
<code class="Cm">SRAM</code> and <code class="Cm">WRAMX</code>, it is needed
|
||||
to specify a bank number after the type.</p>
|
||||
<p class="Pp">When a new bank statement is found, sections found after it will
|
||||
be placed right from the beginning of that bank. If the linker script
|
||||
switches to a different bank and then comes back to a previous one, it will
|
||||
continue from the last address that was used.</p>
|
||||
<p class="Pp">The only two commands are <code class="Ic">ORG</code> and
|
||||
<code class="Ic">ALIGN</code>:</p>
|
||||
<ul class="Bl-bullet">
|
||||
<li><a class="permalink" href="#ORG"><code class="Ic" id="ORG">ORG</code></a>
|
||||
sets the address in which new sections will be placed. It can not be lower
|
||||
than the current address.</li>
|
||||
<li><a class="permalink" href="#ALIGN"><code class="Ic" id="ALIGN">ALIGN</code></a>
|
||||
will increase the address until it is aligned to the specified boundary
|
||||
(it tries to set to 0 the number of bits specified after the command:
|
||||
‘<code class="Li">ALIGN 8</code>’ will align to $100).</li>
|
||||
</ul>
|
||||
<p class="Pp"><b class="Sy">Note:</b> The bank, alignment, address and type of
|
||||
sections can be specified both in the source code and in the linker script.
|
||||
For a section to be able to be placed with the linker script, the bank,
|
||||
address and alignment must be left unassigned in the source code or be
|
||||
compatible with what is specified in the linker script. For example,
|
||||
‘<code class="Li">ALIGN[8]</code>’ in the source code is
|
||||
compatible with ‘<code class="Li">ORG $F00</code>’ in the
|
||||
linker script.</p>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
|
||||
ALSO</a></h1>
|
||||
<a class="Xr" href="rgbasm.1.html">rgbasm(1)</a>, <a class="Xr" href="rgblink.1.html">rgblink(1)</a>,
|
||||
<a class="Xr" href="rgbfix.1.html">rgbfix(1)</a>, <a class="Xr" href="rgbds.5.html">rgbds(5)</a>,
|
||||
<a class="Xr" href="rgbds.7.html">rgbds(7)</a>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
|
||||
<code class="Nm">rgblink</code> was originally written by Carsten
|
||||
Sørensen as part of the ASMotor package, and was later packaged in
|
||||
RGBDS by Justin Lloyd. It is now maintained by a number of contributors at
|
||||
<a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.
|
||||
</section>
|
||||
</div>
|
||||
<table class="foot">
|
||||
<tr>
|
||||
<td class="foot-date">November 26, 2019</td>
|
||||
<td class="foot-os">General</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@@ -21,22 +21,11 @@
|
||||
#include "asm/localasm.h"
|
||||
#include "asm/symbol.h"
|
||||
|
||||
#define MAXUNIONS 128
|
||||
#define MAXMACROARGS 99999
|
||||
#define MAXINCPATHS 128
|
||||
|
||||
extern int32_t nLineNo;
|
||||
extern uint32_t nTotalLines;
|
||||
extern uint32_t nIFDepth;
|
||||
extern bool skipElif;
|
||||
extern uint32_t nUnionDepth;
|
||||
extern uint32_t unionStart[MAXUNIONS];
|
||||
extern uint32_t unionSize[MAXUNIONS];
|
||||
extern char tzCurrentFileName[_MAX_PATH + 1];
|
||||
extern struct Section *pCurrentSection;
|
||||
extern bool oDontExpandStrings;
|
||||
|
||||
size_t symvaluetostring(char *dest, size_t maxLength, char *sym,
|
||||
const char *mode);
|
||||
|
||||
#endif /* RGBDS_ASM_ASM_H */
|
||||
|
||||
@@ -11,36 +11,12 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define MAXCHARMAPS 512
|
||||
#define CHARMAPLENGTH 16
|
||||
#define MAXCHARNODES (MAXCHARMAPS * CHARMAPLENGTH + 1)
|
||||
|
||||
/*
|
||||
* A node for trie structure.
|
||||
*/
|
||||
struct Charnode {
|
||||
uint8_t code; /* the value in a key-value pair. */
|
||||
uint8_t isCode; /* has 1 if it's a code node, not just a bridge node. */
|
||||
struct Charnode *next[256]; /* each index representing the next possible
|
||||
* character from its current state.
|
||||
*/
|
||||
};
|
||||
|
||||
struct Charmap {
|
||||
char name[MAXSYMLEN + 1];
|
||||
int32_t charCount; /* user-side count. */
|
||||
int32_t nodeCount; /* node-side count. */
|
||||
struct Charnode nodes[MAXCHARNODES]; /* first node is reserved for the
|
||||
* root node in charmap.
|
||||
*/
|
||||
};
|
||||
|
||||
void charmap_InitMain(void);
|
||||
struct Charmap *charmap_New(const char *name, const char *baseName);
|
||||
void charmap_Delete(struct Charmap *charmap);
|
||||
void charmap_Set(const char *name);
|
||||
void charmap_Push(void);
|
||||
void charmap_Pop(void);
|
||||
int32_t charmap_Add(char *input, uint8_t output);
|
||||
int32_t charmap_Convert(char **input);
|
||||
void charmap_Add(char *mapping, uint8_t value);
|
||||
size_t charmap_Convert(char const *input, uint8_t *output);
|
||||
|
||||
#endif /* RGBDS_ASM_CHARMAP_H */
|
||||
|
||||
@@ -21,36 +21,59 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
struct MacroArgs;
|
||||
struct FileStackNode {
|
||||
struct FileStackNode *parent; /* Pointer to parent node, for error reporting */
|
||||
/* Line at which the parent context was exited; meaningless for the root level */
|
||||
uint32_t lineNo;
|
||||
|
||||
struct sContext {
|
||||
YY_BUFFER_STATE FlexHandle;
|
||||
struct Symbol const *pMacro;
|
||||
struct sContext *pNext;
|
||||
char tzFileName[_MAX_PATH + 1];
|
||||
struct MacroArgs *macroArgs;
|
||||
uint32_t uniqueID;
|
||||
int32_t nLine;
|
||||
uint32_t nStatus;
|
||||
FILE *pFile;
|
||||
char *pREPTBlock;
|
||||
uint32_t nREPTBlockCount;
|
||||
uint32_t nREPTBlockSize;
|
||||
int32_t nREPTBodyFirstLine;
|
||||
int32_t nREPTBodyLastLine;
|
||||
struct FileStackNode *next; /* Next node in the output linked list */
|
||||
bool referenced; /* If referenced, don't free! */
|
||||
uint32_t ID; /* Set only if referenced: ID within the object file, -1 if not output yet */
|
||||
|
||||
enum {
|
||||
NODE_REPT,
|
||||
NODE_FILE,
|
||||
NODE_MACRO,
|
||||
} type;
|
||||
};
|
||||
|
||||
extern unsigned int nMaxRecursionDepth;
|
||||
struct FileStackReptNode { /* NODE_REPT */
|
||||
struct FileStackNode node;
|
||||
uint32_t reptDepth;
|
||||
/* WARNING: if changing this type, change overflow check in `fstk_Init` */
|
||||
uint32_t iters[]; /* REPT iteration counts since last named node, in reverse depth order */
|
||||
};
|
||||
|
||||
void fstk_RunInclude(char *tzFileName);
|
||||
void fstk_Init(char *s);
|
||||
void fstk_Dump(void);
|
||||
void fstk_DumpToStr(char *buf, size_t len);
|
||||
void fstk_DumpStringExpansions(void);
|
||||
void fstk_AddIncludePath(char *s);
|
||||
void fstk_RunMacro(char *s, struct MacroArgs *args);
|
||||
void fstk_RunRept(uint32_t count, int32_t nReptLineNo);
|
||||
FILE *fstk_FindFile(char const *fname, char **incPathUsed);
|
||||
int32_t fstk_GetLine(void);
|
||||
struct FileStackNamedNode { /* NODE_FILE, NODE_MACRO */
|
||||
struct FileStackNode node;
|
||||
char name[]; /* File name for files, file::macro name for macros */
|
||||
};
|
||||
|
||||
extern size_t nMaxRecursionDepth;
|
||||
|
||||
struct MacroArgs;
|
||||
|
||||
void fstk_Dump(struct FileStackNode const *node, uint32_t lineNo);
|
||||
void fstk_DumpCurrent(void);
|
||||
struct FileStackNode *fstk_GetFileStack(void);
|
||||
/* The lifetime of the returned chars is until reaching the end of that file */
|
||||
char const *fstk_GetFileName(void);
|
||||
|
||||
void fstk_AddIncludePath(char const *s);
|
||||
/**
|
||||
* @param path The user-provided file name
|
||||
* @param fullPath The address of a pointer, which will be made to point at the full path
|
||||
* The pointer's value must be a valid argument to `realloc`, including NULL
|
||||
* @param size Current size of the buffer, or 0 if the pointer is NULL
|
||||
* @return True if the file was found, false if no path worked
|
||||
*/
|
||||
bool fstk_FindFile(char const *path, char **fullPath, size_t *size);
|
||||
|
||||
bool yywrap(void);
|
||||
void fstk_RunInclude(char const *path);
|
||||
void fstk_RunMacro(char const *macroName, struct MacroArgs *args);
|
||||
void fstk_RunRept(uint32_t count, int32_t nReptLineNo, char *body, size_t size);
|
||||
|
||||
void fstk_Init(char const *mainPath, size_t maxRecursionDepth);
|
||||
|
||||
#endif /* RGBDS_ASM_FSTACK_H */
|
||||
|
||||
@@ -9,78 +9,65 @@
|
||||
#ifndef RGBDS_ASM_LEXER_H
|
||||
#define RGBDS_ASM_LEXER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define LEXHASHSIZE (1 << 11)
|
||||
#define MAXSTRLEN 255
|
||||
|
||||
struct sLexInitString {
|
||||
char *tzName;
|
||||
uint32_t nToken;
|
||||
struct LexerState;
|
||||
extern struct LexerState *lexerState;
|
||||
extern struct LexerState *lexerStateEOL;
|
||||
|
||||
static inline struct LexerState *lexer_GetState(void)
|
||||
{
|
||||
return lexerState;
|
||||
}
|
||||
|
||||
static inline void lexer_SetState(struct LexerState *state)
|
||||
{
|
||||
lexerState = state;
|
||||
}
|
||||
|
||||
static inline void lexer_SetStateAtEOL(struct LexerState *state)
|
||||
{
|
||||
lexerStateEOL = state;
|
||||
}
|
||||
|
||||
extern char const *binDigits;
|
||||
extern char const *gfxDigits;
|
||||
|
||||
static inline void lexer_SetBinDigits(char const *digits)
|
||||
{
|
||||
binDigits = digits;
|
||||
}
|
||||
|
||||
static inline void lexer_SetGfxDigits(char const *digits)
|
||||
{
|
||||
gfxDigits = digits;
|
||||
}
|
||||
|
||||
/*
|
||||
* `path` is referenced, but not held onto..!
|
||||
*/
|
||||
struct LexerState *lexer_OpenFile(char const *path);
|
||||
struct LexerState *lexer_OpenFileView(char *buf, size_t size, uint32_t lineNo);
|
||||
void lexer_RestartRept(uint32_t lineNo);
|
||||
void lexer_DeleteState(struct LexerState *state);
|
||||
void lexer_Init(void);
|
||||
|
||||
enum LexerMode {
|
||||
LEXER_NORMAL,
|
||||
LEXER_RAW,
|
||||
LEXER_SKIP_TO_ELIF,
|
||||
LEXER_SKIP_TO_ENDC
|
||||
};
|
||||
|
||||
struct sLexFloat {
|
||||
uint32_t (*Callback)(char *s, uint32_t size);
|
||||
uint32_t nToken;
|
||||
};
|
||||
void lexer_SetMode(enum LexerMode mode);
|
||||
void lexer_ToggleStringExpansion(bool enable);
|
||||
|
||||
struct yy_buffer_state {
|
||||
/* Actual starting address */
|
||||
char *pBufferRealStart;
|
||||
/* Address where the data is initially written after a safety margin */
|
||||
char *pBufferStart;
|
||||
char *pBuffer;
|
||||
size_t nBufferSize;
|
||||
uint32_t oAtLineStart;
|
||||
};
|
||||
|
||||
enum eLexerState {
|
||||
LEX_STATE_NORMAL,
|
||||
LEX_STATE_MACROARGS
|
||||
};
|
||||
|
||||
struct sStringExpansionPos {
|
||||
char *tzName;
|
||||
char *pBuffer;
|
||||
char *pBufferPos;
|
||||
struct sStringExpansionPos *pParent;
|
||||
};
|
||||
|
||||
#define INITIAL 0
|
||||
#define macroarg 3
|
||||
|
||||
typedef struct yy_buffer_state *YY_BUFFER_STATE;
|
||||
|
||||
void setup_lexer(void);
|
||||
|
||||
void yy_set_state(enum eLexerState i);
|
||||
YY_BUFFER_STATE yy_create_buffer(FILE *f);
|
||||
YY_BUFFER_STATE yy_scan_bytes(char const *mem, uint32_t size);
|
||||
void yy_delete_buffer(YY_BUFFER_STATE buf);
|
||||
void yy_switch_to_buffer(YY_BUFFER_STATE buf);
|
||||
uint32_t lex_FloatAlloc(const struct sLexFloat *tok);
|
||||
void lex_FloatAddRange(uint32_t id, uint16_t start, uint16_t end);
|
||||
void lex_FloatDeleteRange(uint32_t id, uint16_t start, uint16_t end);
|
||||
void lex_FloatAddFirstRange(uint32_t id, uint16_t start, uint16_t end);
|
||||
void lex_FloatDeleteFirstRange(uint32_t id, uint16_t start, uint16_t end);
|
||||
void lex_FloatAddSecondRange(uint32_t id, uint16_t start, uint16_t end);
|
||||
void lex_FloatDeleteSecondRange(uint32_t id, uint16_t start, uint16_t end);
|
||||
void lex_Init(void);
|
||||
void lex_AddStrings(const struct sLexInitString *lex);
|
||||
void lex_SetBuffer(char *buffer, uint32_t len);
|
||||
void lex_BeginStringExpansion(const char *tzName);
|
||||
int yywrap(void);
|
||||
char const *lexer_GetFileName(void);
|
||||
uint32_t lexer_GetLineNo(void);
|
||||
uint32_t lexer_GetColNo(void);
|
||||
void lexer_DumpStringExpansions(void);
|
||||
int yylex(void);
|
||||
void yyunput(char c);
|
||||
void yyunputstr(const char *s);
|
||||
void yyskipbytes(uint32_t count);
|
||||
void yyunputbytes(uint32_t count);
|
||||
|
||||
extern YY_BUFFER_STATE pCurrentBuffer;
|
||||
extern struct sStringExpansionPos *pCurrentStringExpansion;
|
||||
|
||||
void upperstring(char *s);
|
||||
void lowerstring(char *s);
|
||||
void lexer_CaptureRept(char **capture, size_t *size);
|
||||
void lexer_CaptureMacroBody(char **capture, size_t *size);
|
||||
|
||||
#endif /* RGBDS_ASM_LEXER_H */
|
||||
|
||||
@@ -28,7 +28,8 @@ char const *macro_GetArg(uint32_t i);
|
||||
uint32_t macro_GetUniqueID(void);
|
||||
char const *macro_GetUniqueIDStr(void);
|
||||
void macro_SetUniqueID(uint32_t id);
|
||||
void macro_ShiftCurrentArgs(void);
|
||||
uint32_t macro_UseNewUniqueID(void);
|
||||
void macro_ShiftCurrentArgs(int32_t count);
|
||||
uint32_t macro_NbArgs(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -26,8 +26,6 @@ extern uint32_t ulNewMacroSize;
|
||||
extern int32_t nGBGfxID;
|
||||
extern int32_t nBinaryID;
|
||||
|
||||
extern uint32_t curOffset; /* Offset into the current section */
|
||||
|
||||
extern struct sOptions DefaultOptions;
|
||||
extern struct sOptions CurrentOptions;
|
||||
extern bool haltnop;
|
||||
@@ -45,6 +43,10 @@ void opt_Push(void);
|
||||
void opt_Pop(void);
|
||||
void opt_Parse(char *s);
|
||||
|
||||
void upperstring(char *s);
|
||||
void lowerstring(char *s);
|
||||
|
||||
/* TODO: are these really needed? */
|
||||
#define YY_FATAL_ERROR fatalerror
|
||||
|
||||
#ifdef YYLMAX
|
||||
|
||||
@@ -18,6 +18,8 @@ struct Expression;
|
||||
extern char *tzObjectname;
|
||||
extern struct Section *pSectionList, *pCurrentSection;
|
||||
|
||||
void out_RegisterNode(struct FileStackNode *node);
|
||||
void out_ReplaceNode(struct FileStackNode *node);
|
||||
void out_SetFileName(char *s);
|
||||
void out_CreatePatch(uint32_t type, struct Expression const *expr,
|
||||
uint32_t ofs);
|
||||
|
||||
@@ -27,7 +27,7 @@ struct Expression {
|
||||
uint32_t nRPNPatchSize; // Size the expression will take in the obj file
|
||||
};
|
||||
|
||||
/* FIXME: Should be defined in `asmy.h`, but impossible with POSIX Yacc */
|
||||
/* FIXME: Should be defined in `parser.h`, but impossible with POSIX Yacc */
|
||||
extern int32_t nPCOffset;
|
||||
|
||||
/*
|
||||
@@ -46,9 +46,11 @@ static inline bool rpn_isSymbol(const struct Expression *expr)
|
||||
return expr->isSymbol;
|
||||
}
|
||||
|
||||
void rpn_Symbol(struct Expression *expr, char *tzSym);
|
||||
void rpn_Symbol(struct Expression *expr, char const *tzSym);
|
||||
void rpn_Number(struct Expression *expr, uint32_t i);
|
||||
void rpn_LOGNOT(struct Expression *expr, const struct Expression *src);
|
||||
struct Symbol const *rpn_SymbolOf(struct Expression const *expr);
|
||||
bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *sym);
|
||||
void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
||||
const struct Expression *src1,
|
||||
const struct Expression *src2);
|
||||
|
||||
@@ -17,17 +17,17 @@
|
||||
struct Expression;
|
||||
|
||||
struct Section {
|
||||
char *pzName;
|
||||
enum SectionType nType;
|
||||
char *name;
|
||||
enum SectionType type;
|
||||
enum SectionModifier modifier;
|
||||
uint32_t size;
|
||||
uint32_t nOrg;
|
||||
uint32_t nBank;
|
||||
uint8_t nAlign;
|
||||
uint32_t org;
|
||||
uint32_t bank;
|
||||
uint8_t align;
|
||||
uint16_t alignOfs;
|
||||
struct Section *pNext;
|
||||
struct Patch *pPatches;
|
||||
uint8_t *tData;
|
||||
struct Section *next;
|
||||
struct Patch *patches;
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
struct SectionSpec {
|
||||
@@ -36,8 +36,8 @@ struct SectionSpec {
|
||||
uint16_t alignOfs;
|
||||
};
|
||||
|
||||
struct Section *out_FindSectionByName(const char *pzName);
|
||||
void out_NewSection(char const *pzName, uint32_t secttype, uint32_t org,
|
||||
struct Section *out_FindSectionByName(const char *name);
|
||||
void out_NewSection(char const *name, uint32_t secttype, uint32_t org,
|
||||
struct SectionSpec const *attributes,
|
||||
enum SectionModifier mod);
|
||||
void out_SetLoadSection(char const *name, uint32_t secttype, uint32_t org,
|
||||
@@ -45,9 +45,15 @@ void out_SetLoadSection(char const *name, uint32_t secttype, uint32_t org,
|
||||
void out_EndLoadSection(void);
|
||||
|
||||
struct Section *sect_GetSymbolSection(void);
|
||||
uint32_t sect_GetSymbolOffset(void);
|
||||
uint32_t sect_GetOutputOffset(void);
|
||||
void sect_AlignPC(uint8_t alignment, uint16_t offset);
|
||||
|
||||
void sect_StartUnion(void);
|
||||
void sect_NextUnionMember(void);
|
||||
void sect_EndUnion(void);
|
||||
void sect_CheckUnionClosed(void);
|
||||
|
||||
void out_AbsByte(uint8_t b);
|
||||
void out_AbsByteGroup(uint8_t const *s, int32_t length);
|
||||
void out_Skip(int32_t skip, bool ds);
|
||||
@@ -57,7 +63,7 @@ void out_RelBytes(struct Expression *expr, uint32_t n);
|
||||
void out_RelWord(struct Expression *expr);
|
||||
void out_RelLong(struct Expression *expr);
|
||||
void out_PCRelByte(struct Expression *expr);
|
||||
void out_BinaryFile(char const *s);
|
||||
void out_BinaryFile(char const *s, int32_t startPos);
|
||||
void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length);
|
||||
|
||||
void out_PushSection(void);
|
||||
|
||||
@@ -34,20 +34,22 @@ struct Symbol {
|
||||
enum SymbolType type;
|
||||
bool isExported; /* Whether the symbol is to be exported */
|
||||
bool isBuiltin; /* Whether the symbol is a built-in */
|
||||
struct Symbol const *scope;
|
||||
struct Section *section;
|
||||
char fileName[_MAX_PATH + 1]; /* File where the symbol was defined. */
|
||||
uint32_t fileLine; /* Line where the symbol was defined. */
|
||||
struct FileStackNode *src; /* Where the symbol was defined */
|
||||
uint32_t fileLine; /* Line where the symbol was defined */
|
||||
|
||||
bool hasCallback;
|
||||
union {
|
||||
struct { /* If sym_IsNumeric */
|
||||
int32_t value;
|
||||
int32_t (*callback)(void);
|
||||
};
|
||||
struct { /* For SYM_MACRO */
|
||||
uint32_t macroSize;
|
||||
/* If sym_IsNumeric */
|
||||
int32_t value;
|
||||
int32_t (*numCallback)(void);
|
||||
/* For SYM_MACRO */
|
||||
struct {
|
||||
size_t macroSize;
|
||||
char *macro;
|
||||
};
|
||||
/* For SYM_EQUS, TODO: separate "base" fields from SYM_MACRO */
|
||||
char const *(*strCallback)(void); /* For SYM_EQUS */
|
||||
};
|
||||
|
||||
uint32_t ID; /* ID of the symbol in the object file (-1 if none) */
|
||||
@@ -71,7 +73,7 @@ static inline bool sym_IsConstant(struct Symbol const *sym)
|
||||
if (sym->type == SYM_LABEL) {
|
||||
struct Section const *sect = sym_GetSection(sym);
|
||||
|
||||
return sect && sect->nOrg != -1;
|
||||
return sect && sect->org != -1;
|
||||
}
|
||||
return sym->type == SYM_EQU || sym->type == SYM_SET;
|
||||
}
|
||||
@@ -102,6 +104,8 @@ static inline bool sym_IsExported(struct Symbol const *sym)
|
||||
*/
|
||||
static inline char const *sym_GetStringValue(struct Symbol const *sym)
|
||||
{
|
||||
if (sym->hasCallback)
|
||||
return sym->strCallback();
|
||||
return sym->macro;
|
||||
}
|
||||
|
||||
@@ -109,23 +113,35 @@ void sym_ForEach(void (*func)(struct Symbol *, void *), void *arg);
|
||||
|
||||
int32_t sym_GetValue(struct Symbol const *sym);
|
||||
void sym_SetExportAll(bool set);
|
||||
struct Symbol *sym_AddLocalReloc(char const *symName);
|
||||
struct Symbol *sym_AddReloc(char const *symName);
|
||||
struct Symbol *sym_AddLocalLabel(char const *symName);
|
||||
struct Symbol *sym_AddLabel(char const *symName);
|
||||
void sym_Export(char const *symName);
|
||||
struct Symbol *sym_AddEqu(char const *symName, int32_t value);
|
||||
struct Symbol *sym_AddSet(char const *symName, int32_t value);
|
||||
uint32_t sym_GetPCValue(void);
|
||||
uint32_t sym_GetConstantSymValue(struct Symbol const *sym);
|
||||
uint32_t sym_GetConstantValue(char const *s);
|
||||
struct Symbol *sym_FindSymbol(char const *symName);
|
||||
struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo);
|
||||
/*
|
||||
* Find a symbol by exact name, bypassing expansion checks
|
||||
*/
|
||||
struct Symbol *sym_FindExactSymbol(char const *name);
|
||||
/*
|
||||
* Find a symbol by exact name; may not be scoped, produces an error if it is
|
||||
*/
|
||||
struct Symbol *sym_FindUnscopedSymbol(char const *name);
|
||||
/*
|
||||
* Find a symbol, possibly scoped, by name
|
||||
*/
|
||||
struct Symbol *sym_FindScopedSymbol(char const *name);
|
||||
struct Symbol const *sym_GetPC(void);
|
||||
struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size);
|
||||
struct Symbol *sym_Ref(char const *symName);
|
||||
struct Symbol *sym_AddString(char const *symName, char const *value);
|
||||
uint32_t sym_GetDefinedValue(char const *s);
|
||||
void sym_Purge(char const *symName);
|
||||
void sym_Init(void);
|
||||
|
||||
/* Functions to save and restore the current symbol scope. */
|
||||
struct Symbol *sym_GetCurrentSymbolScope(void);
|
||||
void sym_SetCurrentSymbolScope(struct Symbol *newScope);
|
||||
char const *sym_GetCurrentSymbolScope(void);
|
||||
void sym_SetCurrentSymbolScope(char const *newScope);
|
||||
|
||||
#endif /* RGBDS_SYMBOL_H */
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t calchash(const char *s);
|
||||
int32_t readUTF8Char(char *dest, char *src);
|
||||
char const *print(int c);
|
||||
size_t readUTF8Char(uint8_t *dest, char const *src);
|
||||
|
||||
#endif /* RGBDS_UTIL_H */
|
||||
|
||||
@@ -14,18 +14,20 @@
|
||||
extern unsigned int nbErrors;
|
||||
|
||||
enum WarningID {
|
||||
WARNING_ASSERT,
|
||||
WARNING_BUILTIN_ARG,
|
||||
WARNING_DIV,
|
||||
WARNING_EMPTY_DATA_DIRECTIVE,
|
||||
WARNING_EMPTY_ENTRY,
|
||||
WARNING_LARGE_CONSTANT,
|
||||
WARNING_LONG_STR,
|
||||
WARNING_OBSOLETE,
|
||||
WARNING_SHIFT,
|
||||
WARNING_SHIFT_AMOUNT,
|
||||
WARNING_TRUNCATION,
|
||||
WARNING_USER,
|
||||
WARNING_ASSERT, /* Assertions */
|
||||
WARNING_BUILTIN_ARG, /* Invalid args to builtins */
|
||||
WARNING_CHARMAP_REDEF, /* Charmap entry re-definition */
|
||||
WARNING_DIV, /* Division undefined behavior */
|
||||
WARNING_EMPTY_DATA_DIRECTIVE, /* `db`, `dw` or `dl` with no directive in ROM */
|
||||
WARNING_EMPTY_ENTRY, /* Empty entry in `db`, `dw` or `dl` */
|
||||
WARNING_LARGE_CONSTANT, /* Constants too large */
|
||||
WARNING_LONG_STR, /* String too long for internal buffers */
|
||||
WARNING_NESTED_COMMENT, /* Comment-start delimeter in a block comment */
|
||||
WARNING_OBSOLETE, /* Obsolete things */
|
||||
WARNING_SHIFT, /* Shifting undefined behavior */
|
||||
WARNING_SHIFT_AMOUNT, /* Strange shift amount */
|
||||
WARNING_TRUNCATION, /* Implicit truncation loses some bits */
|
||||
WARNING_USER, /* User warnings */
|
||||
|
||||
NB_WARNINGS,
|
||||
|
||||
@@ -53,7 +55,7 @@ void warning(enum WarningID id, const char *fmt, ...);
|
||||
* It is also used when the assembler goes into an invalid state (for example,
|
||||
* when it fails to allocate memory).
|
||||
*/
|
||||
noreturn_ void fatalerror(const char *fmt, ...);
|
||||
_Noreturn void fatalerror(const char *fmt, ...);
|
||||
|
||||
/*
|
||||
* Used for errors that make it impossible to assemble correctly, but don't
|
||||
@@ -61,6 +63,6 @@ noreturn_ void fatalerror(const char *fmt, ...);
|
||||
* get a list of all errors at the end, making it easier to fix all of them at
|
||||
* once.
|
||||
*/
|
||||
void yyerror(const char *fmt, ...);
|
||||
void error(const char *fmt, ...);
|
||||
|
||||
#endif
|
||||
|
||||
8
include/extern/err.h
vendored
8
include/extern/err.h
vendored
@@ -34,10 +34,10 @@ void vwarn(const char *fmt, va_list ap) format_(printf, 1, 0);
|
||||
void warnx(const char *fmt, ...) format_(printf, 1, 2);
|
||||
void vwarnx(const char *fmt, va_list ap) format_(printf, 1, 0);
|
||||
|
||||
noreturn_ void err(int status, const char *fmt, ...) format_(printf, 2, 3);
|
||||
noreturn_ void verr(int status, const char *fmt, va_list ap) format_(printf, 2, 0);
|
||||
noreturn_ void errx(int status, const char *fmt, ...) format_(printf, 2, 3);
|
||||
noreturn_ void verrx(int status, const char *fmt, va_list ap) format_(printf, 2, 0);
|
||||
_Noreturn void err(int status, const char *fmt, ...) format_(printf, 2, 3);
|
||||
_Noreturn void verr(int status, const char *fmt, va_list ap) format_(printf, 2, 0);
|
||||
_Noreturn void errx(int status, const char *fmt, ...) format_(printf, 2, 3);
|
||||
_Noreturn void verrx(int status, const char *fmt, va_list ap) format_(printf, 2, 0);
|
||||
|
||||
#endif /* ERR_IN_LIBC */
|
||||
|
||||
|
||||
2
include/extern/utf8decoder.h
vendored
2
include/extern/utf8decoder.h
vendored
@@ -9,6 +9,6 @@
|
||||
#ifndef EXTERN_UTF8DECODER_H
|
||||
#define EXTERN_UTF8DECODER_H
|
||||
|
||||
uint32_t decode(uint32_t *state, uint32_t *codep, uint32_t byte);
|
||||
uint32_t decode(uint32_t *state, uint32_t *codep, uint8_t byte);
|
||||
|
||||
#endif /* EXTERN_UTF8DECODER_H */
|
||||
|
||||
@@ -33,6 +33,16 @@ typedef struct HashMapEntry *HashMap[HASHMAP_NB_BUCKETS];
|
||||
*/
|
||||
bool hash_AddElement(HashMap map, char const *key, void *element);
|
||||
|
||||
/**
|
||||
* Replaces an element with an already-present key in a hashmap.
|
||||
* @warning Inserting a NULL will make `hash_GetElement`'s return ambiguous!
|
||||
* @param map The HashMap to replace the element in
|
||||
* @param key The key with which the element will be stored and retrieved
|
||||
* @param element The element to replace
|
||||
* @return True if the element was found and replaced
|
||||
*/
|
||||
bool hash_ReplaceElement(HashMap const map, char const *key, void *element);
|
||||
|
||||
/**
|
||||
* Removes an element from a hashmap.
|
||||
* @param map The HashMap to remove the element from
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of RGBDS.
|
||||
*
|
||||
* Copyright (c) 2014-2018, RGBDS contributors.
|
||||
* Copyright (c) 2014-2020, RGBDS contributors.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
@@ -9,25 +9,30 @@
|
||||
#ifndef HELPERS_H
|
||||
#define HELPERS_H
|
||||
|
||||
#ifdef __GNUC__
|
||||
/* GCC or compatible */
|
||||
// Of course, MSVC does not support C11, so no _Noreturn there...
|
||||
#ifdef _MSC_VER
|
||||
#define _Noreturn __declspec(noreturn)
|
||||
#endif
|
||||
|
||||
// Ideally, we'd use `__has_attribute` and `__has_builtin`, but these were only introduced in GCC 9
|
||||
#ifdef __GNUC__ // GCC or compatible
|
||||
#define format_(archetype, str_index, first_arg) \
|
||||
__attribute__ ((format (archetype, str_index, first_arg)))
|
||||
#define noreturn_ __attribute__ ((noreturn))
|
||||
#define trap_ __builtin_trap()
|
||||
// In release builds, define "unreachable" as such, but trap in debug builds
|
||||
#ifdef NDEBUG
|
||||
#define unreachable_ __builtin_unreachable
|
||||
#else
|
||||
#define unreachable_ __builtin_trap
|
||||
#endif
|
||||
#else
|
||||
/* Unsupported, but no need to throw a fit */
|
||||
// Unsupported, but no need to throw a fit
|
||||
#define format_(archetype, str_index, first_arg)
|
||||
#define noreturn_
|
||||
#define unused_
|
||||
#define trap_
|
||||
// This seems to generate similar code to __builtin_unreachable, despite different semantics
|
||||
// Note that executing this is undefined behavior (declared _Noreturn, but does return)
|
||||
static inline _Noreturn unreachable_(void) {}
|
||||
#endif
|
||||
|
||||
#ifndef DEVELOP
|
||||
#define DEVELOP 0
|
||||
#endif
|
||||
|
||||
/* Macros for stringification */
|
||||
// Macros for stringification
|
||||
#define STR(x) #x
|
||||
#define EXPAND_AND_STR(x) STR(x)
|
||||
|
||||
|
||||
@@ -29,15 +29,45 @@ extern bool beVerbose;
|
||||
extern bool isWRA0Mode;
|
||||
extern bool disablePadding;
|
||||
|
||||
struct FileStackNode {
|
||||
struct FileStackNode *parent;
|
||||
/* Line at which the parent context was exited; meaningless for the root level */
|
||||
uint32_t lineNo;
|
||||
|
||||
enum {
|
||||
NODE_REPT,
|
||||
NODE_FILE,
|
||||
NODE_MACRO,
|
||||
} type;
|
||||
union {
|
||||
char *name; /* NODE_FILE, NODE_MACRO */
|
||||
struct { /* NODE_REPT */
|
||||
uint32_t reptDepth;
|
||||
uint32_t *iters;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/* Helper macro for printing verbose-mode messages */
|
||||
#define verbosePrint(...) do { \
|
||||
if (beVerbose) \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
void error(char const *fmt, ...);
|
||||
/**
|
||||
* Dump a file stack to stderr
|
||||
* @param node The leaf node to dump the context of
|
||||
*/
|
||||
char const *dumpFileStack(struct FileStackNode const *node);
|
||||
|
||||
noreturn_ void fatal(char const *fmt, ...);
|
||||
void warning(struct FileStackNode const *where, uint32_t lineNo,
|
||||
char const *fmt, ...) format_(printf, 3, 4);
|
||||
|
||||
void error(struct FileStackNode const *where, uint32_t lineNo,
|
||||
char const *fmt, ...) format_(printf, 3, 4);
|
||||
|
||||
_Noreturn void fatal(struct FileStackNode const *where, uint32_t lineNo,
|
||||
char const *fmt, ...) format_(printf, 3, 4);
|
||||
|
||||
/**
|
||||
* Opens a file if specified, and aborts on error.
|
||||
|
||||
@@ -14,8 +14,9 @@
|
||||
/**
|
||||
* Read an object (.o) file, and add its info to the data structures.
|
||||
* @param fileName A path to the object file to be read
|
||||
* @param i The ID of the file
|
||||
*/
|
||||
void obj_ReadFile(char const *fileName);
|
||||
void obj_ReadFile(char const *fileName, unsigned int i);
|
||||
|
||||
/**
|
||||
* Perform validation on the object files' contents
|
||||
@@ -27,6 +28,12 @@ void obj_DoSanityChecks(void);
|
||||
*/
|
||||
void obj_CheckAssertions(void);
|
||||
|
||||
/**
|
||||
* Sets up object file reading
|
||||
* @param nbFiles The number of object files that will be read
|
||||
*/
|
||||
void obj_Setup(unsigned int nbFiles);
|
||||
|
||||
/**
|
||||
* `free`s all object memory that was allocated.
|
||||
*/
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "linkdefs.h"
|
||||
|
||||
struct FileStackNode;
|
||||
struct Section;
|
||||
|
||||
struct AttachedSymbol {
|
||||
@@ -27,7 +28,8 @@ struct AttachedSymbol {
|
||||
};
|
||||
|
||||
struct Patch {
|
||||
char *fileName;
|
||||
struct FileStackNode const *src;
|
||||
uint32_t lineNo;
|
||||
int32_t offset;
|
||||
uint32_t pcSectionID;
|
||||
uint32_t pcOffset;
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
|
||||
#include "linkdefs.h"
|
||||
|
||||
struct FileStackNode;
|
||||
|
||||
struct Symbol {
|
||||
/* Info contained in the object files */
|
||||
char *name;
|
||||
enum ExportLevel type;
|
||||
char const *objFileName;
|
||||
char *fileName;
|
||||
struct FileStackNode const *src;
|
||||
int32_t lineNo;
|
||||
int32_t sectionID;
|
||||
union {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#define RGBDS_OBJECT_VERSION_STRING "RGB%1u"
|
||||
#define RGBDS_OBJECT_VERSION_NUMBER 9U
|
||||
#define RGBDS_OBJECT_REV 5U
|
||||
#define RGBDS_OBJECT_REV 6U
|
||||
|
||||
enum AssertionType {
|
||||
ASSERT_WARN,
|
||||
|
||||
@@ -32,4 +32,11 @@
|
||||
# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
|
||||
#endif
|
||||
|
||||
/* MSVC doesn't use POSIX types or defines for `read` */
|
||||
#ifdef _MSC_VER
|
||||
# define STDIN_FILENO 0
|
||||
# define ssize_t int
|
||||
# define SSIZE_MAX INT_MAX
|
||||
#endif
|
||||
|
||||
#endif /* RGBDS_PLATFORM_H */
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
#define PACKAGE_VERSION_MAJOR (0)
|
||||
#define PACKAGE_VERSION_MINOR (4)
|
||||
#define PACKAGE_VERSION_PATCH (1)
|
||||
#define PACKAGE_VERSION_PATCH (2)
|
||||
|
||||
const char *get_package_version_string(void);
|
||||
|
||||
|
||||
@@ -12,28 +12,26 @@ set(common_src
|
||||
"version.c"
|
||||
)
|
||||
|
||||
BISON_TARGET(ASMy "asm/asmy.y"
|
||||
"${PROJECT_SOURCE_DIR}/src/asm/asmy.c"
|
||||
DEFINES_FILE "${PROJECT_SOURCE_DIR}/src/asm/asmy.h"
|
||||
)
|
||||
find_package(BISON REQUIRED)
|
||||
find_package(PkgConfig)
|
||||
|
||||
# Lexer is not present yet
|
||||
if(False) # FLEX_FOUND
|
||||
FLEX_TARGET(Lexer "asm/lexer.l"
|
||||
"${PROJECT_SOURCE_DIR}/src/asm/lexer.c"
|
||||
)
|
||||
ADD_FLEX_BISON_DEPENDENCY(Lexer ASMy)
|
||||
set(Lexer_SOURCE "${FLEX_Lexer_OUTPUTS}")
|
||||
if(NOT PKG_CONFIG_FOUND)
|
||||
# fallback to find_package
|
||||
find_package(PNG REQUIRED)
|
||||
else()
|
||||
set(Lexer_SOURCE "asm/lexer.c")
|
||||
pkg_check_modules(LIBPNG REQUIRED libpng)
|
||||
endif()
|
||||
|
||||
BISON_TARGET(PARSER "asm/parser.y"
|
||||
"${PROJECT_SOURCE_DIR}/src/asm/parser.c"
|
||||
DEFINES_FILE "${PROJECT_SOURCE_DIR}/src/asm/parser.h"
|
||||
)
|
||||
|
||||
set(rgbasm_src
|
||||
"${BISON_ASMy_OUTPUT_SOURCE}"
|
||||
"${Lexer_SOURCE}"
|
||||
"${BISON_PARSER_OUTPUT_SOURCE}"
|
||||
"asm/charmap.c"
|
||||
"asm/fstack.c"
|
||||
"asm/globlex.c"
|
||||
"asm/lexer.c"
|
||||
"asm/macro.c"
|
||||
"asm/main.c"
|
||||
"asm/math.c"
|
||||
@@ -79,10 +77,26 @@ foreach(PROG "asm" "fix" "gfx" "link")
|
||||
install(TARGETS rgb${PROG} RUNTIME DESTINATION bin)
|
||||
endforeach()
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS 2.8.12)
|
||||
add_definitions(${PNG_DEFINITIONS})
|
||||
include_directories(${PNG_INCLUDE_DIRS})
|
||||
target_link_libraries(rgbgfx ${PNG_LIBRARIES})
|
||||
set(MANDIR "share/man")
|
||||
set(man1 "asm/rgbasm.1"
|
||||
"fix/rgbfix.1"
|
||||
"gfx/rgbgfx.1"
|
||||
"link/rgblink.1")
|
||||
set(man5 "asm/rgbasm.5"
|
||||
"link/rgblink.5"
|
||||
"rgbds.5")
|
||||
set(man7 "gbz80.7"
|
||||
"rgbds.7")
|
||||
|
||||
foreach(SECTION "man1" "man5" "man7")
|
||||
set(DEST "${MANDIR}/${SECTION}")
|
||||
install(FILES ${${SECTION}} DESTINATION ${DEST})
|
||||
endforeach()
|
||||
|
||||
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})
|
||||
@@ -92,9 +106,5 @@ endif()
|
||||
include(CheckLibraryExists)
|
||||
check_library_exists("m" "sin" "" HAS_LIBM)
|
||||
if(HAS_LIBM)
|
||||
if(CMAKE_VERSION VERSION_LESS 2.8.12)
|
||||
target_link_libraries(rgbasm LINK_PRIVATE "m")
|
||||
else()
|
||||
target_link_libraries(rgbasm PRIVATE "m")
|
||||
endif()
|
||||
target_link_libraries(rgbasm PRIVATE "m")
|
||||
endif()
|
||||
|
||||
4
src/asm/.gitignore
vendored
4
src/asm/.gitignore
vendored
@@ -1,2 +1,2 @@
|
||||
asmy.c
|
||||
asmy.h
|
||||
/parser.c
|
||||
/parser.h
|
||||
|
||||
@@ -22,18 +22,36 @@
|
||||
|
||||
#include "hashmap.h"
|
||||
|
||||
#define CHARMAP_HASH_SIZE (1 << 9)
|
||||
/*
|
||||
* 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,
|
||||
*/
|
||||
struct Charnode {
|
||||
bool isTerminal; /* Whether there exists a mapping that ends here */
|
||||
uint8_t value; /* If the above is true, its corresponding value */
|
||||
/* This MUST be indexes and not pointers, because pointers get invalidated by `realloc`!! */
|
||||
size_t next[255]; /* Indexes of where to go next, 0 = nowhere */
|
||||
};
|
||||
|
||||
#define INITIAL_CAPACITY 32
|
||||
|
||||
struct Charmap {
|
||||
char *name;
|
||||
size_t usedNodes; /* How many nodes are being used */
|
||||
size_t capacity; /* How many nodes have been allocated */
|
||||
struct Charnode nodes[]; /* first node is reserved for the root node */
|
||||
};
|
||||
|
||||
static HashMap charmaps;
|
||||
|
||||
static struct Charmap *currentCharmap;
|
||||
|
||||
struct CharmapStackEntry {
|
||||
struct Charmap *charmap;
|
||||
struct CharmapStackEntry *next;
|
||||
};
|
||||
|
||||
static HashMap charmaps;
|
||||
|
||||
static struct Charmap *mainCharmap;
|
||||
static struct Charmap *currentCharmap;
|
||||
|
||||
struct CharmapStackEntry *charmapStack;
|
||||
|
||||
static inline struct Charmap *charmap_Get(const char *name)
|
||||
@@ -41,16 +59,21 @@ static inline struct Charmap *charmap_Get(const char *name)
|
||||
return hash_GetElement(charmaps, name);
|
||||
}
|
||||
|
||||
static void CopyNode(struct Charmap *dest,
|
||||
const struct Charmap *src,
|
||||
int nodeIdx)
|
||||
static inline struct Charmap *resizeCharmap(struct Charmap *map, size_t capacity)
|
||||
{
|
||||
dest->nodes[nodeIdx].code = src->nodes[nodeIdx].code;
|
||||
dest->nodes[nodeIdx].isCode = src->nodes[nodeIdx].isCode;
|
||||
for (int i = 0; i < 256; i++)
|
||||
if (src->nodes[nodeIdx].next[i])
|
||||
dest->nodes[nodeIdx].next[i] = dest->nodes +
|
||||
(src->nodes[nodeIdx].next[i] - src->nodes);
|
||||
struct Charmap *new = realloc(map, sizeof(*map) + sizeof(*map->nodes) * capacity);
|
||||
|
||||
if (!new)
|
||||
fatalerror("Failed to %s charmap: %s\n",
|
||||
map ? "create" : "resize", strerror(errno));
|
||||
new->capacity = capacity;
|
||||
return new;
|
||||
}
|
||||
|
||||
static inline void initNode(struct Charnode *node)
|
||||
{
|
||||
node->isTerminal = false;
|
||||
memset(node->next, 0, sizeof(node->next));
|
||||
}
|
||||
|
||||
struct Charmap *charmap_New(const char *name, const char *baseName)
|
||||
@@ -61,33 +84,28 @@ struct Charmap *charmap_New(const char *name, const char *baseName)
|
||||
base = charmap_Get(baseName);
|
||||
|
||||
if (base == NULL)
|
||||
yyerror("Base charmap '%s' doesn't exist", baseName);
|
||||
error("Base charmap '%s' doesn't exist\n", baseName);
|
||||
}
|
||||
|
||||
struct Charmap *charmap = charmap_Get(name);
|
||||
|
||||
if (charmap != NULL) {
|
||||
yyerror("Charmap '%s' already exists", name);
|
||||
return NULL;
|
||||
if (charmap) {
|
||||
error("Charmap '%s' already exists\n", name);
|
||||
return charmap;
|
||||
}
|
||||
|
||||
charmap = malloc(sizeof(*charmap));
|
||||
if (charmap == NULL)
|
||||
fatalerror("Failed to create charmap: %s", strerror(errno));
|
||||
|
||||
/* Init the new charmap's fields */
|
||||
snprintf(charmap->name, sizeof(charmap->name), "%s", name);
|
||||
if (base != NULL) {
|
||||
charmap->charCount = base->charCount;
|
||||
charmap->nodeCount = base->nodeCount;
|
||||
if (base) {
|
||||
charmap = resizeCharmap(NULL, base->capacity);
|
||||
charmap->usedNodes = base->usedNodes;
|
||||
|
||||
for (int i = 0; i < MAXCHARNODES; i++)
|
||||
CopyNode(charmap, base, i);
|
||||
memcpy(charmap->nodes, base->nodes, sizeof(base->nodes[0]) * charmap->usedNodes);
|
||||
} else {
|
||||
charmap->charCount = 0;
|
||||
charmap->nodeCount = 0;
|
||||
memset(charmap->nodes, 0, sizeof(charmap->nodes));
|
||||
charmap = resizeCharmap(NULL, INITIAL_CAPACITY);
|
||||
charmap->usedNodes = 1;
|
||||
initNode(&charmap->nodes[0]); /* Init the root node */
|
||||
}
|
||||
charmap->name = strdup(name);
|
||||
|
||||
hash_AddElement(charmaps, charmap->name, charmap);
|
||||
currentCharmap = charmap;
|
||||
@@ -95,12 +113,18 @@ struct Charmap *charmap_New(const char *name, const char *baseName)
|
||||
return charmap;
|
||||
}
|
||||
|
||||
void charmap_Delete(struct Charmap *charmap)
|
||||
{
|
||||
free(charmap->name);
|
||||
free(charmap);
|
||||
}
|
||||
|
||||
void charmap_Set(const char *name)
|
||||
{
|
||||
struct Charmap *charmap = charmap_Get(name);
|
||||
|
||||
if (charmap == NULL)
|
||||
yyerror("Charmap '%s' doesn't exist", name);
|
||||
error("Charmap '%s' doesn't exist\n", name);
|
||||
else
|
||||
currentCharmap = charmap;
|
||||
}
|
||||
@@ -109,9 +133,9 @@ void charmap_Push(void)
|
||||
{
|
||||
struct CharmapStackEntry *stackEntry;
|
||||
|
||||
stackEntry = malloc(sizeof(struct CharmapStackEntry));
|
||||
stackEntry = malloc(sizeof(*stackEntry));
|
||||
if (stackEntry == NULL)
|
||||
fatalerror("No memory for charmap stack");
|
||||
fatalerror("Failed to alloc charmap stack entry: %s\n", strerror(errno));
|
||||
|
||||
stackEntry->charmap = currentCharmap;
|
||||
stackEntry->next = charmapStack;
|
||||
@@ -121,8 +145,10 @@ void charmap_Push(void)
|
||||
|
||||
void charmap_Pop(void)
|
||||
{
|
||||
if (charmapStack == NULL)
|
||||
fatalerror("No entries in the charmap stack");
|
||||
if (charmapStack == NULL) {
|
||||
error("No entries in the charmap stack\n");
|
||||
return;
|
||||
}
|
||||
|
||||
struct CharmapStackEntry *top = charmapStack;
|
||||
|
||||
@@ -131,111 +157,87 @@ void charmap_Pop(void)
|
||||
free(top);
|
||||
}
|
||||
|
||||
void charmap_InitMain(void)
|
||||
void charmap_Add(char *mapping, uint8_t value)
|
||||
{
|
||||
mainCharmap = charmap_New("main", NULL);
|
||||
}
|
||||
struct Charnode *node = ¤tCharmap->nodes[0];
|
||||
|
||||
int32_t charmap_Add(char *input, uint8_t output)
|
||||
{
|
||||
int32_t i;
|
||||
uint8_t v;
|
||||
for (uint8_t c; *mapping; mapping++) {
|
||||
c = *mapping - 1;
|
||||
|
||||
struct Charmap *charmap = currentCharmap;
|
||||
struct Charnode *curr_node, *temp_node;
|
||||
|
||||
if (charmap->charCount >= MAXCHARMAPS || strlen(input) > CHARMAPLENGTH)
|
||||
return -1;
|
||||
|
||||
curr_node = &charmap->nodes[0];
|
||||
|
||||
for (i = 0; (v = (uint8_t)input[i]); i++) {
|
||||
if (curr_node->next[v]) {
|
||||
curr_node = curr_node->next[v];
|
||||
if (node->next[c]) {
|
||||
node = ¤tCharmap->nodes[node->next[c]];
|
||||
} else {
|
||||
temp_node = &charmap->nodes[charmap->nodeCount + 1];
|
||||
/* Register next available node */
|
||||
node->next[c] = currentCharmap->usedNodes;
|
||||
/* If no more nodes are available, get new ones */
|
||||
if (currentCharmap->usedNodes == currentCharmap->capacity) {
|
||||
currentCharmap->capacity *= 2;
|
||||
currentCharmap = resizeCharmap(currentCharmap, currentCharmap->capacity);
|
||||
hash_ReplaceElement(charmaps, currentCharmap->name, currentCharmap);
|
||||
}
|
||||
|
||||
curr_node->next[v] = temp_node;
|
||||
curr_node = temp_node;
|
||||
|
||||
++charmap->nodeCount;
|
||||
/* Switch to and init new node */
|
||||
node = ¤tCharmap->nodes[currentCharmap->usedNodes++];
|
||||
initNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
/* prevent duplicated keys by accepting only first key-value pair. */
|
||||
if (curr_node->isCode)
|
||||
return charmap->charCount;
|
||||
if (node->isTerminal)
|
||||
warning(WARNING_CHARMAP_REDEF, "Overriding charmap mapping");
|
||||
|
||||
curr_node->code = output;
|
||||
curr_node->isCode = 1;
|
||||
|
||||
return ++charmap->charCount;
|
||||
node->isTerminal = true;
|
||||
node->value = value;
|
||||
}
|
||||
|
||||
int32_t charmap_Convert(char **input)
|
||||
size_t charmap_Convert(char const *input, uint8_t *output)
|
||||
{
|
||||
struct Charmap *charmap = currentCharmap;
|
||||
struct Charnode *charnode;
|
||||
/*
|
||||
* The goal is to match the longest mapping possible.
|
||||
* For that, advance through the trie with each character read.
|
||||
* If that would lead to a dead end, rewind characters until the last match, and output.
|
||||
* If no match, read a UTF-8 codepoint and output that.
|
||||
*/
|
||||
size_t outputLen = 0;
|
||||
struct Charnode const *node = ¤tCharmap->nodes[0];
|
||||
struct Charnode const *match = NULL;
|
||||
size_t rewindDistance = 0;
|
||||
|
||||
char *output;
|
||||
char outchar[8];
|
||||
for (;;) {
|
||||
/* We still want NULs to reach the `else` path, to give a chance to rewind */
|
||||
uint8_t c = *input - 1;
|
||||
|
||||
int32_t i, match, length;
|
||||
uint8_t v, foundCode;
|
||||
if (*input && node->next[c]) {
|
||||
input++; /* Consume that char */
|
||||
rewindDistance++;
|
||||
|
||||
output = malloc(strlen(*input));
|
||||
if (output == NULL)
|
||||
fatalerror("Not enough memory for buffer");
|
||||
node = ¤tCharmap->nodes[node->next[c]];
|
||||
if (node->isTerminal) {
|
||||
match = node;
|
||||
rewindDistance = 0; /* Rewind from after the match */
|
||||
}
|
||||
|
||||
length = 0;
|
||||
} else {
|
||||
input -= rewindDistance; /* Rewind */
|
||||
rewindDistance = 0;
|
||||
node = ¤tCharmap->nodes[0];
|
||||
|
||||
while (**input) {
|
||||
charnode = &charmap->nodes[0];
|
||||
if (match) { /* Arrived at a dead end with a match found */
|
||||
*output++ = match->value;
|
||||
outputLen++;
|
||||
match = NULL; /* Reset match for next round */
|
||||
|
||||
/*
|
||||
* Find the longest valid match which has been registered in
|
||||
* charmap, possibly yielding multiple or no matches.
|
||||
* The longest match is taken, meaning partial matches shorter
|
||||
* than the longest one are ignored.
|
||||
*/
|
||||
for (i = match = 0; (v = (*input)[i]);) {
|
||||
if (!charnode->next[v])
|
||||
} else if (*input) { /* No match found */
|
||||
size_t codepointLen = readUTF8Char(output, input);
|
||||
|
||||
input += codepointLen; /* OK because UTF-8 has no NUL in multi-byte chars */
|
||||
output += codepointLen;
|
||||
outputLen += codepointLen;
|
||||
}
|
||||
|
||||
if (!*input)
|
||||
break;
|
||||
|
||||
charnode = charnode->next[v];
|
||||
i++;
|
||||
|
||||
if (charnode->isCode) {
|
||||
match = i;
|
||||
foundCode = charnode->code;
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
output[length] = foundCode;
|
||||
|
||||
length++;
|
||||
} else {
|
||||
/*
|
||||
* put a utf-8 character
|
||||
* if failed to find a match.
|
||||
*/
|
||||
match = readUTF8Char(outchar, *input);
|
||||
|
||||
if (match) {
|
||||
memcpy(output + length, *input, match);
|
||||
} else {
|
||||
output[length] = 0;
|
||||
match = 1;
|
||||
}
|
||||
|
||||
length += match;
|
||||
}
|
||||
|
||||
*input += match;
|
||||
}
|
||||
|
||||
*input = output;
|
||||
|
||||
return length;
|
||||
return outputLen;
|
||||
}
|
||||
|
||||
810
src/asm/fstack.c
810
src/asm/fstack.c
@@ -6,540 +6,470 @@
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
* FileStack routines
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "asm/fstack.h"
|
||||
#include "asm/lexer.h"
|
||||
#include "asm/macro.h"
|
||||
#include "asm/main.h"
|
||||
#include "asm/output.h"
|
||||
#include "asm/symbol.h"
|
||||
#include "asm/warning.h"
|
||||
#include "platform.h" /* S_ISDIR (stat macro) */
|
||||
|
||||
#include "extern/err.h"
|
||||
#ifdef LEXER_DEBUG
|
||||
#define dbgPrint(...) fprintf(stderr, "[lexer] " __VA_ARGS__)
|
||||
#else
|
||||
#define dbgPrint(...)
|
||||
#endif
|
||||
|
||||
#include "platform.h" // S_ISDIR (stat macro)
|
||||
#include "types.h"
|
||||
struct Context {
|
||||
struct Context *parent;
|
||||
struct FileStackNode *fileInfo;
|
||||
struct LexerState *lexerState;
|
||||
uint32_t uniqueID;
|
||||
struct MacroArgs *macroArgs; /* Macro args are *saved* here */
|
||||
uint32_t nbReptIters;
|
||||
};
|
||||
|
||||
static struct sContext *pFileStack;
|
||||
static unsigned int nFileStackDepth;
|
||||
unsigned int nMaxRecursionDepth;
|
||||
static struct Symbol const *pCurrentMacro;
|
||||
static YY_BUFFER_STATE CurrentFlexHandle;
|
||||
static FILE *pCurrentFile;
|
||||
static uint32_t nCurrentStatus;
|
||||
char tzCurrentFileName[_MAX_PATH + 1];
|
||||
static char IncludePaths[MAXINCPATHS][_MAX_PATH + 1];
|
||||
static int32_t NextIncPath;
|
||||
static uint32_t nMacroCount;
|
||||
static struct Context *contextStack;
|
||||
static size_t contextDepth = 0;
|
||||
#define DEFAULT_MAX_DEPTH 64
|
||||
size_t nMaxRecursionDepth;
|
||||
|
||||
static char *pCurrentREPTBlock;
|
||||
static uint32_t nCurrentREPTBlockSize;
|
||||
static uint32_t nCurrentREPTBlockCount;
|
||||
static int32_t nCurrentREPTBodyFirstLine;
|
||||
static int32_t nCurrentREPTBodyLastLine;
|
||||
static unsigned int nbIncPaths = 0;
|
||||
static char const *includePaths[MAXINCPATHS];
|
||||
|
||||
uint32_t ulMacroReturnValue;
|
||||
|
||||
/*
|
||||
* defines for nCurrentStatus
|
||||
*/
|
||||
#define STAT_isInclude 0 /* 'Normal' state as well */
|
||||
#define STAT_isMacro 1
|
||||
#define STAT_isMacroArg 2
|
||||
#define STAT_isREPTBlock 3
|
||||
|
||||
/* Max context stack size */
|
||||
|
||||
/*
|
||||
* Context push and pop
|
||||
*/
|
||||
static void pushcontext(void)
|
||||
char const *dumpNodeAndParents(struct FileStackNode const *node)
|
||||
{
|
||||
struct sContext **ppFileStack;
|
||||
char const *name;
|
||||
|
||||
if (++nFileStackDepth > nMaxRecursionDepth)
|
||||
fatalerror("Recursion limit (%u) exceeded", nMaxRecursionDepth);
|
||||
if (node->type == NODE_REPT) {
|
||||
assert(node->parent); /* REPT nodes should always have a parent */
|
||||
struct FileStackReptNode const *reptInfo = (struct FileStackReptNode const *)node;
|
||||
|
||||
ppFileStack = &pFileStack;
|
||||
while (*ppFileStack)
|
||||
ppFileStack = &((*ppFileStack)->pNext);
|
||||
|
||||
*ppFileStack = malloc(sizeof(struct sContext));
|
||||
|
||||
if (*ppFileStack == NULL)
|
||||
fatalerror("No memory for context");
|
||||
|
||||
(*ppFileStack)->FlexHandle = CurrentFlexHandle;
|
||||
(*ppFileStack)->pNext = NULL;
|
||||
strcpy((char *)(*ppFileStack)->tzFileName, (char *)tzCurrentFileName);
|
||||
(*ppFileStack)->nLine = nLineNo;
|
||||
|
||||
switch ((*ppFileStack)->nStatus = nCurrentStatus) {
|
||||
case STAT_isMacroArg:
|
||||
case STAT_isMacro:
|
||||
(*ppFileStack)->macroArgs = macro_GetCurrentArgs();
|
||||
(*ppFileStack)->pMacro = pCurrentMacro;
|
||||
break;
|
||||
case STAT_isInclude:
|
||||
(*ppFileStack)->pFile = pCurrentFile;
|
||||
break;
|
||||
case STAT_isREPTBlock:
|
||||
(*ppFileStack)->macroArgs = macro_GetCurrentArgs();
|
||||
(*ppFileStack)->pREPTBlock = pCurrentREPTBlock;
|
||||
(*ppFileStack)->nREPTBlockSize = nCurrentREPTBlockSize;
|
||||
(*ppFileStack)->nREPTBlockCount = nCurrentREPTBlockCount;
|
||||
(*ppFileStack)->nREPTBodyFirstLine = nCurrentREPTBodyFirstLine;
|
||||
(*ppFileStack)->nREPTBodyLastLine = nCurrentREPTBodyLastLine;
|
||||
break;
|
||||
default:
|
||||
fatalerror("%s: Internal error.", __func__);
|
||||
}
|
||||
(*ppFileStack)->uniqueID = macro_GetUniqueID();
|
||||
|
||||
nLineNo = 0;
|
||||
}
|
||||
|
||||
static int32_t popcontext(void)
|
||||
{
|
||||
struct sContext *pLastFile, **ppLastFile;
|
||||
|
||||
if (nCurrentStatus == STAT_isREPTBlock) {
|
||||
if (--nCurrentREPTBlockCount) {
|
||||
char *pREPTIterationWritePtr;
|
||||
unsigned long nREPTIterationNo;
|
||||
int nNbCharsWritten;
|
||||
int nNbCharsLeft;
|
||||
|
||||
yy_delete_buffer(CurrentFlexHandle);
|
||||
CurrentFlexHandle =
|
||||
yy_scan_bytes(pCurrentREPTBlock,
|
||||
nCurrentREPTBlockSize);
|
||||
yy_switch_to_buffer(CurrentFlexHandle);
|
||||
macro_SetUniqueID(nMacroCount++);
|
||||
|
||||
/* Increment REPT count in file path */
|
||||
pREPTIterationWritePtr =
|
||||
strrchr(tzCurrentFileName, '~') + 1;
|
||||
nREPTIterationNo =
|
||||
strtoul(pREPTIterationWritePtr, NULL, 10);
|
||||
nNbCharsLeft = sizeof(tzCurrentFileName)
|
||||
- (pREPTIterationWritePtr - tzCurrentFileName);
|
||||
nNbCharsWritten = snprintf(pREPTIterationWritePtr,
|
||||
nNbCharsLeft, "%lu",
|
||||
nREPTIterationNo + 1);
|
||||
if (nNbCharsWritten >= nNbCharsLeft) {
|
||||
/*
|
||||
* The string is probably corrupted somehow,
|
||||
* revert the change to avoid a bad error
|
||||
* output.
|
||||
*/
|
||||
sprintf(pREPTIterationWritePtr, "%lu",
|
||||
nREPTIterationNo);
|
||||
fatalerror("Cannot write REPT count to file path");
|
||||
}
|
||||
|
||||
nLineNo = nCurrentREPTBodyFirstLine;
|
||||
return 0;
|
||||
name = dumpNodeAndParents(node->parent);
|
||||
fprintf(stderr, "(%" PRIu32 ") -> %s", node->lineNo, name);
|
||||
for (uint32_t i = reptInfo->reptDepth; i--; )
|
||||
fprintf(stderr, "::REPT~%" PRIu32, reptInfo->iters[i]);
|
||||
} else {
|
||||
name = ((struct FileStackNamedNode const *)node)->name;
|
||||
if (node->parent) {
|
||||
dumpNodeAndParents(node->parent);
|
||||
fprintf(stderr, "(%" PRIu32 ") -> %s", node->lineNo, name);
|
||||
} else {
|
||||
fputs(name, stderr);
|
||||
}
|
||||
}
|
||||
|
||||
pLastFile = pFileStack;
|
||||
if (pLastFile == NULL)
|
||||
return 1;
|
||||
|
||||
ppLastFile = &pFileStack;
|
||||
while (pLastFile->pNext) {
|
||||
ppLastFile = &(pLastFile->pNext);
|
||||
pLastFile = *ppLastFile;
|
||||
}
|
||||
|
||||
yy_delete_buffer(CurrentFlexHandle);
|
||||
nLineNo = nCurrentStatus == STAT_isREPTBlock ? nCurrentREPTBodyLastLine
|
||||
: pLastFile->nLine;
|
||||
|
||||
if (nCurrentStatus == STAT_isInclude)
|
||||
fclose(pCurrentFile);
|
||||
|
||||
if (nCurrentStatus == STAT_isMacro
|
||||
|| nCurrentStatus == STAT_isREPTBlock)
|
||||
nLineNo++;
|
||||
|
||||
CurrentFlexHandle = pLastFile->FlexHandle;
|
||||
strcpy((char *)tzCurrentFileName, (char *)pLastFile->tzFileName);
|
||||
|
||||
switch (pLastFile->nStatus) {
|
||||
struct MacroArgs *args;
|
||||
|
||||
case STAT_isMacroArg:
|
||||
case STAT_isMacro:
|
||||
args = macro_GetCurrentArgs();
|
||||
if (nCurrentStatus == STAT_isMacro) {
|
||||
macro_FreeArgs(args);
|
||||
free(args);
|
||||
}
|
||||
macro_UseNewArgs(pLastFile->macroArgs);
|
||||
pCurrentMacro = pLastFile->pMacro;
|
||||
break;
|
||||
case STAT_isInclude:
|
||||
pCurrentFile = pLastFile->pFile;
|
||||
break;
|
||||
case STAT_isREPTBlock:
|
||||
args = macro_GetCurrentArgs();
|
||||
if (nCurrentStatus == STAT_isMacro) {
|
||||
macro_FreeArgs(args);
|
||||
free(args);
|
||||
}
|
||||
macro_UseNewArgs(pLastFile->macroArgs);
|
||||
pCurrentREPTBlock = pLastFile->pREPTBlock;
|
||||
nCurrentREPTBlockSize = pLastFile->nREPTBlockSize;
|
||||
nCurrentREPTBlockCount = pLastFile->nREPTBlockCount;
|
||||
nCurrentREPTBodyFirstLine = pLastFile->nREPTBodyFirstLine;
|
||||
break;
|
||||
default:
|
||||
fatalerror("%s: Internal error.", __func__);
|
||||
}
|
||||
macro_SetUniqueID(pLastFile->uniqueID);
|
||||
|
||||
nCurrentStatus = pLastFile->nStatus;
|
||||
|
||||
nFileStackDepth--;
|
||||
|
||||
free(*ppLastFile);
|
||||
*ppLastFile = NULL;
|
||||
yy_switch_to_buffer(CurrentFlexHandle);
|
||||
return 0;
|
||||
return name;
|
||||
}
|
||||
|
||||
int32_t fstk_GetLine(void)
|
||||
void fstk_Dump(struct FileStackNode const *node, uint32_t lineNo)
|
||||
{
|
||||
struct sContext *pLastFile, **ppLastFile;
|
||||
|
||||
switch (nCurrentStatus) {
|
||||
case STAT_isInclude:
|
||||
/* This is the normal mode, also used when including a file. */
|
||||
return nLineNo;
|
||||
case STAT_isMacro:
|
||||
break; /* Peek top file of the stack */
|
||||
case STAT_isMacroArg:
|
||||
return nLineNo; /* ??? */
|
||||
case STAT_isREPTBlock:
|
||||
break; /* Peek top file of the stack */
|
||||
default:
|
||||
fatalerror("%s: Internal error.", __func__);
|
||||
}
|
||||
|
||||
pLastFile = pFileStack;
|
||||
|
||||
if (pLastFile != NULL) {
|
||||
while (pLastFile->pNext) {
|
||||
ppLastFile = &(pLastFile->pNext);
|
||||
pLastFile = *ppLastFile;
|
||||
}
|
||||
return pLastFile->nLine;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is only reached if the lexer is in REPT or MACRO mode but there
|
||||
* are no saved contexts with the origin of said REPT or MACRO.
|
||||
*/
|
||||
fatalerror("%s: Internal error.", __func__);
|
||||
dumpNodeAndParents(node);
|
||||
fprintf(stderr, "(%" PRIu32 ")", lineNo);
|
||||
}
|
||||
|
||||
int yywrap(void)
|
||||
void fstk_DumpCurrent(void)
|
||||
{
|
||||
return popcontext();
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump the context stack to stderr
|
||||
*/
|
||||
void fstk_Dump(void)
|
||||
{
|
||||
const struct sContext *pLastFile;
|
||||
|
||||
pLastFile = pFileStack;
|
||||
|
||||
while (pLastFile) {
|
||||
fprintf(stderr, "%s(%" PRId32 ") -> ", pLastFile->tzFileName,
|
||||
pLastFile->nLine);
|
||||
pLastFile = pLastFile->pNext;
|
||||
if (!contextStack) {
|
||||
fputs("at top level", stderr);
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s(%" PRId32 ")", tzCurrentFileName, nLineNo);
|
||||
fstk_Dump(contextStack->fileInfo, lexer_GetLineNo());
|
||||
}
|
||||
|
||||
void fstk_DumpToStr(char *buf, size_t buflen)
|
||||
struct FileStackNode *fstk_GetFileStack(void)
|
||||
{
|
||||
const struct sContext *pLastFile = pFileStack;
|
||||
int retcode;
|
||||
size_t len = buflen;
|
||||
struct FileStackNode *node = contextStack->fileInfo;
|
||||
|
||||
while (pLastFile) {
|
||||
retcode = snprintf(&buf[buflen - len], len, "%s(%" PRId32 ") -> ",
|
||||
pLastFile->tzFileName, pLastFile->nLine);
|
||||
if (retcode < 0)
|
||||
fatalerror("Failed to dump file stack to string: %s",
|
||||
strerror(errno));
|
||||
else if (retcode >= len)
|
||||
len = 0;
|
||||
else
|
||||
len -= retcode;
|
||||
pLastFile = pLastFile->pNext;
|
||||
/* Mark node and all of its parents as referenced if not already so they don't get freed */
|
||||
while (node && !node->referenced) {
|
||||
node->ID = -1;
|
||||
node->referenced = true;
|
||||
node = node->parent;
|
||||
}
|
||||
|
||||
retcode = snprintf(&buf[buflen - len], len, "%s(%" PRId32 ")",
|
||||
tzCurrentFileName, nLineNo);
|
||||
if (retcode < 0)
|
||||
fatalerror("Failed to dump file stack to string: %s",
|
||||
strerror(errno));
|
||||
else if (retcode >= len)
|
||||
len = 0;
|
||||
else
|
||||
len -= retcode;
|
||||
|
||||
if (!len)
|
||||
warning(WARNING_LONG_STR, "File stack dump too long, got truncated");
|
||||
return contextStack->fileInfo;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump the string expansion stack to stderr
|
||||
*/
|
||||
void fstk_DumpStringExpansions(void)
|
||||
char const *fstk_GetFileName(void)
|
||||
{
|
||||
const struct sStringExpansionPos *pExpansion = pCurrentStringExpansion;
|
||||
/* Iterating via the nodes themselves skips nested REPTs */
|
||||
struct FileStackNode const *node = contextStack->fileInfo;
|
||||
|
||||
while (pExpansion) {
|
||||
fprintf(stderr, "while expanding symbol \"%s\"\n",
|
||||
pExpansion->tzName);
|
||||
pExpansion = pExpansion->pParent;
|
||||
while (node->type != NODE_FILE)
|
||||
node = node->parent;
|
||||
return ((struct FileStackNamedNode const *)node)->name;
|
||||
}
|
||||
|
||||
void fstk_AddIncludePath(char const *path)
|
||||
{
|
||||
if (path[0] == '\0')
|
||||
return;
|
||||
if (nbIncPaths >= MAXINCPATHS) {
|
||||
error("Too many include directories passed from command line\n");
|
||||
return;
|
||||
}
|
||||
size_t len = strlen(path);
|
||||
size_t allocSize = len + (path[len - 1] != '/') + 1;
|
||||
char *str = malloc(allocSize);
|
||||
|
||||
if (!str) {
|
||||
/* Attempt to continue without that path */
|
||||
error("Failed to allocate new include path: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
memcpy(str, path, len);
|
||||
char *end = str + len - 1;
|
||||
|
||||
if (*end++ != '/')
|
||||
*end++ = '/';
|
||||
*end = '\0';
|
||||
includePaths[nbIncPaths++] = str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extra includepath stuff
|
||||
*/
|
||||
void fstk_AddIncludePath(char *s)
|
||||
{
|
||||
if (NextIncPath == MAXINCPATHS)
|
||||
fatalerror("Too many include directories passed from command line");
|
||||
|
||||
// Find last occurrence of slash; is it at the end of the string?
|
||||
char const *lastSlash = strrchr(s, '/');
|
||||
char const *pattern = lastSlash && *(lastSlash + 1) == 0 ? "%s" : "%s/";
|
||||
|
||||
if (snprintf(IncludePaths[NextIncPath++], _MAX_PATH, pattern,
|
||||
s) >= _MAX_PATH)
|
||||
fatalerror("Include path too long '%s'", s);
|
||||
}
|
||||
|
||||
static void printdep(const char *fileName)
|
||||
static void printDep(char const *path)
|
||||
{
|
||||
if (dependfile) {
|
||||
fprintf(dependfile, "%s: %s\n", tzTargetFileName, fileName);
|
||||
fprintf(dependfile, "%s: %s\n", tzTargetFileName, path);
|
||||
if (oGeneratePhonyDeps)
|
||||
fprintf(dependfile, "%s:\n", fileName);
|
||||
fprintf(dependfile, "%s:\n", path);
|
||||
}
|
||||
}
|
||||
|
||||
static FILE *getFile(char const *pathname)
|
||||
static bool isPathValid(char const *path)
|
||||
{
|
||||
struct stat statbuf;
|
||||
|
||||
if (stat(pathname, &statbuf) != 0)
|
||||
return NULL;
|
||||
if (stat(path, &statbuf) != 0)
|
||||
return false;
|
||||
|
||||
/* Reject directories */
|
||||
if (S_ISDIR(statbuf.st_mode))
|
||||
return NULL;
|
||||
|
||||
return fopen(pathname, "rb");
|
||||
return !S_ISDIR(statbuf.st_mode);
|
||||
}
|
||||
|
||||
FILE *fstk_FindFile(char const *fname, char **incPathUsed)
|
||||
bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
|
||||
{
|
||||
if (fname == NULL)
|
||||
return NULL;
|
||||
|
||||
char path[_MAX_PATH];
|
||||
FILE *f = getFile(fname);
|
||||
|
||||
if (f) {
|
||||
printdep(fname);
|
||||
return f;
|
||||
if (!*size) {
|
||||
*size = 64; /* This is arbitrary, really */
|
||||
*fullPath = realloc(*fullPath, *size);
|
||||
if (!*fullPath)
|
||||
error("realloc error during include path search: %s\n",
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < NextIncPath; ++i) {
|
||||
/*
|
||||
* The function snprintf() does not write more than `size` bytes
|
||||
* (including the terminating null byte ('\0')). If the output
|
||||
* was truncated due to this limit, the return value is the
|
||||
* number of characters (excluding the terminating null byte)
|
||||
* which would have been written to the final string if enough
|
||||
* space had been available. Thus, a return value of `size` or
|
||||
* more means that the output was truncated.
|
||||
*/
|
||||
int fullpathlen = snprintf(path, sizeof(path), "%s%s",
|
||||
IncludePaths[i], fname);
|
||||
if (*fullPath) {
|
||||
for (size_t i = 0; i <= nbIncPaths; ++i) {
|
||||
char const *incPath = i ? includePaths[i - 1] : "";
|
||||
int len = snprintf(*fullPath, *size, "%s%s", incPath, path);
|
||||
|
||||
if (fullpathlen >= (int)sizeof(path))
|
||||
continue;
|
||||
/* Oh how I wish `asnprintf` was standard... */
|
||||
if (len >= *size) { /* `len` doesn't include the terminator, `size` does */
|
||||
*size = len + 1;
|
||||
*fullPath = realloc(*fullPath, *size);
|
||||
if (!*fullPath) {
|
||||
error("realloc error during include path search: %s\n",
|
||||
strerror(errno));
|
||||
break;
|
||||
}
|
||||
len = sprintf(*fullPath, "%s%s", incPath, path);
|
||||
}
|
||||
|
||||
f = getFile(path);
|
||||
if (f) {
|
||||
printdep(path);
|
||||
|
||||
if (incPathUsed)
|
||||
*incPathUsed = IncludePaths[i];
|
||||
return f;
|
||||
if (len < 0) {
|
||||
error("snprintf error during include path search: %s\n",
|
||||
strerror(errno));
|
||||
} else if (isPathValid(*fullPath)) {
|
||||
printDep(*fullPath);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
errno = ENOENT;
|
||||
if (oGeneratedMissingIncludes)
|
||||
printdep(fname);
|
||||
return NULL;
|
||||
printDep(path);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up an include file for parsing
|
||||
*/
|
||||
void fstk_RunInclude(char *tzFileName)
|
||||
bool yywrap(void)
|
||||
{
|
||||
char *incPathUsed = "";
|
||||
FILE *f = fstk_FindFile(tzFileName, &incPathUsed);
|
||||
if (contextStack->fileInfo->type == NODE_REPT) { /* The context is a REPT block, which may loop */
|
||||
struct FileStackReptNode *fileInfo = (struct FileStackReptNode *)contextStack->fileInfo;
|
||||
|
||||
if (f == NULL) {
|
||||
if (oGeneratedMissingIncludes) {
|
||||
oFailedOnMissingInclude = true;
|
||||
return;
|
||||
/* If the node is referenced, we can't edit it; duplicate it */
|
||||
if (contextStack->fileInfo->referenced) {
|
||||
size_t size = sizeof(*fileInfo) + sizeof(fileInfo->iters[0]) * fileInfo->reptDepth;
|
||||
struct FileStackReptNode *copy = malloc(size);
|
||||
|
||||
if (!copy)
|
||||
fatalerror("Failed to duplicate REPT file node: %s\n", strerror(errno));
|
||||
/* Copy all info but the referencing */
|
||||
memcpy(copy, fileInfo, size);
|
||||
copy->node.next = NULL;
|
||||
copy->node.referenced = false;
|
||||
|
||||
fileInfo = copy;
|
||||
contextStack->fileInfo = (struct FileStackNode *)fileInfo;
|
||||
}
|
||||
yyerror("Unable to open included file '%s': %s", tzFileName,
|
||||
strerror(errno));
|
||||
return;
|
||||
|
||||
fileInfo->iters[0]++;
|
||||
/* If this wasn't the last iteration, wrap instead of popping */
|
||||
if (fileInfo->iters[0] <= contextStack->nbReptIters) {
|
||||
lexer_RestartRept(contextStack->fileInfo->lineNo);
|
||||
contextStack->uniqueID = macro_UseNewUniqueID();
|
||||
return false;
|
||||
}
|
||||
} else if (!contextStack->parent) {
|
||||
return true;
|
||||
}
|
||||
dbgPrint("Popping context\n");
|
||||
|
||||
pushcontext();
|
||||
nLineNo = 1;
|
||||
nCurrentStatus = STAT_isInclude;
|
||||
snprintf(tzCurrentFileName, sizeof(tzCurrentFileName), "%s%s",
|
||||
incPathUsed, tzFileName);
|
||||
if (verbose)
|
||||
printf("Assembling %s\n", tzCurrentFileName);
|
||||
pCurrentFile = f;
|
||||
CurrentFlexHandle = yy_create_buffer(pCurrentFile);
|
||||
yy_switch_to_buffer(CurrentFlexHandle);
|
||||
struct Context *context = contextStack;
|
||||
|
||||
/* Dirty hack to give the INCLUDE directive a linefeed */
|
||||
contextStack = contextStack->parent;
|
||||
contextDepth--;
|
||||
|
||||
yyunput('\n');
|
||||
nLineNo--;
|
||||
lexer_DeleteState(context->lexerState);
|
||||
/* Restore args if a macro (not REPT) saved them */
|
||||
if (context->fileInfo->type == NODE_MACRO) {
|
||||
dbgPrint("Restoring macro args %p\n", contextStack->macroArgs);
|
||||
macro_UseNewArgs(contextStack->macroArgs);
|
||||
}
|
||||
/* Free the file stack node */
|
||||
if (!context->fileInfo->referenced)
|
||||
free(context->fileInfo);
|
||||
/* Free the entry and make its parent the current entry */
|
||||
free(context);
|
||||
|
||||
lexer_SetState(contextStack->lexerState);
|
||||
macro_SetUniqueID(contextStack->uniqueID);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up a macro for parsing
|
||||
* Make sure not to switch the lexer state before calling this, so the saved line no is correct
|
||||
* BE CAREFUL!! This modifies the file stack directly, you should have set up the file info first
|
||||
*/
|
||||
void fstk_RunMacro(char *s, struct MacroArgs *args)
|
||||
static void newContext(struct FileStackNode *fileInfo)
|
||||
{
|
||||
struct Symbol const *sym = sym_FindSymbol(s);
|
||||
int nPrintedChars;
|
||||
if (++contextDepth >= nMaxRecursionDepth)
|
||||
fatalerror("Recursion limit (%zu) exceeded\n", nMaxRecursionDepth);
|
||||
struct Context *context = malloc(sizeof(*context));
|
||||
|
||||
if (sym == NULL) {
|
||||
yyerror("Macro \"%s\" not defined", s);
|
||||
if (!context)
|
||||
fatalerror("Failed to allocate memory for new context: %s\n", strerror(errno));
|
||||
fileInfo->parent = contextStack->fileInfo;
|
||||
fileInfo->lineNo = 0; /* Init to a default value, see struct definition for info */
|
||||
fileInfo->referenced = false;
|
||||
fileInfo->lineNo = lexer_GetLineNo();
|
||||
context->fileInfo = fileInfo;
|
||||
/*
|
||||
* Link new entry to its parent so it's reachable later
|
||||
* ERRORS SHOULD NOT OCCUR AFTER THIS!!
|
||||
*/
|
||||
context->parent = contextStack;
|
||||
contextStack = context;
|
||||
|
||||
}
|
||||
|
||||
void fstk_RunInclude(char const *path)
|
||||
{
|
||||
dbgPrint("Including path \"%s\"\n", path);
|
||||
|
||||
char *fullPath = NULL;
|
||||
size_t size = 0;
|
||||
|
||||
if (!fstk_FindFile(path, &fullPath, &size)) {
|
||||
free(fullPath);
|
||||
if (oGeneratedMissingIncludes)
|
||||
oFailedOnMissingInclude = true;
|
||||
else
|
||||
error("Unable to open included file '%s': %s\n", path, strerror(errno));
|
||||
return;
|
||||
}
|
||||
if (sym->type != SYM_MACRO) {
|
||||
yyerror("\"%s\" is not a macro", s);
|
||||
dbgPrint("Full path: \"%s\"\n", fullPath);
|
||||
|
||||
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + size);
|
||||
|
||||
if (!fileInfo) {
|
||||
error("Failed to alloc file info for INCLUDE: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
fileInfo->node.type = NODE_FILE;
|
||||
strcpy(fileInfo->name, fullPath);
|
||||
free(fullPath);
|
||||
|
||||
pushcontext();
|
||||
macro_SetUniqueID(nMacroCount++);
|
||||
/* Minus 1 because there is a newline at the beginning of the buffer */
|
||||
nLineNo = sym->fileLine - 1;
|
||||
newContext((struct FileStackNode *)fileInfo);
|
||||
contextStack->lexerState = lexer_OpenFile(fileInfo->name);
|
||||
if (!contextStack->lexerState)
|
||||
fatalerror("Failed to set up lexer for file include\n");
|
||||
lexer_SetStateAtEOL(contextStack->lexerState);
|
||||
/* We're back at top-level, so most things are reset */
|
||||
contextStack->uniqueID = 0;
|
||||
macro_SetUniqueID(0);
|
||||
}
|
||||
|
||||
void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
|
||||
{
|
||||
dbgPrint("Running macro \"%s\"\n", macroName);
|
||||
|
||||
struct Symbol *macro = sym_FindExactSymbol(macroName);
|
||||
|
||||
if (!macro) {
|
||||
error("Macro \"%s\" not defined\n", macroName);
|
||||
return;
|
||||
}
|
||||
if (macro->type != SYM_MACRO) {
|
||||
error("\"%s\" is not a macro\n", macroName);
|
||||
return;
|
||||
}
|
||||
contextStack->macroArgs = macro_GetCurrentArgs();
|
||||
|
||||
/* Compute total length of this node's name: <base name>::<macro> */
|
||||
size_t reptNameLen = 0;
|
||||
struct FileStackNode const *node = macro->src;
|
||||
|
||||
if (node->type == NODE_REPT) {
|
||||
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
|
||||
|
||||
/* 4294967295 = 2^32 - 1, aka UINT32_MAX */
|
||||
reptNameLen += reptNode->reptDepth * strlen("::REPT~4294967295");
|
||||
/* Look for next named node */
|
||||
do {
|
||||
node = node->parent;
|
||||
} while (node->type == NODE_REPT);
|
||||
}
|
||||
struct FileStackNamedNode const *baseNode = (struct FileStackNamedNode const *)node;
|
||||
size_t baseLen = strlen(baseNode->name);
|
||||
size_t macroNameLen = strlen(macro->name);
|
||||
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + baseLen
|
||||
+ reptNameLen + 2 + macroNameLen + 1);
|
||||
|
||||
if (!fileInfo) {
|
||||
error("Failed to alloc file info for \"%s\": %s\n", macro->name, strerror(errno));
|
||||
return;
|
||||
}
|
||||
fileInfo->node.type = NODE_MACRO;
|
||||
/* Print the name... */
|
||||
char *dest = fileInfo->name;
|
||||
|
||||
memcpy(dest, baseNode->name, baseLen);
|
||||
dest += baseLen;
|
||||
if (node->type == NODE_REPT) {
|
||||
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
|
||||
|
||||
for (uint32_t i = reptNode->reptDepth; i--; ) {
|
||||
int nbChars = sprintf(dest, "::REPT~%" PRIu32, reptNode->iters[i]);
|
||||
|
||||
if (nbChars < 0)
|
||||
fatalerror("Failed to write macro invocation info: %s\n",
|
||||
strerror(errno));
|
||||
dest += nbChars;
|
||||
}
|
||||
}
|
||||
*dest++ = ':';
|
||||
*dest++ = ':';
|
||||
memcpy(dest, macro->name, macroNameLen + 1);
|
||||
|
||||
newContext((struct FileStackNode *)fileInfo);
|
||||
/* Line minus 1 because buffer begins with a newline */
|
||||
contextStack->lexerState = lexer_OpenFileView(macro->macro, macro->macroSize,
|
||||
macro->fileLine - 1);
|
||||
if (!contextStack->lexerState)
|
||||
fatalerror("Failed to set up lexer for macro invocation\n");
|
||||
lexer_SetStateAtEOL(contextStack->lexerState);
|
||||
contextStack->uniqueID = macro_UseNewUniqueID();
|
||||
macro_UseNewArgs(args);
|
||||
nCurrentStatus = STAT_isMacro;
|
||||
nPrintedChars = snprintf(tzCurrentFileName, _MAX_PATH + 1,
|
||||
"%s::%s", sym->fileName, s);
|
||||
if (nPrintedChars > _MAX_PATH) {
|
||||
popcontext();
|
||||
fatalerror("File name + macro name is too large to fit into buffer");
|
||||
}
|
||||
|
||||
pCurrentMacro = sym;
|
||||
/* TODO: why is `strlen` being used when there's a macro size field? */
|
||||
CurrentFlexHandle = yy_scan_bytes(pCurrentMacro->macro,
|
||||
strlen(pCurrentMacro->macro));
|
||||
yy_switch_to_buffer(CurrentFlexHandle);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up a repeat block for parsing
|
||||
*/
|
||||
void fstk_RunRept(uint32_t count, int32_t nReptLineNo)
|
||||
void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size)
|
||||
{
|
||||
if (count) {
|
||||
static const char *tzReptStr = "::REPT~1";
|
||||
dbgPrint("Running REPT(%" PRIu32 ")\n", count);
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
/* For error printing to make sense, fake nLineNo */
|
||||
nCurrentREPTBodyLastLine = nLineNo;
|
||||
nLineNo = nReptLineNo;
|
||||
pushcontext();
|
||||
macro_SetUniqueID(nMacroCount++);
|
||||
nCurrentREPTBlockCount = count;
|
||||
nCurrentStatus = STAT_isREPTBlock;
|
||||
nCurrentREPTBlockSize = ulNewMacroSize;
|
||||
pCurrentREPTBlock = tzNewMacro;
|
||||
nCurrentREPTBodyFirstLine = nReptLineNo + 1;
|
||||
nLineNo = nReptLineNo;
|
||||
uint32_t reptDepth = contextStack->fileInfo->type == NODE_REPT
|
||||
? ((struct FileStackReptNode *)contextStack->fileInfo)->reptDepth
|
||||
: 0;
|
||||
struct FileStackReptNode *fileInfo = malloc(sizeof(*fileInfo)
|
||||
+ (reptDepth + 1) * sizeof(fileInfo->iters[0]));
|
||||
|
||||
if (strlen(tzCurrentFileName) + strlen(tzReptStr) > _MAX_PATH)
|
||||
fatalerror("Cannot append \"%s\" to file path",
|
||||
tzReptStr);
|
||||
strcat(tzCurrentFileName, tzReptStr);
|
||||
|
||||
CurrentFlexHandle =
|
||||
yy_scan_bytes(pCurrentREPTBlock, nCurrentREPTBlockSize);
|
||||
yy_switch_to_buffer(CurrentFlexHandle);
|
||||
if (!fileInfo) {
|
||||
error("Failed to alloc file info for REPT: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
fileInfo->node.type = NODE_REPT;
|
||||
fileInfo->reptDepth = reptDepth + 1;
|
||||
fileInfo->iters[0] = 1;
|
||||
if (reptDepth)
|
||||
/* Copy all parent iter counts */
|
||||
memcpy(&fileInfo->iters[1],
|
||||
((struct FileStackReptNode *)contextStack->fileInfo)->iters,
|
||||
reptDepth * sizeof(fileInfo->iters[0]));
|
||||
|
||||
newContext((struct FileStackNode *)fileInfo);
|
||||
/* Correct our line number, which currently points to the `ENDR` line */
|
||||
contextStack->fileInfo->lineNo = reptLineNo;
|
||||
|
||||
contextStack->lexerState = lexer_OpenFileView(body, size, reptLineNo);
|
||||
if (!contextStack->lexerState)
|
||||
fatalerror("Failed to set up lexer for rept block\n");
|
||||
lexer_SetStateAtEOL(contextStack->lexerState);
|
||||
contextStack->uniqueID = macro_UseNewUniqueID();
|
||||
contextStack->nbReptIters = count;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the filestack routines
|
||||
*/
|
||||
void fstk_Init(char *pFileName)
|
||||
void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
|
||||
{
|
||||
char tzSymFileName[_MAX_PATH + 1 + 2];
|
||||
struct LexerState *state = lexer_OpenFile(mainPath);
|
||||
|
||||
snprintf(tzSymFileName, sizeof(tzSymFileName), "\"%s\"", pFileName);
|
||||
sym_AddString("__FILE__", tzSymFileName);
|
||||
if (!state)
|
||||
fatalerror("Failed to open main file!\n");
|
||||
lexer_SetState(state);
|
||||
char const *fileName = lexer_GetFileName();
|
||||
size_t len = strlen(fileName);
|
||||
struct Context *context = malloc(sizeof(*contextStack));
|
||||
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + len + 1);
|
||||
|
||||
pFileStack = NULL;
|
||||
if (strcmp(pFileName, "-") == 0) {
|
||||
pCurrentFile = stdin;
|
||||
if (!context)
|
||||
fatalerror("Failed to allocate memory for main context: %s\n", strerror(errno));
|
||||
if (!fileInfo)
|
||||
fatalerror("Failed to allocate memory for main file info: %s\n", strerror(errno));
|
||||
|
||||
context->fileInfo = (struct FileStackNode *)fileInfo;
|
||||
/* lineNo and reptIter are unused on the top-level context */
|
||||
context->fileInfo->parent = NULL;
|
||||
context->fileInfo->referenced = false;
|
||||
context->fileInfo->type = NODE_FILE;
|
||||
memcpy(fileInfo->name, fileName, len + 1);
|
||||
|
||||
context->parent = NULL;
|
||||
context->lexerState = state;
|
||||
context->uniqueID = 0;
|
||||
macro_SetUniqueID(0);
|
||||
context->nbReptIters = 0;
|
||||
|
||||
/* Now that it's set up properly, register the context */
|
||||
contextStack = context;
|
||||
|
||||
/*
|
||||
* Check that max recursion depth won't allow overflowing node `malloc`s
|
||||
* This assumes that the rept node is larger
|
||||
*/
|
||||
#define DEPTH_LIMIT ((SIZE_MAX - sizeof(struct FileStackReptNode)) / sizeof(uint32_t))
|
||||
if (maxRecursionDepth > DEPTH_LIMIT) {
|
||||
error("Recursion depth may not be higher than %zu, defaulting to "
|
||||
EXPAND_AND_STR(DEFAULT_MAX_DEPTH) "\n", DEPTH_LIMIT);
|
||||
nMaxRecursionDepth = DEFAULT_MAX_DEPTH;
|
||||
} else {
|
||||
pCurrentFile = fopen(pFileName, "rb");
|
||||
if (pCurrentFile == NULL)
|
||||
fatalerror("Unable to open file '%s': %s", pFileName,
|
||||
strerror(errno));
|
||||
nMaxRecursionDepth = maxRecursionDepth;
|
||||
}
|
||||
nFileStackDepth = 0;
|
||||
|
||||
nMacroCount = 0;
|
||||
nCurrentStatus = STAT_isInclude;
|
||||
snprintf(tzCurrentFileName, _MAX_PATH + 1, "%s", pFileName);
|
||||
CurrentFlexHandle = yy_create_buffer(pCurrentFile);
|
||||
yy_switch_to_buffer(CurrentFlexHandle);
|
||||
nLineNo = 1;
|
||||
/* Make sure that the default of 64 is OK, though */
|
||||
assert(DEPTH_LIMIT >= DEFAULT_MAX_DEPTH);
|
||||
#undef DEPTH_LIMIT
|
||||
}
|
||||
|
||||
@@ -1,704 +0,0 @@
|
||||
/*
|
||||
* This file is part of RGBDS.
|
||||
*
|
||||
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "asm/asm.h"
|
||||
#include "asm/lexer.h"
|
||||
#include "asm/macro.h"
|
||||
#include "asm/main.h"
|
||||
#include "asm/rpn.h"
|
||||
#include "asm/section.h"
|
||||
#include "asm/warning.h"
|
||||
|
||||
#include "helpers.h"
|
||||
|
||||
#include "asmy.h"
|
||||
|
||||
bool oDontExpandStrings;
|
||||
int32_t nGBGfxID = -1;
|
||||
int32_t nBinaryID = -1;
|
||||
|
||||
static int32_t gbgfx2bin(char ch)
|
||||
{
|
||||
int32_t i;
|
||||
|
||||
for (i = 0; i <= 3; i++) {
|
||||
if (CurrentOptions.gbgfx[i] == ch)
|
||||
return i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t binary2bin(char ch)
|
||||
{
|
||||
int32_t i;
|
||||
|
||||
for (i = 0; i <= 1; i++) {
|
||||
if (CurrentOptions.binary[i] == ch)
|
||||
return i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t char2bin(char ch)
|
||||
{
|
||||
if (ch >= 'a' && ch <= 'f')
|
||||
return (ch - 'a' + 10);
|
||||
|
||||
if (ch >= 'A' && ch <= 'F')
|
||||
return (ch - 'A' + 10);
|
||||
|
||||
if (ch >= '0' && ch <= '9')
|
||||
return (ch - '0');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef int32_t(*x2bin) (char ch);
|
||||
|
||||
static int32_t ascii2bin(char *s)
|
||||
{
|
||||
char *start = s;
|
||||
uint32_t radix = 10;
|
||||
uint32_t result = 0;
|
||||
x2bin convertfunc = char2bin;
|
||||
|
||||
switch (*s) {
|
||||
case '$':
|
||||
radix = 16;
|
||||
s++;
|
||||
convertfunc = char2bin;
|
||||
break;
|
||||
case '&':
|
||||
radix = 8;
|
||||
s++;
|
||||
convertfunc = char2bin;
|
||||
break;
|
||||
case '`':
|
||||
radix = 4;
|
||||
s++;
|
||||
convertfunc = gbgfx2bin;
|
||||
break;
|
||||
case '%':
|
||||
radix = 2;
|
||||
s++;
|
||||
convertfunc = binary2bin;
|
||||
break;
|
||||
default:
|
||||
/* Handle below */
|
||||
break;
|
||||
}
|
||||
|
||||
const uint32_t max_q = UINT32_MAX / radix;
|
||||
const uint32_t max_r = UINT32_MAX % radix;
|
||||
|
||||
if (*s == '\0') {
|
||||
/*
|
||||
* There are no digits after the radix prefix
|
||||
* (or the string is empty, which shouldn't happen).
|
||||
*/
|
||||
yyerror("Invalid integer constant");
|
||||
} else if (radix == 4) {
|
||||
int32_t size = 0;
|
||||
int32_t c;
|
||||
|
||||
while (*s != '\0') {
|
||||
c = convertfunc(*s++);
|
||||
result = result * 2 + ((c & 2) << 7) + (c & 1);
|
||||
size++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extending a graphics constant longer than 8 pixels,
|
||||
* the Game Boy tile width, produces a nonsensical result.
|
||||
*/
|
||||
if (size > 8) {
|
||||
warning(WARNING_LARGE_CONSTANT, "Graphics constant '%s' is too long",
|
||||
start);
|
||||
}
|
||||
} else {
|
||||
bool overflow = false;
|
||||
|
||||
while (*s != '\0') {
|
||||
int32_t digit = convertfunc(*s++);
|
||||
|
||||
if (result > max_q
|
||||
|| (result == max_q && digit > max_r)) {
|
||||
overflow = true;
|
||||
}
|
||||
result = result * radix + digit;
|
||||
}
|
||||
|
||||
if (overflow)
|
||||
warning(WARNING_LARGE_CONSTANT, "Integer constant '%s' is too large",
|
||||
start);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t ParseFixedPoint(char *s, uint32_t size)
|
||||
{
|
||||
uint32_t i;
|
||||
uint32_t dot = 0;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (s[i] == '.') {
|
||||
dot++;
|
||||
|
||||
if (dot == 2)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
yyskipbytes(i);
|
||||
|
||||
yylval.nConstValue = (int32_t)(atof(s) * 65536);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t ParseNumber(char *s, uint32_t size)
|
||||
{
|
||||
char dest[256];
|
||||
|
||||
if (size > 255)
|
||||
fatalerror("Number token too long");
|
||||
|
||||
strncpy(dest, s, size);
|
||||
dest[size] = 0;
|
||||
yylval.nConstValue = ascii2bin(dest);
|
||||
|
||||
yyskipbytes(size);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the symbol name ends before the end of the macro arg,
|
||||
* return a pointer to the rest of the macro arg.
|
||||
* Otherwise, return NULL.
|
||||
*/
|
||||
char const *AppendMacroArg(char whichArg, char *dest, size_t *destIndex)
|
||||
{
|
||||
char const *marg;
|
||||
|
||||
if (whichArg == '@')
|
||||
marg = macro_GetUniqueIDStr();
|
||||
else if (whichArg >= '1' && whichArg <= '9')
|
||||
marg = macro_GetArg(whichArg - '0');
|
||||
else
|
||||
fatalerror("Invalid macro argument '\\%c' in symbol", whichArg);
|
||||
|
||||
if (!marg)
|
||||
fatalerror("Macro argument '\\%c' not defined", whichArg);
|
||||
|
||||
char ch;
|
||||
|
||||
while ((ch = *marg) != 0) {
|
||||
if ((ch >= 'a' && ch <= 'z')
|
||||
|| (ch >= 'A' && ch <= 'Z')
|
||||
|| (ch >= '0' && ch <= '9')
|
||||
|| ch == '_'
|
||||
|| ch == '@'
|
||||
|| ch == '#'
|
||||
|| ch == '.') {
|
||||
if (*destIndex >= MAXSYMLEN)
|
||||
fatalerror("Symbol too long");
|
||||
|
||||
dest[*destIndex] = ch;
|
||||
(*destIndex)++;
|
||||
} else {
|
||||
return marg;
|
||||
}
|
||||
|
||||
marg++;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t ParseSymbol(char *src, uint32_t size)
|
||||
{
|
||||
char dest[MAXSYMLEN + 1];
|
||||
size_t srcIndex = 0;
|
||||
size_t destIndex = 0;
|
||||
char const *rest = NULL;
|
||||
|
||||
while (srcIndex < size) {
|
||||
char ch = src[srcIndex++];
|
||||
|
||||
if (ch == '\\') {
|
||||
/*
|
||||
* We don't check if srcIndex is still less than size,
|
||||
* but that can only fail to be true when the
|
||||
* following char is neither '@' nor a digit.
|
||||
* In that case, AppendMacroArg() will catch the error.
|
||||
*/
|
||||
ch = src[srcIndex++];
|
||||
|
||||
rest = AppendMacroArg(ch, dest, &destIndex);
|
||||
/* If the symbol's end was in the middle of the token */
|
||||
if (rest)
|
||||
break;
|
||||
} else {
|
||||
if (destIndex >= MAXSYMLEN)
|
||||
fatalerror("Symbol too long");
|
||||
dest[destIndex++] = ch;
|
||||
}
|
||||
}
|
||||
|
||||
dest[destIndex] = 0;
|
||||
|
||||
/* Tell the lexer we read all bytes that we did */
|
||||
yyskipbytes(srcIndex);
|
||||
|
||||
/*
|
||||
* If an escape's expansion left some chars after the symbol's end,
|
||||
* such as the `::` in a `Backup\1` expanded to `BackupCamX::`,
|
||||
* put those into the buffer.
|
||||
* Note that this NEEDS to be done after the `yyskipbytes` above.
|
||||
*/
|
||||
if (rest)
|
||||
yyunputstr(rest);
|
||||
|
||||
/* If the symbol is an EQUS, expand it */
|
||||
if (!oDontExpandStrings) {
|
||||
struct Symbol const *sym = sym_FindSymbol(dest);
|
||||
|
||||
if (sym && sym->type == SYM_EQUS) {
|
||||
char const *s;
|
||||
|
||||
lex_BeginStringExpansion(dest);
|
||||
|
||||
/* Feed the symbol's contents into the buffer */
|
||||
yyunputstr(s = sym_GetStringValue(sym));
|
||||
|
||||
/* Lines inserted this way shall not increase nLineNo */
|
||||
while (*s) {
|
||||
if (*s++ == '\n')
|
||||
nLineNo--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
strcpy(yylval.tzSym, dest);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t PutMacroArg(char *src, uint32_t size)
|
||||
{
|
||||
char const *s;
|
||||
|
||||
yyskipbytes(size);
|
||||
if ((size == 2 && src[1] >= '1' && src[1] <= '9')) {
|
||||
s = macro_GetArg(src[1] - '0');
|
||||
|
||||
if (s != NULL)
|
||||
yyunputstr(s);
|
||||
else
|
||||
yyerror("Macro argument '\\%c' not defined", src[1]);
|
||||
} else {
|
||||
yyerror("Invalid macro argument '\\%c'", src[1]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t PutUniqueID(char *src, uint32_t size)
|
||||
{
|
||||
(void)src;
|
||||
char const *s;
|
||||
|
||||
yyskipbytes(size);
|
||||
|
||||
s = macro_GetUniqueIDStr();
|
||||
|
||||
if (s != NULL)
|
||||
yyunputstr(s);
|
||||
else
|
||||
yyerror("Macro unique label string not defined");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum {
|
||||
T_LEX_MACROARG = 3000,
|
||||
T_LEX_MACROUNIQUE
|
||||
};
|
||||
|
||||
const struct sLexInitString lexer_strings[] = {
|
||||
{"adc", T_Z80_ADC},
|
||||
{"add", T_Z80_ADD},
|
||||
{"and", T_Z80_AND},
|
||||
{"bit", T_Z80_BIT},
|
||||
{"call", T_Z80_CALL},
|
||||
{"ccf", T_Z80_CCF},
|
||||
{"cpl", T_Z80_CPL},
|
||||
{"cp", T_Z80_CP},
|
||||
{"daa", T_Z80_DAA},
|
||||
{"dec", T_Z80_DEC},
|
||||
{"di", T_Z80_DI},
|
||||
{"ei", T_Z80_EI},
|
||||
{"halt", T_Z80_HALT},
|
||||
{"inc", T_Z80_INC},
|
||||
{"jp", T_Z80_JP},
|
||||
{"jr", T_Z80_JR},
|
||||
{"ld", T_Z80_LD},
|
||||
{"ldi", T_Z80_LDI},
|
||||
{"ldd", T_Z80_LDD},
|
||||
{"ldio", T_Z80_LDIO},
|
||||
{"ldh", T_Z80_LDIO},
|
||||
{"nop", T_Z80_NOP},
|
||||
{"or", T_Z80_OR},
|
||||
{"pop", T_Z80_POP},
|
||||
{"push", T_Z80_PUSH},
|
||||
{"res", T_Z80_RES},
|
||||
{"reti", T_Z80_RETI},
|
||||
{"ret", T_Z80_RET},
|
||||
{"rlca", T_Z80_RLCA},
|
||||
{"rlc", T_Z80_RLC},
|
||||
{"rla", T_Z80_RLA},
|
||||
{"rl", T_Z80_RL},
|
||||
{"rrc", T_Z80_RRC},
|
||||
{"rrca", T_Z80_RRCA},
|
||||
{"rra", T_Z80_RRA},
|
||||
{"rr", T_Z80_RR},
|
||||
{"rst", T_Z80_RST},
|
||||
{"sbc", T_Z80_SBC},
|
||||
{"scf", T_Z80_SCF},
|
||||
{"set", T_POP_SET},
|
||||
{"sla", T_Z80_SLA},
|
||||
{"sra", T_Z80_SRA},
|
||||
{"srl", T_Z80_SRL},
|
||||
{"stop", T_Z80_STOP},
|
||||
{"sub", T_Z80_SUB},
|
||||
{"swap", T_Z80_SWAP},
|
||||
{"xor", T_Z80_XOR},
|
||||
|
||||
{"nz", T_CC_NZ},
|
||||
{"z", T_CC_Z},
|
||||
{"nc", T_CC_NC},
|
||||
/* Handled in list of registers */
|
||||
/* { "c", T_TOKEN_C }, */
|
||||
|
||||
{"[bc]", T_MODE_BC_IND},
|
||||
{"[de]", T_MODE_DE_IND},
|
||||
{"[hl]", T_MODE_HL_IND},
|
||||
{"[hl+]", T_MODE_HL_INDINC},
|
||||
{"[hl-]", T_MODE_HL_INDDEC},
|
||||
{"[hli]", T_MODE_HL_INDINC},
|
||||
{"[hld]", T_MODE_HL_INDDEC},
|
||||
{"af", T_MODE_AF},
|
||||
{"bc", T_MODE_BC},
|
||||
{"de", T_MODE_DE},
|
||||
{"hl", T_MODE_HL},
|
||||
{"sp", T_MODE_SP},
|
||||
{"[c]", T_MODE_C_IND},
|
||||
{"[$ff00+c]", T_MODE_C_IND},
|
||||
{"[$ff00 + c]", T_MODE_C_IND},
|
||||
|
||||
{"a", T_TOKEN_A},
|
||||
{"b", T_TOKEN_B},
|
||||
{"c", T_TOKEN_C},
|
||||
{"d", T_TOKEN_D},
|
||||
{"e", T_TOKEN_E},
|
||||
{"h", T_TOKEN_H},
|
||||
{"l", T_TOKEN_L},
|
||||
|
||||
{"||", T_OP_LOGICOR},
|
||||
{"&&", T_OP_LOGICAND},
|
||||
{"==", T_OP_LOGICEQU},
|
||||
{">", T_OP_LOGICGT},
|
||||
{"<", T_OP_LOGICLT},
|
||||
{">=", T_OP_LOGICGE},
|
||||
{"<=", T_OP_LOGICLE},
|
||||
{"!=", T_OP_LOGICNE},
|
||||
{"!", T_OP_LOGICNOT},
|
||||
{"|", T_OP_OR},
|
||||
{"^", T_OP_XOR},
|
||||
{"&", T_OP_AND},
|
||||
{"<<", T_OP_SHL},
|
||||
{">>", T_OP_SHR},
|
||||
{"+", T_OP_ADD},
|
||||
{"-", T_OP_SUB},
|
||||
{"*", T_OP_MUL},
|
||||
{"/", T_OP_DIV},
|
||||
{"%", T_OP_MOD},
|
||||
{"~", T_OP_NOT},
|
||||
|
||||
{"def", T_OP_DEF},
|
||||
|
||||
{"fragment", T_POP_FRAGMENT},
|
||||
{"bank", T_OP_BANK},
|
||||
{"align", T_OP_ALIGN},
|
||||
|
||||
{"round", T_OP_ROUND},
|
||||
{"ceil", T_OP_CEIL},
|
||||
{"floor", T_OP_FLOOR},
|
||||
{"div", T_OP_FDIV},
|
||||
{"mul", T_OP_FMUL},
|
||||
{"sin", T_OP_SIN},
|
||||
{"cos", T_OP_COS},
|
||||
{"tan", T_OP_TAN},
|
||||
{"asin", T_OP_ASIN},
|
||||
{"acos", T_OP_ACOS},
|
||||
{"atan", T_OP_ATAN},
|
||||
{"atan2", T_OP_ATAN2},
|
||||
|
||||
{"high", T_OP_HIGH},
|
||||
{"low", T_OP_LOW},
|
||||
{"isconst", T_OP_ISCONST},
|
||||
|
||||
{"strcmp", T_OP_STRCMP},
|
||||
{"strin", T_OP_STRIN},
|
||||
{"strsub", T_OP_STRSUB},
|
||||
{"strlen", T_OP_STRLEN},
|
||||
{"strcat", T_OP_STRCAT},
|
||||
{"strupr", T_OP_STRUPR},
|
||||
{"strlwr", T_OP_STRLWR},
|
||||
|
||||
{"include", T_POP_INCLUDE},
|
||||
{"printt", T_POP_PRINTT},
|
||||
{"printi", T_POP_PRINTI},
|
||||
{"printv", T_POP_PRINTV},
|
||||
{"printf", T_POP_PRINTF},
|
||||
{"export", T_POP_EXPORT},
|
||||
{"xdef", T_POP_XDEF},
|
||||
{"global", T_POP_GLOBAL},
|
||||
{"ds", T_POP_DS},
|
||||
{"db", T_POP_DB},
|
||||
{"dw", T_POP_DW},
|
||||
{"dl", T_POP_DL},
|
||||
{"section", T_POP_SECTION},
|
||||
{"purge", T_POP_PURGE},
|
||||
|
||||
{"rsreset", T_POP_RSRESET},
|
||||
{"rsset", T_POP_RSSET},
|
||||
|
||||
{"incbin", T_POP_INCBIN},
|
||||
{"charmap", T_POP_CHARMAP},
|
||||
{"newcharmap", T_POP_NEWCHARMAP},
|
||||
{"setcharmap", T_POP_SETCHARMAP},
|
||||
{"pushc", T_POP_PUSHC},
|
||||
{"popc", T_POP_POPC},
|
||||
|
||||
{"fail", T_POP_FAIL},
|
||||
{"warn", T_POP_WARN},
|
||||
{"fatal", T_POP_FATAL},
|
||||
{"assert", T_POP_ASSERT},
|
||||
{"static_assert", T_POP_STATIC_ASSERT},
|
||||
|
||||
{"macro", T_POP_MACRO},
|
||||
/* Not needed but we have it here just to protect the name */
|
||||
{"endm", T_POP_ENDM},
|
||||
{"shift", T_POP_SHIFT},
|
||||
|
||||
{"rept", T_POP_REPT},
|
||||
/* Not needed but we have it here just to protect the name */
|
||||
{"endr", T_POP_ENDR},
|
||||
|
||||
{"load", T_POP_LOAD},
|
||||
{"endl", T_POP_ENDL},
|
||||
|
||||
{"if", T_POP_IF},
|
||||
{"else", T_POP_ELSE},
|
||||
{"elif", T_POP_ELIF},
|
||||
{"endc", T_POP_ENDC},
|
||||
|
||||
{"union", T_POP_UNION},
|
||||
{"nextu", T_POP_NEXTU},
|
||||
{"endu", T_POP_ENDU},
|
||||
|
||||
{"wram0", T_SECT_WRAM0},
|
||||
{"vram", T_SECT_VRAM},
|
||||
{"romx", T_SECT_ROMX},
|
||||
{"rom0", T_SECT_ROM0},
|
||||
{"hram", T_SECT_HRAM},
|
||||
{"wramx", T_SECT_WRAMX},
|
||||
{"sram", T_SECT_SRAM},
|
||||
{"oam", T_SECT_OAM},
|
||||
|
||||
{"rb", T_POP_RB},
|
||||
{"rw", T_POP_RW},
|
||||
{"equ", T_POP_EQU},
|
||||
{"equs", T_POP_EQUS},
|
||||
|
||||
/* Handled before in list of CPU instructions */
|
||||
/* {"set", T_POP_SET}, */
|
||||
{"=", T_POP_EQUAL},
|
||||
|
||||
{"pushs", T_POP_PUSHS},
|
||||
{"pops", T_POP_POPS},
|
||||
{"pusho", T_POP_PUSHO},
|
||||
{"popo", T_POP_POPO},
|
||||
|
||||
{"opt", T_POP_OPT},
|
||||
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
const struct sLexFloat tNumberToken = {
|
||||
ParseNumber,
|
||||
T_NUMBER
|
||||
};
|
||||
|
||||
const struct sLexFloat tFixedPointToken = {
|
||||
ParseFixedPoint,
|
||||
T_NUMBER
|
||||
};
|
||||
|
||||
const struct sLexFloat tIDToken = {
|
||||
ParseSymbol,
|
||||
T_ID
|
||||
};
|
||||
|
||||
const struct sLexFloat tMacroArgToken = {
|
||||
PutMacroArg,
|
||||
T_LEX_MACROARG
|
||||
};
|
||||
|
||||
const struct sLexFloat tMacroUniqueToken = {
|
||||
PutUniqueID,
|
||||
T_LEX_MACROUNIQUE
|
||||
};
|
||||
|
||||
void setup_lexer(void)
|
||||
{
|
||||
uint32_t id;
|
||||
|
||||
lex_Init();
|
||||
lex_AddStrings(lexer_strings);
|
||||
|
||||
//Macro arguments
|
||||
|
||||
id = lex_FloatAlloc(&tMacroArgToken);
|
||||
lex_FloatAddFirstRange(id, '\\', '\\');
|
||||
lex_FloatAddSecondRange(id, '1', '9');
|
||||
id = lex_FloatAlloc(&tMacroUniqueToken);
|
||||
lex_FloatAddFirstRange(id, '\\', '\\');
|
||||
lex_FloatAddSecondRange(id, '@', '@');
|
||||
|
||||
//Decimal constants
|
||||
|
||||
id = lex_FloatAlloc(&tNumberToken);
|
||||
lex_FloatAddFirstRange(id, '0', '9');
|
||||
lex_FloatAddSecondRange(id, '0', '9');
|
||||
lex_FloatAddRange(id, '0', '9');
|
||||
|
||||
//Binary constants
|
||||
|
||||
id = lex_FloatAlloc(&tNumberToken);
|
||||
nBinaryID = id;
|
||||
lex_FloatAddFirstRange(id, '%', '%');
|
||||
lex_FloatAddSecondRange(id, CurrentOptions.binary[0],
|
||||
CurrentOptions.binary[0]);
|
||||
lex_FloatAddSecondRange(id, CurrentOptions.binary[1],
|
||||
CurrentOptions.binary[1]);
|
||||
lex_FloatAddRange(id, CurrentOptions.binary[0],
|
||||
CurrentOptions.binary[0]);
|
||||
lex_FloatAddRange(id, CurrentOptions.binary[1],
|
||||
CurrentOptions.binary[1]);
|
||||
|
||||
//Octal constants
|
||||
|
||||
id = lex_FloatAlloc(&tNumberToken);
|
||||
lex_FloatAddFirstRange(id, '&', '&');
|
||||
lex_FloatAddSecondRange(id, '0', '7');
|
||||
lex_FloatAddRange(id, '0', '7');
|
||||
|
||||
//Gameboy gfx constants
|
||||
|
||||
id = lex_FloatAlloc(&tNumberToken);
|
||||
nGBGfxID = id;
|
||||
lex_FloatAddFirstRange(id, '`', '`');
|
||||
lex_FloatAddSecondRange(id, CurrentOptions.gbgfx[0],
|
||||
CurrentOptions.gbgfx[0]);
|
||||
lex_FloatAddSecondRange(id, CurrentOptions.gbgfx[1],
|
||||
CurrentOptions.gbgfx[1]);
|
||||
lex_FloatAddSecondRange(id, CurrentOptions.gbgfx[2],
|
||||
CurrentOptions.gbgfx[2]);
|
||||
lex_FloatAddSecondRange(id, CurrentOptions.gbgfx[3],
|
||||
CurrentOptions.gbgfx[3]);
|
||||
lex_FloatAddRange(id, CurrentOptions.gbgfx[0], CurrentOptions.gbgfx[0]);
|
||||
lex_FloatAddRange(id, CurrentOptions.gbgfx[1], CurrentOptions.gbgfx[1]);
|
||||
lex_FloatAddRange(id, CurrentOptions.gbgfx[2], CurrentOptions.gbgfx[2]);
|
||||
lex_FloatAddRange(id, CurrentOptions.gbgfx[3], CurrentOptions.gbgfx[3]);
|
||||
|
||||
//Hex constants
|
||||
|
||||
id = lex_FloatAlloc(&tNumberToken);
|
||||
lex_FloatAddFirstRange(id, '$', '$');
|
||||
lex_FloatAddSecondRange(id, '0', '9');
|
||||
lex_FloatAddSecondRange(id, 'A', 'F');
|
||||
lex_FloatAddSecondRange(id, 'a', 'f');
|
||||
lex_FloatAddRange(id, '0', '9');
|
||||
lex_FloatAddRange(id, 'A', 'F');
|
||||
lex_FloatAddRange(id, 'a', 'f');
|
||||
|
||||
//ID 's
|
||||
|
||||
id = lex_FloatAlloc(&tIDToken);
|
||||
lex_FloatAddFirstRange(id, 'a', 'z');
|
||||
lex_FloatAddFirstRange(id, 'A', 'Z');
|
||||
lex_FloatAddFirstRange(id, '_', '_');
|
||||
lex_FloatAddSecondRange(id, '.', '.');
|
||||
lex_FloatAddSecondRange(id, 'a', 'z');
|
||||
lex_FloatAddSecondRange(id, 'A', 'Z');
|
||||
lex_FloatAddSecondRange(id, '0', '9');
|
||||
lex_FloatAddSecondRange(id, '_', '_');
|
||||
lex_FloatAddSecondRange(id, '\\', '\\');
|
||||
lex_FloatAddSecondRange(id, '@', '@');
|
||||
lex_FloatAddSecondRange(id, '#', '#');
|
||||
lex_FloatAddRange(id, '.', '.');
|
||||
lex_FloatAddRange(id, 'a', 'z');
|
||||
lex_FloatAddRange(id, 'A', 'Z');
|
||||
lex_FloatAddRange(id, '0', '9');
|
||||
lex_FloatAddRange(id, '_', '_');
|
||||
lex_FloatAddRange(id, '\\', '\\');
|
||||
lex_FloatAddRange(id, '@', '@');
|
||||
lex_FloatAddRange(id, '#', '#');
|
||||
|
||||
//Local ID
|
||||
|
||||
id = lex_FloatAlloc(&tIDToken);
|
||||
lex_FloatAddFirstRange(id, '.', '.');
|
||||
lex_FloatAddSecondRange(id, 'a', 'z');
|
||||
lex_FloatAddSecondRange(id, 'A', 'Z');
|
||||
lex_FloatAddSecondRange(id, '_', '_');
|
||||
lex_FloatAddRange(id, 'a', 'z');
|
||||
lex_FloatAddRange(id, 'A', 'Z');
|
||||
lex_FloatAddRange(id, '0', '9');
|
||||
lex_FloatAddRange(id, '_', '_');
|
||||
lex_FloatAddRange(id, '\\', '\\');
|
||||
lex_FloatAddRange(id, '@', '@');
|
||||
lex_FloatAddRange(id, '#', '#');
|
||||
|
||||
// "@"
|
||||
|
||||
id = lex_FloatAlloc(&tIDToken);
|
||||
lex_FloatAddFirstRange(id, '@', '@');
|
||||
|
||||
//Fixed point constants
|
||||
|
||||
id = lex_FloatAlloc(&tFixedPointToken);
|
||||
lex_FloatAddFirstRange(id, '.', '.');
|
||||
lex_FloatAddFirstRange(id, '0', '9');
|
||||
lex_FloatAddSecondRange(id, '.', '.');
|
||||
lex_FloatAddSecondRange(id, '0', '9');
|
||||
lex_FloatAddRange(id, '.', '.');
|
||||
lex_FloatAddRange(id, '0', '9');
|
||||
}
|
||||
2897
src/asm/lexer.c
2897
src/asm/lexer.c
File diff suppressed because it is too large
Load Diff
@@ -29,7 +29,8 @@ struct MacroArgs {
|
||||
sizeof(((struct MacroArgs){0}).args[0]) * (nbArgs))
|
||||
|
||||
static struct MacroArgs *macroArgs = NULL;
|
||||
static uint32_t uniqueID = -1;
|
||||
static uint32_t uniqueID = 0;
|
||||
static uint32_t maxUniqueID = 0;
|
||||
/*
|
||||
* The initialization is somewhat harmful, since it is never used, but it
|
||||
* guarantees the size of the buffer will be correct. I was unable to find a
|
||||
@@ -48,7 +49,7 @@ struct MacroArgs *macro_NewArgs(void)
|
||||
struct MacroArgs *args = malloc(SIZEOF_ARGS(INITIAL_ARG_SIZE));
|
||||
|
||||
if (!args)
|
||||
fatalerror("Unable to register macro arguments: %s", strerror(errno));
|
||||
fatalerror("Unable to register macro arguments: %s\n", strerror(errno));
|
||||
|
||||
args->nbArgs = 0;
|
||||
args->shift = 0;
|
||||
@@ -60,16 +61,16 @@ void macro_AppendArg(struct MacroArgs **argPtr, char *s)
|
||||
{
|
||||
#define macArgs (*argPtr)
|
||||
if (macArgs->nbArgs == MAXMACROARGS)
|
||||
yyerror("A maximum of " EXPAND_AND_STR(MAXMACROARGS)
|
||||
" arguments is allowed");
|
||||
error("A maximum of " EXPAND_AND_STR(MAXMACROARGS)
|
||||
" arguments is allowed\n");
|
||||
if (macArgs->nbArgs >= macArgs->capacity) {
|
||||
macArgs->capacity *= 2;
|
||||
/* Check that overflow didn't roll us back */
|
||||
if (macArgs->capacity <= macArgs->nbArgs)
|
||||
fatalerror("Failed to add new macro argument: possible capacity overflow");
|
||||
fatalerror("Failed to add new macro argument: possible capacity overflow\n");
|
||||
macArgs = realloc(macArgs, SIZEOF_ARGS(macArgs->capacity));
|
||||
if (!macArgs)
|
||||
fatalerror("Error adding new macro argument: %s", strerror(errno));
|
||||
fatalerror("Error adding new macro argument: %s\n", strerror(errno));
|
||||
}
|
||||
macArgs->args[macArgs->nbArgs++] = s;
|
||||
#undef macArgs
|
||||
@@ -88,6 +89,9 @@ void macro_FreeArgs(struct MacroArgs *args)
|
||||
|
||||
char const *macro_GetArg(uint32_t i)
|
||||
{
|
||||
if (!macroArgs)
|
||||
return NULL;
|
||||
|
||||
uint32_t realIndex = i + macroArgs->shift - 1;
|
||||
|
||||
return realIndex >= macroArgs->nbArgs ? NULL
|
||||
@@ -107,19 +111,34 @@ char const *macro_GetUniqueIDStr(void)
|
||||
void macro_SetUniqueID(uint32_t id)
|
||||
{
|
||||
uniqueID = id;
|
||||
if (id == -1) {
|
||||
if (id == 0) {
|
||||
uniqueIDPtr = NULL;
|
||||
} else {
|
||||
if (uniqueID > maxUniqueID)
|
||||
maxUniqueID = uniqueID;
|
||||
/* The buffer is guaranteed to be the correct size */
|
||||
sprintf(uniqueIDBuf, "_%" PRIu32, id);
|
||||
uniqueIDPtr = uniqueIDBuf;
|
||||
}
|
||||
}
|
||||
|
||||
void macro_ShiftCurrentArgs(void)
|
||||
uint32_t macro_UseNewUniqueID(void)
|
||||
{
|
||||
if (macroArgs->shift != macroArgs->nbArgs)
|
||||
macroArgs->shift++;
|
||||
macro_SetUniqueID(++maxUniqueID);
|
||||
return maxUniqueID;
|
||||
}
|
||||
|
||||
void macro_ShiftCurrentArgs(int32_t count)
|
||||
{
|
||||
if (!macroArgs) {
|
||||
error("Cannot shift macro arguments outside of a macro\n");
|
||||
} else if (count < 0) {
|
||||
error("Cannot shift arguments by negative amount %" PRId32 "\n", count);
|
||||
} else if (macroArgs->shift < macroArgs->nbArgs) {
|
||||
macroArgs->shift += count;
|
||||
if (macroArgs->shift > macroArgs->nbArgs)
|
||||
macroArgs->shift = macroArgs->nbArgs;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t macro_NbArgs(void)
|
||||
|
||||
159
src/asm/main.c
159
src/asm/main.c
@@ -6,6 +6,7 @@
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <float.h>
|
||||
#include <inttypes.h>
|
||||
@@ -17,13 +18,15 @@
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "asm/symbol.h"
|
||||
#include "asm/charmap.h"
|
||||
#include "asm/fstack.h"
|
||||
#include "asm/lexer.h"
|
||||
#include "asm/output.h"
|
||||
#include "asm/main.h"
|
||||
#include "asm/charmap.h"
|
||||
#include "asm/output.h"
|
||||
#include "asm/rpn.h"
|
||||
#include "asm/symbol.h"
|
||||
#include "asm/warning.h"
|
||||
#include "parser.h"
|
||||
|
||||
#include "extern/err.h"
|
||||
#include "extern/getopt.h"
|
||||
@@ -31,7 +34,9 @@
|
||||
#include "helpers.h"
|
||||
#include "version.h"
|
||||
|
||||
extern int yyparse(void);
|
||||
// Old Bison versions (confirmed for 2.3) do not forward-declare `yyparse` in the generated header
|
||||
// Unfortunately, macOS still ships 2.3, which is from 2008...
|
||||
int yyparse(void);
|
||||
|
||||
size_t cldefines_index;
|
||||
size_t cldefines_numindices;
|
||||
@@ -40,12 +45,7 @@ const size_t cldefine_entrysize = 2 * sizeof(void *);
|
||||
char **cldefines;
|
||||
|
||||
clock_t nStartClock, nEndClock;
|
||||
uint32_t nTotalLines, nIFDepth, nUnionDepth;
|
||||
bool skipElif;
|
||||
uint32_t unionStart[128], unionSize[128];
|
||||
|
||||
int32_t nLineNo;
|
||||
uint32_t curOffset;
|
||||
uint32_t nTotalLines, nIFDepth;
|
||||
|
||||
#if defined(YYDEBUG) && YYDEBUG
|
||||
extern int yydebug;
|
||||
@@ -70,71 +70,16 @@ bool warnings; /* True to enable warnings, false to disable them. */
|
||||
|
||||
struct sOptionStackEntry {
|
||||
struct sOptions Options;
|
||||
struct sOptionStackEntry *pNext;
|
||||
struct sOptionStackEntry *next;
|
||||
};
|
||||
|
||||
struct sOptionStackEntry *pOptionStack;
|
||||
|
||||
void opt_SetCurrentOptions(struct sOptions *pOpt)
|
||||
void opt_SetCurrentOptions(struct sOptions *opt)
|
||||
{
|
||||
if (nGBGfxID != -1) {
|
||||
lex_FloatDeleteRange(nGBGfxID, CurrentOptions.gbgfx[0],
|
||||
CurrentOptions.gbgfx[0]);
|
||||
lex_FloatDeleteRange(nGBGfxID, CurrentOptions.gbgfx[1],
|
||||
CurrentOptions.gbgfx[1]);
|
||||
lex_FloatDeleteRange(nGBGfxID, CurrentOptions.gbgfx[2],
|
||||
CurrentOptions.gbgfx[2]);
|
||||
lex_FloatDeleteRange(nGBGfxID, CurrentOptions.gbgfx[3],
|
||||
CurrentOptions.gbgfx[3]);
|
||||
lex_FloatDeleteSecondRange(nGBGfxID, CurrentOptions.gbgfx[0],
|
||||
CurrentOptions.gbgfx[0]);
|
||||
lex_FloatDeleteSecondRange(nGBGfxID, CurrentOptions.gbgfx[1],
|
||||
CurrentOptions.gbgfx[1]);
|
||||
lex_FloatDeleteSecondRange(nGBGfxID, CurrentOptions.gbgfx[2],
|
||||
CurrentOptions.gbgfx[2]);
|
||||
lex_FloatDeleteSecondRange(nGBGfxID, CurrentOptions.gbgfx[3],
|
||||
CurrentOptions.gbgfx[3]);
|
||||
}
|
||||
if (nBinaryID != -1) {
|
||||
lex_FloatDeleteRange(nBinaryID, CurrentOptions.binary[0],
|
||||
CurrentOptions.binary[0]);
|
||||
lex_FloatDeleteRange(nBinaryID, CurrentOptions.binary[1],
|
||||
CurrentOptions.binary[1]);
|
||||
lex_FloatDeleteSecondRange(nBinaryID, CurrentOptions.binary[0],
|
||||
CurrentOptions.binary[0]);
|
||||
lex_FloatDeleteSecondRange(nBinaryID, CurrentOptions.binary[1],
|
||||
CurrentOptions.binary[1]);
|
||||
}
|
||||
CurrentOptions = *pOpt;
|
||||
|
||||
if (nGBGfxID != -1) {
|
||||
lex_FloatAddRange(nGBGfxID, CurrentOptions.gbgfx[0],
|
||||
CurrentOptions.gbgfx[0]);
|
||||
lex_FloatAddRange(nGBGfxID, CurrentOptions.gbgfx[1],
|
||||
CurrentOptions.gbgfx[1]);
|
||||
lex_FloatAddRange(nGBGfxID, CurrentOptions.gbgfx[2],
|
||||
CurrentOptions.gbgfx[2]);
|
||||
lex_FloatAddRange(nGBGfxID, CurrentOptions.gbgfx[3],
|
||||
CurrentOptions.gbgfx[3]);
|
||||
lex_FloatAddSecondRange(nGBGfxID, CurrentOptions.gbgfx[0],
|
||||
CurrentOptions.gbgfx[0]);
|
||||
lex_FloatAddSecondRange(nGBGfxID, CurrentOptions.gbgfx[1],
|
||||
CurrentOptions.gbgfx[1]);
|
||||
lex_FloatAddSecondRange(nGBGfxID, CurrentOptions.gbgfx[2],
|
||||
CurrentOptions.gbgfx[2]);
|
||||
lex_FloatAddSecondRange(nGBGfxID, CurrentOptions.gbgfx[3],
|
||||
CurrentOptions.gbgfx[3]);
|
||||
}
|
||||
if (nBinaryID != -1) {
|
||||
lex_FloatAddRange(nBinaryID, CurrentOptions.binary[0],
|
||||
CurrentOptions.binary[0]);
|
||||
lex_FloatAddRange(nBinaryID, CurrentOptions.binary[1],
|
||||
CurrentOptions.binary[1]);
|
||||
lex_FloatAddSecondRange(nBinaryID, CurrentOptions.binary[0],
|
||||
CurrentOptions.binary[0]);
|
||||
lex_FloatAddSecondRange(nBinaryID, CurrentOptions.binary[1],
|
||||
CurrentOptions.binary[1]);
|
||||
}
|
||||
CurrentOptions = *opt;
|
||||
lexer_SetGfxDigits(CurrentOptions.gbgfx);
|
||||
lexer_SetBinDigits(CurrentOptions.binary);
|
||||
}
|
||||
|
||||
void opt_Parse(char *s)
|
||||
@@ -151,7 +96,7 @@ void opt_Parse(char *s)
|
||||
newopt.gbgfx[2] = s[3];
|
||||
newopt.gbgfx[3] = s[4];
|
||||
} else {
|
||||
yyerror("Must specify exactly 4 characters for option 'g'");
|
||||
error("Must specify exactly 4 characters for option 'g'\n");
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
@@ -159,11 +104,11 @@ void opt_Parse(char *s)
|
||||
newopt.binary[0] = s[1];
|
||||
newopt.binary[1] = s[2];
|
||||
} else {
|
||||
yyerror("Must specify exactly 2 characters for option 'b'");
|
||||
error("Must specify exactly 2 characters for option 'b'\n");
|
||||
}
|
||||
break;
|
||||
case 'z':
|
||||
warning(WARNING_OBSOLETE, "Option 'z' is a deprecated alias for 'p'");
|
||||
warning(WARNING_OBSOLETE, "Option 'z' is a deprecated alias for 'p'\n");
|
||||
/* fallthrough */
|
||||
case 'p':
|
||||
if (strlen(&s[1]) <= 2) {
|
||||
@@ -172,15 +117,15 @@ void opt_Parse(char *s)
|
||||
|
||||
result = sscanf(&s[1], "%x", &fillchar);
|
||||
if (result != EOF && result != 1)
|
||||
yyerror("Invalid argument for option 'z'");
|
||||
error("Invalid argument for option 'z'\n");
|
||||
else
|
||||
newopt.fillchar = fillchar;
|
||||
} else {
|
||||
yyerror("Invalid argument for option 'z'");
|
||||
error("Invalid argument for option 'z'\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
yyerror("Unknown option");
|
||||
error("Unknown option\n");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -194,23 +139,23 @@ void opt_Push(void)
|
||||
pOpt = malloc(sizeof(struct sOptionStackEntry));
|
||||
|
||||
if (pOpt == NULL)
|
||||
fatalerror("No memory for option stack");
|
||||
fatalerror("No memory for option stack\n");
|
||||
|
||||
pOpt->Options = CurrentOptions;
|
||||
pOpt->pNext = pOptionStack;
|
||||
pOpt->next = pOptionStack;
|
||||
pOptionStack = pOpt;
|
||||
}
|
||||
|
||||
void opt_Pop(void)
|
||||
{
|
||||
if (pOptionStack == NULL)
|
||||
fatalerror("No entries in the option stack");
|
||||
fatalerror("No entries in the option stack\n");
|
||||
|
||||
struct sOptionStackEntry *pOpt;
|
||||
|
||||
pOpt = pOptionStack;
|
||||
opt_SetCurrentOptions(&(pOpt->Options));
|
||||
pOptionStack = pOpt->pNext;
|
||||
pOptionStack = pOpt->next;
|
||||
free(pOpt);
|
||||
}
|
||||
|
||||
@@ -221,17 +166,17 @@ void opt_AddDefine(char *s)
|
||||
if (cldefines_index >= cldefines_numindices) {
|
||||
/* Check for overflows */
|
||||
if ((cldefines_numindices * 2) < cldefines_numindices)
|
||||
fatalerror("No memory for command line defines");
|
||||
fatalerror("No memory for command line defines\n");
|
||||
|
||||
if ((cldefines_bufsize * 2) < cldefines_bufsize)
|
||||
fatalerror("No memory for command line defines");
|
||||
fatalerror("No memory for command line defines\n");
|
||||
|
||||
cldefines_numindices *= 2;
|
||||
cldefines_bufsize *= 2;
|
||||
|
||||
cldefines = realloc(cldefines, cldefines_bufsize);
|
||||
if (!cldefines)
|
||||
fatalerror("No memory for command line defines");
|
||||
fatalerror("No memory for command line defines\n");
|
||||
}
|
||||
equals = strchr(s, '=');
|
||||
if (equals) {
|
||||
@@ -252,6 +197,22 @@ static void opt_ParseDefines(void)
|
||||
sym_AddString(cldefines[i], cldefines[i + 1]);
|
||||
}
|
||||
|
||||
void upperstring(char *s)
|
||||
{
|
||||
while (*s) {
|
||||
*s = toupper(*s);
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
void lowerstring(char *s)
|
||||
{
|
||||
while (*s) {
|
||||
*s = tolower(*s);
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Escapes Make-special chars from a string */
|
||||
static char *make_escape(const char *str)
|
||||
{
|
||||
@@ -324,7 +285,7 @@ static void print_usage(void)
|
||||
" -V, --version print RGBASM version and exit\n"
|
||||
" -W, --warning <warning> enable or disable warnings\n"
|
||||
"\n"
|
||||
"For help, use `man rgbasm' or go to https://rednex.github.io/rgbds/\n",
|
||||
"For help, use `man rgbasm' or go to https://rgbds.gbdev.io/docs/\n",
|
||||
stderr);
|
||||
exit(1);
|
||||
}
|
||||
@@ -345,17 +306,17 @@ int main(int argc, char *argv[])
|
||||
cldefines_bufsize = cldefines_numindices * cldefine_entrysize;
|
||||
cldefines = malloc(cldefines_bufsize);
|
||||
if (!cldefines)
|
||||
fatalerror("No memory for command line defines");
|
||||
fatalerror("No memory for command line defines\n");
|
||||
|
||||
#if defined(YYDEBUG) && YYDEBUG
|
||||
yydebug = 1;
|
||||
#endif
|
||||
|
||||
nMaxRecursionDepth = 64;
|
||||
oGeneratePhonyDeps = false;
|
||||
oGeneratedMissingIncludes = false;
|
||||
oFailedOnMissingInclude = false;
|
||||
tzTargetFileName = NULL;
|
||||
uint32_t maxRecursionDepth = 64;
|
||||
size_t nTargetFileNameLen = 0;
|
||||
|
||||
DefaultOptions.gbgfx[0] = '0';
|
||||
@@ -434,7 +395,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
break;
|
||||
case 'r':
|
||||
nMaxRecursionDepth = strtoul(optarg, &ep, 0);
|
||||
maxRecursionDepth = strtoul(optarg, &ep, 0);
|
||||
|
||||
if (optarg[0] == '\0' || *ep != '\0')
|
||||
errx(1, "Invalid argument for option 'r'");
|
||||
@@ -475,7 +436,8 @@ int main(int argc, char *argv[])
|
||||
/* On first alloc, make an empty str */
|
||||
tzTargetFileName =
|
||||
malloc(nTargetFileNameLen + 1);
|
||||
*tzTargetFileName = '\0';
|
||||
if (tzTargetFileName)
|
||||
*tzTargetFileName = '\0';
|
||||
} else {
|
||||
tzTargetFileName =
|
||||
realloc(tzTargetFileName,
|
||||
@@ -517,8 +479,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
tzMainfile = argv[argc - 1];
|
||||
|
||||
setup_lexer();
|
||||
|
||||
if (verbose)
|
||||
printf("Assembling %s\n", tzMainfile);
|
||||
|
||||
@@ -529,20 +489,20 @@ int main(int argc, char *argv[])
|
||||
fprintf(dependfile, "%s: %s\n", tzTargetFileName, tzMainfile);
|
||||
}
|
||||
|
||||
/* Init file stack; important to do first, since it provides the file name, line, etc */
|
||||
lexer_Init();
|
||||
fstk_Init(tzMainfile, maxRecursionDepth);
|
||||
|
||||
nStartClock = clock();
|
||||
|
||||
nLineNo = 1;
|
||||
nTotalLines = 0;
|
||||
nIFDepth = 0;
|
||||
skipElif = true;
|
||||
nUnionDepth = 0;
|
||||
sym_Init();
|
||||
sym_SetExportAll(exportall);
|
||||
fstk_Init(tzMainfile);
|
||||
opt_ParseDefines();
|
||||
charmap_InitMain();
|
||||
|
||||
yy_set_state(LEX_STATE_NORMAL);
|
||||
opt_ParseDefines();
|
||||
charmap_New("main", NULL);
|
||||
|
||||
opt_SetCurrentOptions(&DefaultOptions);
|
||||
|
||||
if (yyparse() != 0 || nbErrors != 0)
|
||||
@@ -554,10 +514,7 @@ int main(int argc, char *argv[])
|
||||
errx(1, "Unterminated IF construct (%" PRIu32 " levels)!",
|
||||
nIFDepth);
|
||||
|
||||
if (nUnionDepth != 0) {
|
||||
errx(1, "Unterminated UNION construct (%" PRIu32 " levels)!",
|
||||
nUnionDepth);
|
||||
}
|
||||
sect_CheckUnionClosed();
|
||||
|
||||
double timespent;
|
||||
|
||||
|
||||
288
src/asm/output.c
288
src/asm/output.c
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
@@ -33,14 +34,15 @@
|
||||
#include "platform.h" // strdup
|
||||
|
||||
struct Patch {
|
||||
char tzFilename[_MAX_PATH + 1];
|
||||
struct FileStackNode const *src;
|
||||
uint32_t lineNo;
|
||||
uint32_t nOffset;
|
||||
struct Section *pcSection;
|
||||
uint32_t pcOffset;
|
||||
uint8_t nType;
|
||||
uint8_t type;
|
||||
uint32_t nRPNSize;
|
||||
uint8_t *pRPN;
|
||||
struct Patch *pNext;
|
||||
struct Patch *next;
|
||||
};
|
||||
|
||||
struct Assertion {
|
||||
@@ -62,19 +64,17 @@ static uint32_t nbSymbols = 0; /* Length of the above list */
|
||||
|
||||
static struct Assertion *assertions = NULL;
|
||||
|
||||
static struct FileStackNode *fileStackNodes = NULL;
|
||||
|
||||
/*
|
||||
* Count the number of sections used in this object
|
||||
*/
|
||||
static uint32_t countsections(void)
|
||||
{
|
||||
struct Section *pSect;
|
||||
uint32_t count = 0;
|
||||
|
||||
pSect = pSectionList;
|
||||
while (pSect) {
|
||||
for (struct Section const *sect = pSectionList; sect; sect = sect->next)
|
||||
count++;
|
||||
pSect = pSect->pNext;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
@@ -82,12 +82,12 @@ static uint32_t countsections(void)
|
||||
/*
|
||||
* Count the number of patches used in this object
|
||||
*/
|
||||
static uint32_t countpatches(struct Section const *pSect)
|
||||
static uint32_t countpatches(struct Section const *sect)
|
||||
{
|
||||
uint32_t r = 0;
|
||||
|
||||
for (struct Patch const *patch = pSect->pPatches; patch != NULL;
|
||||
patch = patch->pNext)
|
||||
for (struct Patch const *patch = sect->patches; patch != NULL;
|
||||
patch = patch->next)
|
||||
r++;
|
||||
|
||||
return r;
|
||||
@@ -129,24 +129,68 @@ static void fputstring(char const *s, FILE *f)
|
||||
fputc(0, f);
|
||||
}
|
||||
|
||||
static uint32_t getNbFileStackNodes(void)
|
||||
{
|
||||
return fileStackNodes ? fileStackNodes->ID + 1 : 0;
|
||||
}
|
||||
|
||||
void out_RegisterNode(struct FileStackNode *node)
|
||||
{
|
||||
/* If node is not already registered, register it (and parents), and give it a unique ID */
|
||||
while (node->ID == -1) {
|
||||
node->ID = getNbFileStackNodes();
|
||||
if (node->ID == -1)
|
||||
fatalerror("Reached too many file stack nodes; try splitting the file up\n");
|
||||
node->next = fileStackNodes;
|
||||
fileStackNodes = node;
|
||||
|
||||
/* Also register the node's parents */
|
||||
node = node->parent;
|
||||
if (!node)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void out_ReplaceNode(struct FileStackNode *node)
|
||||
{
|
||||
(void)node;
|
||||
#if 0
|
||||
This is code intended to replace a node, which is pretty useless until ref counting is added...
|
||||
|
||||
struct FileStackNode **ptr = &fileStackNodes;
|
||||
|
||||
/*
|
||||
* The linked list is supposed to have decrementing IDs, so iterate with less memory reads,
|
||||
* to hopefully hit the cache less. A debug check is added after, in case a change is made
|
||||
* that breaks this assumption.
|
||||
*/
|
||||
for (uint32_t i = fileStackNodes->ID; i != node->ID; i--)
|
||||
ptr = &(*ptr)->next;
|
||||
assert((*ptr)->ID == node->ID);
|
||||
|
||||
node->next = (*ptr)->next;
|
||||
assert(!node->next || node->next->ID == node->ID - 1); /* Catch inconsistencies early */
|
||||
/* TODO: unreference the node */
|
||||
*ptr = node;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a section's ID
|
||||
*/
|
||||
static uint32_t getsectid(struct Section const *pSect)
|
||||
static uint32_t getsectid(struct Section const *sect)
|
||||
{
|
||||
struct Section const *sec;
|
||||
struct Section const *sec = pSectionList;
|
||||
uint32_t ID = 0;
|
||||
|
||||
sec = pSectionList;
|
||||
|
||||
while (sec) {
|
||||
if (sec == pSect)
|
||||
if (sec == sect)
|
||||
return ID;
|
||||
ID++;
|
||||
sec = sec->pNext;
|
||||
sec = sec->next;
|
||||
}
|
||||
|
||||
fatalerror("Unknown section '%s'", pSect->pzName);
|
||||
fatalerror("Unknown section '%s'\n", sect->name);
|
||||
}
|
||||
|
||||
static uint32_t getSectIDIfAny(struct Section const *sect)
|
||||
@@ -157,42 +201,45 @@ static uint32_t getSectIDIfAny(struct Section const *sect)
|
||||
/*
|
||||
* Write a patch to a file
|
||||
*/
|
||||
static void writepatch(struct Patch const *pPatch, FILE *f)
|
||||
static void writepatch(struct Patch const *patch, FILE *f)
|
||||
{
|
||||
fputstring(pPatch->tzFilename, f);
|
||||
fputlong(pPatch->nOffset, f);
|
||||
fputlong(getSectIDIfAny(pPatch->pcSection), f);
|
||||
fputlong(pPatch->pcOffset, f);
|
||||
fputc(pPatch->nType, f);
|
||||
fputlong(pPatch->nRPNSize, f);
|
||||
fwrite(pPatch->pRPN, 1, pPatch->nRPNSize, f);
|
||||
assert(patch->src->ID != -1);
|
||||
|
||||
fputlong(patch->src->ID, f);
|
||||
fputlong(patch->lineNo, f);
|
||||
fputlong(patch->nOffset, f);
|
||||
fputlong(getSectIDIfAny(patch->pcSection), f);
|
||||
fputlong(patch->pcOffset, f);
|
||||
fputc(patch->type, f);
|
||||
fputlong(patch->nRPNSize, f);
|
||||
fwrite(patch->pRPN, 1, patch->nRPNSize, f);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a section to a file
|
||||
*/
|
||||
static void writesection(struct Section const *pSect, FILE *f)
|
||||
static void writesection(struct Section const *sect, FILE *f)
|
||||
{
|
||||
fputstring(pSect->pzName, f);
|
||||
fputstring(sect->name, f);
|
||||
|
||||
fputlong(pSect->size, f);
|
||||
fputlong(sect->size, f);
|
||||
|
||||
bool isUnion = pSect->modifier == SECTION_UNION;
|
||||
bool isFragment = pSect->modifier == SECTION_FRAGMENT;
|
||||
bool isUnion = sect->modifier == SECTION_UNION;
|
||||
bool isFragment = sect->modifier == SECTION_FRAGMENT;
|
||||
|
||||
fputc(pSect->nType | isUnion << 7 | isFragment << 6, f);
|
||||
fputc(sect->type | isUnion << 7 | isFragment << 6, f);
|
||||
|
||||
fputlong(pSect->nOrg, f);
|
||||
fputlong(pSect->nBank, f);
|
||||
fputc(pSect->nAlign, f);
|
||||
fputlong(pSect->alignOfs, f);
|
||||
fputlong(sect->org, f);
|
||||
fputlong(sect->bank, f);
|
||||
fputc(sect->align, f);
|
||||
fputlong(sect->alignOfs, f);
|
||||
|
||||
if (sect_HasData(pSect->nType)) {
|
||||
fwrite(pSect->tData, 1, pSect->size, f);
|
||||
fputlong(countpatches(pSect), f);
|
||||
if (sect_HasData(sect->type)) {
|
||||
fwrite(sect->data, 1, sect->size, f);
|
||||
fputlong(countpatches(sect), f);
|
||||
|
||||
for (struct Patch const *patch = pSect->pPatches; patch != NULL;
|
||||
patch = patch->pNext)
|
||||
for (struct Patch const *patch = sect->patches; patch != NULL;
|
||||
patch = patch->next)
|
||||
writepatch(patch, f);
|
||||
}
|
||||
}
|
||||
@@ -206,26 +253,35 @@ static void writesymbol(struct Symbol const *sym, FILE *f)
|
||||
if (!sym_IsDefined(sym)) {
|
||||
fputc(SYMTYPE_IMPORT, f);
|
||||
} else {
|
||||
assert(sym->src->ID != -1);
|
||||
|
||||
fputc(sym->isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, f);
|
||||
fputstring(sym->fileName, f);
|
||||
fputlong(sym->src->ID, f);
|
||||
fputlong(sym->fileLine, f);
|
||||
fputlong(getSectIDIfAny(sym_GetSection(sym)), f);
|
||||
fputlong(sym->value, f);
|
||||
}
|
||||
}
|
||||
|
||||
static void registerSymbol(struct Symbol *sym)
|
||||
{
|
||||
*objectSymbolsTail = sym;
|
||||
objectSymbolsTail = &sym->next;
|
||||
out_RegisterNode(sym->src);
|
||||
if (nbSymbols == -1)
|
||||
fatalerror("Registered too many symbols (%" PRIu32
|
||||
"); try splitting up your files\n", (uint32_t)-1);
|
||||
sym->ID = nbSymbols++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a symbol's ID within the object file
|
||||
* If the symbol does not have one, one is assigned by registering the symbol
|
||||
*/
|
||||
static uint32_t getSymbolID(struct Symbol *sym)
|
||||
{
|
||||
if (sym->ID == -1) {
|
||||
sym->ID = nbSymbols++;
|
||||
|
||||
*objectSymbolsTail = sym;
|
||||
objectSymbolsTail = &sym->next;
|
||||
}
|
||||
if (sym->ID == -1 && !sym_IsPC(sym))
|
||||
registerSymbol(sym);
|
||||
return sym->ID;
|
||||
}
|
||||
|
||||
@@ -240,6 +296,11 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
||||
uint8_t rpndata = popbyte();
|
||||
|
||||
switch (rpndata) {
|
||||
struct Symbol *sym;
|
||||
uint32_t value;
|
||||
uint8_t b;
|
||||
size_t i;
|
||||
|
||||
case RPN_CONST:
|
||||
writebyte(RPN_CONST);
|
||||
writebyte(popbyte());
|
||||
@@ -247,13 +308,15 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
||||
writebyte(popbyte());
|
||||
writebyte(popbyte());
|
||||
break;
|
||||
case RPN_SYM:
|
||||
{
|
||||
for (unsigned int i = -1; (tzSym[++i] = popbyte()); )
|
||||
;
|
||||
struct Symbol *sym = sym_FindSymbol(tzSym);
|
||||
uint32_t value;
|
||||
|
||||
case RPN_SYM:
|
||||
i = 0;
|
||||
do {
|
||||
tzSym[i] = popbyte();
|
||||
} while (tzSym[i++]);
|
||||
|
||||
// The symbol name is always written expanded
|
||||
sym = sym_FindExactSymbol(tzSym);
|
||||
if (sym_IsConstant(sym)) {
|
||||
writebyte(RPN_CONST);
|
||||
value = sym_GetConstantValue(tzSym);
|
||||
@@ -261,18 +324,22 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
||||
writebyte(RPN_SYM);
|
||||
value = getSymbolID(sym);
|
||||
}
|
||||
|
||||
writebyte(value & 0xFF);
|
||||
writebyte(value >> 8);
|
||||
writebyte(value >> 16);
|
||||
writebyte(value >> 24);
|
||||
break;
|
||||
}
|
||||
|
||||
case RPN_BANK_SYM:
|
||||
{
|
||||
for (unsigned int i = -1; (tzSym[++i] = popbyte()); )
|
||||
;
|
||||
struct Symbol *sym = sym_FindSymbol(tzSym);
|
||||
uint32_t value = getSymbolID(sym);
|
||||
i = 0;
|
||||
do {
|
||||
tzSym[i] = popbyte();
|
||||
} while (tzSym[i++]);
|
||||
|
||||
// The symbol name is always written expanded
|
||||
sym = sym_FindExactSymbol(tzSym);
|
||||
value = getSymbolID(sym);
|
||||
|
||||
writebyte(RPN_BANK_SYM);
|
||||
writebyte(value & 0xFF);
|
||||
@@ -280,18 +347,15 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
||||
writebyte(value >> 16);
|
||||
writebyte(value >> 24);
|
||||
break;
|
||||
}
|
||||
case RPN_BANK_SECT:
|
||||
{
|
||||
uint8_t b;
|
||||
|
||||
case RPN_BANK_SECT:
|
||||
writebyte(RPN_BANK_SECT);
|
||||
do {
|
||||
b = popbyte();
|
||||
writebyte(b);
|
||||
} while (b != 0);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
writebyte(rpndata);
|
||||
break;
|
||||
@@ -303,31 +367,45 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
||||
|
||||
/*
|
||||
* Allocate a new patch structure and link it into the list
|
||||
* WARNING: all patches are assumed to eventually be written, so the file stack node is registered
|
||||
*/
|
||||
static struct Patch *allocpatch(uint32_t type, struct Expression const *expr,
|
||||
uint32_t ofs)
|
||||
static struct Patch *allocpatch(uint32_t type, struct Expression const *expr, uint32_t ofs)
|
||||
{
|
||||
struct Patch *pPatch = malloc(sizeof(struct Patch));
|
||||
struct Patch *patch = malloc(sizeof(struct Patch));
|
||||
uint32_t rpnSize = expr->isKnown ? 5 : expr->nRPNPatchSize;
|
||||
struct FileStackNode *node = fstk_GetFileStack();
|
||||
|
||||
if (!pPatch)
|
||||
fatalerror("No memory for patch: %s", strerror(errno));
|
||||
pPatch->pRPN = malloc(sizeof(*pPatch->pRPN) * expr->nRPNPatchSize);
|
||||
if (!patch)
|
||||
fatalerror("No memory for patch: %s\n", strerror(errno));
|
||||
|
||||
if (!pPatch->pRPN)
|
||||
fatalerror("No memory for patch's RPN expression: %s",
|
||||
strerror(errno));
|
||||
patch->pRPN = malloc(sizeof(*patch->pRPN) * rpnSize);
|
||||
if (!patch->pRPN)
|
||||
fatalerror("No memory for patch's RPN expression: %s\n", strerror(errno));
|
||||
|
||||
pPatch->nRPNSize = 0;
|
||||
pPatch->nType = type;
|
||||
fstk_DumpToStr(pPatch->tzFilename, sizeof(pPatch->tzFilename));
|
||||
pPatch->nOffset = ofs;
|
||||
pPatch->pcSection = sect_GetSymbolSection();
|
||||
pPatch->pcOffset = curOffset;
|
||||
patch->type = type;
|
||||
patch->src = node;
|
||||
out_RegisterNode(node);
|
||||
patch->lineNo = lexer_GetLineNo();
|
||||
patch->nOffset = ofs;
|
||||
patch->pcSection = sect_GetSymbolSection();
|
||||
patch->pcOffset = sect_GetSymbolOffset();
|
||||
|
||||
writerpn(pPatch->pRPN, &pPatch->nRPNSize, expr->tRPN, expr->nRPNLength);
|
||||
assert(pPatch->nRPNSize == expr->nRPNPatchSize);
|
||||
/* If the expression's value is known, output a constant RPN expression directly */
|
||||
if (expr->isKnown) {
|
||||
patch->nRPNSize = rpnSize;
|
||||
/* Make sure to update `rpnSize` above if modifying this! */
|
||||
patch->pRPN[0] = RPN_CONST;
|
||||
patch->pRPN[1] = (uint32_t)(expr->nVal) & 0xFF;
|
||||
patch->pRPN[2] = (uint32_t)(expr->nVal) >> 8;
|
||||
patch->pRPN[3] = (uint32_t)(expr->nVal) >> 16;
|
||||
patch->pRPN[4] = (uint32_t)(expr->nVal) >> 24;
|
||||
} else {
|
||||
patch->nRPNSize = 0;
|
||||
writerpn(patch->pRPN, &patch->nRPNSize, expr->tRPN, expr->nRPNLength);
|
||||
}
|
||||
assert(patch->nRPNSize == rpnSize);
|
||||
|
||||
return pPatch;
|
||||
return patch;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -335,10 +413,10 @@ static struct Patch *allocpatch(uint32_t type, struct Expression const *expr,
|
||||
*/
|
||||
void out_CreatePatch(uint32_t type, struct Expression const *expr, uint32_t ofs)
|
||||
{
|
||||
struct Patch *pPatch = allocpatch(type, expr, ofs);
|
||||
struct Patch *patch = allocpatch(type, expr, ofs);
|
||||
|
||||
pPatch->pNext = pCurrentSection->pPatches;
|
||||
pCurrentSection->pPatches = pPatch;
|
||||
patch->next = pCurrentSection->patches;
|
||||
pCurrentSection->patches = patch;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -371,13 +449,28 @@ static void writeassert(struct Assertion *assert, FILE *f)
|
||||
fputstring(assert->message, f);
|
||||
}
|
||||
|
||||
static void writeFileStackNode(struct FileStackNode const *node, FILE *f)
|
||||
{
|
||||
fputlong(node->parent ? node->parent->ID : -1, f);
|
||||
fputlong(node->lineNo, f);
|
||||
fputc(node->type, f);
|
||||
if (node->type != NODE_REPT) {
|
||||
fputstring(((struct FileStackNamedNode const *)node)->name, f);
|
||||
} else {
|
||||
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
|
||||
|
||||
fputlong(reptNode->reptDepth, f);
|
||||
/* Iters are stored by decreasing depth, so reverse the order for output */
|
||||
for (uint32_t i = reptNode->reptDepth; i--; )
|
||||
fputlong(reptNode->iters[i], f);
|
||||
}
|
||||
}
|
||||
|
||||
static void registerExportedSymbol(struct Symbol *symbol, void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
if (sym_IsExported(symbol) && symbol->ID == -1) {
|
||||
*objectSymbolsTail = symbol;
|
||||
objectSymbolsTail = &symbol->next;
|
||||
nbSymbols++;
|
||||
registerSymbol(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,7 +479,11 @@ static void registerExportedSymbol(struct Symbol *symbol, void *arg)
|
||||
*/
|
||||
void out_WriteObject(void)
|
||||
{
|
||||
FILE *f = fopen(tzObjectname, "wb");
|
||||
FILE *f;
|
||||
if (strcmp(tzObjectname, "-") != 0)
|
||||
f = fopen(tzObjectname, "wb");
|
||||
else
|
||||
f = fdopen(1, "wb");
|
||||
|
||||
if (!f)
|
||||
err(1, "Couldn't write file '%s'", tzObjectname);
|
||||
@@ -400,10 +497,19 @@ void out_WriteObject(void)
|
||||
fputlong(nbSymbols, f);
|
||||
fputlong(countsections(), f);
|
||||
|
||||
fputlong(getNbFileStackNodes(), f);
|
||||
for (struct FileStackNode const *node = fileStackNodes; node; node = node->next) {
|
||||
writeFileStackNode(node, f);
|
||||
if (node->next && node->next->ID != node->ID - 1)
|
||||
fatalerror("Internal error: fstack node #%" PRIu32 " follows #%" PRIu32
|
||||
". Please report this to the developers!\n",
|
||||
node->next->ID, node->ID);
|
||||
}
|
||||
|
||||
for (struct Symbol const *sym = objectSymbols; sym; sym = sym->next)
|
||||
writesymbol(sym, f);
|
||||
|
||||
for (struct Section *sect = pSectionList; sect; sect = sect->pNext)
|
||||
for (struct Section *sect = pSectionList; sect; sect = sect->next)
|
||||
writesection(sect, f);
|
||||
|
||||
fputlong(countasserts(), f);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -112,9 +112,9 @@ from erroring out when dependency files are deleted.
|
||||
Add a target to the rules emitted by
|
||||
.Fl M .
|
||||
The exact string provided will be written, including spaces and special characters.
|
||||
.Dl Fl MT fileA Fl MT fileB
|
||||
.Dl Fl MT No fileA Fl MT No fileB
|
||||
is equivalent to
|
||||
.Dl Fl MT 'fileA fileB' .
|
||||
.Dl Fl MT No 'fileA fileB' .
|
||||
If neither this nor
|
||||
.Fl MQ
|
||||
is specified, the output file name is used.
|
||||
@@ -203,6 +203,10 @@ Warn about incorrect arguments to built-in functions, such as
|
||||
with indexes outside of the string's bounds.
|
||||
This warning is enabled by
|
||||
.Fl Wall .
|
||||
.It Fl Wcharmap-redef
|
||||
Warn when re-defining a charmap mapping.
|
||||
This warning is enabled by
|
||||
.Fl Wall .
|
||||
.It Fl Wdiv
|
||||
Warn when dividing the smallest negative integer by -1, which yields itself due to integer overflow.
|
||||
.It Fl Wempty-entry
|
||||
@@ -260,7 +264,7 @@ and then
|
||||
.Xr rgbfix 1 .
|
||||
.Sh BUGS
|
||||
Please report bugs on
|
||||
.Lk https://github.com/rednex/rgbds/issues GitHub .
|
||||
.Lk https://github.com/gbdev/rgbds/issues GitHub .
|
||||
.Sh SEE ALSO
|
||||
.Xr rgbasm 5 ,
|
||||
.Xr rgbfix 1 ,
|
||||
@@ -272,4 +276,4 @@ Please report bugs on
|
||||
.Nm
|
||||
was originally written by Carsten S\(/orensen as part of the ASMotor package, and was later packaged in RGBDS by Justin Lloyd.
|
||||
It is now maintained by a number of contributors at
|
||||
.Lk https://github.com/rednex/rgbds .
|
||||
.Lk https://github.com/gbdev/rgbds .
|
||||
|
||||
174
src/asm/rgbasm.5
174
src/asm/rgbasm.5
@@ -12,7 +12,6 @@
|
||||
.Nm rgbasm
|
||||
.Nd language documentation
|
||||
.Sh DESCRIPTION
|
||||
.Pp
|
||||
This is the full description of the language used by
|
||||
.Xr rgbasm 1 .
|
||||
The description of the instructions supported by the Game Boy CPU is in
|
||||
@@ -30,7 +29,6 @@ but any program that processes RGB object files (described in
|
||||
.Xr rgbds 5 )
|
||||
can be used in its place.
|
||||
.Sh SYNTAX
|
||||
.Pp
|
||||
The syntax is line‐based, just as in any other assembler, meaning that you do one instruction or pseudo‐op per line:
|
||||
.Pp
|
||||
.Dl Oo Ar label Oc Oo Ar instruction Oc Oo Ar ;\ comment Oc
|
||||
@@ -47,13 +45,23 @@ The assembler
|
||||
.Em always
|
||||
ignores comments and their contents.
|
||||
.Pp
|
||||
There are two syntaxes for comments. The most common is that anything that follows a semicolon
|
||||
There are three syntaxes for comments.
|
||||
The most common is that anything that follows a semicolon
|
||||
.Ql \&;
|
||||
not inside a string, is a comment until the end of the line.
|
||||
The other is that lines beginning with a
|
||||
The second is a block comment, beginning with
|
||||
.Ql /*
|
||||
and ending with
|
||||
.Ql */ .
|
||||
It can be split across multiple lines, or occur in the middle of an expression:
|
||||
.Bd -literal -offset indent
|
||||
X = /* the value of x
|
||||
should be 3 */ 3
|
||||
.Ed
|
||||
The third is that lines beginning with a
|
||||
.Ql *
|
||||
(not even spaces before it) are ignored.
|
||||
This second syntax is deprecated (will be removed in a future version) and should be replaced with the first one.
|
||||
This third syntax is deprecated (will be removed in a future version) and should be replaced with either of the first two.
|
||||
.Pp
|
||||
Sometimes lines can be too long and it may be necessary to split them.
|
||||
To do so, put a backslash at the end of the line:
|
||||
@@ -72,7 +80,6 @@ like this:
|
||||
"world!")
|
||||
.Ed
|
||||
.Sh EXPRESSIONS
|
||||
.Pp
|
||||
An expression can be composed of many things.
|
||||
Numerical expressions are always evaluated using signed 32-bit math.
|
||||
Zero is considered to be the only "false" number, all non-zero numbers (including negative) are "true".
|
||||
@@ -86,9 +93,7 @@ section.
|
||||
.Pp
|
||||
The instructions in the macro-language generally require constant expressions.
|
||||
.Ss Numeric Formats
|
||||
.Pp
|
||||
There are a number of numeric formats.
|
||||
.Pp
|
||||
.Bl -column -offset indent "Fixed point (16.16)" "Prefix"
|
||||
.It Sy Format type Ta Sy Prefix Ta Sy Accepted characters
|
||||
.It Hexadecimal Ta $ Ta 0123456789ABCDEF
|
||||
@@ -119,9 +124,7 @@ is equivalent to
|
||||
.Pp
|
||||
You can also use symbols, which are implicitly replaced with their value.
|
||||
.Ss Operators
|
||||
.Pp
|
||||
A great number of operators you can use in expressions are available (listed from highest to lowest precedence):
|
||||
.Pp
|
||||
.Bl -column -offset indent "!= == <= >= < >"
|
||||
.It Sy Operator Ta Sy Meaning
|
||||
.It Li \&( \&) Ta Precedence override
|
||||
@@ -167,7 +170,6 @@ and
|
||||
.Pp
|
||||
! returns 1 if the operand was 0, and 0 otherwise.
|
||||
.Ss Fixed‐point Expressions
|
||||
.Pp
|
||||
Fixed-point numbers are basically normal (32-bit) integers, which count 65536th's instead of entire units, offering better precision than integers but limiting the range of values.
|
||||
The upper 16 bits are used for the integer part and the lower 16 bits are used for the fraction (65536ths).
|
||||
Since they are still akin to integers, you can use them in normal integer expressions, and some integer operators like
|
||||
@@ -182,7 +184,6 @@ The following functions are designed to operate with fixed-point numbers:
|
||||
.EQ
|
||||
delim $$
|
||||
.EN
|
||||
.Pp
|
||||
.Bl -column -offset indent "ATAN2(x, y)"
|
||||
.It Sy Name Ta Sy Operation
|
||||
.It Fn DIV x y Ta $x \[di] y$
|
||||
@@ -213,7 +214,6 @@ ANGLE = ANGLE + 256.0 ; 256 = 65536 / table_len, with table_len = 256
|
||||
ENDR
|
||||
.Ed
|
||||
.Ss String Expressions
|
||||
.Pp
|
||||
The most basic string expression is any number of characters contained in double quotes
|
||||
.Pq Ql \&"for instance" .
|
||||
The backslash character
|
||||
@@ -222,8 +222,7 @@ is special in that it causes the character following it to be
|
||||
.Dq escaped ,
|
||||
meaning that it is treated differently from normal.
|
||||
There are a number of escape sequences you can use within a string:
|
||||
.Pp
|
||||
.Bl -column -offset indent "'\1' - '\9'"
|
||||
.Bl -column -offset indent "Qo \[rs]1 Qc \[en] Qo \[rs]9 Qc"
|
||||
.It Sy String Ta Sy Meaning
|
||||
.It Ql \[rs]\[rs] Ta Produces a backslash
|
||||
.It Ql \[rs]" Ta Produces a double quote without terminating
|
||||
@@ -278,19 +277,17 @@ The symbol's value is again inserted directly.
|
||||
.Pp
|
||||
The following functions operate on string expressions.
|
||||
Most of them return a string, however some of these functions actually return an integer and can be used as part of an integer expression!
|
||||
.Pp
|
||||
.Bl -column "STRSUB(str, pos, len)"
|
||||
.It Sy Name Ta Sy Operation
|
||||
.It Fn STRLEN string Ta Returns the number of characters in Ar string .
|
||||
.It Fn STRCAT str1 str2 Ta Appends Ar str2 No to Ar str1 .
|
||||
.It Fn STRCMP str1 str2 Ta Returns negative if Ar str1 No is alphabetically lower than Ar str2 No , zero if they match, positive if Ar str1 No is greater than Ar str2 .
|
||||
.It Fn STRCMP str1 str2 Ta Returns -1 if Ar str1 No is alphabetically lower than Ar str2 No , zero if they match, 1 if Ar str1 No is greater than Ar str2 .
|
||||
.It Fn STRIN str1 str2 Ta Returns the position of Ar str2 No in Ar str1 No or zero if it's not present Pq first character is position 1 .
|
||||
.It Fn STRSUB str pos len Ta Returns a substring from Ar str No starting at Ar pos Po first character is position 1 Pc and Ar len No characters long.
|
||||
.It Fn STRUPR str Ta Converts all characters in Ar str No to capitals and returns the new string.
|
||||
.It Fn STRLWR str Ta Converts all characters in Ar str No to lower case and returns the new string.
|
||||
.El
|
||||
.Ss Character maps
|
||||
.Pp
|
||||
When writing text that is meant to be displayed in the Game Boy, the characters used in the source code may have a different encoding than the default of ASCII.
|
||||
For example, the tiles used for uppercase letters may be placed starting at tile index 128, which makes it difficult to add text strings to the ROM.
|
||||
.Pp
|
||||
@@ -325,9 +322,7 @@ This means that any string that the code may want to print as debug information
|
||||
The output value of a mapping can be 0.
|
||||
If this happens, the assembler will treat this as the end of the string and the rest of it will be trimmed.
|
||||
.Ss Other functions
|
||||
.Pp
|
||||
There are a few other functions that do various useful things:
|
||||
.Pp
|
||||
.Bl -column "DEF(label)"
|
||||
.It Sy Name Ta Sy Operation
|
||||
.It Fn BANK arg Ta Returns a bank number.
|
||||
@@ -356,7 +351,6 @@ String symbols are not expanded within the parentheses.
|
||||
or 0 if only RGBLINK can compute its value.
|
||||
.El
|
||||
.Sh SECTIONS
|
||||
.Pp
|
||||
Before you can start writing code, you must define a section.
|
||||
This tells the assembler what kind of information follows and, if it is code, where to put it.
|
||||
.Pp
|
||||
@@ -373,7 +367,6 @@ All other sections must have a unique name, even in different source files, or t
|
||||
Possible section
|
||||
.Ar type Ns s
|
||||
are as follows:
|
||||
.Pp
|
||||
.Bl -tag
|
||||
.It Ic ROM0
|
||||
A ROM section.
|
||||
@@ -559,7 +552,6 @@ SECTION "VRAM Data",ROMX,BANK[2],ALIGN[4] ;\ align to 16 bytes
|
||||
.Ed
|
||||
.El
|
||||
.Ss Section Stack
|
||||
.Pp
|
||||
.Ic POPS
|
||||
and
|
||||
.Ic PUSHS
|
||||
@@ -572,7 +564,6 @@ will push the current section context on the section stack.
|
||||
can then later be used to restore it.
|
||||
Useful for defining sections in included files when you don't want to override the section context at the point the file was included.
|
||||
.Ss RAM Code
|
||||
.Pp
|
||||
Sometimes you want to have some code in RAM.
|
||||
But then you can't simply put it in a RAM section, you have to store it in ROM and copy it to RAM at some point.
|
||||
.Pp
|
||||
@@ -633,7 +624,6 @@ You cannot nest
|
||||
.Ic LOAD
|
||||
blocks, nor can you change the current section within them.
|
||||
.Ss Unionized Sections
|
||||
.Pp
|
||||
When you're tight on RAM, you may want to define overlapping blocks of variables, as explained in the
|
||||
.Sx Unions
|
||||
section.
|
||||
@@ -681,6 +671,11 @@ Similarly, the size of an unionized section is the largest of all its declaratio
|
||||
.Ss Section Fragments
|
||||
Section fragments are sections with a small twist: when several of the same name are encountered, they are concatenated instead of producing an error.
|
||||
This works within the same file (paralleling the behavior "plain" sections has in previous versions), but also across object files.
|
||||
To declare an section fragment, add a
|
||||
.Ic FRAGMENT
|
||||
keyword after the
|
||||
.Ic SECTION
|
||||
one; the declaration is otherwise not different.
|
||||
However, similarly to
|
||||
.Sx Unionized Sections ,
|
||||
some rules must be followed:
|
||||
@@ -722,12 +717,11 @@ and the one from
|
||||
.Ql bar.o
|
||||
last.
|
||||
.Sh SYMBOLS
|
||||
.Pp
|
||||
RGBDS supports several types of symbols:
|
||||
.Pp
|
||||
.Bl -hang
|
||||
.It Sy Label
|
||||
Numerical symbol designating a memory location. May or may not have a value known at assembly time.
|
||||
Numerical symbol designating a memory location.
|
||||
May or may not have a value known at assembly time.
|
||||
.It Sy Constant
|
||||
Numerical symbol whose value has to be known at assembly time.
|
||||
.It Sy Macro
|
||||
@@ -738,12 +732,18 @@ code that can be invoked later.
|
||||
String symbol that can be evaluated, similarly to a macro.
|
||||
.El
|
||||
.Pp
|
||||
Symbol names can contain letters, numbers, underscores, hashes and
|
||||
Symbol names can contain letters, numbers, underscores
|
||||
.Sq _ ,
|
||||
hashes
|
||||
.Sq #
|
||||
and at signs
|
||||
.Sq @ .
|
||||
However, they must begin with either a letter, a number, or an underscore.
|
||||
Periods are allowed exclusively for labels, as described below.
|
||||
However, they must begin with either a letter, or an underscore.
|
||||
Periods
|
||||
.Sq \&.
|
||||
are allowed exclusively for labels, as described below.
|
||||
A symbol cannot have the same name as a reserved keyword.
|
||||
.Em \&In the line where a symbol is defined there mustn't be any whitespace before it ,
|
||||
.Em \&In the line where a symbol is defined there must not be any whitespace before it ,
|
||||
otherwise
|
||||
.Nm
|
||||
will treat it as a macro invocation.
|
||||
@@ -841,7 +841,6 @@ str_SIZEOF EQU 259
|
||||
.Ed
|
||||
.Pp
|
||||
There are five commands in the RS group of commands:
|
||||
.Pp
|
||||
.Bl -column "RSSET constexpr"
|
||||
.It Sy Command Ta Sy Meaning
|
||||
.It Ic RSRESET Ta Equivalent to Ql RSSET 0 .
|
||||
@@ -852,6 +851,12 @@ There are five commands in the RS group of commands:
|
||||
(In practice, this one cannot be used due to a bug).
|
||||
.El
|
||||
.Pp
|
||||
If the argument to
|
||||
.Ic RB , RW ,
|
||||
or
|
||||
.Ic RL
|
||||
is omitted, it's assumed to be 1.
|
||||
.Pp
|
||||
Note that colons
|
||||
.Ql \&:
|
||||
following the name are not allowed.
|
||||
@@ -918,9 +923,28 @@ Note that a single colon
|
||||
.Ql \&:
|
||||
following the macro's name is required.
|
||||
Macros can't be exported or imported.
|
||||
.Pp
|
||||
Plainly nesting macro definitions is not allowed, but this can be worked around using
|
||||
.Ic EQUS .
|
||||
This won't work:
|
||||
.Bd -literal -offset indent
|
||||
outer: MACRO
|
||||
inner: MACRO
|
||||
PRINTT "Hello!\[rs]n"
|
||||
ENDM
|
||||
ENDM
|
||||
.Ed
|
||||
.Pp
|
||||
But this will:
|
||||
.Bd -literal -offset indent
|
||||
outer: MACRO
|
||||
definition equs "inner: MACRO\[rs]nPRINTT \[rs]"Hello!\[rs]\[rs]n\[rs]"\[rs]nENDM"
|
||||
definition
|
||||
PURGE definition
|
||||
ENDM
|
||||
.Ed
|
||||
.El
|
||||
.Ss Exporting and importing symbols
|
||||
.Pp
|
||||
Importing and exporting of symbols is a feature that is very useful when your project spans many source files and, for example, you need to jump to a routine defined in another file.
|
||||
.Pp
|
||||
Exporting of symbols has to be done manually, importing is done automatically if
|
||||
@@ -932,15 +956,56 @@ The following will cause
|
||||
and so on to be accessible to other files during the link process:
|
||||
.Dl Ic EXPORT Ar symbol1 Bq , Ar symbol2 , No ...
|
||||
.Pp
|
||||
For example, if you have the following three files:
|
||||
.Pp
|
||||
.Ql a.asm :
|
||||
.Bd -literal -compact
|
||||
SECTION "a", WRAM0
|
||||
LabelA:
|
||||
.Ed
|
||||
.Pp
|
||||
.Ql b.asm :
|
||||
.Bd -literal -compact
|
||||
SECTION "b", WRAM0
|
||||
ExportedLabelB1::
|
||||
ExportedLabelB2:
|
||||
EXPORT ExportedLabelB2
|
||||
.Ed
|
||||
.Pp
|
||||
.Ql c.asm :
|
||||
.Bd -literal -compact
|
||||
SECTION "C", ROM0[0]
|
||||
dw LabelA
|
||||
dw ExportedLabelB1
|
||||
dw ExportedLabelB2
|
||||
.Ed
|
||||
.Pp
|
||||
Then
|
||||
.Ql c.asm
|
||||
can use
|
||||
.Ql ExportedLabelB1
|
||||
and
|
||||
.Ql ExportedLabelB2 ,
|
||||
but not
|
||||
.Ql LabelA ,
|
||||
so linking them together will fail:
|
||||
.Bd -literal
|
||||
$ rgbasm -o a.o a.asm
|
||||
$ rgbasm -o b.o b.asm
|
||||
$ rgbasm -o c.o c.asm
|
||||
$ rgblink a.o b.o c.o
|
||||
error: c.asm(2): Unknown symbol "LabelA"
|
||||
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 .
|
||||
.Pp
|
||||
.Ic GLOBAL
|
||||
is a deprecated synonym for
|
||||
.Ic EXPORT ,
|
||||
do not use it.
|
||||
.Pp
|
||||
Note also that only exported symbols will appear in symbol and map files produced by
|
||||
.Xr rgblink 1 .
|
||||
.Ss Purging symbols
|
||||
.Pp
|
||||
.Ic PURGE
|
||||
allows you to completely remove a symbol from the symbol table as if it had never existed.
|
||||
.Em USE WITH EXTREME CAUTION!!!
|
||||
@@ -959,15 +1024,13 @@ Note that, as an exception, string symbols in the argument list of a
|
||||
command
|
||||
.Em will not be expanded .
|
||||
.Ss Predeclared Symbols
|
||||
.Pp
|
||||
The following symbols are defined by the assembler:
|
||||
.Pp
|
||||
.Bl -column -offset indent "EQUS" "__ISO_8601_LOCAL__"
|
||||
.It Sy Type Ta Sy Name Ta Sy Contents
|
||||
.It Ic EQU Ta Dv @ Ta PC value
|
||||
.It Ic EQU Ta Dv @ Ta PC value (essentially, the current memory address)
|
||||
.It Ic EQU Ta Dv _PI Ta Fixed point \[*p]
|
||||
.It Ic SET Ta Dv _RS Ta _RS Counter
|
||||
.It Ic EQU Ta Dv _NARG Ta Number of arguments passed to macro
|
||||
.It Ic EQU Ta Dv _NARG Ta Number of arguments passed to macro, updated by Ic SHIFT
|
||||
.It Ic EQU Ta Dv __LINE__ Ta The current line number
|
||||
.It Ic EQUS Ta Dv __FILE__ Ta The current filename
|
||||
.It Ic EQUS Ta Dv __DATE__ Ta Today's date
|
||||
@@ -986,7 +1049,6 @@ The following symbols are defined by the assembler:
|
||||
.El
|
||||
.Sh DEFINING DATA
|
||||
.Ss Declaring variables in a RAM section
|
||||
.Pp
|
||||
.Ic DS
|
||||
allocates a number of empty bytes.
|
||||
This is the preferred method of allocating space in a RAM section.
|
||||
@@ -1007,7 +1069,6 @@ In ROM sections, it will be filled with the value passed to the
|
||||
command-line option, except when using overlays with
|
||||
.Fl O .
|
||||
.Ss Defining constant data
|
||||
.Pp
|
||||
.Ic DB
|
||||
defines a list of bytes that will be stored in the final image.
|
||||
Ideal for tables and text.
|
||||
@@ -1059,7 +1120,6 @@ can be used in a
|
||||
.Ic SRAM
|
||||
section.
|
||||
.Ss Including binary files
|
||||
.Pp
|
||||
You probably have some graphics, level data, etc. you'd like to include.
|
||||
Use
|
||||
.Ic INCBIN
|
||||
@@ -1080,8 +1140,10 @@ The example below includes 256 bytes from data.bin, starting from byte 78.
|
||||
.Bd -literal -offset indent
|
||||
INCBIN "data.bin",78,256
|
||||
.Ed
|
||||
.Ss Unions
|
||||
.Pp
|
||||
The length argument is optional.
|
||||
If only the start position is specified, the bytes from the start position until the end of the file will be included.
|
||||
.Ss Unions
|
||||
Unions allow multiple memory allocations to overlap, like unions in C.
|
||||
This does not increase the amount of memory available, but allows re-using the same memory region for different purposes.
|
||||
.Pp
|
||||
@@ -1133,7 +1195,6 @@ like commands (see
|
||||
.Sx Declaring variables in a RAM section ) .
|
||||
.Sh THE MACRO LANGUAGE
|
||||
.Ss Invoking macros
|
||||
.Pp
|
||||
You execute the macro by inserting its name.
|
||||
.Bd -literal -offset indent
|
||||
add a,b
|
||||
@@ -1195,7 +1256,6 @@ Also, a macro can have inside an
|
||||
.Sy EQUS
|
||||
which references the same macro, which has the same problem.
|
||||
.Pp
|
||||
.Pp
|
||||
It's possible to pass arguments to macros as well!
|
||||
You retrieve the arguments by using the escape sequences
|
||||
.Ic \[rs]1
|
||||
@@ -1269,7 +1329,9 @@ is a special command only available in macros.
|
||||
Very useful in
|
||||
.Ic REPT
|
||||
blocks.
|
||||
It will shift the arguments by one to the left.
|
||||
It will shift the arguments by one to the left, and decrease
|
||||
.Dv _NARG
|
||||
by 1.
|
||||
.Ic \[rs]1
|
||||
will get the value of
|
||||
.Ic \[rs]2 , \[rs]2
|
||||
@@ -1282,7 +1344,6 @@ This is the only way of accessing the value of arguments from 10 to 256.
|
||||
.Ic SHIFT
|
||||
can optionally be given an integer parameter, and will apply the above shifting that number of times.
|
||||
.Ss Printing things during assembly
|
||||
.Pp
|
||||
The next four commands print text and values to the standard output.
|
||||
Useful for debugging macros, or wherever you may feel the need to tell yourself some important information.
|
||||
.Bd -literal -offset indent
|
||||
@@ -1291,7 +1352,6 @@ PRINTI (2 + 3) / 5
|
||||
PRINTV $FF00 + $F0
|
||||
PRINTF MUL(3.14, 3987.0)
|
||||
.Ed
|
||||
.Pp
|
||||
.Bl -inset
|
||||
.It Ic PRINTT
|
||||
prints out a string.
|
||||
@@ -1310,7 +1370,6 @@ prints out a fixed point value.
|
||||
Be careful that none of those automatically print a line feed; if you need one, use
|
||||
.Ic PRINTT "\[rs]n" .
|
||||
.Ss Automatically repeating blocks of code
|
||||
.Pp
|
||||
Suppose you want to unroll a time consuming loop without copy-pasting it.
|
||||
.Ic REPT
|
||||
is here for that purpose.
|
||||
@@ -1347,7 +1406,6 @@ As in macros, you can also use the escape sequence
|
||||
.Ic REPT
|
||||
blocks can be nested.
|
||||
.Ss Aborting the assembly process
|
||||
.Pp
|
||||
.Ic FAIL
|
||||
and
|
||||
.Ic WARN
|
||||
@@ -1368,7 +1426,6 @@ If you need to ensure some assumption is correct when compiling, you can use
|
||||
and
|
||||
.Ic STATIC_ASSERT .
|
||||
Syntax examples are given below:
|
||||
.Pp
|
||||
.Bd -literal -offset indent
|
||||
Function:
|
||||
xor a
|
||||
@@ -1419,11 +1476,10 @@ to be emitted;
|
||||
.Ic FATAL
|
||||
immediately aborts.
|
||||
.Ss Including other source files
|
||||
.Pp
|
||||
Use
|
||||
.Ic INCLUDE
|
||||
to process another assembler file and then return to the current file when done.
|
||||
If the file isn't found in the current directory the include path list (see the
|
||||
If the file isn't found in the current directory, the include path list (see the
|
||||
.Fl i
|
||||
option in
|
||||
.Xr rgbasm 1 )
|
||||
@@ -1435,7 +1491,6 @@ calls infinitely (or until you run out of memory, whichever comes first).
|
||||
INCLUDE "irq.inc"
|
||||
.Ed
|
||||
.Ss Conditional assembling
|
||||
.Pp
|
||||
The four commands
|
||||
.Ic IF , ELIF , ELSE ,
|
||||
and
|
||||
@@ -1485,7 +1540,6 @@ Also, if there is more than one
|
||||
block, all of them but the first one are ignored.
|
||||
.Sh MISCELLANEOUS
|
||||
.Ss Changing options while assembling
|
||||
.Pp
|
||||
.Ic OPT
|
||||
can be used to change some of the options during assembling from within the source, instead of defining them on the command-line.
|
||||
.Pp
|
||||
@@ -1515,7 +1569,6 @@ can then later be used to restore them.
|
||||
Useful if you want to change some options in an include file and you don't want to destroy the options set by the program that included your file.
|
||||
The stack's number of entries is limited only by the amount of memory in your machine.
|
||||
.Ss Requesting alignment
|
||||
.Pp
|
||||
While
|
||||
.Ic ALIGN
|
||||
as presented in
|
||||
@@ -1543,9 +1596,8 @@ is a shorthand for
|
||||
.Xr rgbds 7 ,
|
||||
.Xr gbz80 7
|
||||
.Sh HISTORY
|
||||
.Pp
|
||||
.Nm
|
||||
was originally written by Carsten S\(/orensen as part of the ASMotor package,
|
||||
and was later packaged in RGBDS by Justin Lloyd.
|
||||
It is now maintained by a number of contributors at
|
||||
.Lk https://github.com/rednex/rgbds .
|
||||
.Lk https://github.com/gbdev/rgbds .
|
||||
|
||||
140
src/asm/rpn.c
140
src/asm/rpn.c
@@ -31,7 +31,7 @@
|
||||
/* If we had `asprintf` this would be great, but alas. */ \
|
||||
_expr->reason = malloc(128); /* Use an initial reasonable size */ \
|
||||
if (!_expr->reason) \
|
||||
fatalerror("Can't allocate err string: %s", strerror(errno)); \
|
||||
fatalerror("Can't allocate err string: %s\n", strerror(errno)); \
|
||||
int size = snprintf(_expr->reason, 128, __VA_ARGS__); \
|
||||
if (size >= 128) { /* If this wasn't enough, try again */ \
|
||||
_expr->reason = realloc(_expr->reason, size + 1); \
|
||||
@@ -46,22 +46,23 @@ static uint8_t *reserveSpace(struct Expression *expr, uint32_t size)
|
||||
/* If there isn't enough room to reserve the space, realloc */
|
||||
if (!expr->tRPN)
|
||||
expr->nRPNCapacity = 256; /* Initial size */
|
||||
else if (expr->nRPNCapacity >= MAXRPNLEN)
|
||||
/*
|
||||
* To avoid generating humongous object files, cap the
|
||||
* size of RPN expressions
|
||||
*/
|
||||
fatalerror("RPN expression cannot grow larger than %lu bytes",
|
||||
(unsigned long)MAXRPNLEN);
|
||||
else if (expr->nRPNCapacity > MAXRPNLEN / 2)
|
||||
expr->nRPNCapacity = MAXRPNLEN;
|
||||
else
|
||||
expr->nRPNCapacity *= 2;
|
||||
while (expr->nRPNCapacity - expr->nRPNLength < size) {
|
||||
if (expr->nRPNCapacity >= MAXRPNLEN)
|
||||
/*
|
||||
* To avoid generating humongous object files, cap the
|
||||
* size of RPN expressions
|
||||
*/
|
||||
fatalerror("RPN expression cannot grow larger than "
|
||||
EXPAND_AND_STR(MAXRPNLEN) " bytes\n");
|
||||
else if (expr->nRPNCapacity > MAXRPNLEN / 2)
|
||||
expr->nRPNCapacity = MAXRPNLEN;
|
||||
else
|
||||
expr->nRPNCapacity *= 2;
|
||||
}
|
||||
expr->tRPN = realloc(expr->tRPN, expr->nRPNCapacity);
|
||||
|
||||
if (!expr->tRPN)
|
||||
fatalerror("Failed to grow RPN expression: %s",
|
||||
strerror(errno));
|
||||
fatalerror("Failed to grow RPN expression: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
uint8_t *ptr = expr->tRPN + expr->nRPNLength;
|
||||
@@ -103,28 +104,26 @@ void rpn_Number(struct Expression *expr, uint32_t i)
|
||||
expr->nVal = i;
|
||||
}
|
||||
|
||||
void rpn_Symbol(struct Expression *expr, char *tzSym)
|
||||
void rpn_Symbol(struct Expression *expr, char const *tzSym)
|
||||
{
|
||||
struct Symbol *sym = sym_FindSymbol(tzSym);
|
||||
struct Symbol *sym = sym_FindScopedSymbol(tzSym);
|
||||
|
||||
if (sym_IsPC(sym) && !sect_GetSymbolSection()) {
|
||||
yyerror("PC has no value outside a section");
|
||||
error("PC has no value outside a section\n");
|
||||
rpn_Number(expr, 0);
|
||||
} else if (!sym || !sym_IsConstant(sym)) {
|
||||
rpn_Init(expr);
|
||||
expr->isSymbol = true;
|
||||
|
||||
sym_Ref(tzSym);
|
||||
makeUnknown(expr, sym_IsPC(sym)
|
||||
? "PC is not constant at assembly time"
|
||||
: "'%s' is not constant at assembly time",
|
||||
tzSym);
|
||||
makeUnknown(expr, sym_IsPC(sym) ? "PC is not constant at assembly time"
|
||||
: "'%s' is not constant at assembly time", tzSym);
|
||||
sym = sym_Ref(tzSym);
|
||||
expr->nRPNPatchSize += 5; /* 1-byte opcode + 4-byte symbol ID */
|
||||
|
||||
size_t nameLen = strlen(tzSym) + 1; /* Don't forget NUL! */
|
||||
size_t nameLen = strlen(sym->name) + 1; /* Don't forget NUL! */
|
||||
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
||||
*ptr++ = RPN_SYM;
|
||||
memcpy(ptr, tzSym, nameLen);
|
||||
memcpy(ptr, sym->name, nameLen);
|
||||
|
||||
/* RGBLINK assumes PC is at the byte being computed... */
|
||||
if (sym_IsPC(sym) && nPCOffset) {
|
||||
@@ -145,20 +144,20 @@ void rpn_BankSelf(struct Expression *expr)
|
||||
rpn_Init(expr);
|
||||
|
||||
if (!pCurrentSection) {
|
||||
yyerror("PC has no bank outside a section");
|
||||
error("PC has no bank outside a section\n");
|
||||
expr->nVal = 1;
|
||||
} else if (pCurrentSection->nBank == -1) {
|
||||
} else if (pCurrentSection->bank == -1) {
|
||||
makeUnknown(expr, "Current section's bank is not known");
|
||||
expr->nRPNPatchSize++;
|
||||
*reserveSpace(expr, 1) = RPN_BANK_SELF;
|
||||
} else {
|
||||
expr->nVal = pCurrentSection->nBank;
|
||||
expr->nVal = pCurrentSection->bank;
|
||||
}
|
||||
}
|
||||
|
||||
void rpn_BankSymbol(struct Expression *expr, char const *tzSym)
|
||||
{
|
||||
struct Symbol const *sym = sym_FindSymbol(tzSym);
|
||||
struct Symbol const *sym = sym_FindScopedSymbol(tzSym);
|
||||
|
||||
/* The @ symbol is treated differently. */
|
||||
if (sym_IsPC(sym)) {
|
||||
@@ -168,24 +167,22 @@ void rpn_BankSymbol(struct Expression *expr, char const *tzSym)
|
||||
|
||||
rpn_Init(expr);
|
||||
if (sym && !sym_IsLabel(sym)) {
|
||||
yyerror("BANK argument must be a label");
|
||||
error("BANK argument must be a label\n");
|
||||
} else {
|
||||
sym_Ref(tzSym);
|
||||
if (!sym)
|
||||
/* If the symbol didn't exist, `sym_Ref` created it */
|
||||
sym = sym_FindSymbol(tzSym);
|
||||
sym = sym_Ref(tzSym);
|
||||
assert(sym); // If the symbol didn't exist, it should have been created
|
||||
|
||||
if (sym_GetSection(sym) && sym_GetSection(sym)->nBank != -1) {
|
||||
if (sym_GetSection(sym) && sym_GetSection(sym)->bank != -1) {
|
||||
/* Symbol's section is known and bank is fixed */
|
||||
expr->nVal = sym_GetSection(sym)->nBank;
|
||||
expr->nVal = sym_GetSection(sym)->bank;
|
||||
} else {
|
||||
makeUnknown(expr, "\"%s\"'s bank is not known", tzSym);
|
||||
expr->nRPNPatchSize += 5; /* opcode + 4-byte sect ID */
|
||||
|
||||
size_t nameLen = strlen(tzSym) + 1; /* Room for NUL! */
|
||||
size_t nameLen = strlen(sym->name) + 1; /* Room for NUL! */
|
||||
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
||||
*ptr++ = RPN_BANK_SYM;
|
||||
memcpy(ptr, tzSym, nameLen);
|
||||
memcpy(ptr, sym->name, nameLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -196,8 +193,8 @@ void rpn_BankSection(struct Expression *expr, char const *tzSectionName)
|
||||
|
||||
struct Section *pSection = out_FindSectionByName(tzSectionName);
|
||||
|
||||
if (pSection && pSection->nBank != -1) {
|
||||
expr->nVal = pSection->nBank;
|
||||
if (pSection && pSection->bank != -1) {
|
||||
expr->nVal = pSection->bank;
|
||||
} else {
|
||||
makeUnknown(expr, "Section \"%s\"'s bank is not known",
|
||||
tzSectionName);
|
||||
@@ -223,8 +220,7 @@ void rpn_CheckHRAM(struct Expression *expr, const struct Expression *src)
|
||||
/* That range is valid, but only keep the lower byte */
|
||||
expr->nVal &= 0xFF;
|
||||
} else if (expr->nVal < 0 || expr->nVal > 0xFF) {
|
||||
yyerror("Source address $%" PRIx32 " not between $FF00 to $FFFF",
|
||||
expr->nVal);
|
||||
error("Source address $%" PRIx32 " not between $FF00 to $FFFF\n", expr->nVal);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,8 +231,7 @@ void rpn_CheckRST(struct Expression *expr, const struct Expression *src)
|
||||
if (rpn_isKnown(expr)) {
|
||||
/* A valid RST address must be masked with 0x38 */
|
||||
if (expr->nVal & ~0x38)
|
||||
yyerror("Invalid address $%" PRIx32 " for RST",
|
||||
expr->nVal);
|
||||
error("Invalid address $%" PRIx32 " for RST\n", expr->nVal);
|
||||
/* The target is in the "0x38" bits, all other bits are set */
|
||||
expr->nVal |= 0xC7;
|
||||
} else {
|
||||
@@ -263,8 +258,8 @@ static int32_t shift(int32_t shiftee, int32_t amount)
|
||||
if (amount >= 0) {
|
||||
// Left shift
|
||||
if (amount >= 32) {
|
||||
warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %" PRId32,
|
||||
amount);
|
||||
warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %"
|
||||
PRId32 "\n", amount);
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
@@ -279,8 +274,8 @@ static int32_t shift(int32_t shiftee, int32_t amount)
|
||||
// Right shift
|
||||
amount = -amount;
|
||||
if (amount >= 32) {
|
||||
warning(WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32,
|
||||
amount);
|
||||
warning(WARNING_SHIFT_AMOUNT,
|
||||
"Shifting right by large amount %" PRId32 "\n", amount);
|
||||
return shiftee < 0 ? -1 : 0;
|
||||
|
||||
} else if (shiftee >= 0) {
|
||||
@@ -297,25 +292,30 @@ static int32_t shift(int32_t shiftee, int32_t amount)
|
||||
}
|
||||
}
|
||||
|
||||
static struct Symbol const *symbolOf(struct Expression const *expr)
|
||||
struct Symbol const *rpn_SymbolOf(struct Expression const *expr)
|
||||
{
|
||||
if (!rpn_isSymbol(expr))
|
||||
return NULL;
|
||||
return sym_FindSymbol((char *)expr->tRPN + 1);
|
||||
return sym_FindScopedSymbol((char *)expr->tRPN + 1);
|
||||
}
|
||||
|
||||
bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *sym)
|
||||
{
|
||||
/* Check if both expressions only refer to a single symbol */
|
||||
struct Symbol const *sym1 = rpn_SymbolOf(src);
|
||||
|
||||
if (!sym1 || !sym || sym1->type != SYM_LABEL || sym->type != SYM_LABEL)
|
||||
return false;
|
||||
|
||||
struct Section const *section1 = sym_GetSection(sym1);
|
||||
struct Section const *section2 = sym_GetSection(sym);
|
||||
return section1 && (section1 == section2);
|
||||
}
|
||||
|
||||
static bool isDiffConstant(struct Expression const *src1,
|
||||
struct Expression const *src2)
|
||||
{
|
||||
/* Check if both expressions only refer to a single symbol */
|
||||
struct Symbol const *symbol1 = symbolOf(src1);
|
||||
struct Symbol const *symbol2 = symbolOf(src2);
|
||||
|
||||
if (!symbol1 || !symbol2
|
||||
|| symbol1->type != SYM_LABEL || symbol2->type != SYM_LABEL)
|
||||
return false;
|
||||
|
||||
return sym_GetSection(symbol1) == sym_GetSection(symbol2);
|
||||
return rpn_IsDiffConstant(src1, rpn_SymbolOf(src2));
|
||||
}
|
||||
|
||||
void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
||||
@@ -373,18 +373,20 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
||||
break;
|
||||
case RPN_SHL:
|
||||
if (src2->nVal < 0)
|
||||
warning(WARNING_SHIFT_AMOUNT, "Shifting left by negative amount %" PRId32,
|
||||
warning(WARNING_SHIFT_AMOUNT,
|
||||
"Shifting left by negative amount %" PRId32 "\n",
|
||||
src2->nVal);
|
||||
|
||||
expr->nVal = shift(src1->nVal, src2->nVal);
|
||||
break;
|
||||
case RPN_SHR:
|
||||
if (src1->nVal < 0)
|
||||
warning(WARNING_SHIFT, "Shifting negative value %" PRId32,
|
||||
warning(WARNING_SHIFT, "Shifting negative value %" PRId32 "\n",
|
||||
src1->nVal);
|
||||
|
||||
if (src2->nVal < 0)
|
||||
warning(WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32,
|
||||
warning(WARNING_SHIFT_AMOUNT,
|
||||
"Shifting right by negative amount %" PRId32 "\n",
|
||||
src2->nVal);
|
||||
|
||||
expr->nVal = shift(src1->nVal, -src2->nVal);
|
||||
@@ -394,11 +396,11 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
||||
break;
|
||||
case RPN_DIV:
|
||||
if (src2->nVal == 0)
|
||||
fatalerror("Division by zero");
|
||||
fatalerror("Division by zero\n");
|
||||
|
||||
if (src1->nVal == INT32_MIN
|
||||
&& src2->nVal == -1) {
|
||||
warning(WARNING_DIV, "Division of min value by -1");
|
||||
if (src1->nVal == INT32_MIN && src2->nVal == -1) {
|
||||
warning(WARNING_DIV, "Division of %" PRId32 " by -1 yields %"
|
||||
PRId32 "\n", INT32_MIN, INT32_MIN);
|
||||
expr->nVal = INT32_MIN;
|
||||
} else {
|
||||
expr->nVal = src1->nVal / src2->nVal;
|
||||
@@ -406,7 +408,7 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
||||
break;
|
||||
case RPN_MOD:
|
||||
if (src2->nVal == 0)
|
||||
fatalerror("Division by zero");
|
||||
fatalerror("Division by zero\n");
|
||||
|
||||
if (src1->nVal == INT32_MIN && src2->nVal == -1)
|
||||
expr->nVal = 0;
|
||||
@@ -424,12 +426,12 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
||||
case RPN_RST:
|
||||
case RPN_CONST:
|
||||
case RPN_SYM:
|
||||
fatalerror("%d is not a binary operator", op);
|
||||
fatalerror("%d is not a binary operator\n", op);
|
||||
}
|
||||
|
||||
} else if (op == RPN_SUB && isDiffConstant(src1, src2)) {
|
||||
struct Symbol const *symbol1 = symbolOf(src1);
|
||||
struct Symbol const *symbol2 = symbolOf(src2);
|
||||
struct Symbol const *symbol1 = rpn_SymbolOf(src1);
|
||||
struct Symbol const *symbol2 = rpn_SymbolOf(src2);
|
||||
|
||||
expr->nVal = sym_GetValue(symbol1) - sym_GetValue(symbol2);
|
||||
expr->isKnown = true;
|
||||
|
||||
@@ -17,15 +17,22 @@
|
||||
#include "platform.h" // strdup
|
||||
|
||||
struct SectionStackEntry {
|
||||
struct Section *pSection;
|
||||
struct Symbol *pScope; /* Section's symbol scope */
|
||||
struct Section *section;
|
||||
char const *scope; /* Section's symbol scope */
|
||||
uint32_t offset;
|
||||
struct SectionStackEntry *pNext;
|
||||
struct SectionStackEntry *next;
|
||||
};
|
||||
|
||||
struct SectionStackEntry *pSectionStack;
|
||||
struct SectionStackEntry *sectionStack;
|
||||
uint32_t curOffset; /* Offset into the current section (see sect_GetSymbolOffset) */
|
||||
static struct Section *currentLoadSection = NULL;
|
||||
uint32_t loadOffset = 0; /* The offset of the LOAD section within its parent */
|
||||
uint32_t loadOffset; /* The offset of the LOAD section within its parent */
|
||||
|
||||
struct UnionStackEntry {
|
||||
uint32_t start;
|
||||
uint32_t size;
|
||||
struct UnionStackEntry *next;
|
||||
} *unionStack = NULL;
|
||||
|
||||
/*
|
||||
* A quick check to see if we have an initialized section
|
||||
@@ -33,7 +40,7 @@ uint32_t loadOffset = 0; /* The offset of the LOAD section within its parent */
|
||||
static inline void checksection(void)
|
||||
{
|
||||
if (pCurrentSection == NULL)
|
||||
fatalerror("Code generation before SECTION directive");
|
||||
fatalerror("Code generation before SECTION directive\n");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -44,41 +51,45 @@ static inline void checkcodesection(void)
|
||||
{
|
||||
checksection();
|
||||
|
||||
if (!sect_HasData(pCurrentSection->nType))
|
||||
fatalerror("Section '%s' cannot contain code or data (not ROM0 or ROMX)",
|
||||
pCurrentSection->pzName);
|
||||
else if (nUnionDepth > 0)
|
||||
fatalerror("UNIONs cannot contain code or data");
|
||||
if (!sect_HasData(pCurrentSection->type))
|
||||
fatalerror("Section '%s' cannot contain code or data (not ROM0 or ROMX)\n",
|
||||
pCurrentSection->name);
|
||||
}
|
||||
|
||||
static inline void checkSectionSize(struct Section const *sect, uint32_t size)
|
||||
{
|
||||
uint32_t maxSize = maxsize[sect->type];
|
||||
|
||||
if (size > maxSize)
|
||||
fatalerror("Section '%s' grew too big (max size = 0x%" PRIX32
|
||||
" bytes, reached 0x%" PRIX32 ").\n", sect->name, maxSize, size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the section has grown too much.
|
||||
*/
|
||||
static void reserveSpace(uint32_t delta_size)
|
||||
static inline void reserveSpace(uint32_t delta_size)
|
||||
{
|
||||
uint32_t maxSize = maxsize[pCurrentSection->nType];
|
||||
uint32_t newSize = curOffset + delta_size;
|
||||
|
||||
/*
|
||||
* This check is here to trap broken code that generates sections that
|
||||
* are too big and to prevent the assembler from generating huge object
|
||||
* files or trying to allocate too much memory.
|
||||
* A check at the linking stage is still necessary.
|
||||
*/
|
||||
if (newSize > maxSize)
|
||||
fatalerror("Section '%s' is too big (max size = 0x%" PRIX32 " bytes, reached 0x%" PRIX32 ").",
|
||||
pCurrentSection->pzName, maxSize, newSize);
|
||||
checkSectionSize(pCurrentSection, curOffset + loadOffset + delta_size);
|
||||
if (currentLoadSection)
|
||||
checkSectionSize(currentLoadSection, curOffset + delta_size);
|
||||
}
|
||||
|
||||
struct Section *out_FindSectionByName(const char *pzName)
|
||||
struct Section *out_FindSectionByName(const char *name)
|
||||
{
|
||||
struct Section *pSect = pSectionList;
|
||||
struct Section *sect = pSectionList;
|
||||
|
||||
while (pSect) {
|
||||
if (strcmp(pzName, pSect->pzName) == 0)
|
||||
return pSect;
|
||||
while (sect) {
|
||||
if (strcmp(name, sect->name) == 0)
|
||||
return sect;
|
||||
|
||||
pSect = pSect->pNext;
|
||||
sect = sect->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -87,7 +98,7 @@ struct Section *out_FindSectionByName(const char *pzName)
|
||||
/*
|
||||
* Find a section by name and type. If it doesn't exist, create it
|
||||
*/
|
||||
static struct Section *getSection(char const *pzName, enum SectionType type,
|
||||
static struct Section *getSection(char const *name, enum SectionType type,
|
||||
uint32_t org, struct SectionSpec const *attrs,
|
||||
enum SectionModifier mod)
|
||||
{
|
||||
@@ -99,16 +110,16 @@ static struct Section *getSection(char const *pzName, enum SectionType type,
|
||||
if (bank != -1) {
|
||||
if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM
|
||||
&& type != SECTTYPE_SRAM && type != SECTTYPE_WRAMX)
|
||||
yyerror("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections");
|
||||
error("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections\n");
|
||||
else if (bank < bankranges[type][0]
|
||||
|| bank > bankranges[type][1])
|
||||
yyerror("%s bank value $%" PRIx32 " out of range ($%" PRIx32 " to $%" PRIx32 ")",
|
||||
typeNames[type], bank,
|
||||
error("%s bank value $%" PRIx32 " out of range ($%" PRIx32 " to $%"
|
||||
PRIx32 ")\n", typeNames[type], bank,
|
||||
bankranges[type][0], bankranges[type][1]);
|
||||
}
|
||||
|
||||
if (alignOffset >= 1 << alignment) {
|
||||
yyerror("Alignment offset must not be greater than alignment (%" PRIu16 " < %u)",
|
||||
error("Alignment offset must not be greater than alignment (%" PRIu16 " < %u)\n",
|
||||
alignOffset, 1U << alignment);
|
||||
alignOffset = 0;
|
||||
}
|
||||
@@ -119,41 +130,42 @@ static struct Section *getSection(char const *pzName, enum SectionType type,
|
||||
|
||||
if (org != -1) {
|
||||
if ((org - alignOffset) & mask)
|
||||
yyerror("Section \"%s\"'s fixed address doesn't match its alignment",
|
||||
pzName);
|
||||
error("Section \"%s\"'s fixed address doesn't match its alignment\n",
|
||||
name);
|
||||
alignment = 0; /* Ignore it if it's satisfied */
|
||||
} else if (startaddr[type] & mask) {
|
||||
yyerror("Section \"%s\"'s alignment cannot be attained in %s",
|
||||
pzName, typeNames[type]);
|
||||
error("Section \"%s\"'s alignment cannot be attained in %s\n",
|
||||
name, typeNames[type]);
|
||||
}
|
||||
}
|
||||
|
||||
if (org != -1) {
|
||||
if (org < startaddr[type] || org > endaddr(type))
|
||||
yyerror("Section \"%s\"'s fixed address %#" PRIx32 " is outside of range [%#" PRIx16 "; %#" PRIx16 "]",
|
||||
pzName, org, startaddr[type], endaddr(type));
|
||||
error("Section \"%s\"'s fixed address %#" PRIx32
|
||||
" is outside of range [%#" PRIx16 "; %#" PRIx16 "]\n",
|
||||
name, org, startaddr[type], endaddr(type));
|
||||
}
|
||||
|
||||
if (nbbanks(type) == 1)
|
||||
bank = bankranges[type][0];
|
||||
|
||||
struct Section *pSect = out_FindSectionByName(pzName);
|
||||
struct Section *sect = out_FindSectionByName(name);
|
||||
|
||||
if (pSect) {
|
||||
if (sect) {
|
||||
unsigned int nbSectErrors = 0;
|
||||
#define fail(...) \
|
||||
do { \
|
||||
yyerror(__VA_ARGS__); \
|
||||
error(__VA_ARGS__); \
|
||||
nbSectErrors++; \
|
||||
} while (0)
|
||||
|
||||
if (type != pSect->nType)
|
||||
fail("Section \"%s\" already exists but with type %s",
|
||||
pSect->pzName, typeNames[pSect->nType]);
|
||||
if (type != sect->type)
|
||||
fail("Section \"%s\" already exists but with type %s\n",
|
||||
sect->name, typeNames[sect->type]);
|
||||
|
||||
if (pSect->modifier != mod)
|
||||
fail("Section \"%s\" already declared as %s section",
|
||||
pSect->pzName, sectionModNames[pSect->modifier]);
|
||||
if (sect->modifier != mod)
|
||||
fail("Section \"%s\" already declared as %s section\n",
|
||||
sect->name, sectionModNames[sect->modifier]);
|
||||
/*
|
||||
* Normal sections need to have exactly identical constraints;
|
||||
* but unionized sections only need "compatible" constraints,
|
||||
@@ -165,128 +177,127 @@ static struct Section *getSection(char const *pzName, enum SectionType type,
|
||||
* `EndLoadSection` if modifying the following check!
|
||||
*/
|
||||
if (sect_HasData(type))
|
||||
fail("Cannot declare ROM sections as UNION");
|
||||
fail("Cannot declare ROM sections as UNION\n");
|
||||
if (org != -1) {
|
||||
/* If both are fixed, they must be the same */
|
||||
if (pSect->nOrg != -1 && pSect->nOrg != org)
|
||||
fail("Section \"%s\" already declared as fixed at different address $%" PRIx32,
|
||||
pSect->pzName, pSect->nOrg);
|
||||
else if (pSect->nAlign != 0
|
||||
&& (mask(pSect->nAlign)
|
||||
& (org - pSect->alignOfs)))
|
||||
fail("Section \"%s\" already declared as aligned to %u bytes (offset %" PRIu16 ")",
|
||||
pSect->pzName, 1U << pSect->nAlign,
|
||||
pSect->alignOfs);
|
||||
if (sect->org != -1 && sect->org != org)
|
||||
fail("Section \"%s\" already declared as fixed at different address $%"
|
||||
PRIx32 "\n",
|
||||
sect->name, sect->org);
|
||||
else if (sect->align != 0
|
||||
&& (mask(sect->align)
|
||||
& (org - sect->alignOfs)))
|
||||
fail("Section \"%s\" already declared as aligned to %u bytes (offset %"
|
||||
PRIu16 ")\n", sect->name, 1U << sect->align, sect->alignOfs);
|
||||
else
|
||||
/* Otherwise, just override */
|
||||
pSect->nOrg = org;
|
||||
sect->org = org;
|
||||
} else if (alignment != 0) {
|
||||
/* Make sure any fixed address is compatible */
|
||||
if (pSect->nOrg != -1) {
|
||||
if ((pSect->nOrg - alignOffset)
|
||||
if (sect->org != -1) {
|
||||
if ((sect->org - alignOffset)
|
||||
& mask(alignment))
|
||||
fail("Section \"%s\" already declared as fixed at incompatible address $%" PRIx32,
|
||||
pSect->pzName,
|
||||
pSect->nOrg);
|
||||
fail("Section \"%s\" already declared as fixed at incompatible address $%"
|
||||
PRIx32 "\n", sect->name, sect->org);
|
||||
/* Check if alignment offsets are compatible */
|
||||
} else if ((alignOffset & mask(pSect->nAlign))
|
||||
!= (pSect->alignOfs
|
||||
} else if ((alignOffset & mask(sect->align))
|
||||
!= (sect->alignOfs
|
||||
& mask(alignment))) {
|
||||
fail("Section \"%s\" already declared with incompatible %" PRIu8 "-byte alignment (offset %" PRIu16 ")",
|
||||
pSect->pzName, pSect->nAlign,
|
||||
pSect->alignOfs);
|
||||
} else if (alignment > pSect->nAlign) {
|
||||
fail("Section \"%s\" already declared with incompatible %"
|
||||
PRIu8 "-byte alignment (offset %" PRIu16 ")\n",
|
||||
sect->name, sect->align, sect->alignOfs);
|
||||
} else if (alignment > sect->align) {
|
||||
/*
|
||||
* If the section is not fixed,
|
||||
* its alignment is the largest of both
|
||||
*/
|
||||
pSect->nAlign = alignment;
|
||||
pSect->alignOfs = alignOffset;
|
||||
sect->align = alignment;
|
||||
sect->alignOfs = alignOffset;
|
||||
}
|
||||
}
|
||||
/* If the section's bank is unspecified, override it */
|
||||
if (pSect->nBank == -1)
|
||||
pSect->nBank = bank;
|
||||
if (sect->bank == -1)
|
||||
sect->bank = bank;
|
||||
/* If both specify a bank, it must be the same one */
|
||||
else if (bank != -1 && pSect->nBank != bank)
|
||||
fail("Section \"%s\" already declared with different bank %" PRIu32,
|
||||
pSect->pzName, pSect->nBank);
|
||||
else if (bank != -1 && sect->bank != bank)
|
||||
fail("Section \"%s\" already declared with different bank %"
|
||||
PRIu32 "\n", sect->name, sect->bank);
|
||||
} else { /* Section fragments are handled identically in RGBASM */
|
||||
/* However, concaternating non-fragments will be made an error */
|
||||
if (pSect->modifier != SECTION_FRAGMENT || mod != SECTION_FRAGMENT)
|
||||
warning(WARNING_OBSOLETE, "Concatenation of non-fragment sections is deprecated");
|
||||
if (sect->modifier != SECTION_FRAGMENT || mod != SECTION_FRAGMENT)
|
||||
warning(WARNING_OBSOLETE,
|
||||
"Concatenation of non-fragment sections is deprecated\n");
|
||||
|
||||
if (org != pSect->nOrg) {
|
||||
if (pSect->nOrg == -1)
|
||||
fail("Section \"%s\" already declared as floating",
|
||||
pSect->pzName);
|
||||
if (org != sect->org) {
|
||||
if (sect->org == -1)
|
||||
fail("Section \"%s\" already declared as floating\n",
|
||||
sect->name);
|
||||
else
|
||||
fail("Section \"%s\" already declared as fixed at $%" PRIx32,
|
||||
pSect->pzName, pSect->nOrg);
|
||||
fail("Section \"%s\" already declared as fixed at $%"
|
||||
PRIx32 "\n", sect->name, sect->org);
|
||||
}
|
||||
if (bank != pSect->nBank) {
|
||||
if (pSect->nBank == -1)
|
||||
fail("Section \"%s\" already declared as floating bank",
|
||||
pSect->pzName);
|
||||
if (bank != sect->bank) {
|
||||
if (sect->bank == -1)
|
||||
fail("Section \"%s\" already declared as floating bank\n",
|
||||
sect->name);
|
||||
else
|
||||
fail("Section \"%s\" already declared as fixed at bank %" PRIu32,
|
||||
pSect->pzName, pSect->nBank);
|
||||
fail("Section \"%s\" already declared as fixed at bank %"
|
||||
PRIu32 "\n", sect->name, sect->bank);
|
||||
}
|
||||
if (alignment != pSect->nAlign) {
|
||||
if (pSect->nAlign == 0)
|
||||
fail("Section \"%s\" already declared as unaligned",
|
||||
pSect->pzName);
|
||||
if (alignment != sect->align) {
|
||||
if (sect->align == 0)
|
||||
fail("Section \"%s\" already declared as unaligned\n",
|
||||
sect->name);
|
||||
else
|
||||
fail("Section \"%s\" already declared as aligned to %u bytes",
|
||||
pSect->pzName,
|
||||
1U << pSect->nAlign);
|
||||
fail("Section \"%s\" already declared as aligned to %u bytes\n",
|
||||
sect->name, 1U << sect->align);
|
||||
}
|
||||
}
|
||||
|
||||
if (nbSectErrors)
|
||||
fatalerror("Cannot create section \"%s\" (%u errors)",
|
||||
pSect->pzName, nbSectErrors);
|
||||
fatalerror("Cannot create section \"%s\" (%u errors)\n",
|
||||
sect->name, nbSectErrors);
|
||||
#undef fail
|
||||
return pSect;
|
||||
return sect;
|
||||
}
|
||||
|
||||
pSect = malloc(sizeof(*pSect));
|
||||
if (pSect == NULL)
|
||||
fatalerror("Not enough memory for section");
|
||||
sect = malloc(sizeof(*sect));
|
||||
if (sect == NULL)
|
||||
fatalerror("Not enough memory for section: %s\n", strerror(errno));
|
||||
|
||||
pSect->pzName = strdup(pzName);
|
||||
if (pSect->pzName == NULL)
|
||||
fatalerror("Not enough memory for sectionname");
|
||||
sect->name = strdup(name);
|
||||
if (sect->name == NULL)
|
||||
fatalerror("Not enough memory for section name: %s\n", strerror(errno));
|
||||
|
||||
pSect->nType = type;
|
||||
pSect->modifier = mod;
|
||||
pSect->size = 0;
|
||||
pSect->nOrg = org;
|
||||
pSect->nBank = bank;
|
||||
pSect->nAlign = alignment;
|
||||
pSect->alignOfs = alignOffset;
|
||||
pSect->pNext = pSectionList;
|
||||
pSect->pPatches = NULL;
|
||||
sect->type = type;
|
||||
sect->modifier = mod;
|
||||
sect->size = 0;
|
||||
sect->org = org;
|
||||
sect->bank = bank;
|
||||
sect->align = alignment;
|
||||
sect->alignOfs = alignOffset;
|
||||
sect->next = pSectionList;
|
||||
sect->patches = NULL;
|
||||
|
||||
/* It is only needed to allocate memory for ROM sections. */
|
||||
if (sect_HasData(type)) {
|
||||
uint32_t sectsize;
|
||||
|
||||
sectsize = maxsize[type];
|
||||
pSect->tData = malloc(sectsize);
|
||||
if (pSect->tData == NULL)
|
||||
fatalerror("Not enough memory for section");
|
||||
sect->data = malloc(sectsize);
|
||||
if (sect->data == NULL)
|
||||
fatalerror("Not enough memory for section: %s\n", strerror(errno));
|
||||
} else {
|
||||
pSect->tData = NULL;
|
||||
sect->data = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the new section to the list
|
||||
* at the beginning because order doesn't matter
|
||||
*/
|
||||
pSectionList = pSect;
|
||||
pSectionList = sect;
|
||||
|
||||
return pSect;
|
||||
return sect;
|
||||
#undef mask
|
||||
}
|
||||
|
||||
@@ -295,8 +306,8 @@ static struct Section *getSection(char const *pzName, enum SectionType type,
|
||||
*/
|
||||
static void changeSection(void)
|
||||
{
|
||||
if (nUnionDepth > 0)
|
||||
fatalerror("Cannot change the section within a UNION");
|
||||
if (unionStack)
|
||||
fatalerror("Cannot change the section within a UNION\n");
|
||||
|
||||
sym_SetCurrentSymbolScope(NULL);
|
||||
}
|
||||
@@ -304,17 +315,17 @@ static void changeSection(void)
|
||||
/*
|
||||
* Set the current section by name and type
|
||||
*/
|
||||
void out_NewSection(char const *pzName, uint32_t type, uint32_t org,
|
||||
void out_NewSection(char const *name, uint32_t type, uint32_t org,
|
||||
struct SectionSpec const *attribs, enum SectionModifier mod)
|
||||
{
|
||||
if (currentLoadSection)
|
||||
fatalerror("Cannot change the section within a `LOAD` block");
|
||||
fatalerror("Cannot change the section within a `LOAD` block\n");
|
||||
|
||||
struct Section *pSect = getSection(pzName, type, org, attribs, mod);
|
||||
struct Section *sect = getSection(name, type, org, attribs, mod);
|
||||
|
||||
changeSection();
|
||||
curOffset = mod == SECTION_UNION ? 0 : pSect->size;
|
||||
pCurrentSection = pSect;
|
||||
curOffset = mod == SECTION_UNION ? 0 : sect->size;
|
||||
pCurrentSection = sect;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -326,20 +337,20 @@ void out_SetLoadSection(char const *name, uint32_t type, uint32_t org,
|
||||
checkcodesection();
|
||||
|
||||
if (currentLoadSection)
|
||||
fatalerror("`LOAD` blocks cannot be nested");
|
||||
fatalerror("`LOAD` blocks cannot be nested\n");
|
||||
|
||||
struct Section *pSect = getSection(name, type, org, attribs, false);
|
||||
struct Section *sect = getSection(name, type, org, attribs, false);
|
||||
|
||||
loadOffset = curOffset;
|
||||
curOffset = 0; /* curOffset -= loadOffset; */
|
||||
changeSection();
|
||||
currentLoadSection = pSect;
|
||||
currentLoadSection = sect;
|
||||
}
|
||||
|
||||
void out_EndLoadSection(void)
|
||||
{
|
||||
if (!currentLoadSection)
|
||||
yyerror("Found `ENDL` outside of a `LOAD` block");
|
||||
error("Found `ENDL` outside of a `LOAD` block\n");
|
||||
currentLoadSection = NULL;
|
||||
|
||||
changeSection();
|
||||
@@ -352,6 +363,14 @@ struct Section *sect_GetSymbolSection(void)
|
||||
return currentLoadSection ? currentLoadSection : pCurrentSection;
|
||||
}
|
||||
|
||||
/*
|
||||
* The offset into the section above
|
||||
*/
|
||||
uint32_t sect_GetSymbolOffset(void)
|
||||
{
|
||||
return curOffset;
|
||||
}
|
||||
|
||||
uint32_t sect_GetOutputOffset(void)
|
||||
{
|
||||
return curOffset + loadOffset;
|
||||
@@ -359,24 +378,25 @@ uint32_t sect_GetOutputOffset(void)
|
||||
|
||||
void sect_AlignPC(uint8_t alignment, uint16_t offset)
|
||||
{
|
||||
checksection();
|
||||
struct Section *sect = sect_GetSymbolSection();
|
||||
|
||||
if (sect->nOrg != -1) {
|
||||
if (sect->org != -1) {
|
||||
if ((sym_GetPCValue() - offset) % (1 << alignment))
|
||||
yyerror("Section's fixed address fails required alignment (PC = $%04" PRIx32 ")",
|
||||
sym_GetPCValue());
|
||||
} else if (sect->nAlign != 0) {
|
||||
if ((((sect->alignOfs + curOffset) % (1 << sect->nAlign))
|
||||
error("Section's fixed address fails required alignment (PC = $%04"
|
||||
PRIx32 ")\n", sym_GetPCValue());
|
||||
} else if (sect->align != 0) {
|
||||
if ((((sect->alignOfs + curOffset) % (1 << sect->align))
|
||||
- offset) % (1 << alignment)) {
|
||||
yyerror("Section's alignment fails required alignment (offset from section start = $%04" PRIx32 ")",
|
||||
curOffset);
|
||||
} else if (alignment > sect->nAlign) {
|
||||
sect->nAlign = alignment;
|
||||
error("Section's alignment fails required alignment (offset from section start = $%04"
|
||||
PRIx32 ")\n", curOffset);
|
||||
} else if (alignment > sect->align) {
|
||||
sect->align = alignment;
|
||||
sect->alignOfs =
|
||||
(offset - curOffset) % (1 << alignment);
|
||||
}
|
||||
} else {
|
||||
sect->nAlign = alignment;
|
||||
sect->align = alignment;
|
||||
sect->alignOfs = offset;
|
||||
}
|
||||
}
|
||||
@@ -384,15 +404,15 @@ void sect_AlignPC(uint8_t alignment, uint16_t offset)
|
||||
static inline void growSection(uint32_t growth)
|
||||
{
|
||||
curOffset += growth;
|
||||
if (curOffset > pCurrentSection->size)
|
||||
pCurrentSection->size = curOffset;
|
||||
if (curOffset + loadOffset > pCurrentSection->size)
|
||||
pCurrentSection->size = curOffset + loadOffset;
|
||||
if (currentLoadSection && curOffset > currentLoadSection->size)
|
||||
currentLoadSection->size = curOffset;
|
||||
}
|
||||
|
||||
static inline void writebyte(uint8_t byte)
|
||||
{
|
||||
pCurrentSection->tData[sect_GetOutputOffset()] = byte;
|
||||
pCurrentSection->data[sect_GetOutputOffset()] = byte;
|
||||
growSection(1);
|
||||
}
|
||||
|
||||
@@ -416,6 +436,56 @@ static inline void createPatch(enum PatchType type,
|
||||
out_CreatePatch(type, expr, sect_GetOutputOffset());
|
||||
}
|
||||
|
||||
void sect_StartUnion(void)
|
||||
{
|
||||
if (!pCurrentSection)
|
||||
fatalerror("UNIONs must be inside a SECTION\n");
|
||||
if (sect_HasData(pCurrentSection->type))
|
||||
fatalerror("Cannot use UNION inside of ROM0 or ROMX sections\n");
|
||||
struct UnionStackEntry *entry = malloc(sizeof(*entry));
|
||||
|
||||
if (!entry)
|
||||
fatalerror("Failed to allocate new union stack entry: %s\n", strerror(errno));
|
||||
entry->start = curOffset;
|
||||
entry->size = 0;
|
||||
entry->next = unionStack;
|
||||
unionStack = entry;
|
||||
}
|
||||
|
||||
static void endUnionMember(void)
|
||||
{
|
||||
uint32_t memberSize = curOffset - unionStack->start;
|
||||
|
||||
if (memberSize > unionStack->size)
|
||||
unionStack->size = memberSize;
|
||||
curOffset = unionStack->start;
|
||||
}
|
||||
|
||||
void sect_NextUnionMember(void)
|
||||
{
|
||||
if (!unionStack)
|
||||
fatalerror("Found NEXTU outside of a UNION construct\n");
|
||||
endUnionMember();
|
||||
}
|
||||
|
||||
void sect_EndUnion(void)
|
||||
{
|
||||
if (!unionStack)
|
||||
fatalerror("Found ENDU outside of a UNION construct\n");
|
||||
endUnionMember();
|
||||
curOffset += unionStack->size;
|
||||
struct UnionStackEntry *next = unionStack->next;
|
||||
|
||||
free(unionStack);
|
||||
unionStack = next;
|
||||
}
|
||||
|
||||
void sect_CheckUnionClosed(void)
|
||||
{
|
||||
if (unionStack)
|
||||
fatalerror("Unterminated UNION construct!\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Output an absolute byte
|
||||
*/
|
||||
@@ -444,14 +514,11 @@ void out_Skip(int32_t skip, bool ds)
|
||||
checksection();
|
||||
reserveSpace(skip);
|
||||
|
||||
if (!ds && sect_HasData(pCurrentSection->nType))
|
||||
warning(WARNING_EMPTY_DATA_DIRECTIVE, "db/dw/dl directive without data in ROM");
|
||||
if (!ds && sect_HasData(pCurrentSection->type))
|
||||
warning(WARNING_EMPTY_DATA_DIRECTIVE, "db/dw/dl directive without data in ROM\n");
|
||||
|
||||
if (!sect_HasData(pCurrentSection->nType)) {
|
||||
if (!sect_HasData(pCurrentSection->type)) {
|
||||
growSection(skip);
|
||||
} else if (nUnionDepth > 0) {
|
||||
while (skip--)
|
||||
writebyte(CurrentOptions.fillchar);
|
||||
} else {
|
||||
checkcodesection();
|
||||
while (skip--)
|
||||
@@ -553,18 +620,24 @@ void out_PCRelByte(struct Expression *expr)
|
||||
{
|
||||
checkcodesection();
|
||||
reserveSpace(1);
|
||||
struct Symbol const *pc = sym_GetPC();
|
||||
|
||||
if (!rpn_isKnown(expr) || pCurrentSection->nOrg == -1) {
|
||||
if (!rpn_IsDiffConstant(expr, pc)) {
|
||||
createPatch(PATCHTYPE_JR, expr);
|
||||
writebyte(0);
|
||||
} else {
|
||||
/* Target is relative to the byte *after* the operand */
|
||||
uint16_t address = sym_GetPCValue() + 1;
|
||||
/* The offset wraps (jump from ROM to HRAM, for loopexample) */
|
||||
int16_t offset = expr->nVal - address;
|
||||
struct Symbol const *sym = rpn_SymbolOf(expr);
|
||||
/* The offset wraps (jump from ROM to HRAM, for example) */
|
||||
int16_t offset;
|
||||
|
||||
/* Offset is relative to the byte *after* the operand */
|
||||
if (sym == pc)
|
||||
offset = -2; /* PC as operand to `jr` is lower than reference PC by 2 */
|
||||
else
|
||||
offset = sym_GetValue(sym) - (sym_GetValue(pc) + 1);
|
||||
|
||||
if (offset < -128 || offset > 127) {
|
||||
yyerror("jr target out of reach (expected -129 < %" PRId16 " < 128)",
|
||||
error("jr target out of reach (expected -129 < %" PRId16 " < 128)\n",
|
||||
offset);
|
||||
writebyte(0);
|
||||
} else {
|
||||
@@ -577,17 +650,27 @@ void out_PCRelByte(struct Expression *expr)
|
||||
/*
|
||||
* Output a binary file
|
||||
*/
|
||||
void out_BinaryFile(char const *s)
|
||||
void out_BinaryFile(char const *s, int32_t startPos)
|
||||
{
|
||||
FILE *f = fstk_FindFile(s, NULL);
|
||||
if (startPos < 0) {
|
||||
error("Start position cannot be negative (%" PRId32 ")\n", startPos);
|
||||
startPos = 0;
|
||||
}
|
||||
|
||||
char *fullPath = NULL;
|
||||
size_t size = 0;
|
||||
FILE *f = NULL;
|
||||
|
||||
if (fstk_FindFile(s, &fullPath, &size))
|
||||
f = fopen(fullPath, "rb");
|
||||
free(fullPath);
|
||||
|
||||
if (!f) {
|
||||
if (oGeneratedMissingIncludes) {
|
||||
oFailedOnMissingInclude = true;
|
||||
return;
|
||||
}
|
||||
fatalerror("Error opening INCBIN file '%s': %s", s,
|
||||
strerror(errno));
|
||||
fatalerror("Error opening INCBIN file '%s': %s\n", s, strerror(errno));
|
||||
}
|
||||
|
||||
int32_t fsize = -1;
|
||||
@@ -596,12 +679,22 @@ void out_BinaryFile(char const *s)
|
||||
checkcodesection();
|
||||
if (fseek(f, 0, SEEK_END) != -1) {
|
||||
fsize = ftell(f);
|
||||
rewind(f);
|
||||
|
||||
reserveSpace(fsize);
|
||||
} else if (errno != ESPIPE) {
|
||||
yyerror("Error determining size of INCBIN file '%s': %s", s,
|
||||
strerror(errno));
|
||||
if (startPos >= fsize) {
|
||||
error("Specified start position is greater than length of file\n");
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
fseek(f, startPos, SEEK_SET);
|
||||
reserveSpace(fsize - startPos);
|
||||
} else {
|
||||
if (errno != ESPIPE)
|
||||
error("Error determining size of INCBIN file '%s': %s\n",
|
||||
s, strerror(errno));
|
||||
/* The file isn't seekable, so we'll just skip bytes */
|
||||
while (startPos--)
|
||||
(void)fgetc(f);
|
||||
}
|
||||
|
||||
while ((byte = fgetc(f)) != EOF) {
|
||||
@@ -611,8 +704,7 @@ void out_BinaryFile(char const *s)
|
||||
}
|
||||
|
||||
if (ferror(f))
|
||||
yyerror("Error reading INCBIN file '%s': %s", s,
|
||||
strerror(errno));
|
||||
error("Error reading INCBIN file '%s': %s\n", s, strerror(errno));
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
@@ -620,28 +712,31 @@ void out_BinaryFile(char const *s)
|
||||
void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
|
||||
{
|
||||
if (start_pos < 0) {
|
||||
yyerror("Start position cannot be negative (%" PRId32 ")",
|
||||
start_pos);
|
||||
error("Start position cannot be negative (%" PRId32 ")\n", start_pos);
|
||||
start_pos = 0;
|
||||
}
|
||||
|
||||
if (length < 0) {
|
||||
yyerror("Number of bytes to read cannot be negative (%" PRId32 ")",
|
||||
length);
|
||||
error("Number of bytes to read cannot be negative (%" PRId32 ")\n", length);
|
||||
length = 0;
|
||||
}
|
||||
if (length == 0) /* Don't even bother with 0-byte slices */
|
||||
return;
|
||||
|
||||
FILE *f = fstk_FindFile(s, NULL);
|
||||
char *fullPath = NULL;
|
||||
size_t size = 0;
|
||||
FILE *f = NULL;
|
||||
|
||||
if (fstk_FindFile(s, &fullPath, &size))
|
||||
f = fopen(fullPath, "rb");
|
||||
|
||||
if (!f) {
|
||||
free(fullPath);
|
||||
if (oGeneratedMissingIncludes) {
|
||||
oFailedOnMissingInclude = true;
|
||||
return;
|
||||
}
|
||||
fatalerror("Error opening INCBIN file '%s': %s", s,
|
||||
strerror(errno));
|
||||
fatalerror("Error opening INCBIN file '%s': %s\n", s, strerror(errno));
|
||||
}
|
||||
|
||||
checkcodesection();
|
||||
@@ -653,17 +748,17 @@ void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
|
||||
fsize = ftell(f);
|
||||
|
||||
if (start_pos >= fsize) {
|
||||
yyerror("Specified start position is greater than length of file");
|
||||
error("Specified start position is greater than length of file\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((start_pos + length) > fsize)
|
||||
fatalerror("Specified range in INCBIN is out of bounds");
|
||||
fatalerror("Specified range in INCBIN is out of bounds\n");
|
||||
|
||||
fseek(f, start_pos, SEEK_SET);
|
||||
} else {
|
||||
if (errno != ESPIPE)
|
||||
yyerror("Error determining size of INCBIN file '%s': %s",
|
||||
error("Error determining size of INCBIN file '%s': %s\n",
|
||||
s, strerror(errno));
|
||||
/* The file isn't seekable, so we'll just skip bytes */
|
||||
while (start_pos--)
|
||||
@@ -678,15 +773,15 @@ void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
|
||||
if (byte != EOF) {
|
||||
writebyte(byte);
|
||||
} else if (ferror(f)) {
|
||||
yyerror("Error reading INCBIN file '%s': %s", s,
|
||||
strerror(errno));
|
||||
error("Error reading INCBIN file '%s': %s\n", s, strerror(errno));
|
||||
} else {
|
||||
yyerror("Premature end of file (%" PRId32 " bytes left to read)",
|
||||
error("Premature end of file (%" PRId32 " bytes left to read)\n",
|
||||
todo + 1);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
free(fullPath);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -694,36 +789,34 @@ void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
|
||||
*/
|
||||
void out_PushSection(void)
|
||||
{
|
||||
struct SectionStackEntry *pSect;
|
||||
struct SectionStackEntry *sect = malloc(sizeof(*sect));
|
||||
|
||||
pSect = malloc(sizeof(struct SectionStackEntry));
|
||||
if (pSect == NULL)
|
||||
fatalerror("No memory for section stack");
|
||||
|
||||
pSect->pSection = pCurrentSection;
|
||||
pSect->pScope = sym_GetCurrentSymbolScope();
|
||||
pSect->offset = curOffset;
|
||||
pSect->pNext = pSectionStack;
|
||||
pSectionStack = pSect;
|
||||
if (sect == NULL)
|
||||
fatalerror("No memory for section stack: %s\n", strerror(errno));
|
||||
sect->section = pCurrentSection;
|
||||
sect->scope = sym_GetCurrentSymbolScope();
|
||||
sect->offset = curOffset;
|
||||
sect->next = sectionStack;
|
||||
sectionStack = sect;
|
||||
/* TODO: maybe set current section to NULL? */
|
||||
}
|
||||
|
||||
void out_PopSection(void)
|
||||
{
|
||||
if (pSectionStack == NULL)
|
||||
fatalerror("No entries in the section stack");
|
||||
if (!sectionStack)
|
||||
fatalerror("No entries in the section stack\n");
|
||||
|
||||
if (currentLoadSection)
|
||||
fatalerror("Cannot change the section within a `LOAD` block!");
|
||||
fatalerror("Cannot change the section within a `LOAD` block!\n");
|
||||
|
||||
struct SectionStackEntry *pSect;
|
||||
struct SectionStackEntry *sect;
|
||||
|
||||
pSect = pSectionStack;
|
||||
sect = sectionStack;
|
||||
changeSection();
|
||||
pCurrentSection = pSect->pSection;
|
||||
sym_SetCurrentSymbolScope(pSect->pScope);
|
||||
curOffset = pSect->offset;
|
||||
pCurrentSection = sect->section;
|
||||
sym_SetCurrentSymbolScope(sect->scope);
|
||||
curOffset = sect->offset;
|
||||
|
||||
pSectionStack = pSect->pNext;
|
||||
free(pSect);
|
||||
sectionStack = sect->next;
|
||||
free(sect);
|
||||
}
|
||||
|
||||
428
src/asm/symbol.c
428
src/asm/symbol.c
@@ -23,6 +23,7 @@
|
||||
#include "asm/macro.h"
|
||||
#include "asm/main.h"
|
||||
#include "asm/mymath.h"
|
||||
#include "asm/output.h"
|
||||
#include "asm/section.h"
|
||||
#include "asm/symbol.h"
|
||||
#include "asm/util.h"
|
||||
@@ -36,7 +37,7 @@
|
||||
|
||||
HashMap symbols;
|
||||
|
||||
static struct Symbol *symbolScope; /* Current section symbol scope */
|
||||
static char const *labelScope; /* Current section's label scope */
|
||||
static struct Symbol *PCSymbol;
|
||||
static char savedTIME[256];
|
||||
static char savedDATE[256];
|
||||
@@ -77,19 +78,62 @@ void sym_ForEach(void (*func)(struct Symbol *, void *), void *arg)
|
||||
|
||||
static int32_t Callback_NARG(void)
|
||||
{
|
||||
if (!macro_GetCurrentArgs()) {
|
||||
error("_NARG does not make sense outside of a macro\n");
|
||||
return 0;
|
||||
}
|
||||
return macro_NbArgs();
|
||||
}
|
||||
|
||||
static int32_t Callback__LINE__(void)
|
||||
{
|
||||
return nLineNo;
|
||||
return lexer_GetLineNo();
|
||||
}
|
||||
|
||||
static char const *Callback__FILE__(void)
|
||||
{
|
||||
/*
|
||||
* FIXME: this is dangerous, and here's why this is CURRENTLY okay. It's still bad, fix it.
|
||||
* There are only two call sites for this; one copies the contents directly, the other is
|
||||
* EQUS expansions, which cannot straddle file boundaries. So this should be fine.
|
||||
*/
|
||||
static char *buf = NULL;
|
||||
static size_t bufsize = 0;
|
||||
char const *fileName = fstk_GetFileName();
|
||||
size_t j = 1;
|
||||
|
||||
/* TODO: is there a way for a file name to be empty? */
|
||||
assert(fileName[0]);
|
||||
/* The assertion above ensures the loop runs at least once */
|
||||
for (size_t i = 0; fileName[i]; i++, j++) {
|
||||
/* Account for the extra backslash inserted below */
|
||||
if (fileName[i] == '"')
|
||||
j++;
|
||||
/* Ensure there will be enough room; DO NOT PRINT ANYTHING ABOVE THIS!! */
|
||||
if (j + 2 >= bufsize) { /* Always keep room for 2 tail chars */
|
||||
bufsize = bufsize ? bufsize * 2 : 64;
|
||||
buf = realloc(buf, bufsize);
|
||||
if (!buf)
|
||||
fatalerror("Failed to grow buffer for file name: %s\n",
|
||||
strerror(errno));
|
||||
}
|
||||
/* Escape quotes, since we're returning a string */
|
||||
if (fileName[i] == '"')
|
||||
buf[j - 1] = '\\';
|
||||
buf[j] = fileName[i];
|
||||
}
|
||||
/* Write everything after the loop, to ensure the buffer has been allocated */
|
||||
buf[0] = '"';
|
||||
buf[j++] = '"';
|
||||
buf[j] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int32_t CallbackPC(void)
|
||||
{
|
||||
struct Section const *section = sect_GetSymbolSection();
|
||||
|
||||
return section ? section->nOrg + curOffset : 0;
|
||||
return section ? section->org + sect_GetSymbolOffset() : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -97,26 +141,45 @@ static int32_t CallbackPC(void)
|
||||
*/
|
||||
int32_t sym_GetValue(struct Symbol const *sym)
|
||||
{
|
||||
if (sym_IsNumeric(sym) && sym->callback)
|
||||
return sym->callback();
|
||||
if (sym_IsNumeric(sym) && sym->hasCallback)
|
||||
return sym->numCallback();
|
||||
|
||||
if (sym->type == SYM_LABEL)
|
||||
/* TODO: do not use section's org directly */
|
||||
return sym->value + sym_GetSection(sym)->nOrg;
|
||||
return sym->value + sym_GetSection(sym)->org;
|
||||
|
||||
return sym->value;
|
||||
}
|
||||
|
||||
static void dumpFilename(struct Symbol const *sym)
|
||||
{
|
||||
if (!sym->src)
|
||||
fputs("<builtin>", stderr);
|
||||
else
|
||||
fstk_Dump(sym->src, sym->fileLine);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a symbol's definition filename and line
|
||||
*/
|
||||
static void setSymbolFilename(struct Symbol *sym)
|
||||
{
|
||||
sym->src = fstk_GetFileStack();
|
||||
sym->fileLine = lexer_GetLineNo();
|
||||
}
|
||||
|
||||
/*
|
||||
* Update a symbol's definition filename and line
|
||||
*/
|
||||
static void updateSymbolFilename(struct Symbol *sym)
|
||||
{
|
||||
if (snprintf(sym->fileName, _MAX_PATH + 1, "%s",
|
||||
tzCurrentFileName) > _MAX_PATH)
|
||||
fatalerror("%s: File name is too long: '%s'", __func__,
|
||||
tzCurrentFileName);
|
||||
sym->fileLine = fstk_GetLine();
|
||||
struct FileStackNode *oldSrc = sym->src;
|
||||
|
||||
setSymbolFilename(sym);
|
||||
/* If the old node was referenced, ensure the new one is */
|
||||
if (oldSrc->referenced && oldSrc->ID != -1)
|
||||
out_RegisterNode(sym->src);
|
||||
/* TODO: unref the old node, and use `out_ReplaceNode` instead if deleting it */
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -127,16 +190,16 @@ static struct Symbol *createsymbol(char const *s)
|
||||
struct Symbol *symbol = malloc(sizeof(*symbol));
|
||||
|
||||
if (!symbol)
|
||||
fatalerror("Failed to create symbol: %s", strerror(errno));
|
||||
fatalerror("Failed to create symbol '%s': %s\n", s, strerror(errno));
|
||||
|
||||
if (snprintf(symbol->name, MAXSYMLEN + 1, "%s", s) > MAXSYMLEN)
|
||||
warning(WARNING_LONG_STR, "Symbol name is too long: '%s'", s);
|
||||
warning(WARNING_LONG_STR, "Symbol name is too long: '%s'\n", s);
|
||||
|
||||
symbol->isExported = false;
|
||||
symbol->isBuiltin = false;
|
||||
symbol->scope = NULL;
|
||||
symbol->hasCallback = false;
|
||||
symbol->section = NULL;
|
||||
updateSymbolFilename(symbol);
|
||||
setSymbolFilename(symbol);
|
||||
symbol->ID = -1;
|
||||
symbol->next = NULL;
|
||||
|
||||
@@ -149,43 +212,52 @@ static struct Symbol *createsymbol(char const *s)
|
||||
* the name with the parent symbol's name.
|
||||
*/
|
||||
static void fullSymbolName(char *output, size_t outputSize,
|
||||
char const *localName, const struct Symbol *scope)
|
||||
char const *localName, char const *scopeName)
|
||||
{
|
||||
const struct Symbol *parent = scope->scope ? scope->scope : scope;
|
||||
int n = snprintf(output, outputSize, "%s%s", parent->name, localName);
|
||||
int ret = snprintf(output, outputSize, "%s%s", scopeName, localName);
|
||||
|
||||
if (n >= (int)outputSize)
|
||||
fatalerror("Symbol name is too long: '%s%s'", parent->name,
|
||||
localName);
|
||||
if (ret < 0)
|
||||
fatalerror("snprintf error when expanding symbol name: %s", strerror(errno));
|
||||
else if ((size_t)ret >= outputSize)
|
||||
fatalerror("Symbol name is too long: '%s%s'\n", scopeName, localName);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a symbol by name and scope
|
||||
*/
|
||||
static struct Symbol *findsymbol(char const *s, struct Symbol const *scope)
|
||||
struct Symbol *sym_FindExactSymbol(char const *name)
|
||||
{
|
||||
char fullname[MAXSYMLEN + 1];
|
||||
return hash_GetElement(symbols, name);
|
||||
}
|
||||
|
||||
if (s[0] == '.' && scope) {
|
||||
fullSymbolName(fullname, sizeof(fullname), s, scope);
|
||||
s = fullname;
|
||||
struct Symbol *sym_FindUnscopedSymbol(char const *name)
|
||||
{
|
||||
if (strchr(name, '.')) {
|
||||
error("Expected non-scoped symbol name, not \"%s\"\n", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char const *separator = strchr(s, '.');
|
||||
|
||||
if (separator && strchr(separator + 1, '.'))
|
||||
fatalerror("'%s' is a nonsensical reference to a nested local symbol",
|
||||
s);
|
||||
|
||||
return hash_GetElement(symbols, s);
|
||||
return sym_FindExactSymbol(name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a symbol by name, with automatically determined scope
|
||||
*/
|
||||
struct Symbol *sym_FindSymbol(char const *symName)
|
||||
struct Symbol *sym_FindScopedSymbol(char const *name)
|
||||
{
|
||||
return findsymbol(symName, symName[0] == '.' ? symbolScope : NULL);
|
||||
char const *dotPtr = strchr(name, '.');
|
||||
|
||||
if (dotPtr) {
|
||||
if (strchr(dotPtr + 1, '.'))
|
||||
fatalerror("'%s' is a nonsensical reference to a nested local symbol\n",
|
||||
name);
|
||||
/* If auto-scoped local label, expand the name */
|
||||
if (dotPtr == name) { /* Meaning, the name begins with the dot */
|
||||
char fullname[MAXSYMLEN + 1];
|
||||
|
||||
fullSymbolName(fullname, sizeof(fullname), name, labelScope);
|
||||
return sym_FindExactSymbol(fullname);
|
||||
}
|
||||
}
|
||||
return sym_FindExactSymbol(name);
|
||||
}
|
||||
|
||||
struct Symbol const *sym_GetPC(void)
|
||||
{
|
||||
return PCSymbol;
|
||||
}
|
||||
|
||||
static inline bool isReferenced(struct Symbol const *sym)
|
||||
@@ -198,20 +270,21 @@ static inline bool isReferenced(struct Symbol const *sym)
|
||||
*/
|
||||
void sym_Purge(char const *symName)
|
||||
{
|
||||
struct Symbol *scope = symName[0] == '.' ? symbolScope : NULL;
|
||||
struct Symbol *symbol = findsymbol(symName, scope);
|
||||
struct Symbol *symbol = sym_FindScopedSymbol(symName);
|
||||
|
||||
if (!symbol) {
|
||||
yyerror("'%s' not defined", symName);
|
||||
error("'%s' not defined\n", symName);
|
||||
} else if (symbol->isBuiltin) {
|
||||
yyerror("Built-in symbol '%s' cannot be purged", symName);
|
||||
error("Built-in symbol '%s' cannot be purged\n", symName);
|
||||
} else if (isReferenced(symbol)) {
|
||||
yyerror("Symbol \"%s\" is referenced and thus cannot be purged",
|
||||
symName);
|
||||
error("Symbol \"%s\" is referenced and thus cannot be purged\n", symName);
|
||||
} else {
|
||||
/* Do not keep a reference to the label's name after purging it */
|
||||
if (symbol->name == labelScope)
|
||||
labelScope = NULL;
|
||||
|
||||
hash_RemoveElement(symbols, symbol->name);
|
||||
if (symbol->type == SYM_MACRO)
|
||||
free(symbol->macro);
|
||||
/* TODO: ideally, also unref the file stack nodes */
|
||||
free(symbol);
|
||||
}
|
||||
}
|
||||
@@ -221,27 +294,23 @@ uint32_t sym_GetPCValue(void)
|
||||
struct Section const *sect = sect_GetSymbolSection();
|
||||
|
||||
if (!sect)
|
||||
yyerror("PC has no value outside a section");
|
||||
else if (sect->nOrg == -1)
|
||||
yyerror("Expected constant PC but section is not fixed");
|
||||
error("PC has no value outside a section\n");
|
||||
else if (sect->org == -1)
|
||||
error("Expected constant PC but section is not fixed\n");
|
||||
else
|
||||
return CallbackPC();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a constant symbols value
|
||||
* Return a constant symbol's value, assuming it's defined
|
||||
*/
|
||||
uint32_t sym_GetConstantValue(char const *s)
|
||||
uint32_t sym_GetConstantSymValue(struct Symbol const *sym)
|
||||
{
|
||||
struct Symbol const *sym = sym_FindSymbol(s);
|
||||
|
||||
if (sym == NULL)
|
||||
yyerror("'%s' not defined", s);
|
||||
else if (sym == PCSymbol)
|
||||
if (sym == PCSymbol)
|
||||
return sym_GetPCValue();
|
||||
else if (!sym_IsConstant(sym))
|
||||
yyerror("\"%s\" does not have a constant value", s);
|
||||
error("\"%s\" does not have a constant value\n", sym->name);
|
||||
else
|
||||
return sym_GetValue(sym);
|
||||
|
||||
@@ -249,30 +318,28 @@ uint32_t sym_GetConstantValue(char const *s)
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a defined symbols value... aborts if not defined yet
|
||||
* Return a constant symbol's value
|
||||
*/
|
||||
uint32_t sym_GetDefinedValue(char const *s)
|
||||
uint32_t sym_GetConstantValue(char const *s)
|
||||
{
|
||||
struct Symbol const *sym = sym_FindSymbol(s);
|
||||
struct Symbol const *sym = sym_FindScopedSymbol(s);
|
||||
|
||||
if (sym == NULL || !sym_IsDefined(sym))
|
||||
yyerror("'%s' not defined", s);
|
||||
else if (!sym_IsNumeric(sym))
|
||||
yyerror("'%s' is a macro or string symbol", s);
|
||||
if (sym == NULL)
|
||||
error("'%s' not defined\n", s);
|
||||
else
|
||||
return sym_GetValue(sym);
|
||||
return sym_GetConstantSymValue(sym);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct Symbol *sym_GetCurrentSymbolScope(void)
|
||||
char const *sym_GetCurrentSymbolScope(void)
|
||||
{
|
||||
return symbolScope;
|
||||
return labelScope;
|
||||
}
|
||||
|
||||
void sym_SetCurrentSymbolScope(struct Symbol *newScope)
|
||||
void sym_SetCurrentSymbolScope(char const *newScope)
|
||||
{
|
||||
symbolScope = newScope;
|
||||
labelScope = newScope;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -282,13 +349,15 @@ void sym_SetCurrentSymbolScope(struct Symbol *newScope)
|
||||
*/
|
||||
static struct Symbol *createNonrelocSymbol(char const *symbolName)
|
||||
{
|
||||
struct Symbol *symbol = findsymbol(symbolName, NULL);
|
||||
struct Symbol *symbol = sym_FindExactSymbol(symbolName);
|
||||
|
||||
if (!symbol)
|
||||
if (!symbol) {
|
||||
symbol = createsymbol(symbolName);
|
||||
else if (sym_IsDefined(symbol))
|
||||
yyerror("'%s' already defined at %s(%" PRIu32 ")", symbolName,
|
||||
symbol->fileName, symbol->fileLine);
|
||||
} else if (sym_IsDefined(symbol)) {
|
||||
error("'%s' already defined at ", symbolName);
|
||||
dumpFilename(symbol);
|
||||
putc('\n', stderr);
|
||||
}
|
||||
|
||||
return symbol;
|
||||
}
|
||||
@@ -301,7 +370,6 @@ struct Symbol *sym_AddEqu(char const *symName, int32_t value)
|
||||
struct Symbol *sym = createNonrelocSymbol(symName);
|
||||
|
||||
sym->type = SYM_EQU;
|
||||
sym->callback = NULL;
|
||||
sym->value = value;
|
||||
|
||||
return sym;
|
||||
@@ -326,7 +394,7 @@ struct Symbol *sym_AddString(char const *symName, char const *value)
|
||||
char *string = malloc(len + 1);
|
||||
|
||||
if (string == NULL)
|
||||
fatalerror("No memory for string equate");
|
||||
fatalerror("No memory for string equate: %s\n", strerror(errno));
|
||||
strcpy(string, value);
|
||||
|
||||
sym->type = SYM_EQUS;
|
||||
@@ -342,96 +410,109 @@ struct Symbol *sym_AddString(char const *symName, char const *value)
|
||||
*/
|
||||
struct Symbol *sym_AddSet(char const *symName, int32_t value)
|
||||
{
|
||||
struct Symbol *sym = findsymbol(symName, NULL);
|
||||
struct Symbol *sym = sym_FindExactSymbol(symName);
|
||||
|
||||
if (sym == NULL)
|
||||
if (sym == NULL) {
|
||||
sym = createsymbol(symName);
|
||||
else if (sym_IsDefined(sym) && sym->type != SYM_SET)
|
||||
yyerror("'%s' already defined as %s at %s(%" PRIu32 ")",
|
||||
symName, sym->type == SYM_LABEL ? "label" : "constant",
|
||||
sym->fileName, sym->fileLine);
|
||||
else
|
||||
/* TODO: can the scope be incorrect when talking over refs? */
|
||||
} else if (sym_IsDefined(sym) && sym->type != SYM_SET) {
|
||||
error("'%s' already defined as %s at ",
|
||||
symName, sym->type == SYM_LABEL ? "label" : "constant");
|
||||
dumpFilename(sym);
|
||||
putc('\n', stderr);
|
||||
return sym;
|
||||
} else {
|
||||
updateSymbolFilename(sym);
|
||||
}
|
||||
|
||||
sym->type = SYM_SET;
|
||||
sym->callback = NULL;
|
||||
sym->value = value;
|
||||
|
||||
return sym;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a local (.name) relocatable symbol
|
||||
* Add a label (aka "relocatable symbol")
|
||||
* @param name The label's full name (so `.name` is invalid)
|
||||
* @return The created symbol
|
||||
*/
|
||||
struct Symbol *sym_AddLocalReloc(char const *symName)
|
||||
static struct Symbol *addLabel(char const *name)
|
||||
{
|
||||
if (!symbolScope) {
|
||||
yyerror("Local label '%s' in main scope", symName);
|
||||
assert(name[0] != '.'); /* The symbol name must have been expanded prior */
|
||||
struct Symbol *sym = sym_FindExactSymbol(name);
|
||||
|
||||
if (!sym) {
|
||||
sym = createsymbol(name);
|
||||
} else if (sym_IsDefined(sym)) {
|
||||
error("'%s' already defined at ", name);
|
||||
dumpFilename(sym);
|
||||
putc('\n', stderr);
|
||||
return NULL;
|
||||
} else {
|
||||
updateSymbolFilename(sym);
|
||||
}
|
||||
/* If the symbol already exists as a ref, just "take over" it */
|
||||
sym->type = SYM_LABEL;
|
||||
sym->value = sect_GetSymbolOffset();
|
||||
if (exportall)
|
||||
sym->isExported = true;
|
||||
sym->section = sect_GetSymbolSection();
|
||||
|
||||
if (sym && !sym->section)
|
||||
error("Label \"%s\" created outside of a SECTION\n", name);
|
||||
return sym;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a local (.name or Parent.name) relocatable symbol
|
||||
*/
|
||||
struct Symbol *sym_AddLocalLabel(char const *name)
|
||||
{
|
||||
if (!labelScope) {
|
||||
error("Local label '%s' in main scope\n", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char fullname[MAXSYMLEN + 1];
|
||||
|
||||
fullSymbolName(fullname, sizeof(fullname), symName, symbolScope);
|
||||
return sym_AddReloc(fullname);
|
||||
if (name[0] == '.') {
|
||||
/* If symbol is of the form `.name`, expand to the full `Parent.name` name */
|
||||
fullSymbolName(fullname, sizeof(fullname), name, labelScope);
|
||||
name = fullname; /* Use the expanded name instead */
|
||||
} else {
|
||||
size_t i = 0;
|
||||
|
||||
/* Otherwise, check that `Parent` is in fact the current scope */
|
||||
while (labelScope[i] && name[i] == labelScope[i])
|
||||
i++;
|
||||
/* Assuming no dots in `labelScope` */
|
||||
assert(strchr(&name[i], '.')); /* There should be at least one dot, though */
|
||||
size_t parentLen = i + (strchr(&name[i], '.') - name);
|
||||
|
||||
/*
|
||||
* Check that `labelScope[i]` ended the check, guaranteeing that `name` is at least
|
||||
* as long, and then that this was the entirety of the `Parent` part of `name`.
|
||||
*/
|
||||
if (labelScope[i] != '\0' || name[i] != '.')
|
||||
error("Not currently in the scope of '%.*s'\n", parentLen, name);
|
||||
if (strchr(&name[parentLen + 1], '.')) /* There will at least be a terminator */
|
||||
fatalerror("'%s' is a nonsensical reference to a nested local label\n",
|
||||
name);
|
||||
}
|
||||
|
||||
return addLabel(name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a relocatable symbol
|
||||
*/
|
||||
struct Symbol *sym_AddReloc(char const *symName)
|
||||
struct Symbol *sym_AddLabel(char const *name)
|
||||
{
|
||||
struct Symbol const *scope = NULL;
|
||||
char *localPtr = strchr(symName, '.');
|
||||
|
||||
if (localPtr != NULL) {
|
||||
if (!symbolScope) {
|
||||
yyerror("Local label in main scope");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
scope = symbolScope->scope ? symbolScope->scope : symbolScope;
|
||||
uint32_t parentLen = localPtr - symName;
|
||||
|
||||
if (strchr(localPtr + 1, '.') != NULL)
|
||||
fatalerror("'%s' is a nonsensical reference to a nested local symbol",
|
||||
symName);
|
||||
else if (strlen(scope->name) != parentLen
|
||||
|| strncmp(symName, scope->name, parentLen) != 0)
|
||||
yyerror("Not currently in the scope of '%.*s'",
|
||||
parentLen, symName);
|
||||
}
|
||||
|
||||
struct Symbol *sym = findsymbol(symName, scope);
|
||||
|
||||
if (!sym)
|
||||
sym = createsymbol(symName);
|
||||
else if (sym_IsDefined(sym))
|
||||
yyerror("'%s' already defined in %s(%" PRIu32 ")", symName,
|
||||
sym->fileName, sym->fileLine);
|
||||
/* If the symbol already exists as a ref, just "take over" it */
|
||||
|
||||
sym->type = SYM_LABEL;
|
||||
sym->callback = NULL;
|
||||
sym->value = curOffset;
|
||||
|
||||
if (exportall)
|
||||
sym->isExported = true;
|
||||
|
||||
sym->scope = scope;
|
||||
sym->section = sect_GetSymbolSection();
|
||||
/* Labels need to be assigned a section, except PC */
|
||||
if (!sym->section && strcmp(symName, "@"))
|
||||
yyerror("Label \"%s\" created outside of a SECTION",
|
||||
symName);
|
||||
|
||||
updateSymbolFilename(sym);
|
||||
struct Symbol *sym = addLabel(name);
|
||||
|
||||
/* Set the symbol as the new scope */
|
||||
/* TODO: don't do this for local labels */
|
||||
symbolScope = findsymbol(symName, scope);
|
||||
return symbolScope;
|
||||
if (sym)
|
||||
labelScope = sym->name;
|
||||
return sym;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -439,7 +520,7 @@ struct Symbol *sym_AddReloc(char const *symName)
|
||||
*/
|
||||
void sym_Export(char const *symName)
|
||||
{
|
||||
struct Symbol *sym = sym_FindSymbol(symName);
|
||||
struct Symbol *sym = sym_FindScopedSymbol(symName);
|
||||
|
||||
/* If the symbol doesn't exist, create a ref that can be purged */
|
||||
if (!sym)
|
||||
@@ -450,14 +531,14 @@ void sym_Export(char const *symName)
|
||||
/*
|
||||
* Add a macro definition
|
||||
*/
|
||||
struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo)
|
||||
struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size)
|
||||
{
|
||||
struct Symbol *sym = createNonrelocSymbol(symName);
|
||||
|
||||
sym->type = SYM_MACRO;
|
||||
sym->macroSize = ulNewMacroSize;
|
||||
sym->macro = tzNewMacro;
|
||||
updateSymbolFilename(sym);
|
||||
sym->macroSize = size;
|
||||
sym->macro = body;
|
||||
setSymbolFilename(sym); /* TODO: is this really necessary? */
|
||||
/*
|
||||
* The symbol is created at the line after the `endm`,
|
||||
* override this with the actual definition line
|
||||
@@ -473,26 +554,20 @@ struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo)
|
||||
*/
|
||||
struct Symbol *sym_Ref(char const *symName)
|
||||
{
|
||||
struct Symbol *nsym = sym_FindSymbol(symName);
|
||||
struct Symbol *nsym = sym_FindScopedSymbol(symName);
|
||||
|
||||
if (nsym == NULL) {
|
||||
char fullname[MAXSYMLEN + 1];
|
||||
struct Symbol const *scope = NULL;
|
||||
|
||||
if (symName[0] == '.') {
|
||||
if (!symbolScope)
|
||||
fatalerror("Local label reference '%s' in main scope",
|
||||
symName);
|
||||
scope = symbolScope->scope ? symbolScope->scope
|
||||
: symbolScope;
|
||||
fullSymbolName(fullname, sizeof(fullname), symName,
|
||||
symbolScope);
|
||||
if (!labelScope)
|
||||
fatalerror("Local label reference '%s' in main scope\n", symName);
|
||||
fullSymbolName(fullname, sizeof(fullname), symName, labelScope);
|
||||
symName = fullname;
|
||||
}
|
||||
|
||||
nsym = createsymbol(symName);
|
||||
nsym->type = SYM_REF;
|
||||
nsym->scope = scope;
|
||||
}
|
||||
|
||||
return nsym;
|
||||
@@ -517,21 +592,36 @@ static inline char const *removeLeadingZeros(char const *ptr)
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static inline struct Symbol *createBuiltinSymbol(char const *name)
|
||||
{
|
||||
struct Symbol *sym = createsymbol(name);
|
||||
|
||||
sym->isBuiltin = true;
|
||||
sym->hasCallback = true;
|
||||
sym->src = NULL;
|
||||
sym->fileLine = 0;
|
||||
return sym;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the symboltable
|
||||
*/
|
||||
void sym_Init(void)
|
||||
{
|
||||
struct Symbol *_NARGSymbol = sym_AddEqu("_NARG", 0);
|
||||
struct Symbol *__LINE__Symbol = sym_AddEqu("__LINE__", 0);
|
||||
PCSymbol = createBuiltinSymbol("@");
|
||||
struct Symbol *_NARGSymbol = createBuiltinSymbol("_NARG");
|
||||
struct Symbol *__LINE__Symbol = createBuiltinSymbol("__LINE__");
|
||||
struct Symbol *__FILE__Symbol = createBuiltinSymbol("__FILE__");
|
||||
|
||||
PCSymbol = sym_AddReloc("@"),
|
||||
PCSymbol->isBuiltin = true;
|
||||
PCSymbol->callback = CallbackPC;
|
||||
_NARGSymbol->isBuiltin = true;
|
||||
_NARGSymbol->callback = Callback_NARG;
|
||||
__LINE__Symbol->isBuiltin = true;
|
||||
__LINE__Symbol->callback = Callback__LINE__;
|
||||
PCSymbol->type = SYM_LABEL;
|
||||
PCSymbol->section = NULL;
|
||||
PCSymbol->numCallback = CallbackPC;
|
||||
_NARGSymbol->type = SYM_EQU;
|
||||
_NARGSymbol->numCallback = Callback_NARG;
|
||||
__LINE__Symbol->type = SYM_EQU;
|
||||
__LINE__Symbol->numCallback = Callback__LINE__;
|
||||
__FILE__Symbol->type = SYM_EQUS;
|
||||
__FILE__Symbol->strCallback = Callback__FILE__;
|
||||
sym_AddSet("_RS", 0)->isBuiltin = true;
|
||||
|
||||
sym_AddEqu("__RGBDS_MAJOR__", PACKAGE_VERSION_MAJOR)->isBuiltin = true;
|
||||
@@ -580,7 +670,7 @@ void sym_Init(void)
|
||||
addString("__UTC_SECOND__", removeLeadingZeros(savedSECOND));
|
||||
#undef addString
|
||||
|
||||
symbolScope = NULL;
|
||||
labelScope = NULL;
|
||||
|
||||
math_DefinePI();
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "asm/main.h"
|
||||
@@ -27,21 +28,54 @@ uint32_t calchash(const char *s)
|
||||
return hash;
|
||||
}
|
||||
|
||||
int32_t readUTF8Char(char *dest, char *src)
|
||||
char const *print(int c)
|
||||
{
|
||||
uint32_t state;
|
||||
uint32_t codep;
|
||||
int32_t i;
|
||||
static char buf[5]; /* '\xNN' + '\0' */
|
||||
|
||||
for (i = 0, state = 0;; i++) {
|
||||
if (decode(&state, &codep, (uint8_t)src[i]) == 1)
|
||||
fatalerror("invalid UTF-8 character");
|
||||
if (c == EOF)
|
||||
return "EOF";
|
||||
|
||||
if (isprint(c)) {
|
||||
buf[0] = c;
|
||||
buf[1] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
buf[0] = '\\';
|
||||
switch (c) {
|
||||
case '\n':
|
||||
buf[1] = 'n';
|
||||
break;
|
||||
case '\r':
|
||||
buf[1] = 'r';
|
||||
break;
|
||||
case '\t':
|
||||
buf[1] = 't';
|
||||
break;
|
||||
|
||||
default: /* Print as hex */
|
||||
buf[1] = 'x';
|
||||
sprintf(&buf[2], "%02hhx", c);
|
||||
return buf;
|
||||
}
|
||||
buf[2] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
size_t readUTF8Char(uint8_t *dest, char const *src)
|
||||
{
|
||||
uint32_t state = 0;
|
||||
uint32_t codep;
|
||||
size_t i = 0;
|
||||
|
||||
for (;;) {
|
||||
if (decode(&state, &codep, src[i]) == 1)
|
||||
fatalerror("invalid UTF-8 character\n");
|
||||
|
||||
dest[i] = src[i];
|
||||
i++;
|
||||
|
||||
if (state == 0) {
|
||||
dest[++i] = '\0';
|
||||
if (state == 0)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,18 +28,20 @@ enum WarningState {
|
||||
};
|
||||
|
||||
static enum WarningState const defaultWarnings[NB_WARNINGS] = {
|
||||
WARNING_ENABLED, /* Assertions */
|
||||
WARNING_DISABLED, /* Invalid args to builtins */
|
||||
WARNING_DISABLED, /* Division undefined behavior */
|
||||
WARNING_DISABLED, /* `db`, `dw`, or `dl` with no directive in ROM */
|
||||
WARNING_DISABLED, /* Empty entry in `db`, `dw` or `dl` */
|
||||
WARNING_DISABLED, /* Constants too large */
|
||||
WARNING_DISABLED, /* String too long for internal buffers */
|
||||
WARNING_ENABLED, /* Obsolete things */
|
||||
WARNING_DISABLED, /* Shifting undefined behavior */
|
||||
WARNING_DISABLED, /* Strange shift amount */
|
||||
WARNING_ENABLED, /* Implicit truncation loses some bits */
|
||||
WARNING_ENABLED, /* User warnings */
|
||||
[WARNING_ASSERT] = WARNING_ENABLED,
|
||||
[WARNING_BUILTIN_ARG] = WARNING_DISABLED,
|
||||
[WARNING_CHARMAP_REDEF] = WARNING_DISABLED,
|
||||
[WARNING_DIV] = WARNING_DISABLED,
|
||||
[WARNING_EMPTY_DATA_DIRECTIVE] = WARNING_DISABLED,
|
||||
[WARNING_EMPTY_ENTRY] = WARNING_DISABLED,
|
||||
[WARNING_LARGE_CONSTANT] = WARNING_DISABLED,
|
||||
[WARNING_LONG_STR] = WARNING_DISABLED,
|
||||
[WARNING_NESTED_COMMENT] = WARNING_ENABLED,
|
||||
[WARNING_OBSOLETE] = WARNING_ENABLED,
|
||||
[WARNING_SHIFT] = WARNING_DISABLED,
|
||||
[WARNING_SHIFT_AMOUNT] = WARNING_DISABLED,
|
||||
[WARNING_TRUNCATION] = WARNING_ENABLED,
|
||||
[WARNING_USER] = WARNING_ENABLED,
|
||||
};
|
||||
|
||||
static enum WarningState warningStates[NB_WARNINGS];
|
||||
@@ -68,11 +70,13 @@ static enum WarningState warningState(enum WarningID id)
|
||||
static char const *warningFlags[NB_WARNINGS_ALL] = {
|
||||
"assert",
|
||||
"builtin-args",
|
||||
"charmap-redef",
|
||||
"div",
|
||||
"empty-data-directive",
|
||||
"empty-entry",
|
||||
"large-constant",
|
||||
"long-string",
|
||||
"nested-comment",
|
||||
"obsolete",
|
||||
"shift",
|
||||
"shift-amount",
|
||||
@@ -92,6 +96,7 @@ enum MetaWarningCommand {
|
||||
/* Warnings that probably indicate an error */
|
||||
static uint8_t const _wallCommands[] = {
|
||||
WARNING_BUILTIN_ARG,
|
||||
WARNING_CHARMAP_REDEF,
|
||||
WARNING_EMPTY_DATA_DIRECTIVE,
|
||||
WARNING_LARGE_CONSTANT,
|
||||
WARNING_LONG_STR,
|
||||
@@ -101,6 +106,7 @@ static uint8_t const _wallCommands[] = {
|
||||
/* Warnings that are less likely to indicate an error */
|
||||
static uint8_t const _wextraCommands[] = {
|
||||
WARNING_EMPTY_ENTRY,
|
||||
WARNING_NESTED_COMMENT,
|
||||
META_WARNING_DONE
|
||||
};
|
||||
|
||||
@@ -112,6 +118,7 @@ static uint8_t const _weverythingCommands[] = {
|
||||
WARNING_EMPTY_ENTRY,
|
||||
WARNING_LARGE_CONSTANT,
|
||||
WARNING_LONG_STR,
|
||||
WARNING_NESTED_COMMENT,
|
||||
WARNING_OBSOLETE,
|
||||
WARNING_SHIFT,
|
||||
WARNING_SHIFT_AMOUNT,
|
||||
@@ -195,32 +202,32 @@ void processWarningFlag(char const *flag)
|
||||
warnx("Unknown warning `%s`", flag);
|
||||
}
|
||||
|
||||
void verror(const char *fmt, va_list args, char const *flag)
|
||||
void printDiag(const char *fmt, va_list args, char const *type,
|
||||
char const *flagfmt, char const *flag)
|
||||
{
|
||||
fputs("ERROR: ", stderr);
|
||||
fstk_Dump();
|
||||
fprintf(stderr, flag ? ": [-Werror=%s]\n " : ":\n ", flag);
|
||||
fputs(type, stderr);
|
||||
fstk_DumpCurrent();
|
||||
fprintf(stderr, flagfmt, flag);
|
||||
vfprintf(stderr, fmt, args);
|
||||
fputc('\n', stderr);
|
||||
fstk_DumpStringExpansions();
|
||||
lexer_DumpStringExpansions();
|
||||
}
|
||||
|
||||
void error(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
printDiag(fmt, args, "ERROR: ", ":\n ", NULL);
|
||||
va_end(args);
|
||||
nbErrors++;
|
||||
}
|
||||
|
||||
void yyerror(const char *fmt, ...)
|
||||
_Noreturn void fatalerror(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
verror(fmt, args, NULL);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
noreturn_ void fatalerror(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
verror(fmt, args, NULL);
|
||||
printDiag(fmt, args, "FATAL: ", ":\n ", NULL);
|
||||
va_end(args);
|
||||
|
||||
exit(1);
|
||||
@@ -238,24 +245,19 @@ void warning(enum WarningID id, char const *fmt, ...)
|
||||
return;
|
||||
|
||||
case WARNING_ERROR:
|
||||
verror(fmt, args, flag);
|
||||
printDiag(fmt, args, "ERROR: ", ": [-Werror=%s]\n ", flag);
|
||||
va_end(args);
|
||||
return;
|
||||
|
||||
case WARNING_DEFAULT:
|
||||
trap_;
|
||||
unreachable_();
|
||||
/* Not reached */
|
||||
|
||||
case WARNING_ENABLED:
|
||||
break;
|
||||
}
|
||||
|
||||
fputs("warning: ", stderr);
|
||||
fstk_Dump();
|
||||
fprintf(stderr, ": [-W%s]\n ", flag);
|
||||
vfprintf(stderr, fmt, args);
|
||||
fputc('\n', stderr);
|
||||
fstk_DumpStringExpansions();
|
||||
printDiag(fmt, args, "warning: ", ": [-W%s]\n ", flag);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
8
src/extern/err.c
vendored
8
src/extern/err.c
vendored
@@ -34,7 +34,7 @@ void rgbds_vwarnx(const char *fmt, va_list ap)
|
||||
putc('\n', stderr);
|
||||
}
|
||||
|
||||
noreturn_ void rgbds_verr(int status, const char *fmt, va_list ap)
|
||||
_Noreturn void rgbds_verr(int status, const char *fmt, va_list ap)
|
||||
{
|
||||
fprintf(stderr, "error: ");
|
||||
if (fmt) {
|
||||
@@ -46,7 +46,7 @@ noreturn_ void rgbds_verr(int status, const char *fmt, va_list ap)
|
||||
exit(status);
|
||||
}
|
||||
|
||||
noreturn_ void rgbds_verrx(int status, const char *fmt, va_list ap)
|
||||
_Noreturn void rgbds_verrx(int status, const char *fmt, va_list ap)
|
||||
{
|
||||
fprintf(stderr, "error");
|
||||
if (fmt) {
|
||||
@@ -75,7 +75,7 @@ void rgbds_warnx(const char *fmt, ...)
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
noreturn_ void rgbds_err(int status, const char *fmt, ...)
|
||||
_Noreturn void rgbds_err(int status, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
@@ -84,7 +84,7 @@ noreturn_ void rgbds_err(int status, const char *fmt, ...)
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
noreturn_ void rgbds_errx(int status, const char *fmt, ...)
|
||||
_Noreturn void rgbds_errx(int status, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
|
||||
2
src/extern/utf8decoder.c
vendored
2
src/extern/utf8decoder.c
vendored
@@ -38,7 +38,7 @@ static const uint8_t utf8d[] = {
|
||||
1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* s8 */
|
||||
};
|
||||
|
||||
uint32_t decode(uint32_t *state, uint32_t *codep, uint32_t byte)
|
||||
uint32_t decode(uint32_t *state, uint32_t *codep, uint8_t byte)
|
||||
{
|
||||
uint32_t type = utf8d[byte];
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ static void print_usage(void)
|
||||
" -V, --version print RGBFIX version and exit\n"
|
||||
" -v, --validate fix the header logo and both checksums (-f lhg)\n"
|
||||
"\n"
|
||||
"For help, use `man rgbfix' or go to https://rednex.github.io/rgbds/\n",
|
||||
"For help, use `man rgbfix' or go to https://rgbds.gbdev.io/docs/\n",
|
||||
stderr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ The following will duplicate the header (sans global checksum) of the game
|
||||
SurvivalKids.gbc
|
||||
.Sh BUGS
|
||||
Please report bugs on
|
||||
.Lk https://github.com/rednex/rgbds/issues GitHub .
|
||||
.Lk https://github.com/gbdev/rgbds/issues GitHub .
|
||||
.Sh SEE ALSO
|
||||
.Xr rgbasm 1 ,
|
||||
.Xr rgblink 1 ,
|
||||
@@ -173,4 +173,4 @@ Please report bugs on
|
||||
.Nm
|
||||
was originally released by Carsten S\(/orensen as a standalone program called gbfix, and was later packaged in RGBDS by Justin Lloyd.
|
||||
It is now maintained by a number of contributors at
|
||||
.Lk https://github.com/rednex/rgbds .
|
||||
.Lk https://github.com/gbdev/rgbds .
|
||||
|
||||
@@ -22,12 +22,10 @@ as destination can omit the destination as it is assumed to be register
|
||||
.Sy A
|
||||
by default.
|
||||
The following two lines have the same effect:
|
||||
.Pp
|
||||
.Bd -literal -offset indent
|
||||
OR A,B
|
||||
OR B
|
||||
.Ed
|
||||
.Pp
|
||||
.Sh LEGEND
|
||||
List of abbreviations used in this document.
|
||||
.Bl -tag
|
||||
@@ -1859,4 +1857,4 @@ Flags: See
|
||||
was originally written by Carsten S\(/orensen as part of the ASMotor package,
|
||||
and was later packaged in RGBDS by Justin Lloyd.
|
||||
It is now maintained by a number of contributors at
|
||||
.Lk https://github.com/rednex/rgbds .
|
||||
.Lk https://github.com/gbdev/rgbds .
|
||||
|
||||
@@ -66,7 +66,7 @@ static void print_usage(void)
|
||||
" -u, --unique-tiles optimize out identical tiles\n"
|
||||
" -V, --version print RGBGFX version and exit\n"
|
||||
"\n"
|
||||
"For help, use `man rgbgfx' or go to https://rednex.github.io/rgbds/\n",
|
||||
"For help, use `man rgbgfx' or go to https://rgbds.gbdev.io/docs/\n",
|
||||
stderr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
The
|
||||
.Nm
|
||||
program converts PNG images into the Nintendo Game Boy's planar tile format.
|
||||
|
||||
.Pp
|
||||
The resulting colors and their palette indices are determined differently depending on the input PNG file:
|
||||
.Bl -dash -width Ds
|
||||
.It
|
||||
@@ -39,7 +39,7 @@ If the image has multiple shades that map to the same index, the palette is inst
|
||||
.It
|
||||
If the image has color (or the grayscale method failed), the colors are sorted from lightest to darkest.
|
||||
.El
|
||||
|
||||
.Pp
|
||||
The input image may not contain more colors than the selected bit depth allows.
|
||||
Transparent pixels are set to palette index 0.
|
||||
.Sh ARGUMENTS
|
||||
@@ -53,7 +53,7 @@ is invalid because it could also be
|
||||
.Fl Fl version .
|
||||
The arguments are as follows:
|
||||
.Bl -tag -width Ds
|
||||
.It Fl a Ar attrmap, Fl Fl attr-map Ar attrmap
|
||||
.It Fl a Ar attrmap , Fl Fl attr-map Ar attrmap
|
||||
Generate a file of tile mirroring attributes for OAM or (CGB-only) background tiles.
|
||||
For each tile in the input file, a byte is written representing the dimensions that the associated tile in the output file should be mirrored.
|
||||
Useful in combination with
|
||||
@@ -143,7 +143,7 @@ The following will do nothing:
|
||||
.D1 $ rgbgfx in.png
|
||||
.Sh BUGS
|
||||
Please report bugs on
|
||||
.Lk https://github.com/rednex/rgbds/issues GitHub .
|
||||
.Lk https://github.com/gbdev/rgbds/issues GitHub .
|
||||
.Sh SEE ALSO
|
||||
.Xr rgbds 7 ,
|
||||
.Xr rgbasm 1 ,
|
||||
@@ -156,4 +156,4 @@ was created by
|
||||
.An stag019
|
||||
to be included in RGBDS.
|
||||
It is now maintained by a number of contributors at
|
||||
.Lk https://github.com/rednex/rgbds .
|
||||
.Lk https://github.com/gbdev/rgbds .
|
||||
|
||||
@@ -64,6 +64,22 @@ bool hash_AddElement(HashMap map, char const *key, void *element)
|
||||
return newEntry->next != NULL;
|
||||
}
|
||||
|
||||
bool hash_ReplaceElement(HashMap const map, char const *key, void *element)
|
||||
{
|
||||
HashType hashedKey = hash(key);
|
||||
struct HashMapEntry *ptr = map[(HalfHashType)hashedKey];
|
||||
|
||||
while (ptr) {
|
||||
if (hashedKey >> HALF_HASH_NB_BITS == ptr->hash
|
||||
&& !strcmp(ptr->key, key)) {
|
||||
ptr->content = element;
|
||||
return true;
|
||||
}
|
||||
ptr = ptr->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hash_RemoveElement(HashMap map, char const *key)
|
||||
{
|
||||
HashType hashedKey = hash(key);
|
||||
|
||||
@@ -81,14 +81,14 @@ static void processLinkerScript(void)
|
||||
|
||||
/* Check if this doesn't conflict with what the code says */
|
||||
if (section->isBankFixed && placement->bank != section->bank)
|
||||
error("Linker script contradicts \"%s\"'s bank placement",
|
||||
error(NULL, 0, "Linker script contradicts \"%s\"'s bank placement",
|
||||
section->name);
|
||||
if (section->isAddressFixed && placement->org != section->org)
|
||||
error("Linker script contradicts \"%s\"'s address placement",
|
||||
error(NULL, 0, "Linker script contradicts \"%s\"'s address placement",
|
||||
section->name);
|
||||
if (section->isAlignFixed
|
||||
&& (placement->org & section->alignMask) != 0)
|
||||
error("Linker script contradicts \"%s\"'s alignment",
|
||||
error(NULL, 0, "Linker script contradicts \"%s\"'s alignment",
|
||||
section->name);
|
||||
|
||||
section->isAddressFixed = true;
|
||||
@@ -439,7 +439,7 @@ void assign_AssignSections(void)
|
||||
return;
|
||||
}
|
||||
|
||||
trap_;
|
||||
unreachable_();
|
||||
}
|
||||
|
||||
void assign_Cleanup(void)
|
||||
|
||||
@@ -6,11 +6,14 @@
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
@@ -39,11 +42,55 @@ bool disablePadding; /* -x */
|
||||
|
||||
static uint32_t nbErrors = 0;
|
||||
|
||||
void error(char const *fmt, ...)
|
||||
/***** Helper function to dump a file stack to stderr *****/
|
||||
|
||||
char const *dumpFileStack(struct FileStackNode const *node)
|
||||
{
|
||||
char const *lastName;
|
||||
|
||||
if (node->parent) {
|
||||
lastName = dumpFileStack(node->parent);
|
||||
/* REPT nodes use their parent's name */
|
||||
if (node->type != NODE_REPT)
|
||||
lastName = node->name;
|
||||
fprintf(stderr, "(%" PRIu32 ") -> %s", node->lineNo, lastName);
|
||||
if (node->type == NODE_REPT) {
|
||||
for (uint32_t i = 0; i < node->reptDepth; i++)
|
||||
fprintf(stderr, "::REPT~%" PRIu32, node->iters[i]);
|
||||
}
|
||||
} else {
|
||||
assert(node->type != NODE_REPT);
|
||||
lastName = node->name;
|
||||
fputs(lastName, stderr);
|
||||
}
|
||||
|
||||
return lastName;
|
||||
}
|
||||
|
||||
void warning(struct FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
fprintf(stderr, "error: ");
|
||||
fputs("warning: ", stderr);
|
||||
if (where) {
|
||||
dumpFileStack(where);
|
||||
fprintf(stderr, "(%" PRIu32 "): ", lineNo);
|
||||
}
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
putc('\n', stderr);
|
||||
}
|
||||
|
||||
void error(struct FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
fputs("error: ", stderr);
|
||||
if (where) {
|
||||
dumpFileStack(where);
|
||||
fprintf(stderr, "(%" PRIu32 "): ", lineNo);
|
||||
}
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
@@ -53,11 +100,15 @@ void error(char const *fmt, ...)
|
||||
nbErrors++;
|
||||
}
|
||||
|
||||
noreturn_ void fatal(char const *fmt, ...)
|
||||
_Noreturn void fatal(struct FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
fprintf(stderr, "fatal: ");
|
||||
fputs("fatal: ", stderr);
|
||||
if (where) {
|
||||
dumpFileStack(where);
|
||||
fprintf(stderr, "(%" PRIu32 "): ", lineNo);
|
||||
}
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
@@ -76,7 +127,13 @@ FILE *openFile(char const *fileName, char const *mode)
|
||||
if (!fileName)
|
||||
return NULL;
|
||||
|
||||
FILE *file = fopen(fileName, mode);
|
||||
FILE *file;
|
||||
if (strcmp(fileName, "-") != 0)
|
||||
file = fopen(fileName, mode);
|
||||
else if (mode[0] == 'r')
|
||||
file = fdopen(0, mode);
|
||||
else
|
||||
file = fdopen(1, mode);
|
||||
|
||||
if (!file)
|
||||
err(1, "Could not open file \"%s\"", fileName);
|
||||
@@ -132,7 +189,7 @@ static void printUsage(void)
|
||||
" -x, --nopad disable padding of output binary\n"
|
||||
" -V, --version print RGBLINK version and exits\n"
|
||||
"\n"
|
||||
"For help, use `man rgblink' or go to https://rednex.github.io/rgbds/\n",
|
||||
"For help, use `man rgblink' or go to https://rgbds.gbdev.io/docs/\n",
|
||||
stderr);
|
||||
}
|
||||
|
||||
@@ -177,11 +234,11 @@ int main(int argc, char *argv[])
|
||||
case 'p':
|
||||
value = strtoul(optarg, &endptr, 0);
|
||||
if (optarg[0] == '\0' || *endptr != '\0') {
|
||||
error("Invalid argument for option 'p'");
|
||||
error(NULL, 0, "Invalid argument for option 'p'");
|
||||
value = 0xFF;
|
||||
}
|
||||
if (value > 0xFF) {
|
||||
error("Argument for 'p' must be a byte (between 0 and 0xFF)");
|
||||
error(NULL, 0, "Argument for 'p' must be a byte (between 0 and 0xFF)");
|
||||
value = 0xFF;
|
||||
}
|
||||
padValue = value;
|
||||
@@ -189,7 +246,7 @@ int main(int argc, char *argv[])
|
||||
case 's':
|
||||
/* FIXME: nobody knows what this does, figure it out */
|
||||
(void)optarg;
|
||||
warnx("Nobody has any idea what `-s` does");
|
||||
warning(NULL, 0, "Nobody has any idea what `-s` does");
|
||||
break;
|
||||
case 't':
|
||||
is32kMode = true;
|
||||
@@ -234,8 +291,8 @@ int main(int argc, char *argv[])
|
||||
bankranges[SECTTYPE_VRAM][1] = BANK_MIN_VRAM;
|
||||
|
||||
/* Read all object files first, */
|
||||
while (curArgIndex < argc)
|
||||
obj_ReadFile(argv[curArgIndex++]);
|
||||
for (obj_Setup(argc - curArgIndex); curArgIndex < argc; curArgIndex++)
|
||||
obj_ReadFile(argv[curArgIndex], argc - curArgIndex - 1);
|
||||
|
||||
/* then process them, */
|
||||
obj_DoSanityChecks();
|
||||
|
||||
@@ -31,6 +31,11 @@ static struct SymbolList {
|
||||
struct SymbolList *next;
|
||||
} *symbolLists;
|
||||
|
||||
unsigned int nbObjFiles;
|
||||
static struct {
|
||||
struct FileStackNode *nodes;
|
||||
uint32_t nbNodes;
|
||||
} *nodes;
|
||||
static struct Assertion *assertions;
|
||||
|
||||
/***** Helper functions for reading object files *****/
|
||||
@@ -170,12 +175,56 @@ static char *readstr(FILE *file)
|
||||
/***** Functions to parse object files *****/
|
||||
|
||||
/**
|
||||
* Reads a RGB6 symbol from a file.
|
||||
* Reads a file stack node form a file.
|
||||
* @param file The file to read from
|
||||
* @param nodes The file's array of nodes
|
||||
* @param i The ID of the node in the array
|
||||
* @param fileName The filename to report in errors
|
||||
*/
|
||||
static void readFileStackNode(FILE *file, struct FileStackNode fileNodes[], uint32_t i,
|
||||
char const *fileName)
|
||||
{
|
||||
uint32_t parentID;
|
||||
|
||||
tryReadlong(parentID, file,
|
||||
"%s: Cannot read node #%" PRIu32 "'s parent ID: %s", fileName, i);
|
||||
fileNodes[i].parent = parentID == -1 ? NULL : &fileNodes[parentID];
|
||||
tryReadlong(fileNodes[i].lineNo, file,
|
||||
"%s: Cannot read node #%" PRIu32 "'s line number: %s", fileName, i);
|
||||
tryGetc(fileNodes[i].type, file, "%s: Cannot read node #%" PRIu32 "'s type: %s",
|
||||
fileName, i);
|
||||
switch (fileNodes[i].type) {
|
||||
case NODE_FILE:
|
||||
case NODE_MACRO:
|
||||
tryReadstr(fileNodes[i].name, file,
|
||||
"%s: Cannot read node #%" PRIu32 "'s file name: %s", fileName, i);
|
||||
break;
|
||||
|
||||
case NODE_REPT:
|
||||
tryReadlong(fileNodes[i].reptDepth, file,
|
||||
"%s: Cannot read node #%" PRIu32 "'s rept depth: %s", fileName, i);
|
||||
fileNodes[i].iters = malloc(sizeof(*fileNodes[i].iters) * fileNodes[i].reptDepth);
|
||||
if (!fileNodes[i].iters)
|
||||
fatal(NULL, 0, "%s: Failed to alloc node #%" PRIu32 "'s iters: %s",
|
||||
fileName, i, strerror(errno));
|
||||
for (uint32_t k = 0; k < fileNodes[i].reptDepth; k++)
|
||||
tryReadlong(fileNodes[i].iters[k], file,
|
||||
"%s: Cannot read node #%" PRIu32 "'s iter #%" PRIu32 ": %s",
|
||||
fileName, i, k);
|
||||
if (!fileNodes[i].parent)
|
||||
fatal(NULL, 0, "%s is not a valid object file: root node (#%"
|
||||
PRIu32 ") may not be REPT", fileName, i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a symbol from a file.
|
||||
* @param file The file to read from
|
||||
* @param symbol The struct to fill
|
||||
* @param fileName The filename to report in errors
|
||||
*/
|
||||
static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName)
|
||||
static void readSymbol(FILE *file, struct Symbol *symbol,
|
||||
char const *fileName, struct FileStackNode fileNodes[])
|
||||
{
|
||||
tryReadstr(symbol->name, file, "%s: Cannot read symbol name: %s",
|
||||
fileName);
|
||||
@@ -184,9 +233,12 @@ static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName)
|
||||
/* If the symbol is defined in this file, read its definition */
|
||||
if (symbol->type != SYMTYPE_IMPORT) {
|
||||
symbol->objFileName = fileName;
|
||||
tryReadstr(symbol->fileName, file,
|
||||
"%s: Cannot read \"%s\"'s file name: %s",
|
||||
uint32_t nodeID;
|
||||
|
||||
tryReadlong(nodeID, file,
|
||||
"%s: Cannot read \"%s\"'s node ID: %s",
|
||||
fileName, symbol->name);
|
||||
symbol->src = &fileNodes[nodeID];
|
||||
tryReadlong(symbol->lineNo, file,
|
||||
"%s: Cannot read \"%s\"'s line number: %s",
|
||||
fileName, symbol->name);
|
||||
@@ -202,7 +254,7 @@ static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a RGB6 patch from a file.
|
||||
* Reads a patch from a file.
|
||||
* @param file The file to read from
|
||||
* @param patch The struct to fill
|
||||
* @param fileName The filename to report in errors
|
||||
@@ -210,20 +262,25 @@ static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName)
|
||||
*/
|
||||
static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
|
||||
char const *sectName, uint32_t i,
|
||||
struct Section *fileSections[])
|
||||
struct Section *fileSections[], struct FileStackNode fileNodes[])
|
||||
{
|
||||
tryReadstr(patch->fileName, file,
|
||||
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s name: %s",
|
||||
uint32_t nodeID;
|
||||
|
||||
tryReadlong(nodeID, file,
|
||||
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s node ID: %s",
|
||||
fileName, sectName, i);
|
||||
patch->src = &fileNodes[nodeID];
|
||||
tryReadlong(patch->lineNo, file,
|
||||
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s line number: %s",
|
||||
fileName, sectName, i);
|
||||
tryReadlong(patch->offset, file,
|
||||
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s offset: %s",
|
||||
fileName, sectName, i);
|
||||
tryReadlong(patch->pcSectionID, file,
|
||||
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
|
||||
fileName, sectName, i);
|
||||
patch->pcSection = patch->pcSectionID == -1
|
||||
? NULL
|
||||
: fileSections[patch->pcSectionID];
|
||||
patch->pcSection = patch->pcSectionID == -1 ? NULL
|
||||
: fileSections[patch->pcSectionID];
|
||||
tryReadlong(patch->pcOffset, file,
|
||||
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
|
||||
fileName, sectName, i);
|
||||
@@ -234,16 +291,17 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
|
||||
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s RPN size: %s",
|
||||
fileName, sectName, i);
|
||||
|
||||
uint8_t *rpnExpression =
|
||||
malloc(sizeof(*rpnExpression) * patch->rpnSize);
|
||||
size_t nbElementsRead = fread(rpnExpression, sizeof(*rpnExpression),
|
||||
patch->rpnExpression = malloc(sizeof(*patch->rpnExpression) * patch->rpnSize);
|
||||
if (!patch->rpnExpression)
|
||||
err(1, "%s: Failed to alloc \"%s\"'s patch #%" PRIu32 "'s RPN expression",
|
||||
fileName, sectName, i);
|
||||
size_t nbElementsRead = fread(patch->rpnExpression, sizeof(*patch->rpnExpression),
|
||||
patch->rpnSize, file);
|
||||
|
||||
if (nbElementsRead != patch->rpnSize)
|
||||
errx(1, "%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN expression: %s",
|
||||
fileName, sectName, i,
|
||||
feof(file) ? "Unexpected end of file" : strerror(errno));
|
||||
patch->rpnExpression = rpnExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -252,8 +310,8 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
|
||||
* @param section The struct to fill
|
||||
* @param fileName The filename to report in errors
|
||||
*/
|
||||
static void readSection(FILE *file, struct Section *section,
|
||||
char const *fileName, struct Section *fileSections[])
|
||||
static void readSection(FILE *file, struct Section *section, char const *fileName,
|
||||
struct Section *fileSections[], struct FileStackNode fileNodes[])
|
||||
{
|
||||
int32_t tmp;
|
||||
uint8_t byte;
|
||||
@@ -280,7 +338,7 @@ static void readSection(FILE *file, struct Section *section,
|
||||
fileName, section->name);
|
||||
section->isAddressFixed = tmp >= 0;
|
||||
if (tmp > UINT16_MAX) {
|
||||
error("\"%s\"'s org is too large (%" PRId32 ")",
|
||||
error(NULL, 0, "\"%s\"'s org is too large (%" PRId32 ")",
|
||||
section->name, tmp);
|
||||
tmp = UINT16_MAX;
|
||||
}
|
||||
@@ -296,7 +354,7 @@ static void readSection(FILE *file, struct Section *section,
|
||||
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s alignment offset: %s",
|
||||
fileName, section->name);
|
||||
if (tmp > UINT16_MAX) {
|
||||
error("\"%s\"'s alignment offset is too large (%" PRId32 ")",
|
||||
error(NULL, 0, "\"%s\"'s alignment offset is too large (%" PRId32 ")",
|
||||
section->name, tmp);
|
||||
tmp = UINT16_MAX;
|
||||
}
|
||||
@@ -332,7 +390,7 @@ static void readSection(FILE *file, struct Section *section,
|
||||
section->name);
|
||||
for (uint32_t i = 0; i < section->nbPatches; i++) {
|
||||
readPatch(file, &patches[i], fileName, section->name,
|
||||
i, fileSections);
|
||||
i, fileSections, fileNodes);
|
||||
}
|
||||
section->patches = patches;
|
||||
}
|
||||
@@ -375,13 +433,13 @@ static void linkSymToSect(struct Symbol const *symbol, struct Section *section)
|
||||
*/
|
||||
static void readAssertion(FILE *file, struct Assertion *assert,
|
||||
char const *fileName, uint32_t i,
|
||||
struct Section *fileSections[])
|
||||
struct Section *fileSections[], struct FileStackNode fileNodes[])
|
||||
{
|
||||
char assertName[sizeof("Assertion #" EXPAND_AND_STR(UINT32_MAX))];
|
||||
|
||||
snprintf(assertName, sizeof(assertName), "Assertion #%" PRIu32, i);
|
||||
|
||||
readPatch(file, &assert->patch, fileName, assertName, 0, fileSections);
|
||||
readPatch(file, &assert->patch, fileName, assertName, 0, fileSections, fileNodes);
|
||||
tryReadstr(assert->message, file, "%s: Cannot read assertion's message: %s",
|
||||
fileName);
|
||||
}
|
||||
@@ -394,11 +452,7 @@ static inline struct Section *getMainSection(struct Section *section)
|
||||
return section;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an object file of any supported format
|
||||
* @param fileName The filename to report for errors
|
||||
*/
|
||||
void obj_ReadFile(char const *fileName)
|
||||
void obj_ReadFile(char const *fileName, unsigned int fileID)
|
||||
{
|
||||
FILE *file = strcmp("-", fileName) ? fopen(fileName, "rb") : stdin;
|
||||
|
||||
@@ -438,6 +492,14 @@ void obj_ReadFile(char const *fileName)
|
||||
|
||||
nbSectionsToAssign += nbSections;
|
||||
|
||||
tryReadlong(nodes[fileID].nbNodes, file, "%s: Cannot read number of nodes: %s", fileName);
|
||||
nodes[fileID].nodes = calloc(nodes[fileID].nbNodes, sizeof(nodes[fileID].nodes[0]));
|
||||
if (!nodes[fileID].nodes)
|
||||
err(1, "Failed to get memory for %s's nodes", fileName);
|
||||
verbosePrint("Reading %u nodes...\n", nodes[fileID].nbNodes);
|
||||
for (uint32_t i = nodes[fileID].nbNodes; i--; )
|
||||
readFileStackNode(file, nodes[fileID].nodes, i, fileName);
|
||||
|
||||
/* This file's symbols, kept to link sections to them */
|
||||
struct Symbol **fileSymbols =
|
||||
malloc(sizeof(*fileSymbols) * nbSymbols + 1);
|
||||
@@ -464,7 +526,7 @@ void obj_ReadFile(char const *fileName)
|
||||
|
||||
if (!symbol)
|
||||
err(1, "%s: Couldn't create new symbol", fileName);
|
||||
readSymbol(file, symbol, fileName);
|
||||
readSymbol(file, symbol, fileName, nodes[fileID].nodes);
|
||||
|
||||
fileSymbols[i] = symbol;
|
||||
if (symbol->type == SYMTYPE_EXPORT)
|
||||
@@ -485,7 +547,7 @@ void obj_ReadFile(char const *fileName)
|
||||
err(1, "%s: Couldn't create new section", fileName);
|
||||
|
||||
fileSections[i]->nextu = NULL;
|
||||
readSection(file, fileSections[i], fileName, fileSections);
|
||||
readSection(file, fileSections[i], fileName, fileSections, nodes[fileID].nodes);
|
||||
fileSections[i]->fileSymbols = fileSymbols;
|
||||
if (nbSymPerSect[i]) {
|
||||
fileSections[i]->symbols = malloc(nbSymPerSect[i]
|
||||
@@ -535,7 +597,7 @@ void obj_ReadFile(char const *fileName)
|
||||
|
||||
if (!assertion)
|
||||
err(1, "%s: Couldn't create new assertion", fileName);
|
||||
readAssertion(file, assertion, fileName, i, fileSections);
|
||||
readAssertion(file, assertion, fileName, i, fileSections, nodes[fileID].nodes);
|
||||
assertion->fileSymbols = fileSymbols;
|
||||
assertion->next = assertions;
|
||||
assertions = assertion;
|
||||
@@ -555,6 +617,15 @@ void obj_CheckAssertions(void)
|
||||
patch_CheckAssertions(assertions);
|
||||
}
|
||||
|
||||
void obj_Setup(unsigned int nbFiles)
|
||||
{
|
||||
nbObjFiles = nbFiles;
|
||||
|
||||
if (nbFiles > SIZE_MAX / sizeof(*nodes))
|
||||
fatal(NULL, 0, "Impossible to link more than %zu files!", SIZE_MAX / sizeof(*nodes));
|
||||
nodes = malloc(sizeof(*nodes) * nbFiles);
|
||||
}
|
||||
|
||||
static void freeSection(struct Section *section, void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
@@ -562,12 +633,8 @@ static void freeSection(struct Section *section, void *arg)
|
||||
free(section->name);
|
||||
if (sect_HasData(section->type)) {
|
||||
free(section->data);
|
||||
for (int32_t i = 0; i < section->nbPatches; i++) {
|
||||
struct Patch *patch = §ion->patches[i];
|
||||
|
||||
free(patch->fileName);
|
||||
free(patch->rpnExpression);
|
||||
}
|
||||
for (int32_t i = 0; i < section->nbPatches; i++)
|
||||
free(section->patches[i].rpnExpression);
|
||||
free(section->patches);
|
||||
}
|
||||
free(section->symbols);
|
||||
@@ -577,13 +644,20 @@ static void freeSection(struct Section *section, void *arg)
|
||||
static void freeSymbol(struct Symbol *symbol)
|
||||
{
|
||||
free(symbol->name);
|
||||
if (symbol->type != SYMTYPE_IMPORT)
|
||||
free(symbol->fileName);
|
||||
free(symbol);
|
||||
}
|
||||
|
||||
void obj_Cleanup(void)
|
||||
{
|
||||
for (unsigned int i = 0; i < nbObjFiles; i++) {
|
||||
for (uint32_t j = 0; j < nodes[i].nbNodes; j++) {
|
||||
if (nodes[i].nodes[j].type == NODE_REPT)
|
||||
free(nodes[i].nodes[j].iters);
|
||||
}
|
||||
free(nodes[i].nodes);
|
||||
}
|
||||
free(nodes);
|
||||
|
||||
sym_CleanupSymbols();
|
||||
|
||||
sect_ForEach(freeSection, NULL);
|
||||
|
||||
203
src/link/patch.c
203
src/link/patch.c
@@ -6,11 +6,13 @@
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "link/object.h"
|
||||
#include "link/patch.h"
|
||||
#include "link/section.h"
|
||||
#include "link/symbol.h"
|
||||
@@ -60,9 +62,17 @@ static int32_t asl(int32_t value, int32_t shiftamt)
|
||||
return (uint32_t)value << shiftamt;
|
||||
}
|
||||
|
||||
/* This is an "empty"-type stack */
|
||||
/*
|
||||
* This is an "empty"-type stack. Apart from the actual values, we also remember
|
||||
* whether the value is a placeholder inserted for error recovery. This allows
|
||||
* us to avoid cascading errors.
|
||||
*
|
||||
* The best way to think about this is a stack of (value, errorFlag) pairs.
|
||||
* They are only separated for reasons of memory efficiency.
|
||||
*/
|
||||
struct RPNStack {
|
||||
int32_t *buf;
|
||||
int32_t *values;
|
||||
bool *errorFlags;
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
} stack;
|
||||
@@ -70,8 +80,9 @@ struct RPNStack {
|
||||
static inline void initRPNStack(void)
|
||||
{
|
||||
stack.capacity = 64;
|
||||
stack.buf = malloc(sizeof(*stack.buf) * stack.capacity);
|
||||
if (!stack.buf)
|
||||
stack.values = malloc(sizeof(*stack.values) * stack.capacity);
|
||||
stack.errorFlags = malloc(sizeof(*stack.errorFlags) * stack.capacity);
|
||||
if (!stack.values || !stack.errorFlags)
|
||||
err(1, "Failed to init RPN stack");
|
||||
}
|
||||
|
||||
@@ -80,7 +91,7 @@ static inline void clearRPNStack(void)
|
||||
stack.size = 0;
|
||||
}
|
||||
|
||||
static void pushRPN(int32_t value)
|
||||
static void pushRPN(int32_t value, bool comesFromError)
|
||||
{
|
||||
if (stack.size >= stack.capacity) {
|
||||
static const size_t increase_factor = 2;
|
||||
@@ -89,48 +100,59 @@ static void pushRPN(int32_t value)
|
||||
errx(1, "Overflow in RPN stack resize");
|
||||
|
||||
stack.capacity *= increase_factor;
|
||||
stack.buf =
|
||||
realloc(stack.buf, sizeof(*stack.buf) * stack.capacity);
|
||||
stack.values =
|
||||
realloc(stack.values, sizeof(*stack.values) * stack.capacity);
|
||||
stack.errorFlags =
|
||||
realloc(stack.errorFlags, sizeof(*stack.errorFlags) * stack.capacity);
|
||||
/*
|
||||
* Static analysis tools complain that the capacity might become
|
||||
* zero due to overflow, but fail to realize that it's caught by
|
||||
* the overflow check above. Hence the stringent check below.
|
||||
*/
|
||||
if (!stack.buf || !stack.capacity)
|
||||
if (!stack.values || !stack.errorFlags || !stack.capacity)
|
||||
err(1, "Failed to resize RPN stack");
|
||||
}
|
||||
|
||||
stack.buf[stack.size] = value;
|
||||
stack.values[stack.size] = value;
|
||||
stack.errorFlags[stack.size] = comesFromError;
|
||||
stack.size++;
|
||||
}
|
||||
|
||||
static int32_t popRPN(char const *fileName)
|
||||
// This flag tracks whether the RPN op that is currently being evaluated
|
||||
// has popped any values with the error flag set.
|
||||
static bool isError = false;
|
||||
|
||||
static int32_t popRPN(struct FileStackNode const *node, uint32_t lineNo)
|
||||
{
|
||||
if (stack.size == 0)
|
||||
errx(1, "%s: Internal error, RPN stack empty", fileName);
|
||||
fatal(node, lineNo, "Internal error, RPN stack empty");
|
||||
|
||||
stack.size--;
|
||||
return stack.buf[stack.size];
|
||||
isError |= stack.errorFlags[stack.size];
|
||||
return stack.values[stack.size];
|
||||
}
|
||||
|
||||
static inline void freeRPNStack(void)
|
||||
{
|
||||
free(stack.buf);
|
||||
free(stack.values);
|
||||
free(stack.errorFlags);
|
||||
}
|
||||
|
||||
/* RPN operators */
|
||||
|
||||
static uint32_t getRPNByte(uint8_t const **expression, int32_t *size,
|
||||
char const *fileName)
|
||||
struct FileStackNode const *node, uint32_t lineNo)
|
||||
{
|
||||
if (!(*size)--)
|
||||
errx(1, "%s: RPN expression overread", fileName);
|
||||
fatal(node, lineNo, "Internal error, RPN expression overread");
|
||||
|
||||
return *(*expression)++;
|
||||
}
|
||||
|
||||
static struct Symbol const *getSymbol(struct Symbol const * const *symbolList,
|
||||
uint32_t index)
|
||||
{
|
||||
assert(index != -1); /* PC needs to be handled specially, not here */
|
||||
struct Symbol const *symbol = symbolList[index];
|
||||
|
||||
/* If the symbol is defined elsewhere... */
|
||||
@@ -145,12 +167,14 @@ static struct Symbol const *getSymbol(struct Symbol const * const *symbolList,
|
||||
* @param patch The patch to compute the value of
|
||||
* @param section The section the patch is contained in
|
||||
* @return The patch's value
|
||||
* @return isError Set if an error occurred during evaluation, and further
|
||||
* errors caused by the value should be suppressed.
|
||||
*/
|
||||
static int32_t computeRPNExpr(struct Patch const *patch,
|
||||
struct Symbol const * const *fileSymbols)
|
||||
{
|
||||
/* Small shortcut to avoid a lot of repetition */
|
||||
#define popRPN() popRPN(patch->fileName)
|
||||
#define popRPN() popRPN(patch->src, patch->lineNo)
|
||||
|
||||
uint8_t const *expression = patch->rpnExpression;
|
||||
int32_t size = patch->rpnSize;
|
||||
@@ -159,9 +183,11 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
||||
|
||||
while (size > 0) {
|
||||
enum RPNCommand command = getRPNByte(&expression, &size,
|
||||
patch->fileName);
|
||||
patch->src, patch->lineNo);
|
||||
int32_t value;
|
||||
|
||||
isError = false;
|
||||
|
||||
/*
|
||||
* Friendly reminder:
|
||||
* Be VERY careful with two `popRPN` in the same expression.
|
||||
@@ -187,7 +213,9 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
||||
case RPN_DIV:
|
||||
value = popRPN();
|
||||
if (value == 0) {
|
||||
error("%s: Division by 0", patch->fileName);
|
||||
if (!isError)
|
||||
error(patch->src, patch->lineNo, "Division by 0");
|
||||
isError = true;
|
||||
popRPN();
|
||||
value = INT32_MAX;
|
||||
} else {
|
||||
@@ -197,7 +225,9 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
||||
case RPN_MOD:
|
||||
value = popRPN();
|
||||
if (value == 0) {
|
||||
error("%s: Modulo by 0", patch->fileName);
|
||||
if (!isError)
|
||||
error(patch->src, patch->lineNo, "Modulo by 0");
|
||||
isError = true;
|
||||
popRPN();
|
||||
value = 0;
|
||||
} else {
|
||||
@@ -269,18 +299,20 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
||||
value = 0;
|
||||
for (uint8_t shift = 0; shift < 32; shift += 8)
|
||||
value |= getRPNByte(&expression, &size,
|
||||
patch->fileName) << shift;
|
||||
patch->src, patch->lineNo) << shift;
|
||||
symbol = getSymbol(fileSymbols, value);
|
||||
|
||||
if (!symbol) {
|
||||
error("%s: Requested BANK() of symbol \"%s\", which was not found",
|
||||
patch->fileName,
|
||||
error(patch->src, patch->lineNo,
|
||||
"Requested BANK() of symbol \"%s\", which was not found",
|
||||
fileSymbols[value]->name);
|
||||
isError = true;
|
||||
value = 1;
|
||||
} else if (!symbol->section) {
|
||||
error("%s: Requested BANK() of non-label symbol \"%s\"",
|
||||
patch->fileName,
|
||||
error(patch->src, patch->lineNo,
|
||||
"Requested BANK() of non-label symbol \"%s\"",
|
||||
fileSymbols[value]->name);
|
||||
isError = true;
|
||||
value = 1;
|
||||
} else {
|
||||
value = symbol->section->bank;
|
||||
@@ -289,14 +321,16 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
||||
|
||||
case RPN_BANK_SECT:
|
||||
name = (char const *)expression;
|
||||
while (getRPNByte(&expression, &size, patch->fileName))
|
||||
while (getRPNByte(&expression, &size, patch->src, patch->lineNo))
|
||||
;
|
||||
|
||||
sect = sect_GetSection(name);
|
||||
|
||||
if (!sect) {
|
||||
error("%s: Requested BANK() of section \"%s\", which was not found",
|
||||
patch->fileName, name);
|
||||
error(patch->src, patch->lineNo,
|
||||
"Requested BANK() of section \"%s\", which was not found",
|
||||
name);
|
||||
isError = true;
|
||||
value = 1;
|
||||
} else {
|
||||
value = sect->bank;
|
||||
@@ -305,7 +339,9 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
||||
|
||||
case RPN_BANK_SELF:
|
||||
if (!patch->pcSection) {
|
||||
error("%s: PC has no bank outside a section");
|
||||
error(patch->src, patch->lineNo,
|
||||
"PC has no bank outside a section");
|
||||
isError = true;
|
||||
value = 1;
|
||||
} else {
|
||||
value = patch->pcSection->bank;
|
||||
@@ -314,11 +350,13 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
||||
|
||||
case RPN_HRAM:
|
||||
value = popRPN();
|
||||
if (value < 0
|
||||
|| (value > 0xFF && value < 0xFF00)
|
||||
|| value > 0xFFFF)
|
||||
error("%s: Value %" PRId32 " is not in HRAM range",
|
||||
patch->fileName, value);
|
||||
if (!isError && (value < 0
|
||||
|| (value > 0xFF && value < 0xFF00)
|
||||
|| value > 0xFFFF)) {
|
||||
error(patch->src, patch->lineNo,
|
||||
"Value %" PRId32 " is not in HRAM range", value);
|
||||
isError = true;
|
||||
}
|
||||
value &= 0xFF;
|
||||
break;
|
||||
|
||||
@@ -327,9 +365,12 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
||||
/* Acceptable values are 0x00, 0x08, 0x10, ..., 0x38
|
||||
* They can be easily checked with a bitmask
|
||||
*/
|
||||
if (value & ~0x38)
|
||||
error("%s: Value %" PRId32 " is not a RST vector",
|
||||
patch->fileName, value);
|
||||
if (value & ~0x38) {
|
||||
if (!isError)
|
||||
error(patch->src, patch->lineNo,
|
||||
"Value %" PRId32 " is not a RST vector", value);
|
||||
isError = true;
|
||||
}
|
||||
value |= 0xC7;
|
||||
break;
|
||||
|
||||
@@ -337,43 +378,49 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
||||
value = 0;
|
||||
for (uint8_t shift = 0; shift < 32; shift += 8)
|
||||
value |= getRPNByte(&expression, &size,
|
||||
patch->fileName) << shift;
|
||||
patch->src, patch->lineNo) << shift;
|
||||
break;
|
||||
|
||||
case RPN_SYM:
|
||||
value = 0;
|
||||
for (uint8_t shift = 0; shift < 32; shift += 8)
|
||||
value |= getRPNByte(&expression, &size,
|
||||
patch->fileName) << shift;
|
||||
patch->src, patch->lineNo) << shift;
|
||||
|
||||
symbol = getSymbol(fileSymbols, value);
|
||||
|
||||
if (!symbol) {
|
||||
error("%s: Unknown symbol \"%s\"",
|
||||
patch->fileName,
|
||||
fileSymbols[value]->name);
|
||||
} else if (strcmp(symbol->name, "@")) {
|
||||
value = symbol->value;
|
||||
/* Symbols attached to sections have offsets */
|
||||
if (symbol->section)
|
||||
value += symbol->section->org;
|
||||
} else if (!patch->pcSection) {
|
||||
error("%s: PC has no value outside a section",
|
||||
patch->fileName);
|
||||
value = 0;
|
||||
if (value == -1) { /* PC */
|
||||
if (!patch->pcSection) {
|
||||
error(patch->src, patch->lineNo,
|
||||
"PC has no value outside a section");
|
||||
value = 0;
|
||||
isError = true;
|
||||
} else {
|
||||
value = patch->pcOffset + patch->pcSection->org;
|
||||
}
|
||||
} else {
|
||||
value = patch->pcOffset + patch->pcSection->org;
|
||||
symbol = getSymbol(fileSymbols, value);
|
||||
|
||||
if (!symbol) {
|
||||
error(patch->src, patch->lineNo,
|
||||
"Unknown symbol \"%s\"", fileSymbols[value]->name);
|
||||
isError = true;
|
||||
} else {
|
||||
value = symbol->value;
|
||||
/* Symbols attached to sections have offsets */
|
||||
if (symbol->section)
|
||||
value += symbol->section->org;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
pushRPN(value);
|
||||
pushRPN(value, isError);
|
||||
}
|
||||
|
||||
if (stack.size > 1)
|
||||
error("%s: RPN stack has %zu entries on exit, not 1",
|
||||
patch->fileName, stack.size);
|
||||
error(patch->src, patch->lineNo,
|
||||
"RPN stack has %zu entries on exit, not 1", stack.size);
|
||||
|
||||
isError = false;
|
||||
return popRPN();
|
||||
|
||||
#undef popRPN
|
||||
@@ -385,27 +432,34 @@ void patch_CheckAssertions(struct Assertion *assert)
|
||||
initRPNStack();
|
||||
|
||||
while (assert) {
|
||||
if (!computeRPNExpr(&assert->patch,
|
||||
(struct Symbol const * const *)
|
||||
assert->fileSymbols)) {
|
||||
switch ((enum AssertionType)assert->patch.type) {
|
||||
int32_t value = computeRPNExpr(&assert->patch,
|
||||
(struct Symbol const * const *)assert->fileSymbols);
|
||||
enum AssertionType type = (enum AssertionType)assert->patch.type;
|
||||
|
||||
if (!isError && !value) {
|
||||
switch (type) {
|
||||
case ASSERT_FATAL:
|
||||
fatal("%s: %s", assert->patch.fileName,
|
||||
fatal(assert->patch.src, assert->patch.lineNo, "%s",
|
||||
assert->message[0] ? assert->message
|
||||
: "assert failure");
|
||||
/* Not reached */
|
||||
break; /* Here so checkpatch doesn't complain */
|
||||
case ASSERT_ERROR:
|
||||
error("%s: %s", assert->patch.fileName,
|
||||
error(assert->patch.src, assert->patch.lineNo, "%s",
|
||||
assert->message[0] ? assert->message
|
||||
: "assert failure");
|
||||
break;
|
||||
case ASSERT_WARN:
|
||||
warnx("%s: %s", assert->patch.fileName,
|
||||
assert->message[0] ? assert->message
|
||||
: "assert failure");
|
||||
warning(assert->patch.src, assert->patch.lineNo, "%s",
|
||||
assert->message[0] ? assert->message
|
||||
: "assert failure");
|
||||
break;
|
||||
}
|
||||
} else if (isError && type == ASSERT_FATAL) {
|
||||
fatal(assert->patch.src, assert->patch.lineNo,
|
||||
"couldn't evaluate assertion%s%s",
|
||||
assert->message[0] ? ": " : "",
|
||||
assert->message);
|
||||
}
|
||||
struct Assertion *next = assert->next;
|
||||
|
||||
@@ -441,9 +495,10 @@ static void applyFilePatches(struct Section *section, struct Section *dataSectio
|
||||
+ patch->pcOffset + 1;
|
||||
int16_t jumpOffset = value - address;
|
||||
|
||||
if (jumpOffset < -128 || jumpOffset > 127)
|
||||
error("%s: jr target out of reach (expected -129 < %" PRId16 " < 128)",
|
||||
patch->fileName, jumpOffset);
|
||||
if (!isError && (jumpOffset < -128 || jumpOffset > 127))
|
||||
error(patch->src, patch->lineNo,
|
||||
"jr target out of reach (expected -129 < %" PRId16 " < 128)",
|
||||
jumpOffset);
|
||||
dataSection->data[offset] = jumpOffset & 0xFF;
|
||||
} else {
|
||||
/* Patch a certain number of bytes */
|
||||
@@ -457,11 +512,11 @@ static void applyFilePatches(struct Section *section, struct Section *dataSectio
|
||||
[PATCHTYPE_LONG] = {4, INT32_MIN, INT32_MAX}
|
||||
};
|
||||
|
||||
if (value < types[patch->type].min
|
||||
|| value > types[patch->type].max)
|
||||
error("%s: Value %#" PRIx32 "%s is not %u-bit",
|
||||
patch->fileName, value,
|
||||
value < 0 ? " (maybe negative?)" : "",
|
||||
if (!isError && (value < types[patch->type].min
|
||||
|| value > types[patch->type].max))
|
||||
error(patch->src, patch->lineNo,
|
||||
"Value %#" PRIx32 "%s is not %u-bit",
|
||||
value, value < 0 ? " (maybe negative?)" : "",
|
||||
types[patch->type].size * 8U);
|
||||
for (uint8_t i = 0; i < types[patch->type].size; i++) {
|
||||
dataSection->data[offset + i] = value & 0xFF;
|
||||
|
||||
@@ -66,7 +66,7 @@ Enable DMG mode.
|
||||
Prohibit the use of sections that doesn't exist on a DMG, such as VRAM bank 1.
|
||||
This option automatically enables
|
||||
.Fl w .
|
||||
.It Fl l Ar linker_script, Fl Fl linkerscript Ar linker_script
|
||||
.It Fl l Ar linker_script , Fl Fl linkerscript Ar linker_script
|
||||
Specify a linker script file that tells the linker how sections must be placed in the ROM.
|
||||
The attributes assigned in the linker script must be consistent with any assigned in the code.
|
||||
See
|
||||
@@ -116,7 +116,7 @@ option!
|
||||
.Sh EXAMPLES
|
||||
All you need for a basic ROM is an object file, which can be made into a ROM image like so:
|
||||
.Pp
|
||||
.D1 $ rgblink -o bar.gb foo.o
|
||||
.Dl $ rgblink -o bar.gb foo.o
|
||||
.Pp
|
||||
The resulting
|
||||
.Ar bar.gb
|
||||
@@ -126,15 +126,13 @@ You should use
|
||||
to fix these so that the program will actually run in a Game Boy:
|
||||
.Pp
|
||||
.Dl $ rgbfix -v bar.gb
|
||||
.Ed
|
||||
.Pp
|
||||
Here is a more complete example:
|
||||
.Pp
|
||||
.Dl $ rgblink -o bin/game.gb -n bin/game.sym -p 0xFF obj/title.o obj/engine.o
|
||||
.Ed
|
||||
.Sh BUGS
|
||||
Please report bugs on
|
||||
.Lk https://github.com/rednex/rgbds/issues GitHub .
|
||||
.Lk https://github.com/gbdev/rgbds/issues GitHub .
|
||||
.Sh SEE ALSO
|
||||
.Xr rgbasm 1 ,
|
||||
.Xr rgblink 5 ,
|
||||
@@ -145,4 +143,4 @@ Please report bugs on
|
||||
.Nm
|
||||
was originally written by Carsten S\(/orensen as part of the ASMotor package, and was later packaged in RGBDS by Justin Lloyd.
|
||||
It is now maintained by a number of contributors at
|
||||
.Lk https://github.com/rednex/rgbds .
|
||||
.Lk https://github.com/gbdev/rgbds .
|
||||
|
||||
@@ -19,7 +19,6 @@ They can be lowercase or uppercase, it is ignored.
|
||||
Any line can contain a comment starting with
|
||||
.Ql \&;
|
||||
that ends at the end of the line:
|
||||
.Pp
|
||||
.Bd -literal -offset indent
|
||||
ROMX $F ; This is a comment
|
||||
"Functions to read array"
|
||||
@@ -86,6 +85,6 @@ in the linker script.
|
||||
.Sh HISTORY
|
||||
.Nm
|
||||
was originally written by Carsten S\(/orensen as part of the ASMotor package,
|
||||
and was later packaged in RGBDS by Justin Lloyd. It is now maintained by a
|
||||
number of contributors at
|
||||
.Lk https://github.com/rednex/rgbds .
|
||||
and was later packaged in RGBDS by Justin Lloyd.
|
||||
It is now maintained by a number of contributors at
|
||||
.Lk https://github.com/gbdev/rgbds .
|
||||
|
||||
@@ -318,7 +318,7 @@ static void processCommand(enum LinkerScriptCommand command, uint16_t arg,
|
||||
{
|
||||
switch (command) {
|
||||
case COMMAND_INVALID:
|
||||
trap_;
|
||||
unreachable_();
|
||||
|
||||
case COMMAND_ORG:
|
||||
break;
|
||||
@@ -331,8 +331,8 @@ static void processCommand(enum LinkerScriptCommand command, uint16_t arg,
|
||||
}
|
||||
|
||||
if (arg < *pc)
|
||||
errx(1, "%s(%" PRIu32 "): `%s` cannot be used to go backwards",
|
||||
linkerScriptName, lineNo, commands[command]);
|
||||
errx(1, "%s(%" PRIu32 "): `%s` cannot be used to go backwards (currently at $%x)",
|
||||
linkerScriptName, lineNo, commands[command], *pc);
|
||||
*pc = arg;
|
||||
}
|
||||
|
||||
@@ -391,12 +391,12 @@ struct SectionPlacement *script_NextSection(void)
|
||||
|
||||
switch (parserState) {
|
||||
case PARSER_FIRSTTIME:
|
||||
trap_;
|
||||
unreachable_();
|
||||
|
||||
case PARSER_LINESTART:
|
||||
switch (token->type) {
|
||||
case TOKEN_INVALID:
|
||||
trap_;
|
||||
unreachable_();
|
||||
|
||||
case TOKEN_EOF:
|
||||
if (!popFile())
|
||||
|
||||
@@ -108,7 +108,7 @@ static void mergeSections(struct Section *target, struct Section *other, enum Se
|
||||
break;
|
||||
|
||||
case SECTION_NORMAL:
|
||||
trap_;
|
||||
unreachable_();
|
||||
}
|
||||
|
||||
other->nextu = target->nextu;
|
||||
|
||||
@@ -8,9 +8,12 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "link/object.h"
|
||||
#include "link/symbol.h"
|
||||
#include "link/main.h"
|
||||
|
||||
#include "extern/err.h"
|
||||
#include "hashmap.h"
|
||||
|
||||
@@ -40,11 +43,15 @@ void sym_AddSymbol(struct Symbol *symbol)
|
||||
/* Check if the symbol already exists */
|
||||
struct Symbol *other = hash_GetElement(symbols, symbol->name);
|
||||
|
||||
if (other)
|
||||
errx(1, "\"%s\" both in %s from %s(%" PRId32 ") and in %s from %s(%" PRId32 ")",
|
||||
symbol->name,
|
||||
symbol->objFileName, symbol->fileName, symbol->lineNo,
|
||||
other->objFileName, other->fileName, other->lineNo);
|
||||
if (other) {
|
||||
fprintf(stderr, "error: \"%s\" both in %s from ", symbol->name, symbol->objFileName);
|
||||
dumpFileStack(symbol->src);
|
||||
fprintf(stderr, "(%" PRIu32 ") and in %s from ",
|
||||
symbol->lineNo, other->objFileName);
|
||||
dumpFileStack(other->src);
|
||||
fprintf(stderr, "(%" PRIu32 ")\n", other->lineNo);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* If not, add it */
|
||||
bool collided = hash_AddElement(symbols, symbol->name, symbol);
|
||||
|
||||
99
src/rgbds.5
99
src/rgbds.5
@@ -1,7 +1,7 @@
|
||||
.\"
|
||||
.\" This file is part of RGBDS.
|
||||
.\"
|
||||
.\" Copyright (c) 2017-2018, Antonio Nino Diaz and RGBDS contributors.
|
||||
.\" Copyright (c) 2017-2020, Antonio Nino Diaz and RGBDS contributors.
|
||||
.\"
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
@@ -16,9 +16,8 @@ This is the description of the object files used by
|
||||
.Xr rgbasm 1
|
||||
and
|
||||
.Xr rgblink 1 .
|
||||
.Em Please note that the specifications may change.
|
||||
.Em Please note that the specifications may change .
|
||||
This toolchain is in development and new features may require adding more information to the current format, or modifying some fields, which would break compatibility with older versions.
|
||||
.Pp
|
||||
.Sh FILE STRUCTURE
|
||||
The following types are used:
|
||||
.Pp
|
||||
@@ -29,41 +28,71 @@ is an 8‐bit integer.
|
||||
.Ar STRING
|
||||
is a 0‐terminated string of
|
||||
.Ar BYTE .
|
||||
.Pp
|
||||
.Bd -literal
|
||||
; Header
|
||||
|
||||
BYTE ID[4] ; "RGB9"
|
||||
LONG RevisionNumber ; The format's revision number this file uses
|
||||
LONG NumberOfSymbols ; The number of symbols used in this file
|
||||
LONG NumberOfSections ; The number of sections used in this file
|
||||
LONG RevisionNumber ; The format's revision number this file uses.
|
||||
LONG NumberOfSymbols ; The number of symbols used in this file.
|
||||
LONG NumberOfSections ; The number of sections used in this file.
|
||||
|
||||
; File info
|
||||
|
||||
LONG NumberOfNodes ; The number of nodes contained in this file.
|
||||
|
||||
REPT NumberOfNodes ; IMPORTANT NOTE: the nodes are actually written in
|
||||
; **reverse** order, meaningthe node with ID 0 is
|
||||
; the last one in the file!
|
||||
|
||||
LONG ParentID ; ID of the parent node, -1 means this is the root.
|
||||
|
||||
LONG ParentLineNo ; Line at which the parent context was exited.
|
||||
; Meaningless on the root node.
|
||||
|
||||
BYTE Type ; 0 = REPT node
|
||||
; 1 = File node
|
||||
; 2 = Macro node
|
||||
|
||||
IF Type != 0 ; If the node is not a REPT...
|
||||
|
||||
STRING Name ; The node's name: either a file name, or macro name
|
||||
; prefixed by its definition file name.
|
||||
|
||||
ELSE ; If the node is a REPT, it also contains the iter
|
||||
; counts of all the parent REPTs.
|
||||
|
||||
LONG Depth ; Size of the array below.
|
||||
|
||||
LONG Iter[Depth] ; The number of REPT iterations by increasing depth.
|
||||
|
||||
ENDC
|
||||
|
||||
ENDR
|
||||
|
||||
; Symbols
|
||||
|
||||
REPT NumberOfSymbols ; Number of symbols defined in this object file.
|
||||
REPT NumberOfSymbols ; Number of symbols defined in this object file.
|
||||
|
||||
STRING Name ; The name of this symbol. Local symbols are stored
|
||||
; as "Scope.Symbol".
|
||||
STRING Name ; The name of this symbol. Local symbols are stored
|
||||
; as "Scope.Symbol".
|
||||
|
||||
BYTE Type ; 0 = LOCAL symbol only used in this file.
|
||||
; 1 = IMPORT this symbol from elsewhere
|
||||
; 2 = EXPORT this symbol to other objects.
|
||||
; Bit 7 is independent from the above value, and
|
||||
; encodes whether the section is unionized
|
||||
BYTE Type ; 0 = LOCAL symbol only used in this file.
|
||||
; 1 = IMPORT this symbol from elsewhere
|
||||
; 2 = EXPORT this symbol to other objects.
|
||||
|
||||
IF (Type & 0x7F) != 1 ; If symbol is defined in this object file.
|
||||
IF (Type & 0x7F) != 1 ; If symbol is defined in this object file.
|
||||
|
||||
STRING FileName ; File where the symbol is defined.
|
||||
LONG SourceFile ; File where the symbol is defined.
|
||||
|
||||
LONG LineNum ; Line number in the file where the symbol is defined.
|
||||
LONG LineNum ; Line number in the file where the symbol is defined.
|
||||
|
||||
LONG SectionID ; The section number (of this object file) in which
|
||||
; this symbol is defined. If it doesn't belong to any
|
||||
; specific section (like a constant), this field has
|
||||
; the value -1.
|
||||
LONG SectionID ; The section number (of this object file) in which
|
||||
; this symbol is defined. If it doesn't belong to any
|
||||
; specific section (like a constant), this field has
|
||||
; the value -1.
|
||||
|
||||
LONG Value ; The symbols value. It's the offset into that
|
||||
; symbol's section.
|
||||
LONG Value ; The symbols value. It's the offset into that
|
||||
; symbol's section.
|
||||
|
||||
ENDC
|
||||
|
||||
@@ -84,6 +113,10 @@ REPT NumberOfSections
|
||||
; 5 = WRAMX
|
||||
; 6 = SRAM
|
||||
; 7 = OAM
|
||||
; Bits 7 and 6 are independent from the above value:
|
||||
; Bit 7 encodes whether the section is unionized
|
||||
; Bit 6 encodes whether the section is a fragment
|
||||
; Bits 6 and 7 may not be both set at the same time!
|
||||
|
||||
LONG Org ; Address to fix this section at. -1 if the linker should
|
||||
; decide (floating address).
|
||||
@@ -105,8 +138,10 @@ REPT NumberOfSections
|
||||
|
||||
REPT NumberOfPatches
|
||||
|
||||
STRING SourceFile ; Name of the source file (for printing error
|
||||
; messages).
|
||||
LONG SourceFile ; ID of the source file node (for printing
|
||||
; error messages).
|
||||
|
||||
LONG LineNo ; Line at which the patch was created.
|
||||
|
||||
LONG Offset ; Offset into the section where patch should
|
||||
; be applied (in bytes).
|
||||
@@ -143,7 +178,9 @@ LONG NumberOfAssertions
|
||||
|
||||
REPT NumberOfAssertions
|
||||
|
||||
STRING SourceFile ; Name of the source file (for printing the failure).
|
||||
LONG SourceFile ; ID of the source file node (for printing the failure).
|
||||
|
||||
LONG LineNo ; Line at which the assertion was created.
|
||||
|
||||
LONG Offset ; Offset into the section where the assertion is located.
|
||||
|
||||
@@ -180,8 +217,7 @@ The
|
||||
In the RGB format, RPN expressions are stored as
|
||||
.Ar BYTE Ns s
|
||||
with some bytes being special prefixes for integers and symbols.
|
||||
.Pp
|
||||
.Bl -column -offset indent ".Sy String" ".Sy String"
|
||||
.Bl -column -offset indent "Sy String" "Sy String"
|
||||
.It Sy Value Ta Sy Meaning
|
||||
.It Li $00 Ta Li + operator
|
||||
.It Li $01 Ta Li - operator
|
||||
@@ -207,7 +243,7 @@ with some bytes being special prefixes for integers and symbols.
|
||||
.It Li $50 Ta Li BANK(symbol) ,
|
||||
a
|
||||
.Ar LONG
|
||||
Symbol ID follows.
|
||||
Symbol ID follows, where -1 means PC
|
||||
.It Li $51 Ta Li BANK(section_name) ,
|
||||
a null-terminated string follows.
|
||||
.It Li $52 Ta Li Current BANK()
|
||||
@@ -220,7 +256,6 @@ integer follows.
|
||||
.It Li $81 Ta Ar LONG
|
||||
symbol ID follows.
|
||||
.El
|
||||
.Pp
|
||||
.Sh SEE ALSO
|
||||
.Xr rgbasm 1 ,
|
||||
.Xr rgblink 1 ,
|
||||
@@ -231,4 +266,4 @@ symbol ID follows.
|
||||
was originally written by Carsten S\(/orensen as part of the ASMotor package,
|
||||
and was later packaged in RGBDS by Justin Lloyd.
|
||||
It is now maintained by a number of contributors at
|
||||
.Lk https://github.com/rednex/rgbds .
|
||||
.Lk https://github.com/gbdev/rgbds .
|
||||
|
||||
13
src/rgbds.7
13
src/rgbds.7
@@ -13,7 +13,6 @@
|
||||
.Nd Rednex Game Boy Development System
|
||||
.Sh EXAMPLES
|
||||
To get a working ROM image from a single assembly source file:
|
||||
.Pp
|
||||
.Bd -literal -offset indent
|
||||
$ rgbasm \-o bar.o foo.asm
|
||||
$ rgblink \-o baz.gb bar.o
|
||||
@@ -26,7 +25,7 @@ $ rgbfix \-v \-p 0 baz.gb
|
||||
.Xr rgbds 5 ,
|
||||
.Xr gbz80 7
|
||||
.Sh HISTORY
|
||||
.Bl -ohang
|
||||
.Bl -item
|
||||
.It
|
||||
1997, Carsten S\(/orensen (AKA SurfSmurf) writes ASMotor as a general-purpose
|
||||
assembler/linker system for DOS/Win32.
|
||||
@@ -37,12 +36,18 @@ assembly/machine code, and releases this version as RGBDS.
|
||||
2009, Vegard Nossum adapts the code to be more UNIX-like and releases this
|
||||
version as rgbds-linux on GitHub.
|
||||
.It
|
||||
2010, Anthony J. Bentley forks that repository. The fork becomes the reference
|
||||
implementation of rgbds.
|
||||
2010, Anthony J. Bentley forks that repository.
|
||||
The fork becomes the reference implementation of rgbds.
|
||||
.It
|
||||
2017, Bentley's repository is moved to a neutral name.
|
||||
It is now maintained by a number of contributors at
|
||||
.Lk https://github.com/rednex/rgbds .
|
||||
.It
|
||||
2018, codebase relicensed under the MIT license.
|
||||
.It
|
||||
2020, repository is moved to the gbdev organisation, at
|
||||
.Lk https://github.com/gbdev/rgbds .
|
||||
The
|
||||
.Lk https://rgbds.gbdev.io
|
||||
website serving documentation and downloads is created.
|
||||
.El
|
||||
|
||||
1
test/asm/.gitignore
vendored
Normal file
1
test/asm/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
quote\"file.*
|
||||
1
test/asm/align-pc-outside-section.asm
Normal file
1
test/asm/align-pc-outside-section.asm
Normal file
@@ -0,0 +1 @@
|
||||
align 1
|
||||
2
test/asm/align-pc-outside-section.err
Normal file
2
test/asm/align-pc-outside-section.err
Normal file
@@ -0,0 +1,2 @@
|
||||
FATAL: align-pc-outside-section.asm(1):
|
||||
Code generation before SECTION directive
|
||||
0
test/asm/align-pc-outside-section.out
Normal file
0
test/asm/align-pc-outside-section.out
Normal file
@@ -6,5 +6,5 @@ ERROR: assert.asm(18):
|
||||
Expected constant expression: 'FloatingBase' is not constant at assembly time
|
||||
ERROR: assert.asm(18):
|
||||
Assertion failed
|
||||
ERROR: assert.asm(21):
|
||||
FATAL: assert.asm(21):
|
||||
Assertion failed
|
||||
|
||||
2
test/asm/block-comment-contents-error.asm
Normal file
2
test/asm/block-comment-contents-error.asm
Normal file
@@ -0,0 +1,2 @@
|
||||
/* block comments containing /* throw warnings */
|
||||
PRINTT "reachable\n"
|
||||
2
test/asm/block-comment-contents-error.err
Normal file
2
test/asm/block-comment-contents-error.err
Normal file
@@ -0,0 +1,2 @@
|
||||
warning: block-comment-contents-error.asm(1): [-Wnested-comment]
|
||||
/* in block comment
|
||||
1
test/asm/block-comment-contents-error.out
Normal file
1
test/asm/block-comment-contents-error.out
Normal file
@@ -0,0 +1 @@
|
||||
reachable
|
||||
1
test/asm/block-comment-termination-error.asm
Normal file
1
test/asm/block-comment-termination-error.asm
Normal file
@@ -0,0 +1 @@
|
||||
PRINTT /* block comments must terminate before EOF
|
||||
5
test/asm/block-comment-termination-error.err
Normal file
5
test/asm/block-comment-termination-error.err
Normal file
@@ -0,0 +1,5 @@
|
||||
ERROR: block-comment-termination-error.asm(1):
|
||||
Unterminated block comment
|
||||
ERROR: block-comment-termination-error.asm(1):
|
||||
syntax error
|
||||
error: Assembly aborted (2 errors)!
|
||||
0
test/asm/block-comment-termination-error.out
Normal file
0
test/asm/block-comment-termination-error.out
Normal file
5
test/asm/block-comment.asm
Normal file
5
test/asm/block-comment.asm
Normal file
@@ -0,0 +1,5 @@
|
||||
PRINTT /* block comments are ignored // ** */ "hi\n"
|
||||
PRINTT "block (/* ... */) comments at ends of line are fine\n" /* hi */
|
||||
PRINTT /* block comments
|
||||
can span multiple lines
|
||||
*/ "mutliline\n"
|
||||
0
test/asm/block-comment.err
Normal file
0
test/asm/block-comment.err
Normal file
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user