mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Compare commits
22 Commits
v0.6.0-rc2
...
race-car2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76c1995559 | ||
|
|
ae84570f04 | ||
|
|
094a31ef8c | ||
|
|
291dcf3b6c | ||
|
|
a890bd072b | ||
|
|
2f6c808ccb | ||
|
|
e80907abd0 | ||
|
|
25d39155d3 | ||
|
|
77021d229b | ||
|
|
1b250b90b2 | ||
|
|
e2b4723489 | ||
|
|
2507413162 | ||
|
|
e023a84d04 | ||
|
|
34c127d9c3 | ||
|
|
9a930988c2 | ||
|
|
8c4204c542 | ||
|
|
663c1930ec | ||
|
|
30ccf43f44 | ||
|
|
fdc17adbcb | ||
|
|
cc196954f3 | ||
|
|
55b6cfff84 | ||
|
|
1fc73b04eb |
@@ -1,99 +0,0 @@
|
|||||||
AccessModifierOffset: -4
|
|
||||||
AlignAfterOpenBracket: Align
|
|
||||||
AlignArrayOfStructures: Left
|
|
||||||
AlignConsecutiveAssignments: None
|
|
||||||
AlignConsecutiveBitFields: Consecutive
|
|
||||||
AlignConsecutiveDeclarations: None
|
|
||||||
AlignConsecutiveMacros: Consecutive
|
|
||||||
AlignEscapedNewlines: Left
|
|
||||||
AlignOperands: Align
|
|
||||||
AlignTrailingComments: false
|
|
||||||
AllowShortBlocksOnASingleLine: Empty
|
|
||||||
AllowShortCaseLabelsOnASingleLine: false
|
|
||||||
AllowShortEnumsOnASingleLine: true
|
|
||||||
AllowShortFunctionsOnASingleLine: InlineOnly
|
|
||||||
AllowShortIfStatementsOnASingleLine: Never
|
|
||||||
AllowShortLambdasOnASingleLine: All
|
|
||||||
AllowShortLoopsOnASingleLine: false
|
|
||||||
AlwaysBreakAfterReturnType: None
|
|
||||||
AlwaysBreakBeforeMultilineStrings: false
|
|
||||||
AlwaysBreakTemplateDeclarations: Yes
|
|
||||||
AttributeMacros:
|
|
||||||
- format_
|
|
||||||
- attr_
|
|
||||||
BinPackArguments: true
|
|
||||||
BinPackParameters: true
|
|
||||||
BitFieldColonSpacing: Both
|
|
||||||
BreakBeforeBinaryOperators: NonAssignment
|
|
||||||
BreakBeforeBraces: Attach
|
|
||||||
BreakBeforeConceptDeclarations: true
|
|
||||||
BreakBeforeTernaryOperators: true
|
|
||||||
BreakConstructorInitializers: BeforeColon
|
|
||||||
BreakInheritanceList: AfterComma
|
|
||||||
BreakStringLiterals: true
|
|
||||||
ColumnLimit: 100
|
|
||||||
CompactNamespaces: false
|
|
||||||
ContinuationIndentWidth: 4
|
|
||||||
Cpp11BracedListStyle: true
|
|
||||||
DeriveLineEnding: true
|
|
||||||
DerivePointerAlignment: false
|
|
||||||
EmptyLineBeforeAccessModifier: Leave
|
|
||||||
FixNamespaceComments: false
|
|
||||||
IncludeBlocks: Regroup
|
|
||||||
IncludeCategories:
|
|
||||||
- Regex: '^<sys/'
|
|
||||||
Priority: 0
|
|
||||||
- Regex: '^<'
|
|
||||||
Priority: 1
|
|
||||||
- Regex: '^"extern/'
|
|
||||||
Priority: 2
|
|
||||||
- Regex: '^"(asm|link|fix|gfx)/'
|
|
||||||
Priority: 3
|
|
||||||
- Regex: '^"'
|
|
||||||
Priority: 2
|
|
||||||
IndentAccessModifiers: false
|
|
||||||
IndentCaseBlocks: false
|
|
||||||
IndentCaseLabels: false
|
|
||||||
IndentExternBlock: NoIndent
|
|
||||||
IndentGotoLabels: false
|
|
||||||
IndentPPDirectives: BeforeHash
|
|
||||||
IndentRequires: true
|
|
||||||
IndentWidth: 4
|
|
||||||
IndentWrappedFunctionNames: true
|
|
||||||
# Only support for Javascript as of clang-format 13...
|
|
||||||
# InsertTrailingCommas: true
|
|
||||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
|
||||||
LambdaBodyIndentation: Signature
|
|
||||||
Language: Cpp
|
|
||||||
MaxEmptyLinesToKeep: 1
|
|
||||||
NamespaceIndentation: None
|
|
||||||
PPIndentWidth: -1
|
|
||||||
PointerAlignment: Right
|
|
||||||
ReflowComments: true
|
|
||||||
SortIncludes: CaseSensitive
|
|
||||||
SortUsingDeclarations: true
|
|
||||||
SpaceAfterCStyleCast: false
|
|
||||||
SpaceAfterLogicalNot: false
|
|
||||||
SpaceAfterTemplateKeyword: false
|
|
||||||
SpaceAroundPointerQualifiers: Both
|
|
||||||
SpaceBeforeAssignmentOperators: true
|
|
||||||
SpaceBeforeCaseColon: false
|
|
||||||
SpaceBeforeCpp11BracedList: false
|
|
||||||
SpaceBeforeCtorInitializerColon: true
|
|
||||||
SpaceBeforeInheritanceColon: true
|
|
||||||
SpaceBeforeParens: ControlStatements
|
|
||||||
SpaceBeforeRangeBasedForLoopColon: true
|
|
||||||
SpaceBeforeSquareBrackets: false
|
|
||||||
SpaceInEmptyBlock: false
|
|
||||||
SpaceInEmptyParentheses: false
|
|
||||||
SpacesBeforeTrailingComments: 1
|
|
||||||
SpacesInAngles: false
|
|
||||||
SpacesInCStyleCastParentheses: false
|
|
||||||
SpacesInConditionalStatement: false
|
|
||||||
SpacesInContainerLiterals: false
|
|
||||||
SpacesInParentheses: false
|
|
||||||
SpacesInSquareBrackets: false
|
|
||||||
Standard: c++17
|
|
||||||
TabWidth: 4
|
|
||||||
UseCRLF: false
|
|
||||||
UseTab: ForIndentation
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
[*]
|
|
||||||
root = true
|
|
||||||
indent_style = tab
|
|
||||||
indent_size = tab
|
|
||||||
tab_width = 8
|
|
||||||
charset = utf-8
|
|
||||||
insert_final_newline = true
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
end_of_line = lf
|
|
||||||
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -1,2 +0,0 @@
|
|||||||
# Shell scripts need Unix line endings (see https://github.com/gbdev/rgbds/issues/841)
|
|
||||||
*.sh text eol=lf
|
|
||||||
3
.github.bak/FUNDING.yml
Normal file
3
.github.bak/FUNDING.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
github: avivace
|
||||||
|
patreon: gbdev01
|
||||||
|
open_collective: gbdev
|
||||||
56
.github.bak/actions/doc_postproc.awk
Executable file
56
.github.bak/actions/doc_postproc.awk
Executable file
@@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/awk -f
|
||||||
|
|
||||||
|
/^\s+<td><b class="Sy">.+<\/b><\/td>$/ {
|
||||||
|
# Assuming that all cells whose contents are bold are heading cells,
|
||||||
|
# use the HTML tag for those
|
||||||
|
sub(/td><b class="Sy"/, "th");
|
||||||
|
sub(/b><\/td/, "th");
|
||||||
|
}
|
||||||
|
|
||||||
|
# The whole page is being generated, so it's not meant to contain any Liquid
|
||||||
|
BEGIN {
|
||||||
|
print "{% raw %}"
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
print "{% endraw %}"
|
||||||
|
}
|
||||||
|
|
||||||
|
BEGIN {
|
||||||
|
in_synopsis = 0
|
||||||
|
}
|
||||||
|
/<table class="Nm">/ {
|
||||||
|
in_synopsis = 1
|
||||||
|
}
|
||||||
|
/<\/table>/ {
|
||||||
|
# Resets synopsis state even when already reset, but whatever
|
||||||
|
in_synopsis = 0
|
||||||
|
}
|
||||||
|
/<code class="Fl">-[a-zA-Z]/ {
|
||||||
|
# Add links to arg descr in synopsis section
|
||||||
|
if (in_synopsis) {
|
||||||
|
while (match($0, /<code class="Fl">-[a-zA-Z]+/)) {
|
||||||
|
# 123456789012345678 -> 18 chars
|
||||||
|
optchars = substr($0, RSTART + 18, RLENGTH - 18)
|
||||||
|
i = length(optchars)
|
||||||
|
while (i) {
|
||||||
|
end = RSTART + 18 + i
|
||||||
|
i -= 1
|
||||||
|
len = i ? 1 : 2
|
||||||
|
$0 = sprintf("%s<a href=\"#%s\">%s</a>%s",
|
||||||
|
substr($0, 0, end - len - 1),
|
||||||
|
substr($0, end - 1, 1),
|
||||||
|
substr($0, end - len, len),
|
||||||
|
substr($0, end))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
# Make long opts (defined using `Fl Fl`) into a single tag
|
||||||
|
gsub(/<code class="Fl">-<\/code>\s*<code class="Fl">/, "<code class=\"Fl\">-")
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
print
|
||||||
|
}
|
||||||
113
.github.bak/actions/get-pages.sh
Executable file
113
.github.bak/actions/get-pages.sh
Executable file
@@ -0,0 +1,113 @@
|
|||||||
|
#!/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 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 and add a new entry to the index
|
||||||
|
(use for releases, not master)
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
is_release=0
|
||||||
|
bad_usage=0
|
||||||
|
while getopts ":hr" opt; do
|
||||||
|
case $opt in
|
||||||
|
r)
|
||||||
|
is_release=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/_documentation/$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 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/_documentation/$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/_documentation/$2/$page"
|
||||||
|
groff -Tpdf -mdoc -wall "${PAGES[$page]##*/}" >"$1/_documentation/$2/$stem.pdf"
|
||||||
|
if [ $is_release -ne 0 ]; then
|
||||||
|
cat - >"$1/_documentation/$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/_documentation/$2/index.html" <<EOF
|
||||||
|
---
|
||||||
|
layout: doc_index
|
||||||
|
permalink: /docs/$2/
|
||||||
|
title: RGBDS online manual [$2]
|
||||||
|
description: RGBDS $2 - Online manual
|
||||||
|
---
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
# If making a release, add a new entry right after `master`
|
||||||
|
if [ $is_release -ne 0 ]; then
|
||||||
|
awk '{ print }
|
||||||
|
/"name": "master"/ { print "\t\t{\"name\": \"'$2'\", \"text\": \"'$2'\" }," }
|
||||||
|
' "$1/_data/doc.json" >"$1/_data/doc.json.tmp"
|
||||||
|
mv "$1/_data/doc.json"{.tmp,}
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
rm "${PAGES[@]##*/}"
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
#!/bin/bash
|
case `echo $1 | cut -d '-' -f 1` in
|
||||||
set -e
|
|
||||||
|
|
||||||
case "${1%-*}" in
|
|
||||||
ubuntu)
|
ubuntu)
|
||||||
sudo apt-get -qq update
|
sudo apt-get -qq update
|
||||||
sudo apt-get install -yq bison libpng-dev pkg-config
|
sudo apt-get install -yq bison libpng-dev pkg-config
|
||||||
@@ -6,7 +6,6 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: github.repository_owner == 'gbdev'
|
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-18.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout rgbds@release
|
- name: Checkout rgbds@release
|
||||||
@@ -23,16 +22,16 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
sudo apt-get -qq update
|
sudo apt-get -qq update
|
||||||
sudo apt-get install -yq groff zlib1g-dev
|
sudo apt-get install -yq groff zlib1g-dev
|
||||||
wget 'http://mandoc.bsd.lv/snapshots/mandoc-1.14.6.tar.gz'
|
wget 'http://mandoc.bsd.lv/snapshots/mandoc-1.14.5.tar.gz'
|
||||||
tar xf mandoc-1.14.6.tar.gz
|
tar xf mandoc-1.14.5.tar.gz
|
||||||
cd mandoc-1.14.6
|
cd mandoc-1.14.5
|
||||||
./configure
|
./configure
|
||||||
make
|
make
|
||||||
sudo make install
|
sudo make install
|
||||||
- name: Update pages
|
- name: Update pages
|
||||||
working-directory: rgbds/man
|
working-directory: rgbds
|
||||||
run: | # The ref appears to be in the format "refs/tags/<version>", so strip that
|
run: | # The ref appears to be in the format "refs/tags/<version>", so strip that
|
||||||
../../rgbds-www/.github/actions/get-pages.sh ${GITHUB_REF##*/} *
|
./.github/actions/get-pages.sh -r ../rgbds-www ${GITHUB_REF##*/}
|
||||||
- name: Push new pages
|
- name: Push new pages
|
||||||
working-directory: rgbds-www
|
working-directory: rgbds-www
|
||||||
run: |
|
run: |
|
||||||
@@ -7,20 +7,18 @@ jobs:
|
|||||||
unix-testing:
|
unix-testing:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-20.04, ubuntu-18.04, macos-11.0, macos-10.15]
|
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04, macos-10.15]
|
||||||
cc: [gcc, clang]
|
cc: [gcc, clang]
|
||||||
buildsys: [make, cmake]
|
buildsys: [make, cmake]
|
||||||
exclude:
|
|
||||||
# `gcc` is just an alias to `clang` on macOS, don't bother
|
|
||||||
- os: macos-10.15
|
|
||||||
cc: gcc
|
|
||||||
- os: macos-11.0
|
|
||||||
cc: gcc
|
|
||||||
include:
|
include:
|
||||||
- cc: gcc
|
- os: ubuntu-18.04
|
||||||
cxx: g++
|
cc: gcc
|
||||||
- cc: clang
|
target: develop
|
||||||
cxx: clang++
|
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
|
fail-fast: false
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
@@ -33,25 +31,25 @@ jobs:
|
|||||||
# Apple's base version is severely outdated, not even supporting -Wall,
|
# Apple's base version is severely outdated, not even supporting -Wall,
|
||||||
# but it overrides Homebrew's version nonetheless...
|
# but it overrides Homebrew's version nonetheless...
|
||||||
- name: Build & install using Make
|
- name: Build & install using Make
|
||||||
if: matrix.buildsys == 'make'
|
|
||||||
run: |
|
run: |
|
||||||
export PATH="/usr/local/opt/bison/bin:$PATH"
|
export PATH="/usr/local/opt/bison/bin:$PATH"
|
||||||
make develop -j Q= CC=${{ matrix.cc }} CXX=${{ matrix.cxx }}
|
make ${{ matrix.target }} -j Q= CC=${{ matrix.cc }}
|
||||||
sudo make install -j Q=
|
sudo make install -j Q=
|
||||||
|
if: matrix.buildsys == 'make'
|
||||||
- name: Build & install using CMake
|
- name: Build & install using CMake
|
||||||
if: matrix.buildsys == 'cmake'
|
|
||||||
run: |
|
run: |
|
||||||
export PATH="/usr/local/opt/bison/bin:$PATH"
|
export PATH="/usr/local/opt/bison/bin:$PATH"
|
||||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=${{ matrix.cc }} -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} -DSANITIZERS=ON -DMORE_WARNINGS=ON
|
cmake -S . -B build -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=${{ matrix.cc }} ${{ matrix.cmakevars }}
|
||||||
cmake --build build -j --verbose
|
cmake --build build
|
||||||
cp build/src/rgb{asm,link,fix,gfx} .
|
cp build/src/rgb{asm,link,fix,gfx} .
|
||||||
sudo cmake --install build --verbose
|
sudo cmake --install build
|
||||||
|
if: matrix.buildsys == 'cmake'
|
||||||
- name: Package binaries
|
- name: Package binaries
|
||||||
run: |
|
run: |
|
||||||
mkdir bins
|
mkdir bins
|
||||||
cp rgb{asm,link,fix,gfx} bins
|
cp rgb{asm,link,fix,gfx} bins
|
||||||
- name: Upload binaries
|
- name: Upload binaries
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v1
|
||||||
with:
|
with:
|
||||||
name: rgbds-canary-${{ matrix.os }}-${{ matrix.cc }}-${{ matrix.buildsys }}
|
name: rgbds-canary-${{ matrix.os }}-${{ matrix.cc }}-${{ matrix.buildsys }}
|
||||||
path: bins
|
path: bins
|
||||||
@@ -76,11 +74,11 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Get zlib, libpng and bison
|
- name: Get zlib, libpng and bison
|
||||||
run: | # TODO: use an array; remember to update the versions being downloaded, *and* the paths being extracted! (`Move-Item`)
|
run: | # TODO: use an array
|
||||||
$wc = New-Object System.Net.WebClient
|
$wc = New-Object System.Net.WebClient
|
||||||
$wc.DownloadFile('https://www.zlib.net/zlib1212.zip', 'zlib.zip')
|
$wc.DownloadFile('https://www.zlib.net/zlib1211.zip', 'zlib.zip')
|
||||||
$hash = (Get-FileHash "zlib.zip" -Algorithm SHA256).Hash
|
$hash = (Get-FileHash "zlib.zip" -Algorithm SHA256).Hash
|
||||||
if ($hash -ne '173e89893dcb8b4a150d7731cd72f0602f1d6b45e60e2a54efdf7f3fc3325fd7') {
|
if ($hash -ne 'd7510a8ee1918b7d0cad197a089c0a2cd4d6df05fee22389f67f115e738b178d') {
|
||||||
Write-Host "zlib SHA256 mismatch! ($hash)"
|
Write-Host "zlib SHA256 mismatch! ($hash)"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
@@ -90,51 +88,38 @@ jobs:
|
|||||||
Write-Host "libpng SHA256 mismatch! ($hash)"
|
Write-Host "libpng SHA256 mismatch! ($hash)"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
$wc.DownloadFile('https://github.com/lexxmark/winflexbison/releases/download/v2.5.24/win_flex_bison-2.5.24.zip', 'winflexbison.zip')
|
$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
|
$hash = (Get-FileHash "winflexbison.zip" -Algorithm SHA256).Hash
|
||||||
if ($hash -ne '39c6086ce211d5415500acc5ed2d8939861ca1696aee48909c7f6daf5122b505') {
|
if ($hash -ne '6AA5C8EA662DA1550020A5804C28BE63FFAA53486DA9F6842E24C379EC422DFC') {
|
||||||
Write-Host "bison SHA256 mismatch! ($hash)"
|
Write-Host "bison SHA256 mismatch! ($hash)"
|
||||||
}
|
}
|
||||||
Expand-Archive -DestinationPath . "zlib.zip"
|
Expand-Archive -DestinationPath . "zlib.zip"
|
||||||
Expand-Archive -DestinationPath . "libpng.zip"
|
Expand-Archive -DestinationPath . "libpng.zip"
|
||||||
Expand-Archive -DestinationPath install_dir "winflexbison.zip"
|
Expand-Archive -DestinationPath install_dir "winflexbison.zip"
|
||||||
Move-Item zlib-1.2.12 zlib
|
Move-Item zlib-1.2.11 zlib
|
||||||
Move-Item lpng1637 libpng
|
Move-Item lpng1637 libpng
|
||||||
- uses: actions/cache@v3
|
|
||||||
id: cache
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
zbuild
|
|
||||||
pngbuild
|
|
||||||
key: ${{ matrix.arch }}-${{ hashFiles('zlib/**', 'libpng/**') }}
|
|
||||||
- name: Build zlib
|
- name: Build zlib
|
||||||
run: | # BUILD_SHARED_LIBS causes the output DLL to be correctly called `zlib1.dll`
|
run: | # BUILD_SHARED_LIBS causes the output DLL to be correctly called `zlib1.dll`
|
||||||
cmake -S zlib -B zbuild -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DBUILD_SHARED_LIBS=ON
|
cmake -S zlib -B zbuild -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DBUILD_SHARED_LIBS=ON
|
||||||
cmake --build zbuild --config Release -j
|
cmake --build zbuild --config Release
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
|
||||||
- name: Install zlib
|
|
||||||
run: |
|
|
||||||
cmake --install zbuild
|
cmake --install zbuild
|
||||||
- name: Build libpng
|
- name: Build libpng
|
||||||
run: |
|
run: |
|
||||||
cmake -S libpng -B pngbuild -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DPNG_SHARED=ON -DPNG_STATIC=ON -DPNG_TESTS=OFF
|
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 -j
|
cmake --build pngbuild --config Release
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
|
||||||
- name: Install libpng
|
|
||||||
run: |
|
|
||||||
cmake --install pngbuild
|
cmake --install pngbuild
|
||||||
- name: Build Windows binaries
|
- name: Build Windows binaries
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B build -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DCMAKE_BUILD_TYPE=Release
|
cmake -S . -B build -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DCMAKE_BUILD_TYPE=Release
|
||||||
cmake --build build --config Release -j --verbose
|
cmake --build build --config Release
|
||||||
cmake --install build --verbose --prefix install_dir
|
cmake --install build
|
||||||
- name: Package binaries
|
- name: Package binaries
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
mkdir bins
|
mkdir bins
|
||||||
cp install_dir/bin/{rgbasm.exe,rgblink.exe,rgbfix.exe,rgbgfx.exe,zlib1.dll,libpng16.dll} bins
|
cp install_dir/bin/{rgbasm.exe,rgblink.exe,rgbfix.exe,rgbgfx.exe,zlib1.dll,libpng16.dll} bins
|
||||||
- name: Upload Windows binaries
|
- name: Upload Windows binaries
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v1
|
||||||
with:
|
with:
|
||||||
name: rgbds-canary-win${{ matrix.bits }}
|
name: rgbds-canary-win${{ matrix.bits }}
|
||||||
path: bins
|
path: bins
|
||||||
@@ -142,7 +127,6 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
cp bins/* .
|
cp bins/* .
|
||||||
cp bins/*.dll test/gfx
|
|
||||||
test/run-tests.sh
|
test/run-tests.sh
|
||||||
|
|
||||||
windows-xbuild:
|
windows-xbuild:
|
||||||
@@ -169,7 +153,7 @@ jobs:
|
|||||||
./.github/actions/install_deps.sh ${{ matrix.os }}
|
./.github/actions/install_deps.sh ${{ matrix.os }}
|
||||||
- name: Install MinGW
|
- name: Install MinGW
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get install {gcc,g++}-mingw-w64-${{ matrix.arch }} mingw-w64-tools libz-mingw-w64-dev
|
sudo apt-get install gcc-mingw-w64-${{ matrix.arch }} mingw-w64-tools libz-mingw-w64-dev
|
||||||
- name: Install libpng dev headers for MinGW
|
- name: Install libpng dev headers for MinGW
|
||||||
run: |
|
run: |
|
||||||
sudo ./.github/actions/mingw-w64-libpng-dev.sh ${{ matrix.triplet }}
|
sudo ./.github/actions/mingw-w64-libpng-dev.sh ${{ matrix.triplet }}
|
||||||
@@ -185,21 +169,12 @@ jobs:
|
|||||||
mv rgbgfx bins/rgbgfx.exe
|
mv rgbgfx bins/rgbgfx.exe
|
||||||
cp /usr/${{ matrix.triplet }}/lib/zlib1.dll bins
|
cp /usr/${{ matrix.triplet }}/lib/zlib1.dll bins
|
||||||
cp /usr/${{ matrix.triplet }}/bin/libpng16-16.dll bins
|
cp /usr/${{ matrix.triplet }}/bin/libpng16-16.dll bins
|
||||||
if [ ${{ matrix.bits }} -eq 32 ]; then cp /usr/lib/gcc/${{ matrix.triplet }}/7.3-win32/lib{gcc_s_sjlj-1,stdc++-6}.dll bins; fi
|
if [ ${{ matrix.bits }} -eq 32 ]; then cp /usr/lib/gcc/${{ matrix.triplet }}/7.3-win32/libgcc_s_sjlj-1.dll bins; fi
|
||||||
mv test/gfx/randtilegen{,.exe}
|
|
||||||
mv test/gfx/rgbgfx_test{,.exe}
|
|
||||||
- name: Upload Windows binaries
|
- name: Upload Windows binaries
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v1
|
||||||
with:
|
with:
|
||||||
name: rgbds-canary-mingw-win${{ matrix.bits }}
|
name: rgbds-canary-mingw-win${{ matrix.bits }}
|
||||||
path: bins
|
path: bins
|
||||||
- name: Upload Windows test binaries
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: testing-programs-mingw-win${{ matrix.bits }}
|
|
||||||
path: |
|
|
||||||
test/gfx/randtilegen.exe
|
|
||||||
test/gfx/rgbgfx_test.exe
|
|
||||||
|
|
||||||
windows-xtesting:
|
windows-xtesting:
|
||||||
needs: windows-xbuild
|
needs: windows-xbuild
|
||||||
@@ -211,20 +186,14 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Retrieve binaries
|
- name: Retrieve binaries
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v1
|
||||||
with:
|
with:
|
||||||
name: rgbds-canary-mingw-win${{ matrix.bits }}
|
name: rgbds-canary-mingw-win${{ matrix.bits }}
|
||||||
path: bins
|
path: bins
|
||||||
- name: Retrieve test binaries
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: testing-programs-mingw-win${{ matrix.bits }}
|
|
||||||
path: test/gfx
|
|
||||||
- name: Extract binaries
|
- name: Extract binaries
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
cp bins/* .
|
cp bins/* .
|
||||||
cp bins/*.dll test/gfx
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@@ -4,19 +4,19 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
paths:
|
paths:
|
||||||
- man/gbz80.7
|
- .github/actions/get-pages.sh
|
||||||
- man/rgbds.5
|
- src/gbz80.7
|
||||||
- man/rgbds.7
|
- src/rgbds.5
|
||||||
- man/rgbasm.1
|
- src/rgbds.7
|
||||||
- man/rgbasm.5
|
- src/asm/rgbasm.1
|
||||||
- man/rgblink.1
|
- src/asm/rgbasm.5
|
||||||
- man/rgblink.5
|
- src/link/rgblink.1
|
||||||
- man/rgbfix.1
|
- src/link/rgblink.5
|
||||||
- man/rgbgfx.1
|
- src/fix/rgbfix.1
|
||||||
|
- src/gfx/rgbgfx.1
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: github.repository_owner == 'gbdev'
|
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-18.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout rgbds@master
|
- name: Checkout rgbds@master
|
||||||
@@ -35,16 +35,16 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
sudo apt-get -qq update
|
sudo apt-get -qq update
|
||||||
sudo apt-get install -yq groff zlib1g-dev
|
sudo apt-get install -yq groff zlib1g-dev
|
||||||
wget 'http://mandoc.bsd.lv/snapshots/mandoc-1.14.6.tar.gz'
|
wget 'http://mandoc.bsd.lv/snapshots/mandoc-1.14.5.tar.gz'
|
||||||
tar xf mandoc-1.14.6.tar.gz
|
tar xf mandoc-1.14.5.tar.gz
|
||||||
cd mandoc-1.14.6
|
cd mandoc-1.14.5
|
||||||
./configure
|
./configure
|
||||||
make
|
make
|
||||||
sudo make install
|
sudo make install
|
||||||
- name: Update pages
|
- name: Update pages
|
||||||
working-directory: rgbds/man
|
working-directory: rgbds
|
||||||
run: |
|
run: |
|
||||||
../../rgbds-www/maintainer/man_to_html.sh master *
|
./.github/actions/get-pages.sh ../rgbds-www master
|
||||||
- name: Push new pages
|
- name: Push new pages
|
||||||
working-directory: rgbds-www
|
working-directory: rgbds-www
|
||||||
run: |
|
run: |
|
||||||
@@ -55,7 +55,7 @@ jobs:
|
|||||||
ssh-add ~/.ssh/id_ed25519
|
ssh-add ~/.ssh/id_ed25519
|
||||||
git config --global user.name "GitHub Action"
|
git config --global user.name "GitHub Action"
|
||||||
git config --global user.email "community@gbdev.io"
|
git config --global user.email "community@gbdev.io"
|
||||||
git add -A
|
git add .
|
||||||
git commit -m "Update RGBDS master documentation"
|
git commit -m "Update RGBDS master documentation"
|
||||||
if git remote | grep -q origin; then
|
if git remote | grep -q origin; then
|
||||||
git remote set-url origin git@github.com:gbdev/rgbds-www.git
|
git remote set-url origin git@github.com:gbdev/rgbds-www.git
|
||||||
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@@ -1 +0,0 @@
|
|||||||
open_collective: gbdev
|
|
||||||
17
.github/workflows/checkdiff.yml
vendored
17
.github/workflows/checkdiff.yml
vendored
@@ -1,17 +0,0 @@
|
|||||||
name: "Code coverage checking"
|
|
||||||
on: pull_request
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
checkdiff:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Set up repo
|
|
||||||
run: |
|
|
||||||
git clone -b "${{ github.event.pull_request.head.ref }}" "${{ github.event.pull_request.head.repo.clone_url }}" rgbds
|
|
||||||
cd rgbds
|
|
||||||
git remote add upstream "${{ github.event.pull_request.base.repo.clone_url }}"
|
|
||||||
git fetch upstream
|
|
||||||
- name: Checkdiff
|
|
||||||
working-directory: rgbds
|
|
||||||
run: |
|
|
||||||
make checkdiff "BASE_REF=${{ github.event.pull_request.base.sha }}" Q= | tee log
|
|
||||||
10
.github/workflows/create-release-artifacts.yaml
vendored
10
.github/workflows/create-release-artifacts.yaml
vendored
@@ -17,9 +17,9 @@ jobs:
|
|||||||
- name: Get zlib, libpng and bison
|
- name: Get zlib, libpng and bison
|
||||||
run: | # TODO: use an array
|
run: | # TODO: use an array
|
||||||
$wc = New-Object System.Net.WebClient
|
$wc = New-Object System.Net.WebClient
|
||||||
$wc.DownloadFile('https://www.zlib.net/zlib1212.zip', 'zlib.zip')
|
$wc.DownloadFile('https://www.zlib.net/zlib1211.zip', 'zlib.zip')
|
||||||
$hash = (Get-FileHash "zlib.zip" -Algorithm SHA256).Hash
|
$hash = (Get-FileHash "zlib.zip" -Algorithm SHA256).Hash
|
||||||
if ($hash -ne '173e89893dcb8b4a150d7731cd72f0602f1d6b45e60e2a54efdf7f3fc3325fd7') {
|
if ($hash -ne 'd7510a8ee1918b7d0cad197a089c0a2cd4d6df05fee22389f67f115e738b178d') {
|
||||||
Write-Host "zlib SHA256 mismatch! ($hash)"
|
Write-Host "zlib SHA256 mismatch! ($hash)"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
@@ -29,15 +29,15 @@ jobs:
|
|||||||
Write-Host "libpng SHA256 mismatch! ($hash)"
|
Write-Host "libpng SHA256 mismatch! ($hash)"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
$wc.DownloadFile('https://github.com/lexxmark/winflexbison/releases/download/v2.5.24/win_flex_bison-2.5.24.zip', 'winflexbison.zip')
|
$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
|
$hash = (Get-FileHash "winflexbison.zip" -Algorithm SHA256).Hash
|
||||||
if ($hash -ne '39c6086ce211d5415500acc5ed2d8939861ca1696aee48909c7f6daf5122b505') {
|
if ($hash -ne '6AA5C8EA662DA1550020A5804C28BE63FFAA53486DA9F6842E24C379EC422DFC') {
|
||||||
Write-Host "bison SHA256 mismatch! ($hash)"
|
Write-Host "bison SHA256 mismatch! ($hash)"
|
||||||
}
|
}
|
||||||
Expand-Archive -DestinationPath . "zlib.zip"
|
Expand-Archive -DestinationPath . "zlib.zip"
|
||||||
Expand-Archive -DestinationPath . "libpng.zip"
|
Expand-Archive -DestinationPath . "libpng.zip"
|
||||||
Expand-Archive -DestinationPath install_dir "winflexbison.zip"
|
Expand-Archive -DestinationPath install_dir "winflexbison.zip"
|
||||||
Move-Item zlib-1.2.12 zlib
|
Move-Item zlib-1.2.11 zlib
|
||||||
Move-Item lpng1637 libpng
|
Move-Item lpng1637 libpng
|
||||||
- name: Build 32-bit zlib
|
- name: Build 32-bit zlib
|
||||||
run: | # BUILD_SHARED_LIBS causes the output DLL to be correctly called `zlib1.dll`
|
run: | # BUILD_SHARED_LIBS causes the output DLL to be correctly called `zlib1.dll`
|
||||||
|
|||||||
16
.gitignore
vendored
16
.gitignore
vendored
@@ -1,12 +1,16 @@
|
|||||||
/rgbasm
|
rgbasm
|
||||||
/rgblink
|
rgblink
|
||||||
/rgbfix
|
rgbfix
|
||||||
/rgbgfx
|
rgbgfx
|
||||||
/rgbshim.sh
|
rgbshim.sh
|
||||||
*.o
|
*.o
|
||||||
*.exe
|
*.exe
|
||||||
*.dll
|
*.dll
|
||||||
.checkpatch-camelcase.*
|
.checkpatch-camelcase.*
|
||||||
CMakeCache.txt
|
CMakeCache.txt
|
||||||
CMakeFiles/
|
CMakeFiles
|
||||||
cmake_install.cmake
|
cmake_install.cmake
|
||||||
|
|
||||||
|
test/pokecrystal
|
||||||
|
test/pokered
|
||||||
|
test/ucity
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
|
||||||
|
|
||||||
project(rgbds
|
project(rgbds
|
||||||
LANGUAGES C CXX)
|
LANGUAGES C)
|
||||||
|
|
||||||
# get real path of source and binary directories
|
# get real path of source and binary directories
|
||||||
get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH)
|
get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH)
|
||||||
@@ -25,85 +25,54 @@ endif()
|
|||||||
|
|
||||||
option(SANITIZERS "Build with sanitizers enabled" OFF) # Ignored on MSVC
|
option(SANITIZERS "Build with sanitizers enabled" OFF) # Ignored on MSVC
|
||||||
option(MORE_WARNINGS "Turn on more warnings" OFF) # Ignored on MSVC
|
option(MORE_WARNINGS "Turn on more warnings" OFF) # Ignored on MSVC
|
||||||
|
option(TRACE_PARSER "Trace parser execution" OFF)
|
||||||
|
option(TRACE_LEXER "Trace lexer execution" OFF)
|
||||||
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
# MSVC's standard library triggers warning C5105,
|
# MSVC's standard library triggers warning C5105,
|
||||||
# "macro expansion producing 'defined' has undefined behavior"
|
# "macro expansion producing 'defined' has undefined behavior"
|
||||||
add_compile_options(/MP /wd5105)
|
add_compile_options(/std:c11 /W1 /MP /wd5105)
|
||||||
add_definitions(/D_CRT_SECURE_NO_WARNINGS)
|
add_definitions(/D_CRT_SECURE_NO_WARNINGS)
|
||||||
# Also, CMake appears not to pass the C11-enabling flag, so we must add it manually... but only for C!
|
|
||||||
if(NOT CMAKE_C_FLAGS MATCHES "std:c11") # The flag may already have been injected by an earlier CMake invocation, so don't add it twice
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std:c11" CACHE STRING "Flags used by the C compiler during all build types." FORCE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(SANITIZERS)
|
|
||||||
set(SAN_FLAGS /fsanitize=address)
|
|
||||||
add_compile_options(${SAN_FLAGS})
|
|
||||||
add_link_options(${SAN_FLAGS})
|
|
||||||
endif()
|
|
||||||
else()
|
else()
|
||||||
add_compile_options(-Wall -pedantic)
|
add_compile_options(-Wall -pedantic)
|
||||||
add_definitions(-D_POSIX_C_SOURCE=200809L -D_ISOC11_SOURCE)
|
|
||||||
if(SANITIZERS)
|
if(SANITIZERS)
|
||||||
set(SAN_FLAGS -fsanitize=shift -fsanitize=integer-divide-by-zero
|
set(SAN_FLAGS -fsanitize=shift -fsanitize=integer-divide-by-zero
|
||||||
-fsanitize=unreachable -fsanitize=vla-bound
|
-fsanitize=unreachable -fsanitize=vla-bound
|
||||||
-fsanitize=signed-integer-overflow -fsanitize=bounds
|
-fsanitize=signed-integer-overflow -fsanitize=bounds
|
||||||
-fsanitize=object-size -fsanitize=bool -fsanitize=enum
|
-fsanitize=object-size -fsanitize=bool -fsanitize=enum
|
||||||
-fsanitize=alignment -fsanitize=null -fsanitize=address)
|
-fsanitize=alignment -fsanitize=null)
|
||||||
add_compile_options(${SAN_FLAGS})
|
add_compile_options(${SAN_FLAGS})
|
||||||
add_link_options(${SAN_FLAGS})
|
link_libraries(${SAN_FLAGS})
|
||||||
# A non-zero optimization level is desired in debug mode, but allow overriding it nonetheless
|
|
||||||
# TODO: this overrides anything previously set... that's a bit sloppy!
|
|
||||||
set(CMAKE_C_FLAGS_DEBUG "-g -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls" CACHE STRING "" FORCE)
|
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "-g -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls" CACHE STRING "" FORCE)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(MORE_WARNINGS)
|
if(MORE_WARNINGS)
|
||||||
add_compile_options(-Werror -Wextra
|
add_compile_options(-Werror -Wextra -Wno-type-limits
|
||||||
-Walloc-zero -Wcast-align -Wcast-qual -Wduplicated-branches -Wduplicated-cond
|
-Wno-sign-compare -Wvla -Wformat -Wformat-security -Wformat-overflow=2
|
||||||
-Wfloat-equal -Wlogical-op -Wnull-dereference -Wshift-overflow=2
|
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused
|
||||||
-Wstringop-overflow=4 -Wstrict-overflow=5 -Wundef -Wuninitialized -Wunused
|
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5
|
||||||
-Wshadow # TODO: -Wshadow=compatible-local ?
|
-Wstringop-overflow=4 -Walloc-zero -Wduplicated-cond
|
||||||
-Wformat=2 -Wformat-overflow=2 -Wformat-truncation=1
|
-Wfloat-equal -Wshadow -Wcast-qual -Wcast-align -Wlogical-op
|
||||||
-Wno-format-nonliteral # We have a couple of "dynamic" prints
|
-Wnested-externs -Wno-aggressive-loop-optimizations -Winline
|
||||||
# We do some range checks that are always false on some platforms (GCC, Clang)
|
-Wundef -Wstrict-prototypes -Wold-style-definition)
|
||||||
-Wno-type-limits -Wno-tautological-constant-out-of-range-compare
|
|
||||||
-Wvla # MSVC does not support VLAs
|
|
||||||
-Wno-unknown-warning-option) # Clang shouldn't diagnose unknown warnings
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Use versioning consistent with Makefile
|
# Use versioning consistent with Makefile
|
||||||
# the git revision is used but uses the fallback in an archive
|
# the git revision is used but uses the fallback in an archive
|
||||||
|
|
||||||
find_program(GIT git)
|
execute_process(COMMAND git describe --tags --dirty --always
|
||||||
if(GIT)
|
OUTPUT_VARIABLE GIT_REV
|
||||||
execute_process(COMMAND ${GIT} describe --tags --dirty --always
|
ERROR_QUIET)
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
string(STRIP "${GIT_REV}" GIT_REV)
|
||||||
OUTPUT_VARIABLE GIT_REV OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
||||||
ERROR_QUIET)
|
|
||||||
message(STATUS "RGBDS version: ${GIT_REV}")
|
|
||||||
else(GIT)
|
|
||||||
message(STATUS "Cannot determine RGBDS version (Git not installed), falling back")
|
|
||||||
endif(GIT)
|
|
||||||
|
|
||||||
find_package(PkgConfig)
|
|
||||||
if(MSVC OR NOT PKG_CONFIG_FOUND)
|
|
||||||
# fallback to find_package
|
|
||||||
find_package(PNG REQUIRED)
|
|
||||||
else()
|
|
||||||
pkg_check_modules(LIBPNG REQUIRED libpng)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
include_directories("${PROJECT_SOURCE_DIR}/include")
|
include_directories("${PROJECT_SOURCE_DIR}/include")
|
||||||
|
|
||||||
|
add_definitions(-DBUILD_VERSION_STRING="${GIT_REV}")
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
set(CMAKE_C_STANDARD_REQUIRED True)
|
set(CMAKE_C_STANDARD_REQUIRED True)
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
add_subdirectory(test)
|
|
||||||
|
|
||||||
# By default, build in Release mode; Debug mode must be explicitly requested
|
# By default, build in Release mode; Debug mode must be explicitly requested
|
||||||
# (You may want to augment it with the options above)
|
# (You may want to augment it with the options above)
|
||||||
@@ -119,18 +88,10 @@ if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(MANDIR "share/man")
|
if(TRACE_PARSER)
|
||||||
set(man1 "man/rgbasm.1"
|
target_compile_definitions(rgbasm PRIVATE -DYYDEBUG)
|
||||||
"man/rgbfix.1"
|
endif()
|
||||||
"man/rgbgfx.1"
|
|
||||||
"man/rgblink.1")
|
|
||||||
set(man5 "man/rgbasm.5"
|
|
||||||
"man/rgblink.5"
|
|
||||||
"man/rgbds.5")
|
|
||||||
set(man7 "man/gbz80.7"
|
|
||||||
"man/rgbds.7")
|
|
||||||
|
|
||||||
foreach(SECTION "man1" "man5" "man7")
|
if(TRACE_LEXER)
|
||||||
set(DEST "${MANDIR}/${SECTION}")
|
target_compile_definitions(rgbasm PRIVATE -DLEXER_DEBUG)
|
||||||
install(FILES ${${SECTION}} DESTINATION ${DEST})
|
endif()
|
||||||
endforeach()
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
Contributing
|
Contributing
|
||||||
============
|
============
|
||||||
|
|
||||||
RGBDS was created in the late '90s and has received contributions from several
|
RGBDS was created in the late 90's and has received contributions from several
|
||||||
developers since then. It wouldn't have been possible to get to this point
|
developers since then. It wouldn't have been possible to get to this point
|
||||||
without their work, and it is always open to the contributions of other people.
|
without their work and, for that reason, it is always open to the contributions
|
||||||
|
of other people.
|
||||||
|
|
||||||
Reporting Bugs
|
Reporting Bugs
|
||||||
--------------
|
--------------
|
||||||
|
|||||||
@@ -19,10 +19,6 @@ Main contributors
|
|||||||
|
|
||||||
- Antonio Niño Díaz <antonio_nd@outlook.com>
|
- Antonio Niño Díaz <antonio_nd@outlook.com>
|
||||||
|
|
||||||
- Eldred "ISSOtm" Habert <eldredhabert0@gmail.com>
|
|
||||||
|
|
||||||
- Rangi <http://github.com/Rangi42>
|
|
||||||
|
|
||||||
Other contributors
|
Other contributors
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
@@ -34,9 +30,7 @@ Other contributors
|
|||||||
|
|
||||||
- David Brotz <dbrotz007@gmail.com>
|
- David Brotz <dbrotz007@gmail.com>
|
||||||
|
|
||||||
- Jakub Kądziołka <kuba@kadziolka.net>
|
- Eldred Habert <eldredhabert0@gmail.com>
|
||||||
|
|
||||||
- James "JL2210" Larrowe <https://github.com/JL2210>
|
|
||||||
|
|
||||||
- The Musl C library <http://www.musl-libc.org>
|
- The Musl C library <http://www.musl-libc.org>
|
||||||
|
|
||||||
@@ -46,6 +40,8 @@ Other contributors
|
|||||||
|
|
||||||
- Quint Guvernator <quint@guvernator.net>
|
- Quint Guvernator <quint@guvernator.net>
|
||||||
|
|
||||||
|
- Rangi <http://github.com/Rangi42>
|
||||||
|
|
||||||
- Sanqui <gsanky@gmail.com>
|
- Sanqui <gsanky@gmail.com>
|
||||||
|
|
||||||
- YamaArashi <shadow962@live.com>
|
- YamaArashi <shadow962@live.com>
|
||||||
|
|||||||
112
Makefile
112
Makefile
@@ -7,7 +7,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
.SUFFIXES:
|
.SUFFIXES:
|
||||||
.SUFFIXES: .h .y .c .cpp .o
|
.SUFFIXES: .h .y .c .o
|
||||||
|
|
||||||
# User-defined variables
|
# User-defined variables
|
||||||
|
|
||||||
@@ -30,17 +30,13 @@ PNGLDLIBS := `${PKG_CONFIG} --libs-only-l libpng`
|
|||||||
# Note: if this comes up empty, `version.c` will automatically fall back to last release number
|
# 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`
|
VERSION_STRING := `git describe --tags --dirty --always 2>/dev/null`
|
||||||
|
|
||||||
WARNFLAGS := -Wall -pedantic
|
WARNFLAGS := -Wall
|
||||||
|
|
||||||
# Overridable CFLAGS
|
# Overridable CFLAGS
|
||||||
CFLAGS ?= -O3 -flto -DNDEBUG
|
CFLAGS ?= -O3 -flto -DNDEBUG
|
||||||
CXXFLAGS ?= -O3 -flto -DNDEBUG
|
|
||||||
# Non-overridable CFLAGS
|
# Non-overridable CFLAGS
|
||||||
# _ISOC11_SOURCE is required on certain platforms to get C11 on top of the C99-based POSIX 2008
|
REALCFLAGS := ${CFLAGS} ${WARNFLAGS} -std=gnu11 -D_POSIX_C_SOURCE=200809L \
|
||||||
REALCFLAGS := ${CFLAGS} ${WARNFLAGS} -std=gnu11 -I include \
|
-Iinclude
|
||||||
-D_POSIX_C_SOURCE=200809L -D_ISOC11_SOURCE
|
|
||||||
REALCXXFLAGS := ${CXXFLAGS} ${WARNFLAGS} -std=c++17 -I include \
|
|
||||||
-D_POSIX_C_SOURCE=200809L -fno-exceptions -fno-rtti
|
|
||||||
# Overridable LDFLAGS
|
# Overridable LDFLAGS
|
||||||
LDFLAGS ?=
|
LDFLAGS ?=
|
||||||
# Non-overridable LDFLAGS
|
# Non-overridable LDFLAGS
|
||||||
@@ -52,9 +48,6 @@ YFLAGS ?= -Wall
|
|||||||
BISON := bison
|
BISON := bison
|
||||||
RM := rm -rf
|
RM := rm -rf
|
||||||
|
|
||||||
# Used for checking pull requests
|
|
||||||
BASE_REF := origin/master
|
|
||||||
|
|
||||||
# Rules to build the RGBDS binaries
|
# Rules to build the RGBDS binaries
|
||||||
|
|
||||||
all: rgbasm rgblink rgbfix rgbgfx
|
all: rgbasm rgblink rgbfix rgbgfx
|
||||||
@@ -67,17 +60,17 @@ rgbasm_obj := \
|
|||||||
src/asm/lexer.o \
|
src/asm/lexer.o \
|
||||||
src/asm/macro.o \
|
src/asm/macro.o \
|
||||||
src/asm/main.o \
|
src/asm/main.o \
|
||||||
|
src/asm/parser.o \
|
||||||
src/asm/opt.o \
|
src/asm/opt.o \
|
||||||
src/asm/output.o \
|
src/asm/output.o \
|
||||||
src/asm/parser.o \
|
|
||||||
src/asm/rpn.o \
|
src/asm/rpn.o \
|
||||||
src/asm/section.o \
|
src/asm/section.o \
|
||||||
src/asm/symbol.o \
|
src/asm/symbol.o \
|
||||||
src/asm/util.o \
|
src/asm/util.o \
|
||||||
src/asm/warning.o \
|
src/asm/warning.o \
|
||||||
|
src/extern/err.o \
|
||||||
src/extern/getopt.o \
|
src/extern/getopt.o \
|
||||||
src/extern/utf8decoder.o \
|
src/extern/utf8decoder.o \
|
||||||
src/error.o \
|
|
||||||
src/hashmap.o \
|
src/hashmap.o \
|
||||||
src/linkdefs.o \
|
src/linkdefs.o \
|
||||||
src/opmath.o
|
src/opmath.o
|
||||||
@@ -91,31 +84,25 @@ rgblink_obj := \
|
|||||||
src/link/output.o \
|
src/link/output.o \
|
||||||
src/link/patch.o \
|
src/link/patch.o \
|
||||||
src/link/script.o \
|
src/link/script.o \
|
||||||
src/link/sdas_obj.o \
|
|
||||||
src/link/section.o \
|
src/link/section.o \
|
||||||
src/link/symbol.o \
|
src/link/symbol.o \
|
||||||
|
src/extern/err.o \
|
||||||
src/extern/getopt.o \
|
src/extern/getopt.o \
|
||||||
src/error.o \
|
|
||||||
src/hashmap.o \
|
src/hashmap.o \
|
||||||
src/linkdefs.o \
|
src/linkdefs.o \
|
||||||
src/opmath.o
|
src/opmath.o
|
||||||
|
|
||||||
rgbfix_obj := \
|
rgbfix_obj := \
|
||||||
src/fix/main.o \
|
src/fix/main.o \
|
||||||
src/extern/getopt.o \
|
src/extern/err.o \
|
||||||
src/error.o
|
src/extern/getopt.o
|
||||||
|
|
||||||
rgbgfx_obj := \
|
rgbgfx_obj := \
|
||||||
|
src/gfx/gb.o \
|
||||||
src/gfx/main.o \
|
src/gfx/main.o \
|
||||||
src/gfx/pal_packing.o \
|
src/gfx/makepng.o \
|
||||||
src/gfx/pal_sorting.o \
|
src/extern/err.o \
|
||||||
src/gfx/pal_spec.o \
|
src/extern/getopt.o
|
||||||
src/gfx/process.o \
|
|
||||||
src/gfx/proto_palette.o \
|
|
||||||
src/gfx/reverse.o \
|
|
||||||
src/gfx/rgba.o \
|
|
||||||
src/extern/getopt.o \
|
|
||||||
src/error.o
|
|
||||||
|
|
||||||
rgbasm: ${rgbasm_obj}
|
rgbasm: ${rgbasm_obj}
|
||||||
$Q${CC} ${REALLDFLAGS} -o $@ ${rgbasm_obj} ${REALCFLAGS} src/version.c -lm
|
$Q${CC} ${REALLDFLAGS} -o $@ ${rgbasm_obj} ${REALCFLAGS} src/version.c -lm
|
||||||
@@ -127,13 +114,7 @@ rgbfix: ${rgbfix_obj}
|
|||||||
$Q${CC} ${REALLDFLAGS} -o $@ ${rgbfix_obj} ${REALCFLAGS} src/version.c
|
$Q${CC} ${REALLDFLAGS} -o $@ ${rgbfix_obj} ${REALCFLAGS} src/version.c
|
||||||
|
|
||||||
rgbgfx: ${rgbgfx_obj}
|
rgbgfx: ${rgbgfx_obj}
|
||||||
$Q${CXX} ${REALLDFLAGS} ${PNGLDFLAGS} -o $@ ${rgbgfx_obj} ${REALCXXFLAGS} -x c++ src/version.c ${PNGLDLIBS}
|
$Q${CC} ${REALLDFLAGS} ${PNGLDFLAGS} -o $@ ${rgbgfx_obj} ${REALCFLAGS} src/version.c ${PNGLDLIBS}
|
||||||
|
|
||||||
test/gfx/randtilegen: test/gfx/randtilegen.c
|
|
||||||
$Q${CC} ${REALLDFLAGS} ${PNGLDFLAGS} -o $@ $^ ${REALCFLAGS} ${PNGCFLAGS} ${PNGLDLIBS}
|
|
||||||
|
|
||||||
test/gfx/rgbgfx_test: test/gfx/rgbgfx_test.cpp
|
|
||||||
$Q${CXX} ${REALLDFLAGS} ${PNGLDFLAGS} -o $@ $^ ${REALCXXFLAGS} ${PNGLDLIBS}
|
|
||||||
|
|
||||||
# Rules to process files
|
# Rules to process files
|
||||||
|
|
||||||
@@ -160,10 +141,7 @@ src/asm/parser.c: src/asm/parser.y
|
|||||||
${BISON} $$DEFS -d ${YFLAGS} -o $@ $<
|
${BISON} $$DEFS -d ${YFLAGS} -o $@ $<
|
||||||
|
|
||||||
.c.o:
|
.c.o:
|
||||||
$Q${CC} ${REALCFLAGS} -c -o $@ $<
|
$Q${CC} ${REALCFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
||||||
|
|
||||||
.cpp.o:
|
|
||||||
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
|
||||||
|
|
||||||
# Target used to remove all files generated by other Makefile targets
|
# Target used to remove all files generated by other Makefile targets
|
||||||
|
|
||||||
@@ -175,7 +153,6 @@ clean:
|
|||||||
$Qfind src/ -name "*.o" -exec rm {} \;
|
$Qfind src/ -name "*.o" -exec rm {} \;
|
||||||
$Q${RM} rgbshim.sh
|
$Q${RM} rgbshim.sh
|
||||||
$Q${RM} src/asm/parser.c src/asm/parser.h
|
$Q${RM} src/asm/parser.c src/asm/parser.h
|
||||||
$Q${RM} test/gfx/randtilegen test/gfx/rgbgfx_test
|
|
||||||
|
|
||||||
# Target used to install the binaries and man pages.
|
# Target used to install the binaries and man pages.
|
||||||
|
|
||||||
@@ -186,15 +163,15 @@ install: all
|
|||||||
$Qinstall ${STRIP} -m ${BINMODE} rgblink ${DESTDIR}${bindir}/rgblink
|
$Qinstall ${STRIP} -m ${BINMODE} rgblink ${DESTDIR}${bindir}/rgblink
|
||||||
$Qinstall ${STRIP} -m ${BINMODE} rgbgfx ${DESTDIR}${bindir}/rgbgfx
|
$Qinstall ${STRIP} -m ${BINMODE} rgbgfx ${DESTDIR}${bindir}/rgbgfx
|
||||||
$Qmkdir -p ${DESTDIR}${mandir}/man1 ${DESTDIR}${mandir}/man5 ${DESTDIR}${mandir}/man7
|
$Qmkdir -p ${DESTDIR}${mandir}/man1 ${DESTDIR}${mandir}/man5 ${DESTDIR}${mandir}/man7
|
||||||
$Qinstall -m ${MANMODE} man/rgbds.7 ${DESTDIR}${mandir}/man7/rgbds.7
|
$Qinstall -m ${MANMODE} src/rgbds.7 ${DESTDIR}${mandir}/man7/rgbds.7
|
||||||
$Qinstall -m ${MANMODE} man/gbz80.7 ${DESTDIR}${mandir}/man7/gbz80.7
|
$Qinstall -m ${MANMODE} src/gbz80.7 ${DESTDIR}${mandir}/man7/gbz80.7
|
||||||
$Qinstall -m ${MANMODE} man/rgbds.5 ${DESTDIR}${mandir}/man5/rgbds.5
|
$Qinstall -m ${MANMODE} src/rgbds.5 ${DESTDIR}${mandir}/man5/rgbds.5
|
||||||
$Qinstall -m ${MANMODE} man/rgbasm.1 ${DESTDIR}${mandir}/man1/rgbasm.1
|
$Qinstall -m ${MANMODE} src/asm/rgbasm.1 ${DESTDIR}${mandir}/man1/rgbasm.1
|
||||||
$Qinstall -m ${MANMODE} man/rgbasm.5 ${DESTDIR}${mandir}/man5/rgbasm.5
|
$Qinstall -m ${MANMODE} src/asm/rgbasm.5 ${DESTDIR}${mandir}/man5/rgbasm.5
|
||||||
$Qinstall -m ${MANMODE} man/rgbfix.1 ${DESTDIR}${mandir}/man1/rgbfix.1
|
$Qinstall -m ${MANMODE} src/fix/rgbfix.1 ${DESTDIR}${mandir}/man1/rgbfix.1
|
||||||
$Qinstall -m ${MANMODE} man/rgblink.1 ${DESTDIR}${mandir}/man1/rgblink.1
|
$Qinstall -m ${MANMODE} src/link/rgblink.1 ${DESTDIR}${mandir}/man1/rgblink.1
|
||||||
$Qinstall -m ${MANMODE} man/rgblink.5 ${DESTDIR}${mandir}/man5/rgblink.5
|
$Qinstall -m ${MANMODE} src/link/rgblink.5 ${DESTDIR}${mandir}/man5/rgblink.5
|
||||||
$Qinstall -m ${MANMODE} man/rgbgfx.1 ${DESTDIR}${mandir}/man1/rgbgfx.1
|
$Qinstall -m ${MANMODE} src/gfx/rgbgfx.1 ${DESTDIR}${mandir}/man1/rgbgfx.1
|
||||||
|
|
||||||
# Target used to check the coding style of the whole codebase.
|
# Target used to check the coding style of the whole codebase.
|
||||||
# `extern/` is excluded, as it contains external code that should not be patched
|
# `extern/` is excluded, as it contains external code that should not be patched
|
||||||
@@ -211,8 +188,9 @@ checkcodebase:
|
|||||||
# the first common commit between the HEAD and origin/master.
|
# the first common commit between the HEAD and origin/master.
|
||||||
# `.y` files aren't checked, unfortunately...
|
# `.y` files aren't checked, unfortunately...
|
||||||
|
|
||||||
|
BASE_REF:= origin/master
|
||||||
checkpatch:
|
checkpatch:
|
||||||
$QCOMMON_COMMIT=`git merge-base HEAD ${BASE_REF}`; \
|
$Qeval COMMON_COMMIT=$$(git merge-base HEAD ${BASE_REF}); \
|
||||||
for commit in `git rev-list $$COMMON_COMMIT..HEAD`; do \
|
for commit in `git rev-list $$COMMON_COMMIT..HEAD`; do \
|
||||||
echo "[*] Analyzing commit '$$commit'"; \
|
echo "[*] Analyzing commit '$$commit'"; \
|
||||||
git format-patch --stdout "$$commit~..$$commit" \
|
git format-patch --stdout "$$commit~..$$commit" \
|
||||||
@@ -220,34 +198,24 @@ checkpatch:
|
|||||||
| ${CHECKPATCH} - || true; \
|
| ${CHECKPATCH} - || true; \
|
||||||
done
|
done
|
||||||
|
|
||||||
# Target used to check for suspiciously missing changed files.
|
|
||||||
|
|
||||||
checkdiff:
|
|
||||||
$Qcontrib/checkdiff.bash `git merge-base HEAD ${BASE_REF}`
|
|
||||||
|
|
||||||
# This target is used during development in order to prevent adding new issues
|
# 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
|
# to the source code. All warnings are treated as errors in order to block the
|
||||||
# compilation and make the continous integration infrastructure return failure.
|
# compilation and make the continous integration infrastructure return failure.
|
||||||
# The rationale for some of the flags is documented in the CMakeLists.
|
|
||||||
|
|
||||||
develop:
|
develop:
|
||||||
$Qenv ${MAKE} WARNFLAGS="-Werror -Wextra \
|
$Qenv $(MAKE) -j WARNFLAGS="-Werror -Wall -Wextra -Wpedantic -Wno-type-limits \
|
||||||
-Walloc-zero -Wcast-align -Wcast-qual -Wduplicated-branches -Wduplicated-cond \
|
-Wno-sign-compare -Wvla -Wformat -Wformat-security -Wformat-overflow=2 \
|
||||||
-Wfloat-equal -Wlogical-op -Wnull-dereference -Wshift-overflow=2 \
|
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused \
|
||||||
-Wstringop-overflow=4 -Wstrict-overflow=5 -Wundef -Wuninitialized -Wunused \
|
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=4 \
|
||||||
-Wshadow \
|
-Wstringop-overflow=4 -Walloc-zero -Wduplicated-cond \
|
||||||
-Wformat=2 -Wformat-overflow=2 -Wformat-truncation=1 \
|
-Wfloat-equal -Wshadow -Wcast-qual -Wcast-align -Wlogical-op \
|
||||||
-Wno-format-nonliteral \
|
-Wnested-externs -Wno-aggressive-loop-optimizations -Winline \
|
||||||
-Wno-type-limits -Wno-tautological-constant-out-of-range-compare \
|
-Wundef -Wstrict-prototypes -Wold-style-definition \
|
||||||
-Wvla \
|
|
||||||
-Wno-unknown-warning-option \
|
|
||||||
-fsanitize=shift -fsanitize=integer-divide-by-zero \
|
-fsanitize=shift -fsanitize=integer-divide-by-zero \
|
||||||
-fsanitize=unreachable -fsanitize=vla-bound \
|
-fsanitize=unreachable -fsanitize=vla-bound \
|
||||||
-fsanitize=signed-integer-overflow -fsanitize=bounds \
|
-fsanitize=signed-integer-overflow -fsanitize=bounds \
|
||||||
-fsanitize=object-size -fsanitize=bool -fsanitize=enum \
|
-fsanitize=object-size -fsanitize=bool -fsanitize=enum \
|
||||||
-fsanitize=alignment -fsanitize=null -fsanitize=address" \
|
-fsanitize=alignment -fsanitize=null" CFLAGS="-ggdb3 -O0"
|
||||||
CFLAGS="-ggdb3 -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls" \
|
|
||||||
CXXFLAGS="-ggdb3 -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls"
|
|
||||||
|
|
||||||
# Targets for the project maintainer to easily create Windows exes.
|
# Targets for the project maintainer to easily create Windows exes.
|
||||||
# This is not for Windows users!
|
# This is not for Windows users!
|
||||||
@@ -255,14 +223,12 @@ develop:
|
|||||||
# install instructions instead.
|
# install instructions instead.
|
||||||
|
|
||||||
mingw32:
|
mingw32:
|
||||||
$Q${MAKE} all test/gfx/randtilegen test/gfx/rgbgfx_test \
|
$Qmake CC=i686-w64-mingw32-gcc BISON=bison \
|
||||||
CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ \
|
PKG_CONFIG=i686-w64-mingw32-pkg-config -j
|
||||||
BISON=bison PKG_CONFIG=i686-w64-mingw32-pkg-config -j
|
|
||||||
|
|
||||||
mingw64:
|
mingw64:
|
||||||
$Q${MAKE} all test/gfx/randtilegen test/gfx/rgbgfx_test \
|
$Qmake CC=x86_64-w64-mingw32-gcc BISON=bison \
|
||||||
CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ \
|
PKG_CONFIG=x86_64-w64-mingw32-pkg-config -j
|
||||||
BISON=bison PKG_CONFIG=x86_64-w64-mingw32-pkg-config -j
|
|
||||||
|
|
||||||
wine-shim:
|
wine-shim:
|
||||||
$Qecho '#!/bin/bash' > rgbshim.sh
|
$Qecho '#!/bin/bash' > rgbshim.sh
|
||||||
|
|||||||
78
README.rst
78
README.rst
@@ -12,18 +12,17 @@ for the Game Boy and Game Boy Color. It consists of:
|
|||||||
This is a fork of the original RGBDS which aims to make the programs more like
|
This is a fork of the original RGBDS which aims to make the programs more like
|
||||||
other UNIX tools.
|
other UNIX tools.
|
||||||
|
|
||||||
This toolchain is maintained `on GitHub <https://github.com/gbdev/rgbds>`__.
|
This toolchain is maintained on `GitHub <https://github.com/rednex/rgbds>`__.
|
||||||
|
|
||||||
The documentation of this toolchain can be viewed online `here <https://rgbds.gbdev.io/docs/>`__, it is generated from the man pages found in this repository.
|
The documentation of this toolchain can be viewed online
|
||||||
The source code of the website itself is on GitHub as well under the repo `rgbds-www <https://github.com/gbdev/rgbds-www>`__.
|
`here <https://rgbds.gbdev.io/docs/>`__, it is generated from the man pages
|
||||||
|
found in this repository.
|
||||||
If you want to contribute or maintain RGBDS, and have questions regarding the code, its organisation, etc. you can find me `on GBDev <https://gbdev.io/chat>`__ or via mail at ``rgbds at eldred dot fr``.
|
|
||||||
|
|
||||||
1. Installing RGBDS
|
1. Installing RGBDS
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
The `installation procedure <https://rgbds.gbdev.io/install>`__ is available
|
The `installation procedure <https://rgbds.gbdev.io/install>`__ is available
|
||||||
online for various platforms. `Building from source <https://rgbds.gbdev.io/install/#building-from-source>`__
|
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.
|
is possible using ``make`` or ``cmake``; follow the link for more detailed instructions.
|
||||||
|
|
||||||
.. code:: sh
|
.. code:: sh
|
||||||
@@ -46,68 +45,53 @@ The RGBDS source code file structure somewhat resembles the following:
|
|||||||
|
|
||||||
.
|
.
|
||||||
├── .github/
|
├── .github/
|
||||||
│ ├── actions/
|
│ ├── actions/
|
||||||
│ │ └── ...
|
│ │ └── ...
|
||||||
│ └── workflows/
|
│ └── workflows/
|
||||||
│ └── ...
|
│ └── ...
|
||||||
├── contrib/
|
├── contrib/
|
||||||
│ ├── zsh_compl/
|
│ ├── zsh_compl/
|
||||||
│ │ └── ...
|
│ │ └── ...
|
||||||
│ └── ...
|
│ └── ...
|
||||||
├── include/
|
├── include/
|
||||||
│ └── ...
|
│ └── ...
|
||||||
├── man/
|
|
||||||
│ └── ...
|
|
||||||
├── src/
|
├── src/
|
||||||
│ ├── asm/
|
│ ├── asm/
|
||||||
│ │ └── ...
|
│ │ └── ...
|
||||||
│ ├── extern/
|
│ ├── extern/
|
||||||
│ │ └── ...
|
│ │ └── ...
|
||||||
│ ├── fix/
|
│ ├── fix/
|
||||||
│ │ └── ...
|
│ │ └── ...
|
||||||
│ ├── gfx/
|
│ ├── gfx/
|
||||||
│ │ └── ...
|
│ │ └── ...
|
||||||
│ ├── link/
|
│ ├── link/
|
||||||
│ │ └── ...
|
│ │ └── ...
|
||||||
│ ├── CMakeLists.txt
|
│ ├── CMakeLists.txt
|
||||||
│ └── ...
|
│ └── ...
|
||||||
├── test/
|
├── test/
|
||||||
│ ├── ...
|
│ ├── ...
|
||||||
│ └── run-tests.sh
|
│ └── run-tests.sh
|
||||||
├── .clang-format
|
|
||||||
├── CMakeLists.txt
|
├── CMakeLists.txt
|
||||||
├── Makefile
|
├── Makefile
|
||||||
└── README.rst
|
└── README.rst
|
||||||
|
|
||||||
.. |clang-format| replace:: ``clang-format``
|
|
||||||
.. _clang-format: https://clang.llvm.org/docs/ClangFormat.html
|
|
||||||
|
|
||||||
- ``.github/`` - files and scripts related to the integration of the RGBDS codebase with
|
- ``.github/`` - files and scripts related to the integration of the RGBDS codebase with
|
||||||
GitHub.
|
GitHub.
|
||||||
|
|
||||||
* ``actions/`` - scripts used by workflow files.
|
|
||||||
* ``workflows/`` - CI workflow description files.
|
|
||||||
|
|
||||||
- ``contrib/`` - scripts and other resources which may be useful to users and developers of
|
- ``contrib/`` - scripts and other resources which may be useful to users and developers of
|
||||||
RGBDS.
|
RGBDS.
|
||||||
|
|
||||||
* ``zsh_compl`` contains tab completion scripts for use with zsh. Put them somewhere in your ``fpath``, and they should auto-load.
|
* ``zsh_compl`` contains tab completion scripts for use with zsh. Put them somewhere in your ``fpath``, and they should auto-load.
|
||||||
|
|
||||||
* ``bash_compl`` contains tab completion scripts for use with bash. Run them with ``source`` somewhere in your ``.bashrc``, and they should load every time you open a shell.
|
|
||||||
|
|
||||||
- ``include/`` - header files for each respective C files in `src`.
|
- ``include/`` - header files for each respective C files in `src`.
|
||||||
|
|
||||||
- ``man/`` - manual pages.
|
- ``src/`` - source code and manual pages for RGBDS.
|
||||||
|
|
||||||
- ``src/`` - source code of RGBDS.
|
|
||||||
|
|
||||||
* Note that the code unique to each RGBDS tool is stored in its respective subdirectory
|
* 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.
|
(rgbasm -> ``src/asm/``, for example). ``src/extern/`` contains code imported from external sources.
|
||||||
|
|
||||||
- ``test/`` - testing framework used to verify that changes to the code don't break or modify the behavior of RGBDS.
|
- ``test/`` - testing framework used to verify that changes to the code don't break or modify the behavior of RGBDS.
|
||||||
|
|
||||||
- ``.clang-format`` - code style for automated formatting with |clang-format|_. The C code does not currently follow this style, but all C++ code should.
|
|
||||||
|
|
||||||
3. History
|
3. History
|
||||||
----------
|
----------
|
||||||
|
|
||||||
@@ -129,11 +113,3 @@ The RGBDS source code file structure somewhat resembles the following:
|
|||||||
- 2018, codebase relicensed under the MIT license.
|
- 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.
|
- 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.
|
||||||
|
|
||||||
4. Acknowledgements
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
RGBGFX generates palettes using algorithms found in the paper
|
|
||||||
`"Algorithms for the Pagination Problem, a Bin Packing with Overlapping Items" <http://arxiv.org/abs/1605.00558>`__
|
|
||||||
(`GitHub <https://github.com/pagination-problem/pagination>`__, MIT license),
|
|
||||||
by Aristide Grange, Imed Kacem, and Sébastien Martin.
|
|
||||||
|
|||||||
67
RELEASE.rst
67
RELEASE.rst
@@ -1,67 +0,0 @@
|
|||||||
Releasing
|
|
||||||
=========
|
|
||||||
|
|
||||||
This describes for the maintainers of RGBDS how to publish a new release on
|
|
||||||
GitHub.
|
|
||||||
|
|
||||||
1. Update, commit, and push `include/version.h <include/version.h>`__ with
|
|
||||||
values for ``PACKAGE_VERSION_MAJOR``, ``PACKAGE_VERSION_MINOR``,
|
|
||||||
``PACKAGE_VERSION_PATCH``, and ``PACKAGE_VERSION_RC``. Only define
|
|
||||||
``PACKAGE_VERSION_RC`` if you are publishing a release candidate! You can
|
|
||||||
use ``git commit -m "Release <version>"`` and ``git push origin master``.
|
|
||||||
|
|
||||||
2. Create a Git tag formatted as ``v<MAJOR>.<MINOR>.<PATCH>``, or
|
|
||||||
``v<MAJOR>.<MINOR>.<PATCH>-rc<RC>`` for a release candidate. ``MAJOR``,
|
|
||||||
``MINOR``, ``PATCH``, and ``RC`` should match their values from
|
|
||||||
`include/version.h <include/version.h>`__. You can use ``git tag <tag>``.
|
|
||||||
|
|
||||||
3. Push the tag to GitHub. You can use ``git push origin <tag>``.
|
|
||||||
|
|
||||||
GitHub Actions will run the `create-release-artifacts.yaml
|
|
||||||
<.github/workflows/create-release-artifacts.yaml>`__ workflow to detect the
|
|
||||||
tag starting with "``v[0-9]``" and automatically do the following:
|
|
||||||
|
|
||||||
1. Build 32-bit and 64-bit RGBDS binaries for Windows with ``cmake``.
|
|
||||||
|
|
||||||
2. Package the binaries into zip files.
|
|
||||||
|
|
||||||
3. Package the source code into a tar.gz file with ``make dist``.
|
|
||||||
|
|
||||||
4. Create a draft GitHub release for the tag, attaching the three
|
|
||||||
packaged files. It will be a prerelease if the tag contains "``-rc``".
|
|
||||||
|
|
||||||
If an error occurred in the above steps, delete the tag and restart the
|
|
||||||
procedure. You can use ``git push --delete origin <tag>`` and
|
|
||||||
``git tag --delete <tag>``.
|
|
||||||
|
|
||||||
4. GitHub Actions will run the `create-release-docs.yml
|
|
||||||
<.github/workflows/create-release-docs.yml>`__ workflow to add the release
|
|
||||||
documentation to `rgbds-www <https://github.com/gbdev/rgbds-www>`__.
|
|
||||||
|
|
||||||
For a release candidate, which creates a prerelease, you will have to
|
|
||||||
take these steps yourself.
|
|
||||||
|
|
||||||
1. Clone `rgbds-www <https://github.com/gbdev/rgbds-www>`__. You can use
|
|
||||||
``git clone https://github.com/gbdev/rgbds-www.git``.
|
|
||||||
|
|
||||||
2. Make sure that you have installed ``groff`` and ``mandoc``. You will
|
|
||||||
need ``mandoc`` 1.14.5 or later to support ``-O toc``.
|
|
||||||
|
|
||||||
3. Run ``.github/actions/get-pages.sh -r <path/to/rgbds-www> <tag>``. This
|
|
||||||
will render the RGBDS documentation as HTML and PDF and copy it to
|
|
||||||
``rgbds-www``.
|
|
||||||
|
|
||||||
If you do not have ``groff`` installed, you can change
|
|
||||||
``groff -Tpdf -mdoc -wall`` to ``mandoc -Tpdf -I os=Linux`` in
|
|
||||||
`.github/actions/get-pages.sh <.github/actions/get-pages.sh>`__ and it
|
|
||||||
will suffice.
|
|
||||||
|
|
||||||
4. Commit and push the documentation. You can use ``git commit -m
|
|
||||||
"Create RGBDS <tag> documentation"`` and ``git push origin master``
|
|
||||||
(within the ``rgbds-www`` directory, not RGBDS).
|
|
||||||
|
|
||||||
5. Write a changelog in the GitHub draft release.
|
|
||||||
|
|
||||||
6. Click the "Publish release" button to publish it!
|
|
||||||
|
|
||||||
7. Update the `release` branch. You can use ``git push origin release``.
|
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Known bugs:
|
|
||||||
# - Newlines in file/directory names break this script
|
|
||||||
# This is because we rely on `compgen -A`, which is broken like this.
|
|
||||||
# A fix would require implementing it ourselves, and no thanks!
|
|
||||||
# - `rgbasm --binary-digits=a` is treated the same as `rgbasm --binary-digits=` (for example)
|
|
||||||
# This is not our fault, Bash passes both of these identically.
|
|
||||||
# Maybe it could be worked around, but such a fix would likely be involved.
|
|
||||||
# The user can work around it by typing `--binary-digits ''` instead, for example.
|
|
||||||
# - Directories are not completed as such in "coalesced" short-opt arguments. For example,
|
|
||||||
# `rgbasm -M d<tab>` can autocomplete to `rgbasm -M dir/` (no space), but
|
|
||||||
# `rgbasm -Md<tab>` would autocomplete to `rgbasm -Mdir ` (trailing space) instead.
|
|
||||||
# This is because directory handling is performed by Readline, whom we can't tell about the short
|
|
||||||
# opt kerfuffle. The user can work around by separating the argument, as shown above.
|
|
||||||
# (Also, there might be more possible bugs if `-Mdir` is actually a directory. Ugh.)
|
|
||||||
|
|
||||||
# Something to note:
|
|
||||||
# `rgbasm --binary-digits=a` gets passed to us as ('rgbasm' '--binary-digits' '=' 'a')
|
|
||||||
# Thus, we don't need to do much to handle that form of argument passing: skip '=' after long opts.
|
|
||||||
|
|
||||||
_rgbasm_completions() {
|
|
||||||
# Format: "long_opt:state_after"
|
|
||||||
# Empty long opt = it doesn't exit
|
|
||||||
# See the `state` variable below for info about `state_after`
|
|
||||||
declare -A opts=(
|
|
||||||
[V]="version:normal"
|
|
||||||
[E]="export-all:normal"
|
|
||||||
[H]="nop-after-halt:normal"
|
|
||||||
[h]="halt-without-nop:normal"
|
|
||||||
[L]="preserve-ld:normal"
|
|
||||||
[l]="auto-ldh:normal"
|
|
||||||
[v]="verbose:normal"
|
|
||||||
[w]=":normal"
|
|
||||||
[b]="binary-digits:unk"
|
|
||||||
[D]="define:unk"
|
|
||||||
[g]="gfx-chars:unk"
|
|
||||||
[i]="include:dir"
|
|
||||||
[M]="dependfile:glob-*.mk *.d"
|
|
||||||
[o]="output:glob-*.o"
|
|
||||||
[p]="pad-value:unk"
|
|
||||||
[Q]="q-precision:unk"
|
|
||||||
[r]="recursion-depth:unk"
|
|
||||||
[W]="warning:warning"
|
|
||||||
)
|
|
||||||
# Parse command-line up to current word
|
|
||||||
local opt_ena=true
|
|
||||||
# Possible states:
|
|
||||||
# - normal = Well, normal. Options are parsed normally.
|
|
||||||
# - unk = An argument that can't be completed, and should just be skipped.
|
|
||||||
# - warning = A warning flag.
|
|
||||||
# - dir = A directory path
|
|
||||||
# - glob-* = A glob, after the dash is a whitespace-separated list of file globs to use
|
|
||||||
local state=normal
|
|
||||||
# The length of the option, used as a return value by the function below
|
|
||||||
local optlen=0
|
|
||||||
# $1: a short option word
|
|
||||||
# `state` will be set to the parsing state after the last option character in the word. If
|
|
||||||
# "normal" is not returned, `optlen` will be set to the length (dash included) of the "option"
|
|
||||||
# part of the argument.
|
|
||||||
parse_short_opt() {
|
|
||||||
# These options act like a long option (= takes up the entire word), but only use a single dash
|
|
||||||
# So, they need some special handling
|
|
||||||
if [[ "$1" = "-M"[GP] ]]; then
|
|
||||||
state=normal
|
|
||||||
optlen=${#1}
|
|
||||||
return;
|
|
||||||
elif [[ "$1" = "-M"[QT] ]]; then
|
|
||||||
state='glob-*.d *.mk *.o'
|
|
||||||
optlen=${#1}
|
|
||||||
return;
|
|
||||||
fi
|
|
||||||
|
|
||||||
for (( i = 1; i < "${#1}"; i++ )); do
|
|
||||||
# If the option is not known, assume it doesn't take an argument
|
|
||||||
local opt="${opts["${1:$i:1}"]:-":normal"}"
|
|
||||||
state="${opt#*:}"
|
|
||||||
# If the option takes an argument, record the length and exit
|
|
||||||
if [[ "$state" != 'normal' ]]; then
|
|
||||||
let optlen="$i + 1"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
optlen=0
|
|
||||||
}
|
|
||||||
|
|
||||||
for (( i = 1; i < COMP_CWORD; i++ )); do
|
|
||||||
local word="${COMP_WORDS[$i]}"
|
|
||||||
|
|
||||||
# If currently processing an argument, skip this word
|
|
||||||
if [[ "$state" != 'normal' ]]; then
|
|
||||||
state=normal
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$word" = '--' ]]; then
|
|
||||||
# Options stop being parsed after this
|
|
||||||
opt_ena=false
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if it's a long option
|
|
||||||
if [[ "$word" = '--'* ]]; then
|
|
||||||
# If the option is unknown, assume it takes no arguments: keep the state at "normal"
|
|
||||||
for long_opt in "${opts[@]}"; do
|
|
||||||
if [[ "$word" = "--${long_opt%%:*}" ]]; then
|
|
||||||
state="${long_opt#*:}"
|
|
||||||
# Check if the next word is just '='; if so, skip it, the argument must follow
|
|
||||||
# (See "known bugs" at the top of this script)
|
|
||||||
let i++
|
|
||||||
if [[ "${COMP_WORDS[$i]}" != '=' ]]; then
|
|
||||||
let i--
|
|
||||||
fi
|
|
||||||
optlen=0
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
# Check if it's a short option
|
|
||||||
elif [[ "$word" = '-'* ]]; then
|
|
||||||
parse_short_opt "$word"
|
|
||||||
# The last option takes an argument...
|
|
||||||
if [[ "$state" != 'normal' ]]; then
|
|
||||||
if [[ "$optlen" -ne "${#word}" ]]; then
|
|
||||||
# If it's contained within the word, we won't complete it, revert to "normal"
|
|
||||||
state=normal
|
|
||||||
else
|
|
||||||
# Otherwise, complete it, but start at the beginning of *that* word
|
|
||||||
optlen=0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Parse current word
|
|
||||||
# Careful that it might look like an option, so use `--` aggressively!
|
|
||||||
local cur_word="${COMP_WORDS[$i]}"
|
|
||||||
|
|
||||||
# Process options, as short ones may change the state
|
|
||||||
if $opt_ena && [[ "$state" = 'normal' && "$cur_word" = '-'* ]]; then
|
|
||||||
# We might want to complete to an option or an arg to that option
|
|
||||||
# Parse the option word to check
|
|
||||||
# There's no whitespace in the option names, so we can ride a little dirty...
|
|
||||||
|
|
||||||
# Is this a long option?
|
|
||||||
if [[ "$cur_word" = '--'* ]]; then
|
|
||||||
# It is, try to complete one
|
|
||||||
mapfile -t COMPREPLY < <(compgen -W "${opts[*]%%:*}" -P '--' -- "${cur_word#--}")
|
|
||||||
return 0
|
|
||||||
elif [[ "$cur_word" = '-M'[GPQT] ]]; then
|
|
||||||
# These options act like long opts with no arguments, so return them and exactly them
|
|
||||||
COMPREPLY=( "$cur_word" )
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
# Short options may be grouped, parse them to determine what to complete
|
|
||||||
parse_short_opt "$cur_word"
|
|
||||||
|
|
||||||
if [[ "$state" = 'normal' ]]; then
|
|
||||||
mapfile -t COMPREPLY < <(compgen -W "${!opts[*]}" -P "$cur_word" ''; compgen -W '-MG -MP -MQ -MT' "$cur_word")
|
|
||||||
return 0
|
|
||||||
elif [[ "$optlen" = "${#cur_word}" && "$state" != "warning" ]]; then
|
|
||||||
# This short option group only awaits its argument!
|
|
||||||
# Post the option group as-is as a reply so that Readline inserts a space,
|
|
||||||
# so that the next completion request switches to the argument
|
|
||||||
# An exception is made for warnings, since it's idiomatic to stick them to the
|
|
||||||
# `-W`, and it doesn't break anything.
|
|
||||||
COMPREPLY=( "$cur_word" )
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
COMPREPLY=()
|
|
||||||
case "$state" in
|
|
||||||
unk) # Return with no replies: no idea what to complete!
|
|
||||||
;;
|
|
||||||
warning)
|
|
||||||
mapfile -t COMPREPLY < <(compgen -W "
|
|
||||||
assert
|
|
||||||
backwards-for
|
|
||||||
builtin-args
|
|
||||||
charmap-redef
|
|
||||||
div
|
|
||||||
empty-data-directive
|
|
||||||
empty-macro-arg
|
|
||||||
empty-strrpl
|
|
||||||
large-constant
|
|
||||||
long-string
|
|
||||||
macro-shift
|
|
||||||
nested-comment
|
|
||||||
numeric-string
|
|
||||||
obsolete
|
|
||||||
shift
|
|
||||||
shift-amount
|
|
||||||
truncation
|
|
||||||
unmapped-char
|
|
||||||
user
|
|
||||||
all
|
|
||||||
extra
|
|
||||||
everything
|
|
||||||
error" -P "${cur_word:0:$optlen}" -- "${cur_word:$optlen}")
|
|
||||||
;;
|
|
||||||
normal) # Acts like a glob...
|
|
||||||
state="glob-*.asm *.inc *.sm83"
|
|
||||||
;&
|
|
||||||
glob-*)
|
|
||||||
while read -r word; do
|
|
||||||
COMPREPLY+=("${cur_word:0:$optlen}$word")
|
|
||||||
done < <(for glob in ${state#glob-}; do compgen -A file -X \!"$glob" -- "${cur_word:$optlen}"; done)
|
|
||||||
# Also complete directories
|
|
||||||
;&
|
|
||||||
dir)
|
|
||||||
while read -r word; do
|
|
||||||
COMPREPLY+=("${cur_word:0:$optlen}$word")
|
|
||||||
done < <(compgen -A directory -- "${cur_word:$optlen}")
|
|
||||||
compopt -o filenames
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo >&2 "Internal completion error: invalid state \"$state\", please report this bug"
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
complete -F _rgbasm_completions rgbasm
|
|
||||||
@@ -1,185 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Same notes as RGBASM
|
|
||||||
|
|
||||||
_rgbfix_completions() {
|
|
||||||
# Format: "long_opt:state_after"
|
|
||||||
# Empty long opt = it doesn't exit
|
|
||||||
# See the `state` variable below for info about `state_after`
|
|
||||||
declare -A opts=(
|
|
||||||
[V]="version:normal"
|
|
||||||
[j]="non-japanese:normal"
|
|
||||||
[s]="sgb-compatible:normal"
|
|
||||||
[v]="validate:normal"
|
|
||||||
[C]="color-only:normal"
|
|
||||||
[c]="color-compatible:normal"
|
|
||||||
[f]="fix-spec:fix-spec"
|
|
||||||
[i]="game-id:unk"
|
|
||||||
[k]="new-licensee:unk"
|
|
||||||
[l]="old-licensee:unk"
|
|
||||||
[m]="mbc-type:mbc"
|
|
||||||
[n]="rom-version:unk"
|
|
||||||
[p]="pad-value:unk"
|
|
||||||
[r]="ram-size:unk"
|
|
||||||
[t]="title:unk"
|
|
||||||
)
|
|
||||||
# Parse command-line up to current word
|
|
||||||
local opt_ena=true
|
|
||||||
# Possible states:
|
|
||||||
# - normal = Well, normal. Options are parsed normally.
|
|
||||||
# - unk = An argument that can't be completed, and should just be skipped.
|
|
||||||
# - warning = A warning flag.
|
|
||||||
# - dir = A directory path
|
|
||||||
# - glob-* = A glob, after the dash is a whitespace-separated list of file globs to use
|
|
||||||
local state=normal
|
|
||||||
# The length of the option, used as a return value by the function below
|
|
||||||
local optlen=0
|
|
||||||
# $1: a short option word
|
|
||||||
# `state` will be set to the parsing state after the last option character in the word. If
|
|
||||||
# "normal" is not returned, `optlen` will be set to the length (dash included) of the "option"
|
|
||||||
# part of the argument.
|
|
||||||
parse_short_opt() {
|
|
||||||
for (( i = 1; i < "${#1}"; i++ )); do
|
|
||||||
# If the option is not known, assume it doesn't take an argument
|
|
||||||
local opt="${opts["${1:$i:1}"]:-":normal"}"
|
|
||||||
state="${opt#*:}"
|
|
||||||
# If the option takes an argument, record the length and exit
|
|
||||||
if [[ "$state" != 'normal' ]]; then
|
|
||||||
let optlen="$i + 1"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
optlen=0
|
|
||||||
}
|
|
||||||
|
|
||||||
for (( i = 1; i < COMP_CWORD; i++ )); do
|
|
||||||
local word="${COMP_WORDS[$i]}"
|
|
||||||
|
|
||||||
# If currently processing an argument, skip this word
|
|
||||||
if [[ "$state" != 'normal' ]]; then
|
|
||||||
state=normal
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$word" = '--' ]]; then
|
|
||||||
# Options stop being parsed after this
|
|
||||||
opt_ena=false
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if it's a long option
|
|
||||||
if [[ "$word" = '--'* ]]; then
|
|
||||||
# If the option is unknown, assume it takes no arguments: keep the state at "normal"
|
|
||||||
for long_opt in "${opts[@]}"; do
|
|
||||||
if [[ "$word" = "--${long_opt%%:*}" ]]; then
|
|
||||||
state="${long_opt#*:}"
|
|
||||||
# Check if the next word is just '='; if so, skip it, the argument must follow
|
|
||||||
# (See "known bugs" at the top of this script)
|
|
||||||
let i++
|
|
||||||
if [[ "${COMP_WORDS[$i]}" != '=' ]]; then
|
|
||||||
let i--
|
|
||||||
fi
|
|
||||||
optlen=0
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
# Check if it's a short option
|
|
||||||
elif [[ "$word" = '-'* ]]; then
|
|
||||||
parse_short_opt "$word"
|
|
||||||
# The last option takes an argument...
|
|
||||||
if [[ "$state" != 'normal' ]]; then
|
|
||||||
if [[ "$optlen" -ne "${#word}" ]]; then
|
|
||||||
# If it's contained within the word, we won't complete it, revert to "normal"
|
|
||||||
state=normal
|
|
||||||
else
|
|
||||||
# Otherwise, complete it, but start at the beginning of *that* word
|
|
||||||
optlen=0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Parse current word
|
|
||||||
# Careful that it might look like an option, so use `--` aggressively!
|
|
||||||
local cur_word="${COMP_WORDS[$i]}"
|
|
||||||
|
|
||||||
# Process options, as short ones may change the state
|
|
||||||
if $opt_ena && [[ "$state" = 'normal' && "$cur_word" = '-'* ]]; then
|
|
||||||
# We might want to complete to an option or an arg to that option
|
|
||||||
# Parse the option word to check
|
|
||||||
# There's no whitespace in the option names, so we can ride a little dirty...
|
|
||||||
|
|
||||||
# Is this a long option?
|
|
||||||
if [[ "$cur_word" = '--'* ]]; then
|
|
||||||
# It is, try to complete one
|
|
||||||
mapfile -t COMPREPLY < <(compgen -W "${opts[*]%%:*}" -P '--' -- "${cur_word#--}")
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
# Short options may be grouped, parse them to determine what to complete
|
|
||||||
parse_short_opt "$cur_word"
|
|
||||||
|
|
||||||
if [[ "$state" = 'normal' ]]; then
|
|
||||||
mapfile -t COMPREPLY < <(compgen -W "${!opts[*]}" -P "$cur_word" '')
|
|
||||||
return 0
|
|
||||||
elif [[ "$optlen" = "${#cur_word}" && "$state" != "warning" ]]; then
|
|
||||||
# This short option group only awaits its argument!
|
|
||||||
# Post the option group as-is as a reply so that Readline inserts a space,
|
|
||||||
# so that the next completion request switches to the argument
|
|
||||||
# An exception is made for warnings, since it's idiomatic to stick them to the
|
|
||||||
# `-W`, and it doesn't break anything.
|
|
||||||
COMPREPLY=( "$cur_word" )
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
COMPREPLY=()
|
|
||||||
case "$state" in
|
|
||||||
unk) # Return with no replies: no idea what to complete!
|
|
||||||
;;
|
|
||||||
fix-spec)
|
|
||||||
COMPREPLY=( "${cur_word}"{l,h,g,L,H,G} )
|
|
||||||
;;
|
|
||||||
mbc)
|
|
||||||
local cur_arg="${cur_word:$optlen}"
|
|
||||||
cur_arg="${cur_arg@U}"
|
|
||||||
compopt -o nosort # Keep `help` first in the list, mainly
|
|
||||||
mapfile -t COMPREPLY < <(compgen -W "help" -P "${cur_word:0:$optlen}" -- "${cur_word:$optlen}")
|
|
||||||
mapfile -t COMPREPLY -O ${#COMPREPLY} < <(compgen -W "
|
|
||||||
ROM_ONLY
|
|
||||||
MBC1{,+RAM,+RAM+BATTERY}
|
|
||||||
MBC2{,+BATTERY}
|
|
||||||
MMM01{,+RAM}
|
|
||||||
MBC3{+TIMER+BATTERY,+TIMER+RAM+BATTERY,,+RAM,+RAM+BATTERY}
|
|
||||||
MBC5{,+RAM,+RAM+BATTERY,+RUMBLE,+RUMBLE+RAM,+RUMBLE+RAM+BATTERY}
|
|
||||||
MBC6
|
|
||||||
MBC7+SENSOR+RUMBLE+RAM+BATTERY
|
|
||||||
POCKET_CAMERA
|
|
||||||
BANDAI_TAMA5
|
|
||||||
HUC3
|
|
||||||
HUC1+RAM+BATTERY
|
|
||||||
TPP1_1.0{,+BATTERY}{,+RTC}{,+RUMBLE,+MULTIRUMBLE}" -P "${cur_word:0:$optlen}" -- "${cur_word/ /_}")
|
|
||||||
;;
|
|
||||||
normal) # Acts like a glob...
|
|
||||||
state="glob-*.gb *.gbc *.sgb"
|
|
||||||
;&
|
|
||||||
glob-*)
|
|
||||||
while read -r word; do
|
|
||||||
COMPREPLY+=("${cur_word:0:$optlen}$word")
|
|
||||||
done < <(for glob in ${state#glob-}; do compgen -A file -X \!"$glob" -- "${cur_word:$optlen}"; done)
|
|
||||||
# Also complete directories
|
|
||||||
;&
|
|
||||||
dir)
|
|
||||||
while read -r word; do
|
|
||||||
COMPREPLY+=("${cur_word:0:$optlen}$word")
|
|
||||||
done < <(compgen -A directory -- "${cur_word:$optlen}")
|
|
||||||
compopt -o filenames
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo >&2 "Internal completion error: invalid state \"$state\", please report this bug"
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
complete -F _rgbfix_completions rgbfix
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Same notes as RGBASM
|
|
||||||
|
|
||||||
_rgbgfx_completions() {
|
|
||||||
# Format: "long_opt:state_after"
|
|
||||||
# Empty long opt = it doesn't exit
|
|
||||||
# See the `state` variable below for info about `state_after`
|
|
||||||
declare -A opts=(
|
|
||||||
[V]="version:normal"
|
|
||||||
[C]="color-curve:normal"
|
|
||||||
[m]="mirror-tiles:normal"
|
|
||||||
[u]="unique-tiles:normal"
|
|
||||||
[v]="verbose:normal"
|
|
||||||
[Z]="columns:normal"
|
|
||||||
[a]="attr-map:glob-*.attrmap"
|
|
||||||
[A]="output-attr-map:normal"
|
|
||||||
[b]="base-tiles:unk"
|
|
||||||
[d]="depth:unk"
|
|
||||||
[L]="slice:unk"
|
|
||||||
[N]="nb-tiles:unk"
|
|
||||||
[n]="nb-palettes:unk"
|
|
||||||
[o]="output:glob-*.2bpp"
|
|
||||||
[p]="palette:glob-*.pal"
|
|
||||||
[P]="output-palette:normal"
|
|
||||||
[q]="palette-map:glob-*.palmap"
|
|
||||||
[Q]="output-palette-map:normal"
|
|
||||||
[r]="reverse:unk"
|
|
||||||
[s]="palette-size:unk"
|
|
||||||
[t]="tilemap:glob-*.tilemap"
|
|
||||||
[T]="output-tilemap:normal"
|
|
||||||
[x]="trim-end:unk"
|
|
||||||
)
|
|
||||||
# Parse command-line up to current word
|
|
||||||
local opt_ena=true
|
|
||||||
# Possible states:
|
|
||||||
# - normal = Well, normal. Options are parsed normally.
|
|
||||||
# - unk = An argument that can't be completed, and should just be skipped.
|
|
||||||
# - warning = A warning flag.
|
|
||||||
# - dir = A directory path
|
|
||||||
# - glob-* = A glob, after the dash is a whitespace-separated list of file globs to use
|
|
||||||
local state=normal
|
|
||||||
# The length of the option, used as a return value by the function below
|
|
||||||
local optlen=0
|
|
||||||
# $1: a short option word
|
|
||||||
# `state` will be set to the parsing state after the last option character in the word. If
|
|
||||||
# "normal" is not returned, `optlen` will be set to the length (dash included) of the "option"
|
|
||||||
# part of the argument.
|
|
||||||
parse_short_opt() {
|
|
||||||
for (( i = 1; i < "${#1}"; i++ )); do
|
|
||||||
# If the option is not known, assume it doesn't take an argument
|
|
||||||
local opt="${opts["${1:$i:1}"]:-":normal"}"
|
|
||||||
state="${opt#*:}"
|
|
||||||
# If the option takes an argument, record the length and exit
|
|
||||||
if [[ "$state" != 'normal' ]]; then
|
|
||||||
let optlen="$i + 1"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
optlen=0
|
|
||||||
}
|
|
||||||
|
|
||||||
for (( i = 1; i < COMP_CWORD; i++ )); do
|
|
||||||
local word="${COMP_WORDS[$i]}"
|
|
||||||
|
|
||||||
# If currently processing an argument, skip this word
|
|
||||||
if [[ "$state" != 'normal' ]]; then
|
|
||||||
state=normal
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$word" = '--' ]]; then
|
|
||||||
# Options stop being parsed after this
|
|
||||||
opt_ena=false
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if it's a long option
|
|
||||||
if [[ "$word" = '--'* ]]; then
|
|
||||||
# If the option is unknown, assume it takes no arguments: keep the state at "normal"
|
|
||||||
for long_opt in "${opts[@]}"; do
|
|
||||||
if [[ "$word" = "--${long_opt%%:*}" ]]; then
|
|
||||||
state="${long_opt#*:}"
|
|
||||||
# Check if the next word is just '='; if so, skip it, the argument must follow
|
|
||||||
# (See "known bugs" at the top of this script)
|
|
||||||
let i++
|
|
||||||
if [[ "${COMP_WORDS[$i]}" != '=' ]]; then
|
|
||||||
let i--
|
|
||||||
fi
|
|
||||||
optlen=0
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
# Check if it's a short option
|
|
||||||
elif [[ "$word" = '-'* ]]; then
|
|
||||||
parse_short_opt "$word"
|
|
||||||
# The last option takes an argument...
|
|
||||||
if [[ "$state" != 'normal' ]]; then
|
|
||||||
if [[ "$optlen" -ne "${#word}" ]]; then
|
|
||||||
# If it's contained within the word, we won't complete it, revert to "normal"
|
|
||||||
state=normal
|
|
||||||
else
|
|
||||||
# Otherwise, complete it, but start at the beginning of *that* word
|
|
||||||
optlen=0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Parse current word
|
|
||||||
# Careful that it might look like an option, so use `--` aggressively!
|
|
||||||
local cur_word="${COMP_WORDS[$i]}"
|
|
||||||
|
|
||||||
# Process options, as short ones may change the state
|
|
||||||
if $opt_ena && [[ "$state" = 'normal' && "$cur_word" = '-'* ]]; then
|
|
||||||
# We might want to complete to an option or an arg to that option
|
|
||||||
# Parse the option word to check
|
|
||||||
# There's no whitespace in the option names, so we can ride a little dirty...
|
|
||||||
|
|
||||||
# Is this a long option?
|
|
||||||
if [[ "$cur_word" = '--'* ]]; then
|
|
||||||
# It is, try to complete one
|
|
||||||
mapfile -t COMPREPLY < <(compgen -W "${opts[*]%%:*}" -P '--' -- "${cur_word#--}")
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
# Short options may be grouped, parse them to determine what to complete
|
|
||||||
parse_short_opt "$cur_word"
|
|
||||||
|
|
||||||
if [[ "$state" = 'normal' ]]; then
|
|
||||||
mapfile -t COMPREPLY < <(compgen -W "${!opts[*]}" -P "$cur_word" '')
|
|
||||||
return 0
|
|
||||||
elif [[ "$optlen" = "${#cur_word}" && "$state" != "warning" ]]; then
|
|
||||||
# This short option group only awaits its argument!
|
|
||||||
# Post the option group as-is as a reply so that Readline inserts a space,
|
|
||||||
# so that the next completion request switches to the argument
|
|
||||||
# An exception is made for warnings, since it's idiomatic to stick them to the
|
|
||||||
# `-W`, and it doesn't break anything.
|
|
||||||
COMPREPLY=( "$cur_word" )
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
COMPREPLY=()
|
|
||||||
case "$state" in
|
|
||||||
unk) # Return with no replies: no idea what to complete!
|
|
||||||
;;
|
|
||||||
normal) # Acts like a glob...
|
|
||||||
state="glob-*.png"
|
|
||||||
;&
|
|
||||||
glob-*)
|
|
||||||
while read -r word; do
|
|
||||||
COMPREPLY+=("${cur_word:0:$optlen}$word")
|
|
||||||
done < <(for glob in ${state#glob-}; do compgen -A file -X \!"$glob" -- "${cur_word:$optlen}"; done)
|
|
||||||
# Also complete directories
|
|
||||||
;&
|
|
||||||
dir)
|
|
||||||
while read -r word; do
|
|
||||||
COMPREPLY+=("${cur_word:0:$optlen}$word")
|
|
||||||
done < <(compgen -A directory -- "${cur_word:$optlen}")
|
|
||||||
compopt -o filenames
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo >&2 "Internal completion error: invalid state \"$state\", please report this bug"
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
complete -F _rgbgfx_completions rgbgfx
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Same notes as RGBASM
|
|
||||||
|
|
||||||
_rgblink_completions() {
|
|
||||||
# Format: "long_opt:state_after"
|
|
||||||
# Empty long opt = it doesn't exit
|
|
||||||
# See the `state` variable below for info about `state_after`
|
|
||||||
declare -A opts=(
|
|
||||||
[V]="version:normal"
|
|
||||||
[d]="dmg:normal"
|
|
||||||
[t]="tiny:normal"
|
|
||||||
[v]="verbose:normal"
|
|
||||||
[w]="wramx:normal"
|
|
||||||
[x]="nopad:normal"
|
|
||||||
[l]="linkerscript:glob-*"
|
|
||||||
[M]="no-sym-in-map:normal"
|
|
||||||
[m]="map:glob-*.map"
|
|
||||||
[n]="sym:glob-*.sym"
|
|
||||||
[O]="overlay:glob-*.gb *.gbc *.sgb"
|
|
||||||
[o]="output:glob-*.gb *.gbc *.sgb"
|
|
||||||
[p]="pad:unk"
|
|
||||||
[s]="smart:unk"
|
|
||||||
)
|
|
||||||
# Parse command-line up to current word
|
|
||||||
local opt_ena=true
|
|
||||||
# Possible states:
|
|
||||||
# - normal = Well, normal. Options are parsed normally.
|
|
||||||
# - unk = An argument that can't be completed, and should just be skipped.
|
|
||||||
# - warning = A warning flag.
|
|
||||||
# - dir = A directory path
|
|
||||||
# - glob-* = A glob, after the dash is a whitespace-separated list of file globs to use
|
|
||||||
local state=normal
|
|
||||||
# The length of the option, used as a return value by the function below
|
|
||||||
local optlen=0
|
|
||||||
# $1: a short option word
|
|
||||||
# `state` will be set to the parsing state after the last option character in the word. If
|
|
||||||
# "normal" is not returned, `optlen` will be set to the length (dash included) of the "option"
|
|
||||||
# part of the argument.
|
|
||||||
parse_short_opt() {
|
|
||||||
for (( i = 1; i < "${#1}"; i++ )); do
|
|
||||||
# If the option is not known, assume it doesn't take an argument
|
|
||||||
local opt="${opts["${1:$i:1}"]:-":normal"}"
|
|
||||||
state="${opt#*:}"
|
|
||||||
# If the option takes an argument, record the length and exit
|
|
||||||
if [[ "$state" != 'normal' ]]; then
|
|
||||||
let optlen="$i + 1"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
optlen=0
|
|
||||||
}
|
|
||||||
|
|
||||||
for (( i = 1; i < COMP_CWORD; i++ )); do
|
|
||||||
local word="${COMP_WORDS[$i]}"
|
|
||||||
|
|
||||||
# If currently processing an argument, skip this word
|
|
||||||
if [[ "$state" != 'normal' ]]; then
|
|
||||||
state=normal
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$word" = '--' ]]; then
|
|
||||||
# Options stop being parsed after this
|
|
||||||
opt_ena=false
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if it's a long option
|
|
||||||
if [[ "$word" = '--'* ]]; then
|
|
||||||
# If the option is unknown, assume it takes no arguments: keep the state at "normal"
|
|
||||||
for long_opt in "${opts[@]}"; do
|
|
||||||
if [[ "$word" = "--${long_opt%%:*}" ]]; then
|
|
||||||
state="${long_opt#*:}"
|
|
||||||
# Check if the next word is just '='; if so, skip it, the argument must follow
|
|
||||||
# (See "known bugs" at the top of this script)
|
|
||||||
let i++
|
|
||||||
if [[ "${COMP_WORDS[$i]}" != '=' ]]; then
|
|
||||||
let i--
|
|
||||||
fi
|
|
||||||
optlen=0
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
# Check if it's a short option
|
|
||||||
elif [[ "$word" = '-'* ]]; then
|
|
||||||
parse_short_opt "$word"
|
|
||||||
# The last option takes an argument...
|
|
||||||
if [[ "$state" != 'normal' ]]; then
|
|
||||||
if [[ "$optlen" -ne "${#word}" ]]; then
|
|
||||||
# If it's contained within the word, we won't complete it, revert to "normal"
|
|
||||||
state=normal
|
|
||||||
else
|
|
||||||
# Otherwise, complete it, but start at the beginning of *that* word
|
|
||||||
optlen=0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Parse current word
|
|
||||||
# Careful that it might look like an option, so use `--` aggressively!
|
|
||||||
local cur_word="${COMP_WORDS[$i]}"
|
|
||||||
|
|
||||||
# Process options, as short ones may change the state
|
|
||||||
if $opt_ena && [[ "$state" = 'normal' && "$cur_word" = '-'* ]]; then
|
|
||||||
# We might want to complete to an option or an arg to that option
|
|
||||||
# Parse the option word to check
|
|
||||||
# There's no whitespace in the option names, so we can ride a little dirty...
|
|
||||||
|
|
||||||
# Is this a long option?
|
|
||||||
if [[ "$cur_word" = '--'* ]]; then
|
|
||||||
# It is, try to complete one
|
|
||||||
mapfile -t COMPREPLY < <(compgen -W "${opts[*]%%:*}" -P '--' -- "${cur_word#--}")
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
# Short options may be grouped, parse them to determine what to complete
|
|
||||||
parse_short_opt "$cur_word"
|
|
||||||
|
|
||||||
if [[ "$state" = 'normal' ]]; then
|
|
||||||
mapfile -t COMPREPLY < <(compgen -W "${!opts[*]}" -P "$cur_word" '')
|
|
||||||
return 0
|
|
||||||
elif [[ "$optlen" = "${#cur_word}" && "$state" != "warning" ]]; then
|
|
||||||
# This short option group only awaits its argument!
|
|
||||||
# Post the option group as-is as a reply so that Readline inserts a space,
|
|
||||||
# so that the next completion request switches to the argument
|
|
||||||
# An exception is made for warnings, since it's idiomatic to stick them to the
|
|
||||||
# `-W`, and it doesn't break anything.
|
|
||||||
COMPREPLY=( "$cur_word" )
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
COMPREPLY=()
|
|
||||||
case "$state" in
|
|
||||||
unk) # Return with no replies: no idea what to complete!
|
|
||||||
;;
|
|
||||||
normal) # Acts like a glob...
|
|
||||||
state="glob-*.o *.obj"
|
|
||||||
;&
|
|
||||||
glob-*)
|
|
||||||
while read -r word; do
|
|
||||||
COMPREPLY+=("${cur_word:0:$optlen}$word")
|
|
||||||
done < <(for glob in ${state#glob-}; do compgen -A file -X \!"$glob" -- "${cur_word:$optlen}"; done)
|
|
||||||
# Also complete directories
|
|
||||||
;&
|
|
||||||
dir)
|
|
||||||
while read -r word; do
|
|
||||||
COMPREPLY+=("${cur_word:0:$optlen}$word")
|
|
||||||
done < <(compgen -A directory -- "${cur_word:$optlen}")
|
|
||||||
compopt -o filenames
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo >&2 "Internal completion error: invalid state \"$state\", please report this bug"
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
complete -F _rgblink_completions rgblink
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# SPDX-License-Identifier: MIT
|
|
||||||
#
|
|
||||||
# Copyright (c) 2021 Rangi
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
# SOFTWARE.
|
|
||||||
|
|
||||||
declare -A FILES
|
|
||||||
while read -r -d '' file; do
|
|
||||||
FILES["$file"]="true"
|
|
||||||
done < <(git diff --name-only -z "$1" HEAD)
|
|
||||||
|
|
||||||
edited () {
|
|
||||||
${FILES["$1"]:-"false"}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependency () {
|
|
||||||
if edited "$1" && ! edited "$2"; then
|
|
||||||
echo "'$1' was modified, but not '$2'! $3" | xargs
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Pull requests that edit the first file without the second may be correct,
|
|
||||||
# but are suspicious enough to require review.
|
|
||||||
|
|
||||||
dependency include/linkdefs.h man/rgbds.5 \
|
|
||||||
"Was the object file format changed?"
|
|
||||||
|
|
||||||
dependency src/asm/parser.y man/rgbasm.5 \
|
|
||||||
"Was the rgbasm grammar changed?"
|
|
||||||
|
|
||||||
dependency include/asm/warning.h man/rgbasm.1 \
|
|
||||||
"Were the rgbasm warnings changed?"
|
|
||||||
|
|
||||||
dependency src/asm/object.c include/linkdefs.h \
|
|
||||||
"Should the object file revision be bumped?"
|
|
||||||
dependency src/link/object.c include/linkdefs.h \
|
|
||||||
"Should the object file revision be bumped?"
|
|
||||||
|
|
||||||
dependency Makefile CMakeLists.txt \
|
|
||||||
"Did the build process change?"
|
|
||||||
dependency Makefile src/CMakeLists.txt \
|
|
||||||
"Did the build process change?"
|
|
||||||
|
|
||||||
dependency src/asm/main.c man/rgbasm.1 \
|
|
||||||
"Did the rgbasm CLI change?"
|
|
||||||
dependency src/asm/main.c contrib/zsh_compl/_rgbasm \
|
|
||||||
"Did the rgbasm CLI change?"
|
|
||||||
dependency src/asm/main.c contrib/bash_compl/_rgbasm.bash \
|
|
||||||
"Did the rgbasm CLI change?"
|
|
||||||
dependency src/link/main.c man/rgblink.1 \
|
|
||||||
"Did the rgblink CLI change?"
|
|
||||||
dependency src/link/main.c contrib/zsh_compl/_rgblink \
|
|
||||||
"Did the rgblink CLI change?"
|
|
||||||
dependency src/link/main.c contrib/bash_compl/_rgblink.bash \
|
|
||||||
"Did the rgblink CLI change?"
|
|
||||||
dependency src/fix/main.c man/rgbfix.1 \
|
|
||||||
"Did the rgbfix CLI change?"
|
|
||||||
dependency src/fix/main.c contrib/zsh_compl/_rgbfix \
|
|
||||||
"Did the rgbfix CLI change?"
|
|
||||||
dependency src/fix/main.c contrib/bash_compl/_rgbfix.bash \
|
|
||||||
"Did the rgbfix CLI change?"
|
|
||||||
dependency src/gfx/main.cpp man/rgbgfx.1 \
|
|
||||||
"Did the rgbgfx CLI change?"
|
|
||||||
dependency src/gfx/main.cpp contrib/zsh_compl/_rgbgfx \
|
|
||||||
"Did the rgbgfx CLI change?"
|
|
||||||
dependency src/gfx/main.cpp contrib/bash_compl/_rgbgfx.bash \
|
|
||||||
"Did the rgbgfx CLI change?"
|
|
||||||
@@ -23,51 +23,51 @@
|
|||||||
# SOFTWARE.
|
# SOFTWARE.
|
||||||
|
|
||||||
STATE=0
|
STATE=0
|
||||||
diff <(xxd "$1") <(xxd "$2") | while read -r LINE; do
|
diff <(xxd $1) <(xxd $2) | while read -r LINE; do
|
||||||
if [[ $STATE -eq 0 ]]; then
|
if [ $STATE -eq 0 ]; then
|
||||||
# Discard first line (line info)
|
# Discard first line (line info)
|
||||||
STATE=1
|
STATE=1
|
||||||
elif [[ "$LINE" = '---' ]]; then
|
elif [ "$LINE" = '---' ]; then
|
||||||
# Separator between files switches states
|
# Separator between files switches states
|
||||||
echo "$LINE"
|
echo $LINE
|
||||||
STATE=3
|
STATE=3
|
||||||
elif grep -Eq '^[0-9]+(,[0-9]+)?[cd][0-9]+(,[0-9]+)?' <<< "$LINE"; then
|
elif grep -Eq '^[0-9]+(,[0-9]+)?[cd][0-9]+(,[0-9]+)?' <<< "$LINE"; then
|
||||||
# Line info resets the whole thing
|
# Line info resets the whole thing
|
||||||
STATE=1
|
STATE=1
|
||||||
elif [[ $STATE -eq 1 || $STATE -eq 3 ]]; then
|
elif [ $STATE -eq 1 -o $STATE -eq 3 ]; then
|
||||||
# Compute the GB address from the ROM offset
|
# Compute the GB address from the ROM offset
|
||||||
OFS=$(cut -d ' ' -f 2 <<< "$LINE" | tr -d ':')
|
OFS=$(cut -d ' ' -f 2 <<< "$LINE" | tr -d ':')
|
||||||
BANK=$((0x$OFS / 0x4000))
|
BANK=$((0x$OFS / 0x4000))
|
||||||
ADDR=$((0x$OFS % 0x4000 + (BANK != 0) * 0x4000))
|
ADDR=$((0x$OFS % 0x4000 + ($BANK != 0) * 0x4000))
|
||||||
# Try finding the preceding symbol closest to the diff
|
# Try finding the preceding symbol closest to the diff
|
||||||
if [[ $STATE -eq 1 ]]; then
|
if [ $STATE -eq 1 ]; then
|
||||||
STATE=2
|
STATE=2
|
||||||
SYMFILE=${1%.*}.sym
|
SYMFILE=${1%.*}.sym
|
||||||
else
|
else
|
||||||
STATE=4
|
STATE=4
|
||||||
SYMFILE=${2%.*}.sym
|
SYMFILE=${2%.*}.sym
|
||||||
fi
|
fi
|
||||||
EXTRA=$(if [[ -f "$SYMFILE" ]]; then
|
EXTRA=$(if [ -f "$SYMFILE" ]; then
|
||||||
# Read the sym file for such a symbol
|
# Read the sym file for such a symbol
|
||||||
# Ignore comment lines, only pick matching bank
|
# Ignore comment lines, only pick matching bank
|
||||||
# (The bank regex ignores comments already, make `cut` and `tr` process less lines)
|
# (The bank regex ignores comments already, make `cut` and `tr` process less lines)
|
||||||
grep -Ei "$(printf "^%02x:" $BANK)" "$SYMFILE" |
|
grep -Ei $(printf "^%02x:" $BANK) "$SYMFILE" |
|
||||||
cut -d ';' -f 1 |
|
cut -d ';' -f 1 |
|
||||||
tr -d "\r" |
|
tr -d "\r" |
|
||||||
while read -r SYMADDR SYM; do
|
while read -r SYMADDR SYM; do
|
||||||
SYMADDR=$((0x${SYMADDR#*:}))
|
SYMADDR=$((0x${SYMADDR#*:}))
|
||||||
if [[ $SYMADDR -le $ADDR ]]; then
|
if [ $SYMADDR -le $ADDR ]; then
|
||||||
printf " (%s+%#x)\n" "$SYM" $((ADDR - SYMADDR))
|
printf " (%s+%#x)\n" "$SYM" $(($ADDR - $SYMADDR))
|
||||||
fi
|
fi
|
||||||
# TODO: assumes sorted sym files
|
# TODO: assumes sorted sym files
|
||||||
done | tail -n 1
|
done | tail -n 1
|
||||||
fi)
|
fi)
|
||||||
printf "%02x:%04x %s\n" $BANK $ADDR "$EXTRA"
|
printf "%02x:%04x %s\n" $BANK $ADDR $EXTRA
|
||||||
fi
|
fi
|
||||||
if [[ $STATE -eq 2 || $STATE -eq 4 ]]; then
|
if [ $STATE -eq 2 -o $STATE -eq 4 ]; then
|
||||||
OFS=$(cut -d ' ' -f 2 <<< "$LINE" | tr -d ':')
|
OFS=$(cut -d ' ' -f 2 <<< "$LINE" | tr -d ':')
|
||||||
BANK=$((0x$OFS / 0x4000))
|
BANK=$((0x$OFS / 0x4000))
|
||||||
ADDR=$((0x$OFS % 0x4000 + (BANK != 0) * 0x4000))
|
ADDR=$((0x$OFS % 0x4000 + ($BANK != 0) * 0x4000))
|
||||||
printf "%s %02x:%04x: %s\n" "${LINE:0:1}" $BANK $ADDR "${LINE#*: }"
|
printf "%s %02x:%04x: %s\n" "${LINE:0:1}" $BANK $ADDR "${LINE#*: }"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -9,27 +9,18 @@ _rgbasm_warnings() {
|
|||||||
'everything:Enable literally everything'
|
'everything:Enable literally everything'
|
||||||
|
|
||||||
'assert:Warn when WARN-type asserts fail'
|
'assert:Warn when WARN-type asserts fail'
|
||||||
'backwards-for:Warn when start and stop are backwards relative to step'
|
|
||||||
'builtin-args:Report incorrect args to built-in funcs'
|
'builtin-args:Report incorrect args to built-in funcs'
|
||||||
'charmap-redef:Warn when redefining a charmap mapping'
|
|
||||||
'div:Warn when dividing the smallest int by -1'
|
'div:Warn when dividing the smallest int by -1'
|
||||||
'empty-data-directive:Warn on arg-less d[bwl] in ROM'
|
'empty-entry:Warn on empty entries in db, dw, dl args'
|
||||||
'empty-macro-arg:Warn on empty macro arg'
|
|
||||||
'empty-strrpl:Warn on calling STRRPL with empty pattern'
|
|
||||||
'large-constant:Warn on constants too large for a signed 32-bit int'
|
'large-constant:Warn on constants too large for a signed 32-bit int'
|
||||||
'long-string:Warn on strings too long'
|
'long-string:Warn on strings too long'
|
||||||
'macro-shift:Warn when shifting macro args part their limits'
|
|
||||||
'nested-comment:Warn on "/*" inside block comments'
|
|
||||||
'numeric-string:Warn when a multi-character string is treated as a number'
|
|
||||||
'obsolete:Warn when using deprecated features'
|
'obsolete:Warn when using deprecated features'
|
||||||
'shift:Warn when shifting negative values'
|
'shift:Warn when shifting negative values'
|
||||||
'shift-amount:Warn when a shift'\''s operand it negative or \> 32'
|
'shift-amount:Warn when a shift'\''s operand it negative or \> 32'
|
||||||
'truncation:Warn when implicit truncation loses bits'
|
'truncation:Warn when implicit truncations lose bits'
|
||||||
'unmapped-char:Warn on unmapped character'
|
|
||||||
'user:Warn when executing the WARN built-in'
|
'user:Warn when executing the WARN built-in'
|
||||||
)
|
)
|
||||||
# TODO: handle `no-` and `error=` somehow?
|
# TODO: handle `no-` and `error=` somehow?
|
||||||
# TODO: handle `=0|1|2` levels for `numeric-string` and `truncation`?
|
|
||||||
_describe warning warnings
|
_describe warning warnings
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,28 +29,21 @@ local args=(
|
|||||||
'(- : * options)'{-V,--version}'[Print version number]'
|
'(- : * options)'{-V,--version}'[Print version number]'
|
||||||
|
|
||||||
'(-E --export-all)'{-E,--export-all}'[Export all symbols]'
|
'(-E --export-all)'{-E,--export-all}'[Export all symbols]'
|
||||||
'(-H --nop-after-halt)'{-H,--nop-after-halt}'[Output a `nop` after `halt`]'
|
'(-h --halt-without-nop)'{-h,--halt-without-nop}'[Avoid outputting a `nop` after `halt`]'
|
||||||
'(-h --halt-without-nop)'{-h,--halt-without-nop}'[Prevent outputting a `nop` after `halt`]'
|
'(-L ---preserve-ld)'{-L,--preserve-ld}'[Prevent auto-optimizing `ld` into `ldh`]'
|
||||||
'(-L --preserve-ld)'{-L,--preserve-ld}'[Prevent optimizing `ld` into `ldh`]'
|
|
||||||
'(-l --auto-ldh)'{-l,--auto-ldh}'[Optimize `ld` into `ldh`]'
|
|
||||||
'(-v --verbose)'{-v,--verbose}'[Print additional messages regarding progression]'
|
'(-v --verbose)'{-v,--verbose}'[Print additional messages regarding progression]'
|
||||||
-w'[Disable all warnings]'
|
-w'[Disable all warnings]'
|
||||||
|
|
||||||
'(-b --binary-digits)'{-b,--binary-digits}'+[Change chars for binary constants]:digit spec:'
|
'(-b --binary-digits)'{-b,--binary-digits}'+[Change chars for binary constants]:digit spec:'
|
||||||
'*'{-D,--define}'+[Define a string symbol]:name + value (default 1):'
|
'(-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:'
|
'(-g --gfx-chars)'{-g,--gfx-chars}'+[Change chars for gfx constants]:chars spec:'
|
||||||
'(-i --include)'{-i,--include}'+[Add an include directory]:include path:_files -/'
|
'(-i --include)'{-i,--include}'+[Add an include directory]:include path:_files -/'
|
||||||
'(-M --dependfile)'{-M,--dependfile}"+[List deps in make format]:output file:_files -g '*.{d,mk}'"
|
'(-M --dependfile)'{-M,--dependfile}"+[List deps in make format]:output file:_files -g '*.{d,mk}"
|
||||||
-MG'[Assume missing files should be generated]'
|
|
||||||
-MP'[Add phony targets to all deps]'
|
|
||||||
'*'-MT"+[Add a target to the rules]:target:_files -g '*.{d,mk,o}'"
|
|
||||||
'*'-MQ"+[Add a target to the rules]:target:_files -g '*.{d,mk,o}'"
|
|
||||||
'(-o --output)'{-o,--output}'+[Output file]:output file:_files'
|
'(-o --output)'{-o,--output}'+[Output file]:output file:_files'
|
||||||
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
|
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
|
||||||
'(-Q --q-precision)'{-Q,--q-precision}'+[Set fixed-point precision]:precision:'
|
|
||||||
'(-r --recursion-depth)'{-r,--recursion-depth}'+[Set maximum recursion depth]:depth:'
|
'(-r --recursion-depth)'{-r,--recursion-depth}'+[Set maximum recursion depth]:depth:'
|
||||||
'(-W --warning)'{-W,--warning}'+[Toggle warning flags]:warning flag:_rgbasm_warnings'
|
'(-W --warning)'{-W,--warning}'+[Toggle warning flags]:warning flag:_rgbasm_warnings'
|
||||||
|
|
||||||
":assembly sources:_files -g '*.asm'"
|
'*'":assembly sources:_files -g '*.asm'"
|
||||||
)
|
)
|
||||||
_arguments -s -S : $args
|
_arguments -s -S : $args
|
||||||
|
|||||||
@@ -1,47 +1,12 @@
|
|||||||
#compdef rgbfix
|
#compdef rgbfix
|
||||||
|
|
||||||
_mbc_names() {
|
|
||||||
local mbc_names=(
|
|
||||||
'ROM:$00'
|
|
||||||
'MBC1:$01'
|
|
||||||
'MBC1+RAM:$02'
|
|
||||||
'MBC1+RAM+BATTERY:$03'
|
|
||||||
'MBC2:$05'
|
|
||||||
'MBC2+BATTERY:$06'
|
|
||||||
'ROM+RAM:$08'
|
|
||||||
'ROM+RAM+BATTERY:$09'
|
|
||||||
'MMM01:$0B'
|
|
||||||
'MMM01+RAM:$0C'
|
|
||||||
'MMM01+RAM+BATTERY:$0D'
|
|
||||||
'MBC3+TIMER+BATTERY:$0F'
|
|
||||||
'MBC3+TIMER+RAM+BATTERY:$10'
|
|
||||||
'MBC3:$11'
|
|
||||||
'MBC3+RAM:$12'
|
|
||||||
'MBC3+RAM+BATTERY:$13'
|
|
||||||
'MBC5:$19'
|
|
||||||
'MBC5+RAM:$1A'
|
|
||||||
'MBC5+RAM+BATTERY:$1B'
|
|
||||||
'MBC5+RUMBLE:$1C'
|
|
||||||
'MBC5+RUMBLE+RAM:$1D'
|
|
||||||
'MBC5+RUMBLE+RAM+BATTERY:$1E'
|
|
||||||
'MBC6:$20'
|
|
||||||
'MBC7+SENSOR+RUMBLE+RAM+BATTERY:$22'
|
|
||||||
'POCKET_CAMERA:$FC'
|
|
||||||
'BANDAI_TAMA5:$FD'
|
|
||||||
'HUC3:$FE'
|
|
||||||
'HUC1+RAM+BATTERY:$FF'
|
|
||||||
)
|
|
||||||
_describe "MBC name" mbc_names
|
|
||||||
}
|
|
||||||
|
|
||||||
local args=(
|
local args=(
|
||||||
# Arguments are listed here in the same order as in the manual, except for the version
|
# Arguments are listed here in the same order as in the manual, except for the version
|
||||||
'(- : * options)'{-V,--version}'[Print version number]'
|
'(- : * options)'{-V,--version}'[Print version number]'
|
||||||
|
|
||||||
'(-C --color-only -c --color-compatible)'{-C,--color-only}'[Mark ROM as GBC-only]'
|
'(-C --color-only)'{-C,--color-only}'[Mark ROM as GBC-only]'
|
||||||
'(-C --color-only -c --color-compatible)'{-c,--color-compatible}'[Mark ROM as GBC-compatible]'
|
'(-c --color-compatible)'{-c,--color-compatible}'[Mark ROM as GBC-compatible]'
|
||||||
'(-j --non-japanese)'{-j,--non-japanese}'[Set the non-Japanese region flag]'
|
'(-j --non-japanese)'{-j,--non-japanese}'[Set the non-Japanese region flag]'
|
||||||
'(-O --overwrite)'{-O,--overwrite}'[Allow overwriting non-zero bytes]'
|
|
||||||
'(-s --sgb-compatible)'{-s,--sgb-compatible}'[Set the SGB flag]'
|
'(-s --sgb-compatible)'{-s,--sgb-compatible}'[Set the SGB flag]'
|
||||||
'(-f --fix-spec -v --validate)'{-v,--validate}'[Shorthand for -f lhg]'
|
'(-f --fix-spec -v --validate)'{-v,--validate}'[Shorthand for -f lhg]'
|
||||||
|
|
||||||
@@ -49,7 +14,7 @@ local args=(
|
|||||||
'(-i --game-id)'{-i,--game-id}'+[Set game ID string]:4-char game ID:'
|
'(-i --game-id)'{-i,--game-id}'+[Set game ID string]:4-char game ID:'
|
||||||
'(-k --new-licensee)'{-k,--new-licensee}'+[Set new licensee string]:2-char licensee ID:'
|
'(-k --new-licensee)'{-k,--new-licensee}'+[Set new licensee string]:2-char licensee ID:'
|
||||||
'(-l --old-licensee)'{-l,--old-licensee}'+[Set old licensee ID]:licensee number:'
|
'(-l --old-licensee)'{-l,--old-licensee}'+[Set old licensee ID]:licensee number:'
|
||||||
'(-m --mbc-type)'{-m,--mbc-type}"+[Set MBC flags]:mbc name:_mbc_names"
|
'(-m --mbc-type)'{-m,--mbc-type}'+[Set MBC flags]:mbc flags byte:'
|
||||||
'(-n --rom-version)'{-n,--rom-version}'+[Set ROM version]:rom version 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:'
|
'(-p --pad-value)'{-p,--pad-value}'+[Pad to next valid size using this byte as padding]:padding byte:'
|
||||||
'(-r --ram-size)'{-r,--ram-size}'+[Set RAM size]:ram size byte:'
|
'(-r --ram-size)'{-r,--ram-size}'+[Set RAM size]:ram size byte:'
|
||||||
|
|||||||
@@ -15,28 +15,23 @@ local args=(
|
|||||||
|
|
||||||
'(-a --attr-map -A --output-attr-map)'{-A,--output-attr-map}'[Shortcut for -a <file>.attrmap]'
|
'(-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]'
|
'(-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]'
|
'(-m --mirror-tiles)'{-m,--mirror-tiles}'[Eliminate mirrored tiles from output]'
|
||||||
'(-p --palette -P --output-palette)'{-P,--output-palette}'[Shortcut for -p <file>.pal]'
|
'(-p --palette -P --output-palette)'{-P,--output-palette}'[Shortcut for -p <file>.pal]'
|
||||||
'(-q --palette-map -Q --output-palette-map)'{-Q,--output-palette-map}'[Shortcut for -p <file>.palmap]'
|
|
||||||
'(-t --tilemap -T --output-tilemap)'{-T,--output-tilemap}'[Shortcut for -t <file>.tilemap]'
|
'(-t --tilemap -T --output-tilemap)'{-T,--output-tilemap}'[Shortcut for -t <file>.tilemap]'
|
||||||
'(-u --unique-tiles)'{-u,--unique-tiles}'[Eliminate redundant tiles]'
|
'(-u --unique-tiles)'{-u,--unique-tiles}'[Eliminate redundant tiles]'
|
||||||
{-v,--verbose}'[Enable verbose output]'
|
'(-v --verbose)'{-v,--verbose}'[Enable verbose output]'
|
||||||
'(-h --horizontal -Z --columns)'{-Z,--columns}'[Read the image in column-major order]'
|
|
||||||
|
|
||||||
'(-a --attr-map -A --output-attr-map)'{-a,--attr-map}'+[Generate a map of tile attributes (mirroring)]:attrmap file:_files'
|
'(-a --attr-map -A --output-attr-map)'{-a,--attr-map}'+[Generate a map of tile attributes (mirroring)]:attrmap file:_files'
|
||||||
'(-b --base-tiles)'{-b,--base-tiles}'+[Base tile IDs for tile map output]:base tile IDs:'
|
|
||||||
'(-d --depth)'{-d,--depth}'+[Set bit depth]:bit depth:_depths'
|
'(-d --depth)'{-d,--depth}'+[Set bit depth]:bit depth:_depths'
|
||||||
'(-L --slice)'{-L,--slice}'+[Only process a portion of the image]:input slice:'
|
|
||||||
'(-N --nb-tiles)'{-n,--nb-tiles}'+[Limit number of tiles]:tile count:'
|
|
||||||
'(-n --nb-palettes)'{-n,--nb-palettes}'+[Limit number of palettes]:palette count:'
|
|
||||||
'(-o --output)'{-o,--output}'+[Set output file]:output file:_files'
|
'(-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"
|
'(-p --palette -P --output-palette)'{-p,--palette}"+[Output the image's palette in little-endian native RGB555 format]:palette file:_files"
|
||||||
'(-q --palette-map -Q --output-palette-map)'{-p,--palette-map}"+[Output the image's palette map]:palette map file:_files"
|
|
||||||
'(-r --reverse)'{-r,--reverse}'+[Yield an image from binary data]:image width (in tiles):'
|
|
||||||
'(-s --palette-size)'{-s,--palette-size}'+[Limit palette size]:palette size:'
|
|
||||||
'(-t --tilemap -T --output-tilemap)'{-t,--tilemap}'+[Generate a map of tile indices]:tilemap 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:'
|
'(-x --trim-end)'{-x,--trim-end}'+[Trim end of output by this many tiles]:tile count:'
|
||||||
|
|
||||||
":input png file:_files -g '*.png'"
|
'*'":input png files:_files -g '*.png'"
|
||||||
)
|
)
|
||||||
_arguments -s -S : $args
|
_arguments -s -S : $args
|
||||||
|
|||||||
@@ -8,16 +8,13 @@ local args=(
|
|||||||
'(-t --tiny)'{-t,--tiny}'[Enable tiny mode, disabling ROM banking]'
|
'(-t --tiny)'{-t,--tiny}'[Enable tiny mode, disabling ROM banking]'
|
||||||
'(-v --verbose)'{-v,--verbose}'[Enable verbose output]'
|
'(-v --verbose)'{-v,--verbose}'[Enable verbose output]'
|
||||||
'(-w --wramx)'{-w,--wramx}'[Disable WRAM banking]'
|
'(-w --wramx)'{-w,--wramx}'[Disable WRAM banking]'
|
||||||
'(-x --nopad)'{-x,--nopad}'[Disable padding the end of the final file]'
|
|
||||||
|
|
||||||
'(-l --linkerscript)'{-l,--linkerscript}"+[Use a linker script]:linker script:_files -g '*.link'"
|
'(-l --linkerscript)'{-l,--linkerscript}"+[Use a linker script]:linker script:_files -g '*.link'"
|
||||||
'(-M --no-sym-in-map)'{-M,--no-sym-in-map}'[Do not output symbol names in map file]'
|
|
||||||
'(-m --map)'{-m,--map}"+[Produce a map file]:map file:_files -g '*.map'"
|
'(-m --map)'{-m,--map}"+[Produce a map file]:map file:_files -g '*.map'"
|
||||||
'(-n --sym)'(-n,--sym)"+[Produce a symbol file]:sym file:_files -g '*.sym'"
|
'(-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 --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}'"
|
'(-o --output)'{-o,--output}"+[Write ROM image to this file]:rom file:_files -g '*.{gb,sgb,gbc}'"
|
||||||
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
|
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
|
||||||
'(-S --scramble)'{-s,--scramble}'+[Activate scrambling]:scramble spec'
|
|
||||||
'(-s --smart)'{-s,--smart}'+[!BROKEN! Perform smart linking from this symbol]:symbol name:'
|
'(-s --smart)'{-s,--smart}'+[!BROKEN! Perform smart linking from this symbol]:symbol name:'
|
||||||
|
|
||||||
'*'":object files:_files -g '*.o'"
|
'*'":object files:_files -g '*.o'"
|
||||||
|
|||||||
@@ -11,13 +11,12 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
struct Charmap *charmap_New(char const *name, char const *baseName);
|
struct Charmap *charmap_New(const char *name, const char *baseName);
|
||||||
void charmap_Delete(struct Charmap *charmap);
|
void charmap_Delete(struct Charmap *charmap);
|
||||||
void charmap_Set(char const *name);
|
void charmap_Set(const char *name);
|
||||||
void charmap_Push(void);
|
void charmap_Push(void);
|
||||||
void charmap_Pop(void);
|
void charmap_Pop(void);
|
||||||
void charmap_Add(char *mapping, uint8_t value);
|
void charmap_Add(char *mapping, uint8_t value);
|
||||||
size_t charmap_Convert(char const *input, uint8_t *output);
|
size_t charmap_Convert(char const *input, uint8_t *output);
|
||||||
size_t charmap_ConvertNext(char const **input, uint8_t **output);
|
|
||||||
|
|
||||||
#endif // RGBDS_ASM_CHARMAP_H
|
#endif /* RGBDS_ASM_CHARMAP_H */
|
||||||
|
|||||||
@@ -11,9 +11,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
extern uint8_t fixPrecision;
|
int32_t fix_Callback_PI(void);
|
||||||
|
|
||||||
double fix_PrecisionFactor(void);
|
|
||||||
void fix_Print(int32_t i);
|
void fix_Print(int32_t i);
|
||||||
int32_t fix_Sin(int32_t i);
|
int32_t fix_Sin(int32_t i);
|
||||||
int32_t fix_Cos(int32_t i);
|
int32_t fix_Cos(int32_t i);
|
||||||
@@ -23,7 +21,6 @@ int32_t fix_ACos(int32_t i);
|
|||||||
int32_t fix_ATan(int32_t i);
|
int32_t fix_ATan(int32_t i);
|
||||||
int32_t fix_ATan2(int32_t i, int32_t j);
|
int32_t fix_ATan2(int32_t i, int32_t j);
|
||||||
int32_t fix_Mul(int32_t i, int32_t j);
|
int32_t fix_Mul(int32_t i, int32_t j);
|
||||||
int32_t fix_Mod(int32_t i, int32_t j);
|
|
||||||
int32_t fix_Div(int32_t i, int32_t j);
|
int32_t fix_Div(int32_t i, int32_t j);
|
||||||
int32_t fix_Pow(int32_t i, int32_t j);
|
int32_t fix_Pow(int32_t i, int32_t j);
|
||||||
int32_t fix_Log(int32_t i, int32_t j);
|
int32_t fix_Log(int32_t i, int32_t j);
|
||||||
@@ -31,4 +28,4 @@ int32_t fix_Round(int32_t i);
|
|||||||
int32_t fix_Ceil(int32_t i);
|
int32_t fix_Ceil(int32_t i);
|
||||||
int32_t fix_Floor(int32_t i);
|
int32_t fix_Floor(int32_t i);
|
||||||
|
|
||||||
#endif // RGBDS_ASM_FIXPOINT_H
|
#endif /* RGBDS_ASM_FIXPOINT_H */
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
#ifndef RGBDS_FORMAT_SPEC_H
|
#ifndef RGBDS_FORMAT_SPEC_H
|
||||||
#define RGBDS_FORMAT_SPEC_H
|
#define RGBDS_FORMAT_SPEC_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
enum FormatState {
|
enum FormatState {
|
||||||
FORMAT_SIGN, // expects '+' or ' ' (optional)
|
FORMAT_SIGN, // expects '+' or ' ' (optional)
|
||||||
@@ -28,9 +28,9 @@ struct FormatSpec {
|
|||||||
bool prefix;
|
bool prefix;
|
||||||
bool alignLeft;
|
bool alignLeft;
|
||||||
bool padZero;
|
bool padZero;
|
||||||
size_t width;
|
uint8_t width;
|
||||||
bool hasFrac;
|
bool hasFrac;
|
||||||
size_t fracWidth;
|
uint8_t fracWidth;
|
||||||
int type;
|
int type;
|
||||||
bool valid;
|
bool valid;
|
||||||
};
|
};
|
||||||
@@ -60,4 +60,4 @@ void fmt_FinishCharacters(struct FormatSpec *fmt);
|
|||||||
void fmt_PrintString(char *buf, size_t bufLen, struct FormatSpec const *fmt, char const *value);
|
void fmt_PrintString(char *buf, size_t bufLen, struct FormatSpec const *fmt, char const *value);
|
||||||
void fmt_PrintNumber(char *buf, size_t bufLen, struct FormatSpec const *fmt, uint32_t value);
|
void fmt_PrintNumber(char *buf, size_t bufLen, struct FormatSpec const *fmt, uint32_t value);
|
||||||
|
|
||||||
#endif // RGBDS_FORMAT_SPEC_H
|
#endif /* RGBDS_FORMAT_SPEC_H */
|
||||||
|
|||||||
@@ -6,7 +6,9 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Contains some assembler-wide defines and externs
|
/*
|
||||||
|
* Contains some assembler-wide defines and externs
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef RGBDS_ASM_FSTACK_H
|
#ifndef RGBDS_ASM_FSTACK_H
|
||||||
#define RGBDS_ASM_FSTACK_H
|
#define RGBDS_ASM_FSTACK_H
|
||||||
@@ -17,15 +19,16 @@
|
|||||||
|
|
||||||
#include "asm/lexer.h"
|
#include "asm/lexer.h"
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
struct FileStackNode {
|
struct FileStackNode {
|
||||||
struct FileStackNode *parent; // Pointer to parent node, for error reporting
|
struct FileStackNode *parent; /* Pointer to parent node, for error reporting */
|
||||||
// Line at which the parent context was exited; meaningless for the root level
|
/* Line at which the parent context was exited; meaningless for the root level */
|
||||||
uint32_t lineNo;
|
uint32_t lineNo;
|
||||||
|
|
||||||
struct FileStackNode *next; // Next node in the output linked list
|
struct FileStackNode *next; /* Next node in the output linked list */
|
||||||
bool referenced; // If referenced, don't free!
|
bool referenced; /* If referenced, don't free! */
|
||||||
uint32_t ID; // Set only if referenced: ID within the object file, -1 if not output yet
|
uint32_t ID; /* Set only if referenced: ID within the object file, -1 if not output yet */
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
NODE_REPT,
|
NODE_REPT,
|
||||||
@@ -34,31 +37,30 @@ struct FileStackNode {
|
|||||||
} type;
|
} type;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FileStackReptNode { // NODE_REPT
|
struct FileStackReptNode { /* NODE_REPT */
|
||||||
struct FileStackNode node;
|
struct FileStackNode node;
|
||||||
uint32_t reptDepth;
|
uint32_t reptDepth;
|
||||||
// WARNING: if changing this type, change overflow check in `fstk_Init`
|
/* 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
|
uint32_t iters[]; /* REPT iteration counts since last named node, in reverse depth order */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FileStackNamedNode { // NODE_FILE, NODE_MACRO
|
struct FileStackNamedNode { /* NODE_FILE, NODE_MACRO */
|
||||||
struct FileStackNode node;
|
struct FileStackNode node;
|
||||||
char name[]; // File name for files, file::macro name for macros
|
char name[]; /* File name for files, file::macro name for macros */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define DEFAULT_MAX_DEPTH 64
|
extern size_t nMaxRecursionDepth;
|
||||||
extern size_t maxRecursionDepth;
|
|
||||||
|
|
||||||
struct MacroArgs;
|
struct MacroArgs;
|
||||||
|
|
||||||
void fstk_Dump(struct FileStackNode const *node, uint32_t lineNo);
|
void fstk_Dump(struct FileStackNode const *node, uint32_t lineNo);
|
||||||
void fstk_DumpCurrent(void);
|
void fstk_DumpCurrent(void);
|
||||||
struct FileStackNode *fstk_GetFileStack(void);
|
struct FileStackNode *fstk_GetFileStack(void);
|
||||||
// The lifetime of the returned chars is until reaching the end of that file
|
/* The lifetime of the returned chars is until reaching the end of that file */
|
||||||
char const *fstk_GetFileName(void);
|
char const *fstk_GetFileName(void);
|
||||||
|
|
||||||
void fstk_AddIncludePath(char const *s);
|
void fstk_AddIncludePath(char const *s);
|
||||||
/*
|
/**
|
||||||
* @param path The user-provided file name
|
* @param path The user-provided file name
|
||||||
* @param fullPath The address of a pointer, which will be made to point at the full path
|
* @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
|
* The pointer's value must be a valid argument to `realloc`, including NULL
|
||||||
@@ -70,13 +72,12 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size);
|
|||||||
bool yywrap(void);
|
bool yywrap(void);
|
||||||
void fstk_RunInclude(char const *path);
|
void fstk_RunInclude(char const *path);
|
||||||
void fstk_RunMacro(char const *macroName, struct MacroArgs *args);
|
void fstk_RunMacro(char const *macroName, struct MacroArgs *args);
|
||||||
void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size);
|
void fstk_RunRept(uint32_t count, int32_t nReptLineNo, char *body, size_t size);
|
||||||
void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step,
|
void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step,
|
||||||
int32_t reptLineNo, char *body, size_t size);
|
int32_t reptLineNo, char *body, size_t size);
|
||||||
void fstk_StopRept(void);
|
void fstk_StopRept(void);
|
||||||
bool fstk_Break(void);
|
bool fstk_Break(void);
|
||||||
|
|
||||||
void fstk_NewRecursionDepth(size_t newDepth);
|
void fstk_Init(char const *mainPath, size_t maxRecursionDepth);
|
||||||
void fstk_Init(char const *mainPath, size_t maxDepth);
|
|
||||||
|
|
||||||
#endif // RGBDS_ASM_FSTACK_H
|
#endif /* RGBDS_ASM_FSTACK_H */
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#define MAXSTRLEN 255
|
#define MAXSTRLEN 255
|
||||||
|
|
||||||
struct LexerState;
|
struct LexerState;
|
||||||
extern struct LexerState *lexerState;
|
extern struct LexerState *lexerState;
|
||||||
@@ -49,9 +49,11 @@ static inline void lexer_SetGfxDigits(char const digits[4])
|
|||||||
gfxDigits[3] = digits[3];
|
gfxDigits[3] = digits[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
// `path` is referenced, but not held onto..!
|
/*
|
||||||
|
* `path` is referenced, but not held onto..!
|
||||||
|
*/
|
||||||
struct LexerState *lexer_OpenFile(char const *path);
|
struct LexerState *lexer_OpenFile(char const *path);
|
||||||
struct LexerState *lexer_OpenFileView(char const *path, char *buf, size_t size, uint32_t lineNo);
|
struct LexerState *lexer_OpenFileView(char *buf, size_t size, uint32_t lineNo);
|
||||||
void lexer_RestartRept(uint32_t lineNo);
|
void lexer_RestartRept(uint32_t lineNo);
|
||||||
void lexer_DeleteState(struct LexerState *state);
|
void lexer_DeleteState(struct LexerState *state);
|
||||||
void lexer_Init(void);
|
void lexer_Init(void);
|
||||||
@@ -81,14 +83,13 @@ struct CaptureBody {
|
|||||||
size_t size;
|
size_t size;
|
||||||
};
|
};
|
||||||
|
|
||||||
void lexer_CheckRecursionDepth(void);
|
|
||||||
char const *lexer_GetFileName(void);
|
char const *lexer_GetFileName(void);
|
||||||
uint32_t lexer_GetLineNo(void);
|
uint32_t lexer_GetLineNo(void);
|
||||||
uint32_t lexer_GetColNo(void);
|
uint32_t lexer_GetColNo(void);
|
||||||
void lexer_DumpStringExpansions(void);
|
void lexer_DumpStringExpansions(void);
|
||||||
int yylex(void);
|
int yylex(void);
|
||||||
bool lexer_CaptureRept(struct CaptureBody *capture);
|
void lexer_CaptureRept(struct CaptureBody *capture);
|
||||||
bool lexer_CaptureMacroBody(struct CaptureBody *capture);
|
void lexer_CaptureMacroBody(struct CaptureBody *capture);
|
||||||
|
|
||||||
#define INITIAL_DS_ARG_SIZE 2
|
#define INITIAL_DS_ARG_SIZE 2
|
||||||
struct DsArgList {
|
struct DsArgList {
|
||||||
@@ -97,4 +98,4 @@ struct DsArgList {
|
|||||||
struct Expression *args;
|
struct Expression *args;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // RGBDS_ASM_LEXER_H
|
#endif /* RGBDS_ASM_LEXER_H */
|
||||||
|
|||||||
@@ -25,14 +25,13 @@ void macro_AppendArg(struct MacroArgs **args, char *s);
|
|||||||
void macro_UseNewArgs(struct MacroArgs *args);
|
void macro_UseNewArgs(struct MacroArgs *args);
|
||||||
void macro_FreeArgs(struct MacroArgs *args);
|
void macro_FreeArgs(struct MacroArgs *args);
|
||||||
char const *macro_GetArg(uint32_t i);
|
char const *macro_GetArg(uint32_t i);
|
||||||
char const *macro_GetAllArgs(void);
|
char *macro_GetAllArgs(void);
|
||||||
|
|
||||||
uint32_t macro_GetUniqueID(void);
|
uint32_t macro_GetUniqueID(void);
|
||||||
char const *macro_GetUniqueIDStr(void);
|
char const *macro_GetUniqueIDStr(void);
|
||||||
void macro_SetUniqueID(uint32_t id);
|
void macro_SetUniqueID(uint32_t id);
|
||||||
uint32_t macro_UseNewUniqueID(void);
|
uint32_t macro_UseNewUniqueID(void);
|
||||||
uint32_t macro_UndefUniqueID(void);
|
|
||||||
void macro_ShiftCurrentArgs(int32_t count);
|
void macro_ShiftCurrentArgs(int32_t count);
|
||||||
uint32_t macro_NbArgs(void);
|
uint32_t macro_NbArgs(void);
|
||||||
|
|
||||||
#endif // RGBDS_MACRO_H
|
#endif
|
||||||
|
|||||||
@@ -16,16 +16,22 @@
|
|||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
|
||||||
extern bool haltnop;
|
extern bool haltnop;
|
||||||
extern bool warnOnHaltNop;
|
extern bool optimizeloads;
|
||||||
extern bool optimizeLoads;
|
|
||||||
extern bool warnOnLdOpt;
|
|
||||||
extern bool verbose;
|
extern bool verbose;
|
||||||
extern bool warnings; // True to enable warnings, false to disable them.
|
extern bool warnings; /* True to enable warnings, false to disable them. */
|
||||||
|
|
||||||
extern FILE *dependfile;
|
extern FILE *dependfile;
|
||||||
extern char *targetFileName;
|
extern char *tzTargetFileName;
|
||||||
extern bool generatedMissingIncludes;
|
extern bool oGeneratedMissingIncludes;
|
||||||
extern bool failedOnMissingInclude;
|
extern bool oFailedOnMissingInclude;
|
||||||
extern bool generatePhonyDeps;
|
extern bool oGeneratePhonyDeps;
|
||||||
|
|
||||||
#endif // RGBDS_MAIN_H
|
/* TODO: are these really needed? */
|
||||||
|
#define YY_FATAL_ERROR fatalerror
|
||||||
|
|
||||||
|
#ifdef YYLMAX
|
||||||
|
#undef YYLMAX
|
||||||
|
#endif
|
||||||
|
#define YYLMAX 65536
|
||||||
|
|
||||||
|
#endif /* RGBDS_MAIN_H */
|
||||||
|
|||||||
@@ -9,18 +9,14 @@
|
|||||||
#ifndef RGBDS_OPT_H
|
#ifndef RGBDS_OPT_H
|
||||||
#define RGBDS_OPT_H
|
#define RGBDS_OPT_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
void opt_B(char const chars[2]);
|
void opt_B(char chars[2]);
|
||||||
void opt_G(char const chars[4]);
|
void opt_G(char chars[4]);
|
||||||
void opt_P(uint8_t padByte);
|
void opt_P(uint8_t fill);
|
||||||
void opt_Q(uint8_t precision);
|
|
||||||
void opt_L(bool optimize);
|
|
||||||
void opt_W(char const *flag);
|
|
||||||
void opt_Parse(char const *option);
|
void opt_Parse(char const *option);
|
||||||
|
|
||||||
void opt_Push(void);
|
void opt_Push(void);
|
||||||
void opt_Pop(void);
|
void opt_Pop(void);
|
||||||
|
|
||||||
#endif // RGBDS_OPT_H
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
struct Expression;
|
struct Expression;
|
||||||
struct FileStackNode;
|
struct FileStackNode;
|
||||||
|
|
||||||
extern char *objectName;
|
extern char *tzObjectname;
|
||||||
extern struct Section *sectionList;
|
extern struct Section *pSectionList, *pCurrentSection;
|
||||||
|
|
||||||
void out_RegisterNode(struct FileStackNode *node);
|
void out_RegisterNode(struct FileStackNode *node);
|
||||||
void out_ReplaceNode(struct FileStackNode *node);
|
void out_ReplaceNode(struct FileStackNode *node);
|
||||||
@@ -27,4 +27,4 @@ bool out_CreateAssert(enum AssertionType type, struct Expression const *expr,
|
|||||||
char const *message, uint32_t ofs);
|
char const *message, uint32_t ofs);
|
||||||
void out_WriteObject(void);
|
void out_WriteObject(void);
|
||||||
|
|
||||||
#endif // RGBDS_ASM_OUTPUT_H
|
#endif /* RGBDS_ASM_OUTPUT_H */
|
||||||
|
|||||||
@@ -17,33 +17,37 @@
|
|||||||
#define MAXRPNLEN 1048576
|
#define MAXRPNLEN 1048576
|
||||||
|
|
||||||
struct Expression {
|
struct Expression {
|
||||||
int32_t val; // If the expression's value is known, it's here
|
int32_t nVal; // If the expression's value is known, it's here
|
||||||
char *reason; // Why the expression is not known, if it isn't
|
char *reason; // Why the expression is not known, if it isn't
|
||||||
bool isKnown; // Whether the expression's value is known
|
bool isKnown; // Whether the expression's value is known
|
||||||
bool isSymbol; // Whether the expression represents a symbol
|
bool isSymbol; // Whether the expression represents a symbol
|
||||||
uint8_t *rpn; // Array of bytes serializing the RPN expression
|
uint8_t *tRPN; // Array of bytes serializing the RPN expression
|
||||||
uint32_t rpnCapacity; // Size of the `rpn` buffer
|
uint32_t nRPNCapacity; // Size of the `tRPN` buffer
|
||||||
uint32_t rpnLength; // Used size of the `rpn` buffer
|
uint32_t nRPNLength; // Used size of the `tRPN` buffer
|
||||||
uint32_t rpnPatchSize; // Size the expression will take in the object file
|
uint32_t nRPNPatchSize; // Size the expression will take in the obj file
|
||||||
};
|
};
|
||||||
|
|
||||||
// Determines if an expression is known at assembly time
|
/*
|
||||||
static inline bool rpn_isKnown(struct Expression const *expr)
|
* Determines if an expression is known at assembly time
|
||||||
|
*/
|
||||||
|
static inline bool rpn_isKnown(const struct Expression *expr)
|
||||||
{
|
{
|
||||||
return expr->isKnown;
|
return expr->isKnown;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determines if an expression is a symbol suitable for const diffing
|
/*
|
||||||
|
* Determines if an expression is a symbol suitable for const diffing
|
||||||
|
*/
|
||||||
static inline bool rpn_isSymbol(const struct Expression *expr)
|
static inline bool rpn_isSymbol(const struct Expression *expr)
|
||||||
{
|
{
|
||||||
return expr->isSymbol;
|
return expr->isSymbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rpn_Symbol(struct Expression *expr, char const *symName);
|
void rpn_Symbol(struct Expression *expr, char const *tzSym);
|
||||||
void rpn_Number(struct Expression *expr, uint32_t i);
|
void rpn_Number(struct Expression *expr, uint32_t i);
|
||||||
void rpn_LOGNOT(struct Expression *expr, const struct Expression *src);
|
void rpn_LOGNOT(struct Expression *expr, const struct Expression *src);
|
||||||
struct Symbol const *rpn_SymbolOf(struct Expression const *expr);
|
struct Symbol const *rpn_SymbolOf(struct Expression const *expr);
|
||||||
bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *symName);
|
bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *sym);
|
||||||
void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
||||||
const struct Expression *src1,
|
const struct Expression *src1,
|
||||||
const struct Expression *src2);
|
const struct Expression *src2);
|
||||||
@@ -52,15 +56,11 @@ void rpn_LOW(struct Expression *expr, const struct Expression *src);
|
|||||||
void rpn_ISCONST(struct Expression *expr, const struct Expression *src);
|
void rpn_ISCONST(struct Expression *expr, const struct Expression *src);
|
||||||
void rpn_UNNEG(struct Expression *expr, const struct Expression *src);
|
void rpn_UNNEG(struct Expression *expr, const struct Expression *src);
|
||||||
void rpn_UNNOT(struct Expression *expr, const struct Expression *src);
|
void rpn_UNNOT(struct Expression *expr, const struct Expression *src);
|
||||||
void rpn_BankSymbol(struct Expression *expr, char const *symName);
|
void rpn_BankSymbol(struct Expression *expr, char const *tzSym);
|
||||||
void rpn_BankSection(struct Expression *expr, char const *sectionName);
|
void rpn_BankSection(struct Expression *expr, char const *tzSectionName);
|
||||||
void rpn_BankSelf(struct Expression *expr);
|
void rpn_BankSelf(struct Expression *expr);
|
||||||
void rpn_SizeOfSection(struct Expression *expr, char const *sectionName);
|
|
||||||
void rpn_StartOfSection(struct Expression *expr, char const *sectionName);
|
|
||||||
void rpn_Free(struct Expression *expr);
|
void rpn_Free(struct Expression *expr);
|
||||||
void rpn_CheckHRAM(struct Expression *expr, const struct Expression *src);
|
void rpn_CheckHRAM(struct Expression *expr, const struct Expression *src);
|
||||||
void rpn_CheckRST(struct Expression *expr, const struct Expression *src);
|
void rpn_CheckRST(struct Expression *expr, const struct Expression *src);
|
||||||
void rpn_CheckNBit(struct Expression const *expr, uint8_t n);
|
|
||||||
int32_t rpn_GetConstVal(struct Expression const *expr);
|
|
||||||
|
|
||||||
#endif // RGBDS_ASM_RPN_H
|
#endif /* RGBDS_ASM_RPN_H */
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "linkdefs.h"
|
#include "linkdefs.h"
|
||||||
#include "platform.h" // NONNULL
|
|
||||||
|
|
||||||
extern uint8_t fillByte;
|
extern uint8_t fillByte;
|
||||||
|
|
||||||
@@ -23,12 +22,12 @@ struct Section {
|
|||||||
char *name;
|
char *name;
|
||||||
enum SectionType type;
|
enum SectionType type;
|
||||||
enum SectionModifier modifier;
|
enum SectionModifier modifier;
|
||||||
struct FileStackNode *src; // Where the section was defined
|
struct FileStackNode *src; /* Where the section was defined */
|
||||||
uint32_t fileLine; // Line where the section was defined
|
uint32_t fileLine; /* Line where the section was defined */
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
uint32_t org;
|
uint32_t org;
|
||||||
uint32_t bank;
|
uint32_t bank;
|
||||||
uint8_t align; // Exactly as specified in `ALIGN[]`
|
uint8_t align;
|
||||||
uint16_t alignOfs;
|
uint16_t alignOfs;
|
||||||
struct Section *next;
|
struct Section *next;
|
||||||
struct Patch *patches;
|
struct Patch *patches;
|
||||||
@@ -41,14 +40,14 @@ struct SectionSpec {
|
|||||||
uint16_t alignOfs;
|
uint16_t alignOfs;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct Section *currentSection;
|
struct Section *out_FindSectionByName(const char *name);
|
||||||
|
void out_NewSection(char const *name, uint32_t secttype, uint32_t org,
|
||||||
struct Section *sect_FindSectionByName(char const *name);
|
struct SectionSpec const *attributes,
|
||||||
void sect_NewSection(char const *name, uint32_t secttype, uint32_t org,
|
enum SectionModifier mod);
|
||||||
struct SectionSpec const *attributes, enum SectionModifier mod);
|
void out_SetLoadSection(char const *name, uint32_t secttype, uint32_t org,
|
||||||
void sect_SetLoadSection(char const *name, uint32_t secttype, uint32_t org,
|
struct SectionSpec const *attributes,
|
||||||
struct SectionSpec const *attributes, enum SectionModifier mod);
|
enum SectionModifier mod);
|
||||||
void sect_EndLoadSection(void);
|
void out_EndLoadSection(void);
|
||||||
|
|
||||||
struct Section *sect_GetSymbolSection(void);
|
struct Section *sect_GetSymbolSection(void);
|
||||||
uint32_t sect_GetSymbolOffset(void);
|
uint32_t sect_GetSymbolOffset(void);
|
||||||
@@ -60,23 +59,21 @@ void sect_NextUnionMember(void);
|
|||||||
void sect_EndUnion(void);
|
void sect_EndUnion(void);
|
||||||
void sect_CheckUnionClosed(void);
|
void sect_CheckUnionClosed(void);
|
||||||
|
|
||||||
void sect_AbsByte(uint8_t b);
|
void out_AbsByte(uint8_t b);
|
||||||
void sect_AbsByteGroup(uint8_t const *s, size_t length);
|
void out_AbsByteGroup(uint8_t const *s, int32_t length);
|
||||||
void sect_AbsWordGroup(uint8_t const *s, size_t length);
|
void out_AbsWordGroup(uint8_t const *s, int32_t length);
|
||||||
void sect_AbsLongGroup(uint8_t const *s, size_t length);
|
void out_AbsLongGroup(uint8_t const *s, int32_t length);
|
||||||
void sect_Skip(uint32_t skip, bool ds);
|
void out_Skip(int32_t skip, bool ds);
|
||||||
void sect_String(char const *s);
|
void out_String(char const *s);
|
||||||
void sect_RelByte(struct Expression *expr, uint32_t pcShift);
|
void out_RelByte(struct Expression *expr, uint32_t pcShift);
|
||||||
void sect_RelBytes(uint32_t n, struct Expression *exprs, size_t size);
|
void out_RelBytes(uint32_t n, struct Expression *exprs, size_t size);
|
||||||
void sect_RelWord(struct Expression *expr, uint32_t pcShift);
|
void out_RelWord(struct Expression *expr, uint32_t pcShift);
|
||||||
void sect_RelLong(struct Expression *expr, uint32_t pcShift);
|
void out_RelLong(struct Expression *expr, uint32_t pcShift);
|
||||||
void sect_PCRelByte(struct Expression *expr, uint32_t pcShift);
|
void out_PCRelByte(struct Expression *expr, uint32_t pcShift);
|
||||||
void sect_BinaryFile(char const *s, int32_t startPos);
|
void out_BinaryFile(char const *s, int32_t startPos);
|
||||||
void sect_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length);
|
void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length);
|
||||||
|
|
||||||
void sect_PushSection(void);
|
void out_PushSection(void);
|
||||||
void sect_PopSection(void);
|
void out_PopSection(void);
|
||||||
|
|
||||||
bool sect_IsSizeKnown(struct Section const NONNULL(name));
|
#endif
|
||||||
|
|
||||||
#endif // RGBDS_SECTION_H
|
|
||||||
|
|||||||
@@ -17,13 +17,15 @@
|
|||||||
#include "asm/section.h"
|
#include "asm/section.h"
|
||||||
|
|
||||||
#include "platform.h" // MIN_NB_ELMS
|
#include "platform.h" // MIN_NB_ELMS
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
#define MAXSYMLEN 255
|
#define HASHSIZE (1 << 16)
|
||||||
|
#define MAXSYMLEN 256
|
||||||
|
|
||||||
enum SymbolType {
|
enum SymbolType {
|
||||||
SYM_LABEL,
|
SYM_LABEL,
|
||||||
SYM_EQU,
|
SYM_EQU,
|
||||||
SYM_VAR,
|
SYM_SET,
|
||||||
SYM_MACRO,
|
SYM_MACRO,
|
||||||
SYM_EQUS,
|
SYM_EQUS,
|
||||||
SYM_REF // Forward reference to a label
|
SYM_REF // Forward reference to a label
|
||||||
@@ -32,28 +34,28 @@ enum SymbolType {
|
|||||||
struct Symbol {
|
struct Symbol {
|
||||||
char name[MAXSYMLEN + 1];
|
char name[MAXSYMLEN + 1];
|
||||||
enum SymbolType type;
|
enum SymbolType type;
|
||||||
bool isExported; // Whether the symbol is to be exported
|
bool isExported; /* Whether the symbol is to be exported */
|
||||||
bool isBuiltin; // Whether the symbol is a built-in
|
bool isBuiltin; /* Whether the symbol is a built-in */
|
||||||
struct Section *section;
|
struct Section *section;
|
||||||
struct FileStackNode *src; // Where the symbol was defined
|
struct FileStackNode *src; /* Where the symbol was defined */
|
||||||
uint32_t fileLine; // Line where the symbol was defined
|
uint32_t fileLine; /* Line where the symbol was defined */
|
||||||
|
|
||||||
bool hasCallback;
|
bool hasCallback;
|
||||||
union {
|
union {
|
||||||
// If sym_IsNumeric
|
/* If sym_IsNumeric */
|
||||||
int32_t value;
|
int32_t value;
|
||||||
int32_t (*numCallback)(void);
|
int32_t (*numCallback)(void);
|
||||||
// For SYM_MACRO and SYM_EQUS; TODO: have separate fields
|
/* For SYM_MACRO */
|
||||||
struct {
|
struct {
|
||||||
size_t macroSize;
|
size_t macroSize;
|
||||||
char *macro;
|
char *macro;
|
||||||
};
|
};
|
||||||
// For SYM_EQUS
|
/* For SYM_EQUS, TODO: separate "base" fields from SYM_MACRO */
|
||||||
char const *(*strCallback)(void);
|
char const *(*strCallback)(void); /* For SYM_EQUS */
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32_t ID; // ID of the symbol in the object file (-1 if none)
|
uint32_t ID; /* ID of the symbol in the object file (-1 if none) */
|
||||||
struct Symbol *next; // Next object to output in the object file
|
struct Symbol *next; /* Next object to output in the object file */
|
||||||
};
|
};
|
||||||
|
|
||||||
bool sym_IsPC(struct Symbol const *sym);
|
bool sym_IsPC(struct Symbol const *sym);
|
||||||
@@ -75,12 +77,13 @@ static inline bool sym_IsConstant(struct Symbol const *sym)
|
|||||||
|
|
||||||
return sect && sect->org != (uint32_t)-1;
|
return sect && sect->org != (uint32_t)-1;
|
||||||
}
|
}
|
||||||
return sym->type == SYM_EQU || sym->type == SYM_VAR;
|
return sym->type == SYM_EQU || sym->type == SYM_SET;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool sym_IsNumeric(struct Symbol const *sym)
|
static inline bool sym_IsNumeric(struct Symbol const *sym)
|
||||||
{
|
{
|
||||||
return sym->type == SYM_LABEL || sym->type == SYM_EQU || sym->type == SYM_VAR;
|
return sym->type == SYM_LABEL || sym->type == SYM_EQU
|
||||||
|
|| sym->type == SYM_SET;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool sym_IsLabel(struct Symbol const *sym)
|
static inline bool sym_IsLabel(struct Symbol const *sym)
|
||||||
@@ -98,7 +101,9 @@ static inline bool sym_IsExported(struct Symbol const *sym)
|
|||||||
return sym->isExported;
|
return sym->isExported;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a string equate's value
|
/*
|
||||||
|
* Get a string equate's value
|
||||||
|
*/
|
||||||
static inline char const *sym_GetStringValue(struct Symbol const *sym)
|
static inline char const *sym_GetStringValue(struct Symbol const *sym)
|
||||||
{
|
{
|
||||||
if (sym->hasCallback)
|
if (sym->hasCallback)
|
||||||
@@ -116,17 +121,22 @@ struct Symbol *sym_AddAnonLabel(void);
|
|||||||
void sym_WriteAnonLabelName(char buf[MIN_NB_ELMS(MAXSYMLEN + 1)], uint32_t ofs, bool neg);
|
void sym_WriteAnonLabelName(char buf[MIN_NB_ELMS(MAXSYMLEN + 1)], uint32_t ofs, bool neg);
|
||||||
void sym_Export(char const *symName);
|
void sym_Export(char const *symName);
|
||||||
struct Symbol *sym_AddEqu(char const *symName, int32_t value);
|
struct Symbol *sym_AddEqu(char const *symName, int32_t value);
|
||||||
struct Symbol *sym_RedefEqu(char const *symName, int32_t value);
|
struct Symbol *sym_AddSet(char const *symName, int32_t value);
|
||||||
struct Symbol *sym_AddVar(char const *symName, int32_t value);
|
|
||||||
uint32_t sym_GetPCValue(void);
|
uint32_t sym_GetPCValue(void);
|
||||||
uint32_t sym_GetConstantSymValue(struct Symbol const *sym);
|
uint32_t sym_GetConstantSymValue(struct Symbol const *sym);
|
||||||
uint32_t sym_GetConstantValue(char const *symName);
|
uint32_t sym_GetConstantValue(char const *s);
|
||||||
// Find a symbol by exact name, bypassing expansion checks
|
/*
|
||||||
struct Symbol *sym_FindExactSymbol(char const *symName);
|
* Find a symbol by exact name, bypassing expansion checks
|
||||||
// Find a symbol by exact name; may not be scoped, produces an error if it is
|
*/
|
||||||
struct Symbol *sym_FindUnscopedSymbol(char const *symName);
|
struct Symbol *sym_FindExactSymbol(char const *name);
|
||||||
// Find a symbol, possibly scoped, by name
|
/*
|
||||||
struct Symbol *sym_FindScopedSymbol(char const *symName);
|
* 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 const *sym_GetPC(void);
|
||||||
struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size);
|
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_Ref(char const *symName);
|
||||||
@@ -135,8 +145,8 @@ struct Symbol *sym_RedefString(char const *symName, char const *value);
|
|||||||
void sym_Purge(char const *symName);
|
void sym_Purge(char const *symName);
|
||||||
void sym_Init(time_t now);
|
void sym_Init(time_t now);
|
||||||
|
|
||||||
// Functions to save and restore the current symbol scope.
|
/* Functions to save and restore the current symbol scope. */
|
||||||
char const *sym_GetCurrentSymbolScope(void);
|
char const *sym_GetCurrentSymbolScope(void);
|
||||||
void sym_SetCurrentSymbolScope(char const *newScope);
|
void sym_SetCurrentSymbolScope(char const *newScope);
|
||||||
|
|
||||||
#endif // RGBDS_SYMBOL_H
|
#endif /* RGBDS_SYMBOL_H */
|
||||||
|
|||||||
@@ -11,11 +11,11 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
char const *printChar(int c);
|
uint32_t calchash(const char *s);
|
||||||
|
char const *print(int c);
|
||||||
/*
|
/*
|
||||||
* @return The number of bytes read, or 0 if invalid data was found
|
* @return The number of bytes read, or 0 if invalid data was found
|
||||||
*/
|
*/
|
||||||
size_t readUTF8Char(uint8_t *dest, char const *src);
|
size_t readUTF8Char(uint8_t *dest, char const *src);
|
||||||
|
|
||||||
#endif // RGBDS_UTIL_H
|
#endif /* RGBDS_UTIL_H */
|
||||||
|
|||||||
@@ -13,66 +13,42 @@
|
|||||||
|
|
||||||
extern unsigned int nbErrors;
|
extern unsigned int nbErrors;
|
||||||
|
|
||||||
enum WarningState {
|
|
||||||
WARNING_DEFAULT,
|
|
||||||
WARNING_DISABLED,
|
|
||||||
WARNING_ENABLED,
|
|
||||||
WARNING_ERROR
|
|
||||||
};
|
|
||||||
|
|
||||||
enum WarningID {
|
enum WarningID {
|
||||||
WARNING_ASSERT, // Assertions
|
WARNING_ASSERT, /* Assertions */
|
||||||
WARNING_BACKWARDS_FOR, // `for` loop with backwards range
|
WARNING_BUILTIN_ARG, /* Invalid args to builtins */
|
||||||
WARNING_BUILTIN_ARG, // Invalid args to builtins
|
WARNING_CHARMAP_REDEF, /* Charmap entry re-definition */
|
||||||
WARNING_CHARMAP_REDEF, // Charmap entry re-definition
|
WARNING_DIV, /* Division undefined behavior */
|
||||||
WARNING_DIV, // Division undefined behavior
|
WARNING_EMPTY_DATA_DIRECTIVE, /* `db`, `dw` or `dl` directive without data in ROM */
|
||||||
WARNING_EMPTY_DATA_DIRECTIVE, // `db`, `dw` or `dl` directive without data in ROM
|
WARNING_EMPTY_MACRO_ARG, /* Empty macro argument */
|
||||||
WARNING_EMPTY_MACRO_ARG, // Empty macro argument
|
WARNING_EMPTY_STRRPL, /* Empty second argument in `STRRPL` */
|
||||||
WARNING_EMPTY_STRRPL, // Empty second argument in `STRRPL`
|
WARNING_LARGE_CONSTANT, /* Constants too large */
|
||||||
WARNING_LARGE_CONSTANT, // Constants too large
|
WARNING_LONG_STR, /* String too long for internal buffers */
|
||||||
WARNING_LONG_STR, // String too long for internal buffers
|
WARNING_MACRO_SHIFT, /* Shift past available arguments in macro */
|
||||||
WARNING_MACRO_SHIFT, // Shift past available arguments in macro
|
WARNING_NESTED_COMMENT, /* Comment-start delimiter in a block comment */
|
||||||
WARNING_NESTED_COMMENT, // Comment-start delimiter in a block comment
|
WARNING_OBSOLETE, /* Obsolete things */
|
||||||
WARNING_OBSOLETE, // Obsolete things
|
WARNING_SHIFT, /* Shifting undefined behavior */
|
||||||
WARNING_SHIFT, // Shifting undefined behavior
|
WARNING_SHIFT_AMOUNT, /* Strange shift amount */
|
||||||
WARNING_SHIFT_AMOUNT, // Strange shift amount
|
WARNING_TRUNCATION, /* Implicit truncation loses some bits */
|
||||||
WARNING_UNMAPPED_CHAR, // Character without charmap entry
|
WARNING_USER, /* User warnings */
|
||||||
WARNING_USER, // User warnings
|
|
||||||
|
|
||||||
NB_PLAIN_WARNINGS,
|
NB_WARNINGS,
|
||||||
|
|
||||||
// Warnings past this point are "parametric" warnings, only mapping to a single flag
|
/* Warnings past this point are "meta" warnings */
|
||||||
#define PARAM_WARNINGS_START NB_PLAIN_WARNINGS
|
WARNING_ALL = NB_WARNINGS,
|
||||||
// Treating string as number may lose some bits
|
|
||||||
WARNING_NUMERIC_STRING_1 = PARAM_WARNINGS_START,
|
|
||||||
WARNING_NUMERIC_STRING_2,
|
|
||||||
// Implicit truncation loses some bits
|
|
||||||
WARNING_TRUNCATION_1,
|
|
||||||
WARNING_TRUNCATION_2,
|
|
||||||
|
|
||||||
NB_PLAIN_AND_PARAM_WARNINGS,
|
|
||||||
#define NB_PARAM_WARNINGS (NB_PLAIN_AND_PARAM_WARNINGS - PARAM_WARNINGS_START)
|
|
||||||
|
|
||||||
// Warnings past this point are "meta" warnings
|
|
||||||
#define META_WARNINGS_START NB_PLAIN_AND_PARAM_WARNINGS
|
|
||||||
WARNING_ALL = META_WARNINGS_START,
|
|
||||||
WARNING_EXTRA,
|
WARNING_EXTRA,
|
||||||
WARNING_EVERYTHING,
|
WARNING_EVERYTHING,
|
||||||
|
|
||||||
NB_WARNINGS,
|
NB_WARNINGS_ALL
|
||||||
#define NB_META_WARNINGS (NB_WARNINGS - META_WARNINGS_START)
|
#define NB_META_WARNINGS (NB_WARNINGS_ALL - NB_WARNINGS)
|
||||||
};
|
};
|
||||||
|
|
||||||
extern enum WarningState warningStates[NB_PLAIN_AND_PARAM_WARNINGS];
|
void processWarningFlag(char const *flag);
|
||||||
extern bool warningsAreErrors;
|
|
||||||
|
|
||||||
void processWarningFlag(char *flag);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Used to warn the user about problems that don't prevent the generation of
|
* Used to warn the user about problems that don't prevent the generation of
|
||||||
* valid code.
|
* valid code.
|
||||||
*/
|
*/
|
||||||
void warning(enum WarningID id, char const *fmt, ...) format_(printf, 2, 3);
|
void warning(enum WarningID id, const char *fmt, ...) format_(printf, 2, 3);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Used for errors that compromise the whole assembly process by affecting the
|
* Used for errors that compromise the whole assembly process by affecting the
|
||||||
@@ -81,7 +57,7 @@ void warning(enum WarningID id, char const *fmt, ...) format_(printf, 2, 3);
|
|||||||
* It is also used when the assembler goes into an invalid state (for example,
|
* It is also used when the assembler goes into an invalid state (for example,
|
||||||
* when it fails to allocate memory).
|
* when it fails to allocate memory).
|
||||||
*/
|
*/
|
||||||
_Noreturn void fatalerror(char const *fmt, ...) format_(printf, 1, 2);
|
_Noreturn void fatalerror(const char *fmt, ...) format_(printf, 1, 2);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Used for errors that make it impossible to assemble correctly, but don't
|
* Used for errors that make it impossible to assemble correctly, but don't
|
||||||
@@ -89,6 +65,6 @@ _Noreturn void fatalerror(char const *fmt, ...) format_(printf, 1, 2);
|
|||||||
* get a list of all errors at the end, making it easier to fix all of them at
|
* get a list of all errors at the end, making it easier to fix all of them at
|
||||||
* once.
|
* once.
|
||||||
*/
|
*/
|
||||||
void error(char const *fmt, ...) format_(printf, 1, 2);
|
void error(const char *fmt, ...) format_(printf, 1, 2);
|
||||||
|
|
||||||
#endif // WARNING_H
|
#endif
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
* Allocator adaptor that interposes construct() calls to convert value-initialization
|
|
||||||
* (which is what you get with e.g. `vector::resize`) into default-initialization (which does not
|
|
||||||
* zero out non-class types).
|
|
||||||
* From
|
|
||||||
* https://stackoverflow.com/questions/21028299/is-this-behavior-of-vectorresizesize-type-n-under-c11-and-boost-container/21028912#21028912
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef DEFAULT_INIT_ALLOC_H
|
|
||||||
#define DEFAULT_INIT_ALLOC_H
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
template<typename T, typename A = std::allocator<T>>
|
|
||||||
class default_init_allocator : public A {
|
|
||||||
using a_t = std::allocator_traits<A>;
|
|
||||||
public:
|
|
||||||
template<typename U>
|
|
||||||
struct rebind {
|
|
||||||
using other = default_init_allocator<U, typename a_t::template rebind_alloc<U>>;
|
|
||||||
};
|
|
||||||
|
|
||||||
using A::A; // Inherit the allocator's constructors
|
|
||||||
|
|
||||||
template<typename U>
|
|
||||||
void construct(U *ptr) noexcept(std::is_nothrow_default_constructible_v<U>) {
|
|
||||||
::new (static_cast<void *>(ptr)) U;
|
|
||||||
}
|
|
||||||
template<typename U, typename... Args>
|
|
||||||
void construct(U *ptr, Args &&...args) {
|
|
||||||
a_t::construct(static_cast<A &>(*this), ptr, std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
using DefaultInitVec = std::vector<T, default_init_allocator<T>>;
|
|
||||||
|
|
||||||
#endif // DEFAULT_INIT_ALLOC_H
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of RGBDS.
|
|
||||||
*
|
|
||||||
* Copyright (c) 1997-2021, RGBDS contributors.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef RGBDS_ERROR_H
|
|
||||||
#define RGBDS_ERROR_H
|
|
||||||
|
|
||||||
#include "helpers.h"
|
|
||||||
#include "platform.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void warn(char const NONNULL(fmt), ...) format_(printf, 1, 2);
|
|
||||||
void warnx(char const NONNULL(fmt), ...) format_(printf, 1, 2);
|
|
||||||
|
|
||||||
_Noreturn void err(char const NONNULL(fmt), ...) format_(printf, 1, 2);
|
|
||||||
_Noreturn void errx(char const NONNULL(fmt), ...) format_(printf, 1, 2);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // RGBDS_ERROR_H
|
|
||||||
44
include/extern/err.h
vendored
Normal file
44
include/extern/err.h
vendored
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of RGBDS.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1997-2018, RGBDS contributors.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EXTERN_ERR_H
|
||||||
|
#define EXTERN_ERR_H
|
||||||
|
|
||||||
|
#ifdef ERR_IN_LIBC
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
|
||||||
|
#else /* ERR_IN_LIBC */
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include "helpers.h"
|
||||||
|
|
||||||
|
#define warn rgbds_warn
|
||||||
|
#define vwarn rgbds_vwarn
|
||||||
|
#define warnx rgbds_warnx
|
||||||
|
#define vwarnx rgbds_vwarnx
|
||||||
|
|
||||||
|
#define err rgbds_err
|
||||||
|
#define verr rgbds_verr
|
||||||
|
#define errx rgbds_errx
|
||||||
|
#define verrx rgbds_verrx
|
||||||
|
|
||||||
|
void warn(const char *fmt, ...) format_(printf, 1, 2);
|
||||||
|
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);
|
||||||
|
|
||||||
|
#endif /* ERR_IN_LIBC */
|
||||||
|
|
||||||
|
#endif /* EXTERN_ERR_H */
|
||||||
13
include/extern/getopt.h
vendored
13
include/extern/getopt.h
vendored
@@ -26,29 +26,20 @@
|
|||||||
#ifndef RGBDS_EXTERN_GETOPT_H
|
#ifndef RGBDS_EXTERN_GETOPT_H
|
||||||
#define RGBDS_EXTERN_GETOPT_H
|
#define RGBDS_EXTERN_GETOPT_H
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern char *musl_optarg;
|
extern char *musl_optarg;
|
||||||
extern int musl_optind, musl_opterr, musl_optopt, musl_optreset;
|
extern int musl_optind, musl_opterr, musl_optopt, musl_optreset;
|
||||||
|
|
||||||
struct option {
|
struct option {
|
||||||
char const *name;
|
const char *name;
|
||||||
int has_arg;
|
int has_arg;
|
||||||
int *flag;
|
int *flag;
|
||||||
int val;
|
int val;
|
||||||
};
|
};
|
||||||
|
|
||||||
int musl_getopt_long_only(int argc, char **argv, char const *optstring,
|
int musl_getopt_long_only(int, char **, const char *, const struct option *, int *);
|
||||||
const struct option *longopts, int *idx);
|
|
||||||
|
|
||||||
#define no_argument 0
|
#define no_argument 0
|
||||||
#define required_argument 1
|
#define required_argument 1
|
||||||
#define optional_argument 2
|
#define optional_argument 2
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} // extern "C"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
2
include/extern/utf8decoder.h
vendored
2
include/extern/utf8decoder.h
vendored
@@ -11,4 +11,4 @@
|
|||||||
|
|
||||||
uint32_t decode(uint32_t *state, uint32_t *codep, uint8_t byte);
|
uint32_t decode(uint32_t *state, uint32_t *codep, uint8_t byte);
|
||||||
|
|
||||||
#endif // EXTERN_UTF8DECODER_H
|
#endif /* EXTERN_UTF8DECODER_H */
|
||||||
|
|||||||
36
include/gfx/gb.h
Normal file
36
include/gfx/gb.h
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of RGBDS.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2018, stag019 and RGBDS contributors.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RGBDS_GFX_GB_H
|
||||||
|
#define RGBDS_GFX_GB_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "gfx/main.h"
|
||||||
|
|
||||||
|
#define XFLIP 0x40
|
||||||
|
#define YFLIP 0x20
|
||||||
|
|
||||||
|
void raw_to_gb(const struct RawIndexedImage *raw_image, struct GBImage *gb);
|
||||||
|
void output_file(const struct Options *opts, const struct GBImage *gb);
|
||||||
|
int get_tile_index(uint8_t *tile, uint8_t **tiles, int num_tiles,
|
||||||
|
int tile_size);
|
||||||
|
uint8_t reverse_bits(uint8_t b);
|
||||||
|
void xflip(uint8_t *tile, uint8_t *tile_xflip, int tile_size);
|
||||||
|
void yflip(uint8_t *tile, uint8_t *tile_yflip, int tile_size);
|
||||||
|
int get_mirrored_tile_index(uint8_t *tile, uint8_t **tiles, int num_tiles,
|
||||||
|
int tile_size, int *flags);
|
||||||
|
void create_mapfiles(const struct Options *opts, struct GBImage *gb,
|
||||||
|
struct Mapfile *tilemap, struct Mapfile *attrmap);
|
||||||
|
void output_tilemap_file(const struct Options *opts,
|
||||||
|
const struct Mapfile *tilemap);
|
||||||
|
void output_attrmap_file(const struct Options *opts,
|
||||||
|
const struct Mapfile *attrmap);
|
||||||
|
void output_palette_file(const struct Options *opts,
|
||||||
|
const struct RawIndexedImage *raw_image);
|
||||||
|
|
||||||
|
#endif
|
||||||
91
include/gfx/main.h
Normal file
91
include/gfx/main.h
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of RGBDS.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2018, stag019 and RGBDS contributors.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RGBDS_GFX_MAIN_H
|
||||||
|
#define RGBDS_GFX_MAIN_H
|
||||||
|
|
||||||
|
#include <png.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "extern/err.h"
|
||||||
|
|
||||||
|
struct Options {
|
||||||
|
bool debug;
|
||||||
|
bool verbose;
|
||||||
|
bool hardfix;
|
||||||
|
bool fix;
|
||||||
|
bool horizontal;
|
||||||
|
bool mirror;
|
||||||
|
bool unique;
|
||||||
|
bool colorcurve;
|
||||||
|
int trim;
|
||||||
|
char *tilemapfile;
|
||||||
|
bool tilemapout;
|
||||||
|
char *attrmapfile;
|
||||||
|
bool attrmapout;
|
||||||
|
char *palfile;
|
||||||
|
bool palout;
|
||||||
|
char *outfile;
|
||||||
|
char *infile;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RGBColor {
|
||||||
|
uint8_t red;
|
||||||
|
uint8_t green;
|
||||||
|
uint8_t blue;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ImageOptions {
|
||||||
|
bool horizontal;
|
||||||
|
int trim;
|
||||||
|
char *tilemapfile;
|
||||||
|
bool tilemapout;
|
||||||
|
char *attrmapfile;
|
||||||
|
bool attrmapout;
|
||||||
|
char *palfile;
|
||||||
|
bool palout;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PNGImage {
|
||||||
|
png_struct *png;
|
||||||
|
png_info *info;
|
||||||
|
|
||||||
|
png_byte **data;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
png_byte depth;
|
||||||
|
png_byte type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RawIndexedImage {
|
||||||
|
uint8_t **data;
|
||||||
|
struct RGBColor *palette;
|
||||||
|
int num_colors;
|
||||||
|
unsigned int width;
|
||||||
|
unsigned int height;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GBImage {
|
||||||
|
uint8_t *data;
|
||||||
|
int size;
|
||||||
|
bool horizontal;
|
||||||
|
int trim;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Mapfile {
|
||||||
|
uint8_t *data;
|
||||||
|
int size;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern int depth, colors;
|
||||||
|
|
||||||
|
#include "gfx/makepng.h"
|
||||||
|
#include "gfx/gb.h"
|
||||||
|
|
||||||
|
#endif /* RGBDS_GFX_MAIN_H */
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of RGBDS.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2022, Eldred Habert and RGBDS contributors.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef RGBDS_GFX_MAIN_HPP
|
|
||||||
#define RGBDS_GFX_MAIN_HPP
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "helpers.h"
|
|
||||||
|
|
||||||
#include "gfx/rgba.hpp"
|
|
||||||
|
|
||||||
struct Options {
|
|
||||||
uint16_t reversedWidth = 0; // -r, in tiles
|
|
||||||
bool reverse() const { return reversedWidth != 0; }
|
|
||||||
|
|
||||||
bool useColorCurve = false; // -C
|
|
||||||
bool allowMirroring = false; // -m
|
|
||||||
bool allowDedup = false; // -u
|
|
||||||
bool columnMajor = false; // -Z, previously -h
|
|
||||||
uint8_t verbosity = 0; // -v
|
|
||||||
|
|
||||||
std::string attrmap{}; // -a, -A
|
|
||||||
std::array<uint8_t, 2> baseTileIDs{0, 0}; // -b
|
|
||||||
enum {
|
|
||||||
NO_SPEC,
|
|
||||||
EXPLICIT,
|
|
||||||
EMBEDDED,
|
|
||||||
} palSpecType = NO_SPEC; // -c
|
|
||||||
std::vector<std::array<Rgba, 4>> palSpec{};
|
|
||||||
uint8_t bitDepth = 2; // -d
|
|
||||||
struct {
|
|
||||||
uint16_t left;
|
|
||||||
uint16_t top;
|
|
||||||
uint16_t width;
|
|
||||||
uint16_t height;
|
|
||||||
} inputSlice{0, 0, 0, 0}; // -L (margins in clockwise order, like CSS)
|
|
||||||
std::array<uint16_t, 2> maxNbTiles{UINT16_MAX, 0}; // -N
|
|
||||||
uint8_t nbPalettes = 8; // -n
|
|
||||||
std::string output{}; // -o
|
|
||||||
std::string palettes{}; // -p, -P
|
|
||||||
std::string palmap{}; // -q, -Q
|
|
||||||
uint8_t nbColorsPerPal = 0; // -s; 0 means "auto" = 1 << bitDepth;
|
|
||||||
std::string tilemap{}; // -t, -T
|
|
||||||
uint64_t trim = 0; // -x
|
|
||||||
|
|
||||||
std::string input{}; // positional arg
|
|
||||||
|
|
||||||
static constexpr uint8_t VERB_NONE = 0; // Normal, no extra output
|
|
||||||
static constexpr uint8_t VERB_CFG = 1; // Print configuration after parsing options
|
|
||||||
static constexpr uint8_t VERB_LOG_ACT = 2; // Log actions before doing them
|
|
||||||
static constexpr uint8_t VERB_INTERM = 3; // Print some intermediate results
|
|
||||||
static constexpr uint8_t VERB_DEBUG = 4; // Internals are logged
|
|
||||||
static constexpr uint8_t VERB_UNMAPPED = 5; // Unused so far
|
|
||||||
static constexpr uint8_t VERB_VVVVVV = 6; // What, can't I have a little fun?
|
|
||||||
format_(printf, 3, 4) void verbosePrint(uint8_t level, char const *fmt, ...) const;
|
|
||||||
|
|
||||||
mutable bool hasTransparentPixels = false;
|
|
||||||
uint8_t maxOpaqueColors() const { return nbColorsPerPal - hasTransparentPixels; }
|
|
||||||
};
|
|
||||||
|
|
||||||
extern Options options;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Prints the error count, and exits with failure
|
|
||||||
*/
|
|
||||||
[[noreturn]] void giveUp();
|
|
||||||
/*
|
|
||||||
* Prints a warning, and does not change the error count
|
|
||||||
*/
|
|
||||||
void warning(char const *fmt, ...);
|
|
||||||
/*
|
|
||||||
* Prints an error, and increments the error count
|
|
||||||
*/
|
|
||||||
void error(char const *fmt, ...);
|
|
||||||
/*
|
|
||||||
* Prints a fatal error, increments the error count, and gives up
|
|
||||||
*/
|
|
||||||
[[noreturn]] void fatal(char const *fmt, ...);
|
|
||||||
|
|
||||||
struct Palette {
|
|
||||||
// An array of 4 GBC-native (RGB555) colors
|
|
||||||
std::array<uint16_t, 4> colors{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX};
|
|
||||||
|
|
||||||
void addColor(uint16_t color);
|
|
||||||
uint8_t indexOf(uint16_t color) const;
|
|
||||||
uint16_t &operator[](size_t index) { return colors[index]; }
|
|
||||||
uint16_t const &operator[](size_t index) const { return colors[index]; }
|
|
||||||
|
|
||||||
decltype(colors)::iterator begin();
|
|
||||||
decltype(colors)::iterator end();
|
|
||||||
decltype(colors)::const_iterator begin() const;
|
|
||||||
decltype(colors)::const_iterator end() const;
|
|
||||||
|
|
||||||
uint8_t size() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
template<typename T, T... i>
|
|
||||||
static constexpr auto flipTable(std::integer_sequence<T, i...>) {
|
|
||||||
return std::array{[](uint8_t byte) {
|
|
||||||
// To flip all the bits, we'll flip both nibbles, then each nibble half, etc.
|
|
||||||
byte = (byte & 0b0000'1111) << 4 | (byte & 0b1111'0000) >> 4;
|
|
||||||
byte = (byte & 0b0011'0011) << 2 | (byte & 0b1100'1100) >> 2;
|
|
||||||
byte = (byte & 0b0101'0101) << 1 | (byte & 0b1010'1010) >> 1;
|
|
||||||
return byte;
|
|
||||||
}(i)...};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Flipping tends to happen fairly often, so take a bite out of dcache to speed it up
|
|
||||||
static constexpr auto flipTable = detail::flipTable(std::make_integer_sequence<uint16_t, 256>());
|
|
||||||
|
|
||||||
#endif // RGBDS_GFX_MAIN_HPP
|
|
||||||
21
include/gfx/makepng.h
Normal file
21
include/gfx/makepng.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of RGBDS.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2018, stag019 and RGBDS contributors.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RGBDS_GFX_PNG_H
|
||||||
|
#define RGBDS_GFX_PNG_H
|
||||||
|
|
||||||
|
#include "gfx/main.h"
|
||||||
|
|
||||||
|
struct RawIndexedImage *input_png_file(const struct Options *opts,
|
||||||
|
struct ImageOptions *png_options);
|
||||||
|
void output_png_file(const struct Options *opts,
|
||||||
|
const struct ImageOptions *png_options,
|
||||||
|
const struct RawIndexedImage *raw_image);
|
||||||
|
void destroy_raw_image(struct RawIndexedImage **raw_image_ptr_ptr);
|
||||||
|
|
||||||
|
#endif /* RGBDS_GFX_PNG_H */
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of RGBDS.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2022, Eldred Habert and RGBDS contributors.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef RGBDS_GFX_PAL_PACKING_HPP
|
|
||||||
#define RGBDS_GFX_PAL_PACKING_HPP
|
|
||||||
|
|
||||||
#include <tuple>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "defaultinitalloc.hpp"
|
|
||||||
|
|
||||||
#include "gfx/main.hpp"
|
|
||||||
|
|
||||||
struct Palette;
|
|
||||||
class ProtoPalette;
|
|
||||||
|
|
||||||
namespace packing {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns which palette each proto-palette maps to, and how many palettes are necessary
|
|
||||||
*/
|
|
||||||
std::tuple<DefaultInitVec<size_t>, size_t>
|
|
||||||
overloadAndRemove(std::vector<ProtoPalette> const &protoPalettes);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // RGBDS_GFX_PAL_PACKING_HPP
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of RGBDS.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2022, Eldred Habert and RGBDS contributors.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef RGBDS_GFX_PAL_SORTING_HPP
|
|
||||||
#define RGBDS_GFX_PAL_SORTING_HPP
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <optional>
|
|
||||||
#include <png.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "gfx/rgba.hpp"
|
|
||||||
|
|
||||||
struct Palette;
|
|
||||||
|
|
||||||
namespace sorting {
|
|
||||||
|
|
||||||
void indexed(std::vector<Palette> &palettes, int palSize, png_color const *palRGB,
|
|
||||||
png_byte *palAlpha);
|
|
||||||
void grayscale(std::vector<Palette> &palettes,
|
|
||||||
std::array<std::optional<Rgba>, 0x8001> const &colors);
|
|
||||||
void rgb(std::vector<Palette> &palettes);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // RGBDS_GFX_PAL_SORTING_HPP
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of RGBDS.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2022, Eldred Habert and RGBDS contributors.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef RGBDS_GFX_PAL_SPEC_HPP
|
|
||||||
#define RGBDS_GFX_PAL_SPEC_HPP
|
|
||||||
|
|
||||||
void parseInlinePalSpec(char const * const arg);
|
|
||||||
void parseExternalPalSpec(char const *arg);
|
|
||||||
|
|
||||||
#endif // RGBDS_GFX_PAL_SPEC_HPP
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of RGBDS.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2022, Eldred Habert and RGBDS contributors.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef RGBDS_GFX_CONVERT_HPP
|
|
||||||
#define RGBDS_GFX_CONVERT_HPP
|
|
||||||
|
|
||||||
void process();
|
|
||||||
|
|
||||||
#endif // RGBDS_GFX_CONVERT_HPP
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of RGBDS.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2022, Eldred Habert and RGBDS contributors.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef RGBDS_GFX_PROTO_PALETTE_HPP
|
|
||||||
#define RGBDS_GFX_PROTO_PALETTE_HPP
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <array>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
class ProtoPalette {
|
|
||||||
// Up to 4 colors, sorted, and where SIZE_MAX means the slot is empty
|
|
||||||
// (OK because it's not a valid color index)
|
|
||||||
// Sorting is done on the raw numerical values to lessen `compare`'s complexity
|
|
||||||
std::array<uint16_t, 4> _colorIndices{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX};
|
|
||||||
|
|
||||||
public:
|
|
||||||
/*
|
|
||||||
* Adds the specified color to the set
|
|
||||||
* Returns false if the set is full
|
|
||||||
*/
|
|
||||||
bool add(uint16_t color);
|
|
||||||
|
|
||||||
enum ComparisonResult {
|
|
||||||
NEITHER,
|
|
||||||
WE_BIGGER,
|
|
||||||
THEY_BIGGER = -1,
|
|
||||||
};
|
|
||||||
ComparisonResult compare(ProtoPalette const &other) const;
|
|
||||||
|
|
||||||
size_t size() const;
|
|
||||||
bool empty() const;
|
|
||||||
|
|
||||||
decltype(_colorIndices)::const_iterator begin() const;
|
|
||||||
decltype(_colorIndices)::const_iterator end() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // RGBDS_GFX_PROTO_PALETTE_HPP
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of RGBDS.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2022, Eldred Habert and RGBDS contributors.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef RGBDS_GFX_REVERSE_HPP
|
|
||||||
#define RGBDS_GFX_REVERSE_HPP
|
|
||||||
|
|
||||||
void reverse();
|
|
||||||
|
|
||||||
#endif // RGBDS_GFX_REVERSE_HPP
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of RGBDS.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2022, Eldred Habert and RGBDS contributors.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef RGBDS_GFX_RGBA_HPP
|
|
||||||
#define RGBDS_GFX_RGBA_HPP
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
struct Rgba {
|
|
||||||
uint8_t red;
|
|
||||||
uint8_t green;
|
|
||||||
uint8_t blue;
|
|
||||||
uint8_t alpha;
|
|
||||||
|
|
||||||
constexpr Rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
|
||||||
: red(r), green(g), blue(b), alpha(a) {}
|
|
||||||
/*
|
|
||||||
* Constructs the color from a "packed" RGBA representation (0xRRGGBBAA)
|
|
||||||
*/
|
|
||||||
explicit constexpr Rgba(uint32_t rgba = 0)
|
|
||||||
: red(rgba >> 24), green(rgba >> 16), blue(rgba >> 8), alpha(rgba) {}
|
|
||||||
|
|
||||||
static constexpr Rgba fromCGBColor(uint16_t cgbColor) {
|
|
||||||
constexpr auto _5to8 = [](uint8_t fiveBpp) -> uint8_t {
|
|
||||||
fiveBpp &= 0b11111; // For caller's convenience
|
|
||||||
return fiveBpp << 3 | fiveBpp >> 2;
|
|
||||||
};
|
|
||||||
return {_5to8(cgbColor), _5to8(cgbColor >> 5), _5to8(cgbColor >> 10),
|
|
||||||
(uint8_t)(cgbColor & 0x8000 ? 0x00 : 0xFF)};
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns this RGBA as a 32-bit number that can be printed in hex (`%08x`) to yield its CSS
|
|
||||||
* representation
|
|
||||||
*/
|
|
||||||
uint32_t toCSS() const {
|
|
||||||
auto shl = [](uint8_t val, unsigned shift) { return static_cast<uint32_t>(val) << shift; };
|
|
||||||
return shl(red, 24) | shl(green, 16) | shl(blue, 8) | shl(alpha, 0);
|
|
||||||
}
|
|
||||||
friend bool operator!=(Rgba const &lhs, Rgba const &rhs) { return lhs.toCSS() != rhs.toCSS(); }
|
|
||||||
|
|
||||||
/*
|
|
||||||
* CGB colors are RGB555, so we use bit 15 to signify that the color is transparent instead
|
|
||||||
* Since the rest of the bits don't matter then, we return 0x8000 exactly.
|
|
||||||
*/
|
|
||||||
static constexpr uint16_t transparent = 0b1'00000'00000'00000;
|
|
||||||
|
|
||||||
static constexpr uint8_t transparency_threshold = 0x10;
|
|
||||||
bool isTransparent() const { return alpha < transparency_threshold; }
|
|
||||||
static constexpr uint8_t opacity_threshold = 0xF0;
|
|
||||||
bool isOpaque() const { return alpha >= opacity_threshold; }
|
|
||||||
/*
|
|
||||||
* Computes the equivalent CGB color, respects the color curve depending on options
|
|
||||||
*/
|
|
||||||
uint16_t cgbColor() const;
|
|
||||||
|
|
||||||
bool isGray() const { return red == green && green == blue; }
|
|
||||||
uint8_t grayIndex() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // RGBDS_GFX_RGBA_HPP
|
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Generic hashmap implementation (C++ templates are calling...)
|
/* Generic hashmap implementation (C++ templates are calling...) */
|
||||||
#ifndef RGBDS_LINK_HASHMAP_H
|
#ifndef RGBDS_LINK_HASHMAP_H
|
||||||
#define RGBDS_LINK_HASHMAP_H
|
#define RGBDS_LINK_HASHMAP_H
|
||||||
|
|
||||||
@@ -18,10 +18,10 @@
|
|||||||
static_assert(HALF_HASH_NB_BITS * 2 == HASH_NB_BITS, "");
|
static_assert(HALF_HASH_NB_BITS * 2 == HASH_NB_BITS, "");
|
||||||
#define HASHMAP_NB_BUCKETS (1 << HALF_HASH_NB_BITS)
|
#define HASHMAP_NB_BUCKETS (1 << HALF_HASH_NB_BITS)
|
||||||
|
|
||||||
// HashMapEntry is internal, please do not attempt to use it
|
/* HashMapEntry is internal, please do not attempt to use it */
|
||||||
typedef struct HashMapEntry *HashMap[HASHMAP_NB_BUCKETS];
|
typedef struct HashMapEntry *HashMap[HASHMAP_NB_BUCKETS];
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Adds an element to a hashmap.
|
* Adds an element to a hashmap.
|
||||||
* @warning Adding a new element with an already-present key will not cause an
|
* @warning Adding a new element with an already-present key will not cause an
|
||||||
* error, this must be handled externally.
|
* error, this must be handled externally.
|
||||||
@@ -29,11 +29,21 @@ typedef struct HashMapEntry *HashMap[HASHMAP_NB_BUCKETS];
|
|||||||
* @param map The HashMap to add the element to
|
* @param map The HashMap to add the element to
|
||||||
* @param key The key with which the element will be stored and retrieved
|
* @param key The key with which the element will be stored and retrieved
|
||||||
* @param element The element to add
|
* @param element The element to add
|
||||||
* @return A pointer to the pointer to the element.
|
* @return True if a collision occurred (for statistics)
|
||||||
*/
|
*/
|
||||||
void **hash_AddElement(HashMap map, char const *key, void *element);
|
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.
|
* Removes an element from a hashmap.
|
||||||
* @param map The HashMap to remove the element from
|
* @param map The HashMap to remove the element from
|
||||||
* @param key The key to search the element with
|
* @param key The key to search the element with
|
||||||
@@ -41,15 +51,7 @@ void **hash_AddElement(HashMap map, char const *key, void *element);
|
|||||||
*/
|
*/
|
||||||
bool hash_RemoveElement(HashMap map, char const *key);
|
bool hash_RemoveElement(HashMap map, char const *key);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Finds an element in a hashmap, and returns a pointer to its value field.
|
|
||||||
* @param map The map to consider the elements of
|
|
||||||
* @param key The key to search an element for
|
|
||||||
* @return A pointer to the pointer to the element, or NULL if not found.
|
|
||||||
*/
|
|
||||||
void **hash_GetNode(HashMap const map, char const *key);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Finds an element in a hashmap.
|
* Finds an element in a hashmap.
|
||||||
* @param map The map to consider the elements of
|
* @param map The map to consider the elements of
|
||||||
* @param key The key to search an element for
|
* @param key The key to search an element for
|
||||||
@@ -58,7 +60,7 @@ void **hash_GetNode(HashMap const map, char const *key);
|
|||||||
*/
|
*/
|
||||||
void *hash_GetElement(HashMap const map, char const *key);
|
void *hash_GetElement(HashMap const map, char const *key);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Executes a function on each element in a hashmap.
|
* Executes a function on each element in a hashmap.
|
||||||
* @param map The map to consider the elements of
|
* @param map The map to consider the elements of
|
||||||
* @param func The function to run. The first argument will be the element,
|
* @param func The function to run. The first argument will be the element,
|
||||||
@@ -67,11 +69,11 @@ void *hash_GetElement(HashMap const map, char const *key);
|
|||||||
*/
|
*/
|
||||||
void hash_ForEach(HashMap const map, void (*func)(void *, void *), void *arg);
|
void hash_ForEach(HashMap const map, void (*func)(void *, void *), void *arg);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Cleanly empties a hashmap from its contents.
|
* Cleanly empties a hashmap from its contents.
|
||||||
* This does not `free` the data structure itself!
|
* This does not `free` the data structure itself!
|
||||||
* @param map The map to empty
|
* @param map The map to empty
|
||||||
*/
|
*/
|
||||||
void hash_EmptyMap(HashMap map);
|
void hash_EmptyMap(HashMap map);
|
||||||
|
|
||||||
#endif // RGBDS_LINK_HASHMAP_H
|
#endif /* RGBDS_LINK_HASHMAP_H */
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
#ifdef __GNUC__ // GCC or compatible
|
#ifdef __GNUC__ // GCC or compatible
|
||||||
#define format_(archetype, str_index, first_arg) \
|
#define format_(archetype, str_index, first_arg) \
|
||||||
__attribute__ ((format (archetype, str_index, first_arg)))
|
__attribute__ ((format (archetype, str_index, first_arg)))
|
||||||
#define attr_(...) __attribute__ ((__VA_ARGS__))
|
|
||||||
// In release builds, define "unreachable" as such, but trap in debug builds
|
// In release builds, define "unreachable" as such, but trap in debug builds
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
#define unreachable_ __builtin_unreachable
|
#define unreachable_ __builtin_unreachable
|
||||||
@@ -28,10 +27,9 @@
|
|||||||
#else
|
#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 format_(archetype, str_index, first_arg)
|
||||||
#define attr_(...)
|
|
||||||
// This seems to generate similar code to __builtin_unreachable, despite different semantics
|
// This seems to generate similar code to __builtin_unreachable, despite different semantics
|
||||||
// Note that executing this is undefined behavior (declared _Noreturn, but does return)
|
// Note that executing this is undefined behavior (declared _Noreturn, but does return)
|
||||||
static inline _Noreturn void unreachable_(void) {}
|
static inline _Noreturn unreachable_(void) {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Use builtins whenever possible, and shim them otherwise
|
// Use builtins whenever possible, and shim them otherwise
|
||||||
@@ -61,6 +59,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
// FIXME: these are rarely used, and need testing...
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
static inline int ctz(unsigned int x)
|
static inline int ctz(unsigned int x)
|
||||||
{
|
{
|
||||||
@@ -89,8 +88,4 @@
|
|||||||
#define STR(x) #x
|
#define STR(x) #x
|
||||||
#define EXPAND_AND_STR(x) STR(x)
|
#define EXPAND_AND_STR(x) STR(x)
|
||||||
|
|
||||||
// Obtaining the size of an array; `arr` must be an expression, not a type!
|
#endif /* HELPERS_H */
|
||||||
// (Having two instances of `arr` is OK because the contents of `sizeof` are not evaluated.)
|
|
||||||
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof *(arr))
|
|
||||||
|
|
||||||
#endif // HELPERS_H
|
|
||||||
|
|||||||
@@ -1,88 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of RGBDS.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2022, Eldred Habert and RGBDS contributors.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef RGBDS_ITERTOOLS_HPP
|
|
||||||
#define RGBDS_ITERTOOLS_HPP
|
|
||||||
|
|
||||||
#include <tuple>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
template<typename... Ts>
|
|
||||||
static inline void report() {
|
|
||||||
puts(__PRETTY_FUNCTION__);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is not a fully generic implementation; its current use cases only require for-loop behavior.
|
|
||||||
// We also assume that all iterators have the same length.
|
|
||||||
template<typename... Iters>
|
|
||||||
class Zip {
|
|
||||||
std::tuple<Iters...> _iters;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit Zip(std::tuple<Iters...> &&iters) : _iters(iters) {}
|
|
||||||
|
|
||||||
Zip &operator++() {
|
|
||||||
std::apply([](auto &&...it) { (++it, ...); }, _iters);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto operator*() const {
|
|
||||||
return std::apply([](auto &&...it) { return std::tuple<decltype(*it)...>(*it...); },
|
|
||||||
_iters);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend auto operator==(Zip const &lhs, Zip const &rhs) {
|
|
||||||
return std::get<0>(lhs._iters) == std::get<0>(rhs._iters);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend auto operator!=(Zip const &lhs, Zip const &rhs) {
|
|
||||||
return std::get<0>(lhs._iters) != std::get<0>(rhs._iters);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
template<typename... Containers>
|
|
||||||
class ZipContainer {
|
|
||||||
std::tuple<Containers...> _containers;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ZipContainer(Containers &&...containers)
|
|
||||||
: _containers(std::forward<Containers>(containers)...) {}
|
|
||||||
|
|
||||||
auto begin() {
|
|
||||||
return Zip(std::apply(
|
|
||||||
[](auto &&...containers) {
|
|
||||||
using std::begin;
|
|
||||||
return std::make_tuple(begin(containers)...);
|
|
||||||
},
|
|
||||||
_containers));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto end() {
|
|
||||||
return Zip(std::apply(
|
|
||||||
[](auto &&...containers) {
|
|
||||||
using std::end;
|
|
||||||
return std::make_tuple(end(containers)...);
|
|
||||||
},
|
|
||||||
_containers));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Take ownership of objects and rvalue refs passed to us, but not lvalue refs
|
|
||||||
template<typename T>
|
|
||||||
using Holder = std::conditional_t<std::is_lvalue_reference_v<T>, T,
|
|
||||||
std::remove_cv_t<std::remove_reference_t<T>>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Does the same number of iterations as the first container's iterator!
|
|
||||||
template<typename... Containers>
|
|
||||||
static constexpr auto zip(Containers &&...cs) {
|
|
||||||
return detail::ZipContainer<detail::Holder<Containers>...>(std::forward<Containers>(cs)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // RGBDS_ITERTOOLS_HPP
|
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Assigning all sections a place
|
/* Assigning all sections a place */
|
||||||
#ifndef RGBDS_LINK_ASSIGN_H
|
#ifndef RGBDS_LINK_ASSIGN_H
|
||||||
#define RGBDS_LINK_ASSIGN_H
|
#define RGBDS_LINK_ASSIGN_H
|
||||||
|
|
||||||
@@ -14,10 +14,14 @@
|
|||||||
|
|
||||||
extern uint64_t nbSectionsToAssign;
|
extern uint64_t nbSectionsToAssign;
|
||||||
|
|
||||||
// Assigns all sections a slice of the address space
|
/**
|
||||||
|
* Assigns all sections a slice of the address space
|
||||||
|
*/
|
||||||
void assign_AssignSections(void);
|
void assign_AssignSections(void);
|
||||||
|
|
||||||
// `free`s all assignment memory that was allocated
|
/**
|
||||||
|
* `free`s all assignment memory that was allocated.
|
||||||
|
*/
|
||||||
void assign_Cleanup(void);
|
void assign_Cleanup(void);
|
||||||
|
|
||||||
#endif // RGBDS_LINK_ASSIGN_H
|
#endif /* RGBDS_LINK_ASSIGN_H */
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Declarations that all modules use, as well as `main` and related
|
/* Declarations that all modules use, as well as `main` and related */
|
||||||
#ifndef RGBDS_LINK_MAIN_H
|
#ifndef RGBDS_LINK_MAIN_H
|
||||||
#define RGBDS_LINK_MAIN_H
|
#define RGBDS_LINK_MAIN_H
|
||||||
|
|
||||||
@@ -16,18 +16,14 @@
|
|||||||
|
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
|
||||||
// Variables related to CLI options
|
/* Variables related to CLI options */
|
||||||
extern bool isDmgMode;
|
extern bool isDmgMode;
|
||||||
extern char *linkerScriptName;
|
extern char *linkerScriptName;
|
||||||
extern char const *mapFileName;
|
extern char const *mapFileName;
|
||||||
extern bool noSymInMap;
|
|
||||||
extern char const *symFileName;
|
extern char const *symFileName;
|
||||||
extern char const *overlayFileName;
|
extern char const *overlayFileName;
|
||||||
extern char const *outputFileName;
|
extern char const *outputFileName;
|
||||||
extern uint8_t padValue;
|
extern uint8_t padValue;
|
||||||
extern uint16_t scrambleROMX;
|
|
||||||
extern uint8_t scrambleWRAMX;
|
|
||||||
extern uint8_t scrambleSRAM;
|
|
||||||
extern bool is32kMode;
|
extern bool is32kMode;
|
||||||
extern bool beVerbose;
|
extern bool beVerbose;
|
||||||
extern bool isWRA0Mode;
|
extern bool isWRA0Mode;
|
||||||
@@ -35,7 +31,7 @@ extern bool disablePadding;
|
|||||||
|
|
||||||
struct FileStackNode {
|
struct FileStackNode {
|
||||||
struct FileStackNode *parent;
|
struct FileStackNode *parent;
|
||||||
// Line at which the parent context was exited; meaningless for the root level
|
/* Line at which the parent context was exited; meaningless for the root level */
|
||||||
uint32_t lineNo;
|
uint32_t lineNo;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@@ -44,21 +40,21 @@ struct FileStackNode {
|
|||||||
NODE_MACRO,
|
NODE_MACRO,
|
||||||
} type;
|
} type;
|
||||||
union {
|
union {
|
||||||
char *name; // NODE_FILE, NODE_MACRO
|
char *name; /* NODE_FILE, NODE_MACRO */
|
||||||
struct { // NODE_REPT
|
struct { /* NODE_REPT */
|
||||||
uint32_t reptDepth;
|
uint32_t reptDepth;
|
||||||
uint32_t *iters;
|
uint32_t *iters;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper macro for printing verbose-mode messages
|
/* Helper macro for printing verbose-mode messages */
|
||||||
#define verbosePrint(...) do { \
|
#define verbosePrint(...) do { \
|
||||||
if (beVerbose) \
|
if (beVerbose) \
|
||||||
fprintf(stderr, __VA_ARGS__); \
|
fprintf(stderr, __VA_ARGS__); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Dump a file stack to stderr
|
* Dump a file stack to stderr
|
||||||
* @param node The leaf node to dump the context of
|
* @param node The leaf node to dump the context of
|
||||||
*/
|
*/
|
||||||
@@ -73,7 +69,7 @@ void error(struct FileStackNode const *where, uint32_t lineNo,
|
|||||||
_Noreturn void fatal(struct FileStackNode const *where, uint32_t lineNo,
|
_Noreturn void fatal(struct FileStackNode const *where, uint32_t lineNo,
|
||||||
char const *fmt, ...) format_(printf, 3, 4);
|
char const *fmt, ...) format_(printf, 3, 4);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Opens a file if specified, and aborts on error.
|
* Opens a file if specified, and aborts on error.
|
||||||
* @param fileName The name of the file to open; if NULL, no file will be opened
|
* @param fileName The name of the file to open; if NULL, no file will be opened
|
||||||
* @param mode The mode to open the file with
|
* @param mode The mode to open the file with
|
||||||
@@ -87,4 +83,4 @@ FILE *openFile(char const *fileName, char const *mode);
|
|||||||
fclose(tmp); \
|
fclose(tmp); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#endif // RGBDS_LINK_MAIN_H
|
#endif /* RGBDS_LINK_MAIN_H */
|
||||||
|
|||||||
@@ -6,37 +6,37 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Declarations related to processing of object (.o) files
|
/* Declarations related to processing of object (.o) files */
|
||||||
|
|
||||||
#ifndef RGBDS_LINK_OBJECT_H
|
#ifndef RGBDS_LINK_OBJECT_H
|
||||||
#define RGBDS_LINK_OBJECT_H
|
#define RGBDS_LINK_OBJECT_H
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Read an object (.o) file, and add its info to the data structures.
|
* Read an object (.o) file, and add its info to the data structures.
|
||||||
* @param fileName A path to the object file to be read
|
* @param fileName A path to the object file to be read
|
||||||
* @param i The ID of the file
|
* @param i The ID of the file
|
||||||
*/
|
*/
|
||||||
void obj_ReadFile(char const *fileName, unsigned int i);
|
void obj_ReadFile(char const *fileName, unsigned int i);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Perform validation on the object files' contents
|
* Perform validation on the object files' contents
|
||||||
*/
|
*/
|
||||||
void obj_DoSanityChecks(void);
|
void obj_DoSanityChecks(void);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Evaluate all assertions
|
* Evaluate all assertions
|
||||||
*/
|
*/
|
||||||
void obj_CheckAssertions(void);
|
void obj_CheckAssertions(void);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Sets up object file reading
|
* Sets up object file reading
|
||||||
* @param nbFiles The number of object files that will be read
|
* @param nbFiles The number of object files that will be read
|
||||||
*/
|
*/
|
||||||
void obj_Setup(unsigned int nbFiles);
|
void obj_Setup(unsigned int nbFiles);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* `free`s all object memory that was allocated.
|
* `free`s all object memory that was allocated.
|
||||||
*/
|
*/
|
||||||
void obj_Cleanup(void);
|
void obj_Cleanup(void);
|
||||||
|
|
||||||
#endif // RGBDS_LINK_OBJECT_H
|
#endif /* RGBDS_LINK_OBJECT_H */
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Outputting the result of linking
|
/* Outputting the result of linking */
|
||||||
#ifndef RGBDS_LINK_OUTPUT_H
|
#ifndef RGBDS_LINK_OUTPUT_H
|
||||||
#define RGBDS_LINK_OUTPUT_H
|
#define RGBDS_LINK_OUTPUT_H
|
||||||
|
|
||||||
@@ -14,22 +14,22 @@
|
|||||||
|
|
||||||
#include "link/section.h"
|
#include "link/section.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Registers a section for output.
|
* Registers a section for output.
|
||||||
* @param section The section to add
|
* @param section The section to add
|
||||||
*/
|
*/
|
||||||
void out_AddSection(struct Section const *section);
|
void out_AddSection(struct Section const *section);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Finds an assigned section overlapping another one.
|
* Finds an assigned section overlapping another one.
|
||||||
* @param section The section that is being overlapped
|
* @param section The section that is being overlapped
|
||||||
* @return A section overlapping it
|
* @return A section overlapping it
|
||||||
*/
|
*/
|
||||||
struct Section const *out_OverlappingSection(struct Section const *section);
|
struct Section const *out_OverlappingSection(struct Section const *section);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Writes all output (bin, sym, map) files.
|
* Writes all output (bin, sym, map) files.
|
||||||
*/
|
*/
|
||||||
void out_WriteFiles(void);
|
void out_WriteFiles(void);
|
||||||
|
|
||||||
#endif // RGBDS_LINK_OUTPUT_H
|
#endif /* RGBDS_LINK_OUTPUT_H */
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Applying patches to SECTIONs
|
/* Applying patches to SECTIONs */
|
||||||
#ifndef RGBDS_LINK_PATCH_H
|
#ifndef RGBDS_LINK_PATCH_H
|
||||||
#define RGBDS_LINK_PATCH_H
|
#define RGBDS_LINK_PATCH_H
|
||||||
|
|
||||||
@@ -21,21 +21,24 @@ struct Assertion {
|
|||||||
struct Patch patch;
|
struct Patch patch;
|
||||||
// enum AssertionType type; The `patch`'s field is instead re-used
|
// enum AssertionType type; The `patch`'s field is instead re-used
|
||||||
char *message;
|
char *message;
|
||||||
// This would be redundant with `.section->fileSymbols`... but `section` is sometimes NULL!
|
/*
|
||||||
|
* This would be redundant with `.section->fileSymbols`... but
|
||||||
|
* `section` is sometimes NULL!
|
||||||
|
*/
|
||||||
struct Symbol **fileSymbols;
|
struct Symbol **fileSymbols;
|
||||||
|
|
||||||
struct Assertion *next;
|
struct Assertion *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Checks all assertions
|
* Checks all assertions
|
||||||
* @return true if assertion failed
|
* @return true if assertion failed
|
||||||
*/
|
*/
|
||||||
void patch_CheckAssertions(struct Assertion *assertion);
|
void patch_CheckAssertions(struct Assertion *assertion);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Applies all SECTIONs' patches to them
|
* Applies all SECTIONs' patches to them
|
||||||
*/
|
*/
|
||||||
void patch_ApplyPatches(void);
|
void patch_ApplyPatches(void);
|
||||||
|
|
||||||
#endif // RGBDS_LINK_PATCH_H
|
#endif /* RGBDS_LINK_PATCH_H */
|
||||||
|
|||||||
@@ -6,33 +6,31 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Parsing a linker script
|
/* Parsing a linker script */
|
||||||
#ifndef RGBDS_LINK_SCRIPT_H
|
#ifndef RGBDS_LINK_SCRIPT_H
|
||||||
#define RGBDS_LINK_SCRIPT_H
|
#define RGBDS_LINK_SCRIPT_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "linkdefs.h"
|
|
||||||
|
|
||||||
extern FILE * linkerScript;
|
extern FILE * linkerScript;
|
||||||
|
|
||||||
struct SectionPlacement {
|
struct SectionPlacement {
|
||||||
struct Section *section;
|
struct Section *section;
|
||||||
enum SectionType type;
|
|
||||||
uint16_t org;
|
uint16_t org;
|
||||||
uint32_t bank;
|
uint32_t bank;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern uint64_t script_lineNo;
|
extern uint64_t script_lineNo;
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Parses the linker script to return the next section constraint
|
* Parses the linker script to return the next section constraint
|
||||||
* @return A pointer to a struct, or NULL on EOF. The pointer shouldn't be freed
|
* @return A pointer to a struct, or NULL on EOF. The pointer shouldn't be freed
|
||||||
*/
|
*/
|
||||||
struct SectionPlacement *script_NextSection(void);
|
struct SectionPlacement *script_NextSection(void);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* `free`s all assignment memory that was allocated.
|
* `free`s all assignment memory that was allocated.
|
||||||
*/
|
*/
|
||||||
void script_Cleanup(void);
|
void script_Cleanup(void);
|
||||||
|
|
||||||
#endif // RGBDS_LINK_SCRIPT_H
|
#endif /* RGBDS_LINK_SCRIPT_H */
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of RGBDS.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2022, Eldred Habert and RGBDS contributors.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Assigning all sections a place
|
|
||||||
#ifndef RGBDS_LINK_SDAS_OBJ_H
|
|
||||||
#define RGBDS_LINK_SDAS_OBJ_H
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
struct FileStackNode;
|
|
||||||
|
|
||||||
void sdobj_ReadFile(struct FileStackNode const *fileName, FILE *file);
|
|
||||||
|
|
||||||
#endif // RGBDS_LINK_SDAS_OBJ_H
|
|
||||||
@@ -6,11 +6,11 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Declarations manipulating symbols
|
/* Declarations manipulating symbols */
|
||||||
#ifndef RGBDS_LINK_SECTION_H
|
#ifndef RGBDS_LINK_SECTION_H
|
||||||
#define RGBDS_LINK_SECTION_H
|
#define RGBDS_LINK_SECTION_H
|
||||||
|
|
||||||
// GUIDELINE: external code MUST NOT BE AWARE of the data structure used!!
|
/* GUIDELINE: external code MUST NOT BE AWARE of the data structure used!! */
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
@@ -41,29 +41,27 @@ struct Patch {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct Section {
|
struct Section {
|
||||||
// Info contained in the object files
|
/* Info contained in the object files */
|
||||||
char *name;
|
char *name;
|
||||||
uint16_t size;
|
uint16_t size;
|
||||||
uint16_t offset;
|
uint16_t offset;
|
||||||
enum SectionType type;
|
enum SectionType type;
|
||||||
enum SectionModifier modifier;
|
enum SectionModifier modifier;
|
||||||
bool isAddressFixed;
|
bool isAddressFixed;
|
||||||
// This `struct`'s address in ROM.
|
|
||||||
// Importantly for fragments, this does not include `offset`!
|
|
||||||
uint16_t org;
|
uint16_t org;
|
||||||
bool isBankFixed;
|
bool isBankFixed;
|
||||||
uint32_t bank;
|
uint32_t bank;
|
||||||
bool isAlignFixed;
|
bool isAlignFixed;
|
||||||
uint16_t alignMask;
|
uint16_t alignMask;
|
||||||
uint16_t alignOfs;
|
uint16_t alignOfs;
|
||||||
uint8_t *data; // Array of size `size`
|
uint8_t *data; /* Array of size `size`*/
|
||||||
uint32_t nbPatches;
|
uint32_t nbPatches;
|
||||||
struct Patch *patches;
|
struct Patch *patches;
|
||||||
// Extra info computed during linking
|
/* Extra info computed during linking */
|
||||||
struct Symbol **fileSymbols;
|
struct Symbol **fileSymbols;
|
||||||
uint32_t nbSymbols;
|
uint32_t nbSymbols;
|
||||||
struct Symbol **symbols;
|
struct Symbol const **symbols;
|
||||||
struct Section *nextu; // The next "component" of this unionized sect
|
struct Section *nextu; /* The next "component" of this unionized sect */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -76,27 +74,27 @@ struct Section {
|
|||||||
*/
|
*/
|
||||||
void sect_ForEach(void (*callback)(struct Section *, void *), void *arg);
|
void sect_ForEach(void (*callback)(struct Section *, void *), void *arg);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Registers a section to be processed.
|
* Registers a section to be processed.
|
||||||
* @param section The section to register.
|
* @param section The section to register.
|
||||||
*/
|
*/
|
||||||
void sect_AddSection(struct Section *section);
|
void sect_AddSection(struct Section *section);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Finds a section by its name.
|
* Finds a section by its name.
|
||||||
* @param name The name of the section to look for
|
* @param name The name of the section to look for
|
||||||
* @return A pointer to the section, or NULL if it wasn't found
|
* @return A pointer to the section, or NULL if it wasn't found
|
||||||
*/
|
*/
|
||||||
struct Section *sect_GetSection(char const *name);
|
struct Section *sect_GetSection(char const *name);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* `free`s all section memory that was allocated.
|
* `free`s all section memory that was allocated.
|
||||||
*/
|
*/
|
||||||
void sect_CleanupSections(void);
|
void sect_CleanupSections(void);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Checks if all sections meet reasonable criteria, such as max size
|
* Checks if all sections meet reasonable criteria, such as max size
|
||||||
*/
|
*/
|
||||||
void sect_DoSanityChecks(void);
|
void sect_DoSanityChecks(void);
|
||||||
|
|
||||||
#endif // RGBDS_LINK_SECTION_H
|
#endif /* RGBDS_LINK_SECTION_H */
|
||||||
|
|||||||
@@ -6,11 +6,11 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Declarations manipulating symbols
|
/* Declarations manipulating symbols */
|
||||||
#ifndef RGBDS_LINK_SYMBOL_H
|
#ifndef RGBDS_LINK_SYMBOL_H
|
||||||
#define RGBDS_LINK_SYMBOL_H
|
#define RGBDS_LINK_SYMBOL_H
|
||||||
|
|
||||||
// GUIDELINE: external code MUST NOT BE AWARE of the data structure used!!
|
/* GUIDELINE: external code MUST NOT BE AWARE of the data structure used!! */
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
struct FileStackNode;
|
struct FileStackNode;
|
||||||
|
|
||||||
struct Symbol {
|
struct Symbol {
|
||||||
// Info contained in the object files
|
/* Info contained in the object files */
|
||||||
char *name;
|
char *name;
|
||||||
enum ExportLevel type;
|
enum ExportLevel type;
|
||||||
char const *objFileName;
|
char const *objFileName;
|
||||||
@@ -27,11 +27,10 @@ struct Symbol {
|
|||||||
int32_t lineNo;
|
int32_t lineNo;
|
||||||
int32_t sectionID;
|
int32_t sectionID;
|
||||||
union {
|
union {
|
||||||
// Both types must be identical
|
|
||||||
int32_t offset;
|
int32_t offset;
|
||||||
int32_t value;
|
int32_t value;
|
||||||
};
|
};
|
||||||
// Extra info computed during linking
|
/* Extra info computed during linking */
|
||||||
struct Section *section;
|
struct Section *section;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -47,16 +46,16 @@ void sym_ForEach(void (*callback)(struct Symbol *, void *), void *arg);
|
|||||||
|
|
||||||
void sym_AddSymbol(struct Symbol *symbol);
|
void sym_AddSymbol(struct Symbol *symbol);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Finds a symbol in all the defined symbols.
|
* Finds a symbol in all the defined symbols.
|
||||||
* @param name The name of the symbol to look for
|
* @param name The name of the symbol to look for
|
||||||
* @return A pointer to the symbol, or NULL if not found.
|
* @return A pointer to the symbol, or NULL if not found.
|
||||||
*/
|
*/
|
||||||
struct Symbol *sym_GetSymbol(char const *name);
|
struct Symbol *sym_GetSymbol(char const *name);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* `free`s all symbol memory that was allocated.
|
* `free`s all symbol memory that was allocated.
|
||||||
*/
|
*/
|
||||||
void sym_CleanupSymbols(void);
|
void sym_CleanupSymbols(void);
|
||||||
|
|
||||||
#endif // RGBDS_LINK_SYMBOL_H
|
#endif /* RGBDS_LINK_SYMBOL_H */
|
||||||
|
|||||||
@@ -9,12 +9,12 @@
|
|||||||
#ifndef RGBDS_LINKDEFS_H
|
#ifndef RGBDS_LINKDEFS_H
|
||||||
#define RGBDS_LINKDEFS_H
|
#define RGBDS_LINKDEFS_H
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define RGBDS_OBJECT_VERSION_STRING "RGB9"
|
#define RGBDS_OBJECT_VERSION_STRING "RGB%1u"
|
||||||
#define RGBDS_OBJECT_REV 9U
|
#define RGBDS_OBJECT_VERSION_NUMBER 9U
|
||||||
|
#define RGBDS_OBJECT_REV 7U
|
||||||
|
|
||||||
enum AssertionType {
|
enum AssertionType {
|
||||||
ASSERT_WARN,
|
ASSERT_WARN,
|
||||||
@@ -23,45 +23,42 @@ enum AssertionType {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum RPNCommand {
|
enum RPNCommand {
|
||||||
RPN_ADD = 0x00,
|
RPN_ADD = 0x00,
|
||||||
RPN_SUB = 0x01,
|
RPN_SUB = 0x01,
|
||||||
RPN_MUL = 0x02,
|
RPN_MUL = 0x02,
|
||||||
RPN_DIV = 0x03,
|
RPN_DIV = 0x03,
|
||||||
RPN_MOD = 0x04,
|
RPN_MOD = 0x04,
|
||||||
RPN_UNSUB = 0x05, // FIXME: should be renamed to "NEG" for consistency
|
RPN_UNSUB = 0x05,
|
||||||
RPN_EXP = 0x06,
|
RPN_EXP = 0x06,
|
||||||
|
|
||||||
RPN_OR = 0x10,
|
RPN_OR = 0x10,
|
||||||
RPN_AND = 0x11,
|
RPN_AND = 0x11,
|
||||||
RPN_XOR = 0x12,
|
RPN_XOR = 0x12,
|
||||||
RPN_UNNOT = 0x13, // FIXME: should be renamed to "NOT" for consistency
|
RPN_UNNOT = 0x13,
|
||||||
|
|
||||||
RPN_LOGAND = 0x21,
|
RPN_LOGAND = 0x21,
|
||||||
RPN_LOGOR = 0x22,
|
RPN_LOGOR = 0x22,
|
||||||
RPN_LOGUNNOT = 0x23, // FIXME: should be renamed to "LOGNOT" for consistency
|
RPN_LOGUNNOT = 0x23,
|
||||||
|
|
||||||
RPN_LOGEQ = 0x30,
|
RPN_LOGEQ = 0x30,
|
||||||
RPN_LOGNE = 0x31,
|
RPN_LOGNE = 0x31,
|
||||||
RPN_LOGGT = 0x32,
|
RPN_LOGGT = 0x32,
|
||||||
RPN_LOGLT = 0x33,
|
RPN_LOGLT = 0x33,
|
||||||
RPN_LOGGE = 0x34,
|
RPN_LOGGE = 0x34,
|
||||||
RPN_LOGLE = 0x35,
|
RPN_LOGLE = 0x35,
|
||||||
|
|
||||||
RPN_SHL = 0x40,
|
RPN_SHL = 0x40,
|
||||||
RPN_SHR = 0x41,
|
RPN_SHR = 0x41,
|
||||||
RPN_USHR = 0x42,
|
|
||||||
|
|
||||||
RPN_BANK_SYM = 0x50,
|
RPN_BANK_SYM = 0x50,
|
||||||
RPN_BANK_SECT = 0x51,
|
RPN_BANK_SECT = 0x51,
|
||||||
RPN_BANK_SELF = 0x52,
|
RPN_BANK_SELF = 0x52,
|
||||||
RPN_SIZEOF_SECT = 0x53,
|
|
||||||
RPN_STARTOF_SECT = 0x54,
|
|
||||||
|
|
||||||
RPN_HRAM = 0x60,
|
RPN_HRAM = 0x60,
|
||||||
RPN_RST = 0x61,
|
RPN_RST = 0x61,
|
||||||
|
|
||||||
RPN_CONST = 0x80,
|
RPN_CONST = 0x80,
|
||||||
RPN_SYM = 0x81
|
RPN_SYM = 0x81
|
||||||
};
|
};
|
||||||
|
|
||||||
enum SectionType {
|
enum SectionType {
|
||||||
@@ -74,50 +71,9 @@ enum SectionType {
|
|||||||
SECTTYPE_SRAM,
|
SECTTYPE_SRAM,
|
||||||
SECTTYPE_OAM,
|
SECTTYPE_OAM,
|
||||||
|
|
||||||
// In RGBLINK, this is used for "indeterminate" sections; this is primarily for SDCC
|
|
||||||
// areas, which do not carry any section type info and must be told from the linker script
|
|
||||||
SECTTYPE_INVALID
|
SECTTYPE_INVALID
|
||||||
};
|
};
|
||||||
|
|
||||||
// Nont-`const` members may be patched in RGBLINK depending on CLI flags
|
|
||||||
extern struct SectionTypeInfo {
|
|
||||||
char const *const name;
|
|
||||||
uint16_t const startAddr;
|
|
||||||
uint16_t size;
|
|
||||||
uint32_t const firstBank;
|
|
||||||
uint32_t lastBank;
|
|
||||||
} sectionTypeInfo[SECTTYPE_INVALID];
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Tells whether a section has data in its object file definition,
|
|
||||||
* depending on type.
|
|
||||||
* @param type The section's type
|
|
||||||
* @return `true` if the section's definition includes data
|
|
||||||
*/
|
|
||||||
static inline bool sect_HasData(enum SectionType type)
|
|
||||||
{
|
|
||||||
assert(type != SECTTYPE_INVALID);
|
|
||||||
return type == SECTTYPE_ROM0 || type == SECTTYPE_ROMX;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Computes a memory region's end address (last byte), eg. 0x7FFF
|
|
||||||
* @return The address of the last byte in that memory region
|
|
||||||
*/
|
|
||||||
static inline uint16_t endaddr(enum SectionType type)
|
|
||||||
{
|
|
||||||
return sectionTypeInfo[type].startAddr + sectionTypeInfo[type].size - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Computes a memory region's number of banks
|
|
||||||
* @return The number of banks, 1 for regions without banking
|
|
||||||
*/
|
|
||||||
static inline uint32_t nbbanks(enum SectionType type)
|
|
||||||
{
|
|
||||||
return sectionTypeInfo[type].lastBank - sectionTypeInfo[type].firstBank + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum SectionModifier {
|
enum SectionModifier {
|
||||||
SECTION_NORMAL,
|
SECTION_NORMAL,
|
||||||
SECTION_UNION,
|
SECTION_UNION,
|
||||||
@@ -126,6 +82,17 @@ enum SectionModifier {
|
|||||||
|
|
||||||
extern char const * const sectionModNames[];
|
extern char const * const sectionModNames[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells whether a section has data in its object file definition,
|
||||||
|
* depending on type.
|
||||||
|
* @param type The section's type
|
||||||
|
* @return `true` if the section's definition includes data
|
||||||
|
*/
|
||||||
|
static inline bool sect_HasData(enum SectionType type)
|
||||||
|
{
|
||||||
|
return type == SECTTYPE_ROM0 || type == SECTTYPE_ROMX;
|
||||||
|
}
|
||||||
|
|
||||||
enum ExportLevel {
|
enum ExportLevel {
|
||||||
SYMTYPE_LOCAL,
|
SYMTYPE_LOCAL,
|
||||||
SYMTYPE_IMPORT,
|
SYMTYPE_IMPORT,
|
||||||
@@ -141,4 +108,45 @@ enum PatchType {
|
|||||||
PATCHTYPE_INVALID
|
PATCHTYPE_INVALID
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // RGBDS_LINKDEFS_H
|
#define BANK_MIN_ROM0 0
|
||||||
|
#define BANK_MAX_ROM0 0
|
||||||
|
#define BANK_MIN_ROMX 1
|
||||||
|
#define BANK_MAX_ROMX 511
|
||||||
|
#define BANK_MIN_VRAM 0
|
||||||
|
#define BANK_MAX_VRAM 1
|
||||||
|
#define BANK_MIN_SRAM 0
|
||||||
|
#define BANK_MAX_SRAM 15
|
||||||
|
#define BANK_MIN_WRAM0 0
|
||||||
|
#define BANK_MAX_WRAM0 0
|
||||||
|
#define BANK_MIN_WRAMX 1
|
||||||
|
#define BANK_MAX_WRAMX 7
|
||||||
|
#define BANK_MIN_OAM 0
|
||||||
|
#define BANK_MAX_OAM 0
|
||||||
|
#define BANK_MIN_HRAM 0
|
||||||
|
#define BANK_MAX_HRAM 0
|
||||||
|
|
||||||
|
extern uint16_t startaddr[];
|
||||||
|
extern uint16_t maxsize[];
|
||||||
|
extern uint32_t bankranges[][2];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes a memory region's end address (last byte), eg. 0x7FFF
|
||||||
|
* @return The address of the last byte in that memory region
|
||||||
|
*/
|
||||||
|
static inline uint16_t endaddr(enum SectionType type)
|
||||||
|
{
|
||||||
|
return startaddr[type] + maxsize[type] - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes a memory region's number of banks
|
||||||
|
* @return The number of banks, 1 for regions without banking
|
||||||
|
*/
|
||||||
|
static inline uint32_t nbbanks(enum SectionType type)
|
||||||
|
{
|
||||||
|
return bankranges[type][1] - bankranges[type][0] + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern char const * const typeNames[SECTTYPE_INVALID];
|
||||||
|
|
||||||
|
#endif /* RGBDS_LINKDEFS_H */
|
||||||
|
|||||||
@@ -16,6 +16,5 @@ int32_t op_modulo(int32_t dividend, int32_t divisor);
|
|||||||
int32_t op_exponent(int32_t base, uint32_t power);
|
int32_t op_exponent(int32_t base, uint32_t power);
|
||||||
int32_t op_shift_left(int32_t value, int32_t amount);
|
int32_t op_shift_left(int32_t value, int32_t amount);
|
||||||
int32_t op_shift_right(int32_t value, int32_t amount);
|
int32_t op_shift_right(int32_t value, int32_t amount);
|
||||||
int32_t op_shift_right_unsigned(int32_t value, int32_t amount);
|
|
||||||
|
|
||||||
#endif // RGBDS_OP_MATH_H
|
#endif /* RGBDS_OP_MATH_H */
|
||||||
|
|||||||
@@ -6,34 +6,33 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// platform-specific hacks
|
/* platform-specific hacks */
|
||||||
|
|
||||||
#ifndef RGBDS_PLATFORM_H
|
#ifndef RGBDS_PLATFORM_H
|
||||||
#define RGBDS_PLATFORM_H
|
#define RGBDS_PLATFORM_H
|
||||||
|
|
||||||
// MSVC doesn't have str(n)casecmp, use a suitable replacement
|
/* MSVC doesn't have strncasecmp, use a suitable replacement */
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
# include <string.h>
|
# include <string.h>
|
||||||
# define strcasecmp _stricmp
|
|
||||||
# define strncasecmp _strnicmp
|
# define strncasecmp _strnicmp
|
||||||
#else
|
#else
|
||||||
# include <strings.h>
|
# include <strings.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// MSVC has deprecated strdup in favor of _strdup
|
/* MSVC has deprecated strdup in favor of _strdup */
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
# define strdup _strdup
|
# define strdup _strdup
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// MSVC prefixes the names of S_* macros with underscores,
|
/* MSVC prefixes the names of S_* macros with underscores,
|
||||||
// and doesn't define any S_IS* macros; define them ourselves
|
and doesn't define any S_IS* macros. Define them ourselves */
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
# define S_IFMT _S_IFMT
|
# define S_IFMT _S_IFMT
|
||||||
# define S_IFDIR _S_IFDIR
|
# define S_IFDIR _S_IFDIR
|
||||||
# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
|
# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// MSVC doesn't use POSIX types or defines for `read`
|
/* MSVC doesn't use POSIX types or defines for `read` */
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
# include <io.h>
|
# include <io.h>
|
||||||
# define STDIN_FILENO 0
|
# define STDIN_FILENO 0
|
||||||
@@ -46,15 +45,11 @@
|
|||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// MSVC doesn't support `[static N]` for array arguments from C99 or C11
|
/* MSVC doesn't support `[static N]` for array arguments from C99 */
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
# define MIN_NB_ELMS(N)
|
# define MIN_NB_ELMS(N)
|
||||||
# define ARR_QUALS(...)
|
|
||||||
# define NONNULL(ptr) *ptr
|
|
||||||
#else
|
#else
|
||||||
# define MIN_NB_ELMS(N) static (N)
|
# define MIN_NB_ELMS(N) static (N)
|
||||||
# define ARR_QUALS(...) __VA_ARGS__
|
|
||||||
# define NONNULL(ptr) ptr[static 1]
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// MSVC uses a different name for O_RDWR, and needs an additional _O_BINARY flag
|
// MSVC uses a different name for O_RDWR, and needs an additional _O_BINARY flag
|
||||||
@@ -75,4 +70,4 @@
|
|||||||
# define setmode(fd, mode) ((void)0)
|
# define setmode(fd, mode) ((void)0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // RGBDS_PLATFORM_H
|
#endif /* RGBDS_PLATFORM_H */
|
||||||
|
|||||||
16
include/types.h
Normal file
16
include/types.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of RGBDS.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RGBDS_TYPES_H
|
||||||
|
#define RGBDS_TYPES_H
|
||||||
|
|
||||||
|
#ifndef _MAX_PATH
|
||||||
|
#define _MAX_PATH 512
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* RGBDS_TYPES_H */
|
||||||
@@ -9,19 +9,11 @@
|
|||||||
#ifndef EXTERN_VERSION_H
|
#ifndef EXTERN_VERSION_H
|
||||||
#define EXTERN_VERSION_H
|
#define EXTERN_VERSION_H
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define PACKAGE_VERSION_MAJOR 0
|
#define PACKAGE_VERSION_MAJOR 0
|
||||||
#define PACKAGE_VERSION_MINOR 6
|
#define PACKAGE_VERSION_MINOR 5
|
||||||
#define PACKAGE_VERSION_PATCH 0
|
#define PACKAGE_VERSION_PATCH 0
|
||||||
#define PACKAGE_VERSION_RC 2
|
#define PACKAGE_VERSION_RC 2
|
||||||
|
|
||||||
char const *get_package_version_string(void);
|
const char *get_package_version_string(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#endif /* EXTERN_VERSION_H */
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // EXTERN_VERSION_H
|
|
||||||
|
|||||||
405
man/rgbds.5
405
man/rgbds.5
@@ -1,405 +0,0 @@
|
|||||||
.\"
|
|
||||||
.\" This file is part of RGBDS.
|
|
||||||
.\"
|
|
||||||
.\" Copyright (c) 2017-2021, Antonio Nino Diaz and RGBDS contributors.
|
|
||||||
.\"
|
|
||||||
.\" SPDX-License-Identifier: MIT
|
|
||||||
.\"
|
|
||||||
.Dd March 28, 2021
|
|
||||||
.Dt RGBDS 5
|
|
||||||
.Os
|
|
||||||
.Sh NAME
|
|
||||||
.Nm rgbds
|
|
||||||
.Nd object file format documentation
|
|
||||||
.Sh DESCRIPTION
|
|
||||||
This is the description of the object files used by
|
|
||||||
.Xr rgbasm 1
|
|
||||||
and
|
|
||||||
.Xr rgblink 1 .
|
|
||||||
.Em Please note that the specification is not stable yet.
|
|
||||||
RGBDS is still in active development, and some new features require adding more information to the object file, or modifying some fields, both of which break compatibility with older versions.
|
|
||||||
.Sh FILE STRUCTURE
|
|
||||||
The following types are used:
|
|
||||||
.Pp
|
|
||||||
.Cm LONG
|
|
||||||
is a 32-bit integer stored in little-endian format.
|
|
||||||
.Cm BYTE
|
|
||||||
is an 8-bit integer.
|
|
||||||
.Cm STRING
|
|
||||||
is a 0-terminated string of
|
|
||||||
.Cm BYTE .
|
|
||||||
Brackets after a type
|
|
||||||
.Pq e.g. Cm LONG Ns Bq Ar n
|
|
||||||
indicate
|
|
||||||
.Ar n
|
|
||||||
consecutive elements
|
|
||||||
.Pq here, Cm LONG Ns s .
|
|
||||||
All items are contiguous, with no padding anywhere\(emthis also means that they may not be aligned in the file!
|
|
||||||
.Pp
|
|
||||||
.Cm REPT Ar n
|
|
||||||
indicates that the fields between the
|
|
||||||
.Cm REPT
|
|
||||||
and corresponding
|
|
||||||
.Cm ENDR
|
|
||||||
are repeated
|
|
||||||
.Ar n
|
|
||||||
times.
|
|
||||||
.Pp
|
|
||||||
All IDs refer to objects within the file; for example, symbol ID $0001 refers to the second symbol defined in
|
|
||||||
.Em this
|
|
||||||
object file's
|
|
||||||
.Sx Symbols
|
|
||||||
array.
|
|
||||||
The only exception is the
|
|
||||||
.Sx Source file info
|
|
||||||
nodes, whose IDs are backwards, i.e. source node ID $0000 refers to the
|
|
||||||
.Em last
|
|
||||||
node in the array, not the first one.
|
|
||||||
References to other object files are made by imports (symbols), by name (sections), etc.\(embut never by ID.
|
|
||||||
.Ss Header
|
|
||||||
.Bl -tag -width Ds -compact
|
|
||||||
.It Cm BYTE Ar Magic[4]
|
|
||||||
"RGB9"
|
|
||||||
.It Cm LONG Ar RevisionNumber
|
|
||||||
The format's revision number this file uses.
|
|
||||||
.Pq This is always in the same place in all revisions.
|
|
||||||
.It Cm LONG Ar NumberOfSymbols
|
|
||||||
How many symbols are defined in this object file.
|
|
||||||
.It Cm LONG Ar NumberOfSections
|
|
||||||
How many sections are defined in this object file.
|
|
||||||
.El
|
|
||||||
.Ss Source file info
|
|
||||||
.Bl -tag -width Ds -compact
|
|
||||||
.It Cm LONG Ar NumberOfNodes
|
|
||||||
The number of source context nodes contained in this file.
|
|
||||||
.It Cm REPT Ar NumberOfNodes
|
|
||||||
.Bl -tag -width Ds -compact
|
|
||||||
.It Cm LONG Ar ParentID
|
|
||||||
ID of the parent node, -1 meaning that this is the root node.
|
|
||||||
.Pp
|
|
||||||
.Sy Important :
|
|
||||||
the nodes are actually written in
|
|
||||||
.Sy reverse
|
|
||||||
order, meaning the node with ID 0 is the last one in the list!
|
|
||||||
.It Cm LONG Ar ParentLineNo
|
|
||||||
Line at which the parent node's context was exited; meaningless for the root node.
|
|
||||||
.It Cm BYTE Ar Type
|
|
||||||
.Bl -column "Value" -compact
|
|
||||||
.It Sy Value Ta Sy Meaning
|
|
||||||
.It 0 Ta REPT node
|
|
||||||
.It 1 Ta File node
|
|
||||||
.It 2 Ta Macro node
|
|
||||||
.El
|
|
||||||
.It Cm IF Ar Type No \(!= 0
|
|
||||||
If the node is not a REPT node...
|
|
||||||
.Pp
|
|
||||||
.Bl -tag -width Ds -compact
|
|
||||||
.It Cm STRING Ar Name
|
|
||||||
The node's name: either a file name, or the macro's name prefixes by its definition's file name
|
|
||||||
.Pq e.g. Ql src/includes/defines.asm::error .
|
|
||||||
.El
|
|
||||||
.It Cm ELSE
|
|
||||||
If the node is a REPT, it also contains the iteration counter of all parent REPTs.
|
|
||||||
.Pp
|
|
||||||
.Bl -tag -width Ds -compact
|
|
||||||
.It Cm LONG Ar Depth
|
|
||||||
.It Cm LONG Ar Iter Ns Bq Ar Depth
|
|
||||||
The number of REPT iterations, by increasing depth.
|
|
||||||
.El
|
|
||||||
.It Cm ENDC
|
|
||||||
.El
|
|
||||||
.It Cm ENDR
|
|
||||||
.El
|
|
||||||
.Ss Symbols
|
|
||||||
.Bl -tag -width Ds -compact
|
|
||||||
.It Cm REPT Ar NumberOfSymbols
|
|
||||||
.Bl -tag -width Ds -compact
|
|
||||||
.It Cm STRING Ar Name
|
|
||||||
This symbol's name.
|
|
||||||
Local symbols are stored as their full name
|
|
||||||
.Pq Ql Scope.symbol .
|
|
||||||
.It Cm BYTE Ar Type
|
|
||||||
.Bl -column "Value" -compact
|
|
||||||
.It Sy Value Ta Sy Meaning
|
|
||||||
.It 0 Ta Sy Local No symbol only used in this file.
|
|
||||||
.It 1 Ta Sy Import No of an exported symbol (by name) from another object file.
|
|
||||||
.It 2 Ta Sy Exported No symbol visible from other object files.
|
|
||||||
.El
|
|
||||||
.It Cm IF Ar Type No \(!= 1
|
|
||||||
If the symbol is defined in this object file...
|
|
||||||
.Pp
|
|
||||||
.Bl -tag -width Ds -compact
|
|
||||||
.It Cm LONG Ar NodeID
|
|
||||||
Context in which the symbol was defined.
|
|
||||||
.It Cm LONG Ar LineNo
|
|
||||||
Line number in the context at which the symbol was defined.
|
|
||||||
.It Cm LONG Ar SectionID
|
|
||||||
The ID of the section in which the symbol is defined.
|
|
||||||
If the symbol doesn't belong to any specific section (i.e. it's a constant), this field contains -1.
|
|
||||||
.It Cm LONG Ar Value
|
|
||||||
The symbol's value.
|
|
||||||
If the symbol belongs to a section, this is the offset within that symbol's section.
|
|
||||||
.El
|
|
||||||
.It Cm ENDC
|
|
||||||
.El
|
|
||||||
.It Cm ENDR
|
|
||||||
.El
|
|
||||||
.Ss Sections
|
|
||||||
.Bl -tag -width Ds -compact
|
|
||||||
.It Cm REPT Ar NumberOfSections
|
|
||||||
.Bl -tag -width Ds -compact
|
|
||||||
.It Cm STRING Ar Name
|
|
||||||
The section's name.
|
|
||||||
.It Cm LONG Ar Size
|
|
||||||
The section's size, in bytes.
|
|
||||||
.It Cm BYTE Ar Type
|
|
||||||
Bits 0\(en2 indicate the section's type:
|
|
||||||
.Bl -column "Value" -compact
|
|
||||||
.It Sy Value Ta Sy Meaning
|
|
||||||
.It 0 Ta WRAM0
|
|
||||||
.It 1 Ta VRAM
|
|
||||||
.It 2 Ta ROMX
|
|
||||||
.It 3 Ta ROM0
|
|
||||||
.It 4 Ta HRAM
|
|
||||||
.It 5 Ta WRAMX
|
|
||||||
.It 6 Ta SRAM
|
|
||||||
.It 7 Ta OAM
|
|
||||||
.El
|
|
||||||
.Pp
|
|
||||||
Bit\ 7 being set means that the section is a "union"
|
|
||||||
.Pq see Do Unionized sections Dc in Xr rgbasm 5 .
|
|
||||||
Bit\ 6 being set means that the section is a "fragment"
|
|
||||||
.Pq see Do Section fragments Dc in Xr rgbasm 5 .
|
|
||||||
These two bits are mutually exclusive.
|
|
||||||
.It Cm LONG Ar Address
|
|
||||||
Address this section must be placed at.
|
|
||||||
This must either be valid for the section's
|
|
||||||
.Ar Type
|
|
||||||
(as affected by flags like
|
|
||||||
.Fl t
|
|
||||||
or
|
|
||||||
.Fl d
|
|
||||||
in
|
|
||||||
.Xr rgblink 1 ) ,
|
|
||||||
or -1 to indicate that the linker should automatically decide
|
|
||||||
.Pq the section is Dq floating .
|
|
||||||
.It Cm LONG Ar Bank
|
|
||||||
ID of the bank this section must be placed in.
|
|
||||||
This must either be valid for the section's
|
|
||||||
.Ar Type
|
|
||||||
(with the same caveats as for the
|
|
||||||
.Ar Address ) ,
|
|
||||||
or -1 to indicate that the linker should automatically decide.
|
|
||||||
.It Cm BYTE Ar Alignment
|
|
||||||
How many bits of the section's address should be equal to
|
|
||||||
.Ar AlignOfs ,
|
|
||||||
starting from the least-significant bit.
|
|
||||||
.It Cm LONG Ar AlignOfs
|
|
||||||
Alignment offset.
|
|
||||||
Must be strictly less than
|
|
||||||
.Ql 1 << Ar Alignment .
|
|
||||||
.It Cm IF Ar Type No \(eq 2 || Ar Type No \(eq 3
|
|
||||||
If the section has ROM type, it contains data.
|
|
||||||
.Pp
|
|
||||||
.Bl -tag -width Ds -compact
|
|
||||||
.It Cm BYTE Ar Data Ns Bq Size
|
|
||||||
The section's raw data.
|
|
||||||
Bytes that will be patched over must be present, even though their contents will be overwritten.
|
|
||||||
.It Cm LONG Ar NumberOfPatches
|
|
||||||
How many patches must be applied to this section's
|
|
||||||
.Ar Data .
|
|
||||||
.It Cm REPT Ar NumberOfPatches
|
|
||||||
.Bl -tag -width Ds -compact
|
|
||||||
.It Cm LONG Ar NodeID
|
|
||||||
Context in which the patch was defined.
|
|
||||||
.It Cm LONG Ar LineNo
|
|
||||||
Line number in the context at which the patch was defined.
|
|
||||||
.It Cm LONG Ar Offset
|
|
||||||
Offset within the section's
|
|
||||||
.Ar Data
|
|
||||||
at which the patch should be applied.
|
|
||||||
Must not be greater than the section's
|
|
||||||
.Ar Size
|
|
||||||
minus the patch's size
|
|
||||||
.Pq see Ar Type No below .
|
|
||||||
.It Cm LONG Ar PCSectionID
|
|
||||||
ID of the section in which PC is located.
|
|
||||||
(This is usually the same section within which the patch is applied, except for e.g.\&
|
|
||||||
.Ql LOAD
|
|
||||||
blocks, see
|
|
||||||
.Do RAM code Dc in Xr rgbasm 5 . )
|
|
||||||
.It Cm LONG Ar PCOffset
|
|
||||||
Offset of the PC symbol within the section designated by
|
|
||||||
.Ar PCSectionID .
|
|
||||||
It is expected that PC points to the instruction's first byte for instruction operands (i.e.\&
|
|
||||||
.Ql jp @
|
|
||||||
must be an infinite loop), and to the patch's first byte otherwise
|
|
||||||
.Ql ( db ,
|
|
||||||
.Ql dw ,
|
|
||||||
.Ql dl ) .
|
|
||||||
.It Cm BYTE Ar Type
|
|
||||||
.Bl -column "Value" -compact
|
|
||||||
.It Sy Value Ta Sy Meaning
|
|
||||||
.It 0 Ta Single-byte patch
|
|
||||||
.It 1 Ta Little-endian two-byte patch
|
|
||||||
.It 2 Ta Little-endian four-byte patch
|
|
||||||
.It 3 Ta Single-byte Ql jr
|
|
||||||
patch; the patch's value will be subtracted to PC + 2 (i.e.\&
|
|
||||||
.Ql jr @
|
|
||||||
must be the infinite loop
|
|
||||||
.Ql 18 FE ) .
|
|
||||||
.El
|
|
||||||
.It Cm LONG Ar RPNSize
|
|
||||||
Size of the
|
|
||||||
.Ar RPNExpr
|
|
||||||
below.
|
|
||||||
.It Cm BYTE Ar RPNExpr Ns Bq RPNSize
|
|
||||||
The patch's value, encoded as a RPN expression
|
|
||||||
.Pq see Sx RPN EXPRESSIONS .
|
|
||||||
.El
|
|
||||||
.It Cm ENDR
|
|
||||||
.El
|
|
||||||
.It Cm ENDC
|
|
||||||
.El
|
|
||||||
.El
|
|
||||||
.Ss Assertions
|
|
||||||
.Bl -tag -width Ds -compact
|
|
||||||
.It Cm LONG Ar NumberOfAssertions
|
|
||||||
How many assertions this object file contains.
|
|
||||||
.It Cm REPT Ar NumberOfAssertions
|
|
||||||
Assertions are essentially patches with a message.
|
|
||||||
.Pp
|
|
||||||
.Bl -tag -width Ds -compact
|
|
||||||
.It Cm LONG Ar NodeID
|
|
||||||
Context in which the assertions was defined.
|
|
||||||
.It Cm LONG Ar LineNo
|
|
||||||
Line number in the context at which the assertion was defined.
|
|
||||||
.It Cm LONG Ar Offset
|
|
||||||
Unused leftover from the patch structure.
|
|
||||||
.It Cm LONG Ar PCSectionID
|
|
||||||
ID of the section in which PC is located.
|
|
||||||
.It Cm LONG Ar PCOffset
|
|
||||||
Offset of the PC symbol within the section designated by
|
|
||||||
.Ar PCSectionID .
|
|
||||||
.It Cm BYTE Ar Type
|
|
||||||
Describes what should happen if the expression evaluates to a non-zero value.
|
|
||||||
.Bl -column "Value" -compact
|
|
||||||
.It Sy Value Ta Sy Meaning
|
|
||||||
.It 0 Ta Print a warning message, and continue linking normally.
|
|
||||||
.It 1 Ta Print an error message, so linking will fail, but allow other assertions to be evaluated.
|
|
||||||
.It 2 Ta Print a fatal error message, and abort immediately.
|
|
||||||
.El
|
|
||||||
.It Cm LONG Ar RPNSize
|
|
||||||
Size of the
|
|
||||||
.Ar RPNExpr
|
|
||||||
below.
|
|
||||||
.It Cm BYTE Ar RPNExpr Ns Bq RPNSize
|
|
||||||
The patch's value, encoded as a RPN expression
|
|
||||||
.Pq see Sx RPN EXPRESSIONS .
|
|
||||||
.It Cm STRING Ar Message
|
|
||||||
The message displayed if the expression evaluates to a non-zero value.
|
|
||||||
If empty, a generic message is displayed instead.
|
|
||||||
.El
|
|
||||||
.It Cm ENDR
|
|
||||||
.El
|
|
||||||
.Ss RPN EXPRESSIONS
|
|
||||||
Expressions in the object file are stored as RPN, or
|
|
||||||
.Dq Reverse Polish Notation ,
|
|
||||||
which is a notation that allows computing arbitrary expressions with just a simple stack.
|
|
||||||
For example, the expression
|
|
||||||
.Ql 2 5 -
|
|
||||||
will first push the value
|
|
||||||
.Dq 2
|
|
||||||
to the stack, then
|
|
||||||
.Dq 5 .
|
|
||||||
The
|
|
||||||
.Ql -
|
|
||||||
operator pops two arguments from the stack, subtracts them, and then pushes back the result
|
|
||||||
.Pq Dq 3
|
|
||||||
on the stack.
|
|
||||||
A well-formed RPN expression never tries to pop from an empty stack, and leaves exactly one value in it at the end.
|
|
||||||
.Pp
|
|
||||||
RGBDS encodes RPN expressions as an array of
|
|
||||||
.Cm BYTE Ns s .
|
|
||||||
The first byte encodes either an operator, or a literal, which consumes more
|
|
||||||
.Cm BYTE Ns s
|
|
||||||
after it.
|
|
||||||
.Bl -column -offset Ds "Value"
|
|
||||||
.It Sy Value Ta Sy Meaning
|
|
||||||
.It Li $00 Ta Addition operator Pq Ql +
|
|
||||||
.It Li $01 Ta Subtraction operator Pq Ql -
|
|
||||||
.It Li $02 Ta Multiplication operator Pq Ql *
|
|
||||||
.It Li $03 Ta Division operator Pq Ql /
|
|
||||||
.It Li $04 Ta Modulo operator Pq Ql %
|
|
||||||
.It Li $05 Ta Negation Pq unary Ql -
|
|
||||||
.It Li $06 Ta Exponent operator Pq Ql **
|
|
||||||
.It Li $10 Ta Bitwise OR operator Pq Ql \&|
|
|
||||||
.It Li $11 Ta Bitwise AND operator Pq Ql &
|
|
||||||
.It Li $12 Ta Bitwise XOR operator Pq Ql ^
|
|
||||||
.It Li $13 Ta Bitwise complement operator Pq unary Ql ~
|
|
||||||
.It Li $21 Ta Logical AND operator Pq Ql &&
|
|
||||||
.It Li $22 Ta Logical OR operator Pq Ql ||
|
|
||||||
.It Li $23 Ta Logical complement operator Pq unary Ql \&!
|
|
||||||
.It Li $30 Ta Equality operator Pq Ql ==
|
|
||||||
.It Li $31 Ta Non-equality operator Pq Ql !=
|
|
||||||
.It Li $32 Ta Greater-than operator Pq Ql >
|
|
||||||
.It Li $33 Ta Less-than operator Pq Ql <
|
|
||||||
.It Li $34 Ta Greater-than-or-equal operator Pq Ql >=
|
|
||||||
.It Li $35 Ta Less-than-or-equal operator Pq Ql <=
|
|
||||||
.It Li $40 Ta Left shift operator Pq Ql <<
|
|
||||||
.It Li $41 Ta Arithmetic/signed right shift operator Pq Ql >>
|
|
||||||
.It Li $42 Ta Logical/unsigned right shift operator Pq Ql >>>
|
|
||||||
.It Li $50 Ta Fn BANK symbol ,
|
|
||||||
followed by the
|
|
||||||
.Ar symbol Ap s Cm LONG
|
|
||||||
ID.
|
|
||||||
.It Li $51 Ta Fn BANK section ,
|
|
||||||
followed by the
|
|
||||||
.Ar section Ap s Cm STRING
|
|
||||||
name.
|
|
||||||
.It Li $52 Ta PC's Fn BANK Pq i.e. Ql BANK(@) .
|
|
||||||
.It Li $53 Ta Fn SIZEOF section ,
|
|
||||||
followed by the
|
|
||||||
.Ar section Ap s Cm STRING
|
|
||||||
name.
|
|
||||||
.It Li $54 Ta Fn STARTOF section ,
|
|
||||||
followed by the
|
|
||||||
.Ar section Ap s Cm STRING
|
|
||||||
name.
|
|
||||||
.It Li $60 Ta Ql ldh
|
|
||||||
check.
|
|
||||||
Checks if the value is a valid
|
|
||||||
.Ql ldh
|
|
||||||
operand
|
|
||||||
.Pq see Do Load Instructions Dc in Xr gbz80 7 ,
|
|
||||||
i.e. that it is between either $00 and $FF, or $FF00 and $FFFF, both inclusive.
|
|
||||||
The value is then ANDed with $00FF
|
|
||||||
.Pq Ql & $FF .
|
|
||||||
.It Li $61 Ta Ql rst
|
|
||||||
check.
|
|
||||||
Checks if the value is a valid
|
|
||||||
.Ql rst
|
|
||||||
.Pq see Do RST vec Dc in Xr gbz80 7
|
|
||||||
vector, that is one of $00, $08, $10, $18, $20, $28, $30, or $38.
|
|
||||||
The value is then ORed with $C7
|
|
||||||
.Pq Ql \&| $C7 .
|
|
||||||
.It Li $80 Ta Integer literal.
|
|
||||||
Followed by the
|
|
||||||
.Cm LONG
|
|
||||||
integer.
|
|
||||||
.It Li $81 Ta A symbol's value.
|
|
||||||
Followed by the symbol's
|
|
||||||
.Cm LONG
|
|
||||||
ID.
|
|
||||||
.El
|
|
||||||
.Sh SEE ALSO
|
|
||||||
.Xr rgbasm 1 ,
|
|
||||||
.Xr rgblink 1 ,
|
|
||||||
.Xr rgbds 7 ,
|
|
||||||
.Xr gbz80 7
|
|
||||||
.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/gbdev/rgbds .
|
|
||||||
591
man/rgbgfx.1
591
man/rgbgfx.1
@@ -1,591 +0,0 @@
|
|||||||
'\" e
|
|
||||||
.\"
|
|
||||||
.\" This file is part of RGBDS.
|
|
||||||
.\"
|
|
||||||
.\" Copyright (c) 2013-2021, stag019 and RGBDS contributors.
|
|
||||||
.\"
|
|
||||||
.\" SPDX-License-Identifier: MIT
|
|
||||||
.\"
|
|
||||||
.Dd March 28, 2021
|
|
||||||
.Dt RGBGFX 1
|
|
||||||
.Os
|
|
||||||
.Sh NAME
|
|
||||||
.Nm rgbgfx
|
|
||||||
.Nd Game Boy graphics converter
|
|
||||||
.Sh SYNOPSIS
|
|
||||||
.Nm
|
|
||||||
.Op Fl r Ar stride
|
|
||||||
.Op Fl CmuVZ
|
|
||||||
.Op Fl v Op Fl v No ...
|
|
||||||
.Op Fl a Ar attrmap | Fl A
|
|
||||||
.Op Fl b Ar base_ids
|
|
||||||
.Op Fl c Ar color_spec
|
|
||||||
.Op Fl d Ar depth
|
|
||||||
.Op Fl L Ar slice
|
|
||||||
.Op Fl N Ar nb_tiles
|
|
||||||
.Op Fl n Ar nb_pals
|
|
||||||
.Op Fl o Ar out_file
|
|
||||||
.Op Fl p Ar pal_file | Fl P
|
|
||||||
.Op Fl q Ar pal_map | Fl Q
|
|
||||||
.Op Fl s Ar nb_colors
|
|
||||||
.Op Fl t Ar tilemap | Fl T
|
|
||||||
.Op Fl x Ar quantity
|
|
||||||
.Ar file
|
|
||||||
.Sh DESCRIPTION
|
|
||||||
The
|
|
||||||
.Nm
|
|
||||||
program converts PNG images into data suitable for display on the Game Boy and Game Boy Color, or vice-versa.
|
|
||||||
.Pp
|
|
||||||
The main function of
|
|
||||||
.Nm
|
|
||||||
is to divide the input PNG into 8\[tmu]8 pixel
|
|
||||||
.Em squares ,
|
|
||||||
convert each of those squares into 1bpp or 2bpp tile data, and save all of the tile data in a file.
|
|
||||||
It also has options to generate a tile map, attribute map, and/or palette set as well; more on that and how the conversion process can be tweaked below.
|
|
||||||
.Sh ARGUMENTS
|
|
||||||
Note that options can be abbreviated as long as the abbreviation is unambiguous:
|
|
||||||
.Fl Fl verb
|
|
||||||
is
|
|
||||||
.Fl Fl verbose ,
|
|
||||||
but
|
|
||||||
.Fl Fl ver
|
|
||||||
is invalid because it could also be
|
|
||||||
.Fl Fl version .
|
|
||||||
.Pp
|
|
||||||
.Nm
|
|
||||||
accepts decimal, binary, and hexadecimal numbers in option arguments.
|
|
||||||
Decimal numbers are written as usual; binary numbers must be prefixed with either
|
|
||||||
.Ql %
|
|
||||||
or
|
|
||||||
.Ql 0b ,
|
|
||||||
and hexadecimal numbers must be prefixed with either
|
|
||||||
.Ql $
|
|
||||||
(which will likely need escaping or quoting to avoid being interpreted by the shell), or
|
|
||||||
.Ql 0x .
|
|
||||||
Leading zeros (after the base prefix, if any) are accepted, and letters are not case-sensitive.
|
|
||||||
All of these are equivalent:
|
|
||||||
.Ql 42 ,
|
|
||||||
.Ql 042 ,
|
|
||||||
.Ql 0b00101010 ,
|
|
||||||
.Ql 0B101010 ,
|
|
||||||
.Ql 0x2A ,
|
|
||||||
.Ql 0X2A ,
|
|
||||||
.Ql 0x2a .
|
|
||||||
.Pp
|
|
||||||
The following options are accepted:
|
|
||||||
.Bl -tag -width Ds
|
|
||||||
.It Fl a Ar attrmap , Fl Fl attr-map Ar attrmap
|
|
||||||
Generate an attribute map, which is a file containing tile
|
|
||||||
.Dq attributes .
|
|
||||||
For each square of the input image, its corresponding attribute map byte contains the mirroring bits (if
|
|
||||||
.Fl m
|
|
||||||
was specified), the bank bit
|
|
||||||
.Pq see Fl N ,
|
|
||||||
and the palette index.
|
|
||||||
See
|
|
||||||
.Lk https://gbdev.io/pandocs/Tile_Maps#bg-map-attributes-cgb-mode-only Pan Docs
|
|
||||||
for the individual bytes' format.
|
|
||||||
The output is written just like the tile map (see
|
|
||||||
.Fl t ) ,
|
|
||||||
follows the same order
|
|
||||||
.Pq Fl Z ,
|
|
||||||
and has the same size.
|
|
||||||
.It Fl A , Fl Fl output-attr-map
|
|
||||||
Same as
|
|
||||||
.Fl a Ar path ,
|
|
||||||
where
|
|
||||||
.Ar path
|
|
||||||
is the input image's path with the extension set to
|
|
||||||
.Pa .attrmap .
|
|
||||||
.It Fl b Ar base_ids , Fl Fl base-tiles Ar base_ids
|
|
||||||
Set the base IDs for tile map output.
|
|
||||||
.Ar base_ids
|
|
||||||
should be one or two numbers between 0 and 255, separated by a comma; they are for bank 0 and bank 1 respectively.
|
|
||||||
Both default to 0.
|
|
||||||
.It Fl C , Fl Fl color-curve
|
|
||||||
When generating palettes, use a color curve mimicking the Game Boy Color's screen.
|
|
||||||
The resulting colors may look closer to the input image's
|
|
||||||
.Sy on hardware and accurate emulators .
|
|
||||||
.It Fl c Ar color_spec , Fl Fl colors Ar color_spec
|
|
||||||
Use the specified color palettes instead of having
|
|
||||||
.Nm
|
|
||||||
automatically determine some.
|
|
||||||
.Ar color_spec
|
|
||||||
can be one of the following:
|
|
||||||
.Bl -tag -width Ds
|
|
||||||
.It Sy inline palette spec
|
|
||||||
If
|
|
||||||
.Ar color_spec
|
|
||||||
begins with a hash character
|
|
||||||
.Ql # ,
|
|
||||||
it is treated as an inline palette specification.
|
|
||||||
It should contain a comma-separated list of hexadecimal colors, each beginning with a hash.
|
|
||||||
Colors in are accepted either as
|
|
||||||
.Ql #rgb
|
|
||||||
or
|
|
||||||
.Ql #rrggbb
|
|
||||||
format.
|
|
||||||
Palettes must be separated by a colon or semicolon (the latter may require quoting to avoid special handling by the shell), and spaces are allowed around colons, semicolons and commas; trailing commas and semicolons are allowed.
|
|
||||||
See
|
|
||||||
.Sx EXAMPLES
|
|
||||||
for an example of an inline palette specification.
|
|
||||||
.It Sy embedded palette spec
|
|
||||||
If
|
|
||||||
.Ar color_spec
|
|
||||||
is the case-insensitive word
|
|
||||||
.Cm embedded ,
|
|
||||||
then the first four colors of the input PNG's embedded palette are used.
|
|
||||||
It is an error if the PNG is not indexed, or if colors other than these 4 are used.
|
|
||||||
.Pq This is different from the default behavior of indexed PNGs, as then unused entries in the embedded palette are ignored, whereas they are not with Fl c Cm embedded .
|
|
||||||
.It Sy external palette spec
|
|
||||||
Otherwise,
|
|
||||||
.Ar color_spec
|
|
||||||
is assumed to be an external palette specification.
|
|
||||||
The expected format is
|
|
||||||
.Ql format:path ,
|
|
||||||
where
|
|
||||||
.Ar path
|
|
||||||
is a path to a file, which will be processed according to the
|
|
||||||
.Ar format .
|
|
||||||
See
|
|
||||||
.Sx PALETTE SPECIFICATION FORMATS
|
|
||||||
for a list of formats and their descriptions.
|
|
||||||
.El
|
|
||||||
.It Fl d Ar depth , Fl Fl depth Ar depth
|
|
||||||
Set the bit depth of the output tile data, in bits per pixel (bpp), either 1 or 2 (the default).
|
|
||||||
This changes how tile data is output, and the maximum number of colors per palette (2 and 4 respectively).
|
|
||||||
.It Fl L Ar slice , Fl Fl slice Ar slice
|
|
||||||
Only process a given rectangle of the image.
|
|
||||||
This is useful for example if the input image is a sheet of some sort, and you want to convert each cel individually.
|
|
||||||
The default is to process the whole image as-is.
|
|
||||||
.Pp
|
|
||||||
.Ar slice
|
|
||||||
must be two number pairs, separated by a colon.
|
|
||||||
The numbers must be separated by commas; space is allowed around all punctuation.
|
|
||||||
The first number pair specifies the X and Y coordinates of the top-left pixel that will be processed (anything above it or to its left will be ignored).
|
|
||||||
The second number pair specifies how many tiles to process horizontally and vertically, respectively.
|
|
||||||
.Pp
|
|
||||||
.Sy Fl L Sy is ignored in reverse mode , No no padding is inserted .
|
|
||||||
.It Fl m , Fl Fl mirror-tiles
|
|
||||||
Deduplicate tiles that are mirrors of each other.
|
|
||||||
Tiles are checked for horizontal, vertical, and horizontal-vertical mirroring.
|
|
||||||
Useful with a tile map and attribute map together to keep track of the duplicated tiles and the dimension(s) mirrored.
|
|
||||||
Implies
|
|
||||||
.Fl u .
|
|
||||||
.It Fl N Ar nb_tiles , Fl Fl nb-tiles Ar nb_tiles
|
|
||||||
Set a maximum number of tiles that can be placed in each VRAM bank.
|
|
||||||
.Ar nb_tiles
|
|
||||||
should be one or two numbers between 0 and 256, separated by a comma; if the latter is omitted, it defaults to 0.
|
|
||||||
Setting either number to 0 prevents any tiles from being output in that bank.
|
|
||||||
.Pp
|
|
||||||
If more tiles are generated than can fit in the two banks combined,
|
|
||||||
.Nm
|
|
||||||
will abort.
|
|
||||||
If
|
|
||||||
.Fl N
|
|
||||||
is not specified, no limit will be set on the amount of tiles placed in bank 0, and tiles will not be placed in bank 1.
|
|
||||||
.It Fl n Ar nb_pals , Fl Fl nb-palettes Ar nb_pals
|
|
||||||
Abort if more than
|
|
||||||
.Ar nb_pals
|
|
||||||
palettes are generated.
|
|
||||||
This may not be more than 256.
|
|
||||||
.Pp
|
|
||||||
Note that attribute map output only has 3 bits for the palette ID, so a limit higher than 8 may yield incomplete data unless relying on a palette map
|
|
||||||
.Pq see Fl q .
|
|
||||||
.It Fl o Ar out_file , Fl Fl output Ar out_file
|
|
||||||
Output the tile data in native 2bpp format or in 1bpp
|
|
||||||
.Pq depending on Fl d
|
|
||||||
to this file.
|
|
||||||
.It Fl p Ar pal_file , Fl Fl palette Ar pal_file
|
|
||||||
Output the image's palette set to this file.
|
|
||||||
.It Fl P , Fl Fl output-palette
|
|
||||||
Same as
|
|
||||||
.Fl p Ar path ,
|
|
||||||
where
|
|
||||||
.Ar path
|
|
||||||
is the input image's path with the extension set to
|
|
||||||
.Pa .pal .
|
|
||||||
.It Fl q Ar pal_file , Fl Fl palette-map Ar pal_file
|
|
||||||
Output the image's palette map to this file.
|
|
||||||
This is useful if the input image contains more than 8 palettes, as the attribute map only contains the lower 3 bits of the palette indices.
|
|
||||||
.It Fl Q , Fl Fl output-palette-map
|
|
||||||
Same as
|
|
||||||
.Fl q Ar path ,
|
|
||||||
where
|
|
||||||
.Ar path
|
|
||||||
is the input image's path with the extension set to
|
|
||||||
.Pa .palmap .
|
|
||||||
.It Fl r Ar width , Fl Fl reverse Ar width
|
|
||||||
Switches
|
|
||||||
.Nm
|
|
||||||
into
|
|
||||||
.Dq Sy reverse
|
|
||||||
mode.
|
|
||||||
In this mode, instead of converting a PNG image into Game Boy data,
|
|
||||||
.Nm
|
|
||||||
will attempt to reverse the process, and render Game Boy data into an image.
|
|
||||||
See
|
|
||||||
.Sx REVERSE MODE
|
|
||||||
below for details.
|
|
||||||
.Pp
|
|
||||||
.Ar width
|
|
||||||
is the image's width, in tiles
|
|
||||||
.Pq including any margins specified by Fl L .
|
|
||||||
.It Fl s Ar nb_colors , Fl Fl palette-size Ar nb_colors
|
|
||||||
Specify how many colors each palette contains, including the transparent one if any.
|
|
||||||
.Ar nb_colors
|
|
||||||
cannot be more than
|
|
||||||
.Ql 1 << Ar depth
|
|
||||||
.Pq see Fl d .
|
|
||||||
.It Fl t Ar tilemap , Fl Fl tilemap Ar tilemap
|
|
||||||
Generate a file of tile indices.
|
|
||||||
For each square of the input image, its corresponding tile map byte contains the index of the associated tile in the tile data file.
|
|
||||||
The IDs wrap around from 255 back to 0, and do not include the bank bit; use
|
|
||||||
.Fl a
|
|
||||||
for that.
|
|
||||||
Useful in combination with
|
|
||||||
.Fl u
|
|
||||||
and/or
|
|
||||||
.Fl m
|
|
||||||
to keep track of duplicate tiles.
|
|
||||||
.It Fl T , Fl Fl output-tilemap
|
|
||||||
Same as
|
|
||||||
.Fl t Ar path ,
|
|
||||||
where
|
|
||||||
.Ar path
|
|
||||||
is the input image's path with the extension set to
|
|
||||||
.Pa .tilemap .
|
|
||||||
.It Fl u , Fl Fl unique-tiles
|
|
||||||
Deduplicate identical tiles, and omit the duplicates from the tile data file.
|
|
||||||
Useful with a tile map
|
|
||||||
.Pq see Fl t
|
|
||||||
to keep track of the duplicated tiles.
|
|
||||||
.Pp
|
|
||||||
Note that if this option is enabled, no guarantee is made on the order in which tiles are output; while it
|
|
||||||
.Em should
|
|
||||||
be consistent across identical runs of a given
|
|
||||||
.Nm
|
|
||||||
release, the same is not true for different releases.
|
|
||||||
.It Fl V , Fl Fl version
|
|
||||||
Print the version of the program and exit.
|
|
||||||
.It Fl v , Fl Fl verbose
|
|
||||||
Be verbose.
|
|
||||||
The verbosity level is increased by one each time the flag is specified, with each level including the previous:
|
|
||||||
.Bl -enum -width 2n -compact
|
|
||||||
.It
|
|
||||||
.Nm
|
|
||||||
prints out its configuration before doing anything.
|
|
||||||
.It
|
|
||||||
A generic message is printed before doing most actions.
|
|
||||||
.It
|
|
||||||
Some of the actions' intermediate results are printed.
|
|
||||||
.It
|
|
||||||
Some internal debug printing is enabled.
|
|
||||||
.El
|
|
||||||
The verbosity level does not go past 6.
|
|
||||||
.Pp
|
|
||||||
Note that verbose output is only intended to be consumed by humans, and may change without notice between RGBDS releases; relying on those for scripts is not advised.
|
|
||||||
.It Fl x Ar quantity , Fl Fl trim-end Ar quantity
|
|
||||||
Do not output the last
|
|
||||||
.Ar quantity
|
|
||||||
tiles to the tile data file; no other output is affected.
|
|
||||||
This is useful for trimming
|
|
||||||
.Dq filler
|
|
||||||
/ blank squares at the end of an image.
|
|
||||||
If fewer than
|
|
||||||
.Ar quantity
|
|
||||||
tiles would have been emitted, the file will be empty.
|
|
||||||
.Pp
|
|
||||||
Note that this is done
|
|
||||||
.Em after
|
|
||||||
deduplication if
|
|
||||||
.Fl u
|
|
||||||
was enabled, so you probably don't want to use this option in combination with
|
|
||||||
.Fl u .
|
|
||||||
Note also that the tiles that don't get output will not count towards
|
|
||||||
.Fl N Ap s
|
|
||||||
limit.
|
|
||||||
.It Fl Z , Fl Fl columns
|
|
||||||
Read squares from the PNG in column-major order (column by column), instead of the default row-major order (line by line).
|
|
||||||
This primarily affects tile map and attribute map output, although it may also change generated tile data and palettes.
|
|
||||||
.El
|
|
||||||
.Ss At-files
|
|
||||||
In a given project, many images are to be converted with different flags.
|
|
||||||
The traditional way of solving this problem has been to specify the different flags for each image in the Makefile / build script; this can be inconvenient, as it centralizes all those flags away from the images they concern.
|
|
||||||
.Pp
|
|
||||||
To avoid these drawbacks,
|
|
||||||
.Nm
|
|
||||||
supports
|
|
||||||
.Dq at-files :
|
|
||||||
any command-line argument that begins with an at sign
|
|
||||||
.Pq Ql @
|
|
||||||
is interpreted as one.
|
|
||||||
The rest of the argument (without the @, that is) is interpreted as the path to a file, whose contents are interpreted as if given on the command line.
|
|
||||||
At-files can be stored right next to the corresponding image, for example.
|
|
||||||
.Pp
|
|
||||||
Since the contents of at-files are interpreted by
|
|
||||||
.Nm ,
|
|
||||||
.Sy no shell processing is performed ;
|
|
||||||
for example, shell variables are not expanded
|
|
||||||
.Ql ( $PWD ,
|
|
||||||
.Ql %WINDIR% ,
|
|
||||||
etc.).
|
|
||||||
In at-files, lines that are empty or contain only whitespace are ignored; lines that begin with a hash sign
|
|
||||||
.Pq Ql # ,
|
|
||||||
optionally preceded by whitespace, are considered comments and also ignored.
|
|
||||||
Each line can contain any number of arguments, which are separated by whitespace.
|
|
||||||
.Pq \&No quoting feature to prevent this is provided.
|
|
||||||
.Pp
|
|
||||||
Note that this special meaning given to arguments has less precedence than option arguments, and that the standard
|
|
||||||
.Ql --
|
|
||||||
to stop option processing also disables at-file processing.
|
|
||||||
For example, the following command line processes
|
|
||||||
.Ql @tilesets/town.png ,
|
|
||||||
outputs tile data to
|
|
||||||
.Ql @tilesets/town.2bpp ,
|
|
||||||
and reads command-line options from
|
|
||||||
.Ql tilesets/town.flags
|
|
||||||
then
|
|
||||||
.Ql tilesets.flags :
|
|
||||||
.Pp
|
|
||||||
.Dl $ rgbgfx -o @tilesets/town.2bpp @tilesets/town.flags @tilesets.flags -- @tilesets/town.png
|
|
||||||
.Pp
|
|
||||||
At-files can also specify the input image directly, and call for more at-files, both using the regular syntax.
|
|
||||||
Note that while
|
|
||||||
.Ql --
|
|
||||||
can be used in an at-file (with identical semantics), it is only effective inside of it\(emnormal option processing continues in the parent scope.
|
|
||||||
.Sh PALETTE SPECIFICATION FORMATS
|
|
||||||
The following formats are supported:
|
|
||||||
.Bl -tag -width Ds
|
|
||||||
.It Sy act
|
|
||||||
.Lk https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_pgfId-1070626 Adobe Photoshop color table .
|
|
||||||
.It Sy aco
|
|
||||||
.Lk https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_pgfId-1055819 Adobe Photoshop color swatch .
|
|
||||||
.It Sy psp
|
|
||||||
.Lk https://www.selapa.net/swatches/colors/fileformats.php#psp_pal Paint Shop Pro palette .
|
|
||||||
.El
|
|
||||||
.Pp
|
|
||||||
If you wish for another format to be supported, please open an issue (see
|
|
||||||
.Sx BUGS
|
|
||||||
below) or contact us, and supply a few sample files.
|
|
||||||
.Sh PALETTE GENERATION
|
|
||||||
.Nm
|
|
||||||
must generate palettes from the colors in the input image, unless
|
|
||||||
.Fl c
|
|
||||||
was used; in that case, the provided palettes will be used.
|
|
||||||
.Sy If the order of colors in the palettes is important to you ,
|
|
||||||
for example because you want to use palette swaps, please use
|
|
||||||
.Fl c
|
|
||||||
to specify the palette explicitly.
|
|
||||||
.Pp
|
|
||||||
First, if the image contains
|
|
||||||
.Em any
|
|
||||||
transparent pixel, color #0 of
|
|
||||||
.Em all
|
|
||||||
palettes will be allocated to it.
|
|
||||||
This is done
|
|
||||||
.Sy even if palettes were explicitly specified using Fl c ;
|
|
||||||
then the specification only covers color #1 onwards.
|
|
||||||
.Pq If you do not want this, ask your image editor to remove the alpha channel.
|
|
||||||
.Pp
|
|
||||||
After generating palettes,
|
|
||||||
.Nm
|
|
||||||
sorts colors within those palettes using the following rules:
|
|
||||||
.EQ
|
|
||||||
delim $$
|
|
||||||
.EN
|
|
||||||
.Bl -bullet -offset indent
|
|
||||||
.It
|
|
||||||
If the PNG file internally contains a palette (often dubbed an
|
|
||||||
.Dq indexed
|
|
||||||
PNG), then colors in each output palette will be sorted according to their order in the PNG's palette.
|
|
||||||
Any unused entries will be ignored, and only the first entry is considered if there are any duplicates.
|
|
||||||
.Po If you want a given color to appear more than once, or an unused color to appear at all, you should specify the palettes explicitly instead using Fl c ;
|
|
||||||
.Fl c Cm embedded
|
|
||||||
may be appropriate.
|
|
||||||
.Pc
|
|
||||||
.It
|
|
||||||
Otherwise, if the PNG only contains shades of gray, they will be categorized into as many
|
|
||||||
.Dq bins
|
|
||||||
as there are colors per palette, and the palette is set to these bins.
|
|
||||||
The darkest gray will end up in bin #0, and so on; note that this is the opposite of the RGB method below.
|
|
||||||
If two distinct grays end up in the same bin, the RGB method is used instead.
|
|
||||||
.Pp
|
|
||||||
Be careful that
|
|
||||||
.Nm
|
|
||||||
is picky about what it considers
|
|
||||||
.Dq grays :
|
|
||||||
the red, green, and blue components of each color must
|
|
||||||
.Em all
|
|
||||||
be
|
|
||||||
.Em exactly
|
|
||||||
the same.
|
|
||||||
.It
|
|
||||||
If none of the above apply, colors are sorted from lightest (first) to darkest (last).
|
|
||||||
The definition of luminance that
|
|
||||||
.Nm
|
|
||||||
uses is
|
|
||||||
.Do
|
|
||||||
$2126 times red + 7152 times green + 722 times blue$
|
|
||||||
.Dc .
|
|
||||||
.El
|
|
||||||
.EQ
|
|
||||||
delim off
|
|
||||||
.EN
|
|
||||||
.Pp
|
|
||||||
Note that the
|
|
||||||
.Dq indexed
|
|
||||||
behavior depends on an internal detail of how the PNG is saved, specifically its
|
|
||||||
.Ql PLTE
|
|
||||||
chunk.
|
|
||||||
Since few image editors (such as GIMP) expose that detail, this behavior is only kept for compatibility and should be considered deprecated.
|
|
||||||
.Sh OUTPUT FILES
|
|
||||||
All files output by
|
|
||||||
.Nm
|
|
||||||
are binary files, and designed to follow the Game Boy and Game Boy Color's native formats.
|
|
||||||
What follows is succinct descriptions of those formats, including
|
|
||||||
.Nm Ns -specific
|
|
||||||
details.
|
|
||||||
For more complete, beginner-friendly descriptions of the native formats with illustrations, please check out
|
|
||||||
.Lk https://gbdev.io/pandocs/Rendering Pan Docs .
|
|
||||||
.Ss Tile data
|
|
||||||
Tile data is output like a binary dump of VRAM, with no padding between tiles.
|
|
||||||
Each tile is 16 bytes, 2 per row of 8 pixels; the bits of color IDs are split into each byte
|
|
||||||
.Pq or Dq bitplane .
|
|
||||||
The leftmost pixel's color ID is stored in the two bytes' most significant bits, and the rightmost pixel's color ID in their least significant bits.
|
|
||||||
.Pp
|
|
||||||
When the bit depth
|
|
||||||
.Pq Fl d
|
|
||||||
is set to 1, the most significant bitplane (second byte) of each row, being all zeros, is simply not output.
|
|
||||||
.Ss Palette data
|
|
||||||
Palette data is output like a dump of palette memory.
|
|
||||||
Each color is written as GBC-native little-endian RGB555, with the unused bit 15 set to 0.
|
|
||||||
There is no padding between colors, nor between palettes; however, empty colors in the palettes are output as 0xFFFF.
|
|
||||||
.EQ
|
|
||||||
delim $$
|
|
||||||
.EN
|
|
||||||
For example, if 5 palettes are generated with
|
|
||||||
.Fl s Cm 4 ,
|
|
||||||
the palette data file will be $2 times 4 times 5 = 40$ bytes long, even if some palettes contain less than 3 colors.
|
|
||||||
.EQ
|
|
||||||
delim off
|
|
||||||
.EN
|
|
||||||
Note that
|
|
||||||
.Fl n
|
|
||||||
only caps how many palettes are generated (and thus this file's size), but fewer may be generated still.
|
|
||||||
.Ss Tile map data
|
|
||||||
A tile map is an array of tile IDs, with one byte per tile ID.
|
|
||||||
The first byte always corresponds to the ID of the tile in top-left corner of the input image; the second byte is either the ID of the tile to its right (by default), or below it
|
|
||||||
.Pq with Fl Z ;
|
|
||||||
and so on, continuing in the same direction.
|
|
||||||
Rows / columns (respectively) are stored consecutively, with no padding.
|
|
||||||
.Ss Attribute map data
|
|
||||||
Attribute maps mirror the format of tile maps, like on the GBC, especially the order in which bytes are output.
|
|
||||||
The contents of individual bytes follows the GBC's native format:
|
|
||||||
.Bl -column "Bit 2\(en0" "Background Palette number"
|
|
||||||
.It Bit 7 Ta BG-to-OAM Priority Ta Set to 0
|
|
||||||
.It Bit 6 Ta Vertical Flip Ta 0=Normal, 1=Mirror vertically
|
|
||||||
.It Bit 5 Ta Horizontal Flip Ta 0=Normal, 1=Mirror horizontally
|
|
||||||
.It Bit 4 Ta Not used Ta Set to 0
|
|
||||||
.It Bit 3 Ta Tile VRAM Bank number Ta 0=Bank 0, 1=Bank 1
|
|
||||||
.It Bit 2\(en0 Ta Background Palette number Ta BGP0-7
|
|
||||||
.El
|
|
||||||
.Pp
|
|
||||||
Note that if more than 8 palettes are used, only the lowest 3 bits of the palette ID are output.
|
|
||||||
.Sh REVERSE MODE
|
|
||||||
.Nm
|
|
||||||
can produce a PNG image from valid data.
|
|
||||||
This may be useful for ripping graphics, recovering lost source images, etc.
|
|
||||||
An important caveat on that last one, though: the conversion process is
|
|
||||||
.Sy lossy
|
|
||||||
both ways, so the
|
|
||||||
.Do reversed Dc image won't be perfectly identical to the original\(embut it should be close to a Game Boy's output .
|
|
||||||
.Pq Keep in mind that many of consoles output different colors, so there is no true reference rendering.
|
|
||||||
.Pp
|
|
||||||
When using reverse mode, make sure to pass the same flags that were given when generating the data, especially
|
|
||||||
.Fl C , d , N , s , x ,
|
|
||||||
and
|
|
||||||
.Fl Z .
|
|
||||||
.Do Sx At-files Dc may help with this .
|
|
||||||
.Nm
|
|
||||||
will warn about any inconsistencies it detects.
|
|
||||||
.Pp
|
|
||||||
Files that are normally outputs
|
|
||||||
.Pq Fl a , p , t
|
|
||||||
become inputs, and
|
|
||||||
.Ar file
|
|
||||||
will be written to instead of read from, and thus needs not exist beforehand.
|
|
||||||
Any of these inputs not passed is assumed to be some default:
|
|
||||||
.Bl -column "attribute map"
|
|
||||||
.It palettes Ta Unspecified palette data makes
|
|
||||||
.Nm
|
|
||||||
assume DMG (monochrome Game Boy) mode: a single palette of 4 grays.
|
|
||||||
It is possible to pass palettes using
|
|
||||||
.Fl c
|
|
||||||
instead of
|
|
||||||
.Fl p .
|
|
||||||
.It tile data Ta Tile data must be provided, as there is no reasonable assumption to fall back on.
|
|
||||||
.It tile map Ta A missing tile map makes
|
|
||||||
.Nm
|
|
||||||
assume that tiles were not deduplicated, and should be laid out in the order they are stored.
|
|
||||||
.It attribute map Ta Without an attribute map,
|
|
||||||
.Nm
|
|
||||||
assumes that no tiles were mirrored.
|
|
||||||
.El
|
|
||||||
.Sh NOTES
|
|
||||||
Some flags have had their functionality removed.
|
|
||||||
.Fl D , f ,
|
|
||||||
and
|
|
||||||
.Fl F
|
|
||||||
are now ignored, and
|
|
||||||
.Fl h
|
|
||||||
is an alias for the new (and less confusingly named)
|
|
||||||
.Fl Z .
|
|
||||||
These will be removed and/or repurposed in future versions of
|
|
||||||
.Nm ,
|
|
||||||
so relying on them is not recommended.
|
|
||||||
The same applies to the corresponding long options.
|
|
||||||
.Pp
|
|
||||||
If you are curious, you may find out that palette generation is an NP-complete problem, so
|
|
||||||
.Nm
|
|
||||||
does not attempt to find the optimal solution, but instead to find a good one in a reasonable amount of time.
|
|
||||||
It is possible to compute the optimal solution externally (using a solver, for example), and then provide it to
|
|
||||||
.Nm
|
|
||||||
via
|
|
||||||
.Fl c .
|
|
||||||
.Sh EXAMPLES
|
|
||||||
The following will only validate the PNG (check its size, that all tiles have a suitable amount of colors, etc.), but output nothing:
|
|
||||||
.Pp
|
|
||||||
.Dl $ rgbgfx src/res/maps/overworld/tileset.png
|
|
||||||
.Pp
|
|
||||||
The following will convert the image using the two given palettes (and only those), and store the generated 2bpp tile data in
|
|
||||||
.Ql tileset.2bpp ,
|
|
||||||
and the attribute map in
|
|
||||||
.Ql tileset.attrmap .
|
|
||||||
.Pp
|
|
||||||
.Dl $ rgbgfx -c '#ffffff,#8d05de, #dc7905,#000000 ; #fff,#8d05de, #7e0000 \&, #000' -A -o tileset.2bpp tileset.png
|
|
||||||
.Pp
|
|
||||||
TODO: more examples.
|
|
||||||
.Sh BUGS
|
|
||||||
Please report bugs and mistakes in this man page on
|
|
||||||
.Lk https://github.com/gbdev/rgbds/issues GitHub .
|
|
||||||
Bug reports and feature requests about RGBDS are also welcome!
|
|
||||||
.Sh SEE ALSO
|
|
||||||
.Xr rgbds 7 ,
|
|
||||||
.Xr rgbasm 1 ,
|
|
||||||
.Xr rgblink 1 ,
|
|
||||||
.Xr rgbfix 1 ,
|
|
||||||
.Xr gbz80 7
|
|
||||||
.Pp
|
|
||||||
The Game Boy hardware reference
|
|
||||||
.Lk https://gbdev.io/pandocs/Rendering.html Pan Docs ,
|
|
||||||
particularly the section about graphics.
|
|
||||||
.Sh HISTORY
|
|
||||||
.Nm
|
|
||||||
was originally created by
|
|
||||||
.An stag019
|
|
||||||
to be included in RGBDS.
|
|
||||||
It was later rewritten by
|
|
||||||
.An ISSOtm ,
|
|
||||||
and is now maintained by a number of contributors at
|
|
||||||
.Lk https://github.com/gbdev/rgbds .
|
|
||||||
2
src/.gitignore
vendored
2
src/.gitignore
vendored
@@ -1,2 +0,0 @@
|
|||||||
# Generated by CMake
|
|
||||||
/.version.c
|
|
||||||
@@ -6,14 +6,20 @@
|
|||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
#
|
#
|
||||||
|
|
||||||
configure_file(version.c _version.c ESCAPE_QUOTES)
|
|
||||||
|
|
||||||
set(common_src
|
set(common_src
|
||||||
"error.c"
|
"extern/err.c"
|
||||||
"extern/getopt.c"
|
"extern/getopt.c"
|
||||||
"_version.c"
|
"version.c"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
find_package(PkgConfig)
|
||||||
|
if(MSVC OR NOT PKG_CONFIG_FOUND)
|
||||||
|
# fallback to find_package
|
||||||
|
find_package(PNG REQUIRED)
|
||||||
|
else()
|
||||||
|
pkg_check_modules(LIBPNG REQUIRED libpng)
|
||||||
|
endif()
|
||||||
|
|
||||||
find_package(BISON REQUIRED)
|
find_package(BISON REQUIRED)
|
||||||
set(BISON_FLAGS "-Wall")
|
set(BISON_FLAGS "-Wall")
|
||||||
# Set sompe optimization flags on versions that support them
|
# Set sompe optimization flags on versions that support them
|
||||||
@@ -62,16 +68,9 @@ set(rgbfix_src
|
|||||||
)
|
)
|
||||||
|
|
||||||
set(rgbgfx_src
|
set(rgbgfx_src
|
||||||
"gfx/main.cpp"
|
"gfx/gb.c"
|
||||||
"gfx/pal_packing.cpp"
|
"gfx/main.c"
|
||||||
"gfx/pal_sorting.cpp"
|
"gfx/makepng.c"
|
||||||
"gfx/pal_spec.cpp"
|
|
||||||
"gfx/process.cpp"
|
|
||||||
"gfx/proto_palette.cpp"
|
|
||||||
"gfx/reverse.cpp"
|
|
||||||
"gfx/rgba.cpp"
|
|
||||||
"extern/getopt.c"
|
|
||||||
"error.c"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set(rgblink_src
|
set(rgblink_src
|
||||||
@@ -81,7 +80,6 @@ set(rgblink_src
|
|||||||
"link/output.c"
|
"link/output.c"
|
||||||
"link/patch.c"
|
"link/patch.c"
|
||||||
"link/script.c"
|
"link/script.c"
|
||||||
"link/sdas_obj.c"
|
|
||||||
"link/section.c"
|
"link/section.c"
|
||||||
"link/symbol.c"
|
"link/symbol.c"
|
||||||
"hashmap.c"
|
"hashmap.c"
|
||||||
@@ -97,6 +95,22 @@ foreach(PROG "asm" "fix" "gfx" "link")
|
|||||||
install(TARGETS rgb${PROG} RUNTIME DESTINATION bin)
|
install(TARGETS rgb${PROG} RUNTIME DESTINATION bin)
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
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
|
if(LIBPNG_FOUND) # pkg-config
|
||||||
target_include_directories(rgbgfx PRIVATE ${LIBPNG_INCLUDE_DIRS})
|
target_include_directories(rgbgfx PRIVATE ${LIBPNG_INCLUDE_DIRS})
|
||||||
target_link_directories(rgbgfx PRIVATE ${LIBPNG_LIBRARY_DIRS})
|
target_link_directories(rgbgfx PRIVATE ${LIBPNG_LIBRARY_DIRS})
|
||||||
|
|||||||
@@ -21,60 +21,61 @@
|
|||||||
|
|
||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
|
|
||||||
// Charmaps are stored using a structure known as "trie".
|
/*
|
||||||
// Essentially a tree, where each nodes stores a single character's worth of info:
|
* Charmaps are stored using a structure known as "trie".
|
||||||
// whether there exists a mapping that ends at the current character,
|
* 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 {
|
struct Charnode {
|
||||||
bool isTerminal; // Whether there exists a mapping that ends here
|
bool isTerminal; /* Whether there exists a mapping that ends here */
|
||||||
uint8_t value; // If the above is true, its corresponding value
|
uint8_t value; /* If the above is true, its corresponding value */
|
||||||
// This MUST be indexes and not pointers, because pointers get invalidated by `realloc`!!
|
/* 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
|
size_t next[255]; /* Indexes of where to go next, 0 = nowhere */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define INITIAL_CAPACITY 32
|
#define INITIAL_CAPACITY 32
|
||||||
|
|
||||||
struct Charmap {
|
struct Charmap {
|
||||||
char *name;
|
char *name;
|
||||||
size_t usedNodes; // How many nodes are being used
|
size_t usedNodes; /* How many nodes are being used */
|
||||||
size_t capacity; // How many nodes have been allocated
|
size_t capacity; /* How many nodes have been allocated */
|
||||||
struct Charnode nodes[]; // first node is reserved for the root node
|
struct Charnode nodes[]; /* first node is reserved for the root node */
|
||||||
};
|
};
|
||||||
|
|
||||||
static HashMap charmaps;
|
static HashMap charmaps;
|
||||||
|
|
||||||
// Store pointers to hashmap nodes, so that there is only one pointer to the memory block
|
static struct Charmap *currentCharmap;
|
||||||
// that gets reallocated.
|
|
||||||
static struct Charmap **currentCharmap;
|
|
||||||
|
|
||||||
struct CharmapStackEntry {
|
struct CharmapStackEntry {
|
||||||
struct Charmap **charmap;
|
struct Charmap *charmap;
|
||||||
struct CharmapStackEntry *next;
|
struct CharmapStackEntry *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CharmapStackEntry *charmapStack;
|
struct CharmapStackEntry *charmapStack;
|
||||||
|
|
||||||
static struct Charmap *charmap_Get(char const *name)
|
static inline struct Charmap *charmap_Get(const char *name)
|
||||||
{
|
{
|
||||||
return hash_GetElement(charmaps, name);
|
return hash_GetElement(charmaps, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void resizeCharmap(struct Charmap **map, size_t capacity)
|
static inline struct Charmap *resizeCharmap(struct Charmap *map, size_t capacity)
|
||||||
{
|
{
|
||||||
*map = realloc(*map, sizeof(**map) + sizeof(*(*map)->nodes) * capacity);
|
struct Charmap *new = realloc(map, sizeof(*map) + sizeof(*map->nodes) * capacity);
|
||||||
|
|
||||||
if (!*map)
|
if (!new)
|
||||||
fatalerror("Failed to %s charmap: %s\n",
|
fatalerror("Failed to %s charmap: %s\n",
|
||||||
*map ? "create" : "resize", strerror(errno));
|
map ? "create" : "resize", strerror(errno));
|
||||||
(**map).capacity = capacity;
|
new->capacity = capacity;
|
||||||
|
return new;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void initNode(struct Charnode *node)
|
static inline void initNode(struct Charnode *node)
|
||||||
{
|
{
|
||||||
node->isTerminal = false;
|
node->isTerminal = false;
|
||||||
memset(node->next, 0, sizeof(node->next));
|
memset(node->next, 0, sizeof(node->next));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Charmap *charmap_New(char const *name, char const *baseName)
|
struct Charmap *charmap_New(const char *name, const char *baseName)
|
||||||
{
|
{
|
||||||
struct Charmap *base = NULL;
|
struct Charmap *base = NULL;
|
||||||
|
|
||||||
@@ -92,20 +93,21 @@ struct Charmap *charmap_New(char const *name, char const *baseName)
|
|||||||
return charmap;
|
return charmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init the new charmap's fields
|
/* Init the new charmap's fields */
|
||||||
if (base) {
|
if (base) {
|
||||||
resizeCharmap(&charmap, base->capacity);
|
charmap = resizeCharmap(NULL, base->capacity);
|
||||||
charmap->usedNodes = base->usedNodes;
|
charmap->usedNodes = base->usedNodes;
|
||||||
|
|
||||||
memcpy(charmap->nodes, base->nodes, sizeof(base->nodes[0]) * charmap->usedNodes);
|
memcpy(charmap->nodes, base->nodes, sizeof(base->nodes[0]) * charmap->usedNodes);
|
||||||
} else {
|
} else {
|
||||||
resizeCharmap(&charmap, INITIAL_CAPACITY);
|
charmap = resizeCharmap(NULL, INITIAL_CAPACITY);
|
||||||
charmap->usedNodes = 1;
|
charmap->usedNodes = 1;
|
||||||
initNode(&charmap->nodes[0]); // Init the root node
|
initNode(&charmap->nodes[0]); /* Init the root node */
|
||||||
}
|
}
|
||||||
charmap->name = strdup(name);
|
charmap->name = strdup(name);
|
||||||
|
|
||||||
currentCharmap = (struct Charmap **)hash_AddElement(charmaps, charmap->name, charmap);
|
hash_AddElement(charmaps, charmap->name, charmap);
|
||||||
|
currentCharmap = charmap;
|
||||||
|
|
||||||
return charmap;
|
return charmap;
|
||||||
}
|
}
|
||||||
@@ -116,9 +118,9 @@ void charmap_Delete(struct Charmap *charmap)
|
|||||||
free(charmap);
|
free(charmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void charmap_Set(char const *name)
|
void charmap_Set(const char *name)
|
||||||
{
|
{
|
||||||
struct Charmap **charmap = (struct Charmap **)hash_GetNode(charmaps, name);
|
struct Charmap *charmap = charmap_Get(name);
|
||||||
|
|
||||||
if (charmap == NULL)
|
if (charmap == NULL)
|
||||||
error("Charmap '%s' doesn't exist\n", name);
|
error("Charmap '%s' doesn't exist\n", name);
|
||||||
@@ -156,26 +158,25 @@ void charmap_Pop(void)
|
|||||||
|
|
||||||
void charmap_Add(char *mapping, uint8_t value)
|
void charmap_Add(char *mapping, uint8_t value)
|
||||||
{
|
{
|
||||||
struct Charmap *charmap = *currentCharmap;
|
struct Charnode *node = ¤tCharmap->nodes[0];
|
||||||
struct Charnode *node = &charmap->nodes[0];
|
|
||||||
|
|
||||||
for (uint8_t c; *mapping; mapping++) {
|
for (uint8_t c; *mapping; mapping++) {
|
||||||
c = *mapping - 1;
|
c = *mapping - 1;
|
||||||
|
|
||||||
if (node->next[c]) {
|
if (node->next[c]) {
|
||||||
node = &charmap->nodes[node->next[c]];
|
node = ¤tCharmap->nodes[node->next[c]];
|
||||||
} else {
|
} else {
|
||||||
// Register next available node
|
/* Register next available node */
|
||||||
node->next[c] = charmap->usedNodes;
|
node->next[c] = currentCharmap->usedNodes;
|
||||||
// If no more nodes are available, get new ones
|
/* If no more nodes are available, get new ones */
|
||||||
if (charmap->usedNodes == charmap->capacity) {
|
if (currentCharmap->usedNodes == currentCharmap->capacity) {
|
||||||
charmap->capacity *= 2;
|
currentCharmap->capacity *= 2;
|
||||||
resizeCharmap(currentCharmap, charmap->capacity);
|
currentCharmap = resizeCharmap(currentCharmap, currentCharmap->capacity);
|
||||||
charmap = *currentCharmap;
|
hash_ReplaceElement(charmaps, currentCharmap->name, currentCharmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Switch to and init new node
|
/* Switch to and init new node */
|
||||||
node = &charmap->nodes[charmap->usedNodes++];
|
node = ¤tCharmap->nodes[currentCharmap->usedNodes++];
|
||||||
initNode(node);
|
initNode(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,77 +190,57 @@ void charmap_Add(char *mapping, uint8_t value)
|
|||||||
|
|
||||||
size_t charmap_Convert(char const *input, uint8_t *output)
|
size_t charmap_Convert(char const *input, uint8_t *output)
|
||||||
{
|
{
|
||||||
uint8_t *start = output;
|
/*
|
||||||
|
* The goal is to match the longest mapping possible.
|
||||||
while (charmap_ConvertNext(&input, &output))
|
* 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.
|
||||||
return output - start;
|
*/
|
||||||
}
|
size_t outputLen = 0;
|
||||||
|
struct Charnode const *node = ¤tCharmap->nodes[0];
|
||||||
size_t charmap_ConvertNext(char const **input, uint8_t **output)
|
|
||||||
{
|
|
||||||
// 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.
|
|
||||||
struct Charmap const *charmap = *currentCharmap;
|
|
||||||
struct Charnode const *node = &charmap->nodes[0];
|
|
||||||
struct Charnode const *match = NULL;
|
struct Charnode const *match = NULL;
|
||||||
size_t rewindDistance = 0;
|
size_t rewindDistance = 0;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
uint8_t c = **input - 1;
|
/* We still want NULs to reach the `else` path, to give a chance to rewind */
|
||||||
|
uint8_t c = *input - 1;
|
||||||
|
|
||||||
if (**input && node->next[c]) {
|
if (*input && node->next[c]) {
|
||||||
// Consume that char
|
input++; /* Consume that char */
|
||||||
(*input)++;
|
|
||||||
rewindDistance++;
|
rewindDistance++;
|
||||||
|
|
||||||
// Advance to next node (index starts at 1)
|
node = ¤tCharmap->nodes[node->next[c]];
|
||||||
node = &charmap->nodes[node->next[c]];
|
|
||||||
if (node->isTerminal) {
|
if (node->isTerminal) {
|
||||||
// This node matches, register it
|
|
||||||
match = node;
|
match = node;
|
||||||
rewindDistance = 0; // If no longer match is found, rewind here
|
rewindDistance = 0; /* Rewind from after the match */
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// We are at a dead end (either because we reached the end of input, or of
|
input -= rewindDistance; /* Rewind */
|
||||||
// the trie), so rewind up to the last match, and output.
|
rewindDistance = 0;
|
||||||
*input -= rewindDistance; // This will rewind all the way if no match found
|
node = ¤tCharmap->nodes[0];
|
||||||
|
|
||||||
if (match) { // A match was found, use it
|
if (match) { /* Arrived at a dead end with a match found */
|
||||||
if (output)
|
*output++ = match->value;
|
||||||
*(*output)++ = match->value;
|
outputLen++;
|
||||||
|
match = NULL; /* Reset match for next round */
|
||||||
|
|
||||||
return 1;
|
} else if (*input) { /* No match found */
|
||||||
|
size_t codepointLen = readUTF8Char(output, input);
|
||||||
|
|
||||||
} else if (**input) { // No match found, but there is some input left
|
if (codepointLen == 0) {
|
||||||
int firstChar = **input;
|
|
||||||
// This will write the codepoint's value to `output`, little-endian
|
|
||||||
size_t codepointLen = readUTF8Char(output ? *output : NULL,
|
|
||||||
*input);
|
|
||||||
|
|
||||||
if (codepointLen == 0)
|
|
||||||
error("Input string is not valid UTF-8!\n");
|
error("Input string is not valid UTF-8!\n");
|
||||||
|
break;
|
||||||
// OK because UTF-8 has no NUL in multi-byte chars
|
}
|
||||||
*input += codepointLen;
|
input += codepointLen; /* OK because UTF-8 has no NUL in multi-byte chars */
|
||||||
if (output)
|
output += codepointLen;
|
||||||
*output += codepointLen;
|
outputLen += codepointLen;
|
||||||
|
|
||||||
// Check if the character map is not the default "main" one, or if
|
|
||||||
// it has any mappings defined
|
|
||||||
if (strcmp(charmap->name, "main") || charmap->usedNodes > 1)
|
|
||||||
warning(WARNING_UNMAPPED_CHAR,
|
|
||||||
"Unmapped character %s\n", printChar(firstChar));
|
|
||||||
|
|
||||||
return codepointLen;
|
|
||||||
|
|
||||||
} else { // End of input
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!*input)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return outputLen;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,9 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Fixed-point math routines
|
/*
|
||||||
|
* Fixed-point math routines
|
||||||
|
*/
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
@@ -17,108 +19,151 @@
|
|||||||
#include "asm/symbol.h"
|
#include "asm/symbol.h"
|
||||||
#include "asm/warning.h"
|
#include "asm/warning.h"
|
||||||
|
|
||||||
|
#define fix2double(i) ((double)((i) / 65536.0))
|
||||||
|
#define double2fix(d) ((int32_t)round((d) * 65536.0))
|
||||||
|
|
||||||
|
// pi radians == 32768 fixed-point "degrees"
|
||||||
|
#define fdeg2rad(f) ((f) * (M_PI / 32768.0))
|
||||||
|
#define rad2fdeg(r) ((r) * (32768.0 / M_PI))
|
||||||
|
|
||||||
#ifndef M_PI
|
#ifndef M_PI
|
||||||
#define M_PI 3.14159265358979323846
|
#define M_PI 3.14159265358979323846
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define fix2double(i) ((double)((i) / fix_PrecisionFactor()))
|
/*
|
||||||
#define double2fix(d) ((int32_t)round((d) * fix_PrecisionFactor()))
|
* Return the _PI symbol value
|
||||||
|
*/
|
||||||
// pi*2 radians == 2**fixPrecision fixed-point "degrees"
|
int32_t fix_Callback_PI(void)
|
||||||
#define fdeg2rad(f) ((f) * (M_PI * 2) / fix_PrecisionFactor())
|
|
||||||
#define rad2fdeg(r) ((r) * fix_PrecisionFactor() / (M_PI * 2))
|
|
||||||
|
|
||||||
uint8_t fixPrecision;
|
|
||||||
|
|
||||||
double fix_PrecisionFactor(void)
|
|
||||||
{
|
{
|
||||||
return pow(2.0, fixPrecision);
|
warning(WARNING_OBSOLETE, "`_PI` is deprecated; use 3.14159\n");
|
||||||
|
|
||||||
|
return double2fix(M_PI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print a fixed point value
|
||||||
|
*/
|
||||||
void fix_Print(int32_t i)
|
void fix_Print(int32_t i)
|
||||||
{
|
{
|
||||||
uint32_t u = i;
|
uint32_t u = i;
|
||||||
char const *sign = "";
|
const char *sign = "";
|
||||||
|
|
||||||
if (i < 0) {
|
if (i < 0) {
|
||||||
u = -u;
|
u = -u;
|
||||||
sign = "-";
|
sign = "-";
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("%s%" PRIu32 ".%05" PRIu32, sign, u >> fixPrecision,
|
printf("%s%" PRIu32 ".%05" PRIu32, sign, u >> 16,
|
||||||
((uint32_t)(fix2double(u) * 100000 + 0.5)) % 100000);
|
((uint32_t)(fix2double(u) * 100000 + 0.5)) % 100000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate sine
|
||||||
|
*/
|
||||||
int32_t fix_Sin(int32_t i)
|
int32_t fix_Sin(int32_t i)
|
||||||
{
|
{
|
||||||
return double2fix(sin(fdeg2rad(fix2double(i))));
|
return double2fix(sin(fdeg2rad(fix2double(i))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate cosine
|
||||||
|
*/
|
||||||
int32_t fix_Cos(int32_t i)
|
int32_t fix_Cos(int32_t i)
|
||||||
{
|
{
|
||||||
return double2fix(cos(fdeg2rad(fix2double(i))));
|
return double2fix(cos(fdeg2rad(fix2double(i))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate tangent
|
||||||
|
*/
|
||||||
int32_t fix_Tan(int32_t i)
|
int32_t fix_Tan(int32_t i)
|
||||||
{
|
{
|
||||||
return double2fix(tan(fdeg2rad(fix2double(i))));
|
return double2fix(tan(fdeg2rad(fix2double(i))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate arcsine
|
||||||
|
*/
|
||||||
int32_t fix_ASin(int32_t i)
|
int32_t fix_ASin(int32_t i)
|
||||||
{
|
{
|
||||||
return double2fix(rad2fdeg(asin(fix2double(i))));
|
return double2fix(rad2fdeg(asin(fix2double(i))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate arccosine
|
||||||
|
*/
|
||||||
int32_t fix_ACos(int32_t i)
|
int32_t fix_ACos(int32_t i)
|
||||||
{
|
{
|
||||||
return double2fix(rad2fdeg(acos(fix2double(i))));
|
return double2fix(rad2fdeg(acos(fix2double(i))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate arctangent
|
||||||
|
*/
|
||||||
int32_t fix_ATan(int32_t i)
|
int32_t fix_ATan(int32_t i)
|
||||||
{
|
{
|
||||||
return double2fix(rad2fdeg(atan(fix2double(i))));
|
return double2fix(rad2fdeg(atan(fix2double(i))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate atan2
|
||||||
|
*/
|
||||||
int32_t fix_ATan2(int32_t i, int32_t j)
|
int32_t fix_ATan2(int32_t i, int32_t j)
|
||||||
{
|
{
|
||||||
return double2fix(rad2fdeg(atan2(fix2double(i), fix2double(j))));
|
return double2fix(rad2fdeg(atan2(fix2double(i), fix2double(j))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Multiplication
|
||||||
|
*/
|
||||||
int32_t fix_Mul(int32_t i, int32_t j)
|
int32_t fix_Mul(int32_t i, int32_t j)
|
||||||
{
|
{
|
||||||
return double2fix(fix2double(i) * fix2double(j));
|
return double2fix(fix2double(i) * fix2double(j));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Division
|
||||||
|
*/
|
||||||
int32_t fix_Div(int32_t i, int32_t j)
|
int32_t fix_Div(int32_t i, int32_t j)
|
||||||
{
|
{
|
||||||
return double2fix(fix2double(i) / fix2double(j));
|
return double2fix(fix2double(i) / fix2double(j));
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t fix_Mod(int32_t i, int32_t j)
|
/*
|
||||||
{
|
* Power
|
||||||
return double2fix(fmod(fix2double(i), fix2double(j)));
|
*/
|
||||||
}
|
|
||||||
|
|
||||||
int32_t fix_Pow(int32_t i, int32_t j)
|
int32_t fix_Pow(int32_t i, int32_t j)
|
||||||
{
|
{
|
||||||
return double2fix(pow(fix2double(i), fix2double(j)));
|
return double2fix(pow(fix2double(i), fix2double(j)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Logarithm
|
||||||
|
*/
|
||||||
int32_t fix_Log(int32_t i, int32_t j)
|
int32_t fix_Log(int32_t i, int32_t j)
|
||||||
{
|
{
|
||||||
return double2fix(log(fix2double(i)) / log(fix2double(j)));
|
return double2fix(log(fix2double(i)) / log(fix2double(j)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Round
|
||||||
|
*/
|
||||||
int32_t fix_Round(int32_t i)
|
int32_t fix_Round(int32_t i)
|
||||||
{
|
{
|
||||||
return double2fix(round(fix2double(i)));
|
return double2fix(round(fix2double(i)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ceil
|
||||||
|
*/
|
||||||
int32_t fix_Ceil(int32_t i)
|
int32_t fix_Ceil(int32_t i)
|
||||||
{
|
{
|
||||||
return double2fix(ceil(fix2double(i)));
|
return double2fix(ceil(fix2double(i)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Floor
|
||||||
|
*/
|
||||||
int32_t fix_Floor(int32_t i)
|
int32_t fix_Floor(int32_t i)
|
||||||
{
|
{
|
||||||
return double2fix(floor(fix2double(i)));
|
return double2fix(floor(fix2double(i)));
|
||||||
|
|||||||
119
src/asm/format.c
119
src/asm/format.c
@@ -6,7 +6,6 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
@@ -15,7 +14,6 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "asm/fixpoint.h"
|
|
||||||
#include "asm/format.h"
|
#include "asm/format.h"
|
||||||
#include "asm/warning.h"
|
#include "asm/warning.h"
|
||||||
|
|
||||||
@@ -47,7 +45,7 @@ void fmt_UseCharacter(struct FormatSpec *fmt, int c)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
// sign
|
/* sign */
|
||||||
case ' ':
|
case ' ':
|
||||||
case '+':
|
case '+':
|
||||||
if (fmt->state > FORMAT_SIGN)
|
if (fmt->state > FORMAT_SIGN)
|
||||||
@@ -56,7 +54,7 @@ void fmt_UseCharacter(struct FormatSpec *fmt, int c)
|
|||||||
fmt->sign = c;
|
fmt->sign = c;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// prefix
|
/* prefix */
|
||||||
case '#':
|
case '#':
|
||||||
if (fmt->state > FORMAT_PREFIX)
|
if (fmt->state > FORMAT_PREFIX)
|
||||||
goto invalid;
|
goto invalid;
|
||||||
@@ -64,7 +62,7 @@ void fmt_UseCharacter(struct FormatSpec *fmt, int c)
|
|||||||
fmt->prefix = true;
|
fmt->prefix = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// align
|
/* align */
|
||||||
case '-':
|
case '-':
|
||||||
if (fmt->state > FORMAT_ALIGN)
|
if (fmt->state > FORMAT_ALIGN)
|
||||||
goto invalid;
|
goto invalid;
|
||||||
@@ -72,11 +70,11 @@ void fmt_UseCharacter(struct FormatSpec *fmt, int c)
|
|||||||
fmt->alignLeft = true;
|
fmt->alignLeft = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// pad and width
|
/* pad and width */
|
||||||
case '0':
|
case '0':
|
||||||
if (fmt->state < FORMAT_WIDTH)
|
if (fmt->state < FORMAT_WIDTH)
|
||||||
fmt->padZero = true;
|
fmt->padZero = true;
|
||||||
// fallthrough
|
/* fallthrough */
|
||||||
case '1':
|
case '1':
|
||||||
case '2':
|
case '2':
|
||||||
case '3':
|
case '3':
|
||||||
@@ -105,7 +103,7 @@ void fmt_UseCharacter(struct FormatSpec *fmt, int c)
|
|||||||
fmt->hasFrac = true;
|
fmt->hasFrac = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// type
|
/* type */
|
||||||
case 'd':
|
case 'd':
|
||||||
case 'u':
|
case 'u':
|
||||||
case 'X':
|
case 'X':
|
||||||
@@ -150,24 +148,20 @@ void fmt_PrintString(char *buf, size_t bufLen, struct FormatSpec const *fmt, cha
|
|||||||
size_t len = strlen(value);
|
size_t len = strlen(value);
|
||||||
size_t totalLen = fmt->width > len ? fmt->width : len;
|
size_t totalLen = fmt->width > len ? fmt->width : len;
|
||||||
|
|
||||||
if (totalLen > bufLen - 1) { // bufLen includes terminator
|
if (totalLen + 1 > bufLen) /* bufLen includes terminator */
|
||||||
error("Formatted string value too long\n");
|
error("Formatted string value too long\n");
|
||||||
totalLen = bufLen - 1;
|
|
||||||
if (len > totalLen)
|
|
||||||
len = totalLen;
|
|
||||||
}
|
|
||||||
assert(len < bufLen && totalLen < bufLen && len <= totalLen);
|
|
||||||
|
|
||||||
size_t padLen = totalLen - len;
|
size_t padLen = fmt->width > len ? fmt->width - len : 0;
|
||||||
|
|
||||||
if (fmt->alignLeft) {
|
if (fmt->alignLeft) {
|
||||||
memcpy(buf, value, len);
|
strncpy(buf, value, len < bufLen ? len : bufLen);
|
||||||
for (size_t i = len; i < totalLen; i++)
|
for (size_t i = 0; i < totalLen && len + i < bufLen; i++)
|
||||||
buf[i] = ' ';
|
buf[len + i] = ' ';
|
||||||
} else {
|
} else {
|
||||||
for (size_t i = 0; i < padLen; i++)
|
for (size_t i = 0; i < padLen && i < bufLen; i++)
|
||||||
buf[i] = ' ';
|
buf[i] = ' ';
|
||||||
memcpy(buf + padLen, value, len);
|
if (bufLen > padLen)
|
||||||
|
strncpy(buf + padLen, value, bufLen - padLen - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
buf[totalLen] = '\0';
|
buf[totalLen] = '\0';
|
||||||
@@ -183,7 +177,7 @@ void fmt_PrintNumber(char *buf, size_t bufLen, struct FormatSpec const *fmt, uin
|
|||||||
if (fmt->type == 's')
|
if (fmt->type == 's')
|
||||||
error("Formatting number as type 's'\n");
|
error("Formatting number as type 's'\n");
|
||||||
|
|
||||||
char sign = fmt->sign; // 0 or ' ' or '+'
|
char sign = fmt->sign; /* 0 or ' ' or '+' */
|
||||||
|
|
||||||
if (fmt->type == 'd' || fmt->type == 'f') {
|
if (fmt->type == 'd' || fmt->type == 'f') {
|
||||||
int32_t v = value;
|
int32_t v = value;
|
||||||
@@ -201,10 +195,10 @@ void fmt_PrintNumber(char *buf, size_t bufLen, struct FormatSpec const *fmt, uin
|
|||||||
: fmt->type == 'o' ? '&'
|
: fmt->type == 'o' ? '&'
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
char valueBuf[262]; // Max 5 digits + decimal + 255 fraction digits + terminator
|
char valueBuf[262]; /* Max 5 digits + decimal + 255 fraction digits + terminator */
|
||||||
|
|
||||||
if (fmt->type == 'b') {
|
if (fmt->type == 'b') {
|
||||||
// Special case for binary
|
/* Special case for binary */
|
||||||
char *ptr = valueBuf;
|
char *ptr = valueBuf;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
@@ -214,7 +208,7 @@ void fmt_PrintNumber(char *buf, size_t bufLen, struct FormatSpec const *fmt, uin
|
|||||||
|
|
||||||
*ptr = '\0';
|
*ptr = '\0';
|
||||||
|
|
||||||
// Reverse the digits
|
/* Reverse the digits */
|
||||||
size_t valueLen = ptr - valueBuf;
|
size_t valueLen = ptr - valueBuf;
|
||||||
|
|
||||||
for (size_t i = 0, j = valueLen - 1; i < j; i++, j--) {
|
for (size_t i = 0, j = valueLen - 1; i < j; i++, j--) {
|
||||||
@@ -224,19 +218,20 @@ void fmt_PrintNumber(char *buf, size_t bufLen, struct FormatSpec const *fmt, uin
|
|||||||
valueBuf[j] = c;
|
valueBuf[j] = c;
|
||||||
}
|
}
|
||||||
} else if (fmt->type == 'f') {
|
} else if (fmt->type == 'f') {
|
||||||
// Special case for fixed-point
|
/* Special case for fixed-point */
|
||||||
|
|
||||||
// Default fractional width (C's is 6 for "%f"; here 5 is enough for Q16.16)
|
/* Default fractional width (C's is 6 for "%f"; here 5 is enough) */
|
||||||
size_t fracWidth = fmt->hasFrac ? fmt->fracWidth : 5;
|
uint8_t fracWidth = fmt->hasFrac ? fmt->fracWidth : 5;
|
||||||
|
|
||||||
if (fracWidth > 255) {
|
if (fracWidth) {
|
||||||
error("Fractional width %zu too long, limiting to 255\n",
|
char spec[16]; /* Max "%" + 5-char PRIu32 + ".%0255.f" + terminator */
|
||||||
fracWidth);
|
|
||||||
fracWidth = 255;
|
snprintf(spec, sizeof(spec), "%%" PRIu32 ".%%0%d.f", fracWidth);
|
||||||
|
snprintf(valueBuf, sizeof(valueBuf), spec, value >> 16,
|
||||||
|
(value % 65536) / 65536.0 * pow(10, fracWidth) + 0.5);
|
||||||
|
} else {
|
||||||
|
snprintf(valueBuf, sizeof(valueBuf), "%" PRIu32, value >> 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(valueBuf, sizeof(valueBuf), "%.*f", (int)fracWidth,
|
|
||||||
value / fix_PrecisionFactor());
|
|
||||||
} else {
|
} else {
|
||||||
char const *spec = fmt->type == 'd' ? "%" PRId32
|
char const *spec = fmt->type == 'd' ? "%" PRId32
|
||||||
: fmt->type == 'u' ? "%" PRIu32
|
: fmt->type == 'u' ? "%" PRIu32
|
||||||
@@ -249,49 +244,55 @@ void fmt_PrintNumber(char *buf, size_t bufLen, struct FormatSpec const *fmt, uin
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t len = strlen(valueBuf);
|
size_t len = strlen(valueBuf);
|
||||||
size_t numLen = !!sign + !!prefix + len;
|
size_t numLen = len;
|
||||||
|
|
||||||
|
if (sign)
|
||||||
|
numLen++;
|
||||||
|
if (prefix)
|
||||||
|
numLen++;
|
||||||
|
|
||||||
size_t totalLen = fmt->width > numLen ? fmt->width : numLen;
|
size_t totalLen = fmt->width > numLen ? fmt->width : numLen;
|
||||||
|
|
||||||
if (totalLen > bufLen - 1) { // bufLen includes terminator
|
if (totalLen + 1 > bufLen) /* bufLen includes terminator */
|
||||||
error("Formatted numeric value too long\n");
|
error("Formatted numeric value too long\n");
|
||||||
totalLen = bufLen - 1;
|
|
||||||
if (numLen > totalLen) {
|
|
||||||
len -= numLen - totalLen;
|
|
||||||
numLen = totalLen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(numLen < bufLen && totalLen < bufLen && numLen <= totalLen && len <= numLen);
|
|
||||||
|
|
||||||
size_t padLen = totalLen - numLen;
|
size_t padLen = fmt->width > numLen ? fmt->width - numLen : 0;
|
||||||
size_t pos = 0;
|
|
||||||
|
|
||||||
if (fmt->alignLeft) {
|
if (fmt->alignLeft) {
|
||||||
if (sign)
|
size_t pos = 0;
|
||||||
|
|
||||||
|
if (sign && pos < bufLen)
|
||||||
buf[pos++] = sign;
|
buf[pos++] = sign;
|
||||||
if (prefix)
|
if (prefix && pos < bufLen)
|
||||||
buf[pos++] = prefix;
|
buf[pos++] = prefix;
|
||||||
memcpy(buf + pos, valueBuf, len);
|
|
||||||
for (size_t i = pos + len; i < totalLen; i++)
|
strcpy(buf + pos, valueBuf);
|
||||||
buf[i] = ' ';
|
pos += len;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < totalLen && pos + i < bufLen; i++)
|
||||||
|
buf[pos + i] = ' ';
|
||||||
} else {
|
} else {
|
||||||
|
size_t pos = 0;
|
||||||
|
|
||||||
if (fmt->padZero) {
|
if (fmt->padZero) {
|
||||||
// sign, then prefix, then zero padding
|
/* sign, then prefix, then zero padding */
|
||||||
if (sign)
|
if (sign && pos < bufLen)
|
||||||
buf[pos++] = sign;
|
buf[pos++] = sign;
|
||||||
if (prefix)
|
if (prefix && pos < bufLen)
|
||||||
buf[pos++] = prefix;
|
buf[pos++] = prefix;
|
||||||
for (size_t i = 0; i < padLen; i++)
|
for (size_t i = 0; i < padLen && pos < bufLen; i++)
|
||||||
buf[pos++] = '0';
|
buf[pos++] = '0';
|
||||||
} else {
|
} else {
|
||||||
// space padding, then sign, then prefix
|
/* space padding, then sign, then prefix */
|
||||||
for (size_t i = 0; i < padLen; i++)
|
for (size_t i = 0; i < padLen && pos < bufLen; i++)
|
||||||
buf[pos++] = ' ';
|
buf[pos++] = ' ';
|
||||||
if (sign)
|
if (sign && pos < bufLen)
|
||||||
buf[pos++] = sign;
|
buf[pos++] = sign;
|
||||||
if (prefix)
|
if (prefix && pos < bufLen)
|
||||||
buf[pos++] = prefix;
|
buf[pos++] = prefix;
|
||||||
}
|
}
|
||||||
memcpy(buf + pos, valueBuf, len);
|
if (bufLen > pos)
|
||||||
|
strcpy(buf + pos, valueBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
buf[totalLen] = '\0';
|
buf[totalLen] = '\0';
|
||||||
|
|||||||
180
src/asm/fstack.c
180
src/asm/fstack.c
@@ -19,16 +19,22 @@
|
|||||||
#include "asm/main.h"
|
#include "asm/main.h"
|
||||||
#include "asm/symbol.h"
|
#include "asm/symbol.h"
|
||||||
#include "asm/warning.h"
|
#include "asm/warning.h"
|
||||||
#include "platform.h" // S_ISDIR (stat macro)
|
#include "platform.h" /* S_ISDIR (stat macro) */
|
||||||
|
|
||||||
#define MAXINCPATHS 128
|
#define MAXINCPATHS 128
|
||||||
|
|
||||||
|
#ifdef LEXER_DEBUG
|
||||||
|
#define dbgPrint(...) fprintf(stderr, "[fstack] " __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define dbgPrint(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
struct Context {
|
struct Context {
|
||||||
struct Context *parent;
|
struct Context *parent;
|
||||||
struct FileStackNode *fileInfo;
|
struct FileStackNode *fileInfo;
|
||||||
struct LexerState *lexerState;
|
struct LexerState *lexerState;
|
||||||
uint32_t uniqueID;
|
uint32_t uniqueID;
|
||||||
struct MacroArgs *macroArgs; // Macro args are *saved* here
|
struct MacroArgs *macroArgs; /* Macro args are *saved* here */
|
||||||
uint32_t nbReptIters;
|
uint32_t nbReptIters;
|
||||||
int32_t forValue;
|
int32_t forValue;
|
||||||
int32_t forStep;
|
int32_t forStep;
|
||||||
@@ -37,7 +43,8 @@ struct Context {
|
|||||||
|
|
||||||
static struct Context *contextStack;
|
static struct Context *contextStack;
|
||||||
static size_t contextDepth = 0;
|
static size_t contextDepth = 0;
|
||||||
size_t maxRecursionDepth;
|
#define DEFAULT_MAX_DEPTH 64
|
||||||
|
size_t nMaxRecursionDepth;
|
||||||
|
|
||||||
static unsigned int nbIncPaths = 0;
|
static unsigned int nbIncPaths = 0;
|
||||||
static char const *includePaths[MAXINCPATHS];
|
static char const *includePaths[MAXINCPATHS];
|
||||||
@@ -47,7 +54,7 @@ static const char *dumpNodeAndParents(struct FileStackNode const *node)
|
|||||||
char const *name;
|
char const *name;
|
||||||
|
|
||||||
if (node->type == NODE_REPT) {
|
if (node->type == NODE_REPT) {
|
||||||
assert(node->parent); // REPT nodes should always have a parent
|
assert(node->parent); /* REPT nodes should always have a parent */
|
||||||
struct FileStackReptNode const *reptInfo = (struct FileStackReptNode const *)node;
|
struct FileStackReptNode const *reptInfo = (struct FileStackReptNode const *)node;
|
||||||
|
|
||||||
name = dumpNodeAndParents(node->parent);
|
name = dumpNodeAndParents(node->parent);
|
||||||
@@ -88,7 +95,7 @@ struct FileStackNode *fstk_GetFileStack(void)
|
|||||||
|
|
||||||
struct FileStackNode *node = contextStack->fileInfo;
|
struct FileStackNode *node = contextStack->fileInfo;
|
||||||
|
|
||||||
// Mark node and all of its parents as referenced if not already so they don't get freed
|
/* Mark node and all of its parents as referenced if not already so they don't get freed */
|
||||||
while (node && !node->referenced) {
|
while (node && !node->referenced) {
|
||||||
node->ID = -1;
|
node->ID = -1;
|
||||||
node->referenced = true;
|
node->referenced = true;
|
||||||
@@ -99,7 +106,7 @@ struct FileStackNode *fstk_GetFileStack(void)
|
|||||||
|
|
||||||
char const *fstk_GetFileName(void)
|
char const *fstk_GetFileName(void)
|
||||||
{
|
{
|
||||||
// Iterating via the nodes themselves skips nested REPTs
|
/* Iterating via the nodes themselves skips nested REPTs */
|
||||||
struct FileStackNode const *node = contextStack->fileInfo;
|
struct FileStackNode const *node = contextStack->fileInfo;
|
||||||
|
|
||||||
while (node->type != NODE_FILE)
|
while (node->type != NODE_FILE)
|
||||||
@@ -120,7 +127,7 @@ void fstk_AddIncludePath(char const *path)
|
|||||||
char *str = malloc(allocSize);
|
char *str = malloc(allocSize);
|
||||||
|
|
||||||
if (!str) {
|
if (!str) {
|
||||||
// Attempt to continue without that path
|
/* Attempt to continue without that path */
|
||||||
error("Failed to allocate new include path: %s\n", strerror(errno));
|
error("Failed to allocate new include path: %s\n", strerror(errno));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -136,8 +143,8 @@ void fstk_AddIncludePath(char const *path)
|
|||||||
static void printDep(char const *path)
|
static void printDep(char const *path)
|
||||||
{
|
{
|
||||||
if (dependfile) {
|
if (dependfile) {
|
||||||
fprintf(dependfile, "%s: %s\n", targetFileName, path);
|
fprintf(dependfile, "%s: %s\n", tzTargetFileName, path);
|
||||||
if (generatePhonyDeps)
|
if (oGeneratePhonyDeps)
|
||||||
fprintf(dependfile, "%s:\n", path);
|
fprintf(dependfile, "%s:\n", path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -149,14 +156,14 @@ static bool isPathValid(char const *path)
|
|||||||
if (stat(path, &statbuf) != 0)
|
if (stat(path, &statbuf) != 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Reject directories
|
/* Reject directories */
|
||||||
return !S_ISDIR(statbuf.st_mode);
|
return !S_ISDIR(statbuf.st_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
|
bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
|
||||||
{
|
{
|
||||||
if (!*size) {
|
if (!*size) {
|
||||||
*size = 64; // This is arbitrary, really
|
*size = 64; /* This is arbitrary, really */
|
||||||
*fullPath = realloc(*fullPath, *size);
|
*fullPath = realloc(*fullPath, *size);
|
||||||
if (!*fullPath)
|
if (!*fullPath)
|
||||||
error("realloc error during include path search: %s\n",
|
error("realloc error during include path search: %s\n",
|
||||||
@@ -174,8 +181,8 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Oh how I wish `asnprintf` was standard...
|
/* Oh how I wish `asnprintf` was standard... */
|
||||||
if ((size_t)len >= *size) { // `size` includes the terminator, `len` doesn't
|
if ((size_t)len >= *size) { /* `len` doesn't include the terminator, `size` does */
|
||||||
*size = len + 1;
|
*size = len + 1;
|
||||||
*fullPath = realloc(*fullPath, *size);
|
*fullPath = realloc(*fullPath, *size);
|
||||||
if (!*fullPath) {
|
if (!*fullPath) {
|
||||||
@@ -184,11 +191,6 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
len = sprintf(*fullPath, "%s%s", incPath, path);
|
len = sprintf(*fullPath, "%s%s", incPath, path);
|
||||||
if (len < 0) {
|
|
||||||
error("sprintf error during include path search: %s\n",
|
|
||||||
strerror(errno));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPathValid(*fullPath)) {
|
if (isPathValid(*fullPath)) {
|
||||||
@@ -199,31 +201,30 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
errno = ENOENT;
|
errno = ENOENT;
|
||||||
if (generatedMissingIncludes)
|
if (oGeneratedMissingIncludes)
|
||||||
printDep(path);
|
printDep(path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool yywrap(void)
|
bool yywrap(void)
|
||||||
{
|
{
|
||||||
uint32_t ifDepth = lexer_GetIFDepth();
|
uint32_t nIFDepth = lexer_GetIFDepth();
|
||||||
|
|
||||||
if (ifDepth != 0)
|
if (nIFDepth != 0)
|
||||||
fatalerror("Ended block with %" PRIu32 " unterminated IF construct%s\n",
|
fatalerror("Ended block with %" PRIu32 " unterminated IF construct%s\n",
|
||||||
ifDepth, ifDepth == 1 ? "" : "s");
|
nIFDepth, nIFDepth == 1 ? "" : "s");
|
||||||
|
|
||||||
if (contextStack->fileInfo->type == NODE_REPT) {
|
if (contextStack->fileInfo->type == NODE_REPT) { /* The context is a REPT block, which may loop */
|
||||||
// The context is a REPT or FOR block, which may loop
|
|
||||||
struct FileStackReptNode *fileInfo = (struct FileStackReptNode *)contextStack->fileInfo;
|
struct FileStackReptNode *fileInfo = (struct FileStackReptNode *)contextStack->fileInfo;
|
||||||
|
|
||||||
// If the node is referenced, we can't edit it; duplicate it
|
/* If the node is referenced, we can't edit it; duplicate it */
|
||||||
if (contextStack->fileInfo->referenced) {
|
if (contextStack->fileInfo->referenced) {
|
||||||
size_t size = sizeof(*fileInfo) + sizeof(fileInfo->iters[0]) * fileInfo->reptDepth;
|
size_t size = sizeof(*fileInfo) + sizeof(fileInfo->iters[0]) * fileInfo->reptDepth;
|
||||||
struct FileStackReptNode *copy = malloc(size);
|
struct FileStackReptNode *copy = malloc(size);
|
||||||
|
|
||||||
if (!copy)
|
if (!copy)
|
||||||
fatalerror("Failed to duplicate REPT file node: %s\n", strerror(errno));
|
fatalerror("Failed to duplicate REPT file node: %s\n", strerror(errno));
|
||||||
// Copy all info but the referencing
|
/* Copy all info but the referencing */
|
||||||
memcpy(copy, fileInfo, size);
|
memcpy(copy, fileInfo, size);
|
||||||
copy->node.next = NULL;
|
copy->node.next = NULL;
|
||||||
copy->node.referenced = false;
|
copy->node.referenced = false;
|
||||||
@@ -232,19 +233,19 @@ bool yywrap(void)
|
|||||||
contextStack->fileInfo = (struct FileStackNode *)fileInfo;
|
contextStack->fileInfo = (struct FileStackNode *)fileInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is a FOR, update the symbol value
|
/* If this is a FOR, update the symbol value */
|
||||||
if (contextStack->forName && fileInfo->iters[0] <= contextStack->nbReptIters) {
|
if (contextStack->forName && fileInfo->iters[0] <= contextStack->nbReptIters) {
|
||||||
contextStack->forValue += contextStack->forStep;
|
contextStack->forValue += contextStack->forStep;
|
||||||
struct Symbol *sym = sym_AddVar(contextStack->forName,
|
struct Symbol *sym = sym_AddSet(contextStack->forName,
|
||||||
contextStack->forValue);
|
contextStack->forValue);
|
||||||
|
|
||||||
// This error message will refer to the current iteration
|
/* This error message will refer to the current iteration */
|
||||||
if (sym->type != SYM_VAR)
|
if (sym->type != SYM_SET)
|
||||||
fatalerror("Failed to update FOR symbol value\n");
|
fatalerror("Failed to update FOR symbol value\n");
|
||||||
}
|
}
|
||||||
// Advance to the next iteration
|
/* Advance to the next iteration */
|
||||||
fileInfo->iters[0]++;
|
fileInfo->iters[0]++;
|
||||||
// If this wasn't the last iteration, wrap instead of popping
|
/* If this wasn't the last iteration, wrap instead of popping */
|
||||||
if (fileInfo->iters[0] <= contextStack->nbReptIters) {
|
if (fileInfo->iters[0] <= contextStack->nbReptIters) {
|
||||||
lexer_RestartRept(contextStack->fileInfo->lineNo);
|
lexer_RestartRept(contextStack->fileInfo->lineNo);
|
||||||
contextStack->uniqueID = macro_UseNewUniqueID();
|
contextStack->uniqueID = macro_UseNewUniqueID();
|
||||||
@@ -253,6 +254,7 @@ bool yywrap(void)
|
|||||||
} else if (!contextStack->parent) {
|
} else if (!contextStack->parent) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
dbgPrint("Popping context\n");
|
||||||
|
|
||||||
struct Context *context = contextStack;
|
struct Context *context = contextStack;
|
||||||
|
|
||||||
@@ -261,15 +263,17 @@ bool yywrap(void)
|
|||||||
contextDepth--;
|
contextDepth--;
|
||||||
|
|
||||||
lexer_DeleteState(context->lexerState);
|
lexer_DeleteState(context->lexerState);
|
||||||
// Restore args if a macro (not REPT) saved them
|
/* Restore args if a macro (not REPT) saved them */
|
||||||
if (context->fileInfo->type == NODE_MACRO)
|
if (context->fileInfo->type == NODE_MACRO) {
|
||||||
|
dbgPrint("Restoring macro args %p\n", (void *)contextStack->macroArgs);
|
||||||
macro_UseNewArgs(contextStack->macroArgs);
|
macro_UseNewArgs(contextStack->macroArgs);
|
||||||
// Free the file stack node
|
}
|
||||||
|
/* Free the file stack node */
|
||||||
if (!context->fileInfo->referenced)
|
if (!context->fileInfo->referenced)
|
||||||
free(context->fileInfo);
|
free(context->fileInfo);
|
||||||
// Free the FOR symbol name
|
/* Free the FOR symbol name */
|
||||||
free(context->forName);
|
free(context->forName);
|
||||||
// Free the entry and make its parent the current entry
|
/* Free the entry and make its parent the current entry */
|
||||||
free(context);
|
free(context);
|
||||||
|
|
||||||
lexer_SetState(contextStack->lexerState);
|
lexer_SetState(contextStack->lexerState);
|
||||||
@@ -277,51 +281,53 @@ bool yywrap(void)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
* Make sure not to switch the lexer state before calling this, so the saved line no is correct
|
||||||
// Callers should set contextStack->lexerState after this so it is not NULL.
|
* BE CAREFUL!! This modifies the file stack directly, you should have set up the file info first
|
||||||
|
* Callers should set contextStack->lexerState after this so it is not NULL
|
||||||
|
*/
|
||||||
static void newContext(struct FileStackNode *fileInfo)
|
static void newContext(struct FileStackNode *fileInfo)
|
||||||
{
|
{
|
||||||
++contextDepth;
|
if (++contextDepth >= nMaxRecursionDepth)
|
||||||
fstk_NewRecursionDepth(maxRecursionDepth); // Only checks if the max depth was exceeded
|
fatalerror("Recursion limit (%zu) exceeded\n", nMaxRecursionDepth);
|
||||||
|
|
||||||
// Save the current `\@` value, to be restored when this context ends
|
|
||||||
contextStack->uniqueID = macro_GetUniqueID();
|
|
||||||
|
|
||||||
struct Context *context = malloc(sizeof(*context));
|
struct Context *context = malloc(sizeof(*context));
|
||||||
|
|
||||||
if (!context)
|
if (!context)
|
||||||
fatalerror("Failed to allocate memory for new context: %s\n", strerror(errno));
|
fatalerror("Failed to allocate memory for new context: %s\n", strerror(errno));
|
||||||
fileInfo->parent = contextStack->fileInfo;
|
fileInfo->parent = contextStack->fileInfo;
|
||||||
fileInfo->lineNo = 0; // Init to a default value, see struct definition for info
|
fileInfo->lineNo = 0; /* Init to a default value, see struct definition for info */
|
||||||
fileInfo->referenced = false;
|
fileInfo->referenced = false;
|
||||||
fileInfo->lineNo = lexer_GetLineNo();
|
fileInfo->lineNo = lexer_GetLineNo();
|
||||||
context->fileInfo = fileInfo;
|
context->fileInfo = fileInfo;
|
||||||
context->forName = NULL;
|
context->forName = NULL;
|
||||||
|
/*
|
||||||
// Link new entry to its parent so it's reachable later
|
* Link new entry to its parent so it's reachable later
|
||||||
// ERRORS SHOULD NOT OCCUR AFTER THIS!!
|
* ERRORS SHOULD NOT OCCUR AFTER THIS!!
|
||||||
|
*/
|
||||||
context->parent = contextStack;
|
context->parent = contextStack;
|
||||||
contextStack = context;
|
contextStack = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_RunInclude(char const *path)
|
void fstk_RunInclude(char const *path)
|
||||||
{
|
{
|
||||||
|
dbgPrint("Including path \"%s\"\n", path);
|
||||||
|
|
||||||
char *fullPath = NULL;
|
char *fullPath = NULL;
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
|
|
||||||
if (!fstk_FindFile(path, &fullPath, &size)) {
|
if (!fstk_FindFile(path, &fullPath, &size)) {
|
||||||
free(fullPath);
|
free(fullPath);
|
||||||
if (generatedMissingIncludes) {
|
if (oGeneratedMissingIncludes) {
|
||||||
if (verbose)
|
if (verbose)
|
||||||
printf("Aborting (-MG) on INCLUDE file '%s' (%s)\n",
|
printf("Aborting (-MG) on INCLUDE file '%s' (%s)\n",
|
||||||
path, strerror(errno));
|
path, strerror(errno));
|
||||||
failedOnMissingInclude = true;
|
oFailedOnMissingInclude = true;
|
||||||
} else {
|
} else {
|
||||||
error("Unable to open included file '%s': %s\n", path, strerror(errno));
|
error("Unable to open included file '%s': %s\n", path, strerror(errno));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
dbgPrint("Full path: \"%s\"\n", fullPath);
|
||||||
|
|
||||||
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + size);
|
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + size);
|
||||||
|
|
||||||
@@ -338,12 +344,15 @@ void fstk_RunInclude(char const *path)
|
|||||||
if (!contextStack->lexerState)
|
if (!contextStack->lexerState)
|
||||||
fatalerror("Failed to set up lexer for file include\n");
|
fatalerror("Failed to set up lexer for file include\n");
|
||||||
lexer_SetStateAtEOL(contextStack->lexerState);
|
lexer_SetStateAtEOL(contextStack->lexerState);
|
||||||
// We're back at top-level, so most things are reset
|
/* We're back at top-level, so most things are reset */
|
||||||
contextStack->uniqueID = macro_UndefUniqueID();
|
contextStack->uniqueID = 0;
|
||||||
|
macro_SetUniqueID(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
|
void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
|
||||||
{
|
{
|
||||||
|
dbgPrint("Running macro \"%s\"\n", macroName);
|
||||||
|
|
||||||
struct Symbol *macro = sym_FindExactSymbol(macroName);
|
struct Symbol *macro = sym_FindExactSymbol(macroName);
|
||||||
|
|
||||||
if (!macro) {
|
if (!macro) {
|
||||||
@@ -356,16 +365,16 @@ void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
|
|||||||
}
|
}
|
||||||
contextStack->macroArgs = macro_GetCurrentArgs();
|
contextStack->macroArgs = macro_GetCurrentArgs();
|
||||||
|
|
||||||
// Compute total length of this node's name: <base name>::<macro>
|
/* Compute total length of this node's name: <base name>::<macro> */
|
||||||
size_t reptNameLen = 0;
|
size_t reptNameLen = 0;
|
||||||
struct FileStackNode const *node = macro->src;
|
struct FileStackNode const *node = macro->src;
|
||||||
|
|
||||||
if (node->type == NODE_REPT) {
|
if (node->type == NODE_REPT) {
|
||||||
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
|
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
|
||||||
|
|
||||||
// 4294967295 = 2^32 - 1, aka UINT32_MAX
|
/* 4294967295 = 2^32 - 1, aka UINT32_MAX */
|
||||||
reptNameLen += reptNode->reptDepth * strlen("::REPT~4294967295");
|
reptNameLen += reptNode->reptDepth * strlen("::REPT~4294967295");
|
||||||
// Look for next named node
|
/* Look for next named node */
|
||||||
do {
|
do {
|
||||||
node = node->parent;
|
node = node->parent;
|
||||||
} while (node->type == NODE_REPT);
|
} while (node->type == NODE_REPT);
|
||||||
@@ -381,7 +390,7 @@ void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fileInfo->node.type = NODE_MACRO;
|
fileInfo->node.type = NODE_MACRO;
|
||||||
// Print the name...
|
/* Print the name... */
|
||||||
char *dest = fileInfo->name;
|
char *dest = fileInfo->name;
|
||||||
|
|
||||||
memcpy(dest, baseNode->name, baseLen);
|
memcpy(dest, baseNode->name, baseLen);
|
||||||
@@ -403,7 +412,7 @@ void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
|
|||||||
memcpy(dest, macro->name, macroNameLen + 1);
|
memcpy(dest, macro->name, macroNameLen + 1);
|
||||||
|
|
||||||
newContext((struct FileStackNode *)fileInfo);
|
newContext((struct FileStackNode *)fileInfo);
|
||||||
contextStack->lexerState = lexer_OpenFileView("MACRO", macro->macro, macro->macroSize,
|
contextStack->lexerState = lexer_OpenFileView(macro->macro, macro->macroSize,
|
||||||
macro->fileLine);
|
macro->fileLine);
|
||||||
if (!contextStack->lexerState)
|
if (!contextStack->lexerState)
|
||||||
fatalerror("Failed to set up lexer for macro invocation\n");
|
fatalerror("Failed to set up lexer for macro invocation\n");
|
||||||
@@ -428,16 +437,16 @@ static bool newReptContext(int32_t reptLineNo, char *body, size_t size)
|
|||||||
fileInfo->reptDepth = reptDepth + 1;
|
fileInfo->reptDepth = reptDepth + 1;
|
||||||
fileInfo->iters[0] = 1;
|
fileInfo->iters[0] = 1;
|
||||||
if (reptDepth)
|
if (reptDepth)
|
||||||
// Copy all parent iter counts
|
/* Copy all parent iter counts */
|
||||||
memcpy(&fileInfo->iters[1],
|
memcpy(&fileInfo->iters[1],
|
||||||
((struct FileStackReptNode *)contextStack->fileInfo)->iters,
|
((struct FileStackReptNode *)contextStack->fileInfo)->iters,
|
||||||
reptDepth * sizeof(fileInfo->iters[0]));
|
reptDepth * sizeof(fileInfo->iters[0]));
|
||||||
|
|
||||||
newContext((struct FileStackNode *)fileInfo);
|
newContext((struct FileStackNode *)fileInfo);
|
||||||
// Correct our line number, which currently points to the `ENDR` line
|
/* Correct our line number, which currently points to the `ENDR` line */
|
||||||
contextStack->fileInfo->lineNo = reptLineNo;
|
contextStack->fileInfo->lineNo = reptLineNo;
|
||||||
|
|
||||||
contextStack->lexerState = lexer_OpenFileView("REPT", body, size, reptLineNo);
|
contextStack->lexerState = lexer_OpenFileView(body, size, reptLineNo);
|
||||||
if (!contextStack->lexerState)
|
if (!contextStack->lexerState)
|
||||||
fatalerror("Failed to set up lexer for REPT block\n");
|
fatalerror("Failed to set up lexer for REPT block\n");
|
||||||
lexer_SetStateAtEOL(contextStack->lexerState);
|
lexer_SetStateAtEOL(contextStack->lexerState);
|
||||||
@@ -447,6 +456,8 @@ static bool newReptContext(int32_t reptLineNo, char *body, size_t size)
|
|||||||
|
|
||||||
void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size)
|
void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size)
|
||||||
{
|
{
|
||||||
|
dbgPrint("Running REPT(%" PRIu32 ")\n", count);
|
||||||
|
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
return;
|
return;
|
||||||
if (!newReptContext(reptLineNo, body, size))
|
if (!newReptContext(reptLineNo, body, size))
|
||||||
@@ -459,9 +470,12 @@ void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size)
|
|||||||
void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step,
|
void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step,
|
||||||
int32_t reptLineNo, char *body, size_t size)
|
int32_t reptLineNo, char *body, size_t size)
|
||||||
{
|
{
|
||||||
struct Symbol *sym = sym_AddVar(symName, start);
|
dbgPrint("Running FOR(\"%s\", %" PRId32 ", %" PRId32 ", %" PRId32 ")\n",
|
||||||
|
symName, start, stop, step);
|
||||||
|
|
||||||
if (sym->type != SYM_VAR)
|
struct Symbol *sym = sym_AddSet(symName, start);
|
||||||
|
|
||||||
|
if (sym->type != SYM_SET)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
@@ -473,10 +487,6 @@ void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step,
|
|||||||
else if (step == 0)
|
else if (step == 0)
|
||||||
error("FOR cannot have a step value of 0\n");
|
error("FOR cannot have a step value of 0\n");
|
||||||
|
|
||||||
if ((step > 0 && start > stop) || (step < 0 && start < stop))
|
|
||||||
warning(WARNING_BACKWARDS_FOR, "FOR goes backwards from %d to %d by %d\n",
|
|
||||||
start, stop, step);
|
|
||||||
|
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
return;
|
return;
|
||||||
if (!newReptContext(reptLineNo, body, size))
|
if (!newReptContext(reptLineNo, body, size))
|
||||||
@@ -492,12 +502,14 @@ void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step,
|
|||||||
|
|
||||||
void fstk_StopRept(void)
|
void fstk_StopRept(void)
|
||||||
{
|
{
|
||||||
// Prevent more iterations
|
/* Prevent more iterations */
|
||||||
contextStack->nbReptIters = 0;
|
contextStack->nbReptIters = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fstk_Break(void)
|
bool fstk_Break(void)
|
||||||
{
|
{
|
||||||
|
dbgPrint("Breaking out of REPT/FOR\n");
|
||||||
|
|
||||||
if (contextStack->fileInfo->type != NODE_REPT) {
|
if (contextStack->fileInfo->type != NODE_REPT) {
|
||||||
error("BREAK can only be used inside a REPT/FOR block\n");
|
error("BREAK can only be used inside a REPT/FOR block\n");
|
||||||
return false;
|
return false;
|
||||||
@@ -507,14 +519,7 @@ bool fstk_Break(void)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_NewRecursionDepth(size_t newDepth)
|
void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
|
||||||
{
|
|
||||||
if (contextDepth > newDepth)
|
|
||||||
fatalerror("Recursion limit (%zu) exceeded\n", newDepth);
|
|
||||||
maxRecursionDepth = newDepth;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fstk_Init(char const *mainPath, size_t maxDepth)
|
|
||||||
{
|
{
|
||||||
struct LexerState *state = lexer_OpenFile(mainPath);
|
struct LexerState *state = lexer_OpenFile(mainPath);
|
||||||
|
|
||||||
@@ -532,7 +537,7 @@ void fstk_Init(char const *mainPath, size_t maxDepth)
|
|||||||
fatalerror("Failed to allocate memory for main file info: %s\n", strerror(errno));
|
fatalerror("Failed to allocate memory for main file info: %s\n", strerror(errno));
|
||||||
|
|
||||||
context->fileInfo = (struct FileStackNode *)fileInfo;
|
context->fileInfo = (struct FileStackNode *)fileInfo;
|
||||||
// lineNo and reptIter are unused on the top-level context
|
/* lineNo and reptIter are unused on the top-level context */
|
||||||
context->fileInfo->parent = NULL;
|
context->fileInfo->parent = NULL;
|
||||||
context->fileInfo->lineNo = 0; // This still gets written to the object file, so init it
|
context->fileInfo->lineNo = 0; // This still gets written to the object file, so init it
|
||||||
context->fileInfo->referenced = false;
|
context->fileInfo->referenced = false;
|
||||||
@@ -541,26 +546,29 @@ void fstk_Init(char const *mainPath, size_t maxDepth)
|
|||||||
|
|
||||||
context->parent = NULL;
|
context->parent = NULL;
|
||||||
context->lexerState = state;
|
context->lexerState = state;
|
||||||
context->uniqueID = macro_UndefUniqueID();
|
context->uniqueID = 0;
|
||||||
|
macro_SetUniqueID(0);
|
||||||
context->nbReptIters = 0;
|
context->nbReptIters = 0;
|
||||||
context->forValue = 0;
|
context->forValue = 0;
|
||||||
context->forStep = 0;
|
context->forStep = 0;
|
||||||
context->forName = NULL;
|
context->forName = NULL;
|
||||||
|
|
||||||
// Now that it's set up properly, register the context
|
/* Now that it's set up properly, register the context */
|
||||||
contextStack = context;
|
contextStack = context;
|
||||||
|
|
||||||
// Check that max recursion depth won't allow overflowing node `malloc`s
|
/*
|
||||||
// This assumes that the rept node is larger
|
* 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))
|
#define DEPTH_LIMIT ((SIZE_MAX - sizeof(struct FileStackReptNode)) / sizeof(uint32_t))
|
||||||
if (maxDepth > DEPTH_LIMIT) {
|
if (maxRecursionDepth > DEPTH_LIMIT) {
|
||||||
error("Recursion depth may not be higher than %zu, defaulting to "
|
error("Recursion depth may not be higher than %zu, defaulting to "
|
||||||
EXPAND_AND_STR(DEFAULT_MAX_DEPTH) "\n", DEPTH_LIMIT);
|
EXPAND_AND_STR(DEFAULT_MAX_DEPTH) "\n", DEPTH_LIMIT);
|
||||||
maxRecursionDepth = DEFAULT_MAX_DEPTH;
|
nMaxRecursionDepth = DEFAULT_MAX_DEPTH;
|
||||||
} else {
|
} else {
|
||||||
maxRecursionDepth = maxDepth;
|
nMaxRecursionDepth = maxRecursionDepth;
|
||||||
}
|
}
|
||||||
// Make sure that the default of 64 is OK, though
|
/* Make sure that the default of 64 is OK, though */
|
||||||
assert(DEPTH_LIMIT >= DEFAULT_MAX_DEPTH);
|
assert(DEPTH_LIMIT >= DEFAULT_MAX_DEPTH);
|
||||||
#undef DEPTH_LIMIT
|
#undef DEPTH_LIMIT
|
||||||
}
|
}
|
||||||
|
|||||||
1907
src/asm/lexer.c
1907
src/asm/lexer.c
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,5 @@
|
|||||||
/*
|
|
||||||
* This file is part of RGBDS.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2022, Eldred Habert and RGBDS contributors.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
@@ -18,11 +12,13 @@
|
|||||||
|
|
||||||
#define MAXMACROARGS 99999
|
#define MAXMACROARGS 99999
|
||||||
|
|
||||||
// Your average macro invocation does not go past the tens, but some go further
|
/*
|
||||||
// This ensures that sane and slightly insane invocations suffer no penalties,
|
* Your average macro invocation does not go past the tens, but some go further
|
||||||
// and the rest is insane and thus will assume responsibility.
|
* This ensures that sane and slightly insane invocations suffer no penalties,
|
||||||
// Additionally, ~300 bytes (on x64) of memory per level of nesting has been
|
* and the rest is insane and thus will assume responsibility.
|
||||||
// deemed reasonable. (Halve that on x86.)
|
* Additionally, ~300 bytes (on x64) of memory per level of nesting has been
|
||||||
|
* deemed reasonable. (Halve that on x86.)
|
||||||
|
*/
|
||||||
#define INITIAL_ARG_SIZE 32
|
#define INITIAL_ARG_SIZE 32
|
||||||
struct MacroArgs {
|
struct MacroArgs {
|
||||||
unsigned int nbArgs;
|
unsigned int nbArgs;
|
||||||
@@ -37,9 +33,11 @@ struct MacroArgs {
|
|||||||
static struct MacroArgs *macroArgs = NULL;
|
static struct MacroArgs *macroArgs = NULL;
|
||||||
static uint32_t uniqueID = 0;
|
static uint32_t uniqueID = 0;
|
||||||
static uint32_t maxUniqueID = 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
|
* The initialization is somewhat harmful, since it is never used, but it
|
||||||
// better solution, but if you have one, please feel free!
|
* guarantees the size of the buffer will be correct. I was unable to find a
|
||||||
|
* better solution, but if you have one, please feel free!
|
||||||
|
*/
|
||||||
static char uniqueIDBuf[] = "_u4294967295"; // UINT32_MAX
|
static char uniqueIDBuf[] = "_u4294967295"; // UINT32_MAX
|
||||||
static char *uniqueIDPtr = NULL;
|
static char *uniqueIDPtr = NULL;
|
||||||
|
|
||||||
@@ -70,7 +68,7 @@ void macro_AppendArg(struct MacroArgs **argPtr, char *s)
|
|||||||
error("A maximum of " EXPAND_AND_STR(MAXMACROARGS) " arguments is allowed\n");
|
error("A maximum of " EXPAND_AND_STR(MAXMACROARGS) " arguments is allowed\n");
|
||||||
if (macArgs->nbArgs >= macArgs->capacity) {
|
if (macArgs->nbArgs >= macArgs->capacity) {
|
||||||
macArgs->capacity *= 2;
|
macArgs->capacity *= 2;
|
||||||
// Check that overflow didn't roll us back
|
/* Check that overflow didn't roll us back */
|
||||||
if (macArgs->capacity <= macArgs->nbArgs)
|
if (macArgs->capacity <= macArgs->nbArgs)
|
||||||
fatalerror("Failed to add new macro argument: capacity overflow\n");
|
fatalerror("Failed to add new macro argument: capacity overflow\n");
|
||||||
macArgs = realloc(macArgs, SIZEOF_ARGS(macArgs->capacity));
|
macArgs = realloc(macArgs, SIZEOF_ARGS(macArgs->capacity));
|
||||||
@@ -103,7 +101,7 @@ char const *macro_GetArg(uint32_t i)
|
|||||||
: macroArgs->args[realIndex];
|
: macroArgs->args[realIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
char const *macro_GetAllArgs(void)
|
char *macro_GetAllArgs(void)
|
||||||
{
|
{
|
||||||
if (!macroArgs)
|
if (!macroArgs)
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -114,9 +112,9 @@ char const *macro_GetAllArgs(void)
|
|||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
|
|
||||||
for (uint32_t i = macroArgs->shift; i < macroArgs->nbArgs; i++)
|
for (uint32_t i = macroArgs->shift; i < macroArgs->nbArgs; i++)
|
||||||
len += strlen(macroArgs->args[i]) + 1; // 1 for comma
|
len += strlen(macroArgs->args[i]) + 1; /* 1 for comma */
|
||||||
|
|
||||||
char *str = malloc(len + 1); // 1 for '\0'
|
char *str = malloc(len + 1); /* 1 for '\0' */
|
||||||
char *ptr = str;
|
char *ptr = str;
|
||||||
|
|
||||||
if (!str)
|
if (!str)
|
||||||
@@ -128,9 +126,9 @@ char const *macro_GetAllArgs(void)
|
|||||||
memcpy(ptr, macroArgs->args[i], n);
|
memcpy(ptr, macroArgs->args[i], n);
|
||||||
ptr += n;
|
ptr += n;
|
||||||
|
|
||||||
// Commas go between args and after a last empty arg
|
/* Commas go between args and after a last empty arg */
|
||||||
if (i < macroArgs->nbArgs - 1 || n == 0)
|
if (i < macroArgs->nbArgs - 1 || n == 0)
|
||||||
*ptr++ = ','; // no space after comma
|
*ptr++ = ','; /* no space after comma */
|
||||||
}
|
}
|
||||||
*ptr = '\0';
|
*ptr = '\0';
|
||||||
|
|
||||||
@@ -144,21 +142,19 @@ uint32_t macro_GetUniqueID(void)
|
|||||||
|
|
||||||
char const *macro_GetUniqueIDStr(void)
|
char const *macro_GetUniqueIDStr(void)
|
||||||
{
|
{
|
||||||
// Generate a new unique ID on the first use of `\@`
|
|
||||||
if (uniqueID == 0)
|
|
||||||
macro_SetUniqueID(++maxUniqueID);
|
|
||||||
|
|
||||||
return uniqueIDPtr;
|
return uniqueIDPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void macro_SetUniqueID(uint32_t id)
|
void macro_SetUniqueID(uint32_t id)
|
||||||
{
|
{
|
||||||
uniqueID = id;
|
uniqueID = id;
|
||||||
if (id == 0 || id == (uint32_t)-1) {
|
if (id == 0) {
|
||||||
uniqueIDPtr = NULL;
|
uniqueIDPtr = NULL;
|
||||||
} else {
|
} else {
|
||||||
// The buffer is guaranteed to be the correct size
|
if (uniqueID > maxUniqueID)
|
||||||
// This is a valid label fragment, but not a valid numeric
|
maxUniqueID = uniqueID;
|
||||||
|
/* The buffer is guaranteed to be the correct size */
|
||||||
|
/* This is a valid label fragment, but not a valid numeric */
|
||||||
sprintf(uniqueIDBuf, "_u%" PRIu32, id);
|
sprintf(uniqueIDBuf, "_u%" PRIu32, id);
|
||||||
uniqueIDPtr = uniqueIDBuf;
|
uniqueIDPtr = uniqueIDBuf;
|
||||||
}
|
}
|
||||||
@@ -166,16 +162,8 @@ void macro_SetUniqueID(uint32_t id)
|
|||||||
|
|
||||||
uint32_t macro_UseNewUniqueID(void)
|
uint32_t macro_UseNewUniqueID(void)
|
||||||
{
|
{
|
||||||
// A new ID will be generated on the first use of `\@`
|
macro_SetUniqueID(++maxUniqueID);
|
||||||
macro_SetUniqueID(0);
|
return maxUniqueID;
|
||||||
return uniqueID;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t macro_UndefUniqueID(void)
|
|
||||||
{
|
|
||||||
// No ID will be generated; use of `\@` is an error
|
|
||||||
macro_SetUniqueID((uint32_t)-1);
|
|
||||||
return uniqueID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void macro_ShiftCurrentArgs(int32_t count)
|
void macro_ShiftCurrentArgs(int32_t count)
|
||||||
|
|||||||
226
src/asm/main.c
226
src/asm/main.c
@@ -19,7 +19,6 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include "asm/charmap.h"
|
#include "asm/charmap.h"
|
||||||
#include "asm/fixpoint.h"
|
|
||||||
#include "asm/format.h"
|
#include "asm/format.h"
|
||||||
#include "asm/fstack.h"
|
#include "asm/fstack.h"
|
||||||
#include "asm/lexer.h"
|
#include "asm/lexer.h"
|
||||||
@@ -31,52 +30,42 @@
|
|||||||
#include "asm/warning.h"
|
#include "asm/warning.h"
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
|
||||||
|
#include "extern/err.h"
|
||||||
#include "extern/getopt.h"
|
#include "extern/getopt.h"
|
||||||
|
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "error.h"
|
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
#ifdef __clang__
|
|
||||||
#if __has_feature(address_sanitizer) && !defined(__SANITIZE_ADDRESS__)
|
|
||||||
#define __SANITIZE_ADDRESS__
|
|
||||||
#endif // __has_feature(address_sanitizer) && !defined(__SANITIZE_ADDRESS__)
|
|
||||||
#endif // __clang__
|
|
||||||
|
|
||||||
#ifdef __SANITIZE_ADDRESS__
|
|
||||||
// There are known, non-trivial to fix leaks. We would still like to have `make develop'
|
|
||||||
// detect memory corruption, though.
|
|
||||||
char const *__asan_default_options(void) { return "detect_leaks=0"; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Old Bison versions (confirmed for 2.3) do not forward-declare `yyparse` in the generated header
|
// 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...
|
// Unfortunately, macOS still ships 2.3, which is from 2008...
|
||||||
int yyparse(void);
|
int yyparse(void);
|
||||||
|
|
||||||
|
#if defined(YYDEBUG) && YYDEBUG
|
||||||
|
extern int yydebug;
|
||||||
|
#endif
|
||||||
|
|
||||||
FILE * dependfile;
|
FILE * dependfile;
|
||||||
bool generatedMissingIncludes;
|
bool oGeneratedMissingIncludes;
|
||||||
bool failedOnMissingInclude;
|
bool oFailedOnMissingInclude;
|
||||||
bool generatePhonyDeps;
|
bool oGeneratePhonyDeps;
|
||||||
char *targetFileName;
|
char *tzTargetFileName;
|
||||||
|
|
||||||
bool haltnop;
|
bool haltnop;
|
||||||
bool warnOnHaltNop;
|
bool optimizeloads;
|
||||||
bool optimizeLoads;
|
|
||||||
bool warnOnLdOpt;
|
|
||||||
bool verbose;
|
bool verbose;
|
||||||
bool warnings; // True to enable warnings, false to disable them.
|
bool warnings; /* True to enable warnings, false to disable them. */
|
||||||
|
|
||||||
// Escapes Make-special chars from a string
|
/* Escapes Make-special chars from a string */
|
||||||
static char *make_escape(char const *str)
|
static char *make_escape(const char *str)
|
||||||
{
|
{
|
||||||
char * const escaped_str = malloc(strlen(str) * 2 + 1);
|
char * const escaped_str = malloc(strlen(str) * 2 + 1);
|
||||||
char *dest = escaped_str;
|
char *dest = escaped_str;
|
||||||
|
|
||||||
if (escaped_str == NULL)
|
if (escaped_str == NULL)
|
||||||
err("%s: Failed to allocate memory", __func__);
|
err(1, "%s: Failed to allocate memory", __func__);
|
||||||
|
|
||||||
while (*str) {
|
while (*str) {
|
||||||
// All dollars needs to be doubled
|
/* All dollars needs to be doubled */
|
||||||
if (*str == '$')
|
if (*str == '$')
|
||||||
*dest++ = '$';
|
*dest++ = '$';
|
||||||
*dest++ = *str++;
|
*dest++ = *str++;
|
||||||
@@ -86,30 +75,30 @@ static char *make_escape(char const *str)
|
|||||||
return escaped_str;
|
return escaped_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Short options
|
/* Short options */
|
||||||
static const char *optstring = "b:D:Eg:Hhi:LlM:o:p:Q:r:VvW:w";
|
static const char *optstring = "b:D:Eg:hi:LM:o:p:r:VvW:w";
|
||||||
|
|
||||||
// Variables for the long-only options
|
/* Variables for the long-only options */
|
||||||
static int depType; // Variants of `-M`
|
static int depType; /* Variants of `-M` */
|
||||||
|
|
||||||
// Equivalent long options
|
/*
|
||||||
// Please keep in the same order as short opts
|
* Equivalent long options
|
||||||
//
|
* Please keep in the same order as short opts
|
||||||
// Also, make sure long opts don't create ambiguity:
|
*
|
||||||
// A long opt's name should start with the same letter as its short opt,
|
* Also, make sure long opts don't create ambiguity:
|
||||||
// except if it doesn't create any ambiguity (`verbose` versus `version`).
|
* A long opt's name should start with the same letter as its short opt,
|
||||||
// This is because long opt matching, even to a single char, is prioritized
|
* except if it doesn't create any ambiguity (`verbose` versus `version`).
|
||||||
// over short opt matching
|
* This is because long opt matching, even to a single char, is prioritized
|
||||||
|
* over short opt matching
|
||||||
|
*/
|
||||||
static struct option const longopts[] = {
|
static struct option const longopts[] = {
|
||||||
{ "binary-digits", required_argument, NULL, 'b' },
|
{ "binary-digits", required_argument, NULL, 'b' },
|
||||||
{ "define", required_argument, NULL, 'D' },
|
{ "define", required_argument, NULL, 'D' },
|
||||||
{ "export-all", no_argument, NULL, 'E' },
|
{ "export-all", no_argument, NULL, 'E' },
|
||||||
{ "gfx-chars", required_argument, NULL, 'g' },
|
{ "gfx-chars", required_argument, NULL, 'g' },
|
||||||
{ "nop-after-halt", no_argument, NULL, 'H' },
|
|
||||||
{ "halt-without-nop", no_argument, NULL, 'h' },
|
{ "halt-without-nop", no_argument, NULL, 'h' },
|
||||||
{ "include", required_argument, NULL, 'i' },
|
{ "include", required_argument, NULL, 'i' },
|
||||||
{ "preserve-ld", no_argument, NULL, 'L' },
|
{ "preserve-ld", no_argument, NULL, 'L' },
|
||||||
{ "auto-ldh", no_argument, NULL, 'l' },
|
|
||||||
{ "dependfile", required_argument, NULL, 'M' },
|
{ "dependfile", required_argument, NULL, 'M' },
|
||||||
{ "MG", no_argument, &depType, 'G' },
|
{ "MG", no_argument, &depType, 'G' },
|
||||||
{ "MP", no_argument, &depType, 'P' },
|
{ "MP", no_argument, &depType, 'P' },
|
||||||
@@ -117,7 +106,6 @@ static struct option const longopts[] = {
|
|||||||
{ "MQ", required_argument, &depType, 'Q' },
|
{ "MQ", required_argument, &depType, 'Q' },
|
||||||
{ "output", required_argument, NULL, 'o' },
|
{ "output", required_argument, NULL, 'o' },
|
||||||
{ "pad-value", required_argument, NULL, 'p' },
|
{ "pad-value", required_argument, NULL, 'p' },
|
||||||
{ "q-precision", required_argument, NULL, 'Q' },
|
|
||||||
{ "recursion-depth", required_argument, NULL, 'r' },
|
{ "recursion-depth", required_argument, NULL, 'r' },
|
||||||
{ "version", no_argument, NULL, 'V' },
|
{ "version", no_argument, NULL, 'V' },
|
||||||
{ "verbose", no_argument, NULL, 'v' },
|
{ "verbose", no_argument, NULL, 'v' },
|
||||||
@@ -128,10 +116,9 @@ static struct option const longopts[] = {
|
|||||||
static void print_usage(void)
|
static void print_usage(void)
|
||||||
{
|
{
|
||||||
fputs(
|
fputs(
|
||||||
"Usage: rgbasm [-EHhLlVvw] [-b chars] [-D name[=value]] [-g chars] [-i path]\n"
|
"Usage: rgbasm [-EhLVvw] [-b chars] [-D name[=value]] [-g chars] [-i path]\n"
|
||||||
" [-M depend_file] [-MG] [-MP] [-MT target_file] [-MQ target_file]\n"
|
" [-M depend_file] [-MG] [-MP] [-MT target_file] [-MQ target_file]\n"
|
||||||
" [-o out_file] [-p pad_value] [-Q precision] [-r depth]\n"
|
" [-o out_file] [-p pad_value] [-r depth] [-W warning] <file>\n"
|
||||||
" [-W warning] <file>\n"
|
|
||||||
"Useful options:\n"
|
"Useful options:\n"
|
||||||
" -E, --export-all export all labels\n"
|
" -E, --export-all export all labels\n"
|
||||||
" -M, --dependfile <path> set the output dependency file\n"
|
" -M, --dependfile <path> set the output dependency file\n"
|
||||||
@@ -153,36 +140,39 @@ int main(int argc, char *argv[])
|
|||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
char const *sourceDateEpoch = getenv("SOURCE_DATE_EPOCH");
|
char const *sourceDateEpoch = getenv("SOURCE_DATE_EPOCH");
|
||||||
|
|
||||||
// Support SOURCE_DATE_EPOCH for reproducible builds
|
/*
|
||||||
// https://reproducible-builds.org/docs/source-date-epoch/
|
* Support SOURCE_DATE_EPOCH for reproducible builds
|
||||||
|
* https://reproducible-builds.org/docs/source-date-epoch/
|
||||||
|
*/
|
||||||
if (sourceDateEpoch)
|
if (sourceDateEpoch)
|
||||||
now = (time_t)strtoul(sourceDateEpoch, NULL, 0);
|
now = (time_t)strtoul(sourceDateEpoch, NULL, 0);
|
||||||
|
|
||||||
dependfile = NULL;
|
dependfile = NULL;
|
||||||
|
|
||||||
|
#if defined(YYDEBUG) && YYDEBUG
|
||||||
|
yydebug = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Perform some init for below
|
// Perform some init for below
|
||||||
sym_Init(now);
|
sym_Init(now);
|
||||||
|
|
||||||
// Set defaults
|
// Set defaults
|
||||||
|
|
||||||
generatePhonyDeps = false;
|
oGeneratePhonyDeps = false;
|
||||||
generatedMissingIncludes = false;
|
oGeneratedMissingIncludes = false;
|
||||||
failedOnMissingInclude = false;
|
oFailedOnMissingInclude = false;
|
||||||
targetFileName = NULL;
|
tzTargetFileName = NULL;
|
||||||
|
|
||||||
opt_B("01");
|
opt_B("01");
|
||||||
opt_G("0123");
|
opt_G("0123");
|
||||||
opt_P(0);
|
opt_P(0);
|
||||||
opt_Q(16);
|
optimizeloads = true;
|
||||||
haltnop = true;
|
haltnop = true;
|
||||||
warnOnHaltNop = true;
|
|
||||||
optimizeLoads = true;
|
|
||||||
warnOnLdOpt = true;
|
|
||||||
verbose = false;
|
verbose = false;
|
||||||
warnings = true;
|
warnings = true;
|
||||||
sym_SetExportAll(false);
|
sym_SetExportAll(false);
|
||||||
uint32_t maxDepth = DEFAULT_MAX_DEPTH;
|
uint32_t maxRecursionDepth = 64;
|
||||||
size_t targetFileNameLen = 0;
|
size_t nTargetFileNameLen = 0;
|
||||||
|
|
||||||
while ((ch = musl_getopt_long_only(argc, argv, optstring, longopts, NULL)) != -1) {
|
while ((ch = musl_getopt_long_only(argc, argv, optstring, longopts, NULL)) != -1) {
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
@@ -190,7 +180,7 @@ int main(int argc, char *argv[])
|
|||||||
if (strlen(musl_optarg) == 2)
|
if (strlen(musl_optarg) == 2)
|
||||||
opt_B(&musl_optarg[1]);
|
opt_B(&musl_optarg[1]);
|
||||||
else
|
else
|
||||||
errx("Must specify exactly 2 characters for option 'b'");
|
errx(1, "Must specify exactly 2 characters for option 'b'");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
char *equals;
|
char *equals;
|
||||||
@@ -212,17 +202,10 @@ int main(int argc, char *argv[])
|
|||||||
if (strlen(musl_optarg) == 4)
|
if (strlen(musl_optarg) == 4)
|
||||||
opt_G(&musl_optarg[1]);
|
opt_G(&musl_optarg[1]);
|
||||||
else
|
else
|
||||||
errx("Must specify exactly 4 characters for option 'g'");
|
errx(1, "Must specify exactly 4 characters for option 'g'");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'H':
|
|
||||||
if (!haltnop)
|
|
||||||
errx("`-H` and `-h` don't make sense together");
|
|
||||||
warnOnHaltNop = false;
|
|
||||||
break;
|
|
||||||
case 'h':
|
case 'h':
|
||||||
if (!warnOnHaltNop)
|
|
||||||
errx("`-H` and `-h` don't make sense together");
|
|
||||||
haltnop = false;
|
haltnop = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -231,14 +214,7 @@ int main(int argc, char *argv[])
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'L':
|
case 'L':
|
||||||
if (!warnOnLdOpt)
|
optimizeloads = false;
|
||||||
errx("`-L` and `-l` don't make sense together");
|
|
||||||
optimizeLoads = false;
|
|
||||||
break;
|
|
||||||
case 'l':
|
|
||||||
if (!optimizeLoads)
|
|
||||||
errx("`-L` and `-l` don't make sense together");
|
|
||||||
warnOnLdOpt = false;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'M':
|
case 'M':
|
||||||
@@ -247,54 +223,36 @@ int main(int argc, char *argv[])
|
|||||||
else
|
else
|
||||||
dependfile = fopen(musl_optarg, "w");
|
dependfile = fopen(musl_optarg, "w");
|
||||||
if (dependfile == NULL)
|
if (dependfile == NULL)
|
||||||
err("Could not open dependfile %s", musl_optarg);
|
err(1, "Could not open dependfile %s", musl_optarg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'o':
|
case 'o':
|
||||||
out_SetFileName(musl_optarg);
|
out_SetFileName(musl_optarg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
unsigned long padByte;
|
unsigned long fill;
|
||||||
case 'p':
|
case 'p':
|
||||||
padByte = strtoul(musl_optarg, &ep, 0);
|
fill = strtoul(musl_optarg, &ep, 0);
|
||||||
|
|
||||||
if (musl_optarg[0] == '\0' || *ep != '\0')
|
if (musl_optarg[0] == '\0' || *ep != '\0')
|
||||||
errx("Invalid argument for option 'p'");
|
errx(1, "Invalid argument for option 'p'");
|
||||||
|
|
||||||
if (padByte > 0xFF)
|
if (fill < 0 || fill > 0xFF)
|
||||||
errx("Argument for option 'p' must be between 0 and 0xFF");
|
errx(1, "Argument for option 'p' must be between 0 and 0xFF");
|
||||||
|
|
||||||
opt_P(padByte);
|
opt_P(fill);
|
||||||
break;
|
|
||||||
|
|
||||||
unsigned long precision;
|
|
||||||
const char *precisionArg;
|
|
||||||
case 'Q':
|
|
||||||
precisionArg = musl_optarg;
|
|
||||||
if (precisionArg[0] == '.')
|
|
||||||
precisionArg++;
|
|
||||||
precision = strtoul(precisionArg, &ep, 0);
|
|
||||||
|
|
||||||
if (musl_optarg[0] == '\0' || *ep != '\0')
|
|
||||||
errx("Invalid argument for option 'Q'");
|
|
||||||
|
|
||||||
if (precision < 1 || precision > 31)
|
|
||||||
errx("Argument for option 'Q' must be between 1 and 31");
|
|
||||||
|
|
||||||
opt_Q(precision);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'r':
|
case 'r':
|
||||||
maxDepth = strtoul(musl_optarg, &ep, 0);
|
maxRecursionDepth = strtoul(musl_optarg, &ep, 0);
|
||||||
|
|
||||||
if (musl_optarg[0] == '\0' || *ep != '\0')
|
if (musl_optarg[0] == '\0' || *ep != '\0')
|
||||||
errx("Invalid argument for option 'r'");
|
errx(1, "Invalid argument for option 'r'");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'V':
|
case 'V':
|
||||||
printf("rgbasm %s\n", get_package_version_string());
|
printf("rgbasm %s\n", get_package_version_string());
|
||||||
exit(0);
|
exit(0);
|
||||||
|
|
||||||
case 'v':
|
case 'v':
|
||||||
verbose = true;
|
verbose = true;
|
||||||
break;
|
break;
|
||||||
@@ -307,49 +265,57 @@ int main(int argc, char *argv[])
|
|||||||
warnings = false;
|
warnings = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Long-only options
|
/* Long-only options */
|
||||||
case 0:
|
case 0:
|
||||||
switch (depType) {
|
switch (depType) {
|
||||||
case 'G':
|
case 'G':
|
||||||
generatedMissingIncludes = true;
|
oGeneratedMissingIncludes = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'P':
|
case 'P':
|
||||||
generatePhonyDeps = true;
|
oGeneratePhonyDeps = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
char *newTarget;
|
|
||||||
case 'Q':
|
case 'Q':
|
||||||
case 'T':
|
case 'T':
|
||||||
newTarget = musl_optarg;
|
if (musl_optind == argc)
|
||||||
|
errx(1, "-M%c takes a target file name argument", depType);
|
||||||
|
ep = musl_optarg;
|
||||||
if (depType == 'Q')
|
if (depType == 'Q')
|
||||||
newTarget = make_escape(newTarget);
|
ep = make_escape(ep);
|
||||||
size_t newTargetLen = strlen(newTarget) + 1; // Plus the space
|
|
||||||
|
|
||||||
targetFileName = realloc(targetFileName,
|
nTargetFileNameLen += strlen(ep) + 1;
|
||||||
targetFileNameLen + newTargetLen + 1);
|
if (!tzTargetFileName) {
|
||||||
if (targetFileName == NULL)
|
/* On first alloc, make an empty str */
|
||||||
err("Cannot append new file to target file list");
|
tzTargetFileName = malloc(nTargetFileNameLen + 1);
|
||||||
memcpy(&targetFileName[targetFileNameLen], newTarget, newTargetLen);
|
if (tzTargetFileName)
|
||||||
|
*tzTargetFileName = '\0';
|
||||||
|
} else {
|
||||||
|
tzTargetFileName = realloc(tzTargetFileName,
|
||||||
|
nTargetFileNameLen + 1);
|
||||||
|
}
|
||||||
|
if (tzTargetFileName == NULL)
|
||||||
|
err(1, "Cannot append new file to target file list");
|
||||||
|
strcat(tzTargetFileName, ep);
|
||||||
if (depType == 'Q')
|
if (depType == 'Q')
|
||||||
free(newTarget);
|
free(ep);
|
||||||
targetFileNameLen += newTargetLen;
|
char *ptr = tzTargetFileName + strlen(tzTargetFileName);
|
||||||
targetFileName[targetFileNameLen - 1] = ' ';
|
|
||||||
|
*ptr++ = ' ';
|
||||||
|
*ptr = '\0';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Unrecognized options
|
/* Unrecognized options */
|
||||||
default:
|
default:
|
||||||
print_usage();
|
print_usage();
|
||||||
// NOTREACHED
|
/* NOTREACHED */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetFileName == NULL)
|
if (tzTargetFileName == NULL)
|
||||||
targetFileName = objectName;
|
tzTargetFileName = tzObjectname;
|
||||||
else
|
|
||||||
targetFileName[targetFileNameLen - 1] = '\0'; // Overwrite the last space
|
|
||||||
|
|
||||||
if (argc == musl_optind) {
|
if (argc == musl_optind) {
|
||||||
fputs("FATAL: No input files\n", stderr);
|
fputs("FATAL: No input files\n", stderr);
|
||||||
@@ -365,17 +331,17 @@ int main(int argc, char *argv[])
|
|||||||
printf("Assembling %s\n", mainFileName);
|
printf("Assembling %s\n", mainFileName);
|
||||||
|
|
||||||
if (dependfile) {
|
if (dependfile) {
|
||||||
if (!targetFileName)
|
if (!tzTargetFileName)
|
||||||
errx("Dependency files can only be created if a target file is specified with either -o, -MQ or -MT");
|
errx(1, "Dependency files can only be created if a target file is specified with either -o, -MQ or -MT\n");
|
||||||
|
|
||||||
fprintf(dependfile, "%s: %s\n", targetFileName, mainFileName);
|
fprintf(dependfile, "%s: %s\n", tzTargetFileName, mainFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
charmap_New("main", NULL);
|
charmap_New("main", NULL);
|
||||||
|
|
||||||
// Init lexer and file stack, providing file info
|
// Init lexer and file stack, prodiving file info
|
||||||
lexer_Init();
|
lexer_Init();
|
||||||
fstk_Init(mainFileName, maxDepth);
|
fstk_Init(mainFileName, maxRecursionDepth);
|
||||||
|
|
||||||
// Perform parse (yyparse is auto-generated from `parser.y`)
|
// Perform parse (yyparse is auto-generated from `parser.y`)
|
||||||
if (yyparse() != 0 && nbErrors == 0)
|
if (yyparse() != 0 && nbErrors == 0)
|
||||||
@@ -387,15 +353,15 @@ int main(int argc, char *argv[])
|
|||||||
sect_CheckUnionClosed();
|
sect_CheckUnionClosed();
|
||||||
|
|
||||||
if (nbErrors != 0)
|
if (nbErrors != 0)
|
||||||
errx("Assembly aborted (%u error%s)!", nbErrors,
|
errx(1, "Assembly aborted (%u error%s)!", nbErrors,
|
||||||
nbErrors == 1 ? "" : "s");
|
nbErrors == 1 ? "" : "s");
|
||||||
|
|
||||||
// If parse aborted due to missing an include, and `-MG` was given, exit normally
|
// If parse aborted due to missing an include, and `-MG` was given, exit normally
|
||||||
if (failedOnMissingInclude)
|
if (oFailedOnMissingInclude)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// If no path specified, don't write file
|
/* If no path specified, don't write file */
|
||||||
if (objectName != NULL)
|
if (tzObjectname != NULL)
|
||||||
out_WriteObject();
|
out_WriteObject();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
213
src/asm/opt.c
213
src/asm/opt.c
@@ -1,12 +1,4 @@
|
|||||||
/*
|
|
||||||
* This file is part of RGBDS.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2022, RGBDS contributors.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -14,80 +6,32 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "asm/fixpoint.h"
|
|
||||||
#include "asm/fstack.h"
|
|
||||||
#include "asm/lexer.h"
|
#include "asm/lexer.h"
|
||||||
#include "asm/main.h"
|
|
||||||
#include "asm/section.h"
|
#include "asm/section.h"
|
||||||
#include "asm/warning.h"
|
#include "asm/warning.h"
|
||||||
|
|
||||||
struct OptStackEntry {
|
struct OptStackEntry {
|
||||||
char binary[2];
|
char binary[2];
|
||||||
char gbgfx[4];
|
char gbgfx[4];
|
||||||
uint8_t fixPrecision;
|
int32_t fillByte;
|
||||||
uint8_t fillByte;
|
|
||||||
bool haltnop;
|
|
||||||
bool warnOnHaltNop;
|
|
||||||
bool optimizeLoads;
|
|
||||||
bool warnOnLdOpt;
|
|
||||||
bool warningsAreErrors;
|
|
||||||
size_t maxRecursionDepth;
|
|
||||||
// Don't be confused: we use the size of the **global variable** `warningStates`!
|
|
||||||
enum WarningState warningStates[sizeof(warningStates)];
|
|
||||||
struct OptStackEntry *next;
|
struct OptStackEntry *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct OptStackEntry *stack = NULL;
|
static struct OptStackEntry *stack = NULL;
|
||||||
|
|
||||||
void opt_B(char const chars[2])
|
void opt_B(char chars[2])
|
||||||
{
|
{
|
||||||
lexer_SetBinDigits(chars);
|
lexer_SetBinDigits(chars);
|
||||||
}
|
}
|
||||||
|
|
||||||
void opt_G(char const chars[4])
|
void opt_G(char chars[4])
|
||||||
{
|
{
|
||||||
lexer_SetGfxDigits(chars);
|
lexer_SetGfxDigits(chars);
|
||||||
}
|
}
|
||||||
|
|
||||||
void opt_P(uint8_t padByte)
|
void opt_P(uint8_t fill)
|
||||||
{
|
{
|
||||||
fillByte = padByte;
|
fillByte = fill;
|
||||||
}
|
|
||||||
|
|
||||||
void opt_Q(uint8_t precision)
|
|
||||||
{
|
|
||||||
fixPrecision = precision;
|
|
||||||
}
|
|
||||||
|
|
||||||
void opt_R(size_t newDepth)
|
|
||||||
{
|
|
||||||
fstk_NewRecursionDepth(newDepth);
|
|
||||||
lexer_CheckRecursionDepth();
|
|
||||||
}
|
|
||||||
|
|
||||||
void opt_H(bool warn)
|
|
||||||
{
|
|
||||||
warnOnHaltNop = warn;
|
|
||||||
}
|
|
||||||
|
|
||||||
void opt_h(bool halt)
|
|
||||||
{
|
|
||||||
haltnop = halt;
|
|
||||||
}
|
|
||||||
|
|
||||||
void opt_L(bool optimize)
|
|
||||||
{
|
|
||||||
optimizeLoads = optimize;
|
|
||||||
}
|
|
||||||
|
|
||||||
void opt_l(bool warn)
|
|
||||||
{
|
|
||||||
warnOnLdOpt = warn;
|
|
||||||
}
|
|
||||||
|
|
||||||
void opt_W(char *flag)
|
|
||||||
{
|
|
||||||
processWarningFlag(flag);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void opt_Parse(char *s)
|
void opt_Parse(char *s)
|
||||||
@@ -110,135 +54,18 @@ void opt_Parse(char *s)
|
|||||||
case 'p':
|
case 'p':
|
||||||
if (strlen(&s[1]) <= 2) {
|
if (strlen(&s[1]) <= 2) {
|
||||||
int result;
|
int result;
|
||||||
unsigned int padByte;
|
unsigned int fillchar;
|
||||||
|
|
||||||
result = sscanf(&s[1], "%x", &padByte);
|
result = sscanf(&s[1], "%x", &fillchar);
|
||||||
if (result != 1)
|
if (result != EOF && result != 1)
|
||||||
error("Invalid argument for option 'p'\n");
|
error("Invalid argument for option 'p'\n");
|
||||||
else if (padByte > 0xFF)
|
|
||||||
error("Argument for option 'p' must be between 0 and 0xFF\n");
|
|
||||||
else
|
else
|
||||||
opt_P(padByte);
|
opt_P(fillchar);
|
||||||
} else {
|
} else {
|
||||||
error("Invalid argument for option 'p'\n");
|
error("Invalid argument for option 'p'\n");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
const char *precisionArg;
|
|
||||||
case 'Q':
|
|
||||||
precisionArg = &s[1];
|
|
||||||
if (precisionArg[0] == '.')
|
|
||||||
precisionArg++;
|
|
||||||
if (strlen(precisionArg) <= 2) {
|
|
||||||
int result;
|
|
||||||
unsigned int precision;
|
|
||||||
|
|
||||||
result = sscanf(precisionArg, "%u", &precision);
|
|
||||||
if (result != 1)
|
|
||||||
error("Invalid argument for option 'Q'\n");
|
|
||||||
else if (precision < 1 || precision > 31)
|
|
||||||
error("Argument for option 'Q' must be between 1 and 31\n");
|
|
||||||
else
|
|
||||||
opt_Q(precision);
|
|
||||||
} else {
|
|
||||||
error("Invalid argument for option 'Q'\n");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'r': {
|
|
||||||
++s; // Skip 'r'
|
|
||||||
while (isblank(*s))
|
|
||||||
++s; // Skip leading whitespace
|
|
||||||
|
|
||||||
if (s[0] == '\0') {
|
|
||||||
error("Missing argument to option 'r'\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *endptr;
|
|
||||||
unsigned long newDepth = strtoul(s, &endptr, 10);
|
|
||||||
|
|
||||||
if (*endptr != '\0') {
|
|
||||||
error("Invalid argument to option 'r' (\"%s\")\n", s);
|
|
||||||
} else if (errno == ERANGE) {
|
|
||||||
error("Argument to 'r' is out of range (\"%s\")\n", s);
|
|
||||||
} else {
|
|
||||||
opt_R(newDepth);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'H':
|
|
||||||
if (s[1] == '\0')
|
|
||||||
opt_H(false);
|
|
||||||
else
|
|
||||||
error("Option 'H' does not take an argument\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'h':
|
|
||||||
if (s[1] == '\0')
|
|
||||||
opt_h(false);
|
|
||||||
else
|
|
||||||
error("Option 'h' does not take an argument\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'L':
|
|
||||||
if (s[1] == '\0')
|
|
||||||
opt_L(false);
|
|
||||||
else
|
|
||||||
error("Option 'L' does not take an argument\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'l':
|
|
||||||
if (s[1] == '\0')
|
|
||||||
opt_l(false);
|
|
||||||
else
|
|
||||||
error("Option 'l' does not take an argument\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'W':
|
|
||||||
if (strlen(&s[1]) > 0)
|
|
||||||
opt_W(&s[1]);
|
|
||||||
else
|
|
||||||
error("Must specify an argument for option 'W'\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '!': // negates flag options that do not take an argument
|
|
||||||
switch (s[1]) {
|
|
||||||
case 'H':
|
|
||||||
if (s[2] == '\0')
|
|
||||||
opt_H(true);
|
|
||||||
else
|
|
||||||
error("Option '!H' does not take an argument\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'h':
|
|
||||||
if (s[2] == '\0')
|
|
||||||
opt_h(true);
|
|
||||||
else
|
|
||||||
error("Option '!h' does not take an argument\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'L':
|
|
||||||
if (s[2] == '\0')
|
|
||||||
opt_L(true);
|
|
||||||
else
|
|
||||||
error("Option '!L' does not take an argument\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'l':
|
|
||||||
if (s[2] == '\0')
|
|
||||||
opt_l(true);
|
|
||||||
else
|
|
||||||
error("Option '!l' does not take an argument\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
error("Unknown option '!%c'\n", s[1]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
error("Unknown option '%c'\n", s[0]);
|
error("Unknown option '%c'\n", s[0]);
|
||||||
break;
|
break;
|
||||||
@@ -261,20 +88,8 @@ void opt_Push(void)
|
|||||||
entry->gbgfx[2] = gfxDigits[2];
|
entry->gbgfx[2] = gfxDigits[2];
|
||||||
entry->gbgfx[3] = gfxDigits[3];
|
entry->gbgfx[3] = gfxDigits[3];
|
||||||
|
|
||||||
entry->fixPrecision = fixPrecision; // Pulled from fixpoint.h
|
|
||||||
|
|
||||||
entry->fillByte = fillByte; // Pulled from section.h
|
entry->fillByte = fillByte; // Pulled from section.h
|
||||||
|
|
||||||
entry->haltnop = haltnop; // Pulled from main.h
|
|
||||||
entry->warnOnHaltNop = warnOnHaltNop;
|
|
||||||
|
|
||||||
entry->optimizeLoads = optimizeLoads; // Pulled from main.h
|
|
||||||
entry->warnOnLdOpt = warnOnLdOpt;
|
|
||||||
|
|
||||||
// Both of these pulled from warning.h
|
|
||||||
entry->warningsAreErrors = warningsAreErrors;
|
|
||||||
memcpy(entry->warningStates, warningStates, sizeof(warningStates));
|
|
||||||
|
|
||||||
entry->next = stack;
|
entry->next = stack;
|
||||||
stack = entry;
|
stack = entry;
|
||||||
}
|
}
|
||||||
@@ -291,16 +106,6 @@ void opt_Pop(void)
|
|||||||
opt_B(entry->binary);
|
opt_B(entry->binary);
|
||||||
opt_G(entry->gbgfx);
|
opt_G(entry->gbgfx);
|
||||||
opt_P(entry->fillByte);
|
opt_P(entry->fillByte);
|
||||||
opt_Q(entry->fixPrecision);
|
|
||||||
opt_H(entry->warnOnHaltNop);
|
|
||||||
opt_h(entry->haltnop);
|
|
||||||
opt_L(entry->optimizeLoads);
|
|
||||||
opt_l(entry->warnOnLdOpt);
|
|
||||||
|
|
||||||
// opt_W does not apply a whole warning state; it processes one flag string
|
|
||||||
warningsAreErrors = entry->warningsAreErrors;
|
|
||||||
memcpy(warningStates, entry->warningStates, sizeof(warningStates));
|
|
||||||
|
|
||||||
stack = entry->next;
|
stack = entry->next;
|
||||||
free(entry);
|
free(entry);
|
||||||
}
|
}
|
||||||
|
|||||||
238
src/asm/output.c
238
src/asm/output.c
@@ -6,7 +6,9 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Outputs an objectfile
|
/*
|
||||||
|
* Outputs an objectfile
|
||||||
|
*/
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@@ -25,19 +27,20 @@
|
|||||||
#include "asm/symbol.h"
|
#include "asm/symbol.h"
|
||||||
#include "asm/warning.h"
|
#include "asm/warning.h"
|
||||||
|
|
||||||
#include "error.h"
|
#include "extern/err.h"
|
||||||
|
|
||||||
#include "linkdefs.h"
|
#include "linkdefs.h"
|
||||||
#include "platform.h" // strdup
|
#include "platform.h" // strdup
|
||||||
|
|
||||||
struct Patch {
|
struct Patch {
|
||||||
struct FileStackNode const *src;
|
struct FileStackNode const *src;
|
||||||
uint32_t lineNo;
|
uint32_t lineNo;
|
||||||
uint32_t offset;
|
uint32_t nOffset;
|
||||||
struct Section *pcSection;
|
struct Section *pcSection;
|
||||||
uint32_t pcOffset;
|
uint32_t pcOffset;
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
uint32_t rpnSize;
|
uint32_t nRPNSize;
|
||||||
uint8_t *rpn;
|
uint8_t *pRPN;
|
||||||
struct Patch *next;
|
struct Patch *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -48,31 +51,36 @@ struct Assertion {
|
|||||||
struct Assertion *next;
|
struct Assertion *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
char *objectName;
|
char *tzObjectname;
|
||||||
|
|
||||||
struct Section *sectionList;
|
/* TODO: shouldn't `pCurrentSection` be somewhere else? */
|
||||||
|
struct Section *pSectionList, *pCurrentSection;
|
||||||
|
|
||||||
// Linked list of symbols to put in the object file
|
/* Linked list of symbols to put in the object file */
|
||||||
static struct Symbol *objectSymbols = NULL;
|
static struct Symbol *objectSymbols = NULL;
|
||||||
static struct Symbol **objectSymbolsTail = &objectSymbols;
|
static struct Symbol **objectSymbolsTail = &objectSymbols;
|
||||||
static uint32_t nbSymbols = 0; // Length of the above list
|
static uint32_t nbSymbols = 0; /* Length of the above list */
|
||||||
|
|
||||||
static struct Assertion *assertions = NULL;
|
static struct Assertion *assertions = NULL;
|
||||||
|
|
||||||
static struct FileStackNode *fileStackNodes = NULL;
|
static struct FileStackNode *fileStackNodes = NULL;
|
||||||
|
|
||||||
// Count the number of sections used in this object
|
/*
|
||||||
|
* Count the number of sections used in this object
|
||||||
|
*/
|
||||||
static uint32_t countSections(void)
|
static uint32_t countSections(void)
|
||||||
{
|
{
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
|
|
||||||
for (struct Section const *sect = sectionList; sect; sect = sect->next)
|
for (struct Section const *sect = pSectionList; sect; sect = sect->next)
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count the number of patches used in this object
|
/*
|
||||||
|
* Count the number of patches used in this object
|
||||||
|
*/
|
||||||
static uint32_t countPatches(struct Section const *sect)
|
static uint32_t countPatches(struct Section const *sect)
|
||||||
{
|
{
|
||||||
uint32_t r = 0;
|
uint32_t r = 0;
|
||||||
@@ -84,7 +92,9 @@ static uint32_t countPatches(struct Section const *sect)
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count the number of assertions used in this object
|
/**
|
||||||
|
* Count the number of assertions used in this object
|
||||||
|
*/
|
||||||
static uint32_t countAsserts(void)
|
static uint32_t countAsserts(void)
|
||||||
{
|
{
|
||||||
struct Assertion *assert = assertions;
|
struct Assertion *assert = assertions;
|
||||||
@@ -97,7 +107,9 @@ static uint32_t countAsserts(void)
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write a long to a file (little-endian)
|
/*
|
||||||
|
* Write a long to a file (little-endian)
|
||||||
|
*/
|
||||||
static void putlong(uint32_t i, FILE *f)
|
static void putlong(uint32_t i, FILE *f)
|
||||||
{
|
{
|
||||||
putc(i, f);
|
putc(i, f);
|
||||||
@@ -106,7 +118,9 @@ static void putlong(uint32_t i, FILE *f)
|
|||||||
putc(i >> 24, f);
|
putc(i >> 24, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write a NULL-terminated string to a file
|
/*
|
||||||
|
* Write a NULL-terminated string to a file
|
||||||
|
*/
|
||||||
static void putstring(char const *s, FILE *f)
|
static void putstring(char const *s, FILE *f)
|
||||||
{
|
{
|
||||||
while (*s)
|
while (*s)
|
||||||
@@ -121,7 +135,7 @@ static uint32_t getNbFileStackNodes(void)
|
|||||||
|
|
||||||
void out_RegisterNode(struct FileStackNode *node)
|
void out_RegisterNode(struct FileStackNode *node)
|
||||||
{
|
{
|
||||||
// If node is not already registered, register it (and parents), and give it a unique ID
|
/* If node is not already registered, register it (and parents), and give it a unique ID */
|
||||||
while (node->ID == (uint32_t)-1) {
|
while (node->ID == (uint32_t)-1) {
|
||||||
node->ID = getNbFileStackNodes();
|
node->ID = getNbFileStackNodes();
|
||||||
if (node->ID == (uint32_t)-1)
|
if (node->ID == (uint32_t)-1)
|
||||||
@@ -129,7 +143,7 @@ void out_RegisterNode(struct FileStackNode *node)
|
|||||||
node->next = fileStackNodes;
|
node->next = fileStackNodes;
|
||||||
fileStackNodes = node;
|
fileStackNodes = node;
|
||||||
|
|
||||||
// Also register the node's parents
|
/* Also register the node's parents */
|
||||||
node = node->parent;
|
node = node->parent;
|
||||||
if (!node)
|
if (!node)
|
||||||
break;
|
break;
|
||||||
@@ -144,24 +158,28 @@ This is code intended to replace a node, which is pretty useless until ref count
|
|||||||
|
|
||||||
struct FileStackNode **ptr = &fileStackNodes;
|
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
|
* The linked list is supposed to have decrementing IDs, so iterate with less memory reads,
|
||||||
// that breaks this assumption.
|
* 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--)
|
for (uint32_t i = fileStackNodes->ID; i != node->ID; i--)
|
||||||
ptr = &(*ptr)->next;
|
ptr = &(*ptr)->next;
|
||||||
assert((*ptr)->ID == node->ID);
|
assert((*ptr)->ID == node->ID);
|
||||||
|
|
||||||
node->next = (*ptr)->next;
|
node->next = (*ptr)->next;
|
||||||
assert(!node->next || node->next->ID == node->ID - 1); // Catch inconsistencies early
|
assert(!node->next || node->next->ID == node->ID - 1); /* Catch inconsistencies early */
|
||||||
// TODO: unreference the node
|
/* TODO: unreference the node */
|
||||||
*ptr = node;
|
*ptr = node;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a section's ID
|
/*
|
||||||
|
* Return a section's ID
|
||||||
|
*/
|
||||||
static uint32_t getsectid(struct Section const *sect)
|
static uint32_t getsectid(struct Section const *sect)
|
||||||
{
|
{
|
||||||
struct Section const *sec = sectionList;
|
struct Section const *sec = pSectionList;
|
||||||
uint32_t ID = 0;
|
uint32_t ID = 0;
|
||||||
|
|
||||||
while (sec) {
|
while (sec) {
|
||||||
@@ -176,24 +194,28 @@ static uint32_t getsectid(struct Section const *sect)
|
|||||||
|
|
||||||
static uint32_t getSectIDIfAny(struct Section const *sect)
|
static uint32_t getSectIDIfAny(struct Section const *sect)
|
||||||
{
|
{
|
||||||
return sect ? getsectid(sect) : (uint32_t)-1;
|
return sect ? getsectid(sect) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write a patch to a file
|
/*
|
||||||
|
* Write a patch to a file
|
||||||
|
*/
|
||||||
static void writepatch(struct Patch const *patch, FILE *f)
|
static void writepatch(struct Patch const *patch, FILE *f)
|
||||||
{
|
{
|
||||||
assert(patch->src->ID != (uint32_t)-1);
|
assert(patch->src->ID != -1);
|
||||||
putlong(patch->src->ID, f);
|
putlong(patch->src->ID, f);
|
||||||
putlong(patch->lineNo, f);
|
putlong(patch->lineNo, f);
|
||||||
putlong(patch->offset, f);
|
putlong(patch->nOffset, f);
|
||||||
putlong(getSectIDIfAny(patch->pcSection), f);
|
putlong(getSectIDIfAny(patch->pcSection), f);
|
||||||
putlong(patch->pcOffset, f);
|
putlong(patch->pcOffset, f);
|
||||||
putc(patch->type, f);
|
putc(patch->type, f);
|
||||||
putlong(patch->rpnSize, f);
|
putlong(patch->nRPNSize, f);
|
||||||
fwrite(patch->rpn, 1, patch->rpnSize, f);
|
fwrite(patch->pRPN, 1, patch->nRPNSize, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write a section to a file
|
/*
|
||||||
|
* Write a section to a file
|
||||||
|
*/
|
||||||
static void writesection(struct Section const *sect, FILE *f)
|
static void writesection(struct Section const *sect, FILE *f)
|
||||||
{
|
{
|
||||||
putstring(sect->name, f);
|
putstring(sect->name, f);
|
||||||
@@ -220,29 +242,16 @@ static void writesection(struct Section const *sect, FILE *f)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void freesection(struct Section const *sect)
|
/*
|
||||||
{
|
* Write a symbol to a file
|
||||||
if (sect_HasData(sect->type)) {
|
*/
|
||||||
struct Patch *patch = sect->patches;
|
|
||||||
|
|
||||||
while (patch != NULL) {
|
|
||||||
struct Patch *next = patch->next;
|
|
||||||
|
|
||||||
free(patch->rpn);
|
|
||||||
free(patch);
|
|
||||||
patch = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write a symbol to a file
|
|
||||||
static void writesymbol(struct Symbol const *sym, FILE *f)
|
static void writesymbol(struct Symbol const *sym, FILE *f)
|
||||||
{
|
{
|
||||||
putstring(sym->name, f);
|
putstring(sym->name, f);
|
||||||
if (!sym_IsDefined(sym)) {
|
if (!sym_IsDefined(sym)) {
|
||||||
putc(SYMTYPE_IMPORT, f);
|
putc(SYMTYPE_IMPORT, f);
|
||||||
} else {
|
} else {
|
||||||
assert(sym->src->ID != (uint32_t)-1);
|
assert(sym->src->ID != -1);
|
||||||
|
|
||||||
putc(sym->isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, f);
|
putc(sym->isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, f);
|
||||||
putlong(sym->src->ID, f);
|
putlong(sym->src->ID, f);
|
||||||
@@ -263,8 +272,10 @@ static void registerSymbol(struct Symbol *sym)
|
|||||||
sym->ID = nbSymbols++;
|
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
|
* 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)
|
static uint32_t getSymbolID(struct Symbol *sym)
|
||||||
{
|
{
|
||||||
if (sym->ID == (uint32_t)-1 && !sym_IsPC(sym))
|
if (sym->ID == (uint32_t)-1 && !sym_IsPC(sym))
|
||||||
@@ -275,7 +286,7 @@ static uint32_t getSymbolID(struct Symbol *sym)
|
|||||||
static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
||||||
uint32_t rpnlen)
|
uint32_t rpnlen)
|
||||||
{
|
{
|
||||||
char symName[512];
|
char tzSym[512];
|
||||||
|
|
||||||
for (size_t offset = 0; offset < rpnlen; ) {
|
for (size_t offset = 0; offset < rpnlen; ) {
|
||||||
#define popbyte() rpn[offset++]
|
#define popbyte() rpn[offset++]
|
||||||
@@ -299,14 +310,14 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
|||||||
case RPN_SYM:
|
case RPN_SYM:
|
||||||
i = 0;
|
i = 0;
|
||||||
do {
|
do {
|
||||||
symName[i] = popbyte();
|
tzSym[i] = popbyte();
|
||||||
} while (symName[i++]);
|
} while (tzSym[i++]);
|
||||||
|
|
||||||
// The symbol name is always written expanded
|
// The symbol name is always written expanded
|
||||||
sym = sym_FindExactSymbol(symName);
|
sym = sym_FindExactSymbol(tzSym);
|
||||||
if (sym_IsConstant(sym)) {
|
if (sym_IsConstant(sym)) {
|
||||||
writebyte(RPN_CONST);
|
writebyte(RPN_CONST);
|
||||||
value = sym_GetConstantValue(symName);
|
value = sym_GetConstantValue(tzSym);
|
||||||
} else {
|
} else {
|
||||||
writebyte(RPN_SYM);
|
writebyte(RPN_SYM);
|
||||||
value = getSymbolID(sym);
|
value = getSymbolID(sym);
|
||||||
@@ -321,11 +332,11 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
|||||||
case RPN_BANK_SYM:
|
case RPN_BANK_SYM:
|
||||||
i = 0;
|
i = 0;
|
||||||
do {
|
do {
|
||||||
symName[i] = popbyte();
|
tzSym[i] = popbyte();
|
||||||
} while (symName[i++]);
|
} while (tzSym[i++]);
|
||||||
|
|
||||||
// The symbol name is always written expanded
|
// The symbol name is always written expanded
|
||||||
sym = sym_FindExactSymbol(symName);
|
sym = sym_FindExactSymbol(tzSym);
|
||||||
value = getSymbolID(sym);
|
value = getSymbolID(sym);
|
||||||
|
|
||||||
writebyte(RPN_BANK_SYM);
|
writebyte(RPN_BANK_SYM);
|
||||||
@@ -343,22 +354,6 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
|||||||
} while (b != 0);
|
} while (b != 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_SIZEOF_SECT:
|
|
||||||
writebyte(RPN_SIZEOF_SECT);
|
|
||||||
do {
|
|
||||||
b = popbyte();
|
|
||||||
writebyte(b);
|
|
||||||
} while (b != 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RPN_STARTOF_SECT:
|
|
||||||
writebyte(RPN_STARTOF_SECT);
|
|
||||||
do {
|
|
||||||
b = popbyte();
|
|
||||||
writebyte(b);
|
|
||||||
} while (b != 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
writebyte(rpndata);
|
writebyte(rpndata);
|
||||||
break;
|
break;
|
||||||
@@ -368,48 +363,52 @@ 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
|
* 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 *patch = malloc(sizeof(struct Patch));
|
struct Patch *patch = malloc(sizeof(struct Patch));
|
||||||
uint32_t rpnSize = expr->isKnown ? 5 : expr->rpnPatchSize;
|
uint32_t rpnSize = expr->isKnown ? 5 : expr->nRPNPatchSize;
|
||||||
struct FileStackNode *node = fstk_GetFileStack();
|
struct FileStackNode *node = fstk_GetFileStack();
|
||||||
|
|
||||||
if (!patch)
|
if (!patch)
|
||||||
fatalerror("No memory for patch: %s\n", strerror(errno));
|
fatalerror("No memory for patch: %s\n", strerror(errno));
|
||||||
|
|
||||||
patch->rpn = malloc(sizeof(*patch->rpn) * rpnSize);
|
patch->pRPN = malloc(sizeof(*patch->pRPN) * rpnSize);
|
||||||
if (!patch->rpn)
|
if (!patch->pRPN)
|
||||||
fatalerror("No memory for patch's RPN rpnSize: %s\n", strerror(errno));
|
fatalerror("No memory for patch's RPN expression: %s\n", strerror(errno));
|
||||||
|
|
||||||
patch->type = type;
|
patch->type = type;
|
||||||
patch->src = node;
|
patch->src = node;
|
||||||
out_RegisterNode(node);
|
out_RegisterNode(node);
|
||||||
patch->lineNo = lexer_GetLineNo();
|
patch->lineNo = lexer_GetLineNo();
|
||||||
patch->offset = ofs;
|
patch->nOffset = ofs;
|
||||||
patch->pcSection = sect_GetSymbolSection();
|
patch->pcSection = sect_GetSymbolSection();
|
||||||
patch->pcOffset = sect_GetSymbolOffset();
|
patch->pcOffset = sect_GetSymbolOffset();
|
||||||
|
|
||||||
// If the rpnSize's value is known, output a constant RPN rpnSize directly
|
/* If the expression's value is known, output a constant RPN expression directly */
|
||||||
if (expr->isKnown) {
|
if (expr->isKnown) {
|
||||||
patch->rpnSize = rpnSize;
|
patch->nRPNSize = rpnSize;
|
||||||
// Make sure to update `rpnSize` above if modifying this!
|
/* Make sure to update `rpnSize` above if modifying this! */
|
||||||
patch->rpn[0] = RPN_CONST;
|
patch->pRPN[0] = RPN_CONST;
|
||||||
patch->rpn[1] = (uint32_t)(expr->val) & 0xFF;
|
patch->pRPN[1] = (uint32_t)(expr->nVal) & 0xFF;
|
||||||
patch->rpn[2] = (uint32_t)(expr->val) >> 8;
|
patch->pRPN[2] = (uint32_t)(expr->nVal) >> 8;
|
||||||
patch->rpn[3] = (uint32_t)(expr->val) >> 16;
|
patch->pRPN[3] = (uint32_t)(expr->nVal) >> 16;
|
||||||
patch->rpn[4] = (uint32_t)(expr->val) >> 24;
|
patch->pRPN[4] = (uint32_t)(expr->nVal) >> 24;
|
||||||
} else {
|
} else {
|
||||||
patch->rpnSize = 0;
|
patch->nRPNSize = 0;
|
||||||
writerpn(patch->rpn, &patch->rpnSize, expr->rpn, expr->rpnLength);
|
writerpn(patch->pRPN, &patch->nRPNSize, expr->tRPN, expr->nRPNLength);
|
||||||
}
|
}
|
||||||
assert(patch->rpnSize == rpnSize);
|
assert(patch->nRPNSize == rpnSize);
|
||||||
|
|
||||||
return patch;
|
return patch;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new patch (includes the rpn expr)
|
/*
|
||||||
|
* Create a new patch (includes the rpn expr)
|
||||||
|
*/
|
||||||
void out_CreatePatch(uint32_t type, struct Expression const *expr, uint32_t ofs, uint32_t pcShift)
|
void out_CreatePatch(uint32_t type, struct Expression const *expr, uint32_t ofs, uint32_t pcShift)
|
||||||
{
|
{
|
||||||
struct Patch *patch = allocpatch(type, expr, ofs);
|
struct Patch *patch = allocpatch(type, expr, ofs);
|
||||||
@@ -419,11 +418,13 @@ void out_CreatePatch(uint32_t type, struct Expression const *expr, uint32_t ofs,
|
|||||||
// before those bytes.
|
// before those bytes.
|
||||||
patch->pcOffset -= pcShift;
|
patch->pcOffset -= pcShift;
|
||||||
|
|
||||||
patch->next = currentSection->patches;
|
patch->next = pCurrentSection->patches;
|
||||||
currentSection->patches = patch;
|
pCurrentSection->patches = patch;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates an assert that will be written to the object file
|
/**
|
||||||
|
* Creates an assert that will be written to the object file
|
||||||
|
*/
|
||||||
bool out_CreateAssert(enum AssertionType type, struct Expression const *expr,
|
bool out_CreateAssert(enum AssertionType type, struct Expression const *expr,
|
||||||
char const *message, uint32_t ofs)
|
char const *message, uint32_t ofs)
|
||||||
{
|
{
|
||||||
@@ -451,16 +452,9 @@ static void writeassert(struct Assertion *assert, FILE *f)
|
|||||||
putstring(assert->message, f);
|
putstring(assert->message, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void freeassert(struct Assertion *assert)
|
|
||||||
{
|
|
||||||
free(assert->patch->rpn);
|
|
||||||
free(assert->patch);
|
|
||||||
free(assert);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void writeFileStackNode(struct FileStackNode const *node, FILE *f)
|
static void writeFileStackNode(struct FileStackNode const *node, FILE *f)
|
||||||
{
|
{
|
||||||
putlong(node->parent ? node->parent->ID : (uint32_t)-1, f);
|
putlong(node->parent ? node->parent->ID : -1, f);
|
||||||
putlong(node->lineNo, f);
|
putlong(node->lineNo, f);
|
||||||
putc(node->type, f);
|
putc(node->type, f);
|
||||||
if (node->type != NODE_REPT) {
|
if (node->type != NODE_REPT) {
|
||||||
@@ -469,7 +463,7 @@ static void writeFileStackNode(struct FileStackNode const *node, FILE *f)
|
|||||||
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
|
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
|
||||||
|
|
||||||
putlong(reptNode->reptDepth, f);
|
putlong(reptNode->reptDepth, f);
|
||||||
// Iters are stored by decreasing depth, so reverse the order for output
|
/* Iters are stored by decreasing depth, so reverse the order for output */
|
||||||
for (uint32_t i = reptNode->reptDepth; i--; )
|
for (uint32_t i = reptNode->reptDepth; i--; )
|
||||||
putlong(reptNode->iters[i], f);
|
putlong(reptNode->iters[i], f);
|
||||||
}
|
}
|
||||||
@@ -485,22 +479,24 @@ static void registerUnregisteredSymbol(struct Symbol *symbol, void *arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write an objectfile
|
/*
|
||||||
|
* Write an objectfile
|
||||||
|
*/
|
||||||
void out_WriteObject(void)
|
void out_WriteObject(void)
|
||||||
{
|
{
|
||||||
FILE *f;
|
FILE *f;
|
||||||
if (strcmp(objectName, "-") != 0)
|
if (strcmp(tzObjectname, "-") != 0)
|
||||||
f = fopen(objectName, "wb");
|
f = fopen(tzObjectname, "wb");
|
||||||
else
|
else
|
||||||
f = fdopen(1, "wb");
|
f = fdopen(1, "wb");
|
||||||
|
|
||||||
if (!f)
|
if (!f)
|
||||||
err("Couldn't write file '%s'", objectName);
|
err(1, "Couldn't write file '%s'", tzObjectname);
|
||||||
|
|
||||||
// Also write symbols that weren't written above
|
/* Also write symbols that weren't written above */
|
||||||
sym_ForEach(registerUnregisteredSymbol, NULL);
|
sym_ForEach(registerUnregisteredSymbol, NULL);
|
||||||
|
|
||||||
fprintf(f, RGBDS_OBJECT_VERSION_STRING);
|
fprintf(f, RGBDS_OBJECT_VERSION_STRING, RGBDS_OBJECT_VERSION_NUMBER);
|
||||||
putlong(RGBDS_OBJECT_REV, f);
|
putlong(RGBDS_OBJECT_REV, f);
|
||||||
|
|
||||||
putlong(nbSymbols, f);
|
putlong(nbSymbols, f);
|
||||||
@@ -518,29 +514,23 @@ void out_WriteObject(void)
|
|||||||
for (struct Symbol const *sym = objectSymbols; sym; sym = sym->next)
|
for (struct Symbol const *sym = objectSymbols; sym; sym = sym->next)
|
||||||
writesymbol(sym, f);
|
writesymbol(sym, f);
|
||||||
|
|
||||||
for (struct Section *sect = sectionList; sect; sect = sect->next) {
|
for (struct Section *sect = pSectionList; sect; sect = sect->next)
|
||||||
writesection(sect, f);
|
writesection(sect, f);
|
||||||
freesection(sect);
|
|
||||||
}
|
|
||||||
|
|
||||||
putlong(countAsserts(), f);
|
putlong(countAsserts(), f);
|
||||||
struct Assertion *assert = assertions;
|
for (struct Assertion *assert = assertions; assert;
|
||||||
|
assert = assert->next)
|
||||||
while (assert != NULL) {
|
|
||||||
struct Assertion *next = assert->next;
|
|
||||||
|
|
||||||
writeassert(assert, f);
|
writeassert(assert, f);
|
||||||
freeassert(assert);
|
|
||||||
assert = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the objectfilename
|
/*
|
||||||
|
* Set the objectfilename
|
||||||
|
*/
|
||||||
void out_SetFileName(char *s)
|
void out_SetFileName(char *s)
|
||||||
{
|
{
|
||||||
objectName = s;
|
tzObjectname = s;
|
||||||
if (verbose)
|
if (verbose)
|
||||||
printf("Output filename %s\n", s);
|
printf("Output filename %s\n", s);
|
||||||
}
|
}
|
||||||
|
|||||||
1691
src/asm/parser.y
1691
src/asm/parser.y
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@
|
|||||||
.Nd Game Boy assembler
|
.Nd Game Boy assembler
|
||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm
|
.Nm
|
||||||
.Op Fl EHhLlVvw
|
.Op Fl EhLVvw
|
||||||
.Op Fl b Ar chars
|
.Op Fl b Ar chars
|
||||||
.Op Fl D Ar name Ns Op = Ns Ar value
|
.Op Fl D Ar name Ns Op = Ns Ar value
|
||||||
.Op Fl g Ar chars
|
.Op Fl g Ar chars
|
||||||
@@ -25,7 +25,6 @@
|
|||||||
.Op Fl MQ Ar target_file
|
.Op Fl MQ Ar target_file
|
||||||
.Op Fl o Ar out_file
|
.Op Fl o Ar out_file
|
||||||
.Op Fl p Ar pad_value
|
.Op Fl p Ar pad_value
|
||||||
.Op Fl Q Ar fix_precision
|
|
||||||
.Op Fl r Ar recursion_depth
|
.Op Fl r Ar recursion_depth
|
||||||
.Op Fl W Ar warning
|
.Op Fl W Ar warning
|
||||||
.Ar
|
.Ar
|
||||||
@@ -56,9 +55,9 @@ The defaults are 01.
|
|||||||
.It Fl D Ar name Ns Oo = Ns Ar value Oc , Fl Fl define Ar name Ns Oo = Ns Ar value Oc
|
.It Fl D Ar name Ns Oo = Ns Ar value Oc , Fl Fl define Ar name Ns Oo = Ns Ar value Oc
|
||||||
Add a string symbol to the compiled source code.
|
Add a string symbol to the compiled source code.
|
||||||
This is equivalent to
|
This is equivalent to
|
||||||
.Ql Ar name Ic EQUS No \(dq Ns Ar value Ns \(dq
|
.Ql Ar name Ic EQUS \(dq Ns Ar value Ns \(dq
|
||||||
in code, or
|
in code, or
|
||||||
.Ql Ar name Ic EQUS No \(dq1\(dq
|
.Ql Ar name Ic EQUS \(dq1\(dq
|
||||||
if
|
if
|
||||||
.Ar value
|
.Ar value
|
||||||
is not specified.
|
is not specified.
|
||||||
@@ -67,43 +66,25 @@ Export all labels, including unreferenced and local labels.
|
|||||||
.It Fl g Ar chars , Fl Fl gfx-chars Ar chars
|
.It Fl g Ar chars , Fl Fl gfx-chars Ar chars
|
||||||
Change the four characters used for gfx constants.
|
Change the four characters used for gfx constants.
|
||||||
The defaults are 0123.
|
The defaults are 0123.
|
||||||
.It Fl H , Fl Fl nop-after-halt
|
.It Fl h , Fl Fl halt-without-nop
|
||||||
By default,
|
By default,
|
||||||
.Nm
|
.Nm
|
||||||
inserts a
|
inserts a
|
||||||
.Ic nop
|
.Ic nop
|
||||||
instruction immediately after any
|
instruction immediately after any
|
||||||
.Ic halt
|
.Ic halt
|
||||||
instruction,
|
|
||||||
but this has been deprecated and prints a warning message the first time it occurs.
|
|
||||||
The
|
|
||||||
.Fl H
|
|
||||||
option opts into this insertion,
|
|
||||||
so no warning will be printed.
|
|
||||||
.It Fl h , Fl Fl halt-without-nop
|
|
||||||
Disables inserting a
|
|
||||||
.Ic nop
|
|
||||||
instruction immediately after any
|
|
||||||
.Ic halt
|
|
||||||
instruction.
|
instruction.
|
||||||
|
The
|
||||||
|
.Fl h
|
||||||
|
option disables this behavior.
|
||||||
.It Fl i Ar path , Fl Fl include Ar path
|
.It Fl i Ar path , Fl Fl include Ar path
|
||||||
Add an include path.
|
Add an include path.
|
||||||
.It Fl L , Fl Fl preserve-ld
|
.It Fl L , Fl Fl preserve-ld
|
||||||
By default,
|
Disable the optimization that turns loads of the form
|
||||||
.Nm
|
|
||||||
optimizes loads of the form
|
|
||||||
.Ic LD [$FF00+n8],A
|
.Ic LD [$FF00+n8],A
|
||||||
into the opcode
|
into the opcode
|
||||||
.Ic LDH [$FF00+n8],A ,
|
.Ic LD [H $FF00+n8],A
|
||||||
but this has been deprecated and prints a warning message the first time it occurs.
|
in order to have full control of the result in the final ROM.
|
||||||
The
|
|
||||||
.Fl L
|
|
||||||
option disables this optimization.
|
|
||||||
.It Fl l , Fl Fl auto-ldh
|
|
||||||
Optimize loads of the form
|
|
||||||
.Ic LD [$FF00+n8],A
|
|
||||||
into the opcode
|
|
||||||
.Ic LDH [$FF00+n8],A .
|
|
||||||
.It Fl M Ar depend_file , Fl Fl dependfile Ar depend_file
|
.It Fl M Ar depend_file , Fl Fl dependfile Ar depend_file
|
||||||
Print
|
Print
|
||||||
.Xr make 1
|
.Xr make 1
|
||||||
@@ -149,16 +130,8 @@ Write an object file to the given filename.
|
|||||||
.It Fl p Ar pad_value , Fl Fl pad-value Ar pad_value
|
.It Fl p Ar pad_value , Fl Fl pad-value Ar pad_value
|
||||||
When padding an image, pad with this value.
|
When padding an image, pad with this value.
|
||||||
The default is 0x00.
|
The default is 0x00.
|
||||||
.It Fl Q Ar fix_precision , Fl Fl q-precision Ar fix_precision
|
|
||||||
Use this as the precision of fixed-point numbers after the decimal point, unless they specify their own precision.
|
|
||||||
The default is 16, so fixed-point numbers are Q16.16 (since they are 32-bit integers).
|
|
||||||
The argument may start with a
|
|
||||||
.Ql \&.
|
|
||||||
to match the Q notation, for example,
|
|
||||||
.Ql Fl Q Ar .16 .
|
|
||||||
.It Fl r Ar recursion_depth , Fl Fl recursion-depth Ar recursion_depth
|
.It Fl r Ar recursion_depth , Fl Fl recursion-depth Ar recursion_depth
|
||||||
Specifies the recursion depth past which RGBASM will assume being in an infinite loop.
|
Specifies the recursion depth at which RGBASM will assume being in an infinite loop.
|
||||||
The default is 64.
|
|
||||||
.It Fl V , Fl Fl version
|
.It Fl V , Fl Fl version
|
||||||
Print the version of the program and exit.
|
Print the version of the program and exit.
|
||||||
.It Fl v , Fl Fl verbose
|
.It Fl v , Fl Fl verbose
|
||||||
@@ -216,7 +189,7 @@ Ignoring the
|
|||||||
prefix, entries are listed alphabetically.
|
prefix, entries are listed alphabetically.
|
||||||
.Bl -tag -width Ds
|
.Bl -tag -width Ds
|
||||||
.It Fl Wno-assert
|
.It Fl Wno-assert
|
||||||
Warn when
|
Warns when
|
||||||
.Ic WARN Ns No -type
|
.Ic WARN Ns No -type
|
||||||
assertions fail. (See
|
assertions fail. (See
|
||||||
.Dq Aborting the assembly process
|
.Dq Aborting the assembly process
|
||||||
@@ -224,12 +197,6 @@ in
|
|||||||
.Xr rgbasm 5
|
.Xr rgbasm 5
|
||||||
for
|
for
|
||||||
.Ic ASSERT ) .
|
.Ic ASSERT ) .
|
||||||
.It Fl Wbackwards-for
|
|
||||||
Warn when
|
|
||||||
.Ic FOR
|
|
||||||
loops have their start and stop values switched according to the step value.
|
|
||||||
This warning is enabled by
|
|
||||||
.Fl Wall .
|
|
||||||
.It Fl Wbuiltin-args
|
.It Fl Wbuiltin-args
|
||||||
Warn about incorrect arguments to built-in functions, such as
|
Warn about incorrect arguments to built-in functions, such as
|
||||||
.Fn STRSUB
|
.Fn STRSUB
|
||||||
@@ -241,7 +208,7 @@ Warn when re-defining a charmap mapping.
|
|||||||
This warning is enabled by
|
This warning is enabled by
|
||||||
.Fl Wall .
|
.Fl Wall .
|
||||||
.It Fl Wdiv
|
.It Fl Wdiv
|
||||||
Warn when dividing the smallest negative integer (-2**31) by -1, which yields itself due to integer overflow.
|
Warn when dividing the smallest negative integer by -1, which yields itself due to integer overflow.
|
||||||
.It Fl Wempty-macro-arg
|
.It Fl Wempty-macro-arg
|
||||||
Warn when a macro argument is empty.
|
Warn when a macro argument is empty.
|
||||||
This warning is enabled by
|
This warning is enabled by
|
||||||
@@ -270,43 +237,15 @@ Warn when obsolete constructs such as the
|
|||||||
constant or
|
constant or
|
||||||
.Ic PRINTT
|
.Ic PRINTT
|
||||||
directive are encountered.
|
directive are encountered.
|
||||||
.It Fl Wnumeric-string=
|
|
||||||
Warn when a multi-character string is treated as a number.
|
|
||||||
.Fl Wnumeric-string=0
|
|
||||||
or
|
|
||||||
.Fl Wno-numeric-string
|
|
||||||
disables this warning.
|
|
||||||
.Fl Wnumeric-string=1
|
|
||||||
or just
|
|
||||||
.Fl Wnumeric-string
|
|
||||||
warns about strings longer than four characters, since four or fewer characters fit within a 32-bit integer.
|
|
||||||
.Fl Wnumeric-string=2
|
|
||||||
warns about any multi-character string.
|
|
||||||
.It Fl Wshift
|
.It Fl Wshift
|
||||||
Warn when shifting right a negative value.
|
Warn when shifting right a negative value.
|
||||||
Use a division by 2**N instead.
|
Use a division by 2^N instead.
|
||||||
.It Fl Wshift-amount
|
.It Fl Wshift-amount
|
||||||
Warn when a shift's operand is negative or greater than 32.
|
Warn when a shift's operand is negative or greater than 32.
|
||||||
.It Fl Wtruncation=
|
.It Fl Wno-truncation
|
||||||
Warn when an implicit truncation (for example,
|
Warn when an implicit truncation (for example,
|
||||||
.Ic db
|
.Ic LD [B @] )
|
||||||
to an 8-bit value) loses some bits.
|
loses some bits.
|
||||||
.Fl Wtruncation=0
|
|
||||||
or
|
|
||||||
.Fl Wno-truncation
|
|
||||||
disables this warning.
|
|
||||||
.Fl Wtruncation=1
|
|
||||||
warns when an N-bit value's absolute value is 2**N or greater.
|
|
||||||
.Fl Wtruncation=2
|
|
||||||
or just
|
|
||||||
.Fl Wtruncation
|
|
||||||
also warns when an N-bit value is less than -2**(N-1), which will not fit in two's complement encoding.
|
|
||||||
.It Fl Wunmapped-char
|
|
||||||
Warn when a character goes through charmap conversion but has no defined mapping.
|
|
||||||
This warning is always disabled if the active charmap is empty, and/or is the default charmap
|
|
||||||
.Sq main .
|
|
||||||
This warning is enabled by
|
|
||||||
.Fl Wall .
|
|
||||||
.It Fl Wno-user
|
.It Fl Wno-user
|
||||||
Warn when the
|
Warn when the
|
||||||
.Ic WARN
|
.Ic WARN
|
||||||
File diff suppressed because it is too large
Load Diff
439
src/asm/rpn.c
439
src/asm/rpn.c
@@ -6,12 +6,13 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Controls RPN expressions for objectfiles
|
/*
|
||||||
|
* Controls RPN expressions for objectfiles
|
||||||
|
*/
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <limits.h>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -26,7 +27,7 @@
|
|||||||
|
|
||||||
#include "opmath.h"
|
#include "opmath.h"
|
||||||
|
|
||||||
// Makes an expression "not known", also setting its error message
|
/* Makes an expression "not known", also setting its error message */
|
||||||
#define makeUnknown(expr_, ...) do { \
|
#define makeUnknown(expr_, ...) do { \
|
||||||
struct Expression *_expr = expr_; \
|
struct Expression *_expr = expr_; \
|
||||||
_expr->isKnown = false; \
|
_expr->isKnown = false; \
|
||||||
@@ -37,72 +38,78 @@
|
|||||||
int size = snprintf(_expr->reason, 128, __VA_ARGS__); \
|
int size = snprintf(_expr->reason, 128, __VA_ARGS__); \
|
||||||
if (size >= 128) { /* If this wasn't enough, try again */ \
|
if (size >= 128) { /* If this wasn't enough, try again */ \
|
||||||
_expr->reason = realloc(_expr->reason, size + 1); \
|
_expr->reason = realloc(_expr->reason, size + 1); \
|
||||||
if (!_expr->reason) \
|
|
||||||
fatalerror("Can't allocate err string: %s\n", strerror(errno)); \
|
|
||||||
sprintf(_expr->reason, __VA_ARGS__); \
|
sprintf(_expr->reason, __VA_ARGS__); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
static uint8_t *reserveSpace(struct Expression *expr, uint32_t size)
|
static uint8_t *reserveSpace(struct Expression *expr, uint32_t size)
|
||||||
{
|
{
|
||||||
// This assumes the RPN length is always less than the capacity
|
/* This assumes the RPN length is always less than the capacity */
|
||||||
if (expr->rpnCapacity - expr->rpnLength < size) {
|
if (expr->nRPNCapacity - expr->nRPNLength < size) {
|
||||||
// If there isn't enough room to reserve the space, realloc
|
/* If there isn't enough room to reserve the space, realloc */
|
||||||
if (!expr->rpn)
|
if (!expr->tRPN)
|
||||||
expr->rpnCapacity = 256; // Initial size
|
expr->nRPNCapacity = 256; /* Initial size */
|
||||||
while (expr->rpnCapacity - expr->rpnLength < size) {
|
while (expr->nRPNCapacity - expr->nRPNLength < size) {
|
||||||
if (expr->rpnCapacity >= MAXRPNLEN)
|
if (expr->nRPNCapacity >= MAXRPNLEN)
|
||||||
// To avoid generating humongous object files, cap the
|
/*
|
||||||
// size of RPN expressions
|
* To avoid generating humongous object files, cap the
|
||||||
|
* size of RPN expressions
|
||||||
|
*/
|
||||||
fatalerror("RPN expression cannot grow larger than "
|
fatalerror("RPN expression cannot grow larger than "
|
||||||
EXPAND_AND_STR(MAXRPNLEN) " bytes\n");
|
EXPAND_AND_STR(MAXRPNLEN) " bytes\n");
|
||||||
else if (expr->rpnCapacity > MAXRPNLEN / 2)
|
else if (expr->nRPNCapacity > MAXRPNLEN / 2)
|
||||||
expr->rpnCapacity = MAXRPNLEN;
|
expr->nRPNCapacity = MAXRPNLEN;
|
||||||
else
|
else
|
||||||
expr->rpnCapacity *= 2;
|
expr->nRPNCapacity *= 2;
|
||||||
}
|
}
|
||||||
expr->rpn = realloc(expr->rpn, expr->rpnCapacity);
|
expr->tRPN = realloc(expr->tRPN, expr->nRPNCapacity);
|
||||||
|
|
||||||
if (!expr->rpn)
|
if (!expr->tRPN)
|
||||||
fatalerror("Failed to grow RPN expression: %s\n", strerror(errno));
|
fatalerror("Failed to grow RPN expression: %s\n", strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *ptr = expr->rpn + expr->rpnLength;
|
uint8_t *ptr = expr->tRPN + expr->nRPNLength;
|
||||||
|
|
||||||
expr->rpnLength += size;
|
expr->nRPNLength += size;
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init a RPN expression
|
/*
|
||||||
|
* Init the RPN expression
|
||||||
|
*/
|
||||||
static void rpn_Init(struct Expression *expr)
|
static void rpn_Init(struct Expression *expr)
|
||||||
{
|
{
|
||||||
expr->reason = NULL;
|
expr->reason = NULL;
|
||||||
expr->isKnown = true;
|
expr->isKnown = true;
|
||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
expr->rpn = NULL;
|
expr->tRPN = NULL;
|
||||||
expr->rpnCapacity = 0;
|
expr->nRPNCapacity = 0;
|
||||||
expr->rpnLength = 0;
|
expr->nRPNLength = 0;
|
||||||
expr->rpnPatchSize = 0;
|
expr->nRPNPatchSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free the RPN expression
|
/*
|
||||||
|
* Free the RPN expression
|
||||||
|
*/
|
||||||
void rpn_Free(struct Expression *expr)
|
void rpn_Free(struct Expression *expr)
|
||||||
{
|
{
|
||||||
free(expr->rpn);
|
free(expr->tRPN);
|
||||||
free(expr->reason);
|
free(expr->reason);
|
||||||
rpn_Init(expr);
|
rpn_Init(expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add symbols, constants and operators to expression
|
/*
|
||||||
|
* Add symbols, constants and operators to expression
|
||||||
|
*/
|
||||||
void rpn_Number(struct Expression *expr, uint32_t i)
|
void rpn_Number(struct Expression *expr, uint32_t i)
|
||||||
{
|
{
|
||||||
rpn_Init(expr);
|
rpn_Init(expr);
|
||||||
expr->val = i;
|
expr->nVal = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rpn_Symbol(struct Expression *expr, char const *symName)
|
void rpn_Symbol(struct Expression *expr, char const *tzSym)
|
||||||
{
|
{
|
||||||
struct Symbol *sym = sym_FindScopedSymbol(symName);
|
struct Symbol *sym = sym_FindScopedSymbol(tzSym);
|
||||||
|
|
||||||
if (sym_IsPC(sym) && !sect_GetSymbolSection()) {
|
if (sym_IsPC(sym) && !sect_GetSymbolSection()) {
|
||||||
error("PC has no value outside a section\n");
|
error("PC has no value outside a section\n");
|
||||||
@@ -112,16 +119,16 @@ void rpn_Symbol(struct Expression *expr, char const *symName)
|
|||||||
expr->isSymbol = true;
|
expr->isSymbol = true;
|
||||||
|
|
||||||
makeUnknown(expr, sym_IsPC(sym) ? "PC is not constant at assembly time"
|
makeUnknown(expr, sym_IsPC(sym) ? "PC is not constant at assembly time"
|
||||||
: "'%s' is not constant at assembly time", symName);
|
: "'%s' is not constant at assembly time", tzSym);
|
||||||
sym = sym_Ref(symName);
|
sym = sym_Ref(tzSym);
|
||||||
expr->rpnPatchSize += 5; // 1-byte opcode + 4-byte symbol ID
|
expr->nRPNPatchSize += 5; /* 1-byte opcode + 4-byte symbol ID */
|
||||||
|
|
||||||
size_t nameLen = strlen(sym->name) + 1; // Don't forget NUL!
|
size_t nameLen = strlen(sym->name) + 1; /* Don't forget NUL! */
|
||||||
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
||||||
*ptr++ = RPN_SYM;
|
*ptr++ = RPN_SYM;
|
||||||
memcpy(ptr, sym->name, nameLen);
|
memcpy(ptr, sym->name, nameLen);
|
||||||
} else {
|
} else {
|
||||||
rpn_Number(expr, sym_GetConstantValue(symName));
|
rpn_Number(expr, sym_GetConstantValue(tzSym));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,23 +136,23 @@ void rpn_BankSelf(struct Expression *expr)
|
|||||||
{
|
{
|
||||||
rpn_Init(expr);
|
rpn_Init(expr);
|
||||||
|
|
||||||
if (!currentSection) {
|
if (!pCurrentSection) {
|
||||||
error("PC has no bank outside a section\n");
|
error("PC has no bank outside a section\n");
|
||||||
expr->val = 1;
|
expr->nVal = 1;
|
||||||
} else if (currentSection->bank == (uint32_t)-1) {
|
} else if (pCurrentSection->bank == (uint32_t)-1) {
|
||||||
makeUnknown(expr, "Current section's bank is not known");
|
makeUnknown(expr, "Current section's bank is not known");
|
||||||
expr->rpnPatchSize++;
|
expr->nRPNPatchSize++;
|
||||||
*reserveSpace(expr, 1) = RPN_BANK_SELF;
|
*reserveSpace(expr, 1) = RPN_BANK_SELF;
|
||||||
} else {
|
} else {
|
||||||
expr->val = currentSection->bank;
|
expr->nVal = pCurrentSection->bank;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void rpn_BankSymbol(struct Expression *expr, char const *symName)
|
void rpn_BankSymbol(struct Expression *expr, char const *tzSym)
|
||||||
{
|
{
|
||||||
struct Symbol const *sym = sym_FindScopedSymbol(symName);
|
struct Symbol const *sym = sym_FindScopedSymbol(tzSym);
|
||||||
|
|
||||||
// The @ symbol is treated differently.
|
/* The @ symbol is treated differently. */
|
||||||
if (sym_IsPC(sym)) {
|
if (sym_IsPC(sym)) {
|
||||||
rpn_BankSelf(expr);
|
rpn_BankSelf(expr);
|
||||||
return;
|
return;
|
||||||
@@ -155,17 +162,17 @@ void rpn_BankSymbol(struct Expression *expr, char const *symName)
|
|||||||
if (sym && !sym_IsLabel(sym)) {
|
if (sym && !sym_IsLabel(sym)) {
|
||||||
error("BANK argument must be a label\n");
|
error("BANK argument must be a label\n");
|
||||||
} else {
|
} else {
|
||||||
sym = sym_Ref(symName);
|
sym = sym_Ref(tzSym);
|
||||||
assert(sym); // If the symbol didn't exist, it should have been created
|
assert(sym); // If the symbol didn't exist, it should have been created
|
||||||
|
|
||||||
if (sym_GetSection(sym) && sym_GetSection(sym)->bank != (uint32_t)-1) {
|
if (sym_GetSection(sym) && sym_GetSection(sym)->bank != (uint32_t)-1) {
|
||||||
// Symbol's section is known and bank is fixed
|
/* Symbol's section is known and bank is fixed */
|
||||||
expr->val = sym_GetSection(sym)->bank;
|
expr->nVal = sym_GetSection(sym)->bank;
|
||||||
} else {
|
} else {
|
||||||
makeUnknown(expr, "\"%s\"'s bank is not known", symName);
|
makeUnknown(expr, "\"%s\"'s bank is not known", tzSym);
|
||||||
expr->rpnPatchSize += 5; // opcode + 4-byte sect ID
|
expr->nRPNPatchSize += 5; /* opcode + 4-byte sect ID */
|
||||||
|
|
||||||
size_t nameLen = strlen(sym->name) + 1; // Room for NUL!
|
size_t nameLen = strlen(sym->name) + 1; /* Room for NUL! */
|
||||||
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
||||||
*ptr++ = RPN_BANK_SYM;
|
*ptr++ = RPN_BANK_SYM;
|
||||||
memcpy(ptr, sym->name, nameLen);
|
memcpy(ptr, sym->name, nameLen);
|
||||||
@@ -173,63 +180,24 @@ void rpn_BankSymbol(struct Expression *expr, char const *symName)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void rpn_BankSection(struct Expression *expr, char const *sectionName)
|
void rpn_BankSection(struct Expression *expr, char const *tzSectionName)
|
||||||
{
|
{
|
||||||
rpn_Init(expr);
|
rpn_Init(expr);
|
||||||
|
|
||||||
struct Section *section = sect_FindSectionByName(sectionName);
|
struct Section *pSection = out_FindSectionByName(tzSectionName);
|
||||||
|
|
||||||
if (section && section->bank != (uint32_t)-1) {
|
if (pSection && pSection->bank != (uint32_t)-1) {
|
||||||
expr->val = section->bank;
|
expr->nVal = pSection->bank;
|
||||||
} else {
|
} else {
|
||||||
makeUnknown(expr, "Section \"%s\"'s bank is not known", sectionName);
|
makeUnknown(expr, "Section \"%s\"'s bank is not known",
|
||||||
|
tzSectionName);
|
||||||
|
|
||||||
size_t nameLen = strlen(sectionName) + 1; // Room for NUL!
|
size_t nameLen = strlen(tzSectionName) + 1; /* Room for NUL! */
|
||||||
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
||||||
|
|
||||||
expr->rpnPatchSize += nameLen + 1;
|
expr->nRPNPatchSize += nameLen + 1;
|
||||||
*ptr++ = RPN_BANK_SECT;
|
*ptr++ = RPN_BANK_SECT;
|
||||||
memcpy(ptr, sectionName, nameLen);
|
memcpy(ptr, tzSectionName, nameLen);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void rpn_SizeOfSection(struct Expression *expr, char const *sectionName)
|
|
||||||
{
|
|
||||||
rpn_Init(expr);
|
|
||||||
|
|
||||||
struct Section *section = sect_FindSectionByName(sectionName);
|
|
||||||
|
|
||||||
if (section && sect_IsSizeKnown(section)) {
|
|
||||||
expr->val = section->size;
|
|
||||||
} else {
|
|
||||||
makeUnknown(expr, "Section \"%s\"'s size is not known", sectionName);
|
|
||||||
|
|
||||||
size_t nameLen = strlen(sectionName) + 1; // Room for NUL!
|
|
||||||
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
|
||||||
|
|
||||||
expr->rpnPatchSize += nameLen + 1;
|
|
||||||
*ptr++ = RPN_SIZEOF_SECT;
|
|
||||||
memcpy(ptr, sectionName, nameLen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void rpn_StartOfSection(struct Expression *expr, char const *sectionName)
|
|
||||||
{
|
|
||||||
rpn_Init(expr);
|
|
||||||
|
|
||||||
struct Section *section = sect_FindSectionByName(sectionName);
|
|
||||||
|
|
||||||
if (section && section->org != (uint32_t)-1) {
|
|
||||||
expr->val = section->org;
|
|
||||||
} else {
|
|
||||||
makeUnknown(expr, "Section \"%s\"'s start is not known", sectionName);
|
|
||||||
|
|
||||||
size_t nameLen = strlen(sectionName) + 1; // Room for NUL!
|
|
||||||
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
|
||||||
|
|
||||||
expr->rpnPatchSize += nameLen + 1;
|
|
||||||
*ptr++ = RPN_STARTOF_SECT;
|
|
||||||
memcpy(ptr, sectionName, nameLen);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,13 +207,13 @@ void rpn_CheckHRAM(struct Expression *expr, const struct Expression *src)
|
|||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
|
|
||||||
if (!rpn_isKnown(expr)) {
|
if (!rpn_isKnown(expr)) {
|
||||||
expr->rpnPatchSize++;
|
expr->nRPNPatchSize++;
|
||||||
*reserveSpace(expr, 1) = RPN_HRAM;
|
*reserveSpace(expr, 1) = RPN_HRAM;
|
||||||
} else if (expr->val >= 0xFF00 && expr->val <= 0xFFFF) {
|
} else if (expr->nVal >= 0xFF00 && expr->nVal <= 0xFFFF) {
|
||||||
// That range is valid, but only keep the lower byte
|
/* That range is valid, but only keep the lower byte */
|
||||||
expr->val &= 0xFF;
|
expr->nVal &= 0xFF;
|
||||||
} else if (expr->val < 0 || expr->val > 0xFF) {
|
} else if (expr->nVal < 0 || expr->nVal > 0xFF) {
|
||||||
error("Source address $%" PRIx32 " not between $FF00 to $FFFF\n", expr->val);
|
error("Source address $%" PRIx32 " not between $FF00 to $FFFF\n", expr->nVal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,51 +222,26 @@ void rpn_CheckRST(struct Expression *expr, const struct Expression *src)
|
|||||||
*expr = *src;
|
*expr = *src;
|
||||||
|
|
||||||
if (rpn_isKnown(expr)) {
|
if (rpn_isKnown(expr)) {
|
||||||
// A valid RST address must be masked with 0x38
|
/* A valid RST address must be masked with 0x38 */
|
||||||
if (expr->val & ~0x38)
|
if (expr->nVal & ~0x38)
|
||||||
error("Invalid address $%" PRIx32 " for RST\n", expr->val);
|
error("Invalid address $%" PRIx32 " for RST\n", expr->nVal);
|
||||||
// The target is in the "0x38" bits, all other bits are set
|
/* The target is in the "0x38" bits, all other bits are set */
|
||||||
expr->val |= 0xC7;
|
expr->nVal |= 0xC7;
|
||||||
} else {
|
} else {
|
||||||
expr->rpnPatchSize++;
|
expr->nRPNPatchSize++;
|
||||||
*reserveSpace(expr, 1) = RPN_RST;
|
*reserveSpace(expr, 1) = RPN_RST;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks that an RPN expression's value fits within N bits (signed or unsigned)
|
|
||||||
void rpn_CheckNBit(struct Expression const *expr, uint8_t n)
|
|
||||||
{
|
|
||||||
assert(n != 0); // That doesn't make sense
|
|
||||||
assert(n < CHAR_BIT * sizeof(int)); // Otherwise `1 << n` is UB
|
|
||||||
|
|
||||||
if (rpn_isKnown(expr)) {
|
|
||||||
int32_t val = expr->val;
|
|
||||||
|
|
||||||
if (val <= -(1 << n) || val >= 1 << n)
|
|
||||||
warning(WARNING_TRUNCATION_1, "Expression must be %u-bit\n", n);
|
|
||||||
else if (val < -(1 << (n - 1)))
|
|
||||||
warning(WARNING_TRUNCATION_2, "Expression must be %u-bit\n", n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t rpn_GetConstVal(struct Expression const *expr)
|
|
||||||
{
|
|
||||||
if (!rpn_isKnown(expr)) {
|
|
||||||
error("Expected constant expression: %s\n", expr->reason);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return expr->val;
|
|
||||||
}
|
|
||||||
|
|
||||||
void rpn_LOGNOT(struct Expression *expr, const struct Expression *src)
|
void rpn_LOGNOT(struct Expression *expr, const struct Expression *src)
|
||||||
{
|
{
|
||||||
*expr = *src;
|
*expr = *src;
|
||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
|
|
||||||
if (rpn_isKnown(expr)) {
|
if (rpn_isKnown(expr)) {
|
||||||
expr->val = !expr->val;
|
expr->nVal = !expr->nVal;
|
||||||
} else {
|
} else {
|
||||||
expr->rpnPatchSize++;
|
expr->nRPNPatchSize++;
|
||||||
*reserveSpace(expr, 1) = RPN_LOGUNNOT;
|
*reserveSpace(expr, 1) = RPN_LOGUNNOT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -307,12 +250,12 @@ struct Symbol const *rpn_SymbolOf(struct Expression const *expr)
|
|||||||
{
|
{
|
||||||
if (!rpn_isSymbol(expr))
|
if (!rpn_isSymbol(expr))
|
||||||
return NULL;
|
return NULL;
|
||||||
return sym_FindScopedSymbol((char const *)expr->rpn + 1);
|
return sym_FindScopedSymbol((char *)expr->tRPN + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *sym)
|
bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *sym)
|
||||||
{
|
{
|
||||||
// Check if both expressions only refer to a single symbol
|
/* Check if both expressions only refer to a single symbol */
|
||||||
struct Symbol const *sym1 = rpn_SymbolOf(src);
|
struct Symbol const *sym1 = rpn_SymbolOf(src);
|
||||||
|
|
||||||
if (!sym1 || !sym || sym1->type != SYM_LABEL || sym->type != SYM_LABEL)
|
if (!sym1 || !sym || sym1->type != SYM_LABEL || sym->type != SYM_LABEL)
|
||||||
@@ -329,175 +272,122 @@ static bool isDiffConstant(struct Expression const *src1,
|
|||||||
return rpn_IsDiffConstant(src1, rpn_SymbolOf(src2));
|
return rpn_IsDiffConstant(src1, rpn_SymbolOf(src2));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Attempts to compute a constant binary AND from non-constant operands
|
|
||||||
* This is possible if one operand is a symbol belonging to an `ALIGN[N]` section, and the other is
|
|
||||||
* a constant that only keeps (some of) the lower N bits.
|
|
||||||
*
|
|
||||||
* @return The constant result if it can be computed, or -1 otherwise.
|
|
||||||
*/
|
|
||||||
static int32_t tryConstMask(struct Expression const *lhs, struct Expression const *rhs)
|
|
||||||
{
|
|
||||||
struct Symbol const *sym = rpn_SymbolOf(lhs);
|
|
||||||
struct Expression const *expr = rhs;
|
|
||||||
|
|
||||||
if (!sym || !sym_GetSection(sym)) {
|
|
||||||
// If the lhs isn't a symbol, try again the other way around
|
|
||||||
sym = rpn_SymbolOf(rhs);
|
|
||||||
expr = lhs;
|
|
||||||
|
|
||||||
if (!sym || !sym_GetSection(sym))
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
assert(sym_IsNumeric(sym));
|
|
||||||
|
|
||||||
if (!rpn_isKnown(expr))
|
|
||||||
return -1;
|
|
||||||
// We can now safely use `expr->val`
|
|
||||||
struct Section const *sect = sym_GetSection(sym);
|
|
||||||
int32_t unknownBits = (1 << 16) - (1 << sect->align); // The max alignment is 16
|
|
||||||
|
|
||||||
// The mask must ignore all unknown bits
|
|
||||||
if ((expr->val & unknownBits) != 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
// `sym_GetValue()` attempts to add the section's address,
|
|
||||||
// but that's "-1" because the section is floating (otherwise we wouldn't be here)
|
|
||||||
assert(sect->org == (uint32_t)-1);
|
|
||||||
int32_t symbolOfs = sym_GetValue(sym) + 1;
|
|
||||||
|
|
||||||
return (symbolOfs + sect->alignOfs) & ~unknownBits;
|
|
||||||
}
|
|
||||||
|
|
||||||
void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
||||||
const struct Expression *src1, const struct Expression *src2)
|
const struct Expression *src1, const struct Expression *src2)
|
||||||
{
|
{
|
||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
int32_t constMaskVal;
|
|
||||||
|
|
||||||
// First, check if the expression is known
|
/* First, check if the expression is known */
|
||||||
expr->isKnown = src1->isKnown && src2->isKnown;
|
expr->isKnown = src1->isKnown && src2->isKnown;
|
||||||
if (expr->isKnown) {
|
if (expr->isKnown) {
|
||||||
rpn_Init(expr); // Init the expression to something sane
|
rpn_Init(expr); /* Init the expression to something sane */
|
||||||
|
|
||||||
// If both expressions are known, just compute the value
|
/* If both expressions are known, just compute the value */
|
||||||
uint32_t uleft = src1->val, uright = src2->val;
|
uint32_t uleft = src1->nVal, uright = src2->nVal;
|
||||||
|
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case RPN_LOGOR:
|
case RPN_LOGOR:
|
||||||
expr->val = src1->val || src2->val;
|
expr->nVal = src1->nVal || src2->nVal;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGAND:
|
case RPN_LOGAND:
|
||||||
expr->val = src1->val && src2->val;
|
expr->nVal = src1->nVal && src2->nVal;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGEQ:
|
case RPN_LOGEQ:
|
||||||
expr->val = src1->val == src2->val;
|
expr->nVal = src1->nVal == src2->nVal;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGGT:
|
case RPN_LOGGT:
|
||||||
expr->val = src1->val > src2->val;
|
expr->nVal = src1->nVal > src2->nVal;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGLT:
|
case RPN_LOGLT:
|
||||||
expr->val = src1->val < src2->val;
|
expr->nVal = src1->nVal < src2->nVal;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGGE:
|
case RPN_LOGGE:
|
||||||
expr->val = src1->val >= src2->val;
|
expr->nVal = src1->nVal >= src2->nVal;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGLE:
|
case RPN_LOGLE:
|
||||||
expr->val = src1->val <= src2->val;
|
expr->nVal = src1->nVal <= src2->nVal;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGNE:
|
case RPN_LOGNE:
|
||||||
expr->val = src1->val != src2->val;
|
expr->nVal = src1->nVal != src2->nVal;
|
||||||
break;
|
break;
|
||||||
case RPN_ADD:
|
case RPN_ADD:
|
||||||
expr->val = uleft + uright;
|
expr->nVal = uleft + uright;
|
||||||
break;
|
break;
|
||||||
case RPN_SUB:
|
case RPN_SUB:
|
||||||
expr->val = uleft - uright;
|
expr->nVal = uleft - uright;
|
||||||
break;
|
break;
|
||||||
case RPN_XOR:
|
case RPN_XOR:
|
||||||
expr->val = src1->val ^ src2->val;
|
expr->nVal = src1->nVal ^ src2->nVal;
|
||||||
break;
|
break;
|
||||||
case RPN_OR:
|
case RPN_OR:
|
||||||
expr->val = src1->val | src2->val;
|
expr->nVal = src1->nVal | src2->nVal;
|
||||||
break;
|
break;
|
||||||
case RPN_AND:
|
case RPN_AND:
|
||||||
expr->val = src1->val & src2->val;
|
expr->nVal = src1->nVal & src2->nVal;
|
||||||
break;
|
break;
|
||||||
case RPN_SHL:
|
case RPN_SHL:
|
||||||
if (src2->val < 0)
|
if (src2->nVal < 0)
|
||||||
warning(WARNING_SHIFT_AMOUNT,
|
warning(WARNING_SHIFT_AMOUNT,
|
||||||
"Shifting left by negative amount %" PRId32 "\n",
|
"Shifting left by negative amount %" PRId32 "\n",
|
||||||
src2->val);
|
src2->nVal);
|
||||||
|
|
||||||
if (src2->val >= 32)
|
if (src2->nVal >= 32)
|
||||||
warning(WARNING_SHIFT_AMOUNT,
|
warning(WARNING_SHIFT_AMOUNT,
|
||||||
"Shifting left by large amount %" PRId32 "\n", src2->val);
|
"Shifting left by large amount %" PRId32 "\n",
|
||||||
|
src2->nVal);
|
||||||
|
|
||||||
expr->val = op_shift_left(src1->val, src2->val);
|
expr->nVal = op_shift_left(src1->nVal, src2->nVal);
|
||||||
break;
|
break;
|
||||||
case RPN_SHR:
|
case RPN_SHR:
|
||||||
if (src1->val < 0)
|
if (src1->nVal < 0)
|
||||||
warning(WARNING_SHIFT,
|
warning(WARNING_SHIFT, "Shifting right negative value %"
|
||||||
"Shifting right negative value %" PRId32 "\n", src1->val);
|
PRId32 "\n",
|
||||||
|
src1->nVal);
|
||||||
|
|
||||||
if (src2->val < 0)
|
if (src2->nVal < 0)
|
||||||
warning(WARNING_SHIFT_AMOUNT,
|
warning(WARNING_SHIFT_AMOUNT,
|
||||||
"Shifting right by negative amount %" PRId32 "\n",
|
"Shifting right by negative amount %" PRId32 "\n",
|
||||||
src2->val);
|
src2->nVal);
|
||||||
|
|
||||||
if (src2->val >= 32)
|
if (src2->nVal >= 32)
|
||||||
warning(WARNING_SHIFT_AMOUNT,
|
warning(WARNING_SHIFT_AMOUNT,
|
||||||
"Shifting right by large amount %" PRId32 "\n",
|
"Shifting right by large amount %" PRId32 "\n",
|
||||||
src2->val);
|
src2->nVal);
|
||||||
|
|
||||||
expr->val = op_shift_right(src1->val, src2->val);
|
expr->nVal = op_shift_right(src1->nVal, src2->nVal);
|
||||||
break;
|
|
||||||
case RPN_USHR:
|
|
||||||
if (src2->val < 0)
|
|
||||||
warning(WARNING_SHIFT_AMOUNT,
|
|
||||||
"Shifting right by negative amount %" PRId32 "\n",
|
|
||||||
src2->val);
|
|
||||||
|
|
||||||
if (src2->val >= 32)
|
|
||||||
warning(WARNING_SHIFT_AMOUNT,
|
|
||||||
"Shifting right by large amount %" PRId32 "\n",
|
|
||||||
src2->val);
|
|
||||||
|
|
||||||
expr->val = op_shift_right_unsigned(src1->val, src2->val);
|
|
||||||
break;
|
break;
|
||||||
case RPN_MUL:
|
case RPN_MUL:
|
||||||
expr->val = uleft * uright;
|
expr->nVal = uleft * uright;
|
||||||
break;
|
break;
|
||||||
case RPN_DIV:
|
case RPN_DIV:
|
||||||
if (src2->val == 0)
|
if (src2->nVal == 0)
|
||||||
fatalerror("Division by zero\n");
|
fatalerror("Division by zero\n");
|
||||||
|
|
||||||
if (src1->val == INT32_MIN && src2->val == -1) {
|
if (src1->nVal == INT32_MIN && src2->nVal == -1) {
|
||||||
warning(WARNING_DIV,
|
warning(WARNING_DIV, "Division of %" PRId32 " by -1 yields %"
|
||||||
"Division of %" PRId32 " by -1 yields %" PRId32 "\n",
|
PRId32 "\n", INT32_MIN, INT32_MIN);
|
||||||
INT32_MIN, INT32_MIN);
|
expr->nVal = INT32_MIN;
|
||||||
expr->val = INT32_MIN;
|
|
||||||
} else {
|
} else {
|
||||||
expr->val = op_divide(src1->val, src2->val);
|
expr->nVal = op_divide(src1->nVal, src2->nVal);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RPN_MOD:
|
case RPN_MOD:
|
||||||
if (src2->val == 0)
|
if (src2->nVal == 0)
|
||||||
fatalerror("Modulo by zero\n");
|
fatalerror("Modulo by zero\n");
|
||||||
|
|
||||||
if (src1->val == INT32_MIN && src2->val == -1)
|
if (src1->nVal == INT32_MIN && src2->nVal == -1)
|
||||||
expr->val = 0;
|
expr->nVal = 0;
|
||||||
else
|
else
|
||||||
expr->val = op_modulo(src1->val, src2->val);
|
expr->nVal = op_modulo(src1->nVal, src2->nVal);
|
||||||
break;
|
break;
|
||||||
case RPN_EXP:
|
case RPN_EXP:
|
||||||
if (src2->val < 0)
|
if (src2->nVal < 0)
|
||||||
fatalerror("Exponentiation by negative power\n");
|
fatalerror("Exponentiation by negative power\n");
|
||||||
|
|
||||||
if (src1->val == INT32_MIN && src2->val == -1)
|
if (src1->nVal == INT32_MIN && src2->nVal == -1)
|
||||||
expr->val = 0;
|
expr->nVal = 0;
|
||||||
else
|
else
|
||||||
expr->val = op_exponent(src1->val, src2->val);
|
expr->nVal = op_exponent(src1->nVal, src2->nVal);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_UNSUB:
|
case RPN_UNSUB:
|
||||||
@@ -506,8 +396,6 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
|||||||
case RPN_BANK_SYM:
|
case RPN_BANK_SYM:
|
||||||
case RPN_BANK_SECT:
|
case RPN_BANK_SECT:
|
||||||
case RPN_BANK_SELF:
|
case RPN_BANK_SELF:
|
||||||
case RPN_SIZEOF_SECT:
|
|
||||||
case RPN_STARTOF_SECT:
|
|
||||||
case RPN_HRAM:
|
case RPN_HRAM:
|
||||||
case RPN_RST:
|
case RPN_RST:
|
||||||
case RPN_CONST:
|
case RPN_CONST:
|
||||||
@@ -519,46 +407,43 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
|||||||
struct Symbol const *symbol1 = rpn_SymbolOf(src1);
|
struct Symbol const *symbol1 = rpn_SymbolOf(src1);
|
||||||
struct Symbol const *symbol2 = rpn_SymbolOf(src2);
|
struct Symbol const *symbol2 = rpn_SymbolOf(src2);
|
||||||
|
|
||||||
expr->val = sym_GetValue(symbol1) - sym_GetValue(symbol2);
|
expr->nVal = sym_GetValue(symbol1) - sym_GetValue(symbol2);
|
||||||
expr->isKnown = true;
|
|
||||||
} else if (op == RPN_AND && (constMaskVal = tryConstMask(src1, src2)) != -1) {
|
|
||||||
expr->val = constMaskVal;
|
|
||||||
expr->isKnown = true;
|
expr->isKnown = true;
|
||||||
} else {
|
} else {
|
||||||
// If it's not known, start computing the RPN expression
|
/* If it's not known, start computing the RPN expression */
|
||||||
|
|
||||||
// Convert the left-hand expression if it's constant
|
/* Convert the left-hand expression if it's constant */
|
||||||
if (src1->isKnown) {
|
if (src1->isKnown) {
|
||||||
uint32_t lval = src1->val;
|
uint32_t lval = src1->nVal;
|
||||||
uint8_t bytes[] = {RPN_CONST, lval, lval >> 8,
|
uint8_t bytes[] = {RPN_CONST, lval, lval >> 8,
|
||||||
lval >> 16, lval >> 24};
|
lval >> 16, lval >> 24};
|
||||||
expr->rpnPatchSize = sizeof(bytes);
|
expr->nRPNPatchSize = sizeof(bytes);
|
||||||
expr->rpn = NULL;
|
expr->tRPN = NULL;
|
||||||
expr->rpnCapacity = 0;
|
expr->nRPNCapacity = 0;
|
||||||
expr->rpnLength = 0;
|
expr->nRPNLength = 0;
|
||||||
memcpy(reserveSpace(expr, sizeof(bytes)), bytes,
|
memcpy(reserveSpace(expr, sizeof(bytes)), bytes,
|
||||||
sizeof(bytes));
|
sizeof(bytes));
|
||||||
|
|
||||||
// Use the other expression's un-const reason
|
/* Use the other expression's un-const reason */
|
||||||
expr->reason = src2->reason;
|
expr->reason = src2->reason;
|
||||||
free(src1->reason);
|
free(src1->reason);
|
||||||
} else {
|
} else {
|
||||||
// Otherwise just reuse its RPN buffer
|
/* Otherwise just reuse its RPN buffer */
|
||||||
expr->rpnPatchSize = src1->rpnPatchSize;
|
expr->nRPNPatchSize = src1->nRPNPatchSize;
|
||||||
expr->rpn = src1->rpn;
|
expr->tRPN = src1->tRPN;
|
||||||
expr->rpnCapacity = src1->rpnCapacity;
|
expr->nRPNCapacity = src1->nRPNCapacity;
|
||||||
expr->rpnLength = src1->rpnLength;
|
expr->nRPNLength = src1->nRPNLength;
|
||||||
expr->reason = src1->reason;
|
expr->reason = src1->reason;
|
||||||
free(src2->reason);
|
free(src2->reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now, merge the right expression into the left one
|
/* Now, merge the right expression into the left one */
|
||||||
uint8_t *ptr = src2->rpn; // Pointer to the right RPN
|
uint8_t *ptr = src2->tRPN; /* Pointer to the right RPN */
|
||||||
uint32_t len = src2->rpnLength; // Size of the right RPN
|
uint32_t len = src2->nRPNLength; /* Size of the right RPN */
|
||||||
uint32_t patchSize = src2->rpnPatchSize;
|
uint32_t patchSize = src2->nRPNPatchSize;
|
||||||
|
|
||||||
// If the right expression is constant, merge a shim instead
|
/* If the right expression is constant, merge a shim instead */
|
||||||
uint32_t rval = src2->val;
|
uint32_t rval = src2->nVal;
|
||||||
uint8_t bytes[] = {RPN_CONST, rval, rval >> 8, rval >> 16,
|
uint8_t bytes[] = {RPN_CONST, rval, rval >> 8, rval >> 16,
|
||||||
rval >> 24};
|
rval >> 24};
|
||||||
if (src2->isKnown) {
|
if (src2->isKnown) {
|
||||||
@@ -566,14 +451,14 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
|||||||
len = sizeof(bytes);
|
len = sizeof(bytes);
|
||||||
patchSize = sizeof(bytes);
|
patchSize = sizeof(bytes);
|
||||||
}
|
}
|
||||||
// Copy the right RPN and append the operator
|
/* Copy the right RPN and append the operator */
|
||||||
uint8_t *buf = reserveSpace(expr, len + 1);
|
uint8_t *buf = reserveSpace(expr, len + 1);
|
||||||
|
|
||||||
memcpy(buf, ptr, len);
|
memcpy(buf, ptr, len);
|
||||||
buf[len] = op;
|
buf[len] = op;
|
||||||
|
|
||||||
free(src2->rpn); // If there was none, this is `free(NULL)`
|
free(src2->tRPN); /* If there was none, this is `free(NULL)` */
|
||||||
expr->rpnPatchSize += patchSize + 1;
|
expr->nRPNPatchSize += patchSize + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -583,11 +468,11 @@ void rpn_HIGH(struct Expression *expr, const struct Expression *src)
|
|||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
|
|
||||||
if (rpn_isKnown(expr)) {
|
if (rpn_isKnown(expr)) {
|
||||||
expr->val = (uint32_t)expr->val >> 8 & 0xFF;
|
expr->nVal = (uint32_t)expr->nVal >> 8 & 0xFF;
|
||||||
} else {
|
} else {
|
||||||
uint8_t bytes[] = {RPN_CONST, 8, 0, 0, 0, RPN_SHR,
|
uint8_t bytes[] = {RPN_CONST, 8, 0, 0, 0, RPN_SHR,
|
||||||
RPN_CONST, 0xFF, 0, 0, 0, RPN_AND};
|
RPN_CONST, 0xFF, 0, 0, 0, RPN_AND};
|
||||||
expr->rpnPatchSize += sizeof(bytes);
|
expr->nRPNPatchSize += sizeof(bytes);
|
||||||
memcpy(reserveSpace(expr, sizeof(bytes)), bytes, sizeof(bytes));
|
memcpy(reserveSpace(expr, sizeof(bytes)), bytes, sizeof(bytes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -598,11 +483,11 @@ void rpn_LOW(struct Expression *expr, const struct Expression *src)
|
|||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
|
|
||||||
if (rpn_isKnown(expr)) {
|
if (rpn_isKnown(expr)) {
|
||||||
expr->val = expr->val & 0xFF;
|
expr->nVal = expr->nVal & 0xFF;
|
||||||
} else {
|
} else {
|
||||||
uint8_t bytes[] = {RPN_CONST, 0xFF, 0, 0, 0, RPN_AND};
|
uint8_t bytes[] = {RPN_CONST, 0xFF, 0, 0, 0, RPN_AND};
|
||||||
|
|
||||||
expr->rpnPatchSize += sizeof(bytes);
|
expr->nRPNPatchSize += sizeof(bytes);
|
||||||
memcpy(reserveSpace(expr, sizeof(bytes)), bytes, sizeof(bytes));
|
memcpy(reserveSpace(expr, sizeof(bytes)), bytes, sizeof(bytes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -610,7 +495,7 @@ void rpn_LOW(struct Expression *expr, const struct Expression *src)
|
|||||||
void rpn_ISCONST(struct Expression *expr, const struct Expression *src)
|
void rpn_ISCONST(struct Expression *expr, const struct Expression *src)
|
||||||
{
|
{
|
||||||
rpn_Init(expr);
|
rpn_Init(expr);
|
||||||
expr->val = rpn_isKnown(src);
|
expr->nVal = rpn_isKnown(src);
|
||||||
expr->isKnown = true;
|
expr->isKnown = true;
|
||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
}
|
}
|
||||||
@@ -621,9 +506,9 @@ void rpn_UNNEG(struct Expression *expr, const struct Expression *src)
|
|||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
|
|
||||||
if (rpn_isKnown(expr)) {
|
if (rpn_isKnown(expr)) {
|
||||||
expr->val = -(uint32_t)expr->val;
|
expr->nVal = -(uint32_t)expr->nVal;
|
||||||
} else {
|
} else {
|
||||||
expr->rpnPatchSize++;
|
expr->nRPNPatchSize++;
|
||||||
*reserveSpace(expr, 1) = RPN_UNSUB;
|
*reserveSpace(expr, 1) = RPN_UNSUB;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -634,9 +519,9 @@ void rpn_UNNOT(struct Expression *expr, const struct Expression *src)
|
|||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
|
|
||||||
if (rpn_isKnown(expr)) {
|
if (rpn_isKnown(expr)) {
|
||||||
expr->val = ~expr->val;
|
expr->nVal = ~expr->nVal;
|
||||||
} else {
|
} else {
|
||||||
expr->rpnPatchSize++;
|
expr->nRPNPatchSize++;
|
||||||
*reserveSpace(expr, 1) = RPN_UNNOT;
|
*reserveSpace(expr, 1) = RPN_UNNOT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user