mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-23 19:42:08 +00:00
Compare commits
110 Commits
v0.5.1
...
v0.6.0-wel
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bffe7eb4de | ||
|
|
cd454d2e9a | ||
|
|
c814a616d6 | ||
|
|
e6ae1992fe | ||
|
|
d9b46cdec9 | ||
|
|
570cd62b81 | ||
|
|
a4ead0c25f | ||
|
|
da66eeb40e | ||
|
|
0c4f1f8334 | ||
|
|
af70d555fc | ||
|
|
e07bd92314 | ||
|
|
9169028e57 | ||
|
|
7dd8ba37f1 | ||
|
|
6842c831fd | ||
|
|
6b903059fe | ||
|
|
eb5af70d79 | ||
|
|
cf19879281 | ||
|
|
ac59ecf3c0 | ||
|
|
72b677a8d7 | ||
|
|
bbae9966e9 | ||
|
|
b3304ae1ac | ||
|
|
a48801a675 | ||
|
|
7dc81a64d3 | ||
|
|
3afa6b5a5a | ||
|
|
9b49f788e4 | ||
|
|
acc31feaa1 | ||
|
|
4ed5ba7508 | ||
|
|
20a26599a3 | ||
|
|
7bdfc9da23 | ||
|
|
d073cffa74 | ||
|
|
8435a29c4e | ||
|
|
b7fe78cad8 | ||
|
|
9d993d84e8 | ||
|
|
fef168b2a5 | ||
|
|
4a4b22c78c | ||
|
|
db79689e81 | ||
|
|
c2ca46c27d | ||
|
|
aac839f389 | ||
|
|
9d9febe1d3 | ||
|
|
b9fd85470e | ||
|
|
ec6d63bce3 | ||
|
|
bdcef6f252 | ||
|
|
54293a9184 | ||
|
|
b04e71ed34 | ||
|
|
f82603f196 | ||
|
|
cedfd2582a | ||
|
|
c7322258fc | ||
|
|
036b6c1b89 | ||
|
|
8e2a164a32 | ||
|
|
b76819792d | ||
|
|
3e945679ad | ||
|
|
efccf6c931 | ||
|
|
e5552e27f2 | ||
|
|
438963fb24 | ||
|
|
0bb815edc0 | ||
|
|
55a02981b5 | ||
|
|
b06e3b239d | ||
|
|
47442941b6 | ||
|
|
b16d2d0695 | ||
|
|
8b1cc72f09 | ||
|
|
11a6a81169 | ||
|
|
0ce66009c1 | ||
|
|
33f2f555a6 | ||
|
|
0487f9f841 | ||
|
|
0d6bfb84ce | ||
|
|
1e4ace8974 | ||
|
|
a378d1e8cb | ||
|
|
81ea39effe | ||
|
|
1a07391a97 | ||
|
|
b002d95459 | ||
|
|
646fc62b89 | ||
|
|
699c00dc20 | ||
|
|
9b9f3ffb96 | ||
|
|
00a67c3fb2 | ||
|
|
50d6403c72 | ||
|
|
9111157b82 | ||
|
|
794dd6cd7e | ||
|
|
ae4352c198 | ||
|
|
4a73eb56ea | ||
|
|
0f321bc797 | ||
|
|
94d07c78d8 | ||
|
|
b51e1c7c2c | ||
|
|
26ddf1ff4d | ||
|
|
20fd6eabbb | ||
|
|
fbe29006d4 | ||
|
|
03bb510588 | ||
|
|
695dfe9dbd | ||
|
|
9782f7d942 | ||
|
|
1b5648bb06 | ||
|
|
a67f5d6e01 | ||
|
|
06b57aa1ce | ||
|
|
6d2db2ef64 | ||
|
|
9868a01163 | ||
|
|
0c8cdd92d6 | ||
|
|
80a376f045 | ||
|
|
0068c1375c | ||
|
|
872af9c7ed | ||
|
|
06ea7b20bf | ||
|
|
a3c4652bfd | ||
|
|
5ad48851ed | ||
|
|
e3b7339dd6 | ||
|
|
69d7f84502 | ||
|
|
e970b6d6eb | ||
|
|
d9cce3fa1f | ||
|
|
23721694ea | ||
|
|
aa02958e18 | ||
|
|
011d4ec392 | ||
|
|
afbaf10185 | ||
|
|
6a5e2f439e | ||
|
|
fba77c4dce |
99
.clang-format
Normal file
99
.clang-format
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
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++20
|
||||||
|
TabWidth: 4
|
||||||
|
UseCRLF: false
|
||||||
|
UseTab: AlignWithSpaces
|
||||||
5
.github/actions/install_deps.sh
vendored
5
.github/actions/install_deps.sh
vendored
@@ -1,4 +1,7 @@
|
|||||||
case `echo $1 | cut -d '-' -f 1` in
|
#!/bin/bash
|
||||||
|
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
|
||||||
|
|||||||
17
.github/workflows/checkdiff.yml
vendored
Normal file
17
.github/workflows/checkdiff.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
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
|
||||||
@@ -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/zlib1211.zip', 'zlib.zip')
|
$wc.DownloadFile('https://www.zlib.net/zlib1212.zip', 'zlib.zip')
|
||||||
$hash = (Get-FileHash "zlib.zip" -Algorithm SHA256).Hash
|
$hash = (Get-FileHash "zlib.zip" -Algorithm SHA256).Hash
|
||||||
if ($hash -ne 'd7510a8ee1918b7d0cad197a089c0a2cd4d6df05fee22389f67f115e738b178d') {
|
if ($hash -ne '173e89893dcb8b4a150d7731cd72f0602f1d6b45e60e2a54efdf7f3fc3325fd7') {
|
||||||
Write-Host "zlib SHA256 mismatch! ($hash)"
|
Write-Host "zlib SHA256 mismatch! ($hash)"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
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.11 zlib
|
Move-Item zlib-1.2.12 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`
|
||||||
|
|||||||
31
.github/workflows/testing.yml
vendored
31
.github/workflows/testing.yml
vendored
@@ -7,18 +7,15 @@ jobs:
|
|||||||
unix-testing:
|
unix-testing:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04, macos-10.15]
|
os: [ubuntu-20.04, ubuntu-18.04, macos-11.0, macos-10.15]
|
||||||
cc: [gcc, clang]
|
cc: [gcc, clang]
|
||||||
buildsys: [make, cmake]
|
buildsys: [make, cmake]
|
||||||
include:
|
exclude:
|
||||||
- os: ubuntu-18.04
|
# `gcc` is just an alias to `clang` on macOS, don't bother
|
||||||
|
- os: macos-10.15
|
||||||
cc: gcc
|
cc: gcc
|
||||||
target: develop
|
- os: macos-11.0
|
||||||
cmakevars: -DSANITIZERS=ON -DMORE_WARNINGS=ON -DCMAKE_BUILD_TYPE=Debug
|
|
||||||
- os: ubuntu-20.04
|
|
||||||
cc: gcc
|
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,14 +30,14 @@ jobs:
|
|||||||
- name: Build & install using Make
|
- name: Build & install using Make
|
||||||
run: |
|
run: |
|
||||||
export PATH="/usr/local/opt/bison/bin:$PATH"
|
export PATH="/usr/local/opt/bison/bin:$PATH"
|
||||||
make ${{ matrix.target }} -j Q= CC=${{ matrix.cc }}
|
make develop -j Q= CC=${{ matrix.cc }}
|
||||||
sudo make install -j Q=
|
sudo make install -j Q=
|
||||||
if: matrix.buildsys == 'make'
|
if: matrix.buildsys == 'make'
|
||||||
- name: Build & install using CMake
|
- name: Build & install using CMake
|
||||||
run: |
|
run: |
|
||||||
export PATH="/usr/local/opt/bison/bin:$PATH"
|
export PATH="/usr/local/opt/bison/bin:$PATH"
|
||||||
cmake -S . -B build -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=${{ matrix.cc }} ${{ matrix.cmakevars }}
|
cmake -S . -B build -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=${{ matrix.cc }} -DSANITIZERS=ON -DMORE_WARNINGS=ON
|
||||||
cmake --build build
|
cmake --build build -j
|
||||||
cp build/src/rgb{asm,link,fix,gfx} .
|
cp build/src/rgb{asm,link,fix,gfx} .
|
||||||
sudo cmake --install build
|
sudo cmake --install build
|
||||||
if: matrix.buildsys == 'cmake'
|
if: matrix.buildsys == 'cmake'
|
||||||
@@ -76,9 +73,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/zlib1211.zip', 'zlib.zip')
|
$wc.DownloadFile('https://www.zlib.net/zlib1212.zip', 'zlib.zip')
|
||||||
$hash = (Get-FileHash "zlib.zip" -Algorithm SHA256).Hash
|
$hash = (Get-FileHash "zlib.zip" -Algorithm SHA256).Hash
|
||||||
if ($hash -ne 'd7510a8ee1918b7d0cad197a089c0a2cd4d6df05fee22389f67f115e738b178d') {
|
if ($hash -ne '173e89893dcb8b4a150d7731cd72f0602f1d6b45e60e2a54efdf7f3fc3325fd7') {
|
||||||
Write-Host "zlib SHA256 mismatch! ($hash)"
|
Write-Host "zlib SHA256 mismatch! ($hash)"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
@@ -96,22 +93,22 @@ jobs:
|
|||||||
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.11 zlib
|
Move-Item zlib-1.2.12 zlib
|
||||||
Move-Item lpng1637 libpng
|
Move-Item lpng1637 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
|
cmake --build zbuild --config Release -j
|
||||||
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
|
cmake --build pngbuild --config Release -j
|
||||||
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
|
cmake --build build --config Release -j
|
||||||
cmake --install build
|
cmake --install build
|
||||||
- name: Package binaries
|
- name: Package binaries
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|||||||
16
.gitignore
vendored
16
.gitignore
vendored
@@ -1,16 +1,12 @@
|
|||||||
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
|
|
||||||
|
|||||||
@@ -25,8 +25,6 @@ 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,
|
||||||
@@ -44,32 +42,43 @@ else()
|
|||||||
-fsanitize=alignment -fsanitize=null -fsanitize=address)
|
-fsanitize=alignment -fsanitize=null -fsanitize=address)
|
||||||
add_compile_options(${SAN_FLAGS})
|
add_compile_options(${SAN_FLAGS})
|
||||||
link_libraries(${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)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(MORE_WARNINGS)
|
if(MORE_WARNINGS)
|
||||||
add_compile_options(-Werror -Wextra -Wno-type-limits
|
add_compile_options(-Werror -Wextra
|
||||||
-Wno-sign-compare -Wvla -Wformat -Wformat-security -Wformat-overflow=2
|
-Walloc-zero -Wcast-align -Wcast-qual -Wduplicated-branches -Wduplicated-cond
|
||||||
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused
|
-Wfloat-equal -Winline -Wlogical-op -Wnested-externs -Wnull-dereference
|
||||||
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5
|
-Wold-style-definition -Wshift-overflow=2 -Wstrict-overflow=5
|
||||||
-Wstringop-overflow=4 -Walloc-zero -Wduplicated-cond
|
-Wstrict-prototypes -Wstringop-overflow=4 -Wundef -Wuninitialized -Wunused
|
||||||
-Wfloat-equal -Wshadow -Wcast-qual -Wcast-align -Wlogical-op
|
-Wshadow # TODO: -Wshadow=compatible-local ?
|
||||||
-Wnested-externs -Wno-aggressive-loop-optimizations -Winline
|
-Wformat=2 -Wformat-overflow=2 -Wformat-truncation=1
|
||||||
-Wundef -Wstrict-prototypes -Wold-style-definition)
|
-Wno-format-nonliteral # We have a couple of "dynamic" prints
|
||||||
|
# We do some range checks that are always false on some platforms (GCC, Clang)
|
||||||
|
-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
|
||||||
|
|
||||||
execute_process(COMMAND git describe --tags --dirty --always
|
find_program(GIT git)
|
||||||
OUTPUT_VARIABLE GIT_REV
|
if(GIT)
|
||||||
ERROR_QUIET)
|
execute_process(COMMAND ${GIT} describe --tags --dirty --always
|
||||||
string(STRIP "${GIT_REV}" GIT_REV)
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
OUTPUT_VARIABLE GIT_REV OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
ERROR_QUIET)
|
||||||
|
message(STATUS "RGBDS version: ${GIT_REV}")
|
||||||
|
else(GIT)
|
||||||
|
message(STATUS "Cannot determine RGBDS version (Git not installed), falling back")
|
||||||
|
endif(GIT)
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
@@ -88,11 +97,3 @@ if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
|||||||
message(CHECK_FAIL "no")
|
message(CHECK_FAIL "no")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(TRACE_PARSER)
|
|
||||||
target_compile_definitions(rgbasm PRIVATE -DYYDEBUG)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(TRACE_LEXER)
|
|
||||||
target_compile_definitions(rgbasm PRIVATE -DLEXER_DEBUG)
|
|
||||||
endif()
|
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ 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
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
@@ -30,7 +34,9 @@ Other contributors
|
|||||||
|
|
||||||
- David Brotz <dbrotz007@gmail.com>
|
- David Brotz <dbrotz007@gmail.com>
|
||||||
|
|
||||||
- Eldred Habert <eldredhabert0@gmail.com>
|
- Jakub Kądziołka <kuba@kadziolka.net>
|
||||||
|
|
||||||
|
- 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>
|
||||||
|
|
||||||
@@ -40,8 +46,6 @@ 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>
|
||||||
|
|||||||
52
Makefile
52
Makefile
@@ -30,7 +30,7 @@ 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
|
WARNFLAGS := -Wall -pedantic
|
||||||
|
|
||||||
# Overridable CFLAGS
|
# Overridable CFLAGS
|
||||||
CFLAGS ?= -O3 -flto -DNDEBUG
|
CFLAGS ?= -O3 -flto -DNDEBUG
|
||||||
@@ -49,6 +49,9 @@ 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
|
||||||
@@ -69,9 +72,9 @@ rgbasm_obj := \
|
|||||||
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
|
||||||
@@ -87,23 +90,23 @@ rgblink_obj := \
|
|||||||
src/link/script.o \
|
src/link/script.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/err.o \
|
src/extern/getopt.o \
|
||||||
src/extern/getopt.o
|
src/error.o
|
||||||
|
|
||||||
rgbgfx_obj := \
|
rgbgfx_obj := \
|
||||||
src/gfx/gb.o \
|
src/gfx/gb.o \
|
||||||
src/gfx/main.o \
|
src/gfx/main.o \
|
||||||
src/gfx/makepng.o \
|
src/gfx/makepng.o \
|
||||||
src/extern/err.o \
|
src/extern/getopt.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
|
||||||
@@ -189,9 +192,8 @@ 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:
|
||||||
$Qeval COMMON_COMMIT=$$(git merge-base HEAD ${BASE_REF}); \
|
$QCOMMON_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" \
|
||||||
@@ -199,25 +201,35 @@ 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 -Wall -Wextra -Wpedantic -Wno-type-limits \
|
$Qenv ${MAKE} WARNFLAGS="-Werror -Wextra \
|
||||||
-Wno-sign-compare -Wvla -Wformat -Wformat-security -Wformat-overflow=2 \
|
-Walloc-zero -Wcast-align -Wcast-qual -Wduplicated-branches -Wduplicated-cond \
|
||||||
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused \
|
-Wfloat-equal -Winline -Wlogical-op -Wnested-externs -Wold-style-definition \
|
||||||
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=4 \
|
-Wshift-overflow=2 \
|
||||||
-Wstringop-overflow=4 -Walloc-zero -Wduplicated-cond \
|
-Wstrict-overflow=5 -Wstrict-prototypes -Wundef -Wuninitialized -Wunused \
|
||||||
-Wfloat-equal -Wshadow -Wcast-qual -Wcast-align -Wlogical-op \
|
-Wshadow \
|
||||||
-Wnested-externs -Wno-aggressive-loop-optimizations -Winline \
|
-Wnull-dereference -Wstringop-overflow=4 \
|
||||||
-Wundef -Wstrict-prototypes -Wold-style-definition \
|
-Wformat=2 -Wformat-overflow=2 -Wformat-truncation=1 \
|
||||||
|
-Wno-format-nonliteral \
|
||||||
|
-Wno-type-limits -Wno-tautological-constant-out-of-range-compare \
|
||||||
|
-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 -fsanitize=address" \
|
||||||
CFLAGS="-ggdb3 -O0"
|
CFLAGS="-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!
|
||||||
@@ -225,11 +237,11 @@ develop:
|
|||||||
# install instructions instead.
|
# install instructions instead.
|
||||||
|
|
||||||
mingw32:
|
mingw32:
|
||||||
$Qmake CC=i686-w64-mingw32-gcc BISON=bison \
|
$Q${MAKE} CC=i686-w64-mingw32-gcc BISON=bison \
|
||||||
PKG_CONFIG=i686-w64-mingw32-pkg-config -j
|
PKG_CONFIG=i686-w64-mingw32-pkg-config -j
|
||||||
|
|
||||||
mingw64:
|
mingw64:
|
||||||
$Qmake CC=x86_64-w64-mingw32-gcc BISON=bison \
|
$Q${MAKE} CC=x86_64-w64-mingw32-gcc BISON=bison \
|
||||||
PKG_CONFIG=x86_64-w64-mingw32-pkg-config -j
|
PKG_CONFIG=x86_64-w64-mingw32-pkg-config -j
|
||||||
|
|
||||||
wine-shim:
|
wine-shim:
|
||||||
|
|||||||
53
README.rst
53
README.rst
@@ -47,44 +47,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/
|
||||||
│ └── ...
|
│ └── ...
|
||||||
├── 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`.
|
||||||
|
|
||||||
- ``src/`` - source code and manual pages for RGBDS.
|
- ``src/`` - source code and manual pages for RGBDS.
|
||||||
@@ -94,6 +103,8 @@ The RGBDS source code file structure somewhat resembles the following:
|
|||||||
|
|
||||||
- ``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
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|||||||
@@ -63,3 +63,5 @@ GitHub.
|
|||||||
5. Write a changelog in the GitHub draft release.
|
5. Write a changelog in the GitHub draft release.
|
||||||
|
|
||||||
6. Click the "Publish release" button to publish it!
|
6. Click the "Publish release" button to publish it!
|
||||||
|
|
||||||
|
7. Update the `release` branch. You can use ``git push origin release``.
|
||||||
|
|||||||
215
contrib/bash_compl/_rgbasm.bash
Executable file
215
contrib/bash_compl/_rgbasm.bash
Executable file
@@ -0,0 +1,215 @@
|
|||||||
|
#/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 dircetory 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() {
|
||||||
|
COMPREPLY=()
|
||||||
|
|
||||||
|
# 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]="halt-without-nop:normal"
|
||||||
|
[L]="preserve-ld: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"
|
||||||
|
[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() {
|
||||||
|
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:0:2}" = '--' ]]; 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:0:1}" = '-' ]]; then
|
||||||
|
# The `-M?` ones are a mix of short and long, augh
|
||||||
|
# They must match the *full* word, but only take a single dash
|
||||||
|
# So, handle them here
|
||||||
|
if [[ "$1" = "-M"[GP] ]]; then
|
||||||
|
state=normal
|
||||||
|
elif [[ "$1" = "-M"[TQ] ]]; then
|
||||||
|
state='glob-*.d *.mk *.o'
|
||||||
|
else
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Parse current word
|
||||||
|
# Careful that it might look like an option, so use `--` aggressively!
|
||||||
|
local cur_word="${COMP_WORDS[$COMP_CWORD]}"
|
||||||
|
|
||||||
|
# Process options, as short ones may change the state
|
||||||
|
if $opt_ena && [[ "$state" = 'normal' && "${cur_word:0:1}" = '-' ]]; 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:1:1}" = '-' ]]; then
|
||||||
|
# It is, try to complete one
|
||||||
|
COMPREPLY+=( $(compgen -W "${opts[*]%%:*}" -P '--' -- "${cur_word#--}") )
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
# Short options may be grouped, parse them to determine what to complete
|
||||||
|
# The `-M?` ones may not be followed by anything
|
||||||
|
if [[ "$1" != "-M"[GPTQ] ]]; then
|
||||||
|
parse_short_opt "$cur_word"
|
||||||
|
# We got some short options that behave like long ones
|
||||||
|
COMPREPLY+=( $(compgen -W '-MG -MP -MT -MQ' -- "$cur_word") )
|
||||||
|
|
||||||
|
if [[ "$state" = 'normal' ]]; then
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$state" in
|
||||||
|
unk) # Return with no replies: no idea what to complete!
|
||||||
|
;;
|
||||||
|
warning)
|
||||||
|
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
|
||||||
|
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
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _rgbasm_completions rgbasm
|
||||||
181
contrib/bash_compl/_rgbfix.bash
Executable file
181
contrib/bash_compl/_rgbfix.bash
Executable file
@@ -0,0 +1,181 @@
|
|||||||
|
#/usr/bin/env bash
|
||||||
|
|
||||||
|
# Same notes as RGBASM
|
||||||
|
|
||||||
|
_rgbfix_completions() {
|
||||||
|
COMPREPLY=()
|
||||||
|
|
||||||
|
# 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:0:2}" = '--' ]]; 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:0:1}" = '-' ]]; 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[$COMP_CWORD]}"
|
||||||
|
|
||||||
|
# Process options, as short ones may change the state
|
||||||
|
if $opt_ena && [[ "$state" = 'normal' && "${cur_word:0:1}" = '-' ]]; 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:1:1}" = '-' ]]; then
|
||||||
|
# It is, try to complete one
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
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}"
|
||||||
|
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}" -- "`tr 'a-z ' 'A-Z_' <<<"${cur_word/ /_}"`") )
|
||||||
|
COMPREPLY+=( $(compgen -W "help" -P "${cur_word:0:$optlen}" -- "${cur_word:$optlen}") )
|
||||||
|
;;
|
||||||
|
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
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _rgbfix_completions rgbfix
|
||||||
162
contrib/bash_compl/_rgbgfx.bash
Executable file
162
contrib/bash_compl/_rgbgfx.bash
Executable file
@@ -0,0 +1,162 @@
|
|||||||
|
#/usr/bin/env bash
|
||||||
|
|
||||||
|
# Same notes as RGBASM
|
||||||
|
|
||||||
|
_rgbgfx_completions() {
|
||||||
|
COMPREPLY=()
|
||||||
|
|
||||||
|
# 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"
|
||||||
|
[D]="debug:normal"
|
||||||
|
[h]="horizontal:normal"
|
||||||
|
[m]="mirror-tiles:normal"
|
||||||
|
[u]="unique-tiles:normal"
|
||||||
|
[v]="verbose:normal"
|
||||||
|
[f]="fix:normal"
|
||||||
|
[F]="fix-and-save:normal"
|
||||||
|
[a]="attr-map:*.attrmap"
|
||||||
|
[A]="output-attr-map:normal"
|
||||||
|
[d]="depth:unk"
|
||||||
|
[o]="output:glob *.2bpp"
|
||||||
|
[p]="palette:glob *.pal"
|
||||||
|
[P]="output-palette:normal"
|
||||||
|
[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:0:2}" = '--' ]]; 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:0:1}" = '-' ]]; 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[$COMP_CWORD]}"
|
||||||
|
|
||||||
|
# Process options, as short ones may change the state
|
||||||
|
if $opt_ena && [[ "$state" = 'normal' && "${cur_word:0:1}" = '-' ]]; 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:1:1}" = '-' ]]; then
|
||||||
|
# It is, try to complete one
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _rgbgfx_completions rgbgfx
|
||||||
157
contrib/bash_compl/_rgblink.bash
Executable file
157
contrib/bash_compl/_rgblink.bash
Executable file
@@ -0,0 +1,157 @@
|
|||||||
|
#/usr/bin/env bash
|
||||||
|
|
||||||
|
# Same notes as RGBASM
|
||||||
|
|
||||||
|
_rgblink_completions() {
|
||||||
|
COMPREPLY=()
|
||||||
|
|
||||||
|
# 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]="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:0:2}" = '--' ]]; 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:0:1}" = '-' ]]; 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[$COMP_CWORD]}"
|
||||||
|
|
||||||
|
# Process options, as short ones may change the state
|
||||||
|
if $opt_ena && [[ "$state" = 'normal' && "${cur_word:0:1}" = '-' ]]; 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:1:1}" = '-' ]]; then
|
||||||
|
# It is, try to complete one
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _rgblink_completions rgblink
|
||||||
85
contrib/checkdiff.bash
Executable file
85
contrib/checkdiff.bash
Executable file
@@ -0,0 +1,85 @@
|
|||||||
|
#!/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 src/rgbds.5 \
|
||||||
|
"Was the object file format changed?"
|
||||||
|
|
||||||
|
dependency src/asm/parser.y src/asm/rgbasm.5 \
|
||||||
|
"Was the rgbasm grammar changed?"
|
||||||
|
|
||||||
|
dependency include/asm/warning.h src/asm/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 src/asm/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 src/link/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 src/fix/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.c src/gfx/rgbgfx.1 \
|
||||||
|
"Did the rgbgfx CLI change?"
|
||||||
|
dependency src/gfx/main.c contrib/zsh_compl/_rgbgfx \
|
||||||
|
"Did the rgbgfx CLI change?"
|
||||||
|
dependency src/gfx/main.c contrib/bash_compl/_rgbgfx.bash \
|
||||||
|
"Did the rgbgfx CLI change?"
|
||||||
@@ -9,18 +9,26 @@ _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-entry:Warn on empty entries in db, dw, dl args'
|
'empty-data-directive:Warn on arg-less d[bwl] in ROM'
|
||||||
|
'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 truncations lose bits'
|
'truncation:Warn when implicit truncation loses bits'
|
||||||
'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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,15 +43,19 @@ local args=(
|
|||||||
-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)'{-D,--define}'+[Define a string symbol]:name + value (default 1):'
|
'*'{-D,--define}'+[Define a string symbol]:name + value (default 1):'
|
||||||
'(-g --gfx-chars)'{-g,--gfx-chars}'+[Change chars for gfx constants]:chars spec:'
|
'(-g --gfx-chars)'{-g,--gfx-chars}'+[Change chars for gfx constants]:chars spec:'
|
||||||
'(-i --include)'{-i,--include}'+[Add an include directory]:include path:_files -/'
|
'(-i --include)'{-i,--include}'+[Add an include directory]:include path:_files -/'
|
||||||
'(-M --dependfile)'{-M,--dependfile}"+[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:'
|
||||||
'(-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,12 +1,47 @@
|
|||||||
#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-only}'[Mark ROM as GBC-only]'
|
'(-C --color-only -c --color-compatible)'{-C,--color-only}'[Mark ROM as GBC-only]'
|
||||||
'(-c --color-compatible)'{-c,--color-compatible}'[Mark ROM as GBC-compatible]'
|
'(-C --color-only -c --color-compatible)'{-c,--color-compatible}'[Mark ROM as GBC-compatible]'
|
||||||
'(-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]'
|
||||||
|
|
||||||
@@ -14,7 +49,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 flags byte:'
|
'(-m --mbc-type)'{-m,--mbc-type}"+[Set MBC flags]:mbc name:_mbc_names"
|
||||||
'(-n --rom-version)'{-n,--rom-version}'+[Set ROM version]:rom version byte:'
|
'(-n --rom-version)'{-n,--rom-version}'+[Set ROM version]:rom version byte:'
|
||||||
'(-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:'
|
||||||
|
|||||||
@@ -32,6 +32,6 @@ local args=(
|
|||||||
'(-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 files:_files -g '*.png'"
|
":input png file:_files -g '*.png'"
|
||||||
)
|
)
|
||||||
_arguments -s -S : $args
|
_arguments -s -S : $args
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ 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 --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'"
|
||||||
@@ -15,6 +16,7 @@ local args=(
|
|||||||
'(-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,9 +11,9 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
struct Charmap *charmap_New(const char *name, const char *baseName);
|
struct Charmap *charmap_New(char const *name, char const *baseName);
|
||||||
void charmap_Delete(struct Charmap *charmap);
|
void charmap_Delete(struct Charmap *charmap);
|
||||||
void charmap_Set(const char *name);
|
void charmap_Set(char const *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);
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
#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 */
|
||||||
@@ -78,6 +77,7 @@ void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step,
|
|||||||
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 maxDepth);
|
void fstk_Init(char const *mainPath, size_t maxDepth);
|
||||||
|
|
||||||
#endif /* RGBDS_ASM_FSTACK_H */
|
#endif /* RGBDS_ASM_FSTACK_H */
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ 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);
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ 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 *macro_GetAllArgs(void);
|
char const *macro_GetAllArgs(void);
|
||||||
|
|
||||||
uint32_t macro_GetUniqueID(void);
|
uint32_t macro_GetUniqueID(void);
|
||||||
char const *macro_GetUniqueIDStr(void);
|
char const *macro_GetUniqueIDStr(void);
|
||||||
|
|||||||
@@ -10,9 +10,10 @@
|
|||||||
#define RGBDS_OPT_H
|
#define RGBDS_OPT_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
void opt_B(char chars[2]);
|
void opt_B(char const chars[2]);
|
||||||
void opt_G(char chars[4]);
|
void opt_G(char const chars[4]);
|
||||||
void opt_P(uint8_t fill);
|
void opt_P(uint8_t fill);
|
||||||
void opt_L(bool optimize);
|
void opt_L(bool optimize);
|
||||||
void opt_W(char const *flag);
|
void opt_W(char const *flag);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ struct Expression {
|
|||||||
uint8_t *rpn; // Array of bytes serializing the RPN expression
|
uint8_t *rpn; // Array of bytes serializing the RPN expression
|
||||||
uint32_t rpnCapacity; // Size of the `rpn` buffer
|
uint32_t rpnCapacity; // Size of the `rpn` buffer
|
||||||
uint32_t rpnLength; // Used size of the `rpn` buffer
|
uint32_t rpnLength; // Used size of the `rpn` buffer
|
||||||
uint32_t rpnPatchSize; // Size the expression will take in the obj file
|
uint32_t rpnPatchSize; // Size the expression will take in the object file
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "linkdefs.h"
|
#include "linkdefs.h"
|
||||||
|
#include "platform.h" // NONNULL
|
||||||
|
|
||||||
extern uint8_t fillByte;
|
extern uint8_t fillByte;
|
||||||
|
|
||||||
@@ -27,7 +28,7 @@ struct Section {
|
|||||||
uint32_t size;
|
uint32_t size;
|
||||||
uint32_t org;
|
uint32_t org;
|
||||||
uint32_t bank;
|
uint32_t bank;
|
||||||
uint8_t align;
|
uint8_t align; // Exactly as specified in `ALIGN[]`
|
||||||
uint16_t alignOfs;
|
uint16_t alignOfs;
|
||||||
struct Section *next;
|
struct Section *next;
|
||||||
struct Patch *patches;
|
struct Patch *patches;
|
||||||
@@ -42,7 +43,7 @@ struct SectionSpec {
|
|||||||
|
|
||||||
extern struct Section *currentSection;
|
extern struct Section *currentSection;
|
||||||
|
|
||||||
struct Section *sect_FindSectionByName(const char *name);
|
struct Section *sect_FindSectionByName(char const *name);
|
||||||
void sect_NewSection(char const *name, uint32_t secttype, uint32_t org,
|
void sect_NewSection(char const *name, uint32_t secttype, uint32_t org,
|
||||||
struct SectionSpec const *attributes, enum SectionModifier mod);
|
struct SectionSpec const *attributes, enum SectionModifier mod);
|
||||||
void sect_SetLoadSection(char const *name, uint32_t secttype, uint32_t org,
|
void sect_SetLoadSection(char const *name, uint32_t secttype, uint32_t org,
|
||||||
@@ -60,10 +61,10 @@ void sect_EndUnion(void);
|
|||||||
void sect_CheckUnionClosed(void);
|
void sect_CheckUnionClosed(void);
|
||||||
|
|
||||||
void sect_AbsByte(uint8_t b);
|
void sect_AbsByte(uint8_t b);
|
||||||
void sect_AbsByteGroup(uint8_t const *s, int32_t length);
|
void sect_AbsByteGroup(uint8_t const *s, size_t length);
|
||||||
void sect_AbsWordGroup(uint8_t const *s, int32_t length);
|
void sect_AbsWordGroup(uint8_t const *s, size_t length);
|
||||||
void sect_AbsLongGroup(uint8_t const *s, int32_t length);
|
void sect_AbsLongGroup(uint8_t const *s, size_t length);
|
||||||
void sect_Skip(int32_t skip, bool ds);
|
void sect_Skip(uint32_t skip, bool ds);
|
||||||
void sect_String(char const *s);
|
void sect_String(char const *s);
|
||||||
void sect_RelByte(struct Expression *expr, uint32_t pcShift);
|
void sect_RelByte(struct Expression *expr, uint32_t pcShift);
|
||||||
void sect_RelBytes(uint32_t n, struct Expression *exprs, size_t size);
|
void sect_RelBytes(uint32_t n, struct Expression *exprs, size_t size);
|
||||||
@@ -76,4 +77,6 @@ void sect_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length);
|
|||||||
void sect_PushSection(void);
|
void sect_PushSection(void);
|
||||||
void sect_PopSection(void);
|
void sect_PopSection(void);
|
||||||
|
|
||||||
|
bool sect_IsSizeKnown(struct Section const NONNULL(name));
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -17,15 +17,13 @@
|
|||||||
#include "asm/section.h"
|
#include "asm/section.h"
|
||||||
|
|
||||||
#include "platform.h" // MIN_NB_ELMS
|
#include "platform.h" // MIN_NB_ELMS
|
||||||
#include "types.h"
|
|
||||||
|
|
||||||
#define HASHSIZE (1 << 16)
|
#define MAXSYMLEN 255
|
||||||
#define MAXSYMLEN 256
|
|
||||||
|
|
||||||
enum SymbolType {
|
enum SymbolType {
|
||||||
SYM_LABEL,
|
SYM_LABEL,
|
||||||
SYM_EQU,
|
SYM_EQU,
|
||||||
SYM_SET,
|
SYM_VAR,
|
||||||
SYM_MACRO,
|
SYM_MACRO,
|
||||||
SYM_EQUS,
|
SYM_EQUS,
|
||||||
SYM_REF // Forward reference to a label
|
SYM_REF // Forward reference to a label
|
||||||
@@ -77,12 +75,12 @@ 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_SET;
|
return sym->type == SYM_EQU || sym->type == SYM_VAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
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_SET;
|
return sym->type == SYM_LABEL || sym->type == SYM_EQU || sym->type == SYM_VAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool sym_IsLabel(struct Symbol const *sym)
|
static inline bool sym_IsLabel(struct Symbol const *sym)
|
||||||
@@ -121,7 +119,7 @@ void sym_WriteAnonLabelName(char buf[MIN_NB_ELMS(MAXSYMLEN + 1)], uint32_t ofs,
|
|||||||
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_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 *symName);
|
||||||
|
|||||||
@@ -21,45 +21,57 @@ enum WarningState {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum WarningID {
|
enum WarningID {
|
||||||
WARNING_ASSERT, /* Assertions */
|
WARNING_ASSERT, // Assertions
|
||||||
WARNING_BACKWARDS_FOR, /* `for` loop with backwards range */
|
WARNING_BACKWARDS_FOR, // `for` loop with backwards range
|
||||||
WARNING_BUILTIN_ARG, /* Invalid args to builtins */
|
WARNING_BUILTIN_ARG, // Invalid args to builtins
|
||||||
WARNING_CHARMAP_REDEF, /* Charmap entry re-definition */
|
WARNING_CHARMAP_REDEF, // Charmap entry re-definition
|
||||||
WARNING_DIV, /* Division undefined behavior */
|
WARNING_DIV, // 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_USER, // User warnings
|
||||||
WARNING_USER, /* User warnings */
|
|
||||||
|
|
||||||
NB_WARNINGS,
|
NB_PLAIN_WARNINGS,
|
||||||
|
|
||||||
/* Warnings past this point are "meta" warnings */
|
// Warnings past this point are "parametric" warnings, only mapping to a single flag
|
||||||
WARNING_ALL = NB_WARNINGS,
|
#define PARAM_WARNINGS_START NB_PLAIN_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_ALL
|
NB_WARNINGS,
|
||||||
#define NB_META_WARNINGS (NB_WARNINGS_ALL - NB_WARNINGS)
|
#define NB_META_WARNINGS (NB_WARNINGS - META_WARNINGS_START)
|
||||||
};
|
};
|
||||||
|
|
||||||
extern enum WarningState warningStates[NB_WARNINGS];
|
extern enum WarningState warningStates[NB_PLAIN_AND_PARAM_WARNINGS];
|
||||||
extern bool warningsAreErrors;
|
extern bool warningsAreErrors;
|
||||||
|
|
||||||
void processWarningFlag(char const *flag);
|
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, const char *fmt, ...) format_(printf, 2, 3);
|
void warning(enum WarningID id, char const *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
|
||||||
@@ -68,7 +80,7 @@ void warning(enum WarningID id, const char *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(const char *fmt, ...) format_(printf, 1, 2);
|
_Noreturn void fatalerror(char const *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
|
||||||
@@ -76,6 +88,6 @@ _Noreturn void fatalerror(const char *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(const char *fmt, ...) format_(printf, 1, 2);
|
void error(char const *fmt, ...) format_(printf, 1, 2);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
21
include/error.h
Normal file
21
include/error.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* 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"
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
#endif /* RGBDS_ERROR_H */
|
||||||
44
include/extern/err.h
vendored
44
include/extern/err.h
vendored
@@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 */
|
|
||||||
5
include/extern/getopt.h
vendored
5
include/extern/getopt.h
vendored
@@ -30,13 +30,14 @@ 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 {
|
||||||
const char *name;
|
char const *name;
|
||||||
int has_arg;
|
int has_arg;
|
||||||
int *flag;
|
int *flag;
|
||||||
int val;
|
int val;
|
||||||
};
|
};
|
||||||
|
|
||||||
int musl_getopt_long_only(int, char **, const char *, const struct option *, int *);
|
int musl_getopt_long_only(int argc, char **argv, char const *optstring,
|
||||||
|
const struct option *longopts, int *idx);
|
||||||
|
|
||||||
#define no_argument 0
|
#define no_argument 0
|
||||||
#define required_argument 1
|
#define required_argument 1
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "extern/err.h"
|
#include "error.h"
|
||||||
|
|
||||||
struct Options {
|
struct Options {
|
||||||
bool debug;
|
bool debug;
|
||||||
@@ -24,7 +24,7 @@ struct Options {
|
|||||||
bool mirror;
|
bool mirror;
|
||||||
bool unique;
|
bool unique;
|
||||||
bool colorcurve;
|
bool colorcurve;
|
||||||
int trim;
|
unsigned int trim;
|
||||||
char *tilemapfile;
|
char *tilemapfile;
|
||||||
bool tilemapout;
|
bool tilemapout;
|
||||||
char *attrmapfile;
|
char *attrmapfile;
|
||||||
@@ -43,7 +43,7 @@ struct RGBColor {
|
|||||||
|
|
||||||
struct ImageOptions {
|
struct ImageOptions {
|
||||||
bool horizontal;
|
bool horizontal;
|
||||||
int trim;
|
unsigned int trim;
|
||||||
char *tilemapfile;
|
char *tilemapfile;
|
||||||
bool tilemapout;
|
bool tilemapout;
|
||||||
char *attrmapfile;
|
char *attrmapfile;
|
||||||
|
|||||||
@@ -89,4 +89,8 @@
|
|||||||
#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!
|
||||||
|
// (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 */
|
#endif /* HELPERS_H */
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ 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;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
#define RGBDS_OBJECT_VERSION_STRING "RGB%1u"
|
#define RGBDS_OBJECT_VERSION_STRING "RGB%1u"
|
||||||
#define RGBDS_OBJECT_VERSION_NUMBER 9U
|
#define RGBDS_OBJECT_VERSION_NUMBER 9U
|
||||||
#define RGBDS_OBJECT_REV 8U
|
#define RGBDS_OBJECT_REV 9U
|
||||||
|
|
||||||
enum AssertionType {
|
enum AssertionType {
|
||||||
ASSERT_WARN,
|
ASSERT_WARN,
|
||||||
@@ -49,6 +49,7 @@ enum RPNCommand {
|
|||||||
|
|
||||||
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,
|
||||||
|
|||||||
@@ -16,5 +16,6 @@ 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 */
|
||||||
|
|||||||
@@ -49,8 +49,10 @@
|
|||||||
/* MSVC doesn't support `[static N]` for array arguments from C99 */
|
/* 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 NONNULL(ptr) *ptr
|
||||||
#else
|
#else
|
||||||
# define MIN_NB_ELMS(N) static (N)
|
# define MIN_NB_ELMS(N) static (N)
|
||||||
|
# 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
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 */
|
|
||||||
@@ -11,8 +11,8 @@
|
|||||||
|
|
||||||
#define PACKAGE_VERSION_MAJOR 0
|
#define PACKAGE_VERSION_MAJOR 0
|
||||||
#define PACKAGE_VERSION_MINOR 5
|
#define PACKAGE_VERSION_MINOR 5
|
||||||
#define PACKAGE_VERSION_PATCH 1
|
#define PACKAGE_VERSION_PATCH 2
|
||||||
|
|
||||||
const char *get_package_version_string(void);
|
char const *get_package_version_string(void);
|
||||||
|
|
||||||
#endif /* EXTERN_VERSION_H */
|
#endif /* EXTERN_VERSION_H */
|
||||||
|
|||||||
2
src/.gitignore
vendored
Normal file
2
src/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Generated by CMake
|
||||||
|
/.version.c
|
||||||
@@ -6,10 +6,12 @@
|
|||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
#
|
#
|
||||||
|
|
||||||
|
configure_file(version.c _version.c ESCAPE_QUOTES)
|
||||||
|
|
||||||
set(common_src
|
set(common_src
|
||||||
"extern/err.c"
|
"error.c"
|
||||||
"extern/getopt.c"
|
"extern/getopt.c"
|
||||||
"version.c"
|
"_version.c"
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(PkgConfig)
|
find_package(PkgConfig)
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ static void initNode(struct Charnode *node)
|
|||||||
memset(node->next, 0, sizeof(node->next));
|
memset(node->next, 0, sizeof(node->next));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Charmap *charmap_New(const char *name, const char *baseName)
|
struct Charmap *charmap_New(char const *name, char const *baseName)
|
||||||
{
|
{
|
||||||
struct Charmap *base = NULL;
|
struct Charmap *base = NULL;
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ void charmap_Delete(struct Charmap *charmap)
|
|||||||
free(charmap);
|
free(charmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void charmap_Set(const char *name)
|
void charmap_Set(char const *name)
|
||||||
{
|
{
|
||||||
struct Charmap **charmap = (struct Charmap **)hash_GetNode(charmaps, name);
|
struct Charmap **charmap = (struct Charmap **)hash_GetNode(charmaps, name);
|
||||||
|
|
||||||
@@ -215,49 +215,50 @@ size_t charmap_ConvertNext(char const **input, uint8_t **output)
|
|||||||
size_t rewindDistance = 0;
|
size_t rewindDistance = 0;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
/* We still want NULs to reach the `else` path, to give a chance to rewind */
|
|
||||||
uint8_t c = **input - 1;
|
uint8_t c = **input - 1;
|
||||||
|
|
||||||
if (**input && node->next[c]) {
|
if (**input && node->next[c]) {
|
||||||
(*input)++; /* Consume that char */
|
// Consume that char
|
||||||
|
(*input)++;
|
||||||
rewindDistance++;
|
rewindDistance++;
|
||||||
|
|
||||||
|
// Advance to next node (index starts at 1)
|
||||||
node = &charmap->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; /* Rewind from after the match */
|
rewindDistance = 0; // If no longer match is found, rewind here
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
*input -= rewindDistance; /* Rewind */
|
// We are at a dead end (either because we reached the end of input, or of
|
||||||
rewindDistance = 0;
|
// the trie), so rewind up to the last match, and output.
|
||||||
node = &charmap->nodes[0];
|
*input -= rewindDistance; // This will rewind all the way if no match found
|
||||||
|
|
||||||
if (match) { /* Arrived at a dead end with a match found */
|
if (match) { // A match was found, use it
|
||||||
if (output)
|
if (output)
|
||||||
*(*output)++ = match->value;
|
*(*output)++ = match->value;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
} else if (**input) { /* No match found */
|
} else if (**input) { // No match found, but there is some input left
|
||||||
|
// This will write the codepoint's value to `output`, little-endian
|
||||||
size_t codepointLen = readUTF8Char(output ? *output : NULL,
|
size_t codepointLen = readUTF8Char(output ? *output : NULL,
|
||||||
*input);
|
*input);
|
||||||
|
|
||||||
if (codepointLen == 0)
|
if (codepointLen == 0)
|
||||||
error("Input string is not valid UTF-8!\n");
|
error("Input string is not valid UTF-8!\n");
|
||||||
|
|
||||||
/* OK because UTF-8 has no NUL in multi-byte chars */
|
// OK because UTF-8 has no NUL in multi-byte chars
|
||||||
*input += codepointLen;
|
*input += codepointLen;
|
||||||
if (output)
|
if (output)
|
||||||
*output += codepointLen;
|
*output += codepointLen;
|
||||||
|
|
||||||
return codepointLen;
|
return codepointLen;
|
||||||
|
|
||||||
} else { /* End of input */
|
} else { // End of input
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unreachable_();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ int32_t fix_Callback_PI(void)
|
|||||||
void fix_Print(int32_t i)
|
void fix_Print(int32_t i)
|
||||||
{
|
{
|
||||||
uint32_t u = i;
|
uint32_t u = i;
|
||||||
const char *sign = "";
|
char const *sign = "";
|
||||||
|
|
||||||
if (i < 0) {
|
if (i < 0) {
|
||||||
u = -u;
|
u = -u;
|
||||||
|
|||||||
@@ -228,21 +228,13 @@ void fmt_PrintNumber(char *buf, size_t bufLen, struct FormatSpec const *fmt, uin
|
|||||||
/* Default fractional width (C's is 6 for "%f"; here 5 is enough) */
|
/* Default fractional width (C's is 6 for "%f"; here 5 is enough) */
|
||||||
size_t fracWidth = fmt->hasFrac ? fmt->fracWidth : 5;
|
size_t fracWidth = fmt->hasFrac ? fmt->fracWidth : 5;
|
||||||
|
|
||||||
if (fracWidth) {
|
if (fracWidth > 255) {
|
||||||
if (fracWidth > 255) {
|
error("Fractional width %zu too long, limiting to 255\n",
|
||||||
error("Fractional width %zu too long, limiting to 255\n",
|
fracWidth);
|
||||||
fracWidth);
|
fracWidth = 255;
|
||||||
fracWidth = 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
char spec[16]; /* Max "%" + 5-char PRIu32 + ".%0255.f" + terminator */
|
|
||||||
|
|
||||||
snprintf(spec, sizeof(spec), "%%" PRIu32 ".%%0%zu.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 / 65536.0);
|
||||||
} else {
|
} else {
|
||||||
char const *spec = fmt->type == 'd' ? "%" PRId32
|
char const *spec = fmt->type == 'd' ? "%" PRId32
|
||||||
: fmt->type == 'u' ? "%" PRIu32
|
: fmt->type == 'u' ? "%" PRIu32
|
||||||
|
|||||||
@@ -23,12 +23,6 @@
|
|||||||
|
|
||||||
#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;
|
||||||
@@ -241,11 +235,11 @@ bool yywrap(void)
|
|||||||
/* 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_AddSet(contextStack->forName,
|
struct Symbol *sym = sym_AddVar(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_SET)
|
if (sym->type != SYM_VAR)
|
||||||
fatalerror("Failed to update FOR symbol value\n");
|
fatalerror("Failed to update FOR symbol value\n");
|
||||||
}
|
}
|
||||||
/* Advance to the next iteration */
|
/* Advance to the next iteration */
|
||||||
@@ -259,7 +253,6 @@ 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;
|
||||||
|
|
||||||
@@ -269,10 +262,8 @@ bool yywrap(void)
|
|||||||
|
|
||||||
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);
|
||||||
@@ -293,8 +284,9 @@ bool yywrap(void)
|
|||||||
*/
|
*/
|
||||||
static void newContext(struct FileStackNode *fileInfo)
|
static void newContext(struct FileStackNode *fileInfo)
|
||||||
{
|
{
|
||||||
if (++contextDepth >= maxRecursionDepth)
|
++contextDepth;
|
||||||
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
|
fstk_NewRecursionDepth(maxRecursionDepth); // Only checks if the max depth was exceeded
|
||||||
|
|
||||||
struct Context *context = malloc(sizeof(*context));
|
struct Context *context = malloc(sizeof(*context));
|
||||||
|
|
||||||
if (!context)
|
if (!context)
|
||||||
@@ -315,8 +307,6 @@ static void newContext(struct FileStackNode *fileInfo)
|
|||||||
|
|
||||||
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;
|
||||||
|
|
||||||
@@ -332,7 +322,6 @@ void fstk_RunInclude(char const *path)
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dbgPrint("Full path: \"%s\"\n", fullPath);
|
|
||||||
|
|
||||||
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + size);
|
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + size);
|
||||||
|
|
||||||
@@ -356,8 +345,6 @@ void fstk_RunInclude(char const *path)
|
|||||||
|
|
||||||
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) {
|
||||||
@@ -461,8 +448,6 @@ 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))
|
||||||
@@ -475,12 +460,9 @@ 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)
|
||||||
{
|
{
|
||||||
dbgPrint("Running FOR(\"%s\", %" PRId32 ", %" PRId32 ", %" PRId32 ")\n",
|
struct Symbol *sym = sym_AddVar(symName, start);
|
||||||
symName, start, stop, step);
|
|
||||||
|
|
||||||
struct Symbol *sym = sym_AddSet(symName, start);
|
if (sym->type != SYM_VAR)
|
||||||
|
|
||||||
if (sym->type != SYM_SET)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
@@ -517,8 +499,6 @@ void fstk_StopRept(void)
|
|||||||
|
|
||||||
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;
|
||||||
@@ -528,6 +508,13 @@ bool fstk_Break(void)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void fstk_NewRecursionDepth(size_t newDepth)
|
||||||
|
{
|
||||||
|
if (contextDepth >= newDepth)
|
||||||
|
fatalerror("Recursion limit (%zu) exceeded\n", newDepth);
|
||||||
|
maxRecursionDepth = newDepth;
|
||||||
|
}
|
||||||
|
|
||||||
void fstk_Init(char const *mainPath, size_t maxDepth)
|
void fstk_Init(char const *mainPath, size_t maxDepth)
|
||||||
{
|
{
|
||||||
struct LexerState *state = lexer_OpenFile(mainPath);
|
struct LexerState *state = lexer_OpenFile(mainPath);
|
||||||
|
|||||||
476
src/asm/lexer.c
476
src/asm/lexer.c
File diff suppressed because it is too large
Load Diff
@@ -101,7 +101,7 @@ char const *macro_GetArg(uint32_t i)
|
|||||||
: macroArgs->args[realIndex];
|
: macroArgs->args[realIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
char *macro_GetAllArgs(void)
|
char const *macro_GetAllArgs(void)
|
||||||
{
|
{
|
||||||
if (!macroArgs)
|
if (!macroArgs)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
@@ -30,10 +30,10 @@
|
|||||||
#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__
|
#ifdef __clang__
|
||||||
@@ -45,17 +45,13 @@
|
|||||||
#ifdef __SANITIZE_ADDRESS__
|
#ifdef __SANITIZE_ADDRESS__
|
||||||
// There are known, non-trivial to fix leaks. We would still like to have `make develop'
|
// There are known, non-trivial to fix leaks. We would still like to have `make develop'
|
||||||
// detect memory corruption, though.
|
// detect memory corruption, though.
|
||||||
const char *__asan_default_options(void) { return "detect_leaks=0"; }
|
char const *__asan_default_options(void) { return "detect_leaks=0"; }
|
||||||
#endif
|
#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 generatedMissingIncludes;
|
||||||
bool failedOnMissingInclude;
|
bool failedOnMissingInclude;
|
||||||
@@ -68,13 +64,13 @@ 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(const char *str)
|
static char *make_escape(char const *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(1, "%s: Failed to allocate memory", __func__);
|
err("%s: Failed to allocate memory", __func__);
|
||||||
|
|
||||||
while (*str) {
|
while (*str) {
|
||||||
/* All dollars needs to be doubled */
|
/* All dollars needs to be doubled */
|
||||||
@@ -146,6 +142,9 @@ static void print_usage(void)
|
|||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
#if YYDEBUG
|
||||||
|
yydebug = 1;
|
||||||
|
#endif
|
||||||
int ch;
|
int ch;
|
||||||
char *ep;
|
char *ep;
|
||||||
|
|
||||||
@@ -161,10 +160,6 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
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);
|
||||||
|
|
||||||
@@ -192,7 +187,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(1, "Must specify exactly 2 characters for option 'b'");
|
errx("Must specify exactly 2 characters for option 'b'");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
char *equals;
|
char *equals;
|
||||||
@@ -214,7 +209,7 @@ 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(1, "Must specify exactly 4 characters for option 'g'");
|
errx("Must specify exactly 4 characters for option 'g'");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'h':
|
case 'h':
|
||||||
@@ -235,7 +230,7 @@ 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(1, "Could not open dependfile %s", musl_optarg);
|
err("Could not open dependfile %s", musl_optarg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'o':
|
case 'o':
|
||||||
@@ -247,10 +242,10 @@ int main(int argc, char *argv[])
|
|||||||
fill = 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(1, "Invalid argument for option 'p'");
|
errx("Invalid argument for option 'p'");
|
||||||
|
|
||||||
if (fill < 0 || fill > 0xFF)
|
if (fill > 0xFF)
|
||||||
errx(1, "Argument for option 'p' must be between 0 and 0xFF");
|
errx("Argument for option 'p' must be between 0 and 0xFF");
|
||||||
|
|
||||||
opt_P(fill);
|
opt_P(fill);
|
||||||
break;
|
break;
|
||||||
@@ -259,7 +254,7 @@ int main(int argc, char *argv[])
|
|||||||
maxDepth = strtoul(musl_optarg, &ep, 0);
|
maxDepth = strtoul(musl_optarg, &ep, 0);
|
||||||
|
|
||||||
if (musl_optarg[0] == '\0' || *ep != '\0')
|
if (musl_optarg[0] == '\0' || *ep != '\0')
|
||||||
errx(1, "Invalid argument for option 'r'");
|
errx("Invalid argument for option 'r'");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'V':
|
case 'V':
|
||||||
@@ -288,33 +283,23 @@ int main(int argc, char *argv[])
|
|||||||
generatePhonyDeps = true;
|
generatePhonyDeps = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
char *newTarget;
|
||||||
case 'Q':
|
case 'Q':
|
||||||
case 'T':
|
case 'T':
|
||||||
if (musl_optind == argc)
|
newTarget = musl_optarg;
|
||||||
errx(1, "-M%c takes a target file name argument", depType);
|
|
||||||
ep = musl_optarg;
|
|
||||||
if (depType == 'Q')
|
if (depType == 'Q')
|
||||||
ep = make_escape(ep);
|
newTarget = make_escape(newTarget);
|
||||||
|
size_t newTargetLen = strlen(newTarget) + 1; // Plus the space
|
||||||
|
|
||||||
targetFileNameLen += strlen(ep) + 1;
|
targetFileName = realloc(targetFileName,
|
||||||
if (!targetFileName) {
|
targetFileNameLen + newTargetLen + 1);
|
||||||
/* On first alloc, make an empty str */
|
|
||||||
targetFileName = malloc(targetFileNameLen + 1);
|
|
||||||
if (targetFileName)
|
|
||||||
*targetFileName = '\0';
|
|
||||||
} else {
|
|
||||||
targetFileName = realloc(targetFileName,
|
|
||||||
targetFileNameLen + 1);
|
|
||||||
}
|
|
||||||
if (targetFileName == NULL)
|
if (targetFileName == NULL)
|
||||||
err(1, "Cannot append new file to target file list");
|
err("Cannot append new file to target file list");
|
||||||
strcat(targetFileName, ep);
|
memcpy(&targetFileName[targetFileNameLen], newTarget, newTargetLen);
|
||||||
if (depType == 'Q')
|
if (depType == 'Q')
|
||||||
free(ep);
|
free(newTarget);
|
||||||
char *ptr = targetFileName + strlen(targetFileName);
|
targetFileNameLen += newTargetLen;
|
||||||
|
targetFileName[targetFileNameLen - 1] = ' ';
|
||||||
*ptr++ = ' ';
|
|
||||||
*ptr = '\0';
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -328,6 +313,8 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
if (targetFileName == NULL)
|
if (targetFileName == NULL)
|
||||||
targetFileName = objectName;
|
targetFileName = objectName;
|
||||||
|
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);
|
||||||
@@ -344,7 +331,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
if (dependfile) {
|
if (dependfile) {
|
||||||
if (!targetFileName)
|
if (!targetFileName)
|
||||||
errx(1, "Dependency files can only be created if a target file is specified with either -o, -MQ or -MT\n");
|
errx("Dependency files can only be created if a target file is specified with either -o, -MQ or -MT");
|
||||||
|
|
||||||
fprintf(dependfile, "%s: %s\n", targetFileName, mainFileName);
|
fprintf(dependfile, "%s: %s\n", targetFileName, mainFileName);
|
||||||
}
|
}
|
||||||
@@ -365,7 +352,7 @@ int main(int argc, char *argv[])
|
|||||||
sect_CheckUnionClosed();
|
sect_CheckUnionClosed();
|
||||||
|
|
||||||
if (nbErrors != 0)
|
if (nbErrors != 0)
|
||||||
errx(1, "Assembly aborted (%u error%s)!", nbErrors,
|
errx("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
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#include <ctype.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -5,6 +6,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "asm/fstack.h"
|
||||||
#include "asm/lexer.h"
|
#include "asm/lexer.h"
|
||||||
#include "asm/main.h"
|
#include "asm/main.h"
|
||||||
#include "asm/section.h"
|
#include "asm/section.h"
|
||||||
@@ -17,18 +19,20 @@ struct OptStackEntry {
|
|||||||
bool haltnop;
|
bool haltnop;
|
||||||
bool optimizeLoads;
|
bool optimizeLoads;
|
||||||
bool warningsAreErrors;
|
bool warningsAreErrors;
|
||||||
enum WarningState warningStates[NB_WARNINGS];
|
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 chars[2])
|
void opt_B(char const chars[2])
|
||||||
{
|
{
|
||||||
lexer_SetBinDigits(chars);
|
lexer_SetBinDigits(chars);
|
||||||
}
|
}
|
||||||
|
|
||||||
void opt_G(char chars[4])
|
void opt_G(char const chars[4])
|
||||||
{
|
{
|
||||||
lexer_SetGfxDigits(chars);
|
lexer_SetGfxDigits(chars);
|
||||||
}
|
}
|
||||||
@@ -38,6 +42,12 @@ void opt_P(uint8_t fill)
|
|||||||
fillByte = fill;
|
fillByte = fill;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void opt_R(size_t newDepth)
|
||||||
|
{
|
||||||
|
fstk_NewRecursionDepth(newDepth);
|
||||||
|
lexer_CheckRecursionDepth();
|
||||||
|
}
|
||||||
|
|
||||||
void opt_h(bool halt)
|
void opt_h(bool halt)
|
||||||
{
|
{
|
||||||
haltnop = halt;
|
haltnop = halt;
|
||||||
@@ -48,7 +58,7 @@ void opt_L(bool optimize)
|
|||||||
optimizeLoads = optimize;
|
optimizeLoads = optimize;
|
||||||
}
|
}
|
||||||
|
|
||||||
void opt_W(char const *flag)
|
void opt_W(char *flag)
|
||||||
{
|
{
|
||||||
processWarningFlag(flag);
|
processWarningFlag(flag);
|
||||||
}
|
}
|
||||||
@@ -85,6 +95,29 @@ void opt_Parse(char *s)
|
|||||||
}
|
}
|
||||||
break;
|
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':
|
case 'h':
|
||||||
if (s[1] == '\0')
|
if (s[1] == '\0')
|
||||||
opt_h(false);
|
opt_h(false);
|
||||||
|
|||||||
@@ -27,8 +27,7 @@
|
|||||||
#include "asm/symbol.h"
|
#include "asm/symbol.h"
|
||||||
#include "asm/warning.h"
|
#include "asm/warning.h"
|
||||||
|
|
||||||
#include "extern/err.h"
|
#include "error.h"
|
||||||
|
|
||||||
#include "linkdefs.h"
|
#include "linkdefs.h"
|
||||||
#include "platform.h" // strdup
|
#include "platform.h" // strdup
|
||||||
|
|
||||||
@@ -193,7 +192,7 @@ 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) : -1;
|
return sect ? getsectid(sect) : (uint32_t)-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -201,7 +200,7 @@ static uint32_t getSectIDIfAny(struct Section const *sect)
|
|||||||
*/
|
*/
|
||||||
static void writepatch(struct Patch const *patch, FILE *f)
|
static void writepatch(struct Patch const *patch, FILE *f)
|
||||||
{
|
{
|
||||||
assert(patch->src->ID != -1);
|
assert(patch->src->ID != (uint32_t)-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->offset, f);
|
||||||
@@ -265,7 +264,7 @@ static void writesymbol(struct Symbol const *sym, FILE *f)
|
|||||||
if (!sym_IsDefined(sym)) {
|
if (!sym_IsDefined(sym)) {
|
||||||
putc(SYMTYPE_IMPORT, f);
|
putc(SYMTYPE_IMPORT, f);
|
||||||
} else {
|
} else {
|
||||||
assert(sym->src->ID != -1);
|
assert(sym->src->ID != (uint32_t)-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);
|
||||||
@@ -491,7 +490,7 @@ static void freeassert(struct Assertion *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 : -1, f);
|
putlong(node->parent ? node->parent->ID : (uint32_t)-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) {
|
||||||
@@ -528,7 +527,7 @@ void out_WriteObject(void)
|
|||||||
f = fdopen(1, "wb");
|
f = fdopen(1, "wb");
|
||||||
|
|
||||||
if (!f)
|
if (!f)
|
||||||
err(1, "Couldn't write file '%s'", objectName);
|
err("Couldn't write file '%s'", objectName);
|
||||||
|
|
||||||
/* Also write symbols that weren't written above */
|
/* Also write symbols that weren't written above */
|
||||||
sym_ForEach(registerUnregisteredSymbol, NULL);
|
sym_ForEach(registerUnregisteredSymbol, NULL);
|
||||||
|
|||||||
230
src/asm/parser.y
230
src/asm/parser.y
@@ -52,22 +52,27 @@ static void lowerstring(char *dest, char const *src)
|
|||||||
*dest = '\0';
|
*dest = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t str2int2(uint8_t *s, int32_t length)
|
static uint32_t str2int2(uint8_t *s, uint32_t length)
|
||||||
{
|
{
|
||||||
int32_t i;
|
if (length > 4)
|
||||||
|
warning(WARNING_NUMERIC_STRING_1,
|
||||||
|
"Treating string as a number ignores first %" PRIu32 " character%s\n",
|
||||||
|
length - 4, length == 5 ? "" : "s");
|
||||||
|
else if (length > 1)
|
||||||
|
warning(WARNING_NUMERIC_STRING_2,
|
||||||
|
"Treating %" PRIu32 "-character string as a number\n", length);
|
||||||
|
|
||||||
uint32_t r = 0;
|
uint32_t r = 0;
|
||||||
|
|
||||||
i = length < 4 ? 0 : length - 4;
|
for (uint32_t i = length < 4 ? 0 : length - 4; i < length; i++) {
|
||||||
while (i < length) {
|
|
||||||
r <<= 8;
|
r <<= 8;
|
||||||
r |= s[i];
|
r |= s[i];
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *strrstr(char *s1, char *s2)
|
static const char *strrstr(char const *s1, char const *s2)
|
||||||
{
|
{
|
||||||
size_t len1 = strlen(s1);
|
size_t len1 = strlen(s1);
|
||||||
size_t len2 = strlen(s2);
|
size_t len2 = strlen(s2);
|
||||||
@@ -75,7 +80,7 @@ static char *strrstr(char *s1, char *s2)
|
|||||||
if (len2 > len1)
|
if (len2 > len1)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
for (char *p = s1 + len1 - len2; p >= s1; p--)
|
for (char const *p = s1 + len1 - len2; p >= s1; p--)
|
||||||
if (!strncmp(p, s2, len2))
|
if (!strncmp(p, s2, len2))
|
||||||
return p;
|
return p;
|
||||||
|
|
||||||
@@ -213,10 +218,10 @@ static uint32_t adjustNegativePos(int32_t pos, size_t len, char const *functionN
|
|||||||
return (uint32_t)pos;
|
return (uint32_t)pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void strrpl(char *dest, size_t destLen, char const *src, char const *old, char const *new)
|
static void strrpl(char *dest, size_t destLen, char const *src, char const *old, char const *rep)
|
||||||
{
|
{
|
||||||
size_t oldLen = strlen(old);
|
size_t oldLen = strlen(old);
|
||||||
size_t newLen = strlen(new);
|
size_t repLen = strlen(rep);
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
|
||||||
if (!oldLen) {
|
if (!oldLen) {
|
||||||
@@ -235,8 +240,8 @@ static void strrpl(char *dest, size_t destLen, char const *src, char const *old,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
// Copy the replacement substring
|
// Copy the replacement substring
|
||||||
memcpy(dest + i, new, newLen < destLen - i ? newLen : destLen - i);
|
memcpy(dest + i, rep, repLen < destLen - i ? repLen : destLen - i);
|
||||||
i += newLen;
|
i += repLen;
|
||||||
if (i >= destLen)
|
if (i >= destLen)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -359,6 +364,17 @@ static void strfmt(char *dest, size_t destLen, char const *fmt, size_t nbArgs, s
|
|||||||
dest[i] = '\0';
|
dest[i] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void compoundAssignment(const char *symName, enum RPNCommand op, int32_t constValue) {
|
||||||
|
struct Expression oldExpr, constExpr, newExpr;
|
||||||
|
int32_t newValue;
|
||||||
|
|
||||||
|
rpn_Symbol(&oldExpr, symName);
|
||||||
|
rpn_Number(&constExpr, constValue);
|
||||||
|
rpn_BinaryOp(op, &newExpr, &oldExpr, &constExpr);
|
||||||
|
newValue = rpn_GetConstVal(&newExpr);
|
||||||
|
sym_AddVar(symName, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
static void initDsArgList(struct DsArgList *args)
|
static void initDsArgList(struct DsArgList *args)
|
||||||
{
|
{
|
||||||
args->nbArgs = 0;
|
args->nbArgs = 0;
|
||||||
@@ -369,7 +385,7 @@ static void initDsArgList(struct DsArgList *args)
|
|||||||
strerror(errno));
|
strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t nextDsArgListIndex(struct DsArgList *args)
|
static void appendDsArgList(struct DsArgList *args, const struct Expression *expr)
|
||||||
{
|
{
|
||||||
if (args->nbArgs == args->capacity) {
|
if (args->nbArgs == args->capacity) {
|
||||||
args->capacity = (args->capacity + 1) * 2;
|
args->capacity = (args->capacity + 1) * 2;
|
||||||
@@ -378,7 +394,7 @@ static size_t nextDsArgListIndex(struct DsArgList *args)
|
|||||||
fatalerror("realloc error while resizing ds arg list: %s\n",
|
fatalerror("realloc error while resizing ds arg list: %s\n",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
}
|
}
|
||||||
return args->nbArgs++;
|
args->args[args->nbArgs++] = *expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void freeDsArgList(struct DsArgList *args)
|
static void freeDsArgList(struct DsArgList *args)
|
||||||
@@ -463,6 +479,7 @@ enum {
|
|||||||
char string[MAXSTRLEN + 1];
|
char string[MAXSTRLEN + 1];
|
||||||
struct Expression expr;
|
struct Expression expr;
|
||||||
int32_t constValue;
|
int32_t constValue;
|
||||||
|
enum RPNCommand compoundEqual;
|
||||||
enum SectionModifier sectMod;
|
enum SectionModifier sectMod;
|
||||||
struct SectionSpec sectSpec;
|
struct SectionSpec sectSpec;
|
||||||
struct MacroArgs *macroArg;
|
struct MacroArgs *macroArg;
|
||||||
@@ -517,7 +534,7 @@ enum {
|
|||||||
%token T_OP_LOGICNE "!=" T_OP_LOGICEQU "=="
|
%token T_OP_LOGICNE "!=" T_OP_LOGICEQU "=="
|
||||||
%token T_OP_ADD "+" T_OP_SUB "-"
|
%token T_OP_ADD "+" T_OP_SUB "-"
|
||||||
%token T_OP_OR "|" T_OP_XOR "^" T_OP_AND "&"
|
%token T_OP_OR "|" T_OP_XOR "^" T_OP_AND "&"
|
||||||
%token T_OP_SHL "<<" T_OP_SHR ">>"
|
%token T_OP_SHL "<<" T_OP_SHR ">>" T_OP_USHR ">>>"
|
||||||
%token T_OP_MUL "*" T_OP_DIV "/" T_OP_MOD "%"
|
%token T_OP_MUL "*" T_OP_DIV "/" T_OP_MOD "%"
|
||||||
%token T_OP_NOT "~"
|
%token T_OP_NOT "~"
|
||||||
%left T_OP_LOGICOR
|
%left T_OP_LOGICOR
|
||||||
@@ -525,7 +542,7 @@ enum {
|
|||||||
%left T_OP_LOGICGT T_OP_LOGICLT T_OP_LOGICGE T_OP_LOGICLE T_OP_LOGICNE T_OP_LOGICEQU
|
%left T_OP_LOGICGT T_OP_LOGICLT T_OP_LOGICGE T_OP_LOGICLE T_OP_LOGICNE T_OP_LOGICEQU
|
||||||
%left T_OP_ADD T_OP_SUB
|
%left T_OP_ADD T_OP_SUB
|
||||||
%left T_OP_OR T_OP_XOR T_OP_AND
|
%left T_OP_OR T_OP_XOR T_OP_AND
|
||||||
%left T_OP_SHL T_OP_SHR
|
%left T_OP_SHL T_OP_SHR T_OP_USHR
|
||||||
%left T_OP_MUL T_OP_DIV T_OP_MOD
|
%left T_OP_MUL T_OP_DIV T_OP_MOD
|
||||||
|
|
||||||
%precedence NEG /* negation -- unary minus */
|
%precedence NEG /* negation -- unary minus */
|
||||||
@@ -574,6 +591,12 @@ enum {
|
|||||||
%token T_POP_EQUAL "="
|
%token T_POP_EQUAL "="
|
||||||
%token T_POP_EQUS "EQUS"
|
%token T_POP_EQUS "EQUS"
|
||||||
|
|
||||||
|
%token T_POP_ADDEQ "+=" T_POP_SUBEQ "-="
|
||||||
|
%token T_POP_MULEQ "*=" T_POP_DIVEQ "/=" T_POP_MODEQ "%="
|
||||||
|
%token T_POP_OREQ "|=" T_POP_XOREQ "^=" T_POP_ANDEQ "&="
|
||||||
|
%token T_POP_SHLEQ "<<=" T_POP_SHREQ ">>="
|
||||||
|
%type <compoundEqual> compoundeq
|
||||||
|
|
||||||
%token T_POP_INCLUDE "INCLUDE"
|
%token T_POP_INCLUDE "INCLUDE"
|
||||||
%token T_POP_PRINT "PRINT" T_POP_PRINTLN "PRINTLN"
|
%token T_POP_PRINT "PRINT" T_POP_PRINTLN "PRINTLN"
|
||||||
%token T_POP_PRINTF "PRINTF" T_POP_PRINTT "PRINTT" T_POP_PRINTV "PRINTV" T_POP_PRINTI "PRINTI"
|
%token T_POP_PRINTF "PRINTF" T_POP_PRINTT "PRINTT" T_POP_PRINTV "PRINTV" T_POP_PRINTI "PRINTI"
|
||||||
@@ -623,36 +646,38 @@ enum {
|
|||||||
%token T_Z80_CALL "call" T_Z80_CCF "ccf" T_Z80_CP "cp" T_Z80_CPL "cpl"
|
%token T_Z80_CALL "call" T_Z80_CCF "ccf" T_Z80_CP "cp" T_Z80_CPL "cpl"
|
||||||
%token T_Z80_DAA "daa" T_Z80_DEC "dec" T_Z80_DI "di"
|
%token T_Z80_DAA "daa" T_Z80_DEC "dec" T_Z80_DI "di"
|
||||||
%token T_Z80_EI "ei"
|
%token T_Z80_EI "ei"
|
||||||
%token T_Z80_HALT "halt"
|
%token T_Z80_HALT "halt✋"
|
||||||
%token T_Z80_INC "inc"
|
%token T_Z80_INC "inc"
|
||||||
%token T_Z80_JP "jp" T_Z80_JR "jr"
|
%token T_Z80_JP "jp" T_Z80_JR "jr"
|
||||||
%token T_Z80_LD "ld"
|
%token T_Z80_LD "ld"
|
||||||
%token T_Z80_LDI "ldi"
|
%token T_Z80_LDI "ldi"
|
||||||
%token T_Z80_LDD "ldd"
|
%token T_Z80_LDD "ldd"
|
||||||
%token T_Z80_LDH "ldh"
|
%token T_Z80_LDH "ldh"
|
||||||
%token T_Z80_NOP "nop"
|
%token T_Z80_NOP "nope"
|
||||||
%token T_Z80_OR "or"
|
%token T_Z80_OR "or"
|
||||||
|
%token T_OWO "owo"
|
||||||
%token T_Z80_POP "pop" T_Z80_PUSH "push"
|
%token T_Z80_POP "pop" T_Z80_PUSH "push"
|
||||||
%token T_Z80_RES "res" T_Z80_RET "ret" T_Z80_RETI "reti" T_Z80_RST "rst"
|
%token T_Z80_RES "res" T_Z80_RET "ret" T_Z80_RETI "reti" T_Z80_RST "rst"
|
||||||
%token T_Z80_RL "rl" T_Z80_RLA "rla" T_Z80_RLC "rlc" T_Z80_RLCA "rlca"
|
%token T_Z80_RL "rl" T_Z80_RLA "rla" T_Z80_RLC "rlc" T_Z80_RLCA "rlca"
|
||||||
%token T_Z80_RR "rr" T_Z80_RRA "rra" T_Z80_RRC "rrc" T_Z80_RRCA "rrca"
|
%token T_Z80_RR "rr" T_Z80_RRA "rra" T_Z80_RRC "rrc" T_Z80_RRCA "rrca"
|
||||||
%token T_Z80_SBC "sbc" T_Z80_SCF "scf" T_Z80_STOP "stop"
|
%token T_Z80_SBC "sbc" T_Z80_SCF "scf" T_Z80_STOP "stop!!🛑"
|
||||||
%token T_Z80_SLA "sla" T_Z80_SRA "sra" T_Z80_SRL "srl" T_Z80_SUB "sub"
|
%token T_Z80_SLA "sla" T_Z80_SRA "sra" T_Z80_SRL "srl" T_Z80_SUB "sub"
|
||||||
%token T_Z80_SWAP "swap"
|
%token T_Z80_SWAP "swap"
|
||||||
%token T_Z80_XOR "xor"
|
%token T_Z80_XOR "xor"
|
||||||
|
|
||||||
%token T_TOKEN_A "a"
|
%token T_TOKEN_A "( •̀A•́)" T_TOKEN_F "𝓕𝓾𝓬𝓴"
|
||||||
%token T_TOKEN_B "b" T_TOKEN_C "c"
|
%token T_TOKEN_B "=B" T_TOKEN_C "♥(˘⌣˘ C)"
|
||||||
%token T_TOKEN_D "d" T_TOKEN_E "e"
|
%token T_TOKEN_D ";D" T_TOKEN_E "(´ε` )♡" T_TOKEN_E_HEART "(´ε` )♡"
|
||||||
%token T_TOKEN_H "h" T_TOKEN_L "l"
|
%token T_TOKEN_H "н" T_TOKEN_L_ARM "∠( ᐛ 」∠)_" T_TOKEN_L_FACE "∠( ᐛ 」∠)_" T_TOKEN_L_BODY "∠( ᐛ 」∠)_" T_TOKEN_L_LEG "∠( ᐛ 」∠)_"
|
||||||
%token T_MODE_AF "af" T_MODE_BC "bc" T_MODE_DE "de" T_MODE_SP "sp"
|
%token T_MODE_AF "af" /* T_MODE_BC "bc" T_MODE_DE "de" */ T_MODE_SP "sp"
|
||||||
%token T_MODE_HL "hl" T_MODE_HL_DEC "hld/hl-" T_MODE_HL_INC "hli/hl+"
|
%token T_MODE_HL_START "н∠( ᐛ 」∠)_" T_MODE_HL_DEC "hld/hl-" T_MODE_HL_INC "hli/hl+"
|
||||||
%token T_CC_NZ "nz" T_CC_Z "z" T_CC_NC "nc" // There is no T_CC_C, only T_TOKEN_C
|
%token T_CC_NZ "nz" T_CC_Z "z" T_CC_NC "nc" T_CC_C "c"
|
||||||
|
|
||||||
%type <constValue> reg_r
|
%type <constValue> reg_r
|
||||||
%type <constValue> reg_ss
|
%type <constValue> reg_ss
|
||||||
%type <constValue> reg_rr
|
%type <constValue> reg_rr
|
||||||
%type <constValue> reg_tt
|
%type <constValue> reg_tt
|
||||||
|
%type <constValue> ccode_expr
|
||||||
%type <constValue> ccode
|
%type <constValue> ccode
|
||||||
%type <expr> op_a_n
|
%type <expr> op_a_n
|
||||||
%type <constValue> op_a_r
|
%type <constValue> op_a_r
|
||||||
@@ -684,8 +709,26 @@ plain_directive : label
|
|||||||
|
|
||||||
line : plain_directive endofline
|
line : plain_directive endofline
|
||||||
| line_directive /* Directives that manage newlines themselves */
|
| line_directive /* Directives that manage newlines themselves */
|
||||||
| error endofline { /* Continue parsing the next line on a syntax error */
|
/* Continue parsing the next line on a syntax error */
|
||||||
|
| error {
|
||||||
|
lexer_SetMode(LEXER_NORMAL);
|
||||||
|
lexer_ToggleStringExpansion(true);
|
||||||
|
} endofline {
|
||||||
fstk_StopRept();
|
fstk_StopRept();
|
||||||
|
yyerrok;
|
||||||
|
}
|
||||||
|
/* Hint about unindented macros parsed as labels */
|
||||||
|
| T_LABEL error {
|
||||||
|
lexer_SetMode(LEXER_NORMAL);
|
||||||
|
lexer_ToggleStringExpansion(true);
|
||||||
|
} endofline {
|
||||||
|
struct Symbol *macro = sym_FindExactSymbol($1);
|
||||||
|
|
||||||
|
if (macro && macro->type == SYM_MACRO)
|
||||||
|
fprintf(stderr,
|
||||||
|
" To invoke `%s` as a macro it must be indented\n", $1);
|
||||||
|
fstk_StopRept();
|
||||||
|
yyerrok;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -870,13 +913,27 @@ directive : endc
|
|||||||
trailing_comma : %empty | T_COMMA
|
trailing_comma : %empty | T_COMMA
|
||||||
;
|
;
|
||||||
|
|
||||||
|
compoundeq : T_POP_ADDEQ { $$ = RPN_ADD; }
|
||||||
|
| T_POP_SUBEQ { $$ = RPN_SUB; }
|
||||||
|
| T_POP_MULEQ { $$ = RPN_MUL; }
|
||||||
|
| T_POP_DIVEQ { $$ = RPN_DIV; }
|
||||||
|
| T_POP_MODEQ { $$ = RPN_MOD; }
|
||||||
|
| T_POP_XOREQ { $$ = RPN_XOR; }
|
||||||
|
| T_POP_OREQ { $$ = RPN_OR; }
|
||||||
|
| T_POP_ANDEQ { $$ = RPN_AND; }
|
||||||
|
| T_POP_SHLEQ { $$ = RPN_SHL; }
|
||||||
|
| T_POP_SHREQ { $$ = RPN_SHR; }
|
||||||
|
;
|
||||||
|
|
||||||
equ : T_LABEL T_POP_EQU const { sym_AddEqu($1, $3); }
|
equ : T_LABEL T_POP_EQU const { sym_AddEqu($1, $3); }
|
||||||
;
|
;
|
||||||
|
|
||||||
set_or_equal : T_POP_SET | T_POP_EQUAL
|
set : T_LABEL T_POP_EQUAL const { sym_AddVar($1, $3); }
|
||||||
;
|
| T_LABEL compoundeq const { compoundAssignment($1, $2, $3); }
|
||||||
|
| T_LABEL T_POP_SET const {
|
||||||
set : T_LABEL set_or_equal const { sym_AddSet($1, $3); }
|
warning(WARNING_OBSOLETE, "`SET` for variables is deprecated; use `=`\n");
|
||||||
|
sym_AddVar($1, $3);
|
||||||
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
equs : T_LABEL T_POP_EQUS string { sym_AddString($1, $3); }
|
equs : T_LABEL T_POP_EQUS string { sym_AddString($1, $3); }
|
||||||
@@ -884,19 +941,19 @@ equs : T_LABEL T_POP_EQUS string { sym_AddString($1, $3); }
|
|||||||
|
|
||||||
rb : T_LABEL T_POP_RB rs_uconst {
|
rb : T_LABEL T_POP_RB rs_uconst {
|
||||||
sym_AddEqu($1, sym_GetConstantValue("_RS"));
|
sym_AddEqu($1, sym_GetConstantValue("_RS"));
|
||||||
sym_AddSet("_RS", sym_GetConstantValue("_RS") + $3);
|
sym_AddVar("_RS", sym_GetConstantValue("_RS") + $3);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
rw : T_LABEL T_POP_RW rs_uconst {
|
rw : T_LABEL T_POP_RW rs_uconst {
|
||||||
sym_AddEqu($1, sym_GetConstantValue("_RS"));
|
sym_AddEqu($1, sym_GetConstantValue("_RS"));
|
||||||
sym_AddSet("_RS", sym_GetConstantValue("_RS") + 2 * $3);
|
sym_AddVar("_RS", sym_GetConstantValue("_RS") + 2 * $3);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
rl : T_LABEL T_Z80_RL rs_uconst {
|
rl : T_LABEL T_Z80_RL rs_uconst {
|
||||||
sym_AddEqu($1, sym_GetConstantValue("_RS"));
|
sym_AddEqu($1, sym_GetConstantValue("_RS"));
|
||||||
sym_AddSet("_RS", sym_GetConstantValue("_RS") + 4 * $3);
|
sym_AddVar("_RS", sym_GetConstantValue("_RS") + 4 * $3);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -1060,15 +1117,13 @@ macrodef : T_POP_MACRO {
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
rsset : T_POP_RSSET uconst { sym_AddSet("_RS", $2); }
|
rsset : T_POP_RSSET uconst { sym_AddVar("_RS", $2); }
|
||||||
;
|
;
|
||||||
|
|
||||||
rsreset : T_POP_RSRESET { sym_AddSet("_RS", 0); }
|
rsreset : T_POP_RSRESET { sym_AddVar("_RS", 0); }
|
||||||
;
|
;
|
||||||
|
|
||||||
rs_uconst : %empty {
|
rs_uconst : %empty { $$ = 1; }
|
||||||
$$ = 1;
|
|
||||||
}
|
|
||||||
| uconst
|
| uconst
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -1090,14 +1145,10 @@ ds : T_POP_DS uconst { sect_Skip($2, true); }
|
|||||||
|
|
||||||
ds_args : reloc_8bit {
|
ds_args : reloc_8bit {
|
||||||
initDsArgList(&$$);
|
initDsArgList(&$$);
|
||||||
size_t i = nextDsArgListIndex(&$$);
|
appendDsArgList(&$$, &$1);
|
||||||
|
|
||||||
$$.args[i] = $1;
|
|
||||||
}
|
}
|
||||||
| ds_args T_COMMA reloc_8bit {
|
| ds_args T_COMMA reloc_8bit {
|
||||||
size_t i = nextDsArgListIndex(&$1);
|
appendDsArgList(&$1, &$3);
|
||||||
|
|
||||||
$1.args[i] = $3;
|
|
||||||
$$ = $1;
|
$$ = $1;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@@ -1114,50 +1165,48 @@ dl : T_POP_DL { sect_Skip(4, false); }
|
|||||||
| T_POP_DL constlist_32bit trailing_comma
|
| T_POP_DL constlist_32bit trailing_comma
|
||||||
;
|
;
|
||||||
|
|
||||||
def_equ : def_id T_POP_EQU const {
|
def_equ : def_id T_POP_EQU const { sym_AddEqu($1, $3); }
|
||||||
sym_AddEqu($1, $3);
|
|
||||||
}
|
|
||||||
;
|
;
|
||||||
|
|
||||||
redef_equ : redef_id T_POP_EQU const {
|
redef_equ : redef_id T_POP_EQU const { sym_RedefEqu($1, $3); }
|
||||||
sym_RedefEqu($1, $3);
|
|
||||||
}
|
|
||||||
;
|
;
|
||||||
|
|
||||||
def_set : def_id set_or_equal const {
|
def_set : def_id T_POP_EQUAL const { sym_AddVar($1, $3); }
|
||||||
sym_AddSet($1, $3);
|
| redef_id T_POP_EQUAL const { sym_AddVar($1, $3); }
|
||||||
|
| def_id compoundeq const { compoundAssignment($1, $2, $3); }
|
||||||
|
| redef_id compoundeq const { compoundAssignment($1, $2, $3); }
|
||||||
|
| def_id T_POP_SET const {
|
||||||
|
warning(WARNING_OBSOLETE, "`SET` for variables is deprecated; use `=`\n");
|
||||||
|
sym_AddVar($1, $3);
|
||||||
}
|
}
|
||||||
| redef_id set_or_equal const {
|
| redef_id T_POP_SET const {
|
||||||
sym_AddSet($1, $3);
|
warning(WARNING_OBSOLETE, "`SET` for variables is deprecated; use `=`\n");
|
||||||
|
sym_AddVar($1, $3);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
def_rb : def_id T_POP_RB rs_uconst {
|
def_rb : def_id T_POP_RB rs_uconst {
|
||||||
sym_AddEqu($1, sym_GetConstantValue("_RS"));
|
sym_AddEqu($1, sym_GetConstantValue("_RS"));
|
||||||
sym_AddSet("_RS", sym_GetConstantValue("_RS") + $3);
|
sym_AddVar("_RS", sym_GetConstantValue("_RS") + $3);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
def_rw : def_id T_POP_RW rs_uconst {
|
def_rw : def_id T_POP_RW rs_uconst {
|
||||||
sym_AddEqu($1, sym_GetConstantValue("_RS"));
|
sym_AddEqu($1, sym_GetConstantValue("_RS"));
|
||||||
sym_AddSet("_RS", sym_GetConstantValue("_RS") + 2 * $3);
|
sym_AddVar("_RS", sym_GetConstantValue("_RS") + 2 * $3);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
def_rl : def_id T_Z80_RL rs_uconst {
|
def_rl : def_id T_Z80_RL rs_uconst {
|
||||||
sym_AddEqu($1, sym_GetConstantValue("_RS"));
|
sym_AddEqu($1, sym_GetConstantValue("_RS"));
|
||||||
sym_AddSet("_RS", sym_GetConstantValue("_RS") + 4 * $3);
|
sym_AddVar("_RS", sym_GetConstantValue("_RS") + 4 * $3);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
def_equs : def_id T_POP_EQUS string {
|
def_equs : def_id T_POP_EQUS string { sym_AddString($1, $3); }
|
||||||
sym_AddString($1, $3);
|
|
||||||
}
|
|
||||||
;
|
;
|
||||||
|
|
||||||
redef_equs : redef_id T_POP_EQUS string {
|
redef_equs : redef_id T_POP_EQUS string { sym_RedefString($1, $3); }
|
||||||
sym_RedefString($1, $3);
|
|
||||||
}
|
|
||||||
;
|
;
|
||||||
|
|
||||||
purge : T_POP_PURGE {
|
purge : T_POP_PURGE {
|
||||||
@@ -1260,13 +1309,13 @@ printv : T_POP_PRINTV const {
|
|||||||
;
|
;
|
||||||
|
|
||||||
printi : T_POP_PRINTI const {
|
printi : T_POP_PRINTI const {
|
||||||
warning(WARNING_OBSOLETE, "`PRINTI` is deprecated; use `PRINT` with `STRFMT`\n");
|
warning(WARNING_OBSOLETE, "`PRINTI` is deprecated; use `PRINT` with `STRFMT` \"%%d\"\n");
|
||||||
printf("%" PRId32, $2);
|
printf("%" PRId32, $2);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
printf : T_POP_PRINTF const {
|
printf : T_POP_PRINTF const {
|
||||||
warning(WARNING_OBSOLETE, "`PRINTF` is deprecated; use `PRINT` with `STRFMT`\n");
|
warning(WARNING_OBSOLETE, "`PRINTF` is deprecated; use `PRINT` with `STRFMT` \"%%f\"\n");
|
||||||
fix_Print($2);
|
fix_Print($2);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@@ -1292,7 +1341,7 @@ constlist_8bit_entry : reloc_8bit_no_str {
|
|||||||
}
|
}
|
||||||
| string {
|
| string {
|
||||||
uint8_t *output = malloc(strlen($1)); /* Cannot be larger than that */
|
uint8_t *output = malloc(strlen($1)); /* Cannot be larger than that */
|
||||||
int32_t length = charmap_Convert($1, output);
|
size_t length = charmap_Convert($1, output);
|
||||||
|
|
||||||
sect_AbsByteGroup(output, length);
|
sect_AbsByteGroup(output, length);
|
||||||
free(output);
|
free(output);
|
||||||
@@ -1308,7 +1357,7 @@ constlist_16bit_entry : reloc_16bit_no_str {
|
|||||||
}
|
}
|
||||||
| string {
|
| string {
|
||||||
uint8_t *output = malloc(strlen($1)); /* Cannot be larger than that */
|
uint8_t *output = malloc(strlen($1)); /* Cannot be larger than that */
|
||||||
int32_t length = charmap_Convert($1, output);
|
size_t length = charmap_Convert($1, output);
|
||||||
|
|
||||||
sect_AbsWordGroup(output, length);
|
sect_AbsWordGroup(output, length);
|
||||||
free(output);
|
free(output);
|
||||||
@@ -1325,7 +1374,7 @@ constlist_32bit_entry : relocexpr_no_str {
|
|||||||
| string {
|
| string {
|
||||||
// Charmaps cannot increase the length of a string
|
// Charmaps cannot increase the length of a string
|
||||||
uint8_t *output = malloc(strlen($1));
|
uint8_t *output = malloc(strlen($1));
|
||||||
int32_t length = charmap_Convert($1, output);
|
size_t length = charmap_Convert($1, output);
|
||||||
|
|
||||||
sect_AbsLongGroup(output, length);
|
sect_AbsLongGroup(output, length);
|
||||||
free(output);
|
free(output);
|
||||||
@@ -1371,7 +1420,7 @@ relocexpr : relocexpr_no_str
|
|||||||
| string {
|
| string {
|
||||||
// Charmaps cannot increase the length of a string
|
// Charmaps cannot increase the length of a string
|
||||||
uint8_t *output = malloc(strlen($1));
|
uint8_t *output = malloc(strlen($1));
|
||||||
int32_t length = charmap_Convert($1, output);
|
uint32_t length = charmap_Convert($1, output);
|
||||||
uint32_t r = str2int2(output, length);
|
uint32_t r = str2int2(output, length);
|
||||||
|
|
||||||
free(output);
|
free(output);
|
||||||
@@ -1429,6 +1478,9 @@ relocexpr_no_str : scoped_anon_id { rpn_Symbol(&$$, $1); }
|
|||||||
| relocexpr T_OP_SHR relocexpr {
|
| relocexpr T_OP_SHR relocexpr {
|
||||||
rpn_BinaryOp(RPN_SHR, &$$, &$1, &$3);
|
rpn_BinaryOp(RPN_SHR, &$$, &$1, &$3);
|
||||||
}
|
}
|
||||||
|
| relocexpr T_OP_USHR relocexpr {
|
||||||
|
rpn_BinaryOp(RPN_USHR, &$$, &$1, &$3);
|
||||||
|
}
|
||||||
| relocexpr T_OP_MUL relocexpr {
|
| relocexpr T_OP_MUL relocexpr {
|
||||||
rpn_BinaryOp(RPN_MUL, &$$, &$1, &$3);
|
rpn_BinaryOp(RPN_MUL, &$$, &$1, &$3);
|
||||||
}
|
}
|
||||||
@@ -1509,12 +1561,12 @@ relocexpr_no_str : scoped_anon_id { rpn_Symbol(&$$, $1); }
|
|||||||
rpn_Number(&$$, strcmp($3, $5));
|
rpn_Number(&$$, strcmp($3, $5));
|
||||||
}
|
}
|
||||||
| T_OP_STRIN T_LPAREN string T_COMMA string T_RPAREN {
|
| T_OP_STRIN T_LPAREN string T_COMMA string T_RPAREN {
|
||||||
char *p = strstr($3, $5);
|
char const *p = strstr($3, $5);
|
||||||
|
|
||||||
rpn_Number(&$$, p ? p - $3 + 1 : 0);
|
rpn_Number(&$$, p ? p - $3 + 1 : 0);
|
||||||
}
|
}
|
||||||
| T_OP_STRRIN T_LPAREN string T_COMMA string T_RPAREN {
|
| T_OP_STRRIN T_LPAREN string T_COMMA string T_RPAREN {
|
||||||
char *p = strrstr($3, $5);
|
char const *p = strrstr($3, $5);
|
||||||
|
|
||||||
rpn_Number(&$$, p ? p - $3 + 1 : 0);
|
rpn_Number(&$$, p ? p - $3 + 1 : 0);
|
||||||
}
|
}
|
||||||
@@ -1718,6 +1770,7 @@ cpu_command : z80_adc
|
|||||||
| z80_sub
|
| z80_sub
|
||||||
| z80_swap
|
| z80_swap
|
||||||
| z80_xor
|
| z80_xor
|
||||||
|
| T_OWO { fatalerror("*BONK* go to horny jail\n"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
z80_adc : T_Z80_ADC op_a_n {
|
z80_adc : T_Z80_ADC op_a_n {
|
||||||
@@ -1757,7 +1810,7 @@ z80_call : T_Z80_CALL reloc_16bit {
|
|||||||
sect_AbsByte(0xCD);
|
sect_AbsByte(0xCD);
|
||||||
sect_RelWord(&$2, 1);
|
sect_RelWord(&$2, 1);
|
||||||
}
|
}
|
||||||
| T_Z80_CALL ccode T_COMMA reloc_16bit {
|
| T_Z80_CALL ccode_expr T_COMMA reloc_16bit {
|
||||||
sect_AbsByte(0xC4 | ($2 << 3));
|
sect_AbsByte(0xC4 | ($2 << 3));
|
||||||
sect_RelWord(&$4, 1);
|
sect_RelWord(&$4, 1);
|
||||||
}
|
}
|
||||||
@@ -1804,7 +1857,7 @@ z80_jp : T_Z80_JP reloc_16bit {
|
|||||||
sect_AbsByte(0xC3);
|
sect_AbsByte(0xC3);
|
||||||
sect_RelWord(&$2, 1);
|
sect_RelWord(&$2, 1);
|
||||||
}
|
}
|
||||||
| T_Z80_JP ccode T_COMMA reloc_16bit {
|
| T_Z80_JP ccode_expr T_COMMA reloc_16bit {
|
||||||
sect_AbsByte(0xC2 | ($2 << 3));
|
sect_AbsByte(0xC2 | ($2 << 3));
|
||||||
sect_RelWord(&$4, 1);
|
sect_RelWord(&$4, 1);
|
||||||
}
|
}
|
||||||
@@ -1817,7 +1870,7 @@ z80_jr : T_Z80_JR reloc_16bit {
|
|||||||
sect_AbsByte(0x18);
|
sect_AbsByte(0x18);
|
||||||
sect_PCRelByte(&$2, 1);
|
sect_PCRelByte(&$2, 1);
|
||||||
}
|
}
|
||||||
| T_Z80_JR ccode T_COMMA reloc_16bit {
|
| T_Z80_JR ccode_expr T_COMMA reloc_16bit {
|
||||||
sect_AbsByte(0x20 | ($2 << 3));
|
sect_AbsByte(0x20 | ($2 << 3));
|
||||||
sect_PCRelByte(&$4, 1);
|
sect_PCRelByte(&$4, 1);
|
||||||
}
|
}
|
||||||
@@ -1999,7 +2052,7 @@ z80_res : T_Z80_RES const_3bit T_COMMA reg_r {
|
|||||||
;
|
;
|
||||||
|
|
||||||
z80_ret : T_Z80_RET { sect_AbsByte(0xC9); }
|
z80_ret : T_Z80_RET { sect_AbsByte(0xC9); }
|
||||||
| T_Z80_RET ccode { sect_AbsByte(0xC0 | ($2 << 3)); }
|
| T_Z80_RET ccode_expr { sect_AbsByte(0xC0 | ($2 << 3)); }
|
||||||
;
|
;
|
||||||
|
|
||||||
z80_reti : T_Z80_RETI { sect_AbsByte(0xD9); }
|
z80_reti : T_Z80_RETI { sect_AbsByte(0xD9); }
|
||||||
@@ -2126,7 +2179,7 @@ op_a_n : reloc_8bit
|
|||||||
| T_MODE_A T_COMMA reloc_8bit { $$ = $3; }
|
| T_MODE_A T_COMMA reloc_8bit { $$ = $3; }
|
||||||
;
|
;
|
||||||
|
|
||||||
T_MODE_A : T_TOKEN_A
|
T_MODE_A : T_LPAREN T_TOKEN_A T_RPAREN
|
||||||
| T_OP_HIGH T_LPAREN T_MODE_AF T_RPAREN
|
| T_OP_HIGH T_LPAREN T_MODE_AF T_RPAREN
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -2134,7 +2187,7 @@ T_MODE_B : T_TOKEN_B
|
|||||||
| T_OP_HIGH T_LPAREN T_MODE_BC T_RPAREN
|
| T_OP_HIGH T_LPAREN T_MODE_BC T_RPAREN
|
||||||
;
|
;
|
||||||
|
|
||||||
T_MODE_C : T_TOKEN_C
|
T_MODE_C : T_TOKEN_C T_CC_C T_RPAREN
|
||||||
| T_OP_LOW T_LPAREN T_MODE_BC T_RPAREN
|
| T_OP_LOW T_LPAREN T_MODE_BC T_RPAREN
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -2142,7 +2195,7 @@ T_MODE_D : T_TOKEN_D
|
|||||||
| T_OP_HIGH T_LPAREN T_MODE_DE T_RPAREN
|
| T_OP_HIGH T_LPAREN T_MODE_DE T_RPAREN
|
||||||
;
|
;
|
||||||
|
|
||||||
T_MODE_E : T_TOKEN_E
|
T_MODE_E : T_TOKEN_E T_RPAREN T_TOKEN_E_HEART
|
||||||
| T_OP_LOW T_LPAREN T_MODE_DE T_RPAREN
|
| T_OP_LOW T_LPAREN T_MODE_DE T_RPAREN
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -2150,14 +2203,29 @@ T_MODE_H : T_TOKEN_H
|
|||||||
| T_OP_HIGH T_LPAREN T_MODE_HL T_RPAREN
|
| T_OP_HIGH T_LPAREN T_MODE_HL T_RPAREN
|
||||||
;
|
;
|
||||||
|
|
||||||
T_MODE_L : T_TOKEN_L
|
T_MODE_L : T_TOKEN_L_ARM T_TOKEN_L_FACE T_TOKEN_L_BODY T_RPAREN T_TOKEN_L_LEG
|
||||||
| T_OP_LOW T_LPAREN T_MODE_HL T_RPAREN
|
| T_OP_LOW T_LPAREN T_MODE_HL T_RPAREN
|
||||||
;
|
;
|
||||||
|
|
||||||
|
T_MODE_BC : T_TOKEN_B T_TOKEN_C T_CC_C T_RPAREN
|
||||||
|
;
|
||||||
|
|
||||||
|
T_MODE_DE : T_TOKEN_D T_TOKEN_E T_RPAREN T_TOKEN_E_HEART
|
||||||
|
;
|
||||||
|
|
||||||
|
T_MODE_HL : T_MODE_HL_START T_TOKEN_L_FACE T_TOKEN_L_BODY T_RPAREN T_TOKEN_L_LEG
|
||||||
|
;
|
||||||
|
|
||||||
|
ccode_expr : ccode
|
||||||
|
| T_OP_LOGICNOT ccode_expr {
|
||||||
|
$$ = $2 ^ 1;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
ccode : T_CC_NZ { $$ = CC_NZ; }
|
ccode : T_CC_NZ { $$ = CC_NZ; }
|
||||||
| T_CC_Z { $$ = CC_Z; }
|
| T_CC_Z { $$ = CC_Z; }
|
||||||
| T_CC_NC { $$ = CC_NC; }
|
| T_CC_NC { $$ = CC_NC; }
|
||||||
| T_TOKEN_C { $$ = CC_C; }
|
| T_CC_C { $$ = CC_C; }
|
||||||
;
|
;
|
||||||
|
|
||||||
reg_r : T_MODE_B { $$ = REG_B; }
|
reg_r : T_MODE_B { $$ = REG_B; }
|
||||||
@@ -2173,7 +2241,7 @@ reg_r : T_MODE_B { $$ = REG_B; }
|
|||||||
reg_tt : T_MODE_BC { $$ = REG_BC; }
|
reg_tt : T_MODE_BC { $$ = REG_BC; }
|
||||||
| T_MODE_DE { $$ = REG_DE; }
|
| T_MODE_DE { $$ = REG_DE; }
|
||||||
| T_MODE_HL { $$ = REG_HL; }
|
| T_MODE_HL { $$ = REG_HL; }
|
||||||
| T_MODE_AF { $$ = REG_AF; }
|
| T_LPAREN T_TOKEN_A T_RPAREN T_TOKEN_F { $$ = REG_AF; }
|
||||||
;
|
;
|
||||||
|
|
||||||
reg_ss : T_MODE_BC { $$ = REG_BC; }
|
reg_ss : T_MODE_BC { $$ = REG_BC; }
|
||||||
|
|||||||
@@ -214,7 +214,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 by -1, which yields itself due to integer overflow.
|
Warn when dividing the smallest negative integer (-2**31) 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
|
||||||
@@ -243,15 +243,37 @@ 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 Wno-truncation
|
.It Fl Wtruncation=
|
||||||
Warn when an implicit truncation (for example,
|
Warn when an implicit truncation (for example,
|
||||||
.Ic db )
|
.Ic db
|
||||||
loses some bits.
|
to an 8-bit value) loses some bits.
|
||||||
|
.Fl Wtruncation=0
|
||||||
|
or
|
||||||
|
.Fl Wno-truncation
|
||||||
|
disables this warning.
|
||||||
|
.Fl Wtruncation=1
|
||||||
|
warns when an N-bit value'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 Wno-user
|
.It Fl Wno-user
|
||||||
Warn when the
|
Warn when the
|
||||||
.Ic WARN
|
.Ic WARN
|
||||||
|
|||||||
800
src/asm/rgbasm.5
800
src/asm/rgbasm.5
File diff suppressed because it is too large
Load Diff
101
src/asm/rpn.c
101
src/asm/rpn.c
@@ -205,28 +205,40 @@ void rpn_SizeOfSection(struct Expression *expr, char const *sectionName)
|
|||||||
{
|
{
|
||||||
rpn_Init(expr);
|
rpn_Init(expr);
|
||||||
|
|
||||||
makeUnknown(expr, "Section \"%s\"'s size is not known", sectionName);
|
struct Section *section = sect_FindSectionByName(sectionName);
|
||||||
|
|
||||||
size_t nameLen = strlen(sectionName) + 1; /* Room for NUL! */
|
if (section && sect_IsSizeKnown(section)) {
|
||||||
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
expr->val = section->size;
|
||||||
|
} else {
|
||||||
|
makeUnknown(expr, "Section \"%s\"'s size is not known", sectionName);
|
||||||
|
|
||||||
expr->rpnPatchSize += nameLen + 1;
|
size_t nameLen = strlen(sectionName) + 1; /* Room for NUL! */
|
||||||
*ptr++ = RPN_SIZEOF_SECT;
|
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
||||||
memcpy(ptr, sectionName, nameLen);
|
|
||||||
|
expr->rpnPatchSize += nameLen + 1;
|
||||||
|
*ptr++ = RPN_SIZEOF_SECT;
|
||||||
|
memcpy(ptr, sectionName, nameLen);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void rpn_StartOfSection(struct Expression *expr, char const *sectionName)
|
void rpn_StartOfSection(struct Expression *expr, char const *sectionName)
|
||||||
{
|
{
|
||||||
rpn_Init(expr);
|
rpn_Init(expr);
|
||||||
|
|
||||||
makeUnknown(expr, "Section \"%s\"'s start is not known", sectionName);
|
struct Section *section = sect_FindSectionByName(sectionName);
|
||||||
|
|
||||||
size_t nameLen = strlen(sectionName) + 1; /* Room for NUL! */
|
if (section && section->org != (uint32_t)-1) {
|
||||||
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
expr->val = section->org;
|
||||||
|
} else {
|
||||||
|
makeUnknown(expr, "Section \"%s\"'s start is not known", sectionName);
|
||||||
|
|
||||||
expr->rpnPatchSize += nameLen + 1;
|
size_t nameLen = strlen(sectionName) + 1; /* Room for NUL! */
|
||||||
*ptr++ = RPN_STARTOF_SECT;
|
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
||||||
memcpy(ptr, sectionName, nameLen);
|
|
||||||
|
expr->rpnPatchSize += nameLen + 1;
|
||||||
|
*ptr++ = RPN_STARTOF_SECT;
|
||||||
|
memcpy(ptr, sectionName, nameLen);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void rpn_CheckHRAM(struct Expression *expr, const struct Expression *src)
|
void rpn_CheckHRAM(struct Expression *expr, const struct Expression *src)
|
||||||
@@ -272,8 +284,10 @@ void rpn_CheckNBit(struct Expression const *expr, uint8_t n)
|
|||||||
if (rpn_isKnown(expr)) {
|
if (rpn_isKnown(expr)) {
|
||||||
int32_t val = expr->val;
|
int32_t val = expr->val;
|
||||||
|
|
||||||
if (val < -(1 << (n - 1)) || val >= 1 << n)
|
if (val <= -(1 << n) || val >= 1 << n)
|
||||||
warning(WARNING_TRUNCATION, "Expression must be %u-bit\n", 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,7 +317,7 @@ 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 *)expr->rpn + 1);
|
return sym_FindScopedSymbol((char const *)expr->rpn + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *sym)
|
bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *sym)
|
||||||
@@ -325,10 +339,51 @@ 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;
|
||||||
@@ -407,6 +462,19 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
|||||||
|
|
||||||
expr->val = op_shift_right(src1->val, src2->val);
|
expr->val = op_shift_right(src1->val, src2->val);
|
||||||
break;
|
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;
|
||||||
case RPN_MUL:
|
case RPN_MUL:
|
||||||
expr->val = uleft * uright;
|
expr->val = uleft * uright;
|
||||||
break;
|
break;
|
||||||
@@ -463,6 +531,9 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
|||||||
|
|
||||||
expr->val = sym_GetValue(symbol1) - sym_GetValue(symbol2);
|
expr->val = sym_GetValue(symbol1) - sym_GetValue(symbol2);
|
||||||
expr->isKnown = true;
|
expr->isKnown = true;
|
||||||
|
} else if (op == RPN_AND && (constMaskVal = tryConstMask(src1, src2)) != -1) {
|
||||||
|
expr->val = constMaskVal;
|
||||||
|
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 */
|
||||||
|
|
||||||
|
|||||||
@@ -15,17 +15,24 @@
|
|||||||
#include "asm/symbol.h"
|
#include "asm/symbol.h"
|
||||||
#include "asm/warning.h"
|
#include "asm/warning.h"
|
||||||
|
|
||||||
#include "extern/err.h"
|
#include "error.h"
|
||||||
#include "platform.h" // strdup
|
#include "platform.h" // strdup
|
||||||
|
|
||||||
uint8_t fillByte;
|
uint8_t fillByte;
|
||||||
|
|
||||||
|
struct UnionStackEntry {
|
||||||
|
uint32_t start;
|
||||||
|
uint32_t size;
|
||||||
|
struct UnionStackEntry *next;
|
||||||
|
} *unionStack = NULL;
|
||||||
|
|
||||||
struct SectionStackEntry {
|
struct SectionStackEntry {
|
||||||
struct Section *section;
|
struct Section *section;
|
||||||
struct Section *loadSection;
|
struct Section *loadSection;
|
||||||
char const *scope; /* Section's symbol scope */
|
char const *scope; /* Section's symbol scope */
|
||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
int32_t loadOffset;
|
int32_t loadOffset;
|
||||||
|
struct UnionStackEntry *unionStack;
|
||||||
struct SectionStackEntry *next;
|
struct SectionStackEntry *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -35,12 +42,6 @@ struct Section *currentSection = NULL;
|
|||||||
static struct Section *currentLoadSection = NULL;
|
static struct Section *currentLoadSection = NULL;
|
||||||
int32_t loadOffset; /* Offset into the LOAD section's parent (see sect_GetOutputOffset) */
|
int32_t loadOffset; /* Offset into the LOAD section's parent (see sect_GetOutputOffset) */
|
||||||
|
|
||||||
struct UnionStackEntry {
|
|
||||||
uint32_t start;
|
|
||||||
uint32_t size;
|
|
||||||
struct UnionStackEntry *next;
|
|
||||||
} *unionStack = NULL;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A quick check to see if we have an initialized section
|
* A quick check to see if we have an initialized section
|
||||||
*/
|
*/
|
||||||
@@ -109,7 +110,7 @@ attr_(warn_unused_result) static bool reserveSpace(uint32_t delta_size)
|
|||||||
&& (!currentLoadSection || currentLoadSection->size != UINT32_MAX);
|
&& (!currentLoadSection || currentLoadSection->size != UINT32_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Section *sect_FindSectionByName(const char *name)
|
struct Section *sect_FindSectionByName(char const *name)
|
||||||
{
|
{
|
||||||
for (struct Section *sect = sectionList; sect; sect = sect->next) {
|
for (struct Section *sect = sectionList; sect; sect = sect->next) {
|
||||||
if (strcmp(name, sect->name) == 0)
|
if (strcmp(name, sect->name) == 0)
|
||||||
@@ -159,9 +160,9 @@ static unsigned int mergeSectUnion(struct Section *sect, enum SectionType type,
|
|||||||
/* Check if alignment offsets are compatible */
|
/* Check if alignment offsets are compatible */
|
||||||
} else if ((alignOffset & mask(sect->align))
|
} else if ((alignOffset & mask(sect->align))
|
||||||
!= (sect->alignOfs & mask(alignment))) {
|
!= (sect->alignOfs & mask(alignment))) {
|
||||||
fail("Section already declared with incompatible %" PRIu8
|
fail("Section already declared with incompatible %u"
|
||||||
"-byte alignment (offset %" PRIu16 ")\n",
|
"-byte alignment (offset %" PRIu16 ")\n",
|
||||||
sect->align, sect->alignOfs);
|
1u << sect->align, sect->alignOfs);
|
||||||
} else if (alignment > sect->align) {
|
} else if (alignment > sect->align) {
|
||||||
// If the section is not fixed, its alignment is the largest of both
|
// If the section is not fixed, its alignment is the largest of both
|
||||||
sect->align = alignment;
|
sect->align = alignment;
|
||||||
@@ -212,9 +213,9 @@ static unsigned int mergeFragments(struct Section *sect, enum SectionType type,
|
|||||||
PRIx32 "\n", sect->org);
|
PRIx32 "\n", sect->org);
|
||||||
/* Check if alignment offsets are compatible */
|
/* Check if alignment offsets are compatible */
|
||||||
} else if ((curOfs & mask(sect->align)) != (sect->alignOfs & mask(alignment))) {
|
} else if ((curOfs & mask(sect->align)) != (sect->alignOfs & mask(alignment))) {
|
||||||
fail("Section already declared with incompatible %" PRIu8
|
fail("Section already declared with incompatible %u"
|
||||||
"-byte alignment (offset %" PRIu16 ")\n",
|
"-byte alignment (offset %" PRIu16 ")\n",
|
||||||
sect->align, sect->alignOfs);
|
1u << sect->align, sect->alignOfs);
|
||||||
} else if (alignment > sect->align) {
|
} else if (alignment > sect->align) {
|
||||||
// If the section is not fixed, its alignment is the largest of both
|
// If the section is not fixed, its alignment is the largest of both
|
||||||
sect->align = alignment;
|
sect->align = alignment;
|
||||||
@@ -418,6 +419,7 @@ void sect_NewSection(char const *name, uint32_t type, uint32_t org,
|
|||||||
|
|
||||||
changeSection();
|
changeSection();
|
||||||
curOffset = mod == SECTION_UNION ? 0 : sect->size;
|
curOffset = mod == SECTION_UNION ? 0 : sect->size;
|
||||||
|
loadOffset = 0; // This is still used when checking for section size overflow!
|
||||||
currentSection = sect;
|
currentSection = sect;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,6 +429,11 @@ void sect_NewSection(char const *name, uint32_t type, uint32_t org,
|
|||||||
void sect_SetLoadSection(char const *name, uint32_t type, uint32_t org,
|
void sect_SetLoadSection(char const *name, uint32_t type, uint32_t org,
|
||||||
struct SectionSpec const *attribs, enum SectionModifier mod)
|
struct SectionSpec const *attribs, enum SectionModifier mod)
|
||||||
{
|
{
|
||||||
|
// Important info: currently, UNION and LOAD cannot interact, since UNION is prohibited in
|
||||||
|
// "code" sections, whereas LOAD is restricted to them.
|
||||||
|
// Therefore, any interactions are NOT TESTED, so lift either of those restrictions at
|
||||||
|
// your own peril! ^^
|
||||||
|
|
||||||
if (!checkcodesection())
|
if (!checkcodesection())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -547,6 +554,11 @@ static void createPatch(enum PatchType type, struct Expression const *expr, uint
|
|||||||
|
|
||||||
void sect_StartUnion(void)
|
void sect_StartUnion(void)
|
||||||
{
|
{
|
||||||
|
// Important info: currently, UNION and LOAD cannot interact, since UNION is prohibited in
|
||||||
|
// "code" sections, whereas LOAD is restricted to them.
|
||||||
|
// Therefore, any interactions are NOT TESTED, so lift either of those restrictions at
|
||||||
|
// your own peril! ^^
|
||||||
|
|
||||||
if (!currentSection) {
|
if (!currentSection) {
|
||||||
error("UNIONs must be inside a SECTION\n");
|
error("UNIONs must be inside a SECTION\n");
|
||||||
return;
|
return;
|
||||||
@@ -616,7 +628,7 @@ void sect_AbsByte(uint8_t b)
|
|||||||
writebyte(b);
|
writebyte(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sect_AbsByteGroup(uint8_t const *s, int32_t length)
|
void sect_AbsByteGroup(uint8_t const *s, size_t length)
|
||||||
{
|
{
|
||||||
if (!checkcodesection())
|
if (!checkcodesection())
|
||||||
return;
|
return;
|
||||||
@@ -627,7 +639,7 @@ void sect_AbsByteGroup(uint8_t const *s, int32_t length)
|
|||||||
writebyte(*s++);
|
writebyte(*s++);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sect_AbsWordGroup(uint8_t const *s, int32_t length)
|
void sect_AbsWordGroup(uint8_t const *s, size_t length)
|
||||||
{
|
{
|
||||||
if (!checkcodesection())
|
if (!checkcodesection())
|
||||||
return;
|
return;
|
||||||
@@ -638,7 +650,7 @@ void sect_AbsWordGroup(uint8_t const *s, int32_t length)
|
|||||||
writeword(*s++);
|
writeword(*s++);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sect_AbsLongGroup(uint8_t const *s, int32_t length)
|
void sect_AbsLongGroup(uint8_t const *s, size_t length)
|
||||||
{
|
{
|
||||||
if (!checkcodesection())
|
if (!checkcodesection())
|
||||||
return;
|
return;
|
||||||
@@ -652,7 +664,7 @@ void sect_AbsLongGroup(uint8_t const *s, int32_t length)
|
|||||||
/*
|
/*
|
||||||
* Skip this many bytes
|
* Skip this many bytes
|
||||||
*/
|
*/
|
||||||
void sect_Skip(int32_t skip, bool ds)
|
void sect_Skip(uint32_t skip, bool ds)
|
||||||
{
|
{
|
||||||
if (!checksection())
|
if (!checksection())
|
||||||
return;
|
return;
|
||||||
@@ -939,9 +951,7 @@ void sect_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
|
|||||||
(void)fgetc(f);
|
(void)fgetc(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t todo = length;
|
while (length--) {
|
||||||
|
|
||||||
while (todo--) {
|
|
||||||
int byte = fgetc(f);
|
int byte = fgetc(f);
|
||||||
|
|
||||||
if (byte != EOF) {
|
if (byte != EOF) {
|
||||||
@@ -950,7 +960,7 @@ void sect_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
|
|||||||
error("Error reading INCBIN file '%s': %s\n", s, strerror(errno));
|
error("Error reading INCBIN file '%s': %s\n", s, strerror(errno));
|
||||||
} else {
|
} else {
|
||||||
error("Premature end of file (%" PRId32 " bytes left to read)\n",
|
error("Premature end of file (%" PRId32 " bytes left to read)\n",
|
||||||
todo + 1);
|
length + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -963,22 +973,24 @@ cleanup:
|
|||||||
*/
|
*/
|
||||||
void sect_PushSection(void)
|
void sect_PushSection(void)
|
||||||
{
|
{
|
||||||
struct SectionStackEntry *sect = malloc(sizeof(*sect));
|
struct SectionStackEntry *entry = malloc(sizeof(*entry));
|
||||||
|
|
||||||
if (sect == NULL)
|
if (entry == NULL)
|
||||||
fatalerror("No memory for section stack: %s\n", strerror(errno));
|
fatalerror("No memory for section stack: %s\n", strerror(errno));
|
||||||
sect->section = currentSection;
|
entry->section = currentSection;
|
||||||
sect->loadSection = currentLoadSection;
|
entry->loadSection = currentLoadSection;
|
||||||
sect->scope = sym_GetCurrentSymbolScope();
|
entry->scope = sym_GetCurrentSymbolScope();
|
||||||
sect->offset = curOffset;
|
entry->offset = curOffset;
|
||||||
sect->loadOffset = loadOffset;
|
entry->loadOffset = loadOffset;
|
||||||
sect->next = sectionStack;
|
entry->unionStack = unionStack;
|
||||||
sectionStack = sect;
|
entry->next = sectionStack;
|
||||||
|
sectionStack = entry;
|
||||||
|
|
||||||
// Reset the section scope
|
// Reset the section scope
|
||||||
currentSection = NULL;
|
currentSection = NULL;
|
||||||
currentLoadSection = NULL;
|
currentLoadSection = NULL;
|
||||||
sym_SetCurrentSymbolScope(NULL);
|
sym_SetCurrentSymbolScope(NULL);
|
||||||
|
unionStack = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sect_PopSection(void)
|
void sect_PopSection(void)
|
||||||
@@ -989,16 +1001,35 @@ void sect_PopSection(void)
|
|||||||
if (currentLoadSection)
|
if (currentLoadSection)
|
||||||
fatalerror("Cannot change the section within a `LOAD` block!\n");
|
fatalerror("Cannot change the section within a `LOAD` block!\n");
|
||||||
|
|
||||||
struct SectionStackEntry *sect;
|
struct SectionStackEntry *entry = sectionStack;
|
||||||
|
|
||||||
sect = sectionStack;
|
|
||||||
changeSection();
|
changeSection();
|
||||||
currentSection = sect->section;
|
currentSection = entry->section;
|
||||||
currentLoadSection = sect->loadSection;
|
currentLoadSection = entry->loadSection;
|
||||||
sym_SetCurrentSymbolScope(sect->scope);
|
sym_SetCurrentSymbolScope(entry->scope);
|
||||||
curOffset = sect->offset;
|
curOffset = entry->offset;
|
||||||
loadOffset = sect->loadOffset;
|
loadOffset = entry->loadOffset;
|
||||||
|
unionStack = entry->unionStack;
|
||||||
|
|
||||||
sectionStack = sect->next;
|
sectionStack = entry->next;
|
||||||
free(sect);
|
free(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sect_IsSizeKnown(struct Section const NONNULL(sect))
|
||||||
|
{
|
||||||
|
// SECTION UNION and SECTION FRAGMENT can still grow
|
||||||
|
if (sect->modifier != SECTION_NORMAL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// The current section (or current load section if within one) is still growing
|
||||||
|
if (sect == currentSection || sect == currentLoadSection)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Any section on the stack is still growing
|
||||||
|
for (struct SectionStackEntry *stack = sectionStack; stack; stack = stack->next) {
|
||||||
|
if (stack->section && !strcmp(sect->name, stack->section->name))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,8 +29,7 @@
|
|||||||
#include "asm/util.h"
|
#include "asm/util.h"
|
||||||
#include "asm/warning.h"
|
#include "asm/warning.h"
|
||||||
|
|
||||||
#include "extern/err.h"
|
#include "error.h"
|
||||||
|
|
||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
@@ -96,6 +95,7 @@ static char const *Callback__FILE__(void)
|
|||||||
char const *fileName = fstk_GetFileName();
|
char const *fileName = fstk_GetFileName();
|
||||||
size_t j = 1;
|
size_t j = 1;
|
||||||
|
|
||||||
|
assert(fileName[0]);
|
||||||
/* The assertion above ensures the loop runs at least once */
|
/* The assertion above ensures the loop runs at least once */
|
||||||
for (size_t i = 0; fileName[i]; i++, j++) {
|
for (size_t i = 0; fileName[i]; i++, j++) {
|
||||||
/* Account for the extra backslash inserted below */
|
/* Account for the extra backslash inserted below */
|
||||||
@@ -244,18 +244,18 @@ struct Symbol *sym_FindUnscopedSymbol(char const *symName)
|
|||||||
|
|
||||||
struct Symbol *sym_FindScopedSymbol(char const *symName)
|
struct Symbol *sym_FindScopedSymbol(char const *symName)
|
||||||
{
|
{
|
||||||
char const *dotPtr = strchr(symName, '.');
|
char const *localName = strchr(symName, '.');
|
||||||
|
|
||||||
if (dotPtr) {
|
if (localName) {
|
||||||
if (strchr(dotPtr + 1, '.'))
|
if (strchr(localName + 1, '.'))
|
||||||
fatalerror("'%s' is a nonsensical reference to a nested local symbol\n",
|
fatalerror("'%s' is a nonsensical reference to a nested local symbol\n",
|
||||||
symName);
|
symName);
|
||||||
/* If auto-scoped local label, expand the name */
|
/* If auto-scoped local label, expand the name */
|
||||||
if (dotPtr == symName) { /* Meaning, the name begins with the dot */
|
if (localName == symName) { /* Meaning, the name begins with the dot */
|
||||||
char fullname[MAXSYMLEN + 1];
|
char fullName[MAXSYMLEN + 1];
|
||||||
|
|
||||||
fullSymbolName(fullname, sizeof(fullname), symName, labelScope);
|
fullSymbolName(fullName, sizeof(fullName), symName, labelScope);
|
||||||
return sym_FindExactSymbol(fullname);
|
return sym_FindExactSymbol(fullName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sym_FindExactSymbol(symName);
|
return sym_FindExactSymbol(symName);
|
||||||
@@ -475,15 +475,15 @@ struct Symbol *sym_RedefString(char const *symName, char const *value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Alter a SET symbol's value
|
* Alter a mutable symbol's value
|
||||||
*/
|
*/
|
||||||
struct Symbol *sym_AddSet(char const *symName, int32_t value)
|
struct Symbol *sym_AddVar(char const *symName, int32_t value)
|
||||||
{
|
{
|
||||||
struct Symbol *sym = sym_FindExactSymbol(symName);
|
struct Symbol *sym = sym_FindExactSymbol(symName);
|
||||||
|
|
||||||
if (!sym) {
|
if (!sym) {
|
||||||
sym = createsymbol(symName);
|
sym = createsymbol(symName);
|
||||||
} else if (sym_IsDefined(sym) && sym->type != SYM_SET) {
|
} else if (sym_IsDefined(sym) && sym->type != SYM_VAR) {
|
||||||
error("'%s' already defined as %s at ",
|
error("'%s' already defined as %s at ",
|
||||||
symName, sym->type == SYM_LABEL ? "label" : "constant");
|
symName, sym->type == SYM_LABEL ? "label" : "constant");
|
||||||
dumpFilename(sym);
|
dumpFilename(sym);
|
||||||
@@ -493,7 +493,7 @@ struct Symbol *sym_AddSet(char const *symName, int32_t value)
|
|||||||
updateSymbolFilename(sym);
|
updateSymbolFilename(sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
sym->type = SYM_SET;
|
sym->type = SYM_VAR;
|
||||||
sym->value = value;
|
sym->value = value;
|
||||||
|
|
||||||
return sym;
|
return sym;
|
||||||
@@ -532,7 +532,7 @@ static struct Symbol *addLabel(char const *symName)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a local (.name or Parent.name) relocatable symbol
|
* Add a local (`.name` or `Parent.name`) relocatable symbol
|
||||||
*/
|
*/
|
||||||
struct Symbol *sym_AddLocalLabel(char const *symName)
|
struct Symbol *sym_AddLocalLabel(char const *symName)
|
||||||
{
|
{
|
||||||
@@ -540,34 +540,40 @@ struct Symbol *sym_AddLocalLabel(char const *symName)
|
|||||||
error("Local label '%s' in main scope\n", symName);
|
error("Local label '%s' in main scope\n", symName);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
assert(!strchr(labelScope, '.')); /* Assuming no dots in `labelScope` */
|
||||||
|
|
||||||
char fullname[MAXSYMLEN + 1];
|
char fullName[MAXSYMLEN + 1];
|
||||||
|
char const *localName = strchr(symName, '.');
|
||||||
|
|
||||||
if (symName[0] == '.') {
|
assert(localName); /* There should be at least one dot in `symName` */
|
||||||
/* If symbol is of the form `.name`, expand to the full `Parent.name` name */
|
/* Check for something after the dot in `localName` */
|
||||||
fullSymbolName(fullname, sizeof(fullname), symName, labelScope);
|
if (localName[1] == '\0') {
|
||||||
symName = fullname; /* Use the expanded name instead */
|
fatalerror("'%s' is a nonsensical reference to an empty local label\n",
|
||||||
|
symName);
|
||||||
|
}
|
||||||
|
/* Check for more than one dot in `localName` */
|
||||||
|
if (strchr(localName + 1, '.'))
|
||||||
|
fatalerror("'%s' is a nonsensical reference to a nested local label\n",
|
||||||
|
symName);
|
||||||
|
|
||||||
|
if (localName == symName) {
|
||||||
|
/* Expand `symName` to the full `labelScope.symName` name */
|
||||||
|
fullSymbolName(fullName, sizeof(fullName), symName, labelScope);
|
||||||
|
symName = fullName;
|
||||||
} else {
|
} else {
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
|
||||||
/* Otherwise, check that `Parent` is in fact the current scope */
|
/* Find where `labelScope` and `symName` first differ */
|
||||||
while (labelScope[i] && symName[i] == labelScope[i])
|
while (labelScope[i] && symName[i] == labelScope[i])
|
||||||
i++;
|
i++;
|
||||||
/* Assuming no dots in `labelScope` */
|
|
||||||
assert(strchr(&symName[i], '.')); /* There should be at least one dot, though */
|
|
||||||
size_t parentLen = i + (strchr(&symName[i], '.') - symName);
|
|
||||||
|
|
||||||
/*
|
/* Check that `symName` starts with `labelScope` and then a '.' */
|
||||||
* Check that `labelScope[i]` ended the check, guaranteeing that `symName` is at
|
|
||||||
* least as long, and then that this was the entire `Parent` part of `symName`.
|
|
||||||
*/
|
|
||||||
if (labelScope[i] != '\0' || symName[i] != '.') {
|
if (labelScope[i] != '\0' || symName[i] != '.') {
|
||||||
|
size_t parentLen = localName - symName;
|
||||||
|
|
||||||
assert(parentLen <= INT_MAX);
|
assert(parentLen <= INT_MAX);
|
||||||
error("Not currently in the scope of '%.*s'\n", (int)parentLen, symName);
|
error("Not currently in the scope of '%.*s'\n", (int)parentLen, symName);
|
||||||
}
|
}
|
||||||
if (strchr(&symName[parentLen + 1], '.')) /* There will at least be a terminator */
|
|
||||||
fatalerror("'%s' is a nonsensical reference to a nested local label\n",
|
|
||||||
symName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return addLabel(symName);
|
return addLabel(symName);
|
||||||
@@ -735,7 +741,7 @@ void sym_Init(time_t now)
|
|||||||
__FILE__Symbol->type = SYM_EQUS;
|
__FILE__Symbol->type = SYM_EQUS;
|
||||||
__FILE__Symbol->strCallback = Callback__FILE__;
|
__FILE__Symbol->strCallback = Callback__FILE__;
|
||||||
|
|
||||||
sym_AddSet("_RS", 0)->isBuiltin = true;
|
sym_AddVar("_RS", 0)->isBuiltin = true;
|
||||||
|
|
||||||
#define addNumber(name, val) sym_AddEqu(name, val)->isBuiltin = true
|
#define addNumber(name, val) sym_AddEqu(name, val)->isBuiltin = true
|
||||||
#define addString(name, val) sym_AddString(name, val)->isBuiltin = true
|
#define addString(name, val) sym_AddString(name, val)->isBuiltin = true
|
||||||
@@ -754,7 +760,6 @@ void sym_Init(time_t now)
|
|||||||
now = 0;
|
now = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct tm *time_utc = gmtime(&now);
|
|
||||||
const struct tm *time_local = localtime(&now);
|
const struct tm *time_local = localtime(&now);
|
||||||
|
|
||||||
strftime(savedTIME, sizeof(savedTIME), "\"%H:%M:%S\"", time_local);
|
strftime(savedTIME, sizeof(savedTIME), "\"%H:%M:%S\"", time_local);
|
||||||
@@ -763,6 +768,8 @@ void sym_Init(time_t now)
|
|||||||
sizeof(savedTIMESTAMP_ISO8601_LOCAL), "\"%Y-%m-%dT%H:%M:%S%z\"",
|
sizeof(savedTIMESTAMP_ISO8601_LOCAL), "\"%Y-%m-%dT%H:%M:%S%z\"",
|
||||||
time_local);
|
time_local);
|
||||||
|
|
||||||
|
const struct tm *time_utc = gmtime(&now);
|
||||||
|
|
||||||
strftime(savedTIMESTAMP_ISO8601_UTC,
|
strftime(savedTIMESTAMP_ISO8601_UTC,
|
||||||
sizeof(savedTIMESTAMP_ISO8601_UTC), "\"%Y-%m-%dT%H:%M:%SZ\"",
|
sizeof(savedTIMESTAMP_ISO8601_UTC), "\"%Y-%m-%dT%H:%M:%SZ\"",
|
||||||
time_utc);
|
time_utc);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -17,11 +18,11 @@
|
|||||||
#include "asm/main.h"
|
#include "asm/main.h"
|
||||||
#include "asm/warning.h"
|
#include "asm/warning.h"
|
||||||
|
|
||||||
#include "extern/err.h"
|
#include "error.h"
|
||||||
|
|
||||||
unsigned int nbErrors = 0;
|
unsigned int nbErrors = 0;
|
||||||
|
|
||||||
static enum WarningState const defaultWarnings[NB_WARNINGS] = {
|
static const enum WarningState defaultWarnings[ARRAY_SIZE(warningStates)] = {
|
||||||
[WARNING_ASSERT] = WARNING_ENABLED,
|
[WARNING_ASSERT] = WARNING_ENABLED,
|
||||||
[WARNING_BACKWARDS_FOR] = WARNING_DISABLED,
|
[WARNING_BACKWARDS_FOR] = WARNING_DISABLED,
|
||||||
[WARNING_BUILTIN_ARG] = WARNING_DISABLED,
|
[WARNING_BUILTIN_ARG] = WARNING_DISABLED,
|
||||||
@@ -37,11 +38,15 @@ static enum WarningState const defaultWarnings[NB_WARNINGS] = {
|
|||||||
[WARNING_OBSOLETE] = WARNING_ENABLED,
|
[WARNING_OBSOLETE] = WARNING_ENABLED,
|
||||||
[WARNING_SHIFT] = WARNING_DISABLED,
|
[WARNING_SHIFT] = WARNING_DISABLED,
|
||||||
[WARNING_SHIFT_AMOUNT] = WARNING_DISABLED,
|
[WARNING_SHIFT_AMOUNT] = WARNING_DISABLED,
|
||||||
[WARNING_TRUNCATION] = WARNING_ENABLED,
|
|
||||||
[WARNING_USER] = WARNING_ENABLED,
|
[WARNING_USER] = WARNING_ENABLED,
|
||||||
|
|
||||||
|
[WARNING_NUMERIC_STRING_1] = WARNING_ENABLED,
|
||||||
|
[WARNING_NUMERIC_STRING_2] = WARNING_DISABLED,
|
||||||
|
[WARNING_TRUNCATION_1] = WARNING_ENABLED,
|
||||||
|
[WARNING_TRUNCATION_2] = WARNING_DISABLED,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum WarningState warningStates[NB_WARNINGS];
|
enum WarningState warningStates[ARRAY_SIZE(warningStates)];
|
||||||
|
|
||||||
bool warningsAreErrors; /* Set if `-Werror` was specified */
|
bool warningsAreErrors; /* Set if `-Werror` was specified */
|
||||||
|
|
||||||
@@ -64,7 +69,7 @@ static enum WarningState warningState(enum WarningID id)
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char const *warningFlags[NB_WARNINGS_ALL] = {
|
static const char * const warningFlags[NB_WARNINGS] = {
|
||||||
"assert",
|
"assert",
|
||||||
"backwards-for",
|
"backwards-for",
|
||||||
"builtin-args",
|
"builtin-args",
|
||||||
@@ -80,15 +85,66 @@ static char const *warningFlags[NB_WARNINGS_ALL] = {
|
|||||||
"obsolete",
|
"obsolete",
|
||||||
"shift",
|
"shift",
|
||||||
"shift-amount",
|
"shift-amount",
|
||||||
"truncation",
|
|
||||||
"user",
|
"user",
|
||||||
|
|
||||||
|
// Parametric warnings
|
||||||
|
"numeric-string",
|
||||||
|
"numeric-string",
|
||||||
|
"truncation",
|
||||||
|
"truncation",
|
||||||
|
|
||||||
/* Meta warnings */
|
/* Meta warnings */
|
||||||
"all",
|
"all",
|
||||||
"extra",
|
"extra",
|
||||||
"everything" /* Especially useful for testing */
|
"everything", /* Especially useful for testing */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct {
|
||||||
|
char const *name;
|
||||||
|
uint8_t nbLevels;
|
||||||
|
uint8_t defaultLevel;
|
||||||
|
} paramWarnings[] = {
|
||||||
|
{ "numeric-string", 2, 1 },
|
||||||
|
{ "truncation", 2, 2 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool tryProcessParamWarning(char const *flag, uint8_t param, enum WarningState state)
|
||||||
|
{
|
||||||
|
enum WarningID baseID = PARAM_WARNINGS_START;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ARRAY_SIZE(paramWarnings); i++) {
|
||||||
|
uint8_t maxParam = paramWarnings[i].nbLevels;
|
||||||
|
|
||||||
|
if (!strcmp(paramWarnings[i].name, flag)) { // Match!
|
||||||
|
// If making the warning an error but param is 0, set to the maximum
|
||||||
|
// This accommodates `-Werror=flag`, but also `-Werror=flag=0`, which is
|
||||||
|
// thus filtered out by the caller.
|
||||||
|
// A param of 0 makes sense for disabling everything, but neither for
|
||||||
|
// enabling nor "erroring". Use the default for those.
|
||||||
|
if (param == 0 && state != WARNING_DISABLED) {
|
||||||
|
param = paramWarnings[i].defaultLevel;
|
||||||
|
} else if (param > maxParam) {
|
||||||
|
if (param != 255) // Don't warn if already capped
|
||||||
|
warnx("Got parameter %" PRIu8
|
||||||
|
" for warning flag \"%s\", but the maximum is %"
|
||||||
|
PRIu8 "; capping.\n",
|
||||||
|
param, flag, maxParam);
|
||||||
|
param = maxParam;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the first <param> to enabled/error, and disable the rest
|
||||||
|
for (uint8_t ofs = 0; ofs < maxParam; ofs++) {
|
||||||
|
warningStates[baseID + ofs] =
|
||||||
|
ofs < param ? state : WARNING_DISABLED;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
baseID += maxParam;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
enum MetaWarningCommand {
|
enum MetaWarningCommand {
|
||||||
META_WARNING_DONE = NB_WARNINGS
|
META_WARNING_DONE = NB_WARNINGS
|
||||||
};
|
};
|
||||||
@@ -102,6 +158,9 @@ static uint8_t const _wallCommands[] = {
|
|||||||
WARNING_EMPTY_STRRPL,
|
WARNING_EMPTY_STRRPL,
|
||||||
WARNING_LARGE_CONSTANT,
|
WARNING_LARGE_CONSTANT,
|
||||||
WARNING_LONG_STR,
|
WARNING_LONG_STR,
|
||||||
|
WARNING_NESTED_COMMENT,
|
||||||
|
WARNING_OBSOLETE,
|
||||||
|
WARNING_NUMERIC_STRING_1,
|
||||||
META_WARNING_DONE
|
META_WARNING_DONE
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -110,6 +169,10 @@ static uint8_t const _wextraCommands[] = {
|
|||||||
WARNING_EMPTY_MACRO_ARG,
|
WARNING_EMPTY_MACRO_ARG,
|
||||||
WARNING_MACRO_SHIFT,
|
WARNING_MACRO_SHIFT,
|
||||||
WARNING_NESTED_COMMENT,
|
WARNING_NESTED_COMMENT,
|
||||||
|
WARNING_OBSOLETE,
|
||||||
|
WARNING_NUMERIC_STRING_2,
|
||||||
|
WARNING_TRUNCATION_1,
|
||||||
|
WARNING_TRUNCATION_2,
|
||||||
META_WARNING_DONE
|
META_WARNING_DONE
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -128,7 +191,10 @@ static uint8_t const _weverythingCommands[] = {
|
|||||||
WARNING_OBSOLETE,
|
WARNING_OBSOLETE,
|
||||||
WARNING_SHIFT,
|
WARNING_SHIFT,
|
||||||
WARNING_SHIFT_AMOUNT,
|
WARNING_SHIFT_AMOUNT,
|
||||||
/* WARNING_TRUNCATION, */
|
WARNING_NUMERIC_STRING_1,
|
||||||
|
WARNING_NUMERIC_STRING_2,
|
||||||
|
WARNING_TRUNCATION_1,
|
||||||
|
WARNING_TRUNCATION_2,
|
||||||
/* WARNING_USER, */
|
/* WARNING_USER, */
|
||||||
META_WARNING_DONE
|
META_WARNING_DONE
|
||||||
};
|
};
|
||||||
@@ -139,36 +205,33 @@ static uint8_t const *metaWarningCommands[NB_META_WARNINGS] = {
|
|||||||
_weverythingCommands
|
_weverythingCommands
|
||||||
};
|
};
|
||||||
|
|
||||||
void processWarningFlag(char const *flag)
|
void processWarningFlag(char *flag)
|
||||||
{
|
{
|
||||||
static bool setError = false;
|
static bool setError = false;
|
||||||
|
|
||||||
/* First, try to match against a "meta" warning */
|
/* First, try to match against a "meta" warning */
|
||||||
for (enum WarningID id = NB_WARNINGS; id < NB_WARNINGS_ALL; id++) {
|
for (enum WarningID id = META_WARNINGS_START; id < NB_WARNINGS; id++) {
|
||||||
/* TODO: improve the matching performance? */
|
/* TODO: improve the matching performance? */
|
||||||
if (!strcmp(flag, warningFlags[id])) {
|
if (!strcmp(flag, warningFlags[id])) {
|
||||||
/* We got a match! */
|
/* We got a match! */
|
||||||
if (setError)
|
if (setError)
|
||||||
errx(1, "Cannot make meta warning \"%s\" into an error",
|
errx("Cannot make meta warning \"%s\" into an error",
|
||||||
flag);
|
flag);
|
||||||
|
|
||||||
uint8_t const *ptr = metaWarningCommands[id - NB_WARNINGS];
|
for (uint8_t const *ptr = metaWarningCommands[id - META_WARNINGS_START];
|
||||||
|
*ptr != META_WARNING_DONE; ptr++) {
|
||||||
for (;;) {
|
|
||||||
if (*ptr == META_WARNING_DONE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Warning flag, set without override */
|
/* Warning flag, set without override */
|
||||||
if (warningStates[*ptr] == WARNING_DEFAULT)
|
if (warningStates[*ptr] == WARNING_DEFAULT)
|
||||||
warningStates[*ptr] = WARNING_ENABLED;
|
warningStates[*ptr] = WARNING_ENABLED;
|
||||||
ptr++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If it's not a meta warning, specially check against `-Werror` */
|
/* If it's not a meta warning, specially check against `-Werror` */
|
||||||
if (!strncmp(flag, "error", strlen("error"))) {
|
if (!strncmp(flag, "error", strlen("error"))) {
|
||||||
char const *errorFlag = flag + strlen("error");
|
char *errorFlag = flag + strlen("error");
|
||||||
|
|
||||||
switch (*errorFlag) {
|
switch (*errorFlag) {
|
||||||
case '\0':
|
case '\0':
|
||||||
@@ -177,7 +240,7 @@ void processWarningFlag(char const *flag)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case '=':
|
case '=':
|
||||||
/* `-Werror=XXX */
|
/* `-Werror=XXX` */
|
||||||
setError = true;
|
setError = true;
|
||||||
processWarningFlag(errorFlag + 1); /* Skip the `=` */
|
processWarningFlag(errorFlag + 1); /* Skip the `=` */
|
||||||
setError = false;
|
setError = false;
|
||||||
@@ -189,14 +252,60 @@ void processWarningFlag(char const *flag)
|
|||||||
|
|
||||||
/* Well, it's either a normal warning or a mistake */
|
/* Well, it's either a normal warning or a mistake */
|
||||||
|
|
||||||
/* Check if this is a negation */
|
|
||||||
bool isNegation = !strncmp(flag, "no-", strlen("no-")) && !setError;
|
|
||||||
char const *rootFlag = isNegation ? flag + strlen("no-") : flag;
|
|
||||||
enum WarningState state = setError ? WARNING_ERROR :
|
enum WarningState state = setError ? WARNING_ERROR :
|
||||||
isNegation ? WARNING_DISABLED : WARNING_ENABLED;
|
/* Not an error, then check if this is a negation */
|
||||||
|
strncmp(flag, "no-", strlen("no-")) ? WARNING_ENABLED
|
||||||
|
: WARNING_DISABLED;
|
||||||
|
char const *rootFlag = state == WARNING_DISABLED ? flag + strlen("no-") : flag;
|
||||||
|
|
||||||
|
// Is this a "parametric" warning?
|
||||||
|
if (state != WARNING_DISABLED) { // The `no-` form cannot be parametrized
|
||||||
|
// First, check if there is an "equals" sign followed by a decimal number
|
||||||
|
char *equals = strchr(rootFlag, '=');
|
||||||
|
|
||||||
|
if (equals && equals[1] != '\0') { // Ignore an equal sign at the very end as well
|
||||||
|
// Is the rest of the string a decimal number?
|
||||||
|
// We want to avoid `strtoul`'s whitespace and sign, so we parse manually
|
||||||
|
uint8_t param = 0;
|
||||||
|
char const *ptr = equals + 1;
|
||||||
|
bool warned = false;
|
||||||
|
|
||||||
|
// The `if`'s condition above ensures that this will run at least once
|
||||||
|
do {
|
||||||
|
// If we don't have a digit, bail
|
||||||
|
if (*ptr < '0' || *ptr > '9')
|
||||||
|
break;
|
||||||
|
// Avoid overflowing!
|
||||||
|
if (param > UINT8_MAX - (*ptr - '0')) {
|
||||||
|
if (!warned)
|
||||||
|
warnx("Invalid warning flag \"%s\": capping parameter at 255\n",
|
||||||
|
flag);
|
||||||
|
warned = true; // Only warn once, cap always
|
||||||
|
param = 255;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
param = param * 10 + (*ptr - '0');
|
||||||
|
|
||||||
|
ptr++;
|
||||||
|
} while (*ptr);
|
||||||
|
|
||||||
|
// If we managed to the end of the string, check that the warning indeed
|
||||||
|
// accepts a parameter
|
||||||
|
if (*ptr == '\0') {
|
||||||
|
if (setError && param == 0) {
|
||||||
|
warnx("Ignoring nonsensical warning flag \"%s\"\n", flag);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*equals = '\0'; // Truncate the param at the '='
|
||||||
|
if (tryProcessParamWarning(rootFlag, param,
|
||||||
|
param == 0 ? WARNING_DISABLED : state))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Try to match the flag against a "normal" flag */
|
/* Try to match the flag against a "normal" flag */
|
||||||
for (enum WarningID id = 0; id < NB_WARNINGS; id++) {
|
for (enum WarningID id = 0; id < NB_PLAIN_WARNINGS; id++) {
|
||||||
if (!strcmp(rootFlag, warningFlags[id])) {
|
if (!strcmp(rootFlag, warningFlags[id])) {
|
||||||
/* We got a match! */
|
/* We got a match! */
|
||||||
warningStates[id] = state;
|
warningStates[id] = state;
|
||||||
@@ -204,10 +313,15 @@ void processWarningFlag(char const *flag)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lastly, this might be a "parametric" warning without an equals sign
|
||||||
|
// If it is, treat the param as 1 if enabling, or 0 if disabling
|
||||||
|
if (tryProcessParamWarning(rootFlag, 0, state))
|
||||||
|
return;
|
||||||
|
|
||||||
warnx("Unknown warning `%s`", flag);
|
warnx("Unknown warning `%s`", flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
void printDiag(const char *fmt, va_list args, char const *type,
|
void printDiag(char const *fmt, va_list args, char const *type,
|
||||||
char const *flagfmt, char const *flag)
|
char const *flagfmt, char const *flag)
|
||||||
{
|
{
|
||||||
fputs(type, stderr);
|
fputs(type, stderr);
|
||||||
@@ -217,17 +331,17 @@ void printDiag(const char *fmt, va_list args, char const *type,
|
|||||||
lexer_DumpStringExpansions();
|
lexer_DumpStringExpansions();
|
||||||
}
|
}
|
||||||
|
|
||||||
void error(const char *fmt, ...)
|
void error(char const *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
|
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
printDiag(fmt, args, "ERROR: ", ":\n ", NULL);
|
printDiag(fmt, args, "error: ", ":\n ", NULL);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
nbErrors++;
|
nbErrors++;
|
||||||
}
|
}
|
||||||
|
|
||||||
_Noreturn void fatalerror(const char *fmt, ...)
|
_Noreturn void fatalerror(char const *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
|
|
||||||
@@ -250,7 +364,7 @@ void warning(enum WarningID id, char const *fmt, ...)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case WARNING_ERROR:
|
case WARNING_ERROR:
|
||||||
printDiag(fmt, args, "ERROR: ", ": [-Werror=%s]\n ", flag);
|
printDiag(fmt, args, "error: ", ": [-Werror=%s]\n ", flag);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
87
src/error.c
Normal file
87
src/error.c
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of RGBDS.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2005-2021, Rich Felker and RGBDS contributors.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "error.h"
|
||||||
|
#include "platform.h"
|
||||||
|
|
||||||
|
static void vwarn(char const NONNULL(fmt), va_list ap)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "warning: ");
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
fputs(": ", stderr);
|
||||||
|
perror(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vwarnx(char const NONNULL(fmt), va_list ap)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "warning");
|
||||||
|
fputs(": ", stderr);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
putc('\n', stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
_Noreturn static void verr(char const NONNULL(fmt), va_list ap)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "error: ");
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
fputs(": ", stderr);
|
||||||
|
fputs(strerror(errno), stderr);
|
||||||
|
putc('\n', stderr);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
_Noreturn static void verrx(char const NONNULL(fmt), va_list ap)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "error");
|
||||||
|
fputs(": ", stderr);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
putc('\n', stderr);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void warn(char const NONNULL(fmt), ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vwarn(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void warnx(char const NONNULL(fmt), ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vwarnx(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
_Noreturn void err(char const NONNULL(fmt), ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
verr(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
_Noreturn void errx(char const NONNULL(fmt), ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
verrx(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
94
src/extern/err.c
vendored
94
src/extern/err.c
vendored
@@ -1,94 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of RGBDS.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005-2018, Rich Felker and RGBDS contributors.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "extern/err.h"
|
|
||||||
|
|
||||||
void rgbds_vwarn(const char *fmt, va_list ap)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "warning: ");
|
|
||||||
if (fmt) {
|
|
||||||
vfprintf(stderr, fmt, ap);
|
|
||||||
fputs(": ", stderr);
|
|
||||||
}
|
|
||||||
perror(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void rgbds_vwarnx(const char *fmt, va_list ap)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "warning");
|
|
||||||
if (fmt) {
|
|
||||||
fputs(": ", stderr);
|
|
||||||
vfprintf(stderr, fmt, ap);
|
|
||||||
}
|
|
||||||
putc('\n', stderr);
|
|
||||||
}
|
|
||||||
|
|
||||||
_Noreturn void rgbds_verr(int status, const char *fmt, va_list ap)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "error: ");
|
|
||||||
if (fmt) {
|
|
||||||
vfprintf(stderr, fmt, ap);
|
|
||||||
fputs(": ", stderr);
|
|
||||||
}
|
|
||||||
fputs(strerror(errno), stderr);
|
|
||||||
putc('\n', stderr);
|
|
||||||
exit(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
_Noreturn void rgbds_verrx(int status, const char *fmt, va_list ap)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "error");
|
|
||||||
if (fmt) {
|
|
||||||
fputs(": ", stderr);
|
|
||||||
vfprintf(stderr, fmt, ap);
|
|
||||||
}
|
|
||||||
putc('\n', stderr);
|
|
||||||
exit(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
void rgbds_warn(const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
vwarn(fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void rgbds_warnx(const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
vwarnx(fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
_Noreturn void rgbds_err(int status, const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
verr(status, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
_Noreturn void rgbds_errx(int status, const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
verrx(status, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
18
src/extern/getopt.c
vendored
18
src/extern/getopt.c
vendored
@@ -37,7 +37,7 @@ int musl_optind = 1, musl_opterr = 1, musl_optopt;
|
|||||||
int musl_optreset = 0;
|
int musl_optreset = 0;
|
||||||
static int musl_optpos;
|
static int musl_optpos;
|
||||||
|
|
||||||
static void musl_getopt_msg(const char *a, const char *b, const char *c, size_t l)
|
static void musl_getopt_msg(char const *a, char const *b, char const *c, size_t l)
|
||||||
{
|
{
|
||||||
FILE *f = stderr;
|
FILE *f = stderr;
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ static void musl_getopt_msg(const char *a, const char *b, const char *c, size_t
|
|||||||
putc('\n', f);
|
putc('\n', f);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getopt(int argc, char *argv[], const char *optstring)
|
static int getopt(int argc, char *argv[], char const *optstring)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
wchar_t c, d;
|
wchar_t c, d;
|
||||||
@@ -140,9 +140,11 @@ static void permute(char **argv, int dest, int src)
|
|||||||
argv[dest] = tmp;
|
argv[dest] = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int musl_getopt_long_core(int argc, char **argv, const char *optstring, const struct option *longopts, int *idx, int longonly);
|
static int musl_getopt_long_core(int argc, char **argv, char const *optstring,
|
||||||
|
const struct option *longopts, int *idx, int longonly);
|
||||||
|
|
||||||
static int musl_getopt_long(int argc, char **argv, const char *optstring, const struct option *longopts, int *idx, int longonly)
|
static int musl_getopt_long(int argc, char **argv, char const *optstring,
|
||||||
|
const struct option *longopts, int *idx, int longonly)
|
||||||
{
|
{
|
||||||
int ret, skipped, resumed;
|
int ret, skipped, resumed;
|
||||||
|
|
||||||
@@ -178,7 +180,8 @@ static int musl_getopt_long(int argc, char **argv, const char *optstring, const
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int musl_getopt_long_core(int argc, char **argv, const char *optstring, const struct option *longopts, int *idx, int longonly)
|
static int musl_getopt_long_core(int argc, char **argv, char const *optstring,
|
||||||
|
const struct option *longopts, int *idx, int longonly)
|
||||||
{
|
{
|
||||||
musl_optarg = 0;
|
musl_optarg = 0;
|
||||||
if (longopts && argv[musl_optind][0] == '-' &&
|
if (longopts && argv[musl_optind][0] == '-' &&
|
||||||
@@ -189,7 +192,7 @@ static int musl_getopt_long_core(int argc, char **argv, const char *optstring, c
|
|||||||
char *arg = 0, *opt, *start = argv[musl_optind] + 1;
|
char *arg = 0, *opt, *start = argv[musl_optind] + 1;
|
||||||
|
|
||||||
for (cnt = i = 0; longopts[i].name; i++) {
|
for (cnt = i = 0; longopts[i].name; i++) {
|
||||||
const char *name = longopts[i].name;
|
char const *name = longopts[i].name;
|
||||||
|
|
||||||
opt = start;
|
opt = start;
|
||||||
if (*opt == '-')
|
if (*opt == '-')
|
||||||
@@ -277,7 +280,8 @@ static int musl_getopt_long_core(int argc, char **argv, const char *optstring, c
|
|||||||
return getopt(argc, argv, optstring);
|
return getopt(argc, argv, optstring);
|
||||||
}
|
}
|
||||||
|
|
||||||
int musl_getopt_long_only(int argc, char **argv, const char *optstring, const struct option *longopts, int *idx)
|
int musl_getopt_long_only(int argc, char **argv, char const *optstring,
|
||||||
|
const struct option *longopts, int *idx)
|
||||||
{
|
{
|
||||||
return musl_getopt_long(argc, argv, optstring, longopts, idx, 1);
|
return musl_getopt_long(argc, argv, optstring, longopts, idx, 1);
|
||||||
}
|
}
|
||||||
|
|||||||
282
src/fix/main.c
282
src/fix/main.c
@@ -30,7 +30,7 @@
|
|||||||
#define BANK_SIZE 0x4000
|
#define BANK_SIZE 0x4000
|
||||||
|
|
||||||
/* Short options */
|
/* Short options */
|
||||||
static const char *optstring = "Ccf:i:jk:l:m:n:p:r:st:Vv";
|
static const char *optstring = "Ccf:i:jk:l:m:n:Op:r:st:Vv";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Equivalent long options
|
* Equivalent long options
|
||||||
@@ -52,6 +52,7 @@ static struct option const longopts[] = {
|
|||||||
{ "old-licensee", required_argument, NULL, 'l' },
|
{ "old-licensee", required_argument, NULL, 'l' },
|
||||||
{ "mbc-type", required_argument, NULL, 'm' },
|
{ "mbc-type", required_argument, NULL, 'm' },
|
||||||
{ "rom-version", required_argument, NULL, 'n' },
|
{ "rom-version", required_argument, NULL, 'n' },
|
||||||
|
{ "overwrite", no_argument, NULL, 'O' },
|
||||||
{ "pad-value", required_argument, NULL, 'p' },
|
{ "pad-value", required_argument, NULL, 'p' },
|
||||||
{ "ram-size", required_argument, NULL, 'r' },
|
{ "ram-size", required_argument, NULL, 'r' },
|
||||||
{ "sgb-compatible", no_argument, NULL, 's' },
|
{ "sgb-compatible", no_argument, NULL, 's' },
|
||||||
@@ -64,7 +65,7 @@ static struct option const longopts[] = {
|
|||||||
static void printUsage(void)
|
static void printUsage(void)
|
||||||
{
|
{
|
||||||
fputs(
|
fputs(
|
||||||
"Usage: rgbfix [-jsVv] [-C | -c] [-f <fix_spec>] [-i <game_id>] [-k <licensee>]\n"
|
"Usage: rgbfix [-jOsVv] [-C | -c] [-f <fix_spec>] [-i <game_id>] [-k <licensee>]\n"
|
||||||
" [-l <licensee_byte>] [-m <mbc_type>] [-n <rom_version>]\n"
|
" [-l <licensee_byte>] [-m <mbc_type>] [-n <rom_version>]\n"
|
||||||
" [-p <pad_value>] [-r <ram_size>] [-t <title_str>] [<file> ...]\n"
|
" [-p <pad_value>] [-r <ram_size>] [-t <title_str>] [<file> ...]\n"
|
||||||
"Useful options:\n"
|
"Useful options:\n"
|
||||||
@@ -143,18 +144,18 @@ enum MbcType {
|
|||||||
TPP1_RUMBLE = 0x101,
|
TPP1_RUMBLE = 0x101,
|
||||||
TPP1_MULTIRUMBLE = 0x102, // Should not be possible
|
TPP1_MULTIRUMBLE = 0x102, // Should not be possible
|
||||||
TPP1_MULTIRUMBLE_RUMBLE = 0x103,
|
TPP1_MULTIRUMBLE_RUMBLE = 0x103,
|
||||||
TPP1_RTC = 0x104,
|
TPP1_TIMER = 0x104,
|
||||||
TPP1_RTC_RUMBLE = 0x105,
|
TPP1_TIMER_RUMBLE = 0x105,
|
||||||
TPP1_RTC_MULTIRUMBLE = 0x106, // Should not be possible
|
TPP1_TIMER_MULTIRUMBLE = 0x106, // Should not be possible
|
||||||
TPP1_RTC_MULTIRUMBLE_RUMBLE = 0x107,
|
TPP1_TIMER_MULTIRUMBLE_RUMBLE = 0x107,
|
||||||
TPP1_BATTERY = 0x108,
|
TPP1_BATTERY = 0x108,
|
||||||
TPP1_BATTERY_RUMBLE = 0x109,
|
TPP1_BATTERY_RUMBLE = 0x109,
|
||||||
TPP1_BATTERY_MULTIRUMBLE = 0x10a, // Should not be possible
|
TPP1_BATTERY_MULTIRUMBLE = 0x10A, // Should not be possible
|
||||||
TPP1_BATTERY_MULTIRUMBLE_RUMBLE = 0x10b,
|
TPP1_BATTERY_MULTIRUMBLE_RUMBLE = 0x10B,
|
||||||
TPP1_BATTERY_RTC = 0x10c,
|
TPP1_BATTERY_TIMER = 0x10C,
|
||||||
TPP1_BATTERY_RTC_RUMBLE = 0x10d,
|
TPP1_BATTERY_TIMER_RUMBLE = 0x10D,
|
||||||
TPP1_BATTERY_RTC_MULTIRUMBLE = 0x10e, // Should not be possible
|
TPP1_BATTERY_TIMER_MULTIRUMBLE = 0x10E, // Should not be possible
|
||||||
TPP1_BATTERY_RTC_MULTIRUMBLE_RUMBLE = 0x10f,
|
TPP1_BATTERY_TIMER_MULTIRUMBLE_RUMBLE = 0x10F,
|
||||||
|
|
||||||
// Error values
|
// Error values
|
||||||
MBC_NONE = UNSPECIFIED, // No MBC specified, do not act on it
|
MBC_NONE = UNSPECIFIED, // No MBC specified, do not act on it
|
||||||
@@ -166,7 +167,7 @@ enum MbcType {
|
|||||||
static void printAcceptedMBCNames(void)
|
static void printAcceptedMBCNames(void)
|
||||||
{
|
{
|
||||||
fputs("\tROM ($00) [aka ROM_ONLY]\n", stderr);
|
fputs("\tROM ($00) [aka ROM_ONLY]\n", stderr);
|
||||||
fputs("\tMBC1 ($02), MBC1+RAM ($02), MBC1+RAM+BATTERY ($03)\n", stderr);
|
fputs("\tMBC1 ($01), MBC1+RAM ($02), MBC1+RAM+BATTERY ($03)\n", stderr);
|
||||||
fputs("\tMBC2 ($05), MBC2+BATTERY ($06)\n", stderr);
|
fputs("\tMBC2 ($05), MBC2+BATTERY ($06)\n", stderr);
|
||||||
fputs("\tROM+RAM ($08) [deprecated], ROM+RAM+BATTERY ($09) [deprecated]\n", stderr);
|
fputs("\tROM+RAM ($08) [deprecated], ROM+RAM+BATTERY ($09) [deprecated]\n", stderr);
|
||||||
fputs("\tMMM01 ($0B), MMM01+RAM ($0C), MMM01+RAM+BATTERY ($0D)\n", stderr);
|
fputs("\tMMM01 ($0B), MMM01+RAM ($0C), MMM01+RAM+BATTERY ($0D)\n", stderr);
|
||||||
@@ -181,11 +182,11 @@ static void printAcceptedMBCNames(void)
|
|||||||
fputs("\tHUC3 ($FE)\n", stderr);
|
fputs("\tHUC3 ($FE)\n", stderr);
|
||||||
fputs("\tHUC1+RAM+BATTERY ($FF)\n", stderr);
|
fputs("\tHUC1+RAM+BATTERY ($FF)\n", stderr);
|
||||||
|
|
||||||
fputs("\n\tTPP1_1.0, TPP1_1.0+RUMBLE, TPP1_1.0+MULTIRUMBLE, TPP1_1.0+RTC,\n", stderr);
|
fputs("\n\tTPP1_1.0, TPP1_1.0+RUMBLE, TPP1_1.0+MULTIRUMBLE, TPP1_1.0+TIMER,\n", stderr);
|
||||||
fputs("\tTPP1_1.0+RTC+RUMBLE, TPP1_1.0+RTC+MULTIRUMBLE, TPP1_1.0+BATTERY,\n", stderr);
|
fputs("\tTPP1_1.0+TIMER+RUMBLE, TPP1_1.0+TIMER+MULTIRUMBLE, TPP1_1.0+BATTERY,\n", stderr);
|
||||||
fputs("\tTPP1_1.0+BATTERY+RUMBLE, TPP1_1.0+BATTERY+MULTIRUMBLE,\n", stderr);
|
fputs("\tTPP1_1.0+BATTERY+RUMBLE, TPP1_1.0+BATTERY+MULTIRUMBLE,\n", stderr);
|
||||||
fputs("\tTPP1_1.0+BATTERY+RTC, TPP1_1.0+BATTERY+RTC+RUMBLE,\n", stderr);
|
fputs("\tTPP1_1.0+BATTERY+TIMER, TPP1_1.0+BATTERY+TIMER+RUMBLE,\n", stderr);
|
||||||
fputs("\tTPP1_1.0+BATTERY+RTC+MULTIRUMBLE\n", stderr);
|
fputs("\tTPP1_1.0+BATTERY+TIMER+MULTIRUMBLE\n", stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t tpp1Rev[2];
|
static uint8_t tpp1Rev[2];
|
||||||
@@ -493,7 +494,9 @@ do { \
|
|||||||
|
|
||||||
case MBC3:
|
case MBC3:
|
||||||
// Handle timer, which also requires battery
|
// Handle timer, which also requires battery
|
||||||
if (features & (TIMER & BATTERY)) {
|
if (features & TIMER) {
|
||||||
|
if (!(features & BATTERY))
|
||||||
|
fprintf(stderr, "warning: MBC3+TIMER implies BATTERY\n");
|
||||||
features &= ~(TIMER | BATTERY); // Reset those bits
|
features &= ~(TIMER | BATTERY); // Reset those bits
|
||||||
mbc = MBC3_TIMER_BATTERY;
|
mbc = MBC3_TIMER_BATTERY;
|
||||||
// RAM is handled below
|
// RAM is handled below
|
||||||
@@ -642,13 +645,13 @@ static char const *mbcName(enum MbcType type)
|
|||||||
case TPP1_MULTIRUMBLE:
|
case TPP1_MULTIRUMBLE:
|
||||||
case TPP1_MULTIRUMBLE_RUMBLE:
|
case TPP1_MULTIRUMBLE_RUMBLE:
|
||||||
return "TPP1+MULTIRUMBLE";
|
return "TPP1+MULTIRUMBLE";
|
||||||
case TPP1_RTC:
|
case TPP1_TIMER:
|
||||||
return "TPP1+RTC";
|
return "TPP1+TIMER";
|
||||||
case TPP1_RTC_RUMBLE:
|
case TPP1_TIMER_RUMBLE:
|
||||||
return "TPP1+RTC+RUMBLE";
|
return "TPP1+TIMER+RUMBLE";
|
||||||
case TPP1_RTC_MULTIRUMBLE:
|
case TPP1_TIMER_MULTIRUMBLE:
|
||||||
case TPP1_RTC_MULTIRUMBLE_RUMBLE:
|
case TPP1_TIMER_MULTIRUMBLE_RUMBLE:
|
||||||
return "TPP1+RTC+MULTIRUMBLE";
|
return "TPP1+TIMER+MULTIRUMBLE";
|
||||||
case TPP1_BATTERY:
|
case TPP1_BATTERY:
|
||||||
return "TPP1+BATTERY";
|
return "TPP1+BATTERY";
|
||||||
case TPP1_BATTERY_RUMBLE:
|
case TPP1_BATTERY_RUMBLE:
|
||||||
@@ -656,13 +659,13 @@ static char const *mbcName(enum MbcType type)
|
|||||||
case TPP1_BATTERY_MULTIRUMBLE:
|
case TPP1_BATTERY_MULTIRUMBLE:
|
||||||
case TPP1_BATTERY_MULTIRUMBLE_RUMBLE:
|
case TPP1_BATTERY_MULTIRUMBLE_RUMBLE:
|
||||||
return "TPP1+BATTERY+MULTIRUMBLE";
|
return "TPP1+BATTERY+MULTIRUMBLE";
|
||||||
case TPP1_BATTERY_RTC:
|
case TPP1_BATTERY_TIMER:
|
||||||
return "TPP1+BATTERY+RTC";
|
return "TPP1+BATTERY+TIMER";
|
||||||
case TPP1_BATTERY_RTC_RUMBLE:
|
case TPP1_BATTERY_TIMER_RUMBLE:
|
||||||
return "TPP1+BATTERY+RTC+RUMBLE";
|
return "TPP1+BATTERY+TIMER+RUMBLE";
|
||||||
case TPP1_BATTERY_RTC_MULTIRUMBLE:
|
case TPP1_BATTERY_TIMER_MULTIRUMBLE:
|
||||||
case TPP1_BATTERY_RTC_MULTIRUMBLE_RUMBLE:
|
case TPP1_BATTERY_TIMER_MULTIRUMBLE_RUMBLE:
|
||||||
return "TPP1+BATTERY+RTC+MULTIRUMBLE";
|
return "TPP1+BATTERY+TIMER+MULTIRUMBLE";
|
||||||
|
|
||||||
// Error values
|
// Error values
|
||||||
case MBC_NONE:
|
case MBC_NONE:
|
||||||
@@ -719,18 +722,18 @@ static bool hasRAM(enum MbcType type)
|
|||||||
case TPP1_RUMBLE:
|
case TPP1_RUMBLE:
|
||||||
case TPP1_MULTIRUMBLE:
|
case TPP1_MULTIRUMBLE:
|
||||||
case TPP1_MULTIRUMBLE_RUMBLE:
|
case TPP1_MULTIRUMBLE_RUMBLE:
|
||||||
case TPP1_RTC:
|
case TPP1_TIMER:
|
||||||
case TPP1_RTC_RUMBLE:
|
case TPP1_TIMER_RUMBLE:
|
||||||
case TPP1_RTC_MULTIRUMBLE:
|
case TPP1_TIMER_MULTIRUMBLE:
|
||||||
case TPP1_RTC_MULTIRUMBLE_RUMBLE:
|
case TPP1_TIMER_MULTIRUMBLE_RUMBLE:
|
||||||
case TPP1_BATTERY:
|
case TPP1_BATTERY:
|
||||||
case TPP1_BATTERY_RUMBLE:
|
case TPP1_BATTERY_RUMBLE:
|
||||||
case TPP1_BATTERY_MULTIRUMBLE:
|
case TPP1_BATTERY_MULTIRUMBLE:
|
||||||
case TPP1_BATTERY_MULTIRUMBLE_RUMBLE:
|
case TPP1_BATTERY_MULTIRUMBLE_RUMBLE:
|
||||||
case TPP1_BATTERY_RTC:
|
case TPP1_BATTERY_TIMER:
|
||||||
case TPP1_BATTERY_RTC_RUMBLE:
|
case TPP1_BATTERY_TIMER_RUMBLE:
|
||||||
case TPP1_BATTERY_RTC_MULTIRUMBLE:
|
case TPP1_BATTERY_TIMER_MULTIRUMBLE:
|
||||||
case TPP1_BATTERY_RTC_MULTIRUMBLE_RUMBLE:
|
case TPP1_BATTERY_TIMER_MULTIRUMBLE_RUMBLE:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -746,6 +749,15 @@ static const uint8_t ninLogo[] = {
|
|||||||
0xDD, 0xDC, 0x99, 0x9F, 0xBB, 0xB9, 0x33, 0x3E
|
0xDD, 0xDC, 0x99, 0x9F, 0xBB, 0xB9, 0x33, 0x3E
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const uint8_t trashLogo[] = {
|
||||||
|
0xFF^0xCE, 0xFF^0xED, 0xFF^0x66, 0xFF^0x66, 0xFF^0xCC, 0xFF^0x0D, 0xFF^0x00, 0xFF^0x0B,
|
||||||
|
0xFF^0x03, 0xFF^0x73, 0xFF^0x00, 0xFF^0x83, 0xFF^0x00, 0xFF^0x0C, 0xFF^0x00, 0xFF^0x0D,
|
||||||
|
0xFF^0x00, 0xFF^0x08, 0xFF^0x11, 0xFF^0x1F, 0xFF^0x88, 0xFF^0x89, 0xFF^0x00, 0xFF^0x0E,
|
||||||
|
0xFF^0xDC, 0xFF^0xCC, 0xFF^0x6E, 0xFF^0xE6, 0xFF^0xDD, 0xFF^0xDD, 0xFF^0xD9, 0xFF^0x99,
|
||||||
|
0xFF^0xBB, 0xFF^0xBB, 0xFF^0x67, 0xFF^0x63, 0xFF^0x6E, 0xFF^0x0E, 0xFF^0xEC, 0xFF^0xCC,
|
||||||
|
0xFF^0xDD, 0xFF^0xDC, 0xFF^0x99, 0xFF^0x9F, 0xFF^0xBB, 0xFF^0xB9, 0xFF^0x33, 0xFF^0x3E
|
||||||
|
};
|
||||||
|
|
||||||
static enum { DMG, BOTH, CGB } model = DMG; // If DMG, byte is left alone
|
static enum { DMG, BOTH, CGB } model = DMG; // If DMG, byte is left alone
|
||||||
#define FIX_LOGO 0x80
|
#define FIX_LOGO 0x80
|
||||||
#define TRASH_LOGO 0x40
|
#define TRASH_LOGO 0x40
|
||||||
@@ -762,6 +774,7 @@ static uint8_t newLicenseeLen;
|
|||||||
static uint16_t oldLicensee = UNSPECIFIED;
|
static uint16_t oldLicensee = UNSPECIFIED;
|
||||||
static enum MbcType cartridgeType = MBC_NONE;
|
static enum MbcType cartridgeType = MBC_NONE;
|
||||||
static uint16_t romVersion = UNSPECIFIED;
|
static uint16_t romVersion = UNSPECIFIED;
|
||||||
|
static bool overwriteRom = false; // If false, warn when overwriting non-zero non-identical bytes
|
||||||
static uint16_t padValue = UNSPECIFIED;
|
static uint16_t padValue = UNSPECIFIED;
|
||||||
static uint16_t ramSize = UNSPECIFIED;
|
static uint16_t ramSize = UNSPECIFIED;
|
||||||
static bool sgb = false; // If false, SGB flags are left alone
|
static bool sgb = false; // If false, SGB flags are left alone
|
||||||
@@ -770,7 +783,7 @@ static uint8_t titleLen;
|
|||||||
|
|
||||||
static uint8_t maxTitleLen(void)
|
static uint8_t maxTitleLen(void)
|
||||||
{
|
{
|
||||||
return gameID ? 11 : model != DMG ? 15 : 16;
|
return gameID ? 11 : model != DMG ? 15 : 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t readBytes(int fd, uint8_t *buf, size_t len)
|
static ssize_t readBytes(int fd, uint8_t *buf, size_t len)
|
||||||
@@ -824,20 +837,45 @@ static ssize_t writeBytes(int fd, void *buf, size_t len)
|
|||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param rom0 A pointer to rom0
|
||||||
|
* @param addr What address to check
|
||||||
|
* @param fixedByte The fixed byte at the address
|
||||||
|
* @param areaName Name to be displayed in the warning message
|
||||||
|
*/
|
||||||
|
static void overwriteByte(uint8_t *rom0, uint16_t addr, uint8_t fixedByte, char const *areaName)
|
||||||
|
{
|
||||||
|
uint8_t origByte = rom0[addr];
|
||||||
|
|
||||||
|
if (!overwriteRom && origByte != 0 && origByte != fixedByte)
|
||||||
|
fprintf(stderr, "warning: Overwrote a non-zero byte in the %s\n", areaName);
|
||||||
|
|
||||||
|
rom0[addr] = fixedByte;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param rom0 A pointer to rom0
|
* @param rom0 A pointer to rom0
|
||||||
* @param startAddr What address to begin checking from
|
* @param startAddr What address to begin checking from
|
||||||
|
* @param fixed The fixed bytes at the address
|
||||||
* @param size How many bytes to check
|
* @param size How many bytes to check
|
||||||
* @param areaName Name to be displayed in the warning message
|
* @param areaName Name to be displayed in the warning message
|
||||||
*/
|
*/
|
||||||
static void warnNonZero(uint8_t *rom0, uint16_t startAddr, uint8_t size, char const *areaName)
|
static void overwriteBytes(uint8_t *rom0, uint16_t startAddr, uint8_t const *fixed, uint8_t size,
|
||||||
|
char const *areaName)
|
||||||
{
|
{
|
||||||
for (uint8_t i = 0; i < size; i++) {
|
if (!overwriteRom) {
|
||||||
if (rom0[i + startAddr] != 0) {
|
for (uint8_t i = 0; i < size; i++) {
|
||||||
fprintf(stderr, "warning: Overwrote a non-zero byte in the %s\n", areaName);
|
uint8_t origByte = rom0[i + startAddr];
|
||||||
break;
|
|
||||||
|
if (origByte != 0 && origByte != fixed[i]) {
|
||||||
|
fprintf(stderr, "warning: Overwrote a non-zero byte in the %s\n",
|
||||||
|
areaName);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memcpy(&rom0[startAddr], fixed, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -857,7 +895,7 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
uint8_t rom0[BANK_SIZE];
|
uint8_t rom0[BANK_SIZE];
|
||||||
ssize_t rom0Len = readBytes(input, rom0, sizeof(rom0));
|
ssize_t rom0Len = readBytes(input, rom0, sizeof(rom0));
|
||||||
// Also used as how many bytes to write back when fixing in-place
|
// Also used as how many bytes to write back when fixing in-place
|
||||||
ssize_t headerSize = (cartridgeType & 0xff00) == TPP1 ? 0x154 : 0x150;
|
ssize_t headerSize = (cartridgeType & 0xFF00) == TPP1 ? 0x154 : 0x150;
|
||||||
|
|
||||||
if (rom0Len == -1) {
|
if (rom0Len == -1) {
|
||||||
report("FATAL: Failed to read \"%s\"'s header: %s\n", name, strerror(errno));
|
report("FATAL: Failed to read \"%s\"'s header: %s\n", name, strerror(errno));
|
||||||
@@ -870,94 +908,68 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
// Accept partial reads if the file contains at least the header
|
// Accept partial reads if the file contains at least the header
|
||||||
|
|
||||||
if (fixSpec & (FIX_LOGO | TRASH_LOGO)) {
|
if (fixSpec & (FIX_LOGO | TRASH_LOGO)) {
|
||||||
warnNonZero(rom0, 0x0104, sizeof(ninLogo), "Nintendo logo");
|
if (fixSpec & FIX_LOGO)
|
||||||
if (fixSpec & FIX_LOGO) {
|
overwriteBytes(rom0, 0x0104, ninLogo, sizeof(ninLogo), "Nintendo logo");
|
||||||
memcpy(&rom0[0x104], ninLogo, sizeof(ninLogo));
|
else
|
||||||
} else {
|
overwriteBytes(rom0, 0x0104, trashLogo, sizeof(trashLogo), "Nintendo logo");
|
||||||
for (uint8_t i = 0; i < sizeof(ninLogo); i++)
|
|
||||||
rom0[i + 0x104] = ~ninLogo[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (title) {
|
if (title)
|
||||||
warnNonZero(rom0, 0x134, titleLen, "title");
|
overwriteBytes(rom0, 0x134, (uint8_t const *)title, titleLen, "title");
|
||||||
memcpy(&rom0[0x134], title, titleLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gameID) {
|
if (gameID)
|
||||||
warnNonZero(rom0, 0x13f, gameIDLen, "manufacturer code");
|
overwriteBytes(rom0, 0x13F, (uint8_t const *)gameID, gameIDLen, "manufacturer code");
|
||||||
memcpy(&rom0[0x13f], gameID, gameIDLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (model != DMG) {
|
if (model != DMG)
|
||||||
warnNonZero(rom0, 0x143, 1, "CGB flag");
|
overwriteByte(rom0, 0x143, model == BOTH ? 0x80 : 0xC0, "CGB flag");
|
||||||
rom0[0x143] = model == BOTH ? 0x80 : 0xc0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newLicensee) {
|
if (newLicensee)
|
||||||
warnNonZero(rom0, 0x144, newLicenseeLen, "new licensee code");
|
overwriteBytes(rom0, 0x144, (uint8_t const *)newLicensee, newLicenseeLen,
|
||||||
memcpy(&rom0[0x144], newLicensee, newLicenseeLen);
|
"new licensee code");
|
||||||
}
|
|
||||||
|
|
||||||
if (sgb) {
|
if (sgb)
|
||||||
warnNonZero(rom0, 0x146, 1, "SGB flag");
|
overwriteByte(rom0, 0x146, 0x03, "SGB flag");
|
||||||
rom0[0x146] = 0x03;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a valid MBC was specified...
|
// If a valid MBC was specified...
|
||||||
if (cartridgeType < MBC_NONE) {
|
if (cartridgeType < MBC_NONE) {
|
||||||
warnNonZero(rom0, 0x147, 1, "cartridge type");
|
|
||||||
uint8_t byte = cartridgeType;
|
uint8_t byte = cartridgeType;
|
||||||
|
|
||||||
if ((cartridgeType & 0xff00) == TPP1) {
|
if ((cartridgeType & 0xFF00) == TPP1) {
|
||||||
// Cartridge type isn't directly actionable, translate it
|
// Cartridge type isn't directly actionable, translate it
|
||||||
byte = 0xBC;
|
byte = 0xBC;
|
||||||
// The other TPP1 identification bytes will be written below
|
// The other TPP1 identification bytes will be written below
|
||||||
}
|
}
|
||||||
rom0[0x147] = byte;
|
overwriteByte(rom0, 0x147, byte, "cartridge type");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ROM size will be written last, after evaluating the file's size
|
// ROM size will be written last, after evaluating the file's size
|
||||||
|
|
||||||
if ((cartridgeType & 0xff00) == TPP1) {
|
if ((cartridgeType & 0xFF00) == TPP1) {
|
||||||
warnNonZero(rom0, 0x149, 2, "TPP1 identification code");
|
uint8_t const tpp1Code[2] = {0xC1, 0x65};
|
||||||
rom0[0x149] = 0xC1;
|
|
||||||
rom0[0x14a] = 0x65;
|
|
||||||
|
|
||||||
warnNonZero(rom0, 0x150, 2, "TPP1 revision number");
|
overwriteBytes(rom0, 0x149, tpp1Code, sizeof(tpp1Code), "TPP1 identification code");
|
||||||
rom0[0x150] = tpp1Rev[0];
|
|
||||||
rom0[0x151] = tpp1Rev[1];
|
|
||||||
|
|
||||||
if (ramSize != UNSPECIFIED) {
|
overwriteBytes(rom0, 0x150, tpp1Rev, sizeof(tpp1Rev), "TPP1 revision number");
|
||||||
warnNonZero(rom0, 0x152, 1, "RAM size");
|
|
||||||
rom0[0x152] = ramSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
warnNonZero(rom0, 0x153, 1, "TPP1 feature flags");
|
if (ramSize != UNSPECIFIED)
|
||||||
rom0[0x153] = cartridgeType & 0xFF;
|
overwriteByte(rom0, 0x152, ramSize, "RAM size");
|
||||||
|
|
||||||
|
overwriteByte(rom0, 0x153, cartridgeType & 0xFF, "TPP1 feature flags");
|
||||||
} else {
|
} else {
|
||||||
// Regular mappers
|
// Regular mappers
|
||||||
|
|
||||||
if (ramSize != UNSPECIFIED) {
|
if (ramSize != UNSPECIFIED)
|
||||||
warnNonZero(rom0, 0x149, 1, "RAM size");
|
overwriteByte(rom0, 0x149, ramSize, "RAM size");
|
||||||
rom0[0x149] = ramSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!japanese) {
|
if (!japanese)
|
||||||
warnNonZero(rom0, 0x14a, 1, "destination code");
|
overwriteByte(rom0, 0x14A, 0x01, "destination code");
|
||||||
rom0[0x14a] = 0x01;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldLicensee != UNSPECIFIED) {
|
if (oldLicensee != UNSPECIFIED)
|
||||||
warnNonZero(rom0, 0x14b, 1, "old licensee code");
|
overwriteByte(rom0, 0x14B, oldLicensee, "old licensee code");
|
||||||
rom0[0x14b] = oldLicensee;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (romVersion != UNSPECIFIED) {
|
if (romVersion != UNSPECIFIED)
|
||||||
warnNonZero(rom0, 0x14c, 1, "mask ROM version number");
|
overwriteByte(rom0, 0x14C, romVersion, "mask ROM version number");
|
||||||
rom0[0x14c] = romVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remain to be handled the ROM size, and header checksum.
|
// Remain to be handled the ROM size, and header checksum.
|
||||||
// The latter depends on the former, and so will be handled after it.
|
// The latter depends on the former, and so will be handled after it.
|
||||||
@@ -1004,8 +1016,7 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
static_assert(0x10000 * BANK_SIZE <= SSIZE_MAX, "Max input file size too large for OS");
|
static_assert(0x10000 * BANK_SIZE <= SSIZE_MAX, "Max input file size too large for OS");
|
||||||
if (nbBanks == 0x10000) {
|
if (nbBanks == 0x10000) {
|
||||||
report("FATAL: \"%s\" has more than 65536 banks\n", name);
|
report("FATAL: \"%s\" has more than 65536 banks\n", name);
|
||||||
free(romx);
|
goto cleanup;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
nbBanks++;
|
nbBanks++;
|
||||||
|
|
||||||
@@ -1055,17 +1066,19 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
if (fixSpec & (FIX_HEADER_SUM | TRASH_HEADER_SUM)) {
|
if (fixSpec & (FIX_HEADER_SUM | TRASH_HEADER_SUM)) {
|
||||||
uint8_t sum = 0;
|
uint8_t sum = 0;
|
||||||
|
|
||||||
for (uint16_t i = 0x134; i < 0x14d; i++)
|
for (uint16_t i = 0x134; i < 0x14D; i++)
|
||||||
sum -= rom0[i] + 1;
|
sum -= rom0[i] + 1;
|
||||||
warnNonZero(rom0, 0x14d, 1, "header checksum");
|
|
||||||
rom0[0x14d] = fixSpec & TRASH_HEADER_SUM ? ~sum : sum;
|
overwriteByte(rom0, 0x14D, fixSpec & TRASH_HEADER_SUM ? ~sum : sum,
|
||||||
|
"header checksum");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fixSpec & (FIX_GLOBAL_SUM | TRASH_GLOBAL_SUM)) {
|
if (fixSpec & (FIX_GLOBAL_SUM | TRASH_GLOBAL_SUM)) {
|
||||||
// Computation of the global checksum assumes 0s being stored in its place
|
// Computation of the global checksum does not include the checksum bytes
|
||||||
rom0[0x14e] = 0;
|
assert(rom0Len >= 0x14E);
|
||||||
rom0[0x14f] = 0;
|
for (uint16_t i = 0; i < 0x14E; i++)
|
||||||
for (uint16_t i = 0; i < rom0Len; i++)
|
globalSum += rom0[i];
|
||||||
|
for (uint16_t i = 0x150; i < rom0Len; i++)
|
||||||
globalSum += rom0[i];
|
globalSum += rom0[i];
|
||||||
// Pipes have already read ROMX and updated globalSum, but not regular files
|
// Pipes have already read ROMX and updated globalSum, but not regular files
|
||||||
if (input == output) {
|
if (input == output) {
|
||||||
@@ -1081,9 +1094,10 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
|
|
||||||
if (fixSpec & TRASH_GLOBAL_SUM)
|
if (fixSpec & TRASH_GLOBAL_SUM)
|
||||||
globalSum = ~globalSum;
|
globalSum = ~globalSum;
|
||||||
warnNonZero(rom0, 0x14e, 2, "global checksum");
|
|
||||||
rom0[0x14e] = globalSum >> 8;
|
uint8_t bytes[2] = {globalSum >> 8, globalSum & 0xFF};
|
||||||
rom0[0x14f] = globalSum & 0xff;
|
|
||||||
|
overwriteBytes(rom0, 0x14E, bytes, sizeof(bytes), "global checksum");
|
||||||
}
|
}
|
||||||
|
|
||||||
// In case the output depends on the input, reset to the beginning of the file, and only
|
// In case the output depends on the input, reset to the beginning of the file, and only
|
||||||
@@ -1091,7 +1105,7 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
if (input == output) {
|
if (input == output) {
|
||||||
if (lseek(output, 0, SEEK_SET) == (off_t)-1) {
|
if (lseek(output, 0, SEEK_SET) == (off_t)-1) {
|
||||||
report("FATAL: Failed to rewind \"%s\": %s\n", name, strerror(errno));
|
report("FATAL: Failed to rewind \"%s\": %s\n", name, strerror(errno));
|
||||||
goto free_romx;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
// If modifying the file in-place, we only need to edit the header
|
// If modifying the file in-place, we only need to edit the header
|
||||||
// However, padding may have modified ROM0 (added padding), so don't in that case
|
// However, padding may have modified ROM0 (added padding), so don't in that case
|
||||||
@@ -1102,11 +1116,11 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
|
|
||||||
if (writeLen == -1) {
|
if (writeLen == -1) {
|
||||||
report("FATAL: Failed to write \"%s\"'s ROM0: %s\n", name, strerror(errno));
|
report("FATAL: Failed to write \"%s\"'s ROM0: %s\n", name, strerror(errno));
|
||||||
goto free_romx;
|
goto cleanup;
|
||||||
} else if (writeLen < rom0Len) {
|
} else if (writeLen < rom0Len) {
|
||||||
report("FATAL: Could only write %jd of \"%s\"'s %jd ROM0 bytes\n",
|
report("FATAL: Could only write %jd of \"%s\"'s %jd ROM0 bytes\n",
|
||||||
(intmax_t)writeLen, name, (intmax_t)rom0Len);
|
(intmax_t)writeLen, name, (intmax_t)rom0Len);
|
||||||
goto free_romx;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output ROMX if it was buffered
|
// Output ROMX if it was buffered
|
||||||
@@ -1116,11 +1130,11 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
writeLen = writeBytes(output, romx, totalRomxLen);
|
writeLen = writeBytes(output, romx, totalRomxLen);
|
||||||
if (writeLen == -1) {
|
if (writeLen == -1) {
|
||||||
report("FATAL: Failed to write \"%s\"'s ROMX: %s\n", name, strerror(errno));
|
report("FATAL: Failed to write \"%s\"'s ROMX: %s\n", name, strerror(errno));
|
||||||
goto free_romx;
|
goto cleanup;
|
||||||
} else if ((size_t)writeLen < totalRomxLen) {
|
} else if ((size_t)writeLen < totalRomxLen) {
|
||||||
report("FATAL: Could only write %jd of \"%s\"'s %zu ROMX bytes\n",
|
report("FATAL: Could only write %jd of \"%s\"'s %zu ROMX bytes\n",
|
||||||
(intmax_t)writeLen, name, totalRomxLen);
|
(intmax_t)writeLen, name, totalRomxLen);
|
||||||
goto free_romx;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1130,7 +1144,7 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
if (lseek(output, 0, SEEK_END) == (off_t)-1) {
|
if (lseek(output, 0, SEEK_END) == (off_t)-1) {
|
||||||
report("FATAL: Failed to seek to end of \"%s\": %s\n",
|
report("FATAL: Failed to seek to end of \"%s\": %s\n",
|
||||||
name, strerror(errno));
|
name, strerror(errno));
|
||||||
goto free_romx;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
memset(bank, padValue, sizeof(bank));
|
memset(bank, padValue, sizeof(bank));
|
||||||
@@ -1152,7 +1166,7 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free_romx:
|
cleanup:
|
||||||
free(romx);
|
free(romx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1177,7 +1191,7 @@ static bool processFilename(char const *name)
|
|||||||
if (input == -1) {
|
if (input == -1) {
|
||||||
report("FATAL: Failed to open \"%s\" for reading+writing: %s\n",
|
report("FATAL: Failed to open \"%s\" for reading+writing: %s\n",
|
||||||
name, strerror(errno));
|
name, strerror(errno));
|
||||||
goto fail;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fstat(input, &stat) == -1) {
|
if (fstat(input, &stat) == -1) {
|
||||||
@@ -1196,8 +1210,8 @@ static bool processFilename(char const *name)
|
|||||||
|
|
||||||
close(input);
|
close(input);
|
||||||
}
|
}
|
||||||
|
finish:
|
||||||
if (nbErrors)
|
if (nbErrors)
|
||||||
fail:
|
|
||||||
fprintf(stderr, "Fixing \"%s\" failed with %u error%s\n",
|
fprintf(stderr, "Fixing \"%s\" failed with %u error%s\n",
|
||||||
name, nbErrors, nbErrors == 1 ? "" : "s");
|
name, nbErrors, nbErrors == 1 ? "" : "s");
|
||||||
return nbErrors;
|
return nbErrors;
|
||||||
@@ -1254,12 +1268,12 @@ do { \
|
|||||||
#define SPEC_H TRASH_HEADER_SUM
|
#define SPEC_H TRASH_HEADER_SUM
|
||||||
#define SPEC_g FIX_GLOBAL_SUM
|
#define SPEC_g FIX_GLOBAL_SUM
|
||||||
#define SPEC_G TRASH_GLOBAL_SUM
|
#define SPEC_G TRASH_GLOBAL_SUM
|
||||||
#define overrideSpec(new, bad) \
|
#define overrideSpec(cur, bad) \
|
||||||
do { \
|
do { \
|
||||||
if (fixSpec & SPEC_##bad) \
|
if (fixSpec & SPEC_##bad) \
|
||||||
fprintf(stderr, \
|
fprintf(stderr, \
|
||||||
"warning: '" #new "' overriding '" #bad "' in fix spec\n"); \
|
"warning: '" #cur "' overriding '" #bad "' in fix spec\n"); \
|
||||||
fixSpec = (fixSpec & ~SPEC_##bad) | SPEC_##new; \
|
fixSpec = (fixSpec & ~SPEC_##bad) | SPEC_##cur; \
|
||||||
} while (0)
|
} while (0)
|
||||||
case 'l':
|
case 'l':
|
||||||
overrideSpec(l, L);
|
overrideSpec(l, L);
|
||||||
@@ -1349,6 +1363,10 @@ do { \
|
|||||||
parseByte(romVersion, "n");
|
parseByte(romVersion, "n");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'O':
|
||||||
|
overwriteRom = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'p':
|
case 'p':
|
||||||
parseByte(padValue, "p");
|
parseByte(padValue, "p");
|
||||||
break;
|
break;
|
||||||
@@ -1390,11 +1408,11 @@ do { \
|
|||||||
#undef parseByte
|
#undef parseByte
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((cartridgeType & 0xff00) == TPP1 && !japanese)
|
if ((cartridgeType & 0xFF00) == TPP1 && !japanese)
|
||||||
fprintf(stderr, "warning: TPP1 overwrites region flag for its identification code, ignoring `-j`\n");
|
fprintf(stderr, "warning: TPP1 overwrites region flag for its identification code, ignoring `-j`\n");
|
||||||
|
|
||||||
// Check that RAM size is correct for "standard" mappers
|
// Check that RAM size is correct for "standard" mappers
|
||||||
if (ramSize != UNSPECIFIED && (cartridgeType & 0xff00) == 0) {
|
if (ramSize != UNSPECIFIED && (cartridgeType & 0xFF00) == 0) {
|
||||||
if (cartridgeType == ROM_RAM || cartridgeType == ROM_RAM_BATTERY) {
|
if (cartridgeType == ROM_RAM || cartridgeType == ROM_RAM_BATTERY) {
|
||||||
if (ramSize != 1)
|
if (ramSize != 1)
|
||||||
fprintf(stderr, "warning: MBC \"%s\" should have 2kiB of RAM (-r 1)\n",
|
fprintf(stderr, "warning: MBC \"%s\" should have 2kiB of RAM (-r 1)\n",
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
.Nd Game Boy header utility and checksum fixer
|
.Nd Game Boy header utility and checksum fixer
|
||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm
|
.Nm
|
||||||
.Op Fl jsVv
|
.Op Fl jOsVv
|
||||||
.Op Fl C | c
|
.Op Fl C | c
|
||||||
.Op Fl f Ar fix_spec
|
.Op Fl f Ar fix_spec
|
||||||
.Op Fl i Ar game_id
|
.Op Fl i Ar game_id
|
||||||
@@ -114,7 +114,9 @@ Set the MBC type
|
|||||||
to a given value from 0 to 0xFF.
|
to a given value from 0 to 0xFF.
|
||||||
.Pp
|
.Pp
|
||||||
This value may also be an MBC name.
|
This value may also be an MBC name.
|
||||||
The list of accepted names can be obtained by passing "help" as the argument.
|
The list of accepted names can be obtained by passing
|
||||||
|
.Ql Cm help
|
||||||
|
as the argument.
|
||||||
Any amount of whitespace (space and tabs) is allowed around plus signs, and the order of "components" is free, as long as the MBC name is first.
|
Any amount of whitespace (space and tabs) is allowed around plus signs, and the order of "components" is free, as long as the MBC name is first.
|
||||||
There are special considerations to take for the TPP1 mapper; see the
|
There are special considerations to take for the TPP1 mapper; see the
|
||||||
.Sx TPP1
|
.Sx TPP1
|
||||||
@@ -123,6 +125,8 @@ section below.
|
|||||||
Set the ROM version
|
Set the ROM version
|
||||||
.Pq Ad 0x14C
|
.Pq Ad 0x14C
|
||||||
to a given value from 0 to 0xFF.
|
to a given value from 0 to 0xFF.
|
||||||
|
.It Fl O , Fl Fl overwrite
|
||||||
|
Allow overwriting different non-zero bytes in the header without a warning being emitted.
|
||||||
.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
|
||||||
Pad the ROM image to a valid size with a given pad value from 0 to 255 (0xFF).
|
Pad the ROM image to a valid size with a given pad value from 0 to 255 (0xFF).
|
||||||
.Nm
|
.Nm
|
||||||
|
|||||||
764
src/gbz80.7
764
src/gbz80.7
File diff suppressed because it is too large
Load Diff
31
src/gfx/gb.c
31
src/gfx/gb.c
@@ -20,7 +20,7 @@ void transpose_tiles(struct GBImage *gb, int width)
|
|||||||
|
|
||||||
newdata = calloc(gb->size, 1);
|
newdata = calloc(gb->size, 1);
|
||||||
if (!newdata)
|
if (!newdata)
|
||||||
err(1, "%s: Failed to allocate memory for new data", __func__);
|
err("%s: Failed to allocate memory for new data", __func__);
|
||||||
|
|
||||||
for (i = 0; i < gb->size; i++) {
|
for (i = 0; i < gb->size; i++) {
|
||||||
newbyte = i / (8 * depth) * width * 8 * depth;
|
newbyte = i / (8 * depth) * width * 8 * depth;
|
||||||
@@ -37,15 +37,14 @@ void transpose_tiles(struct GBImage *gb, int width)
|
|||||||
|
|
||||||
void raw_to_gb(const struct RawIndexedImage *raw_image, struct GBImage *gb)
|
void raw_to_gb(const struct RawIndexedImage *raw_image, struct GBImage *gb)
|
||||||
{
|
{
|
||||||
int x, y, byte;
|
|
||||||
uint8_t index;
|
uint8_t index;
|
||||||
|
|
||||||
for (y = 0; y < raw_image->height; y++) {
|
for (unsigned int y = 0; y < raw_image->height; y++) {
|
||||||
for (x = 0; x < raw_image->width; x++) {
|
for (unsigned int x = 0; x < raw_image->width; x++) {
|
||||||
index = raw_image->data[y][x];
|
index = raw_image->data[y][x];
|
||||||
index &= (1 << depth) - 1;
|
index &= (1 << depth) - 1;
|
||||||
|
|
||||||
byte = y * depth
|
unsigned int byte = y * depth
|
||||||
+ x / 8 * raw_image->height / 8 * 8 * depth;
|
+ x / 8 * raw_image->height / 8 * 8 * depth;
|
||||||
gb->data[byte] |= (index & 1) << (7 - x % 8);
|
gb->data[byte] |= (index & 1) << (7 - x % 8);
|
||||||
if (depth == 2) {
|
if (depth == 2) {
|
||||||
@@ -65,7 +64,7 @@ void output_file(const struct Options *opts, const struct GBImage *gb)
|
|||||||
|
|
||||||
f = fopen(opts->outfile, "wb");
|
f = fopen(opts->outfile, "wb");
|
||||||
if (!f)
|
if (!f)
|
||||||
err(1, "%s: Opening output file '%s' failed", __func__,
|
err("%s: Opening output file '%s' failed", __func__,
|
||||||
opts->outfile);
|
opts->outfile);
|
||||||
|
|
||||||
fwrite(gb->data, 1, gb->size - gb->trim * 8 * depth, f);
|
fwrite(gb->data, 1, gb->size - gb->trim * 8 * depth, f);
|
||||||
@@ -141,7 +140,7 @@ int get_mirrored_tile_index(uint8_t *tile, uint8_t **tiles, int num_tiles,
|
|||||||
|
|
||||||
tile_yflip = malloc(tile_size);
|
tile_yflip = malloc(tile_size);
|
||||||
if (!tile_yflip)
|
if (!tile_yflip)
|
||||||
err(1, "%s: Failed to allocate memory for Y flip of tile",
|
err("%s: Failed to allocate memory for Y flip of tile",
|
||||||
__func__);
|
__func__);
|
||||||
yflip(tile, tile_yflip, tile_size);
|
yflip(tile, tile_yflip, tile_size);
|
||||||
index = get_tile_index(tile_yflip, tiles, num_tiles, tile_size);
|
index = get_tile_index(tile_yflip, tiles, num_tiles, tile_size);
|
||||||
@@ -153,7 +152,7 @@ int get_mirrored_tile_index(uint8_t *tile, uint8_t **tiles, int num_tiles,
|
|||||||
|
|
||||||
tile_xflip = malloc(tile_size);
|
tile_xflip = malloc(tile_size);
|
||||||
if (!tile_xflip)
|
if (!tile_xflip)
|
||||||
err(1, "%s: Failed to allocate memory for X flip of tile",
|
err("%s: Failed to allocate memory for X flip of tile",
|
||||||
__func__);
|
__func__);
|
||||||
xflip(tile, tile_xflip, tile_size);
|
xflip(tile, tile_xflip, tile_size);
|
||||||
index = get_tile_index(tile_xflip, tiles, num_tiles, tile_size);
|
index = get_tile_index(tile_xflip, tiles, num_tiles, tile_size);
|
||||||
@@ -198,13 +197,13 @@ void create_mapfiles(const struct Options *opts, struct GBImage *gb,
|
|||||||
|
|
||||||
tiles = calloc(max_tiles, sizeof(*tiles));
|
tiles = calloc(max_tiles, sizeof(*tiles));
|
||||||
if (!tiles)
|
if (!tiles)
|
||||||
err(1, "%s: Failed to allocate memory for tiles", __func__);
|
err("%s: Failed to allocate memory for tiles", __func__);
|
||||||
num_tiles = 0;
|
num_tiles = 0;
|
||||||
|
|
||||||
if (*opts->tilemapfile) {
|
if (*opts->tilemapfile) {
|
||||||
tilemap->data = calloc(max_tiles, sizeof(*tilemap->data));
|
tilemap->data = calloc(max_tiles, sizeof(*tilemap->data));
|
||||||
if (!tilemap->data)
|
if (!tilemap->data)
|
||||||
err(1, "%s: Failed to allocate memory for tilemap data",
|
err("%s: Failed to allocate memory for tilemap data",
|
||||||
__func__);
|
__func__);
|
||||||
tilemap->size = 0;
|
tilemap->size = 0;
|
||||||
}
|
}
|
||||||
@@ -212,7 +211,7 @@ void create_mapfiles(const struct Options *opts, struct GBImage *gb,
|
|||||||
if (*opts->attrmapfile) {
|
if (*opts->attrmapfile) {
|
||||||
attrmap->data = calloc(max_tiles, sizeof(*attrmap->data));
|
attrmap->data = calloc(max_tiles, sizeof(*attrmap->data));
|
||||||
if (!attrmap->data)
|
if (!attrmap->data)
|
||||||
err(1, "%s: Failed to allocate memory for attrmap data",
|
err("%s: Failed to allocate memory for attrmap data",
|
||||||
__func__);
|
__func__);
|
||||||
attrmap->size = 0;
|
attrmap->size = 0;
|
||||||
}
|
}
|
||||||
@@ -222,7 +221,7 @@ void create_mapfiles(const struct Options *opts, struct GBImage *gb,
|
|||||||
flags = 0;
|
flags = 0;
|
||||||
tile = malloc(tile_size);
|
tile = malloc(tile_size);
|
||||||
if (!tile)
|
if (!tile)
|
||||||
err(1, "%s: Failed to allocate memory for tile",
|
err("%s: Failed to allocate memory for tile",
|
||||||
__func__);
|
__func__);
|
||||||
/*
|
/*
|
||||||
* If the input image doesn't fill the last tile,
|
* If the input image doesn't fill the last tile,
|
||||||
@@ -269,7 +268,7 @@ void create_mapfiles(const struct Options *opts, struct GBImage *gb,
|
|||||||
free(gb->data);
|
free(gb->data);
|
||||||
gb->data = malloc(tile_size * num_tiles);
|
gb->data = malloc(tile_size * num_tiles);
|
||||||
if (!gb->data)
|
if (!gb->data)
|
||||||
err(1, "%s: Failed to allocate memory for tile data",
|
err("%s: Failed to allocate memory for tile data",
|
||||||
__func__);
|
__func__);
|
||||||
for (i = 0; i < num_tiles; i++) {
|
for (i = 0; i < num_tiles; i++) {
|
||||||
tile = tiles[i];
|
tile = tiles[i];
|
||||||
@@ -292,7 +291,7 @@ void output_tilemap_file(const struct Options *opts,
|
|||||||
|
|
||||||
f = fopen(opts->tilemapfile, "wb");
|
f = fopen(opts->tilemapfile, "wb");
|
||||||
if (!f)
|
if (!f)
|
||||||
err(1, "%s: Opening tilemap file '%s' failed", __func__,
|
err("%s: Opening tilemap file '%s' failed", __func__,
|
||||||
opts->tilemapfile);
|
opts->tilemapfile);
|
||||||
|
|
||||||
fwrite(tilemap->data, 1, tilemap->size, f);
|
fwrite(tilemap->data, 1, tilemap->size, f);
|
||||||
@@ -309,7 +308,7 @@ void output_attrmap_file(const struct Options *opts,
|
|||||||
|
|
||||||
f = fopen(opts->attrmapfile, "wb");
|
f = fopen(opts->attrmapfile, "wb");
|
||||||
if (!f)
|
if (!f)
|
||||||
err(1, "%s: Opening attrmap file '%s' failed", __func__,
|
err("%s: Opening attrmap file '%s' failed", __func__,
|
||||||
opts->attrmapfile);
|
opts->attrmapfile);
|
||||||
|
|
||||||
fwrite(attrmap->data, 1, attrmap->size, f);
|
fwrite(attrmap->data, 1, attrmap->size, f);
|
||||||
@@ -352,7 +351,7 @@ void output_palette_file(const struct Options *opts,
|
|||||||
|
|
||||||
f = fopen(opts->palfile, "wb");
|
f = fopen(opts->palfile, "wb");
|
||||||
if (!f)
|
if (!f)
|
||||||
err(1, "%s: Opening palette file '%s' failed", __func__,
|
err("%s: Opening palette file '%s' failed", __func__,
|
||||||
opts->palfile);
|
opts->palfile);
|
||||||
|
|
||||||
for (i = 0; i < raw_image->num_colors; i++) {
|
for (i = 0; i < raw_image->num_colors; i++) {
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ int main(int argc, char *argv[])
|
|||||||
opts.infile = argv[argc - 1];
|
opts.infile = argv[argc - 1];
|
||||||
|
|
||||||
if (depth != 1 && depth != 2)
|
if (depth != 1 && depth != 2)
|
||||||
errx(1, "Depth option must be either 1 or 2.");
|
errx("Depth option must be either 1 or 2.");
|
||||||
|
|
||||||
colors = 1 << depth;
|
colors = 1 << depth;
|
||||||
|
|
||||||
@@ -200,17 +200,17 @@ int main(int argc, char *argv[])
|
|||||||
opts.trim = png_options.trim;
|
opts.trim = png_options.trim;
|
||||||
|
|
||||||
if (raw_image->width % 8) {
|
if (raw_image->width % 8) {
|
||||||
errx(1, "Input PNG file %s not sized correctly. The image's width must be a multiple of 8.",
|
errx("Input PNG file %s not sized correctly. The image's width must be a multiple of 8.",
|
||||||
opts.infile);
|
opts.infile);
|
||||||
}
|
}
|
||||||
if (raw_image->width / 8 > 1 && raw_image->height % 8) {
|
if (raw_image->width / 8 > 1 && raw_image->height % 8) {
|
||||||
errx(1, "Input PNG file %s not sized correctly. If the image is more than 1 tile wide, its height must be a multiple of 8.",
|
errx("Input PNG file %s not sized correctly. If the image is more than 1 tile wide, its height must be a multiple of 8.",
|
||||||
opts.infile);
|
opts.infile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.trim &&
|
if (opts.trim &&
|
||||||
opts.trim > (raw_image->width / 8) * (raw_image->height / 8) - 1) {
|
opts.trim > (raw_image->width / 8) * (raw_image->height / 8) - 1) {
|
||||||
errx(1, "Trim (%d) for input raw_image file '%s' too large (max: %u)",
|
errx("Trim (%d) for input raw_image file '%s' too large (max: %u)",
|
||||||
opts.trim, opts.infile,
|
opts.trim, opts.infile,
|
||||||
(raw_image->width / 8) * (raw_image->height / 8) - 1);
|
(raw_image->width / 8) * (raw_image->height / 8) - 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ struct RawIndexedImage *input_png_file(const struct Options *opts,
|
|||||||
|
|
||||||
f = fopen(opts->infile, "rb");
|
f = fopen(opts->infile, "rb");
|
||||||
if (!f)
|
if (!f)
|
||||||
err(1, "Opening input png file '%s' failed", opts->infile);
|
err("Opening input png file '%s' failed", opts->infile);
|
||||||
|
|
||||||
initialize_png(&img, f);
|
initialize_png(&img, f);
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ struct RawIndexedImage *input_png_file(const struct Options *opts,
|
|||||||
raw_image = truecolor_png_to_raw(&img); break;
|
raw_image = truecolor_png_to_raw(&img); break;
|
||||||
default:
|
default:
|
||||||
/* Shouldn't happen, but might as well handle just in case. */
|
/* Shouldn't happen, but might as well handle just in case. */
|
||||||
errx(1, "Input PNG file is of invalid color type.");
|
errx("Input PNG file is of invalid color type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
get_text(&img, png_options);
|
get_text(&img, png_options);
|
||||||
@@ -83,7 +83,7 @@ void output_png_file(const struct Options *opts,
|
|||||||
if (opts->debug) {
|
if (opts->debug) {
|
||||||
outfile = malloc(strlen(opts->infile) + 5);
|
outfile = malloc(strlen(opts->infile) + 5);
|
||||||
if (!outfile)
|
if (!outfile)
|
||||||
err(1, "%s: Failed to allocate memory for outfile",
|
err("%s: Failed to allocate memory for outfile",
|
||||||
__func__);
|
__func__);
|
||||||
strcpy(outfile, opts->infile);
|
strcpy(outfile, opts->infile);
|
||||||
strcat(outfile, ".out");
|
strcat(outfile, ".out");
|
||||||
@@ -93,16 +93,19 @@ void output_png_file(const struct Options *opts,
|
|||||||
|
|
||||||
f = fopen(outfile, "wb");
|
f = fopen(outfile, "wb");
|
||||||
if (!f)
|
if (!f)
|
||||||
err(1, "Opening output png file '%s' failed", outfile);
|
err("Opening output png file '%s' failed", outfile);
|
||||||
|
|
||||||
|
if (opts->debug)
|
||||||
|
free(outfile);
|
||||||
|
|
||||||
img.png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
img.png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
||||||
NULL, NULL, NULL);
|
NULL, NULL, NULL);
|
||||||
if (!img.png)
|
if (!img.png)
|
||||||
errx(1, "Creating png structure failed");
|
errx("Creating png structure failed");
|
||||||
|
|
||||||
img.info = png_create_info_struct(img.png);
|
img.info = png_create_info_struct(img.png);
|
||||||
if (!img.info)
|
if (!img.info)
|
||||||
errx(1, "Creating png info structure failed");
|
errx("Creating png info structure failed");
|
||||||
|
|
||||||
if (setjmp(png_jmpbuf(img.png)))
|
if (setjmp(png_jmpbuf(img.png)))
|
||||||
exit(1);
|
exit(1);
|
||||||
@@ -115,7 +118,7 @@ void output_png_file(const struct Options *opts,
|
|||||||
|
|
||||||
png_palette = malloc(sizeof(*png_palette) * raw_image->num_colors);
|
png_palette = malloc(sizeof(*png_palette) * raw_image->num_colors);
|
||||||
if (!png_palette)
|
if (!png_palette)
|
||||||
err(1, "%s: Failed to allocate memory for PNG palette",
|
err("%s: Failed to allocate memory for PNG palette",
|
||||||
__func__);
|
__func__);
|
||||||
for (i = 0; i < raw_image->num_colors; i++) {
|
for (i = 0; i < raw_image->num_colors; i++) {
|
||||||
png_palette[i].red = raw_image->palette[i].red;
|
png_palette[i].red = raw_image->palette[i].red;
|
||||||
@@ -135,17 +138,13 @@ void output_png_file(const struct Options *opts,
|
|||||||
|
|
||||||
png_destroy_write_struct(&img.png, &img.info);
|
png_destroy_write_struct(&img.png, &img.info);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
if (opts->debug)
|
|
||||||
free(outfile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroy_raw_image(struct RawIndexedImage **raw_image_ptr_ptr)
|
void destroy_raw_image(struct RawIndexedImage **raw_image_ptr_ptr)
|
||||||
{
|
{
|
||||||
int y;
|
|
||||||
struct RawIndexedImage *raw_image = *raw_image_ptr_ptr;
|
struct RawIndexedImage *raw_image = *raw_image_ptr_ptr;
|
||||||
|
|
||||||
for (y = 0; y < raw_image->height; y++)
|
for (unsigned int y = 0; y < raw_image->height; y++)
|
||||||
free(raw_image->data[y]);
|
free(raw_image->data[y]);
|
||||||
|
|
||||||
free(raw_image->data);
|
free(raw_image->data);
|
||||||
@@ -159,11 +158,11 @@ static void initialize_png(struct PNGImage *img, FILE *f)
|
|||||||
img->png = png_create_read_struct(PNG_LIBPNG_VER_STRING,
|
img->png = png_create_read_struct(PNG_LIBPNG_VER_STRING,
|
||||||
NULL, NULL, NULL);
|
NULL, NULL, NULL);
|
||||||
if (!img->png)
|
if (!img->png)
|
||||||
errx(1, "Creating png structure failed");
|
errx("Creating png structure failed");
|
||||||
|
|
||||||
img->info = png_create_info_struct(img->png);
|
img->info = png_create_info_struct(img->png);
|
||||||
if (!img->info)
|
if (!img->info)
|
||||||
errx(1, "Creating png info structure failed");
|
errx("Creating png info structure failed");
|
||||||
|
|
||||||
if (setjmp(png_jmpbuf(img->png)))
|
if (setjmp(png_jmpbuf(img->png)))
|
||||||
exit(1);
|
exit(1);
|
||||||
@@ -182,7 +181,7 @@ static void read_png(struct PNGImage *img);
|
|||||||
static struct RawIndexedImage *create_raw_image(int width, int height,
|
static struct RawIndexedImage *create_raw_image(int width, int height,
|
||||||
int num_colors);
|
int num_colors);
|
||||||
static void set_raw_image_palette(struct RawIndexedImage *raw_image,
|
static void set_raw_image_palette(struct RawIndexedImage *raw_image,
|
||||||
const png_color *palette, int num_colors);
|
png_color const *palette, int num_colors);
|
||||||
|
|
||||||
static struct RawIndexedImage *indexed_png_to_raw(struct PNGImage *img)
|
static struct RawIndexedImage *indexed_png_to_raw(struct PNGImage *img)
|
||||||
{
|
{
|
||||||
@@ -215,13 +214,13 @@ static struct RawIndexedImage *indexed_png_to_raw(struct PNGImage *img)
|
|||||||
original_palette = palette;
|
original_palette = palette;
|
||||||
palette = malloc(sizeof(*palette) * colors_in_PLTE);
|
palette = malloc(sizeof(*palette) * colors_in_PLTE);
|
||||||
if (!palette)
|
if (!palette)
|
||||||
err(1, "%s: Failed to allocate memory for palette",
|
err("%s: Failed to allocate memory for palette",
|
||||||
__func__);
|
__func__);
|
||||||
colors_in_new_palette = 0;
|
colors_in_new_palette = 0;
|
||||||
old_to_new_palette = malloc(sizeof(*old_to_new_palette)
|
old_to_new_palette = malloc(sizeof(*old_to_new_palette)
|
||||||
* colors_in_PLTE);
|
* colors_in_PLTE);
|
||||||
if (!old_to_new_palette)
|
if (!old_to_new_palette)
|
||||||
err(1, "%s: Failed to allocate memory for new palette",
|
err("%s: Failed to allocate memory for new palette",
|
||||||
__func__);
|
__func__);
|
||||||
|
|
||||||
for (i = 0; i < num_trans; i++) {
|
for (i = 0; i < num_trans; i++) {
|
||||||
@@ -243,7 +242,7 @@ static struct RawIndexedImage *indexed_png_to_raw(struct PNGImage *img)
|
|||||||
sizeof(*palette) *
|
sizeof(*palette) *
|
||||||
colors_in_new_palette);
|
colors_in_new_palette);
|
||||||
if (!palette)
|
if (!palette)
|
||||||
err(1, "%s: Failed to allocate memory for palette",
|
err("%s: Failed to allocate memory for palette",
|
||||||
__func__);
|
__func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,7 +290,7 @@ static void rgba_png_palette(struct PNGImage *img,
|
|||||||
png_color **palette_ptr_ptr, int *num_colors);
|
png_color **palette_ptr_ptr, int *num_colors);
|
||||||
static struct RawIndexedImage
|
static struct RawIndexedImage
|
||||||
*processed_rgba_png_to_raw(const struct PNGImage *img,
|
*processed_rgba_png_to_raw(const struct PNGImage *img,
|
||||||
const png_color *palette,
|
png_color const *palette,
|
||||||
int colors_in_palette);
|
int colors_in_palette);
|
||||||
|
|
||||||
static struct RawIndexedImage *truecolor_png_to_raw(struct PNGImage *img)
|
static struct RawIndexedImage *truecolor_png_to_raw(struct PNGImage *img)
|
||||||
@@ -352,7 +351,7 @@ static void rgba_PLTE_palette(struct PNGImage *img,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void update_built_palette(png_color *palette,
|
static void update_built_palette(png_color *palette,
|
||||||
const png_color *pixel_color, png_byte alpha,
|
png_color const *pixel_color, png_byte alpha,
|
||||||
int *num_colors, bool *only_grayscale);
|
int *num_colors, bool *only_grayscale);
|
||||||
static int fit_grayscale_palette(png_color *palette, int *num_colors);
|
static int fit_grayscale_palette(png_color *palette, int *num_colors);
|
||||||
static void order_color_palette(png_color *palette, int num_colors);
|
static void order_color_palette(png_color *palette, int num_colors);
|
||||||
@@ -372,7 +371,7 @@ static void rgba_build_palette(struct PNGImage *img,
|
|||||||
*/
|
*/
|
||||||
*palette_ptr_ptr = calloc(colors, sizeof(**palette_ptr_ptr));
|
*palette_ptr_ptr = calloc(colors, sizeof(**palette_ptr_ptr));
|
||||||
if (!*palette_ptr_ptr)
|
if (!*palette_ptr_ptr)
|
||||||
err(1, "%s: Failed to allocate memory for palette", __func__);
|
err("%s: Failed to allocate memory for palette", __func__);
|
||||||
palette = *palette_ptr_ptr;
|
palette = *palette_ptr_ptr;
|
||||||
*num_colors = 0;
|
*num_colors = 0;
|
||||||
|
|
||||||
@@ -398,7 +397,7 @@ static void rgba_build_palette(struct PNGImage *img,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void update_built_palette(png_color *palette,
|
static void update_built_palette(png_color *palette,
|
||||||
const png_color *pixel_color, png_byte alpha,
|
png_color const *pixel_color, png_byte alpha,
|
||||||
int *num_colors, bool *only_grayscale)
|
int *num_colors, bool *only_grayscale)
|
||||||
{
|
{
|
||||||
bool color_exists;
|
bool color_exists;
|
||||||
@@ -429,7 +428,7 @@ static void update_built_palette(png_color *palette,
|
|||||||
}
|
}
|
||||||
if (!color_exists) {
|
if (!color_exists) {
|
||||||
if (*num_colors == colors) {
|
if (*num_colors == colors) {
|
||||||
errx(1, "Too many colors in input PNG file to fit into a %d-bit palette (max %d).",
|
errx("Too many colors in input PNG file to fit into a %d-bit palette (max %d).",
|
||||||
depth, colors);
|
depth, colors);
|
||||||
}
|
}
|
||||||
palette[*num_colors] = *pixel_color;
|
palette[*num_colors] = *pixel_color;
|
||||||
@@ -445,9 +444,9 @@ static int fit_grayscale_palette(png_color *palette, int *num_colors)
|
|||||||
int i, shade_index;
|
int i, shade_index;
|
||||||
|
|
||||||
if (!fitted_palette)
|
if (!fitted_palette)
|
||||||
err(1, "%s: Failed to allocate memory for palette", __func__);
|
err("%s: Failed to allocate memory for palette", __func__);
|
||||||
if (!set_indices)
|
if (!set_indices)
|
||||||
err(1, "%s: Failed to allocate memory for indices", __func__);
|
err("%s: Failed to allocate memory for indices", __func__);
|
||||||
|
|
||||||
fitted_palette[0].red = 0xFF;
|
fitted_palette[0].red = 0xFF;
|
||||||
fitted_palette[0].green = 0xFF;
|
fitted_palette[0].green = 0xFF;
|
||||||
@@ -491,7 +490,7 @@ struct ColorWithLuminance {
|
|||||||
int luminance;
|
int luminance;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int compare_luminance(const void *a, const void *b)
|
static int compare_luminance(void const *a, void const *b)
|
||||||
{
|
{
|
||||||
const struct ColorWithLuminance *x, *y;
|
const struct ColorWithLuminance *x, *y;
|
||||||
|
|
||||||
@@ -508,7 +507,7 @@ static void order_color_palette(png_color *palette, int num_colors)
|
|||||||
malloc(sizeof(*palette_with_luminance) * num_colors);
|
malloc(sizeof(*palette_with_luminance) * num_colors);
|
||||||
|
|
||||||
if (!palette_with_luminance)
|
if (!palette_with_luminance)
|
||||||
err(1, "%s: Failed to allocate memory for palette", __func__);
|
err("%s: Failed to allocate memory for palette", __func__);
|
||||||
|
|
||||||
for (i = 0; i < num_colors; i++) {
|
for (i = 0; i < num_colors; i++) {
|
||||||
/*
|
/*
|
||||||
@@ -531,12 +530,12 @@ static void order_color_palette(png_color *palette, int num_colors)
|
|||||||
static void put_raw_image_pixel(struct RawIndexedImage *raw_image,
|
static void put_raw_image_pixel(struct RawIndexedImage *raw_image,
|
||||||
const struct PNGImage *img,
|
const struct PNGImage *img,
|
||||||
int *value_index, int x, int y,
|
int *value_index, int x, int y,
|
||||||
const png_color *palette,
|
png_color const *palette,
|
||||||
int colors_in_palette);
|
int colors_in_palette);
|
||||||
|
|
||||||
static struct RawIndexedImage
|
static struct RawIndexedImage
|
||||||
*processed_rgba_png_to_raw(const struct PNGImage *img,
|
*processed_rgba_png_to_raw(const struct PNGImage *img,
|
||||||
const png_color *palette,
|
png_color const *palette,
|
||||||
int colors_in_palette)
|
int colors_in_palette)
|
||||||
{
|
{
|
||||||
struct RawIndexedImage *raw_image;
|
struct RawIndexedImage *raw_image;
|
||||||
@@ -561,13 +560,13 @@ static struct RawIndexedImage
|
|||||||
return raw_image;
|
return raw_image;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t palette_index_of(const png_color *palette,
|
static uint8_t palette_index_of(png_color const *palette,
|
||||||
int num_colors, const png_color *color);
|
int num_colors, png_color const *color);
|
||||||
|
|
||||||
static void put_raw_image_pixel(struct RawIndexedImage *raw_image,
|
static void put_raw_image_pixel(struct RawIndexedImage *raw_image,
|
||||||
const struct PNGImage *img,
|
const struct PNGImage *img,
|
||||||
int *value_index, int x, int y,
|
int *value_index, int x, int y,
|
||||||
const png_color *palette,
|
png_color const *palette,
|
||||||
int colors_in_palette)
|
int colors_in_palette)
|
||||||
{
|
{
|
||||||
png_color pixel_color;
|
png_color pixel_color;
|
||||||
@@ -588,8 +587,8 @@ static void put_raw_image_pixel(struct RawIndexedImage *raw_image,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t palette_index_of(const png_color *palette,
|
static uint8_t palette_index_of(png_color const *palette,
|
||||||
int num_colors, const png_color *color)
|
int num_colors, png_color const *color)
|
||||||
{
|
{
|
||||||
uint8_t i;
|
uint8_t i;
|
||||||
|
|
||||||
@@ -600,7 +599,7 @@ static uint8_t palette_index_of(const png_color *palette,
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
errx(1, "The input PNG file contains colors that don't appear in its embedded palette.");
|
errx("The input PNG file contains colors that don't appear in its embedded palette.");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void read_png(struct PNGImage *img)
|
static void read_png(struct PNGImage *img)
|
||||||
@@ -611,12 +610,12 @@ static void read_png(struct PNGImage *img)
|
|||||||
|
|
||||||
img->data = malloc(sizeof(*img->data) * img->height);
|
img->data = malloc(sizeof(*img->data) * img->height);
|
||||||
if (!img->data)
|
if (!img->data)
|
||||||
err(1, "%s: Failed to allocate memory for image data",
|
err("%s: Failed to allocate memory for image data",
|
||||||
__func__);
|
__func__);
|
||||||
for (y = 0; y < img->height; y++) {
|
for (y = 0; y < img->height; y++) {
|
||||||
img->data[y] = malloc(png_get_rowbytes(img->png, img->info));
|
img->data[y] = malloc(png_get_rowbytes(img->png, img->info));
|
||||||
if (!img->data[y])
|
if (!img->data[y])
|
||||||
err(1, "%s: Failed to allocate memory for image data",
|
err("%s: Failed to allocate memory for image data",
|
||||||
__func__);
|
__func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -632,7 +631,7 @@ static struct RawIndexedImage *create_raw_image(int width, int height,
|
|||||||
|
|
||||||
raw_image = malloc(sizeof(*raw_image));
|
raw_image = malloc(sizeof(*raw_image));
|
||||||
if (!raw_image)
|
if (!raw_image)
|
||||||
err(1, "%s: Failed to allocate memory for raw image",
|
err("%s: Failed to allocate memory for raw image",
|
||||||
__func__);
|
__func__);
|
||||||
|
|
||||||
raw_image->width = width;
|
raw_image->width = width;
|
||||||
@@ -641,18 +640,18 @@ static struct RawIndexedImage *create_raw_image(int width, int height,
|
|||||||
|
|
||||||
raw_image->palette = malloc(sizeof(*raw_image->palette) * num_colors);
|
raw_image->palette = malloc(sizeof(*raw_image->palette) * num_colors);
|
||||||
if (!raw_image->palette)
|
if (!raw_image->palette)
|
||||||
err(1, "%s: Failed to allocate memory for raw image palette",
|
err("%s: Failed to allocate memory for raw image palette",
|
||||||
__func__);
|
__func__);
|
||||||
|
|
||||||
raw_image->data = malloc(sizeof(*raw_image->data) * height);
|
raw_image->data = malloc(sizeof(*raw_image->data) * height);
|
||||||
if (!raw_image->data)
|
if (!raw_image->data)
|
||||||
err(1, "%s: Failed to allocate memory for raw image data",
|
err("%s: Failed to allocate memory for raw image data",
|
||||||
__func__);
|
__func__);
|
||||||
for (y = 0; y < height; y++) {
|
for (y = 0; y < height; y++) {
|
||||||
raw_image->data[y] = malloc(sizeof(*raw_image->data[y])
|
raw_image->data[y] = malloc(sizeof(*raw_image->data[y])
|
||||||
* width);
|
* width);
|
||||||
if (!raw_image->data[y])
|
if (!raw_image->data[y])
|
||||||
err(1, "%s: Failed to allocate memory for raw image data",
|
err("%s: Failed to allocate memory for raw image data",
|
||||||
__func__);
|
__func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -660,12 +659,12 @@ static struct RawIndexedImage *create_raw_image(int width, int height,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void set_raw_image_palette(struct RawIndexedImage *raw_image,
|
static void set_raw_image_palette(struct RawIndexedImage *raw_image,
|
||||||
const png_color *palette, int num_colors)
|
png_color const *palette, int num_colors)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (num_colors > raw_image->num_colors) {
|
if (num_colors > raw_image->num_colors) {
|
||||||
errx(1, "Too many colors in input PNG file's palette to fit into a %d-bit palette (%d in input palette, max %d).",
|
errx("Too many colors in input PNG file's palette to fit into a %d-bit palette (%d in input palette, max %d).",
|
||||||
raw_image->num_colors >> 1,
|
raw_image->num_colors >> 1,
|
||||||
num_colors, raw_image->num_colors);
|
num_colors, raw_image->num_colors);
|
||||||
}
|
}
|
||||||
@@ -740,7 +739,7 @@ static void set_text(const struct PNGImage *img,
|
|||||||
|
|
||||||
text = malloc(sizeof(*text));
|
text = malloc(sizeof(*text));
|
||||||
if (!text)
|
if (!text)
|
||||||
err(1, "%s: Failed to allocate memory for PNG text",
|
err("%s: Failed to allocate memory for PNG text",
|
||||||
__func__);
|
__func__);
|
||||||
|
|
||||||
if (png_options->horizontal) {
|
if (png_options->horizontal) {
|
||||||
|
|||||||
@@ -12,8 +12,8 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "error.h"
|
||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
#include "extern/err.h"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The lower half of the hash is used to index the "master" table,
|
* The lower half of the hash is used to index the "master" table,
|
||||||
@@ -53,7 +53,7 @@ void **hash_AddElement(HashMap map, char const *key, void *element)
|
|||||||
struct HashMapEntry *newEntry = malloc(sizeof(*newEntry));
|
struct HashMapEntry *newEntry = malloc(sizeof(*newEntry));
|
||||||
|
|
||||||
if (!newEntry)
|
if (!newEntry)
|
||||||
err(1, "%s: Failed to allocate new entry", __func__);
|
err("%s: Failed to allocate new entry", __func__);
|
||||||
|
|
||||||
newEntry->hash = hashedKey >> HALF_HASH_NB_BITS;
|
newEntry->hash = hashedKey >> HALF_HASH_NB_BITS;
|
||||||
newEntry->key = key;
|
newEntry->key = key;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
#include "link/script.h"
|
#include "link/script.h"
|
||||||
#include "link/output.h"
|
#include "link/output.h"
|
||||||
|
|
||||||
#include "extern/err.h"
|
#include "error.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
|
||||||
struct MemoryLocation {
|
struct MemoryLocation {
|
||||||
@@ -46,13 +46,13 @@ static void initFreeSpace(void)
|
|||||||
for (enum SectionType type = 0; type < SECTTYPE_INVALID; type++) {
|
for (enum SectionType type = 0; type < SECTTYPE_INVALID; type++) {
|
||||||
memory[type] = malloc(sizeof(*memory[type]) * nbbanks(type));
|
memory[type] = malloc(sizeof(*memory[type]) * nbbanks(type));
|
||||||
if (!memory[type])
|
if (!memory[type])
|
||||||
err(1, "Failed to init free space for region %d", type);
|
err("Failed to init free space for region %d", type);
|
||||||
|
|
||||||
for (uint32_t bank = 0; bank < nbbanks(type); bank++) {
|
for (uint32_t bank = 0; bank < nbbanks(type); bank++) {
|
||||||
memory[type][bank].next =
|
memory[type][bank].next =
|
||||||
malloc(sizeof(*memory[type][0].next));
|
malloc(sizeof(*memory[type][0].next));
|
||||||
if (!memory[type][bank].next)
|
if (!memory[type][bank].next)
|
||||||
err(1, "Failed to init free space for region %d bank %" PRIu32,
|
err("Failed to init free space for region %d bank %" PRIu32,
|
||||||
type, bank);
|
type, bank);
|
||||||
memory[type][bank].next->address = startaddr[type];
|
memory[type][bank].next->address = startaddr[type];
|
||||||
memory[type][bank].next->size = maxsize[type];
|
memory[type][bank].next->size = maxsize[type];
|
||||||
@@ -159,9 +159,28 @@ static bool isLocationSuitable(struct Section const *section,
|
|||||||
static struct FreeSpace *getPlacement(struct Section const *section,
|
static struct FreeSpace *getPlacement(struct Section const *section,
|
||||||
struct MemoryLocation *location)
|
struct MemoryLocation *location)
|
||||||
{
|
{
|
||||||
location->bank = section->isBankFixed
|
static uint16_t curScrambleROM = 1;
|
||||||
? section->bank
|
static uint8_t curScrambleWRAM = 1;
|
||||||
: bankranges[section->type][0];
|
static uint8_t curScrambleSRAM = 1;
|
||||||
|
|
||||||
|
// Determine which bank we should start searching in
|
||||||
|
if (section->isBankFixed) {
|
||||||
|
location->bank = section->bank;
|
||||||
|
} else if (scrambleROMX && section->type == SECTTYPE_ROMX) {
|
||||||
|
location->bank = curScrambleROM++;
|
||||||
|
if (curScrambleROM > scrambleROMX)
|
||||||
|
curScrambleROM = 1;
|
||||||
|
} else if (scrambleWRAMX && section->type == SECTTYPE_WRAMX) {
|
||||||
|
location->bank = curScrambleWRAM++;
|
||||||
|
if (curScrambleWRAM > scrambleWRAMX)
|
||||||
|
curScrambleWRAM = 1;
|
||||||
|
} else if (scrambleSRAM && section->type == SECTTYPE_SRAM) {
|
||||||
|
location->bank = curScrambleSRAM++;
|
||||||
|
if (curScrambleSRAM > scrambleSRAM)
|
||||||
|
curScrambleSRAM = 0;
|
||||||
|
} else {
|
||||||
|
location->bank = bankranges[section->type][0];
|
||||||
|
}
|
||||||
struct FreeSpace *space;
|
struct FreeSpace *space;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@@ -282,7 +301,7 @@ static void placeSection(struct Section *section)
|
|||||||
struct FreeSpace *newSpace = malloc(sizeof(*newSpace));
|
struct FreeSpace *newSpace = malloc(sizeof(*newSpace));
|
||||||
|
|
||||||
if (!newSpace)
|
if (!newSpace)
|
||||||
err(1, "Failed to split new free space");
|
err("Failed to split new free space");
|
||||||
/* Append the new space after the chosen one */
|
/* Append the new space after the chosen one */
|
||||||
newSpace->prev = freeSpace;
|
newSpace->prev = freeSpace;
|
||||||
newSpace->next = freeSpace->next;
|
newSpace->next = freeSpace->next;
|
||||||
@@ -333,16 +352,16 @@ static void placeSection(struct Section *section)
|
|||||||
|
|
||||||
/* If a section failed to go to several places, nothing we can report */
|
/* If a section failed to go to several places, nothing we can report */
|
||||||
if (!section->isBankFixed || !section->isAddressFixed)
|
if (!section->isBankFixed || !section->isAddressFixed)
|
||||||
errx(1, "Unable to place \"%s\" (%s section) %s",
|
errx("Unable to place \"%s\" (%s section) %s",
|
||||||
section->name, typeNames[section->type], where);
|
section->name, typeNames[section->type], where);
|
||||||
/* If the section just can't fit the bank, report that */
|
/* If the section just can't fit the bank, report that */
|
||||||
else if (section->org + section->size > endaddr(section->type) + 1)
|
else if (section->org + section->size > endaddr(section->type) + 1)
|
||||||
errx(1, "Unable to place \"%s\" (%s section) %s: section runs past end of region ($%04x > $%04x)",
|
errx("Unable to place \"%s\" (%s section) %s: section runs past end of region ($%04x > $%04x)",
|
||||||
section->name, typeNames[section->type], where,
|
section->name, typeNames[section->type], where,
|
||||||
section->org + section->size, endaddr(section->type) + 1);
|
section->org + section->size, endaddr(section->type) + 1);
|
||||||
/* Otherwise there is overlap with another section */
|
/* Otherwise there is overlap with another section */
|
||||||
else
|
else
|
||||||
errx(1, "Unable to place \"%s\" (%s section) %s: section overlaps with \"%s\"",
|
errx("Unable to place \"%s\" (%s section) %s: section overlaps with \"%s\"",
|
||||||
section->name, typeNames[section->type], where,
|
section->name, typeNames[section->type], where,
|
||||||
out_OverlappingSection(section)->name);
|
out_OverlappingSection(section)->name);
|
||||||
}
|
}
|
||||||
@@ -399,7 +418,7 @@ void assign_AssignSections(void)
|
|||||||
/* Generate linked lists of sections to assign */
|
/* Generate linked lists of sections to assign */
|
||||||
sections = malloc(sizeof(*sections) * nbSectionsToAssign + 1);
|
sections = malloc(sizeof(*sections) * nbSectionsToAssign + 1);
|
||||||
if (!sections)
|
if (!sections)
|
||||||
err(1, "Failed to allocate memory for section assignment");
|
err("Failed to allocate memory for section assignment");
|
||||||
|
|
||||||
initFreeSpace();
|
initFreeSpace();
|
||||||
|
|
||||||
@@ -428,7 +447,7 @@ void assign_AssignSections(void)
|
|||||||
/* Overlaying requires only fully-constrained sections */
|
/* Overlaying requires only fully-constrained sections */
|
||||||
verbosePrint("Assigning other sections...\n");
|
verbosePrint("Assigning other sections...\n");
|
||||||
if (overlayFileName)
|
if (overlayFileName)
|
||||||
errx(1, "All sections must be fixed when using an overlay file; %" PRIu64 " %sn't",
|
errx("All sections must be fixed when using an overlay file; %" PRIu64 " %sn't",
|
||||||
nbSectionsToAssign, nbSectionsToAssign == 1 ? "is" : "are");
|
nbSectionsToAssign, nbSectionsToAssign == 1 ? "is" : "are");
|
||||||
|
|
||||||
/* Assign all remaining sections by decreasing constraint order */
|
/* Assign all remaining sections by decreasing constraint order */
|
||||||
|
|||||||
200
src/link/main.c
200
src/link/main.c
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -24,8 +25,10 @@
|
|||||||
#include "link/patch.h"
|
#include "link/patch.h"
|
||||||
#include "link/output.h"
|
#include "link/output.h"
|
||||||
|
|
||||||
#include "extern/err.h"
|
|
||||||
#include "extern/getopt.h"
|
#include "extern/getopt.h"
|
||||||
|
|
||||||
|
#include "error.h"
|
||||||
|
#include "platform.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
bool isDmgMode; /* -d */
|
bool isDmgMode; /* -d */
|
||||||
@@ -35,6 +38,10 @@ char const *symFileName; /* -n */
|
|||||||
char const *overlayFileName; /* -O */
|
char const *overlayFileName; /* -O */
|
||||||
char const *outputFileName; /* -o */
|
char const *outputFileName; /* -o */
|
||||||
uint8_t padValue; /* -p */
|
uint8_t padValue; /* -p */
|
||||||
|
// Setting these three to 0 disables the functionality
|
||||||
|
uint16_t scrambleROMX = 0; /* -S */
|
||||||
|
uint8_t scrambleWRAMX = 0;
|
||||||
|
uint8_t scrambleSRAM = 0;
|
||||||
bool is32kMode; /* -t */
|
bool is32kMode; /* -t */
|
||||||
bool beVerbose; /* -v */
|
bool beVerbose; /* -v */
|
||||||
bool isWRA0Mode; /* -w */
|
bool isWRA0Mode; /* -w */
|
||||||
@@ -100,11 +107,25 @@ void error(struct FileStackNode const *where, uint32_t lineNo, char const *fmt,
|
|||||||
nbErrors++;
|
nbErrors++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void argErr(char flag, char const *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
fprintf(stderr, "error: Invalid argument for option '%c': ", flag);
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
putc('\n', stderr);
|
||||||
|
|
||||||
|
if (nbErrors != UINT32_MAX)
|
||||||
|
nbErrors++;
|
||||||
|
}
|
||||||
|
|
||||||
_Noreturn void fatal(struct FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
|
_Noreturn void fatal(struct FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
fputs("fatal: ", stderr);
|
fputs("FATAL: ", stderr);
|
||||||
if (where) {
|
if (where) {
|
||||||
dumpFileStack(where);
|
dumpFileStack(where);
|
||||||
fprintf(stderr, "(%" PRIu32 "): ", lineNo);
|
fprintf(stderr, "(%" PRIu32 "): ", lineNo);
|
||||||
@@ -118,7 +139,7 @@ _Noreturn void fatal(struct FileStackNode const *where, uint32_t lineNo, char co
|
|||||||
nbErrors++;
|
nbErrors++;
|
||||||
|
|
||||||
fprintf(stderr, "Linking aborted after %" PRIu32 " error%s\n", nbErrors,
|
fprintf(stderr, "Linking aborted after %" PRIu32 " error%s\n", nbErrors,
|
||||||
nbErrors != 1 ? "s" : "");
|
nbErrors == 1 ? "" : "s");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,13 +157,13 @@ FILE *openFile(char const *fileName, char const *mode)
|
|||||||
file = fdopen(1, mode);
|
file = fdopen(1, mode);
|
||||||
|
|
||||||
if (!file)
|
if (!file)
|
||||||
err(1, "Could not open file \"%s\"", fileName);
|
err("Could not open file \"%s\"", fileName);
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Short options */
|
/* Short options */
|
||||||
static char const *optstring = "dl:m:n:O:o:p:s:tVvwx";
|
static const char *optstring = "dl:m:n:O:o:p:S:s:tVvWwx";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Equivalent long options
|
* Equivalent long options
|
||||||
@@ -155,20 +176,21 @@ static char const *optstring = "dl:m:n:O:o:p:s:tVvwx";
|
|||||||
* over short opt matching
|
* over short opt matching
|
||||||
*/
|
*/
|
||||||
static struct option const longopts[] = {
|
static struct option const longopts[] = {
|
||||||
{ "dmg", no_argument, NULL, 'd' },
|
{ "dmg", no_argument, NULL, 'd' },
|
||||||
{ "linkerscript", required_argument, NULL, 'l' },
|
{ "linkerscript", required_argument, NULL, 'l' },
|
||||||
{ "map", required_argument, NULL, 'm' },
|
{ "map", required_argument, NULL, 'm' },
|
||||||
{ "sym", required_argument, NULL, 'n' },
|
{ "sym", required_argument, NULL, 'n' },
|
||||||
{ "overlay", required_argument, NULL, 'O' },
|
{ "overlay", required_argument, NULL, 'O' },
|
||||||
{ "output", required_argument, NULL, 'o' },
|
{ "output", required_argument, NULL, 'o' },
|
||||||
{ "pad", required_argument, NULL, 'p' },
|
{ "pad", required_argument, NULL, 'p' },
|
||||||
{ "smart", required_argument, NULL, 's' },
|
{ "scramble", required_argument, NULL, 'S' },
|
||||||
{ "tiny", no_argument, NULL, 't' },
|
{ "smart", required_argument, NULL, 's' },
|
||||||
{ "version", no_argument, NULL, 'V' },
|
{ "tiny", no_argument, NULL, 't' },
|
||||||
{ "verbose", no_argument, NULL, 'v' },
|
{ "version", no_argument, NULL, 'V' },
|
||||||
{ "wramx", no_argument, NULL, 'w' },
|
{ "verbose", no_argument, NULL, 'v' },
|
||||||
{ "nopad", no_argument, NULL, 'x' },
|
{ "wramx", no_argument, NULL, 'w' },
|
||||||
{ NULL, no_argument, NULL, 0 }
|
{ "nopad", no_argument, NULL, 'x' },
|
||||||
|
{ NULL, no_argument, NULL, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -178,8 +200,8 @@ static void printUsage(void)
|
|||||||
{
|
{
|
||||||
fputs(
|
fputs(
|
||||||
"Usage: rgblink [-dtVvwx] [-l script] [-m map_file] [-n sym_file]\n"
|
"Usage: rgblink [-dtVvwx] [-l script] [-m map_file] [-n sym_file]\n"
|
||||||
" [-O overlay_file] [-o out_file] [-p pad_value] [-s symbol]\n"
|
" [-O overlay_file] [-o out_file] [-p pad_value]\n"
|
||||||
" <file> ...\n"
|
" [-S spec] [-s symbol] <file> ...\n"
|
||||||
"Useful options:\n"
|
"Useful options:\n"
|
||||||
" -l, --linkerscript <path> set the input linker script\n"
|
" -l, --linkerscript <path> set the input linker script\n"
|
||||||
" -m, --map <path> set the output map file\n"
|
" -m, --map <path> set the output map file\n"
|
||||||
@@ -202,6 +224,131 @@ static void cleanup(void)
|
|||||||
obj_Cleanup();
|
obj_Cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ScrambledRegion {
|
||||||
|
SCRAMBLE_ROMX,
|
||||||
|
SCRAMBLE_SRAM,
|
||||||
|
SCRAMBLE_WRAMX,
|
||||||
|
|
||||||
|
SCRAMBLE_UNK, // Used for errors
|
||||||
|
};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
char const *name;
|
||||||
|
uint16_t max;
|
||||||
|
} scrambleSpecs[SCRAMBLE_UNK] = {
|
||||||
|
[SCRAMBLE_ROMX] = { "romx", 65535 },
|
||||||
|
[SCRAMBLE_SRAM] = { "sram", 255 },
|
||||||
|
[SCRAMBLE_WRAMX] = { "wramx", 7},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void parseScrambleSpec(char const *spec)
|
||||||
|
{
|
||||||
|
// Skip any leading whitespace
|
||||||
|
spec += strspn(spec, " \t");
|
||||||
|
|
||||||
|
// The argument to `-S` should be a comma-separated list of sections followed by an '='
|
||||||
|
// indicating their scramble limit.
|
||||||
|
while (spec) {
|
||||||
|
// Invariant: we should not be pointing at whitespace at this point
|
||||||
|
assert(*spec != ' ' && *spec != '\t');
|
||||||
|
|
||||||
|
// Remember where the region's name begins and ends
|
||||||
|
char const *regionName = spec;
|
||||||
|
size_t regionNameLen = strcspn(spec, "=, \t");
|
||||||
|
// Length of region name string slice for printing, truncated if too long
|
||||||
|
int regionNamePrintLen = regionNameLen > INT_MAX ? INT_MAX : (int)regionNameLen;
|
||||||
|
|
||||||
|
// If this trips, `spec` must be pointing at a ',' or '=' (or NUL) due to the assert
|
||||||
|
if (regionNameLen == 0) {
|
||||||
|
argErr('S', "Missing region name");
|
||||||
|
|
||||||
|
if (*spec == '\0')
|
||||||
|
break;
|
||||||
|
if (*spec == '=') // Skip the limit, too
|
||||||
|
spec = strchr(&spec[1], ','); // Skip to next comma, if any
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the next non-blank char after the region name's end
|
||||||
|
spec += regionNameLen + strspn(&spec[regionNameLen], " \t");
|
||||||
|
if (*spec != '\0' && *spec != ',' && *spec != '=') {
|
||||||
|
argErr('S', "Unexpected '%c' after region name \"%.*s\"",
|
||||||
|
regionNamePrintLen, regionName);
|
||||||
|
// Skip to next ',' or '=' (or NUL) and keep parsing
|
||||||
|
spec += 1 + strcspn(&spec[1], ",=");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, determine which region type this is
|
||||||
|
enum ScrambledRegion region = 0;
|
||||||
|
|
||||||
|
for (; region < SCRAMBLE_UNK; region++) {
|
||||||
|
// If the strings match (case-insensitively), we got it!
|
||||||
|
// `strncasecmp` must be used here since `regionName` points
|
||||||
|
// to the entire remaining argument.
|
||||||
|
if (!strncasecmp(scrambleSpecs[region].name, regionName, regionNameLen))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (region == SCRAMBLE_UNK)
|
||||||
|
argErr('S', "Unknown region \"%.*s\"", regionNamePrintLen, regionName);
|
||||||
|
|
||||||
|
if (*spec == '=') {
|
||||||
|
spec++; // `strtoul` will skip the whitespace on its own
|
||||||
|
unsigned long limit;
|
||||||
|
char *endptr;
|
||||||
|
|
||||||
|
if (*spec == '\0' || *spec == ',') {
|
||||||
|
argErr('S', "Empty limit for region \"%.*s\"",
|
||||||
|
regionNamePrintLen, regionName);
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
limit = strtoul(spec, &endptr, 10);
|
||||||
|
endptr += strspn(endptr, " \t");
|
||||||
|
if (*endptr != '\0' && *endptr != ',') {
|
||||||
|
argErr('S', "Invalid non-numeric limit for region \"%.*s\"",
|
||||||
|
regionNamePrintLen, regionName);
|
||||||
|
endptr = strchr(endptr, ',');
|
||||||
|
}
|
||||||
|
spec = endptr;
|
||||||
|
|
||||||
|
if (region != SCRAMBLE_UNK && limit >= scrambleSpecs[region].max) {
|
||||||
|
argErr('S', "Limit for region \"%.*s\" may not exceed %" PRIu16,
|
||||||
|
regionNamePrintLen, regionName, scrambleSpecs[region].max);
|
||||||
|
limit = scrambleSpecs[region].max;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (region) {
|
||||||
|
case SCRAMBLE_ROMX:
|
||||||
|
scrambleROMX = limit;
|
||||||
|
break;
|
||||||
|
case SCRAMBLE_SRAM:
|
||||||
|
scrambleSRAM = limit;
|
||||||
|
break;
|
||||||
|
case SCRAMBLE_WRAMX:
|
||||||
|
scrambleWRAMX = limit;
|
||||||
|
break;
|
||||||
|
case SCRAMBLE_UNK: // The error has already been reported, do nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (region == SCRAMBLE_WRAMX) {
|
||||||
|
// Only WRAMX can be implied, since ROMX and SRAM size may vary
|
||||||
|
scrambleWRAMX = 7;
|
||||||
|
} else {
|
||||||
|
argErr('S', "Cannot imply limit for region \"%.*s\"",
|
||||||
|
regionNamePrintLen, regionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
next:
|
||||||
|
if (spec) {
|
||||||
|
assert(*spec == ',' || *spec == '\0');
|
||||||
|
if (*spec == ',')
|
||||||
|
spec += 1 + strspn(&spec[1], " \t");
|
||||||
|
if (*spec == '\0')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int optionChar;
|
int optionChar;
|
||||||
@@ -234,15 +381,18 @@ int main(int argc, char *argv[])
|
|||||||
case 'p':
|
case 'p':
|
||||||
value = strtoul(musl_optarg, &endptr, 0);
|
value = strtoul(musl_optarg, &endptr, 0);
|
||||||
if (musl_optarg[0] == '\0' || *endptr != '\0') {
|
if (musl_optarg[0] == '\0' || *endptr != '\0') {
|
||||||
error(NULL, 0, "Invalid argument for option 'p'");
|
argErr('p', "");
|
||||||
value = 0xFF;
|
value = 0xFF;
|
||||||
}
|
}
|
||||||
if (value > 0xFF) {
|
if (value > 0xFF) {
|
||||||
error(NULL, 0, "Argument for 'p' must be a byte (between 0 and 0xFF)");
|
argErr('p', "Argument for 'p' must be a byte (between 0 and 0xFF)");
|
||||||
value = 0xFF;
|
value = 0xFF;
|
||||||
}
|
}
|
||||||
padValue = value;
|
padValue = value;
|
||||||
break;
|
break;
|
||||||
|
case 'S':
|
||||||
|
parseScrambleSpec(musl_optarg);
|
||||||
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
/* FIXME: nobody knows what this does, figure it out */
|
/* FIXME: nobody knows what this does, figure it out */
|
||||||
(void)musl_optarg;
|
(void)musl_optarg;
|
||||||
@@ -275,7 +425,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
/* If no input files were specified, the user must have screwed up */
|
/* If no input files were specified, the user must have screwed up */
|
||||||
if (curArgIndex == argc) {
|
if (curArgIndex == argc) {
|
||||||
fputs("fatal: no input files\n", stderr);
|
fputs("FATAL: no input files\n", stderr);
|
||||||
printUsage();
|
printUsage();
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@@ -304,7 +454,7 @@ int main(int argc, char *argv[])
|
|||||||
patch_ApplyPatches();
|
patch_ApplyPatches();
|
||||||
if (nbErrors) {
|
if (nbErrors) {
|
||||||
fprintf(stderr, "Linking failed with %" PRIu32 " error%s\n",
|
fprintf(stderr, "Linking failed with %" PRIu32 " error%s\n",
|
||||||
nbErrors, nbErrors != 1 ? "s" : "");
|
nbErrors, nbErrors == 1 ? "" : "s");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
out_WriteFiles();
|
out_WriteFiles();
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
#include "link/section.h"
|
#include "link/section.h"
|
||||||
#include "link/symbol.h"
|
#include "link/symbol.h"
|
||||||
|
|
||||||
#include "extern/err.h"
|
#include "error.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "linkdefs.h"
|
#include "linkdefs.h"
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ static struct Assertion *assertions;
|
|||||||
type tmpVal = func(tmpFile); \
|
type tmpVal = func(tmpFile); \
|
||||||
/* TODO: maybe mark the condition as `unlikely`; how to do that portably? */ \
|
/* TODO: maybe mark the condition as `unlikely`; how to do that portably? */ \
|
||||||
if (tmpVal == (errval)) { \
|
if (tmpVal == (errval)) { \
|
||||||
errx(1, __VA_ARGS__, feof(tmpFile) \
|
errx(__VA_ARGS__, feof(tmpFile) \
|
||||||
? "Unexpected end of file" \
|
? "Unexpected end of file" \
|
||||||
: strerror(errno)); \
|
: strerror(errno)); \
|
||||||
} \
|
} \
|
||||||
@@ -289,13 +289,13 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName, cha
|
|||||||
|
|
||||||
patch->rpnExpression = malloc(sizeof(*patch->rpnExpression) * patch->rpnSize);
|
patch->rpnExpression = malloc(sizeof(*patch->rpnExpression) * patch->rpnSize);
|
||||||
if (!patch->rpnExpression)
|
if (!patch->rpnExpression)
|
||||||
err(1, "%s: Failed to alloc \"%s\"'s patch #%" PRIu32 "'s RPN expression",
|
err("%s: Failed to alloc \"%s\"'s patch #%" PRIu32 "'s RPN expression",
|
||||||
fileName, sectName, i);
|
fileName, sectName, i);
|
||||||
size_t nbElementsRead = fread(patch->rpnExpression, sizeof(*patch->rpnExpression),
|
size_t nbElementsRead = fread(patch->rpnExpression, sizeof(*patch->rpnExpression),
|
||||||
patch->rpnSize, file);
|
patch->rpnSize, file);
|
||||||
|
|
||||||
if (nbElementsRead != patch->rpnSize)
|
if (nbElementsRead != patch->rpnSize)
|
||||||
errx(1, "%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN expression: %s",
|
errx("%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN expression: %s",
|
||||||
fileName, sectName, i,
|
fileName, sectName, i,
|
||||||
feof(file) ? "Unexpected end of file" : strerror(errno));
|
feof(file) ? "Unexpected end of file" : strerror(errno));
|
||||||
}
|
}
|
||||||
@@ -327,7 +327,7 @@ static void readSection(FILE *file, struct Section *section, char const *fileNam
|
|||||||
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s' size: %s",
|
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s' size: %s",
|
||||||
fileName, section->name);
|
fileName, section->name);
|
||||||
if (tmp < 0 || tmp > UINT16_MAX)
|
if (tmp < 0 || tmp > UINT16_MAX)
|
||||||
errx(1, "\"%s\"'s section size (%" PRId32 ") is invalid",
|
errx("\"%s\"'s section size (%" PRId32 ") is invalid",
|
||||||
section->name, tmp);
|
section->name, tmp);
|
||||||
section->size = tmp;
|
section->size = tmp;
|
||||||
section->offset = 0;
|
section->offset = 0;
|
||||||
@@ -373,13 +373,13 @@ static void readSection(FILE *file, struct Section *section, char const *fileNam
|
|||||||
uint8_t *data = malloc(sizeof(*data) * section->size + 1);
|
uint8_t *data = malloc(sizeof(*data) * section->size + 1);
|
||||||
|
|
||||||
if (!data)
|
if (!data)
|
||||||
err(1, "%s: Unable to read \"%s\"'s data", fileName,
|
err("%s: Unable to read \"%s\"'s data", fileName,
|
||||||
section->name);
|
section->name);
|
||||||
if (section->size) {
|
if (section->size) {
|
||||||
size_t nbElementsRead = fread(data, sizeof(*data),
|
size_t nbElementsRead = fread(data, sizeof(*data),
|
||||||
section->size, file);
|
section->size, file);
|
||||||
if (nbElementsRead != section->size)
|
if (nbElementsRead != section->size)
|
||||||
errx(1, "%s: Cannot read \"%s\"'s data: %s",
|
errx("%s: Cannot read \"%s\"'s data: %s",
|
||||||
fileName, section->name,
|
fileName, section->name,
|
||||||
feof(file) ? "Unexpected end of file"
|
feof(file) ? "Unexpected end of file"
|
||||||
: strerror(errno));
|
: strerror(errno));
|
||||||
@@ -394,7 +394,7 @@ static void readSection(FILE *file, struct Section *section, char const *fileNam
|
|||||||
malloc(sizeof(*patches) * section->nbPatches + 1);
|
malloc(sizeof(*patches) * section->nbPatches + 1);
|
||||||
|
|
||||||
if (!patches)
|
if (!patches)
|
||||||
err(1, "%s: Unable to read \"%s\"'s patches", fileName, section->name);
|
err("%s: Unable to read \"%s\"'s patches", fileName, section->name);
|
||||||
for (uint32_t i = 0; i < section->nbPatches; i++)
|
for (uint32_t i = 0; i < section->nbPatches; i++)
|
||||||
readPatch(file, &patches[i], fileName, section->name, i, fileNodes);
|
readPatch(file, &patches[i], fileName, section->name, i, fileNodes);
|
||||||
section->patches = patches;
|
section->patches = patches;
|
||||||
@@ -462,7 +462,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
|
|||||||
FILE *file = strcmp("-", fileName) ? fopen(fileName, "rb") : stdin;
|
FILE *file = strcmp("-", fileName) ? fopen(fileName, "rb") : stdin;
|
||||||
|
|
||||||
if (!file)
|
if (!file)
|
||||||
err(1, "Could not open file %s", fileName);
|
err("Could not open file %s", fileName);
|
||||||
|
|
||||||
/* Begin by reading the magic bytes and version number */
|
/* Begin by reading the magic bytes and version number */
|
||||||
unsigned versionNumber;
|
unsigned versionNumber;
|
||||||
@@ -470,13 +470,13 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
|
|||||||
&versionNumber);
|
&versionNumber);
|
||||||
|
|
||||||
if (matchedElems != 1)
|
if (matchedElems != 1)
|
||||||
errx(1, "\"%s\" is not a RGBDS object file", fileName);
|
errx("\"%s\" is not a RGBDS object file", fileName);
|
||||||
|
|
||||||
verbosePrint("Reading object file %s, version %u\n",
|
verbosePrint("Reading object file %s, version %u\n",
|
||||||
fileName, versionNumber);
|
fileName, versionNumber);
|
||||||
|
|
||||||
if (versionNumber != RGBDS_OBJECT_VERSION_NUMBER)
|
if (versionNumber != RGBDS_OBJECT_VERSION_NUMBER)
|
||||||
errx(1, "\"%s\" is an incompatible version %u object file",
|
errx("\"%s\" is an incompatible version %u object file",
|
||||||
fileName, versionNumber);
|
fileName, versionNumber);
|
||||||
|
|
||||||
uint32_t revNum;
|
uint32_t revNum;
|
||||||
@@ -484,7 +484,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
|
|||||||
tryReadlong(revNum, file, "%s: Cannot read revision number: %s",
|
tryReadlong(revNum, file, "%s: Cannot read revision number: %s",
|
||||||
fileName);
|
fileName);
|
||||||
if (revNum != RGBDS_OBJECT_REV)
|
if (revNum != RGBDS_OBJECT_REV)
|
||||||
errx(1, "%s is a revision 0x%04" PRIx32 " object file; only 0x%04x is supported",
|
errx("%s is a revision 0x%04" PRIx32 " object file; only 0x%04x is supported",
|
||||||
fileName, revNum, RGBDS_OBJECT_REV);
|
fileName, revNum, RGBDS_OBJECT_REV);
|
||||||
|
|
||||||
uint32_t nbSymbols;
|
uint32_t nbSymbols;
|
||||||
@@ -500,7 +500,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
|
|||||||
tryReadlong(nodes[fileID].nbNodes, file, "%s: Cannot read number of nodes: %s", fileName);
|
tryReadlong(nodes[fileID].nbNodes, file, "%s: Cannot read number of nodes: %s", fileName);
|
||||||
nodes[fileID].nodes = calloc(nodes[fileID].nbNodes, sizeof(nodes[fileID].nodes[0]));
|
nodes[fileID].nodes = calloc(nodes[fileID].nbNodes, sizeof(nodes[fileID].nodes[0]));
|
||||||
if (!nodes[fileID].nodes)
|
if (!nodes[fileID].nodes)
|
||||||
err(1, "Failed to get memory for %s's nodes", fileName);
|
err("Failed to get memory for %s's nodes", fileName);
|
||||||
verbosePrint("Reading %u nodes...\n", nodes[fileID].nbNodes);
|
verbosePrint("Reading %u nodes...\n", nodes[fileID].nbNodes);
|
||||||
for (uint32_t i = nodes[fileID].nbNodes; i--; )
|
for (uint32_t i = nodes[fileID].nbNodes; i--; )
|
||||||
readFileStackNode(file, nodes[fileID].nodes, i, fileName);
|
readFileStackNode(file, nodes[fileID].nodes, i, fileName);
|
||||||
@@ -510,12 +510,12 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
|
|||||||
malloc(sizeof(*fileSymbols) * nbSymbols + 1);
|
malloc(sizeof(*fileSymbols) * nbSymbols + 1);
|
||||||
|
|
||||||
if (!fileSymbols)
|
if (!fileSymbols)
|
||||||
err(1, "Failed to get memory for %s's symbols", fileName);
|
err("Failed to get memory for %s's symbols", fileName);
|
||||||
|
|
||||||
struct SymbolList *symbolList = malloc(sizeof(*symbolList));
|
struct SymbolList *symbolList = malloc(sizeof(*symbolList));
|
||||||
|
|
||||||
if (!symbolList)
|
if (!symbolList)
|
||||||
err(1, "Failed to register %s's symbol list", fileName);
|
err("Failed to register %s's symbol list", fileName);
|
||||||
symbolList->symbolList = fileSymbols;
|
symbolList->symbolList = fileSymbols;
|
||||||
symbolList->nbSymbols = nbSymbols;
|
symbolList->nbSymbols = nbSymbols;
|
||||||
symbolList->next = symbolLists;
|
symbolList->next = symbolLists;
|
||||||
@@ -530,7 +530,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
|
|||||||
struct Symbol *symbol = malloc(sizeof(*symbol));
|
struct Symbol *symbol = malloc(sizeof(*symbol));
|
||||||
|
|
||||||
if (!symbol)
|
if (!symbol)
|
||||||
err(1, "%s: Couldn't create new symbol", fileName);
|
err("%s: Couldn't create new symbol", fileName);
|
||||||
readSymbol(file, symbol, fileName, nodes[fileID].nodes);
|
readSymbol(file, symbol, fileName, nodes[fileID].nodes);
|
||||||
|
|
||||||
fileSymbols[i] = symbol;
|
fileSymbols[i] = symbol;
|
||||||
@@ -549,7 +549,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
|
|||||||
/* Read section */
|
/* Read section */
|
||||||
fileSections[i] = malloc(sizeof(*fileSections[i]));
|
fileSections[i] = malloc(sizeof(*fileSections[i]));
|
||||||
if (!fileSections[i])
|
if (!fileSections[i])
|
||||||
err(1, "%s: Couldn't create new section", fileName);
|
err("%s: Couldn't create new section", fileName);
|
||||||
|
|
||||||
fileSections[i]->nextu = NULL;
|
fileSections[i]->nextu = NULL;
|
||||||
readSection(file, fileSections[i], fileName, nodes[fileID].nodes);
|
readSection(file, fileSections[i], fileName, nodes[fileID].nodes);
|
||||||
@@ -558,7 +558,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
|
|||||||
fileSections[i]->symbols = malloc(nbSymPerSect[i]
|
fileSections[i]->symbols = malloc(nbSymPerSect[i]
|
||||||
* sizeof(*fileSections[i]->symbols));
|
* sizeof(*fileSections[i]->symbols));
|
||||||
if (!fileSections[i]->symbols)
|
if (!fileSections[i]->symbols)
|
||||||
err(1, "%s: Couldn't link to symbols",
|
err("%s: Couldn't link to symbols",
|
||||||
fileName);
|
fileName);
|
||||||
} else {
|
} else {
|
||||||
fileSections[i]->symbols = NULL;
|
fileSections[i]->symbols = NULL;
|
||||||
@@ -609,7 +609,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
|
|||||||
struct Assertion *assertion = malloc(sizeof(*assertion));
|
struct Assertion *assertion = malloc(sizeof(*assertion));
|
||||||
|
|
||||||
if (!assertion)
|
if (!assertion)
|
||||||
err(1, "%s: Couldn't create new assertion", fileName);
|
err("%s: Couldn't create new assertion", fileName);
|
||||||
readAssertion(file, assertion, fileName, i, nodes[fileID].nodes);
|
readAssertion(file, assertion, fileName, i, nodes[fileID].nodes);
|
||||||
linkPatchToPCSect(&assertion->patch, fileSections);
|
linkPatchToPCSect(&assertion->patch, fileSections);
|
||||||
assertion->fileSymbols = fileSymbols;
|
assertion->fileSymbols = fileSymbols;
|
||||||
|
|||||||
@@ -16,10 +16,8 @@
|
|||||||
#include "link/section.h"
|
#include "link/section.h"
|
||||||
#include "link/symbol.h"
|
#include "link/symbol.h"
|
||||||
|
|
||||||
#include "extern/err.h"
|
#include "error.h"
|
||||||
|
|
||||||
#include "linkdefs.h"
|
#include "linkdefs.h"
|
||||||
|
|
||||||
#include "platform.h" // MIN_NB_ELMS
|
#include "platform.h" // MIN_NB_ELMS
|
||||||
|
|
||||||
#define BANK_SIZE 0x4000
|
#define BANK_SIZE 0x4000
|
||||||
@@ -77,7 +75,7 @@ void out_AddSection(struct Section const *section)
|
|||||||
uint32_t minNbBanks = targetBank + 1;
|
uint32_t minNbBanks = targetBank + 1;
|
||||||
|
|
||||||
if (minNbBanks > maxNbBanks[section->type])
|
if (minNbBanks > maxNbBanks[section->type])
|
||||||
errx(1, "Section \"%s\" has an invalid bank range (%" PRIu32 " > %" PRIu32 ")",
|
errx("Section \"%s\" has an invalid bank range (%" PRIu32 " > %" PRIu32 ")",
|
||||||
section->name, section->bank,
|
section->name, section->bank,
|
||||||
maxNbBanks[section->type] - 1);
|
maxNbBanks[section->type] - 1);
|
||||||
|
|
||||||
@@ -92,7 +90,7 @@ void out_AddSection(struct Section const *section)
|
|||||||
sections[section->type].nbBanks = minNbBanks;
|
sections[section->type].nbBanks = minNbBanks;
|
||||||
}
|
}
|
||||||
if (!sections[section->type].banks)
|
if (!sections[section->type].banks)
|
||||||
err(1, "Failed to realloc banks");
|
err("Failed to realloc banks");
|
||||||
|
|
||||||
struct SortedSection *newSection = malloc(sizeof(*newSection));
|
struct SortedSection *newSection = malloc(sizeof(*newSection));
|
||||||
struct SortedSection **ptr = section->size
|
struct SortedSection **ptr = section->size
|
||||||
@@ -100,7 +98,7 @@ void out_AddSection(struct Section const *section)
|
|||||||
: §ions[section->type].banks[targetBank].zeroLenSections;
|
: §ions[section->type].banks[targetBank].zeroLenSections;
|
||||||
|
|
||||||
if (!newSection)
|
if (!newSection)
|
||||||
err(1, "Failed to add new section \"%s\"", section->name);
|
err("Failed to add new section \"%s\"", section->name);
|
||||||
newSection->section = section;
|
newSection->section = section;
|
||||||
|
|
||||||
while (*ptr && (*ptr)->section->org < section->org)
|
while (*ptr && (*ptr)->section->org < section->org)
|
||||||
@@ -145,15 +143,15 @@ static uint32_t checkOverlaySize(void)
|
|||||||
fseek(overlayFile, 0, SEEK_SET);
|
fseek(overlayFile, 0, SEEK_SET);
|
||||||
|
|
||||||
if (overlaySize % BANK_SIZE)
|
if (overlaySize % BANK_SIZE)
|
||||||
errx(1, "Overlay file must have a size multiple of 0x4000");
|
errx("Overlay file must have a size multiple of 0x4000");
|
||||||
|
|
||||||
uint32_t nbOverlayBanks = overlaySize / BANK_SIZE;
|
uint32_t nbOverlayBanks = overlaySize / BANK_SIZE;
|
||||||
|
|
||||||
if (is32kMode && nbOverlayBanks != 2)
|
if (is32kMode && nbOverlayBanks != 2)
|
||||||
errx(1, "Overlay must be exactly 0x8000 bytes large");
|
errx("Overlay must be exactly 0x8000 bytes large");
|
||||||
|
|
||||||
if (nbOverlayBanks < 2)
|
if (nbOverlayBanks < 2)
|
||||||
errx(1, "Overlay must be at least 0x8000 bytes large");
|
errx("Overlay must be at least 0x8000 bytes large");
|
||||||
|
|
||||||
return nbOverlayBanks;
|
return nbOverlayBanks;
|
||||||
}
|
}
|
||||||
@@ -178,7 +176,7 @@ static void coverOverlayBanks(uint32_t nbOverlayBanks)
|
|||||||
realloc(sections[SECTTYPE_ROMX].banks,
|
realloc(sections[SECTTYPE_ROMX].banks,
|
||||||
sizeof(*sections[SECTTYPE_ROMX].banks) * nbUncoveredBanks);
|
sizeof(*sections[SECTTYPE_ROMX].banks) * nbUncoveredBanks);
|
||||||
if (!sections[SECTTYPE_ROMX].banks)
|
if (!sections[SECTTYPE_ROMX].banks)
|
||||||
err(1, "Failed to realloc banks for overlay");
|
err("Failed to realloc banks for overlay");
|
||||||
for (uint32_t i = sections[SECTTYPE_ROMX].nbBanks; i < nbUncoveredBanks; i++) {
|
for (uint32_t i = sections[SECTTYPE_ROMX].nbBanks; i < nbUncoveredBanks; i++) {
|
||||||
sections[SECTTYPE_ROMX].banks[i].sections = NULL;
|
sections[SECTTYPE_ROMX].banks[i].sections = NULL;
|
||||||
sections[SECTTYPE_ROMX].banks[i].zeroLenSections = NULL;
|
sections[SECTTYPE_ROMX].banks[i].zeroLenSections = NULL;
|
||||||
@@ -317,7 +315,7 @@ static void writeSymBank(struct SortedSections const *bankSections,
|
|||||||
struct SortedSymbol *symList = malloc(sizeof(*symList) * nbSymbols);
|
struct SortedSymbol *symList = malloc(sizeof(*symList) * nbSymbols);
|
||||||
|
|
||||||
if (!symList)
|
if (!symList)
|
||||||
err(1, "Failed to allocate symbol list");
|
err("Failed to allocate symbol list");
|
||||||
|
|
||||||
uint32_t idx = 0;
|
uint32_t idx = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -17,11 +17,10 @@
|
|||||||
#include "link/section.h"
|
#include "link/section.h"
|
||||||
#include "link/symbol.h"
|
#include "link/symbol.h"
|
||||||
|
|
||||||
|
#include "error.h"
|
||||||
#include "linkdefs.h"
|
#include "linkdefs.h"
|
||||||
#include "opmath.h"
|
#include "opmath.h"
|
||||||
|
|
||||||
#include "extern/err.h"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is an "empty"-type stack. Apart from the actual values, we also remember
|
* This is an "empty"-type stack. Apart from the actual values, we also remember
|
||||||
* whether the value is a placeholder inserted for error recovery. This allows
|
* whether the value is a placeholder inserted for error recovery. This allows
|
||||||
@@ -43,7 +42,7 @@ static void initRPNStack(void)
|
|||||||
stack.values = malloc(sizeof(*stack.values) * stack.capacity);
|
stack.values = malloc(sizeof(*stack.values) * stack.capacity);
|
||||||
stack.errorFlags = malloc(sizeof(*stack.errorFlags) * stack.capacity);
|
stack.errorFlags = malloc(sizeof(*stack.errorFlags) * stack.capacity);
|
||||||
if (!stack.values || !stack.errorFlags)
|
if (!stack.values || !stack.errorFlags)
|
||||||
err(1, "Failed to init RPN stack");
|
err("Failed to init RPN stack");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clearRPNStack(void)
|
static void clearRPNStack(void)
|
||||||
@@ -57,7 +56,7 @@ static void pushRPN(int32_t value, bool comesFromError)
|
|||||||
static const size_t increase_factor = 2;
|
static const size_t increase_factor = 2;
|
||||||
|
|
||||||
if (stack.capacity > SIZE_MAX / increase_factor)
|
if (stack.capacity > SIZE_MAX / increase_factor)
|
||||||
errx(1, "Overflow in RPN stack resize");
|
errx("Overflow in RPN stack resize");
|
||||||
|
|
||||||
stack.capacity *= increase_factor;
|
stack.capacity *= increase_factor;
|
||||||
stack.values =
|
stack.values =
|
||||||
@@ -70,7 +69,7 @@ static void pushRPN(int32_t value, bool comesFromError)
|
|||||||
* the overflow check above. Hence the stringent check below.
|
* the overflow check above. Hence the stringent check below.
|
||||||
*/
|
*/
|
||||||
if (!stack.values || !stack.errorFlags || !stack.capacity)
|
if (!stack.values || !stack.errorFlags || !stack.capacity)
|
||||||
err(1, "Failed to resize RPN stack");
|
err("Failed to resize RPN stack");
|
||||||
}
|
}
|
||||||
|
|
||||||
stack.values[stack.size] = value;
|
stack.values[stack.size] = value;
|
||||||
@@ -112,7 +111,7 @@ static uint32_t getRPNByte(uint8_t const **expression, int32_t *size,
|
|||||||
static struct Symbol const *getSymbol(struct Symbol const * const *symbolList,
|
static struct Symbol const *getSymbol(struct Symbol const * const *symbolList,
|
||||||
uint32_t index)
|
uint32_t index)
|
||||||
{
|
{
|
||||||
assert(index != -1); /* PC needs to be handled specially, not here */
|
assert(index != (uint32_t)-1); /* PC needs to be handled specially, not here */
|
||||||
struct Symbol const *symbol = symbolList[index];
|
struct Symbol const *symbol = symbolList[index];
|
||||||
|
|
||||||
/* If the symbol is defined elsewhere... */
|
/* If the symbol is defined elsewhere... */
|
||||||
@@ -266,6 +265,10 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
|||||||
value = popRPN();
|
value = popRPN();
|
||||||
value = op_shift_right(popRPN(), value);
|
value = op_shift_right(popRPN(), value);
|
||||||
break;
|
break;
|
||||||
|
case RPN_USHR:
|
||||||
|
value = popRPN();
|
||||||
|
value = op_shift_right_unsigned(popRPN(), value);
|
||||||
|
break;
|
||||||
|
|
||||||
case RPN_BANK_SYM:
|
case RPN_BANK_SYM:
|
||||||
value = 0;
|
value = 0;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
.Op Fl O Ar overlay_file
|
.Op Fl O Ar overlay_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 S Ar spec
|
||||||
.Op Fl s Ar symbol
|
.Op Fl s Ar symbol
|
||||||
.Ar
|
.Ar
|
||||||
.Sh DESCRIPTION
|
.Sh DESCRIPTION
|
||||||
@@ -89,6 +90,14 @@ Has no effect if
|
|||||||
.Fl O
|
.Fl O
|
||||||
is specified.
|
is specified.
|
||||||
The default is 0.
|
The default is 0.
|
||||||
|
.It Fl S Ar spec , Fl Fl scramble Ar spec
|
||||||
|
Enables a different
|
||||||
|
.Dq scrambling
|
||||||
|
algorithm for placing sections.
|
||||||
|
See
|
||||||
|
.Sx Scrambling algorithm
|
||||||
|
below for an explanation and a description of
|
||||||
|
.Ar spec .
|
||||||
.It Fl s Ar symbol , Fl Fl smart Ar symbol
|
.It Fl s Ar symbol , Fl Fl smart Ar symbol
|
||||||
This option is ignored.
|
This option is ignored.
|
||||||
It was supposed to perform smart linking but fell into disrepair, and so has been removed.
|
It was supposed to perform smart linking but fell into disrepair, and so has been removed.
|
||||||
@@ -113,6 +122,56 @@ When making a ROM, be careful that not using this is not a replacement for
|
|||||||
.Xr rgbfix 1 Ap s Fl p
|
.Xr rgbfix 1 Ap s Fl p
|
||||||
option!
|
option!
|
||||||
.El
|
.El
|
||||||
|
.Ss Scrambling algorithm
|
||||||
|
The default section placement algorithm tries to minimize the number of banks used;
|
||||||
|
.Dq scrambling
|
||||||
|
instead places sections into a given pool of banks, trying to minimize the number of sections sharing a given bank.
|
||||||
|
This is useful to catch broken bank assumptions, such as expecting two different sections to land in the same bank (that is not guaranteed unless both are manually assigned the same bank number).
|
||||||
|
.Pp
|
||||||
|
A scrambling spec is a comma-separated list of region specs.
|
||||||
|
A trailing comma is allowed, as well as whitespace between all specs and their components.
|
||||||
|
Each region spec has the following form:
|
||||||
|
.D1 Ar region Ns Op = Ns Ar size
|
||||||
|
.Ar region
|
||||||
|
must be one of the following (case-insensitive), while
|
||||||
|
.Ar size
|
||||||
|
must be a positive decimal integer between 1 and the corresponding maximum.
|
||||||
|
Certain regions allow omitting the size, in which case it defaults to its max value.
|
||||||
|
.Bl -column "Region name" "Max value" "Size optional"
|
||||||
|
Region name Ta Max size Ta Size optional
|
||||||
|
.Cm romx Ta 65535 Ta \&No
|
||||||
|
.Cm sram Ta 255 Ta \&No
|
||||||
|
.Cm wramx Ta 7 Ta Yes
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
A
|
||||||
|
.Ar size
|
||||||
|
of 0 disables scrambling for that region.
|
||||||
|
.Pp
|
||||||
|
For example,
|
||||||
|
.Ql romx=64,wramx=4
|
||||||
|
will scramble
|
||||||
|
.Ic ROMX
|
||||||
|
sections among ROM banks 1 to 64,
|
||||||
|
.Ic WRAMX
|
||||||
|
sections among RAM banks 1 to 4, and will not scramble
|
||||||
|
.Ic SRAM
|
||||||
|
sections.
|
||||||
|
.Pp
|
||||||
|
Later region specs override earlier ones; for example,
|
||||||
|
.Ql romx=42, Romx=0
|
||||||
|
disables scrambling for
|
||||||
|
.Cm romx .
|
||||||
|
.Pp
|
||||||
|
.Cm wramx
|
||||||
|
scrambling is silently ignored if
|
||||||
|
.Fl w
|
||||||
|
is passed (including if implied by
|
||||||
|
.Fl d ) ,
|
||||||
|
as
|
||||||
|
.Ic WRAMX
|
||||||
|
sections will be treated as
|
||||||
|
.Ic WRAM0 .
|
||||||
.Sh EXAMPLES
|
.Sh EXAMPLES
|
||||||
All you need for a basic ROM is an object file, which can be made into a ROM image like so:
|
All you need for a basic ROM is an object file, which can be made into a ROM image like so:
|
||||||
.Pp
|
.Pp
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
#include "link/script.h"
|
#include "link/script.h"
|
||||||
#include "link/section.h"
|
#include "link/section.h"
|
||||||
|
|
||||||
#include "extern/err.h"
|
#include "error.h"
|
||||||
|
|
||||||
FILE *linkerScript;
|
FILE *linkerScript;
|
||||||
char *includeFileName;
|
char *includeFileName;
|
||||||
@@ -36,7 +36,7 @@ static uint32_t fileStackIndex;
|
|||||||
static void pushFile(char *newFileName)
|
static void pushFile(char *newFileName)
|
||||||
{
|
{
|
||||||
if (fileStackIndex == UINT32_MAX)
|
if (fileStackIndex == UINT32_MAX)
|
||||||
errx(1, "%s(%" PRIu32 "): INCLUDE recursion limit reached",
|
errx("%s(%" PRIu32 "): INCLUDE recursion limit reached",
|
||||||
linkerScriptName, lineNo);
|
linkerScriptName, lineNo);
|
||||||
|
|
||||||
if (fileStackIndex == fileStackSize) {
|
if (fileStackIndex == fileStackSize) {
|
||||||
@@ -45,7 +45,7 @@ static void pushFile(char *newFileName)
|
|||||||
fileStackSize *= 2;
|
fileStackSize *= 2;
|
||||||
fileStack = realloc(fileStack, sizeof(*fileStack) * fileStackSize);
|
fileStack = realloc(fileStack, sizeof(*fileStack) * fileStackSize);
|
||||||
if (!fileStack)
|
if (!fileStack)
|
||||||
err(1, "%s(%" PRIu32 "): Internal INCLUDE error",
|
err("%s(%" PRIu32 "): Internal INCLUDE error",
|
||||||
linkerScriptName, lineNo);
|
linkerScriptName, lineNo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ static void pushFile(char *newFileName)
|
|||||||
|
|
||||||
linkerScript = fopen(newFileName, "r");
|
linkerScript = fopen(newFileName, "r");
|
||||||
if (!linkerScript)
|
if (!linkerScript)
|
||||||
err(1, "%s(%" PRIu32 "): Could not open \"%s\"",
|
err("%s(%" PRIu32 "): Could not open \"%s\"",
|
||||||
linkerScriptName, lineNo, newFileName);
|
linkerScriptName, lineNo, newFileName);
|
||||||
lineNo = 1;
|
lineNo = 1;
|
||||||
linkerScriptName = newFileName;
|
linkerScriptName = newFileName;
|
||||||
@@ -177,7 +177,7 @@ static int nextChar(void)
|
|||||||
int curchar = getc(linkerScript);
|
int curchar = getc(linkerScript);
|
||||||
|
|
||||||
if (curchar == EOF && ferror(linkerScript))
|
if (curchar == EOF && ferror(linkerScript))
|
||||||
err(1, "%s(%" PRIu32 "): Unexpected error in %s",
|
err("%s(%" PRIu32 "): Unexpected error in %s",
|
||||||
linkerScriptName, lineNo, __func__);
|
linkerScriptName, lineNo, __func__);
|
||||||
return curchar;
|
return curchar;
|
||||||
}
|
}
|
||||||
@@ -212,10 +212,8 @@ static struct LinkerScriptToken *nextToken(void)
|
|||||||
if (curchar == '\r') {
|
if (curchar == '\r') {
|
||||||
/* Handle CRLF */
|
/* Handle CRLF */
|
||||||
curchar = nextChar();
|
curchar = nextChar();
|
||||||
if (curchar != '\n') {
|
if (curchar != '\n')
|
||||||
ungetc(curchar, linkerScript);
|
ungetc(curchar, linkerScript);
|
||||||
curchar = '\r';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (curchar == '"') {
|
} else if (curchar == '"') {
|
||||||
/* If we have a string start, this is a string */
|
/* If we have a string start, this is a string */
|
||||||
@@ -228,7 +226,7 @@ static struct LinkerScriptToken *nextToken(void)
|
|||||||
do {
|
do {
|
||||||
curchar = nextChar();
|
curchar = nextChar();
|
||||||
if (curchar == EOF || isNewline(curchar)) {
|
if (curchar == EOF || isNewline(curchar)) {
|
||||||
errx(1, "%s(%" PRIu32 "): Unterminated string",
|
errx("%s(%" PRIu32 "): Unterminated string",
|
||||||
linkerScriptName, lineNo);
|
linkerScriptName, lineNo);
|
||||||
} else if (curchar == '"') {
|
} else if (curchar == '"') {
|
||||||
/* Quotes force a string termination */
|
/* Quotes force a string termination */
|
||||||
@@ -237,7 +235,7 @@ static struct LinkerScriptToken *nextToken(void)
|
|||||||
/* Backslashes are escape sequences */
|
/* Backslashes are escape sequences */
|
||||||
curchar = nextChar();
|
curchar = nextChar();
|
||||||
if (curchar == EOF || isNewline(curchar))
|
if (curchar == EOF || isNewline(curchar))
|
||||||
errx(1, "%s(%" PRIu32 "): Unterminated string",
|
errx("%s(%" PRIu32 "): Unterminated string",
|
||||||
linkerScriptName, lineNo);
|
linkerScriptName, lineNo);
|
||||||
else if (curchar == 'n')
|
else if (curchar == 'n')
|
||||||
curchar = '\n';
|
curchar = '\n';
|
||||||
@@ -246,7 +244,7 @@ static struct LinkerScriptToken *nextToken(void)
|
|||||||
else if (curchar == 't')
|
else if (curchar == 't')
|
||||||
curchar = '\t';
|
curchar = '\t';
|
||||||
else if (curchar != '\\' && curchar != '"')
|
else if (curchar != '\\' && curchar != '"')
|
||||||
errx(1, "%s(%" PRIu32 "): Illegal character escape",
|
errx("%s(%" PRIu32 "): Illegal character escape",
|
||||||
linkerScriptName, lineNo);
|
linkerScriptName, lineNo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,7 +252,7 @@ static struct LinkerScriptToken *nextToken(void)
|
|||||||
capacity *= 2;
|
capacity *= 2;
|
||||||
token.attr.string = realloc(token.attr.string, capacity);
|
token.attr.string = realloc(token.attr.string, capacity);
|
||||||
if (!token.attr.string)
|
if (!token.attr.string)
|
||||||
err(1, "%s: Failed to allocate memory for string",
|
err("%s: Failed to allocate memory for string",
|
||||||
__func__);
|
__func__);
|
||||||
}
|
}
|
||||||
token.attr.string[size++] = curchar;
|
token.attr.string[size++] = curchar;
|
||||||
@@ -270,7 +268,7 @@ static struct LinkerScriptToken *nextToken(void)
|
|||||||
capacity *= 2;
|
capacity *= 2;
|
||||||
str = realloc(str, capacity);
|
str = realloc(str, capacity);
|
||||||
if (!str)
|
if (!str)
|
||||||
err(1, "%s: Failed to allocate memory for token",
|
err("%s: Failed to allocate memory for token",
|
||||||
__func__);
|
__func__);
|
||||||
}
|
}
|
||||||
str[size] = toupper(curchar);
|
str[size] = toupper(curchar);
|
||||||
@@ -320,7 +318,7 @@ static struct LinkerScriptToken *nextToken(void)
|
|||||||
if (tryParseNumber(str, &token.attr.number))
|
if (tryParseNumber(str, &token.attr.number))
|
||||||
token.type = TOKEN_NUMBER;
|
token.type = TOKEN_NUMBER;
|
||||||
else
|
else
|
||||||
errx(1, "%s(%" PRIu32 "): Unknown token \"%s\"",
|
errx("%s(%" PRIu32 "): Unknown token \"%s\"",
|
||||||
linkerScriptName, lineNo, str);
|
linkerScriptName, lineNo, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -347,7 +345,7 @@ static void processCommand(enum LinkerScriptCommand command, uint16_t arg, uint1
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (arg < *pc)
|
if (arg < *pc)
|
||||||
errx(1, "%s(%" PRIu32 "): `%s` cannot be used to go backwards (currently at $%x)",
|
errx("%s(%" PRIu32 "): `%s` cannot be used to go backwards (currently at $%x)",
|
||||||
linkerScriptName, lineNo, commands[command], *pc);
|
linkerScriptName, lineNo, commands[command], *pc);
|
||||||
*pc = arg;
|
*pc = arg;
|
||||||
}
|
}
|
||||||
@@ -396,11 +394,11 @@ struct SectionPlacement *script_NextSection(void)
|
|||||||
|
|
||||||
if (type != SECTTYPE_INVALID) {
|
if (type != SECTTYPE_INVALID) {
|
||||||
if (curaddr[type][bankID] > endaddr(type) + 1)
|
if (curaddr[type][bankID] > endaddr(type) + 1)
|
||||||
errx(1, "%s(%" PRIu32 "): Sections would extend past the end of %s ($%04" PRIx16 " > $%04" PRIx16 ")",
|
errx("%s(%" PRIu32 "): Sections would extend past the end of %s ($%04" PRIx16 " > $%04" PRIx16 ")",
|
||||||
linkerScriptName, lineNo, typeNames[type],
|
linkerScriptName, lineNo, typeNames[type],
|
||||||
curaddr[type][bankID], endaddr(type));
|
curaddr[type][bankID], endaddr(type));
|
||||||
if (curaddr[type][bankID] < startaddr[type])
|
if (curaddr[type][bankID] < startaddr[type])
|
||||||
errx(1, "%s(%" PRIu32 "): PC underflowed ($%04" PRIx16 " < $%04" PRIx16 ")",
|
errx("%s(%" PRIu32 "): PC underflowed ($%04" PRIx16 " < $%04" PRIx16 ")",
|
||||||
linkerScriptName, lineNo,
|
linkerScriptName, lineNo,
|
||||||
curaddr[type][bankID], startaddr[type]);
|
curaddr[type][bankID], startaddr[type]);
|
||||||
}
|
}
|
||||||
@@ -421,7 +419,7 @@ struct SectionPlacement *script_NextSection(void)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case TOKEN_NUMBER:
|
case TOKEN_NUMBER:
|
||||||
errx(1, "%s(%" PRIu32 "): stray number \"%" PRIu32 "\"",
|
errx("%s(%" PRIu32 "): stray number \"%" PRIu32 "\"",
|
||||||
linkerScriptName, lineNo,
|
linkerScriptName, lineNo,
|
||||||
token->attr.number);
|
token->attr.number);
|
||||||
|
|
||||||
@@ -434,13 +432,13 @@ struct SectionPlacement *script_NextSection(void)
|
|||||||
parserState = PARSER_LINEEND;
|
parserState = PARSER_LINEEND;
|
||||||
|
|
||||||
if (type == SECTTYPE_INVALID)
|
if (type == SECTTYPE_INVALID)
|
||||||
errx(1, "%s(%" PRIu32 "): Didn't specify a location before the section",
|
errx("%s(%" PRIu32 "): Didn't specify a location before the section",
|
||||||
linkerScriptName, lineNo);
|
linkerScriptName, lineNo);
|
||||||
|
|
||||||
section.section =
|
section.section =
|
||||||
sect_GetSection(token->attr.string);
|
sect_GetSection(token->attr.string);
|
||||||
if (!section.section)
|
if (!section.section)
|
||||||
errx(1, "%s(%" PRIu32 "): Unknown section \"%s\"",
|
errx("%s(%" PRIu32 "): Unknown section \"%s\"",
|
||||||
linkerScriptName, lineNo,
|
linkerScriptName, lineNo,
|
||||||
token->attr.string);
|
token->attr.string);
|
||||||
section.org = curaddr[type][bankID];
|
section.org = curaddr[type][bankID];
|
||||||
@@ -469,10 +467,10 @@ struct SectionPlacement *script_NextSection(void)
|
|||||||
|
|
||||||
if (tokType == TOKEN_COMMAND) {
|
if (tokType == TOKEN_COMMAND) {
|
||||||
if (type == SECTTYPE_INVALID)
|
if (type == SECTTYPE_INVALID)
|
||||||
errx(1, "%s(%" PRIu32 "): Didn't specify a location before the command",
|
errx("%s(%" PRIu32 "): Didn't specify a location before the command",
|
||||||
linkerScriptName, lineNo);
|
linkerScriptName, lineNo);
|
||||||
if (!hasArg)
|
if (!hasArg)
|
||||||
errx(1, "%s(%" PRIu32 "): Command specified without an argument",
|
errx("%s(%" PRIu32 "): Command specified without an argument",
|
||||||
linkerScriptName, lineNo);
|
linkerScriptName, lineNo);
|
||||||
|
|
||||||
processCommand(attr.command, arg, &curaddr[type][bankID]);
|
processCommand(attr.command, arg, &curaddr[type][bankID]);
|
||||||
@@ -483,16 +481,16 @@ struct SectionPlacement *script_NextSection(void)
|
|||||||
* specifying the number is optional.
|
* specifying the number is optional.
|
||||||
*/
|
*/
|
||||||
if (!hasArg && nbbanks(type) != 1)
|
if (!hasArg && nbbanks(type) != 1)
|
||||||
errx(1, "%s(%" PRIu32 "): Didn't specify a bank number",
|
errx("%s(%" PRIu32 "): Didn't specify a bank number",
|
||||||
linkerScriptName, lineNo);
|
linkerScriptName, lineNo);
|
||||||
else if (!hasArg)
|
else if (!hasArg)
|
||||||
arg = bankranges[type][0];
|
arg = bankranges[type][0];
|
||||||
else if (arg < bankranges[type][0])
|
else if (arg < bankranges[type][0])
|
||||||
errx(1, "%s(%" PRIu32 "): specified bank number is too low (%" PRIu32 " < %" PRIu32 ")",
|
errx("%s(%" PRIu32 "): specified bank number is too low (%" PRIu32 " < %" PRIu32 ")",
|
||||||
linkerScriptName, lineNo,
|
linkerScriptName, lineNo,
|
||||||
arg, bankranges[type][0]);
|
arg, bankranges[type][0]);
|
||||||
else if (arg > bankranges[type][1])
|
else if (arg > bankranges[type][1])
|
||||||
errx(1, "%s(%" PRIu32 "): specified bank number is too high (%" PRIu32 " > %" PRIu32 ")",
|
errx("%s(%" PRIu32 "): specified bank number is too high (%" PRIu32 " > %" PRIu32 ")",
|
||||||
linkerScriptName, lineNo,
|
linkerScriptName, lineNo,
|
||||||
arg, bankranges[type][1]);
|
arg, bankranges[type][1]);
|
||||||
bank = arg;
|
bank = arg;
|
||||||
@@ -512,7 +510,7 @@ struct SectionPlacement *script_NextSection(void)
|
|||||||
|
|
||||||
case PARSER_INCLUDE:
|
case PARSER_INCLUDE:
|
||||||
if (token->type != TOKEN_STRING)
|
if (token->type != TOKEN_STRING)
|
||||||
errx(1, "%s(%" PRIu32 "): Expected a file name after INCLUDE",
|
errx("%s(%" PRIu32 "): Expected a file name after INCLUDE",
|
||||||
linkerScriptName, lineNo);
|
linkerScriptName, lineNo);
|
||||||
|
|
||||||
/* Switch to that file */
|
/* Switch to that file */
|
||||||
@@ -532,7 +530,7 @@ lineend:
|
|||||||
return NULL;
|
return NULL;
|
||||||
parserState = PARSER_LINEEND;
|
parserState = PARSER_LINEEND;
|
||||||
} else if (token->type != TOKEN_NEWLINE)
|
} else if (token->type != TOKEN_NEWLINE)
|
||||||
errx(1, "%s(%" PRIu32 "): Unexpected %s at the end of the line",
|
errx("%s(%" PRIu32 "): Unexpected %s at the end of the line",
|
||||||
linkerScriptName, lineNo,
|
linkerScriptName, lineNo,
|
||||||
tokenTypes[token->type]);
|
tokenTypes[token->type]);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -14,8 +14,7 @@
|
|||||||
#include "link/main.h"
|
#include "link/main.h"
|
||||||
#include "link/section.h"
|
#include "link/section.h"
|
||||||
|
|
||||||
#include "extern/err.h"
|
#include "error.h"
|
||||||
|
|
||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
|
|
||||||
HashMap sections;
|
HashMap sections;
|
||||||
@@ -44,12 +43,12 @@ static void checkSectUnionCompat(struct Section *target, struct Section *other)
|
|||||||
if (other->isAddressFixed) {
|
if (other->isAddressFixed) {
|
||||||
if (target->isAddressFixed) {
|
if (target->isAddressFixed) {
|
||||||
if (target->org != other->org)
|
if (target->org != other->org)
|
||||||
errx(1, "Section \"%s\" is defined with conflicting addresses $%04"
|
errx("Section \"%s\" is defined with conflicting addresses $%04"
|
||||||
PRIx16 " and $%04" PRIx16,
|
PRIx16 " and $%04" PRIx16,
|
||||||
other->name, target->org, other->org);
|
other->name, target->org, other->org);
|
||||||
} else if (target->isAlignFixed) {
|
} else if (target->isAlignFixed) {
|
||||||
if ((other->org - target->alignOfs) & target->alignMask)
|
if ((other->org - target->alignOfs) & target->alignMask)
|
||||||
errx(1, "Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
|
errx("Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
|
||||||
PRIu16 ") and address $%04" PRIx16,
|
PRIu16 ") and address $%04" PRIx16,
|
||||||
other->name, target->alignMask + 1,
|
other->name, target->alignMask + 1,
|
||||||
target->alignOfs, other->org);
|
target->alignOfs, other->org);
|
||||||
@@ -60,14 +59,14 @@ static void checkSectUnionCompat(struct Section *target, struct Section *other)
|
|||||||
} else if (other->isAlignFixed) {
|
} else if (other->isAlignFixed) {
|
||||||
if (target->isAddressFixed) {
|
if (target->isAddressFixed) {
|
||||||
if ((target->org - other->alignOfs) & other->alignMask)
|
if ((target->org - other->alignOfs) & other->alignMask)
|
||||||
errx(1, "Section \"%s\" is defined with conflicting address $%04"
|
errx("Section \"%s\" is defined with conflicting address $%04"
|
||||||
PRIx16 " and %d-byte alignment (offset %" PRIu16 ")",
|
PRIx16 " and %d-byte alignment (offset %" PRIu16 ")",
|
||||||
other->name, target->org,
|
other->name, target->org,
|
||||||
other->alignMask + 1, other->alignOfs);
|
other->alignMask + 1, other->alignOfs);
|
||||||
} else if (target->isAlignFixed
|
} else if (target->isAlignFixed
|
||||||
&& (other->alignMask & target->alignOfs)
|
&& (other->alignMask & target->alignOfs)
|
||||||
!= (target->alignMask & other->alignOfs)) {
|
!= (target->alignMask & other->alignOfs)) {
|
||||||
errx(1, "Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
|
errx("Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
|
||||||
PRIu16 ") and %d-byte alignment (offset %" PRIu16 ")",
|
PRIu16 ") and %d-byte alignment (offset %" PRIu16 ")",
|
||||||
other->name, target->alignMask + 1, target->alignOfs,
|
other->name, target->alignMask + 1, target->alignOfs,
|
||||||
other->alignMask + 1, other->alignOfs);
|
other->alignMask + 1, other->alignOfs);
|
||||||
@@ -85,13 +84,13 @@ static void checkFragmentCompat(struct Section *target, struct Section *other)
|
|||||||
|
|
||||||
if (target->isAddressFixed) {
|
if (target->isAddressFixed) {
|
||||||
if (target->org != org)
|
if (target->org != org)
|
||||||
errx(1, "Section \"%s\" is defined with conflicting addresses $%04"
|
errx("Section \"%s\" is defined with conflicting addresses $%04"
|
||||||
PRIx16 " and $%04" PRIx16,
|
PRIx16 " and $%04" PRIx16,
|
||||||
other->name, target->org, other->org);
|
other->name, target->org, other->org);
|
||||||
|
|
||||||
} else if (target->isAlignFixed) {
|
} else if (target->isAlignFixed) {
|
||||||
if ((org - target->alignOfs) & target->alignMask)
|
if ((org - target->alignOfs) & target->alignMask)
|
||||||
errx(1, "Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
|
errx("Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
|
||||||
PRIu16 ") and address $%04" PRIx16,
|
PRIu16 ") and address $%04" PRIx16,
|
||||||
other->name, target->alignMask + 1,
|
other->name, target->alignMask + 1,
|
||||||
target->alignOfs, other->org);
|
target->alignOfs, other->org);
|
||||||
@@ -107,14 +106,14 @@ static void checkFragmentCompat(struct Section *target, struct Section *other)
|
|||||||
|
|
||||||
if (target->isAddressFixed) {
|
if (target->isAddressFixed) {
|
||||||
if ((target->org - ofs) & other->alignMask)
|
if ((target->org - ofs) & other->alignMask)
|
||||||
errx(1, "Section \"%s\" is defined with conflicting address $%04"
|
errx("Section \"%s\" is defined with conflicting address $%04"
|
||||||
PRIx16 " and %d-byte alignment (offset %" PRIu16 ")",
|
PRIx16 " and %d-byte alignment (offset %" PRIu16 ")",
|
||||||
other->name, target->org,
|
other->name, target->org,
|
||||||
other->alignMask + 1, other->alignOfs);
|
other->alignMask + 1, other->alignOfs);
|
||||||
|
|
||||||
} else if (target->isAlignFixed
|
} else if (target->isAlignFixed
|
||||||
&& (other->alignMask & target->alignOfs) != (target->alignMask & ofs)) {
|
&& (other->alignMask & target->alignOfs) != (target->alignMask & ofs)) {
|
||||||
errx(1, "Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
|
errx("Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
|
||||||
PRIu16 ") and %d-byte alignment (offset %" PRIu16 ")",
|
PRIu16 ") and %d-byte alignment (offset %" PRIu16 ")",
|
||||||
other->name, target->alignMask + 1, target->alignOfs,
|
other->name, target->alignMask + 1, target->alignOfs,
|
||||||
other->alignMask + 1, other->alignOfs);
|
other->alignMask + 1, other->alignOfs);
|
||||||
@@ -132,7 +131,7 @@ static void mergeSections(struct Section *target, struct Section *other, enum Se
|
|||||||
// Common checks
|
// Common checks
|
||||||
|
|
||||||
if (target->type != other->type)
|
if (target->type != other->type)
|
||||||
errx(1, "Section \"%s\" is defined with conflicting types %s and %s",
|
errx("Section \"%s\" is defined with conflicting types %s and %s",
|
||||||
other->name, typeNames[target->type], typeNames[other->type]);
|
other->name, typeNames[target->type], typeNames[other->type]);
|
||||||
|
|
||||||
if (other->isBankFixed) {
|
if (other->isBankFixed) {
|
||||||
@@ -140,7 +139,7 @@ static void mergeSections(struct Section *target, struct Section *other, enum Se
|
|||||||
target->isBankFixed = true;
|
target->isBankFixed = true;
|
||||||
target->bank = other->bank;
|
target->bank = other->bank;
|
||||||
} else if (target->bank != other->bank) {
|
} else if (target->bank != other->bank) {
|
||||||
errx(1, "Section \"%s\" is defined with conflicting banks %" PRIu32 " and %"
|
errx("Section \"%s\" is defined with conflicting banks %" PRIu32 " and %"
|
||||||
PRIu32, other->name, target->bank, other->bank);
|
PRIu32, other->name, target->bank, other->bank);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -161,7 +160,7 @@ static void mergeSections(struct Section *target, struct Section *other, enum Se
|
|||||||
target->data = realloc(target->data,
|
target->data = realloc(target->data,
|
||||||
sizeof(*target->data) * target->size + 1);
|
sizeof(*target->data) * target->size + 1);
|
||||||
if (!target->data)
|
if (!target->data)
|
||||||
errx(1, "Failed to concatenate \"%s\"'s fragments", target->name);
|
errx("Failed to concatenate \"%s\"'s fragments", target->name);
|
||||||
memcpy(target->data + target->size - other->size, other->data, other->size);
|
memcpy(target->data + target->size - other->size, other->data, other->size);
|
||||||
/* Adjust patches' PC offsets */
|
/* Adjust patches' PC offsets */
|
||||||
for (uint32_t patchID = 0; patchID < other->nbPatches; patchID++)
|
for (uint32_t patchID = 0; patchID < other->nbPatches; patchID++)
|
||||||
@@ -184,14 +183,14 @@ void sect_AddSection(struct Section *section)
|
|||||||
|
|
||||||
if (other) {
|
if (other) {
|
||||||
if (section->modifier != other->modifier)
|
if (section->modifier != other->modifier)
|
||||||
errx(1, "Section \"%s\" defined as %s and %s", section->name,
|
errx("Section \"%s\" defined as %s and %s", section->name,
|
||||||
sectionModNames[section->modifier], sectionModNames[other->modifier]);
|
sectionModNames[section->modifier], sectionModNames[other->modifier]);
|
||||||
else if (section->modifier == SECTION_NORMAL)
|
else if (section->modifier == SECTION_NORMAL)
|
||||||
errx(1, "Section name \"%s\" is already in use", section->name);
|
errx("Section name \"%s\" is already in use", section->name);
|
||||||
else
|
else
|
||||||
mergeSections(other, section, section->modifier);
|
mergeSections(other, section, section->modifier);
|
||||||
} else if (section->modifier == SECTION_UNION && sect_HasData(section->type)) {
|
} else if (section->modifier == SECTION_UNION && sect_HasData(section->type)) {
|
||||||
errx(1, "Section \"%s\" is of type %s, which cannot be unionized",
|
errx("Section \"%s\" is of type %s, which cannot be unionized",
|
||||||
section->name, typeNames[section->type]);
|
section->name, typeNames[section->type]);
|
||||||
} else {
|
} else {
|
||||||
/* If not, add it */
|
/* If not, add it */
|
||||||
@@ -302,5 +301,5 @@ void sect_DoSanityChecks(void)
|
|||||||
{
|
{
|
||||||
sect_ForEach(doSanityChecks, NULL);
|
sect_ForEach(doSanityChecks, NULL);
|
||||||
if (sanityChecksFailed)
|
if (sanityChecksFailed)
|
||||||
errx(1, "Sanity checks failed");
|
errx("Sanity checks failed");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
#include "link/symbol.h"
|
#include "link/symbol.h"
|
||||||
#include "link/main.h"
|
#include "link/main.h"
|
||||||
|
|
||||||
#include "extern/err.h"
|
#include "error.h"
|
||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
|
|
||||||
HashMap symbols;
|
HashMap symbols;
|
||||||
|
|||||||
15
src/opmath.c
15
src/opmath.c
@@ -86,3 +86,18 @@ int32_t op_shift_right(int32_t value, int32_t amount)
|
|||||||
// undefined, so use a left shift manually sign-extended
|
// undefined, so use a left shift manually sign-extended
|
||||||
return ((uint32_t)value >> amount) | amount_high_bits;
|
return ((uint32_t)value >> amount) | amount_high_bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32_t op_shift_right_unsigned(int32_t value, int32_t amount)
|
||||||
|
{
|
||||||
|
// Repeat the easy cases here to avoid INT_MIN funny business
|
||||||
|
if (amount == 0)
|
||||||
|
return value;
|
||||||
|
if (value == 0 || amount <= -32)
|
||||||
|
return 0;
|
||||||
|
if (amount > 31)
|
||||||
|
return (value < 0) ? -1 : 0;
|
||||||
|
if (amount < 0)
|
||||||
|
return op_shift_left(value, -amount);
|
||||||
|
|
||||||
|
return (uint32_t)value >> amount;
|
||||||
|
}
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ with some bytes being special prefixes for integers and symbols.
|
|||||||
.It Li $13 Ta Li unary ~
|
.It Li $13 Ta Li unary ~
|
||||||
.It Li $21 Ta Li && comparison
|
.It Li $21 Ta Li && comparison
|
||||||
.It Li $22 Ta Li || comparison
|
.It Li $22 Ta Li || comparison
|
||||||
.It Li $23 Ta Li unary\ !
|
.It Li $23 Ta Li unary \&!
|
||||||
.It Li $30 Ta Li == comparison
|
.It Li $30 Ta Li == comparison
|
||||||
.It Li $31 Ta Li != comparison
|
.It Li $31 Ta Li != comparison
|
||||||
.It Li $32 Ta Li > comparison
|
.It Li $32 Ta Li > comparison
|
||||||
@@ -241,6 +241,7 @@ with some bytes being special prefixes for integers and symbols.
|
|||||||
.It Li $35 Ta Li <= comparison
|
.It Li $35 Ta Li <= comparison
|
||||||
.It Li $40 Ta Li << operator
|
.It Li $40 Ta Li << operator
|
||||||
.It Li $41 Ta Li >> operator
|
.It Li $41 Ta Li >> operator
|
||||||
|
.It Li $42 Ta Li >>> operator
|
||||||
.It Li $50 Ta Li BANK(symbol) ,
|
.It Li $50 Ta Li BANK(symbol) ,
|
||||||
a
|
a
|
||||||
.Ar LONG
|
.Ar LONG
|
||||||
|
|||||||
@@ -12,7 +12,14 @@
|
|||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
const char *get_package_version_string(void)
|
// This variable is passed via `-D` from the Makefile, but not from CMake
|
||||||
|
// (in which `configure_file()` is used on this file to replace some syntax)
|
||||||
|
#ifndef BUILD_VERSION_STRING
|
||||||
|
// CMake-specific syntax here
|
||||||
|
#define BUILD_VERSION_STRING "@GIT_REV@"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char const *get_package_version_string(void)
|
||||||
{
|
{
|
||||||
// The following conditional should be simplified by the compiler.
|
// The following conditional should be simplified by the compiler.
|
||||||
if (strlen(BUILD_VERSION_STRING) == 0) {
|
if (strlen(BUILD_VERSION_STRING) == 0) {
|
||||||
|
|||||||
3
test/.gitignore
vendored
Normal file
3
test/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/pokecrystal/
|
||||||
|
/pokered/
|
||||||
|
/ucity/
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
ERROR: align-large-ofs.asm(2):
|
error: align-large-ofs.asm(2):
|
||||||
Alignment offset (2) must be smaller than alignment size (2)
|
Alignment offset (2) must be smaller than alignment size (2)
|
||||||
error: Assembly aborted (1 error)!
|
error: Assembly aborted (1 error)!
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
ERROR: align-pc-outside-section.asm(1):
|
error: align-pc-outside-section.asm(1):
|
||||||
Cannot output data outside of a SECTION
|
Cannot output data outside of a SECTION
|
||||||
error: Assembly aborted (1 error)!
|
error: Assembly aborted (1 error)!
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
ERROR: align-unattainable.asm(3):
|
error: align-unattainable.asm(3):
|
||||||
Section "X"'s alignment cannot be attained in WRAM0
|
Section "X"'s alignment cannot be attained in WRAM0
|
||||||
error: Assembly aborted (1 error)!
|
error: Assembly aborted (1 error)!
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
ERROR: anon-label-bad.asm(2):
|
error: anon-label-bad.asm(2):
|
||||||
Label "!0" created outside of a SECTION
|
Label "!0" created outside of a SECTION
|
||||||
ERROR: anon-label-bad.asm(6):
|
error: anon-label-bad.asm(6):
|
||||||
Reference to anonymous label 2 before, when only 1 has been created so far
|
Reference to anonymous label 2 before, when only 1 has been created so far
|
||||||
ERROR: anon-label-bad.asm(18):
|
error: anon-label-bad.asm(18):
|
||||||
syntax error, unexpected :
|
syntax error, unexpected :
|
||||||
error: Assembly aborted (3 errors)!
|
error: Assembly aborted (3 errors)!
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
ERROR: anon-label-bad.asm(2):
|
error: anon-label-bad.asm(2):
|
||||||
Label "!0" created outside of a SECTION
|
Label "!0" created outside of a SECTION
|
||||||
ERROR: anon-label-bad.asm(6):
|
error: anon-label-bad.asm(6):
|
||||||
Reference to anonymous label 2 before, when only 1 has been created so far
|
Reference to anonymous label 2 before, when only 1 has been created so far
|
||||||
ERROR: anon-label-bad.asm(18):
|
error: anon-label-bad.asm(18):
|
||||||
syntax error
|
syntax error
|
||||||
error: Assembly aborted (3 errors)!
|
error: Assembly aborted (3 errors)!
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
ERROR: assert-nosect-bank.asm(1):
|
error: assert-nosect-bank.asm(1):
|
||||||
PC has no bank outside a section
|
PC has no bank outside a section
|
||||||
error: Assembly aborted (1 error)!
|
error: Assembly aborted (1 error)!
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
ERROR: assert.asm(4):
|
error: assert.asm(4):
|
||||||
Assertion failed
|
Assertion failed
|
||||||
warning: assert.asm(10): [-Wassert]
|
warning: assert.asm(10): [-Wassert]
|
||||||
Assertion failed
|
Assertion failed
|
||||||
ERROR: assert.asm(18):
|
error: assert.asm(18):
|
||||||
Expected constant expression: 'FloatingBase' is not constant at assembly time
|
Expected constant expression: 'FloatingBase' is not constant at assembly time
|
||||||
ERROR: assert.asm(18):
|
error: assert.asm(18):
|
||||||
Assertion failed
|
Assertion failed
|
||||||
FATAL: assert.asm(21):
|
FATAL: assert.asm(21):
|
||||||
Assertion failed
|
Assertion failed
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
ERROR: assert@-no-sect.asm(1):
|
error: assert@-no-sect.asm(1):
|
||||||
PC has no value outside a section
|
PC has no value outside a section
|
||||||
error: Assembly aborted (1 error)!
|
error: Assembly aborted (1 error)!
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
ERROR: bank.asm(13) -> bank.asm::def_sect(8):
|
error: bank.asm(13) -> bank.asm::def_sect(8):
|
||||||
Expected constant expression: Section "ROMX_bad"'s bank is not known
|
Expected constant expression: Section "ROMX_bad"'s bank is not known
|
||||||
ERROR: bank.asm(15) -> bank.asm::def_sect(8):
|
error: bank.asm(15) -> bank.asm::def_sect(8):
|
||||||
Expected constant expression: Section "VRAM_bad"'s bank is not known
|
Expected constant expression: Section "VRAM_bad"'s bank is not known
|
||||||
ERROR: bank.asm(17) -> bank.asm::def_sect(8):
|
error: bank.asm(17) -> bank.asm::def_sect(8):
|
||||||
Expected constant expression: Section "SRAM_bad"'s bank is not known
|
Expected constant expression: Section "SRAM_bad"'s bank is not known
|
||||||
ERROR: bank.asm(20) -> bank.asm::def_sect(8):
|
error: bank.asm(20) -> bank.asm::def_sect(8):
|
||||||
Expected constant expression: Section "WRAMX_bad"'s bank is not known
|
Expected constant expression: Section "WRAMX_bad"'s bank is not known
|
||||||
error: Assembly aborted (4 errors)!
|
error: Assembly aborted (4 errors)!
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
ERROR: block-comment-termination-error.asm(1):
|
error: block-comment-termination-error.asm(1):
|
||||||
Unterminated block comment
|
Unterminated block comment
|
||||||
ERROR: block-comment-termination-error.asm(1):
|
error: block-comment-termination-error.asm(1):
|
||||||
syntax error, unexpected end of buffer
|
syntax error, unexpected end of buffer
|
||||||
error: Assembly aborted (2 errors)!
|
error: Assembly aborted (2 errors)!
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
ERROR: block-comment-termination-error.asm(1):
|
error: block-comment-termination-error.asm(1):
|
||||||
Unterminated block comment
|
Unterminated block comment
|
||||||
ERROR: block-comment-termination-error.asm(1):
|
error: block-comment-termination-error.asm(1):
|
||||||
syntax error
|
syntax error
|
||||||
error: Assembly aborted (2 errors)!
|
error: Assembly aborted (2 errors)!
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
ERROR: bracketed-symbols.asm(16):
|
error: bracketed-symbols.asm(16):
|
||||||
Formatting string as type 'X'
|
Formatting string as type 'X'
|
||||||
ERROR: bracketed-symbols.asm(20):
|
error: bracketed-symbols.asm(20):
|
||||||
"Label" does not have a constant value
|
"Label" does not have a constant value
|
||||||
ERROR: bracketed-symbols.asm(21):
|
error: bracketed-symbols.asm(21):
|
||||||
Expected constant PC but section is not fixed
|
Expected constant PC but section is not fixed
|
||||||
error: Assembly aborted (3 errors)!
|
error: Assembly aborted (3 errors)!
|
||||||
|
|||||||
@@ -1,57 +1,57 @@
|
|||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(7):
|
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(7):
|
||||||
'__LINE__' already defined as constant at <builtin>
|
'__LINE__' already defined as constant at <builtin>
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(8):
|
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(8):
|
||||||
'__LINE__' already defined as constant at <builtin>
|
'__LINE__' already defined as constant at <builtin>
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(11):
|
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(11):
|
||||||
'__LINE__' already defined at <builtin>
|
'__LINE__' already defined at <builtin>
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(12):
|
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(12):
|
||||||
'__LINE__' already defined at <builtin>
|
'__LINE__' already defined at <builtin>
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(16):
|
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(16):
|
||||||
Built-in symbol '__LINE__' cannot be purged
|
Built-in symbol '__LINE__' cannot be purged
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(17):
|
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(17):
|
||||||
Built-in symbol '__LINE__' cannot be purged
|
Built-in symbol '__LINE__' cannot be purged
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(20):
|
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(20):
|
||||||
'__LINE__' already defined at <builtin>
|
'__LINE__' already defined at <builtin>
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(21):
|
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(21):
|
||||||
'__LINE__' already defined at <builtin>
|
'__LINE__' already defined at <builtin>
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(24):
|
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(24):
|
||||||
'__LINE__' already defined as constant at <builtin>
|
'__LINE__' already defined as constant at <builtin>
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(25):
|
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(25):
|
||||||
'__LINE__' already defined as constant at <builtin>
|
'__LINE__' already defined as constant at <builtin>
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(28):
|
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(28):
|
||||||
'__LINE__' already defined at <builtin>
|
'__LINE__' already defined at <builtin>
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(29):
|
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(29):
|
||||||
'__LINE__' already defined at <builtin>
|
'__LINE__' already defined at <builtin>
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(32):
|
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(32):
|
||||||
'__LINE__' already defined as constant at <builtin>
|
'__LINE__' already defined as constant at <builtin>
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(33):
|
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(33):
|
||||||
'__LINE__' already defined as constant at <builtin>
|
'__LINE__' already defined as constant at <builtin>
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(36):
|
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(36):
|
||||||
'__LINE__' already defined as non-EQUS at <builtin>
|
'__LINE__' already defined as non-EQUS at <builtin>
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(37):
|
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(37):
|
||||||
'__LINE__' already defined as non-EQUS at <builtin>
|
'__LINE__' already defined as non-EQUS at <builtin>
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(16):
|
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(16):
|
||||||
Built-in symbol '__FILE__' cannot be purged
|
Built-in symbol '__FILE__' cannot be purged
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(17):
|
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(17):
|
||||||
Built-in symbol '__FILE__' cannot be purged
|
Built-in symbol '__FILE__' cannot be purged
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(20):
|
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(20):
|
||||||
'__FILE__' already defined at <builtin>
|
'__FILE__' already defined at <builtin>
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(21):
|
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(21):
|
||||||
'__FILE__' already defined at <builtin>
|
'__FILE__' already defined at <builtin>
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(24):
|
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(24):
|
||||||
'__FILE__' already defined as constant at <builtin>
|
'__FILE__' already defined as constant at <builtin>
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(25):
|
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(25):
|
||||||
'__FILE__' already defined as constant at <builtin>
|
'__FILE__' already defined as constant at <builtin>
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(28):
|
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(28):
|
||||||
'__FILE__' already defined at <builtin>
|
'__FILE__' already defined at <builtin>
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(29):
|
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(29):
|
||||||
'__FILE__' already defined at <builtin>
|
'__FILE__' already defined at <builtin>
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(32):
|
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(32):
|
||||||
'__FILE__' already defined as constant at <builtin>
|
'__FILE__' already defined as constant at <builtin>
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(33):
|
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(33):
|
||||||
'__FILE__' already defined as constant at <builtin>
|
'__FILE__' already defined as constant at <builtin>
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(36):
|
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(36):
|
||||||
Built-in symbol '__FILE__' cannot be redefined
|
Built-in symbol '__FILE__' cannot be redefined
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(37):
|
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(37):
|
||||||
Built-in symbol '__FILE__' cannot be redefined
|
Built-in symbol '__FILE__' cannot be redefined
|
||||||
error: Assembly aborted (28 errors)!
|
error: Assembly aborted (28 errors)!
|
||||||
|
|||||||
21
test/asm/ccode.asm
Normal file
21
test/asm/ccode.asm
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
SECTION "ccode test", ROM0[0]
|
||||||
|
|
||||||
|
Label:
|
||||||
|
|
||||||
|
.local1
|
||||||
|
jp z, Label
|
||||||
|
jr nz, .local1
|
||||||
|
call c, Label
|
||||||
|
call nc, Label
|
||||||
|
|
||||||
|
.local2
|
||||||
|
jp !nz, Label
|
||||||
|
jr !z, .local2
|
||||||
|
call !nc, Label
|
||||||
|
call !c, Label
|
||||||
|
|
||||||
|
.local3
|
||||||
|
jp !!z, Label
|
||||||
|
jr !!nz, .local3
|
||||||
|
call !!c, Label
|
||||||
|
call !!nc, Label
|
||||||
0
test/asm/ccode.err
Normal file
0
test/asm/ccode.err
Normal file
0
test/asm/ccode.out
Normal file
0
test/asm/ccode.out
Normal file
BIN
test/asm/ccode.out.bin
Normal file
BIN
test/asm/ccode.out.bin
Normal file
Binary file not shown.
@@ -1,15 +1,15 @@
|
|||||||
ERROR: code-after-endm-endr-endc.asm(6):
|
error: code-after-endm-endr-endc.asm(6):
|
||||||
syntax error, unexpected PRINTLN, expecting newline or end of buffer
|
syntax error, unexpected PRINTLN, expecting newline or end of buffer
|
||||||
ERROR: code-after-endm-endr-endc.asm(7):
|
error: code-after-endm-endr-endc.asm(7):
|
||||||
Macro "mac" not defined
|
Macro "mac" not defined
|
||||||
ERROR: code-after-endm-endr-endc.asm(12):
|
error: code-after-endm-endr-endc.asm(12):
|
||||||
syntax error, unexpected PRINTLN, expecting newline or end of buffer
|
syntax error, unexpected PRINTLN, expecting newline or end of buffer
|
||||||
ERROR: code-after-endm-endr-endc.asm(17):
|
error: code-after-endm-endr-endc.asm(17):
|
||||||
syntax error, unexpected PRINTLN, expecting newline
|
syntax error, unexpected PRINTLN, expecting newline
|
||||||
ERROR: code-after-endm-endr-endc.asm(19):
|
error: code-after-endm-endr-endc.asm(19):
|
||||||
syntax error, unexpected PRINTLN, expecting newline or end of buffer
|
syntax error, unexpected PRINTLN, expecting newline or end of buffer
|
||||||
ERROR: code-after-endm-endr-endc.asm(23):
|
error: code-after-endm-endr-endc.asm(23):
|
||||||
syntax error, unexpected PRINTLN, expecting newline
|
syntax error, unexpected PRINTLN, expecting newline
|
||||||
ERROR: code-after-endm-endr-endc.asm(25):
|
error: code-after-endm-endr-endc.asm(25):
|
||||||
syntax error, unexpected PRINTLN, expecting newline or end of buffer
|
syntax error, unexpected PRINTLN, expecting newline or end of buffer
|
||||||
error: Assembly aborted (7 errors)!
|
error: Assembly aborted (7 errors)!
|
||||||
|
|||||||
40
test/asm/compound-assignment.asm
Normal file
40
test/asm/compound-assignment.asm
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
macro try
|
||||||
|
println \1, "\2:"
|
||||||
|
def prefix equs \1
|
||||||
|
{prefix}\2 = 10
|
||||||
|
println \2 ; 10
|
||||||
|
{prefix}\2 += 5
|
||||||
|
println \2 ; 15
|
||||||
|
{prefix}\2 -= 1
|
||||||
|
println \2 ; 14
|
||||||
|
{prefix}\2 *= 2
|
||||||
|
println \2 ; 28
|
||||||
|
{prefix}\2 /= 4
|
||||||
|
println \2 ; 7
|
||||||
|
{prefix}\2 %= 3
|
||||||
|
println \2 ; 1
|
||||||
|
{prefix}\2 |= 11
|
||||||
|
println \2 ; 11
|
||||||
|
{prefix}\2 ^= 12
|
||||||
|
println \2 ; 7
|
||||||
|
{prefix}\2 &= 21
|
||||||
|
println \2 ; 5
|
||||||
|
{prefix}\2 <<= 2
|
||||||
|
println \2 ; 20
|
||||||
|
{prefix}\2 >>= 1
|
||||||
|
println \2 ; 10
|
||||||
|
purge prefix
|
||||||
|
endm
|
||||||
|
|
||||||
|
try "", p
|
||||||
|
try "def ", q
|
||||||
|
try "redef ", r
|
||||||
|
|
||||||
|
_RS += 100
|
||||||
|
println _RS
|
||||||
|
|
||||||
|
__LINE__ *= 200
|
||||||
|
println __LINE__
|
||||||
|
|
||||||
|
UnDeFiNeD ^= 300
|
||||||
|
println UnDeFiNeD
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user