mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-23 19:42:08 +00:00
Compare commits
32 Commits
v0.5.2
...
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 |
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,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`
|
||||||
|
|||||||
23
.github/workflows/testing.yml
vendored
23
.github/workflows/testing.yml
vendored
@@ -16,19 +16,6 @@ jobs:
|
|||||||
cc: gcc
|
cc: gcc
|
||||||
- os: macos-11.0
|
- os: macos-11.0
|
||||||
cc: gcc
|
cc: gcc
|
||||||
include:
|
|
||||||
- os: ubuntu-18.04
|
|
||||||
target: develop
|
|
||||||
cmakevars: -DSANITIZERS=ON -DMORE_WARNINGS=ON -DCMAKE_BUILD_TYPE=Debug
|
|
||||||
- os: ubuntu-20.04
|
|
||||||
target: develop
|
|
||||||
cmakevars: -DSANITIZERS=ON -DMORE_WARNINGS=ON -DCMAKE_BUILD_TYPE=Debug
|
|
||||||
- os: macos-11.0
|
|
||||||
target: develop
|
|
||||||
cmakevars: -DSANITIZERS=ON -DMORE_WARNINGS=ON -DCMAKE_BUILD_TYPE=Debug
|
|
||||||
- os: macos-10.15
|
|
||||||
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:
|
||||||
@@ -43,13 +30,13 @@ 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 -j
|
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
|
||||||
@@ -86,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
|
||||||
}
|
}
|
||||||
@@ -106,7 +93,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 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`
|
||||||
|
|||||||
@@ -42,33 +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
|
||||||
-Wno-unknown-warning-option -Wno-tautological-constant-out-of-range-compare)
|
# 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)
|
||||||
|
execute_process(COMMAND ${GIT} describe --tags --dirty --always
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
OUTPUT_VARIABLE GIT_REV OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
ERROR_QUIET)
|
ERROR_QUIET)
|
||||||
string(STRIP "${GIT_REV}" GIT_REV)
|
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)
|
||||||
|
|
||||||
|
|||||||
30
Makefile
30
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
|
||||||
@@ -209,23 +209,27 @@ checkdiff:
|
|||||||
# 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-unknown-warning-option -Wno-tautological-constant-out-of-range-compare \
|
-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!
|
||||||
@@ -233,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:
|
||||||
|
|||||||
@@ -73,10 +73,14 @@ The RGBDS source code file structure somewhat resembles the following:
|
|||||||
├── 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.
|
||||||
|
|
||||||
@@ -99,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
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|||||||
@@ -77,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);
|
||||||
|
|||||||
@@ -28,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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
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
|
||||||
"error.c"
|
"error.c"
|
||||||
"extern/getopt.c"
|
"extern/getopt.c"
|
||||||
"version.c"
|
"_version.c"
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(PkgConfig)
|
find_package(PkgConfig)
|
||||||
|
|||||||
@@ -284,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)
|
||||||
@@ -507,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);
|
||||||
|
|||||||
187
src/asm/lexer.c
187
src/asm/lexer.c
@@ -109,7 +109,7 @@ static struct KeywordMapping {
|
|||||||
{"DEC", T_Z80_DEC},
|
{"DEC", T_Z80_DEC},
|
||||||
{"DI", T_Z80_DI},
|
{"DI", T_Z80_DI},
|
||||||
{"EI", T_Z80_EI},
|
{"EI", T_Z80_EI},
|
||||||
{"HALT", T_Z80_HALT},
|
{"HALT✋", T_Z80_HALT},
|
||||||
{"INC", T_Z80_INC},
|
{"INC", T_Z80_INC},
|
||||||
{"JP", T_Z80_JP},
|
{"JP", T_Z80_JP},
|
||||||
{"JR", T_Z80_JR},
|
{"JR", T_Z80_JR},
|
||||||
@@ -118,8 +118,9 @@ static struct KeywordMapping {
|
|||||||
{"LDD", T_Z80_LDD},
|
{"LDD", T_Z80_LDD},
|
||||||
{"LDIO", T_Z80_LDH},
|
{"LDIO", T_Z80_LDH},
|
||||||
{"LDH", T_Z80_LDH},
|
{"LDH", T_Z80_LDH},
|
||||||
{"NOP", T_Z80_NOP},
|
{"NOPE", T_Z80_NOP},
|
||||||
{"OR", T_Z80_OR},
|
{"OR", T_Z80_OR},
|
||||||
|
{"OWO", T_OWO},
|
||||||
{"POP", T_Z80_POP},
|
{"POP", T_Z80_POP},
|
||||||
{"PUSH", T_Z80_PUSH},
|
{"PUSH", T_Z80_PUSH},
|
||||||
{"RES", T_Z80_RES},
|
{"RES", T_Z80_RES},
|
||||||
@@ -140,7 +141,7 @@ static struct KeywordMapping {
|
|||||||
{"SLA", T_Z80_SLA},
|
{"SLA", T_Z80_SLA},
|
||||||
{"SRA", T_Z80_SRA},
|
{"SRA", T_Z80_SRA},
|
||||||
{"SRL", T_Z80_SRL},
|
{"SRL", T_Z80_SRL},
|
||||||
{"STOP", T_Z80_STOP},
|
{"STOP!!🛑", T_Z80_STOP},
|
||||||
{"SUB", T_Z80_SUB},
|
{"SUB", T_Z80_SUB},
|
||||||
{"SWAP", T_Z80_SWAP},
|
{"SWAP", T_Z80_SWAP},
|
||||||
{"XOR", T_Z80_XOR},
|
{"XOR", T_Z80_XOR},
|
||||||
@@ -148,24 +149,29 @@ static struct KeywordMapping {
|
|||||||
{"NZ", T_CC_NZ},
|
{"NZ", T_CC_NZ},
|
||||||
{"Z", T_CC_Z},
|
{"Z", T_CC_Z},
|
||||||
{"NC", T_CC_NC},
|
{"NC", T_CC_NC},
|
||||||
/* Handled after as T_TOKEN_C */
|
{"C", T_CC_C},
|
||||||
/* { "C", T_CC_C }, */
|
|
||||||
|
|
||||||
{"AF", T_MODE_AF},
|
{"•̀A•́)𝓕𝓾𝓬𝓴", T_MODE_AF},
|
||||||
{"BC", T_MODE_BC},
|
// {"BC", T_MODE_BC},
|
||||||
{"DE", T_MODE_DE},
|
// {"DE", T_MODE_DE},
|
||||||
{"HL", T_MODE_HL},
|
{"н∠(", T_MODE_HL_START},
|
||||||
{"SP", T_MODE_SP},
|
{"SP", T_MODE_SP},
|
||||||
{"HLD", T_MODE_HL_DEC},
|
{"н∠( ᐛ 」∠)_👁", T_MODE_HL_DEC},
|
||||||
{"HLI", T_MODE_HL_INC},
|
{"н∠( ᐛ 」∠)_👎", T_MODE_HL_INC},
|
||||||
|
|
||||||
{"A", T_TOKEN_A},
|
// HACK: normally this is surrounded by parens, but this is annoying to special-case,
|
||||||
{"B", T_TOKEN_B},
|
// so we use cooperation from the parser.
|
||||||
{"C", T_TOKEN_C},
|
{"•̀A•́", T_TOKEN_A},
|
||||||
{"D", T_TOKEN_D},
|
// {"=B", T_TOKEN_B}, HACK: This begins with a non-identifier character, so we'll cheat
|
||||||
{"E", T_TOKEN_E},
|
{"♥(˘⌣˘", T_TOKEN_C}, // HACK: same for "C" after the space & closing paren
|
||||||
{"H", T_TOKEN_H},
|
// {";D", T_TOKEN_D}, HACK: also needs to be special-cased. God I feel dirty.
|
||||||
{"L", T_TOKEN_L},
|
{"(´ε`", T_TOKEN_E},
|
||||||
|
{"♡", T_TOKEN_E_HEART},
|
||||||
|
{"н", T_TOKEN_H},
|
||||||
|
{"∠(", T_TOKEN_L_ARM},
|
||||||
|
{"ᐛ", T_TOKEN_L_FACE},
|
||||||
|
{"」∠", T_TOKEN_L_BODY},
|
||||||
|
{"_", T_TOKEN_L_LEG},
|
||||||
|
|
||||||
{"DEF", T_OP_DEF},
|
{"DEF", T_OP_DEF},
|
||||||
|
|
||||||
@@ -400,16 +406,16 @@ uint32_t lexer_GetIFDepth(void)
|
|||||||
|
|
||||||
void lexer_IncIFDepth(void)
|
void lexer_IncIFDepth(void)
|
||||||
{
|
{
|
||||||
struct IfStack *new = malloc(sizeof(*new));
|
struct IfStack *ifStack = malloc(sizeof(*ifStack));
|
||||||
|
|
||||||
if (!new)
|
if (!ifStack)
|
||||||
fatalerror("Unable to allocate new IF depth: %s\n", strerror(errno));
|
fatalerror("Unable to allocate new IF depth: %s\n", strerror(errno));
|
||||||
|
|
||||||
new->ranIfBlock = false;
|
ifStack->ranIfBlock = false;
|
||||||
new->reachedElseBlock = false;
|
ifStack->reachedElseBlock = false;
|
||||||
new->next = lexerState->ifStack;
|
ifStack->next = lexerState->ifStack;
|
||||||
|
|
||||||
lexerState->ifStack = new;
|
lexerState->ifStack = ifStack;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lexer_DecIFDepth(void)
|
void lexer_DecIFDepth(void)
|
||||||
@@ -578,16 +584,16 @@ struct KeywordDictNode {
|
|||||||
* In turn, this allows greatly simplifying checking an index into this array,
|
* In turn, this allows greatly simplifying checking an index into this array,
|
||||||
* which should help speed up the lexer.
|
* which should help speed up the lexer.
|
||||||
*/
|
*/
|
||||||
uint16_t children[0x60 - ' '];
|
uint16_t children[256]; // HACK: we "support" UTF-8 as input now
|
||||||
struct KeywordMapping const *keyword;
|
struct KeywordMapping const *keyword;
|
||||||
/* Since the keyword structure is invariant, the min number of nodes is known at compile time */
|
/* Since the keyword structure is invariant, the min number of nodes is known at compile time */
|
||||||
} keywordDict[365] = {0}; /* Make sure to keep this correct when adding keywords! */
|
} keywordDict[690] = {0}; /* Nice */
|
||||||
|
|
||||||
/* Convert a char into its index into the dict */
|
/* Convert a char into its index into the dict */
|
||||||
static uint8_t dictIndex(char c)
|
static uint8_t dictIndex(char c)
|
||||||
{
|
{
|
||||||
/* Translate uppercase to lowercase (roughly) */
|
/* Translate uppercase to lowercase (roughly) */
|
||||||
if (c > 0x60)
|
if (c > 0x60 && c < 0x80)
|
||||||
c = c - ('a' - 'A');
|
c = c - ('a' - 'A');
|
||||||
return c - ' ';
|
return c - ' ';
|
||||||
}
|
}
|
||||||
@@ -609,8 +615,9 @@ void lexer_Init(void)
|
|||||||
|
|
||||||
/* Walk the dictionary, creating intermediate nodes for the keyword */
|
/* Walk the dictionary, creating intermediate nodes for the keyword */
|
||||||
for (char const *ptr = keywords[i].name; *ptr; ptr++) {
|
for (char const *ptr = keywords[i].name; *ptr; ptr++) {
|
||||||
|
unsigned char index = (unsigned char)*ptr - ' ';
|
||||||
/* We should be able to assume all entries are well-formed */
|
/* We should be able to assume all entries are well-formed */
|
||||||
if (keywordDict[nodeID].children[*ptr - ' '] == 0) {
|
if (keywordDict[nodeID].children[index] == 0) {
|
||||||
/*
|
/*
|
||||||
* If this gets tripped up, set the size of keywordDict to
|
* If this gets tripped up, set the size of keywordDict to
|
||||||
* something high, compile with `-DPRINT_NODE_COUNT` (see below),
|
* something high, compile with `-DPRINT_NODE_COUNT` (see below),
|
||||||
@@ -619,10 +626,10 @@ void lexer_Init(void)
|
|||||||
assert(usedNodes < sizeof(keywordDict) / sizeof(*keywordDict));
|
assert(usedNodes < sizeof(keywordDict) / sizeof(*keywordDict));
|
||||||
|
|
||||||
/* There is no node at that location, grab one from the pool */
|
/* There is no node at that location, grab one from the pool */
|
||||||
keywordDict[nodeID].children[*ptr - ' '] = usedNodes;
|
keywordDict[nodeID].children[index] = usedNodes;
|
||||||
usedNodes++;
|
usedNodes++;
|
||||||
}
|
}
|
||||||
nodeID = keywordDict[nodeID].children[*ptr - ' '];
|
nodeID = keywordDict[nodeID].children[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This assumes that no two keywords have the same name */
|
/* This assumes that no two keywords have the same name */
|
||||||
@@ -668,28 +675,32 @@ static void beginExpansion(char const *str, bool owned, char const *name)
|
|||||||
if (!size)
|
if (!size)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (name) {
|
if (name)
|
||||||
|
lexer_CheckRecursionDepth();
|
||||||
|
|
||||||
|
struct Expansion *exp = malloc(sizeof(*exp));
|
||||||
|
|
||||||
|
if (!exp)
|
||||||
|
fatalerror("Unable to allocate new expansion: %s\n", strerror(errno));
|
||||||
|
|
||||||
|
exp->parent = lexerState->expansions;
|
||||||
|
exp->name = name ? strdup(name) : NULL;
|
||||||
|
exp->contents.unowned = str;
|
||||||
|
exp->size = size;
|
||||||
|
exp->offset = 0;
|
||||||
|
exp->owned = owned;
|
||||||
|
|
||||||
|
lexerState->expansions = exp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lexer_CheckRecursionDepth(void)
|
||||||
|
{
|
||||||
size_t depth = 0;
|
size_t depth = 0;
|
||||||
|
|
||||||
for (struct Expansion *exp = lexerState->expansions; exp; exp = exp->parent) {
|
for (struct Expansion *exp = lexerState->expansions; exp; exp = exp->parent) {
|
||||||
if (depth++ >= maxRecursionDepth)
|
if (depth++ >= maxRecursionDepth)
|
||||||
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
|
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
struct Expansion *new = malloc(sizeof(*new));
|
|
||||||
|
|
||||||
if (!new)
|
|
||||||
fatalerror("Unable to allocate new expansion: %s\n", strerror(errno));
|
|
||||||
|
|
||||||
new->parent = lexerState->expansions;
|
|
||||||
new->name = name ? strdup(name) : NULL;
|
|
||||||
new->contents.unowned = str;
|
|
||||||
new->size = size;
|
|
||||||
new->offset = 0;
|
|
||||||
new->owned = owned;
|
|
||||||
|
|
||||||
lexerState->expansions = new;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void freeExpansion(struct Expansion *expansion)
|
static void freeExpansion(struct Expansion *expansion)
|
||||||
@@ -808,6 +819,19 @@ static char const *readMacroArg(char name)
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t readInternal(size_t bufIndex, size_t nbChars)
|
||||||
|
{
|
||||||
|
// This buffer overflow made me lose WEEKS of my life. Never again.
|
||||||
|
assert(bufIndex + nbChars <= LEXER_BUF_SIZE);
|
||||||
|
ssize_t nbReadChars = read(lexerState->fd, &lexerState->buf[bufIndex], nbChars);
|
||||||
|
|
||||||
|
if (nbReadChars == -1)
|
||||||
|
fatalerror("Error while reading \"%s\": %s\n", lexerState->path, strerror(errno));
|
||||||
|
|
||||||
|
// `nbReadChars` cannot be negative, so it's fine to cast to `size_t`
|
||||||
|
return (size_t)nbReadChars;
|
||||||
|
}
|
||||||
|
|
||||||
/* We only need one character of lookahead, for macro arguments */
|
/* We only need one character of lookahead, for macro arguments */
|
||||||
static int peekInternal(uint8_t distance)
|
static int peekInternal(uint8_t distance)
|
||||||
{
|
{
|
||||||
@@ -839,42 +863,31 @@ static int peekInternal(uint8_t distance)
|
|||||||
|
|
||||||
/* Compute the index we'll start writing to */
|
/* Compute the index we'll start writing to */
|
||||||
size_t writeIndex = (lexerState->index + lexerState->nbChars) % LEXER_BUF_SIZE;
|
size_t writeIndex = (lexerState->index + lexerState->nbChars) % LEXER_BUF_SIZE;
|
||||||
ssize_t nbCharsRead = 0, totalCharsRead = 0;
|
|
||||||
|
|
||||||
#define readChars(size) do { \
|
|
||||||
/* This buffer overflow made me lose WEEKS of my life. Never again. */ \
|
|
||||||
assert(writeIndex + (size) <= LEXER_BUF_SIZE); \
|
|
||||||
nbCharsRead = read(lexerState->fd, &lexerState->buf[writeIndex], (size)); \
|
|
||||||
if (nbCharsRead == -1) \
|
|
||||||
fatalerror("Error while reading \"%s\": %s\n", lexerState->path, strerror(errno)); \
|
|
||||||
totalCharsRead += nbCharsRead; \
|
|
||||||
writeIndex += nbCharsRead; \
|
|
||||||
if (writeIndex == LEXER_BUF_SIZE) \
|
|
||||||
writeIndex = 0; \
|
|
||||||
target -= nbCharsRead; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
/* If the range to fill passes over the buffer wrapping point, we need two reads */
|
/* If the range to fill passes over the buffer wrapping point, we need two reads */
|
||||||
if (writeIndex + target > LEXER_BUF_SIZE) {
|
if (writeIndex + target > LEXER_BUF_SIZE) {
|
||||||
size_t nbExpectedChars = LEXER_BUF_SIZE - writeIndex;
|
size_t nbExpectedChars = LEXER_BUF_SIZE - writeIndex;
|
||||||
|
size_t nbReadChars = readInternal(writeIndex, nbExpectedChars);
|
||||||
|
|
||||||
|
lexerState->nbChars += nbReadChars;
|
||||||
|
|
||||||
|
writeIndex += nbReadChars;
|
||||||
|
if (writeIndex == LEXER_BUF_SIZE)
|
||||||
|
writeIndex = 0;
|
||||||
|
|
||||||
readChars(nbExpectedChars);
|
|
||||||
// If the read was incomplete, don't perform a second read
|
// If the read was incomplete, don't perform a second read
|
||||||
// `nbCharsRead` cannot be negative, so it's fine to cast to `size_t`
|
target -= nbReadChars;
|
||||||
if ((size_t)nbCharsRead < nbExpectedChars)
|
if (nbReadChars < nbExpectedChars)
|
||||||
target = 0;
|
target = 0;
|
||||||
}
|
}
|
||||||
if (target != 0)
|
if (target != 0)
|
||||||
readChars(target);
|
lexerState->nbChars += readInternal(writeIndex, target);
|
||||||
|
|
||||||
#undef readChars
|
|
||||||
|
|
||||||
lexerState->nbChars += totalCharsRead;
|
|
||||||
|
|
||||||
/* If there aren't enough chars even after refilling, give up */
|
/* If there aren't enough chars even after refilling, give up */
|
||||||
if (lexerState->nbChars <= distance)
|
if (lexerState->nbChars <= distance)
|
||||||
return EOF;
|
return EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (unsigned char)lexerState->buf[(lexerState->index + distance) % LEXER_BUF_SIZE];
|
return (unsigned char)lexerState->buf[(lexerState->index + distance) % LEXER_BUF_SIZE];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1283,12 +1296,16 @@ static uint32_t readGfxConstant(void)
|
|||||||
static bool startsIdentifier(int c)
|
static bool startsIdentifier(int c)
|
||||||
{
|
{
|
||||||
// Anonymous labels internally start with '!'
|
// Anonymous labels internally start with '!'
|
||||||
return (c <= 'Z' && c >= 'A') || (c <= 'z' && c >= 'a') || c == '.' || c == '_';
|
return (c <= 'Z' && c >= 'A') || (c <= 'z' && c >= 'a') || c == '.' || c == '_' || c >= 0x80 || c == '(';
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool continuesIdentifier(int c)
|
static bool continuesIdentifier(int c)
|
||||||
{
|
{
|
||||||
return startsIdentifier(c) || (c <= '9' && c >= '0') || c == '#' || c == '@';
|
// April Fools HACK: allow UTF-8 :D
|
||||||
|
// This would normally be quite unsafe (hello, RTL control codes?),
|
||||||
|
// but since this is for a joke I'll also make the code a joke
|
||||||
|
// Also, hi if you're reading this!
|
||||||
|
return startsIdentifier(c) || (c <= '9' && c >= '0') || c == '#' || c == '@' || c == '!';
|
||||||
}
|
}
|
||||||
|
|
||||||
static int readIdentifier(char firstChar)
|
static int readIdentifier(char firstChar)
|
||||||
@@ -1768,6 +1785,10 @@ static int yylex_NORMAL(void)
|
|||||||
/* Ignore whitespace and comments */
|
/* Ignore whitespace and comments */
|
||||||
|
|
||||||
case ';':
|
case ';':
|
||||||
|
if (peek() == 'D') {
|
||||||
|
shiftChar();
|
||||||
|
return T_TOKEN_D;
|
||||||
|
}
|
||||||
discardComment();
|
discardComment();
|
||||||
/* fallthrough */
|
/* fallthrough */
|
||||||
case ' ':
|
case ' ':
|
||||||
@@ -1788,8 +1809,6 @@ static int yylex_NORMAL(void)
|
|||||||
return T_LBRACK;
|
return T_LBRACK;
|
||||||
case ']':
|
case ']':
|
||||||
return T_RBRACK;
|
return T_RBRACK;
|
||||||
case '(':
|
|
||||||
return T_LPAREN;
|
|
||||||
case ')':
|
case ')':
|
||||||
return T_RPAREN;
|
return T_RPAREN;
|
||||||
case ',':
|
case ',':
|
||||||
@@ -1857,9 +1876,14 @@ static int yylex_NORMAL(void)
|
|||||||
return T_OP_XOR;
|
return T_OP_XOR;
|
||||||
|
|
||||||
case '=': /* Either assignment or EQ */
|
case '=': /* Either assignment or EQ */
|
||||||
if (peek() == '=') {
|
switch (peek()) {
|
||||||
|
case '=':
|
||||||
shiftChar();
|
shiftChar();
|
||||||
return T_OP_LOGICEQU;
|
return T_OP_LOGICEQU;
|
||||||
|
case 'b':
|
||||||
|
case 'B':
|
||||||
|
shiftChar();
|
||||||
|
return T_TOKEN_B;
|
||||||
}
|
}
|
||||||
return T_POP_EQUAL;
|
return T_POP_EQUAL;
|
||||||
|
|
||||||
@@ -1888,18 +1912,23 @@ static int yylex_NORMAL(void)
|
|||||||
return T_OP_LOGICLT;
|
return T_OP_LOGICLT;
|
||||||
}
|
}
|
||||||
|
|
||||||
case '>': /* Either >>=, GT, GTE, or right shift */
|
case '>': /* Either >>=, GT, GTE, or either kind of right shift */
|
||||||
switch (peek()) {
|
switch (peek()) {
|
||||||
case '=':
|
case '=':
|
||||||
shiftChar();
|
shiftChar();
|
||||||
return T_OP_LOGICGE;
|
return T_OP_LOGICGE;
|
||||||
case '>':
|
case '>':
|
||||||
shiftChar();
|
shiftChar();
|
||||||
if (peek() == '=') {
|
switch (peek()) {
|
||||||
|
case '=':
|
||||||
shiftChar();
|
shiftChar();
|
||||||
return T_POP_SHREQ;
|
return T_POP_SHREQ;
|
||||||
}
|
case '>':
|
||||||
|
shiftChar();
|
||||||
|
return T_OP_USHR;
|
||||||
|
default:
|
||||||
return T_OP_SHR;
|
return T_OP_SHR;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return T_OP_LOGICGT;
|
return T_OP_LOGICGT;
|
||||||
}
|
}
|
||||||
@@ -1993,6 +2022,12 @@ static int yylex_NORMAL(void)
|
|||||||
|
|
||||||
/* Handle identifiers... or report garbage characters */
|
/* Handle identifiers... or report garbage characters */
|
||||||
|
|
||||||
|
case '(':
|
||||||
|
if (peek() != (unsigned char)"´"[0]) {
|
||||||
|
return T_LPAREN;
|
||||||
|
}
|
||||||
|
// fallthrough
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (startsIdentifier(c)) {
|
if (startsIdentifier(c)) {
|
||||||
int tokenType = readIdentifier(c);
|
int tokenType = readIdentifier(c);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -142,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;
|
||||||
|
|
||||||
@@ -241,7 +244,7 @@ int main(int argc, char *argv[])
|
|||||||
if (musl_optarg[0] == '\0' || *ep != '\0')
|
if (musl_optarg[0] == '\0' || *ep != '\0')
|
||||||
errx("Invalid argument for option 'p'");
|
errx("Invalid argument for option 'p'");
|
||||||
|
|
||||||
if (fill < 0 || fill > 0xFF)
|
if (fill > 0xFF)
|
||||||
errx("Argument for option 'p' must be between 0 and 0xFF");
|
errx("Argument for option 'p' must be between 0 and 0xFF");
|
||||||
|
|
||||||
opt_P(fill);
|
opt_P(fill);
|
||||||
|
|||||||
@@ -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,6 +19,7 @@ struct OptStackEntry {
|
|||||||
bool haltnop;
|
bool haltnop;
|
||||||
bool optimizeLoads;
|
bool optimizeLoads;
|
||||||
bool warningsAreErrors;
|
bool warningsAreErrors;
|
||||||
|
size_t maxRecursionDepth;
|
||||||
// Don't be confused: we use the size of the **global variable** `warningStates`!
|
// Don't be confused: we use the size of the **global variable** `warningStates`!
|
||||||
enum WarningState warningStates[sizeof(warningStates)];
|
enum WarningState warningStates[sizeof(warningStates)];
|
||||||
struct OptStackEntry *next;
|
struct OptStackEntry *next;
|
||||||
@@ -24,12 +27,12 @@ struct OptStackEntry {
|
|||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
@@ -39,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;
|
||||||
@@ -86,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);
|
||||||
|
|||||||
@@ -192,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -200,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);
|
||||||
@@ -264,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);
|
||||||
@@ -490,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) {
|
||||||
|
|||||||
@@ -218,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) {
|
||||||
@@ -240,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;
|
||||||
|
|
||||||
@@ -385,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;
|
||||||
@@ -394,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)
|
||||||
@@ -534,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
|
||||||
@@ -542,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 */
|
||||||
@@ -646,31 +646,32 @@ 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
|
||||||
@@ -1144,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;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@@ -1481,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);
|
||||||
}
|
}
|
||||||
@@ -1770,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 {
|
||||||
@@ -2178,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
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -2186,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
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -2194,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
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -2202,10 +2203,19 @@ 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
|
ccode_expr : ccode
|
||||||
| T_OP_LOGICNOT ccode_expr {
|
| T_OP_LOGICNOT ccode_expr {
|
||||||
$$ = $2 ^ 1;
|
$$ = $2 ^ 1;
|
||||||
@@ -2215,7 +2225,7 @@ ccode_expr : ccode
|
|||||||
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; }
|
||||||
@@ -2231,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; }
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
'\" e
|
||||||
.\"
|
.\"
|
||||||
.\" This file is part of RGBDS.
|
.\" This file is part of RGBDS.
|
||||||
.\"
|
.\"
|
||||||
@@ -251,7 +252,9 @@ A great number of operators you can use in expressions are available (listed fro
|
|||||||
.It Li ** Ta Exponent
|
.It Li ** Ta Exponent
|
||||||
.It Li ~ + - Ta Unary complement/plus/minus
|
.It Li ~ + - Ta Unary complement/plus/minus
|
||||||
.It Li * / % Ta Multiply/divide/modulo
|
.It Li * / % Ta Multiply/divide/modulo
|
||||||
.It Li << >> Ta Shift left/right
|
.It Li << Ta Shift left
|
||||||
|
.It Li >> Ta Signed shift right (sign-extension)
|
||||||
|
.It Li >>> Ta Unsigned shift right (zero-extension)
|
||||||
.It Li & \&| ^ Ta Binary and/or/xor
|
.It Li & \&| ^ Ta Binary and/or/xor
|
||||||
.It Li + - Ta Add/subtract
|
.It Li + - Ta Add/subtract
|
||||||
.It Li != == <= >= < > Ta Comparison
|
.It Li != == <= >= < > Ta Comparison
|
||||||
@@ -1384,6 +1387,12 @@ Alternatively, you can use
|
|||||||
to store a list of words (16-bit) or
|
to store a list of words (16-bit) or
|
||||||
.Ic DL
|
.Ic DL
|
||||||
to store a list of double-words/longs (32-bit).
|
to store a list of double-words/longs (32-bit).
|
||||||
|
Both of these write their data in little-endian byte order; for example,
|
||||||
|
.Ql dw $CAFE
|
||||||
|
is equivalent to
|
||||||
|
.Ql db $FE, $CA
|
||||||
|
and not
|
||||||
|
.Ql db $CA, $FE .
|
||||||
.Pp
|
.Pp
|
||||||
Strings are handled a little specially: they first undergo charmap conversion (see
|
Strings are handled a little specially: they first undergo charmap conversion (see
|
||||||
.Sx Character maps ) ,
|
.Sx Character maps ) ,
|
||||||
@@ -1985,6 +1994,7 @@ block, all of them but the first one are ignored.
|
|||||||
.Ss Changing options while assembling
|
.Ss Changing options while assembling
|
||||||
.Ic OPT
|
.Ic OPT
|
||||||
can be used to change some of the options during assembling from within the source, instead of defining them on the command-line.
|
can be used to change some of the options during assembling from within the source, instead of defining them on the command-line.
|
||||||
|
.Pq See Xr rgbasm 1 .
|
||||||
.Pp
|
.Pp
|
||||||
.Ic OPT
|
.Ic OPT
|
||||||
takes a comma-separated list of options as its argument:
|
takes a comma-separated list of options as its argument:
|
||||||
@@ -2000,8 +2010,10 @@ POPO
|
|||||||
LD [$FF88], A ; optimized to use LDH by default
|
LD [$FF88], A ; optimized to use LDH by default
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
The options that OPT can modify are currently:
|
The options that
|
||||||
.Cm b , g , p , h , L ,
|
.Ic OPT
|
||||||
|
can modify are currently:
|
||||||
|
.Cm b , g , p , r , h , L ,
|
||||||
and
|
and
|
||||||
.Cm W .
|
.Cm W .
|
||||||
The Boolean flag options
|
The Boolean flag options
|
||||||
|
|||||||
@@ -317,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)
|
||||||
@@ -339,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;
|
||||||
@@ -421,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;
|
||||||
@@ -477,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 */
|
||||||
|
|
||||||
|
|||||||
@@ -760,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);
|
||||||
@@ -769,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);
|
||||||
|
|||||||
@@ -1268,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);
|
||||||
|
|||||||
713
src/gbz80.7
713
src/gbz80.7
File diff suppressed because it is too large
Load Diff
@@ -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) {
|
||||||
|
|||||||
@@ -95,6 +95,9 @@ void output_png_file(const struct Options *opts,
|
|||||||
if (!f)
|
if (!f)
|
||||||
err("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)
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -281,13 +281,12 @@ static void parseScrambleSpec(char const *spec)
|
|||||||
// Now, determine which region type this is
|
// Now, determine which region type this is
|
||||||
enum ScrambledRegion region = 0;
|
enum ScrambledRegion region = 0;
|
||||||
|
|
||||||
while (region < SCRAMBLE_UNK) {
|
for (; region < SCRAMBLE_UNK; region++) {
|
||||||
// If the strings match (case-insensitively), we got it!
|
// If the strings match (case-insensitively), we got it!
|
||||||
// It's OK not to use `strncasecmp` because `regionName` is still
|
// `strncasecmp` must be used here since `regionName` points
|
||||||
// NUL-terminated, since the encompassing spec is.
|
// to the entire remaining argument.
|
||||||
if (!strcasecmp(scrambleSpecs[region].name, regionName))
|
if (!strncasecmp(scrambleSpecs[region].name, regionName, regionNameLen))
|
||||||
break;
|
break;
|
||||||
region++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (region == SCRAMBLE_UNK)
|
if (region == SCRAMBLE_UNK)
|
||||||
|
|||||||
@@ -111,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... */
|
||||||
@@ -265,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;
|
||||||
|
|||||||
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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,6 +12,13 @@
|
|||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
|
// 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)
|
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.
|
||||||
|
|||||||
17
test/asm/const-and.asm
Normal file
17
test/asm/const-and.asm
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
println @ & 0 ; This should produce an error, but not crash
|
||||||
|
|
||||||
|
SECTION "Test", ROM0,ALIGN[4,2]
|
||||||
|
|
||||||
|
ds 40
|
||||||
|
Aligned:
|
||||||
|
|
||||||
|
println Aligned & $0f
|
||||||
|
println ~$fffc & Aligned
|
||||||
|
println Aligned & $1f ; Not constant
|
||||||
|
println @ & $0f
|
||||||
|
|
||||||
|
SECTION "Unaligned", ROM0
|
||||||
|
|
||||||
|
println @ & 0
|
||||||
|
println @ & 1 ; Nope
|
||||||
7
test/asm/const-and.err
Normal file
7
test/asm/const-and.err
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
error: const-and.asm(2):
|
||||||
|
PC has no value outside a section
|
||||||
|
error: const-and.asm(11):
|
||||||
|
Expected constant expression: 'Aligned' is not constant at assembly time
|
||||||
|
error: const-and.asm(17):
|
||||||
|
Expected constant expression: PC is not constant at assembly time
|
||||||
|
error: Assembly aborted (3 errors)!
|
||||||
7
test/asm/const-and.out
Normal file
7
test/asm/const-and.out
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
$0
|
||||||
|
$A
|
||||||
|
$A
|
||||||
|
$0
|
||||||
|
$A
|
||||||
|
$0
|
||||||
|
$0
|
||||||
13
test/asm/opt-r.asm
Normal file
13
test/asm/opt-r.asm
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
OPT r34 ; :3
|
||||||
|
OPT r 360
|
||||||
|
|
||||||
|
; Invalid
|
||||||
|
OPT r ; Missing arg
|
||||||
|
OPT r 2a ; Bad decimal
|
||||||
|
|
||||||
|
; Check that it has an effect
|
||||||
|
OPT r 1
|
||||||
|
MACRO m
|
||||||
|
m
|
||||||
|
ENDM
|
||||||
|
m
|
||||||
6
test/asm/opt-r.err
Normal file
6
test/asm/opt-r.err
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
error: opt-r.asm(5):
|
||||||
|
Missing argument to option 'r'
|
||||||
|
error: opt-r.asm(6):
|
||||||
|
Invalid argument to option 'r' ("2a")
|
||||||
|
FATAL: opt-r.asm(13):
|
||||||
|
Recursion limit (1) exceeded
|
||||||
0
test/asm/opt-r.out
Normal file
0
test/asm/opt-r.out
Normal file
@@ -1,10 +1,11 @@
|
|||||||
test: macro
|
macro test
|
||||||
; Test the rpn system, as well as the linker...
|
; Test the rpn system, as well as the linker...
|
||||||
dl \1 + zero
|
DEF expr EQUS STRRPL(STRRPL("\1 + zero)", "<< ", "<< ("), ">> ", ">> (")
|
||||||
|
dl expr
|
||||||
|
PURGE expr
|
||||||
|
|
||||||
; ...as well as the constexpr system
|
; ...as well as the constexpr system
|
||||||
result\@ equ \1
|
println "\1 = ", (\1)
|
||||||
println "\1 = {result\@}"
|
|
||||||
endm
|
endm
|
||||||
|
|
||||||
section "test", ROM0[0]
|
section "test", ROM0[0]
|
||||||
@@ -23,5 +24,8 @@ section "test", ROM0[0]
|
|||||||
test -4 >> 2
|
test -4 >> 2
|
||||||
test -1 >> -9001
|
test -1 >> -9001
|
||||||
|
|
||||||
|
test $DEADBEEF >> 1
|
||||||
|
test $DEADBEEF >>> 1
|
||||||
|
|
||||||
SECTION "Zero", ROM0[0]
|
SECTION "Zero", ROM0[0]
|
||||||
zero:
|
zero:
|
||||||
|
|||||||
@@ -1,52 +1,28 @@
|
|||||||
warning: shift.asm(13) -> shift.asm::test(3): [-Wshift-amount]
|
warning: shift.asm(14) -> shift.asm::test(8): [-Wshift-amount]
|
||||||
Shifting left by large amount 32
|
Shifting left by large amount 32
|
||||||
warning: shift.asm(13) -> shift.asm::test(6): [-Wshift-amount]
|
warning: shift.asm(15) -> shift.asm::test(8): [-Wshift-amount]
|
||||||
Shifting left by large amount 32
|
|
||||||
warning: shift.asm(14) -> shift.asm::test(3): [-Wshift-amount]
|
|
||||||
Shifting left by large amount 9001
|
Shifting left by large amount 9001
|
||||||
warning: shift.asm(14) -> shift.asm::test(6): [-Wshift-amount]
|
warning: shift.asm(17) -> shift.asm::test(8): [-Wshift-amount]
|
||||||
Shifting left by large amount 9001
|
|
||||||
warning: shift.asm(16) -> shift.asm::test(3): [-Wshift-amount]
|
|
||||||
Shifting left by large amount 32
|
Shifting left by large amount 32
|
||||||
warning: shift.asm(16) -> shift.asm::test(6): [-Wshift-amount]
|
warning: shift.asm(18) -> shift.asm::test(8): [-Wshift-amount]
|
||||||
Shifting left by large amount 32
|
|
||||||
warning: shift.asm(17) -> shift.asm::test(3): [-Wshift-amount]
|
|
||||||
Shifting left by negative amount -9001
|
Shifting left by negative amount -9001
|
||||||
warning: shift.asm(17) -> shift.asm::test(6): [-Wshift-amount]
|
warning: shift.asm(20) -> shift.asm::test(8): [-Wshift]
|
||||||
Shifting left by negative amount -9001
|
|
||||||
warning: shift.asm(19) -> shift.asm::test(3): [-Wshift]
|
|
||||||
Shifting right negative value -1
|
Shifting right negative value -1
|
||||||
warning: shift.asm(19) -> shift.asm::test(6): [-Wshift]
|
warning: shift.asm(21) -> shift.asm::test(8): [-Wshift]
|
||||||
Shifting right negative value -1
|
Shifting right negative value -1
|
||||||
warning: shift.asm(20) -> shift.asm::test(3): [-Wshift]
|
warning: shift.asm(21) -> shift.asm::test(8): [-Wshift-amount]
|
||||||
Shifting right negative value -1
|
|
||||||
warning: shift.asm(20) -> shift.asm::test(3): [-Wshift-amount]
|
|
||||||
Shifting right by large amount 32
|
Shifting right by large amount 32
|
||||||
warning: shift.asm(20) -> shift.asm::test(6): [-Wshift]
|
warning: shift.asm(22) -> shift.asm::test(8): [-Wshift]
|
||||||
Shifting right negative value -1
|
Shifting right negative value -1
|
||||||
warning: shift.asm(20) -> shift.asm::test(6): [-Wshift-amount]
|
warning: shift.asm(22) -> shift.asm::test(8): [-Wshift-amount]
|
||||||
Shifting right by large amount 32
|
|
||||||
warning: shift.asm(21) -> shift.asm::test(3): [-Wshift]
|
|
||||||
Shifting right negative value -1
|
|
||||||
warning: shift.asm(21) -> shift.asm::test(3): [-Wshift-amount]
|
|
||||||
Shifting right by large amount 9001
|
Shifting right by large amount 9001
|
||||||
warning: shift.asm(21) -> shift.asm::test(6): [-Wshift]
|
warning: shift.asm(23) -> shift.asm::test(8): [-Wshift]
|
||||||
|
Shifting right negative value -4
|
||||||
|
warning: shift.asm(24) -> shift.asm::test(8): [-Wshift]
|
||||||
|
Shifting right negative value -4
|
||||||
|
warning: shift.asm(25) -> shift.asm::test(8): [-Wshift]
|
||||||
Shifting right negative value -1
|
Shifting right negative value -1
|
||||||
warning: shift.asm(21) -> shift.asm::test(6): [-Wshift-amount]
|
warning: shift.asm(25) -> shift.asm::test(8): [-Wshift-amount]
|
||||||
Shifting right by large amount 9001
|
|
||||||
warning: shift.asm(22) -> shift.asm::test(3): [-Wshift]
|
|
||||||
Shifting right negative value -4
|
|
||||||
warning: shift.asm(22) -> shift.asm::test(6): [-Wshift]
|
|
||||||
Shifting right negative value -4
|
|
||||||
warning: shift.asm(23) -> shift.asm::test(3): [-Wshift]
|
|
||||||
Shifting right negative value -4
|
|
||||||
warning: shift.asm(23) -> shift.asm::test(6): [-Wshift]
|
|
||||||
Shifting right negative value -4
|
|
||||||
warning: shift.asm(24) -> shift.asm::test(3): [-Wshift]
|
|
||||||
Shifting right negative value -1
|
|
||||||
warning: shift.asm(24) -> shift.asm::test(3): [-Wshift-amount]
|
|
||||||
Shifting right by negative amount -9001
|
|
||||||
warning: shift.asm(24) -> shift.asm::test(6): [-Wshift]
|
|
||||||
Shifting right negative value -1
|
|
||||||
warning: shift.asm(24) -> shift.asm::test(6): [-Wshift-amount]
|
|
||||||
Shifting right by negative amount -9001
|
Shifting right by negative amount -9001
|
||||||
|
warning: shift.asm(27) -> shift.asm::test(8): [-Wshift]
|
||||||
|
Shifting right negative value -559038737
|
||||||
|
|||||||
@@ -10,3 +10,5 @@
|
|||||||
-4 >> 1 = $FFFFFFFE
|
-4 >> 1 = $FFFFFFFE
|
||||||
-4 >> 2 = $FFFFFFFF
|
-4 >> 2 = $FFFFFFFF
|
||||||
-1 >> -9001 = $0
|
-1 >> -9001 = $0
|
||||||
|
$DEADBEEF >> 1 = $EF56DF77
|
||||||
|
$DEADBEEF >>> 1 = $6F56DF77
|
||||||
|
|||||||
Binary file not shown.
@@ -1,11 +1,9 @@
|
|||||||
|
|
||||||
SECTION "test", ROM0
|
SECTION "test", ROM0
|
||||||
|
|
||||||
ds 123
|
|
||||||
|
|
||||||
FloatingBase:
|
FloatingBase:
|
||||||
assert WARN, FloatingBase & 0, "Worry about me, but not too much."
|
assert WARN, FloatingBase, "Worry about me, but not too much."
|
||||||
assert FAIL, FloatingBase & 0, "Okay, this is getting serious!"
|
assert FAIL, FloatingBase, "Okay, this is getting serious!"
|
||||||
assert FATAL, FloatingBase & 0, "It all ends now."
|
assert FATAL, FloatingBase, "It all ends now."
|
||||||
assert FAIL, FloatingBase & 0, "Not even time to roll credits!"
|
assert FAIL, FloatingBase, "Not even time to roll credits!"
|
||||||
assert WARN, 0, "Still can finish the film, though!"
|
assert WARN, 0, "Still can finish the film, though!"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
warning: assert.asm(7): Worry about me, but not too much.
|
warning: assert.asm(5): Worry about me, but not too much.
|
||||||
error: assert.asm(8): Okay, this is getting serious!
|
error: assert.asm(6): Okay, this is getting serious!
|
||||||
FATAL: assert.asm(9): It all ends now.
|
FATAL: assert.asm(7): It all ends now.
|
||||||
Linking aborted after 2 errors
|
Linking aborted after 2 errors
|
||||||
|
|||||||
@@ -22,23 +22,25 @@ done
|
|||||||
|
|
||||||
# Test some significant external projects that use RGBDS
|
# Test some significant external projects that use RGBDS
|
||||||
# When adding new ones, don't forget to add them to the .gitignore!
|
# When adding new ones, don't forget to add them to the .gitignore!
|
||||||
|
# When updating subprojects, change the commit being checked out, and set the `shallow-since`
|
||||||
|
# to the day before, to reduce the amount of refs being transferred and thus speed up CI.
|
||||||
|
|
||||||
if [ ! -d pokecrystal ]; then
|
if [ ! -d pokecrystal ]; then
|
||||||
git clone https://github.com/pret/pokecrystal.git --shallow-since=2021-04-01 --single-branch
|
git clone https://github.com/pret/pokecrystal.git --shallow-since=2022-03-12 --single-branch
|
||||||
fi
|
fi
|
||||||
pushd pokecrystal
|
pushd pokecrystal
|
||||||
git fetch
|
git fetch
|
||||||
git checkout b8fc67848e1d5911204fa42bbd9b954fdec6228a
|
git checkout a3e31d6463e6313aed12ebc733b3f772f2fc78d7
|
||||||
make clean
|
make clean
|
||||||
make -j4 compare RGBDS=../../
|
make -j4 compare RGBDS=../../
|
||||||
popd
|
popd
|
||||||
|
|
||||||
if [ ! -d pokered ]; then
|
if [ ! -d pokered ]; then
|
||||||
git clone --recursive https://github.com/pret/pokered.git --shallow-since=2021-04-01 --single-branch
|
git clone https://github.com/pret/pokered.git --shallow-since=2022-03-07 --single-branch
|
||||||
fi
|
fi
|
||||||
pushd pokered
|
pushd pokered
|
||||||
git fetch
|
git fetch
|
||||||
git checkout 0af787ea6d42d6f9c16f952b46519ab94f356abb
|
git checkout a75dd222709c92ae136d835ff2451391d5a88e45
|
||||||
make clean
|
make clean
|
||||||
make -j4 compare RGBDS=../../
|
make -j4 compare RGBDS=../../
|
||||||
popd
|
popd
|
||||||
|
|||||||
Reference in New Issue
Block a user