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)
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get install -yq bison libpng-dev pkg-config
|
||||
|
||||
@@ -17,9 +17,9 @@ jobs:
|
||||
- name: Get zlib, libpng and bison
|
||||
run: | # TODO: use an array
|
||||
$wc = New-Object System.Net.WebClient
|
||||
$wc.DownloadFile('https://www.zlib.net/zlib1211.zip', 'zlib.zip')
|
||||
$wc.DownloadFile('https://www.zlib.net/zlib1212.zip', 'zlib.zip')
|
||||
$hash = (Get-FileHash "zlib.zip" -Algorithm SHA256).Hash
|
||||
if ($hash -ne 'd7510a8ee1918b7d0cad197a089c0a2cd4d6df05fee22389f67f115e738b178d') {
|
||||
if ($hash -ne '173e89893dcb8b4a150d7731cd72f0602f1d6b45e60e2a54efdf7f3fc3325fd7') {
|
||||
Write-Host "zlib SHA256 mismatch! ($hash)"
|
||||
exit 1
|
||||
}
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
Expand-Archive -DestinationPath . "zlib.zip"
|
||||
Expand-Archive -DestinationPath . "libpng.zip"
|
||||
Expand-Archive -DestinationPath install_dir "winflexbison.zip"
|
||||
Move-Item zlib-1.2.11 zlib
|
||||
Move-Item zlib-1.2.12 zlib
|
||||
Move-Item lpng1637 libpng
|
||||
- name: Build 32-bit zlib
|
||||
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
|
||||
- os: macos-11.0
|
||||
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
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
@@ -43,13 +30,13 @@ jobs:
|
||||
- name: Build & install using Make
|
||||
run: |
|
||||
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=
|
||||
if: matrix.buildsys == 'make'
|
||||
- name: Build & install using CMake
|
||||
run: |
|
||||
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
|
||||
cp build/src/rgb{asm,link,fix,gfx} .
|
||||
sudo cmake --install build
|
||||
@@ -86,9 +73,9 @@ jobs:
|
||||
- name: Get zlib, libpng and bison
|
||||
run: | # TODO: use an array
|
||||
$wc = New-Object System.Net.WebClient
|
||||
$wc.DownloadFile('https://www.zlib.net/zlib1211.zip', 'zlib.zip')
|
||||
$wc.DownloadFile('https://www.zlib.net/zlib1212.zip', 'zlib.zip')
|
||||
$hash = (Get-FileHash "zlib.zip" -Algorithm SHA256).Hash
|
||||
if ($hash -ne 'd7510a8ee1918b7d0cad197a089c0a2cd4d6df05fee22389f67f115e738b178d') {
|
||||
if ($hash -ne '173e89893dcb8b4a150d7731cd72f0602f1d6b45e60e2a54efdf7f3fc3325fd7') {
|
||||
Write-Host "zlib SHA256 mismatch! ($hash)"
|
||||
exit 1
|
||||
}
|
||||
@@ -106,7 +93,7 @@ jobs:
|
||||
Expand-Archive -DestinationPath . "zlib.zip"
|
||||
Expand-Archive -DestinationPath . "libpng.zip"
|
||||
Expand-Archive -DestinationPath install_dir "winflexbison.zip"
|
||||
Move-Item zlib-1.2.11 zlib
|
||||
Move-Item zlib-1.2.12 zlib
|
||||
Move-Item lpng1637 libpng
|
||||
- name: Build zlib
|
||||
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)
|
||||
add_compile_options(${SAN_FLAGS})
|
||||
link_libraries(${SAN_FLAGS})
|
||||
# A non-zero optimization level is desired in debug mode, but allow overriding it nonetheless
|
||||
# TODO: this overrides anything previously set... that's a bit sloppy!
|
||||
set(CMAKE_C_FLAGS_DEBUG "-g -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls" CACHE STRING "" FORCE)
|
||||
endif()
|
||||
|
||||
if(MORE_WARNINGS)
|
||||
add_compile_options(-Werror -Wextra -Wno-type-limits
|
||||
-Wno-sign-compare -Wvla -Wformat -Wformat-security -Wformat-overflow=2
|
||||
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused
|
||||
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5
|
||||
-Wstringop-overflow=4 -Walloc-zero -Wduplicated-cond
|
||||
-Wfloat-equal -Wshadow -Wcast-qual -Wcast-align -Wlogical-op
|
||||
-Wnested-externs -Wno-aggressive-loop-optimizations -Winline
|
||||
-Wundef -Wstrict-prototypes -Wold-style-definition
|
||||
-Wno-unknown-warning-option -Wno-tautological-constant-out-of-range-compare)
|
||||
add_compile_options(-Werror -Wextra
|
||||
-Walloc-zero -Wcast-align -Wcast-qual -Wduplicated-branches -Wduplicated-cond
|
||||
-Wfloat-equal -Winline -Wlogical-op -Wnested-externs -Wnull-dereference
|
||||
-Wold-style-definition -Wshift-overflow=2 -Wstrict-overflow=5
|
||||
-Wstrict-prototypes -Wstringop-overflow=4 -Wundef -Wuninitialized -Wunused
|
||||
-Wshadow # TODO: -Wshadow=compatible-local ?
|
||||
-Wformat=2 -Wformat-overflow=2 -Wformat-truncation=1
|
||||
-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()
|
||||
|
||||
# Use versioning consistent with Makefile
|
||||
# the git revision is used but uses the fallback in an archive
|
||||
|
||||
execute_process(COMMAND git describe --tags --dirty --always
|
||||
OUTPUT_VARIABLE GIT_REV
|
||||
find_program(GIT git)
|
||||
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)
|
||||
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")
|
||||
|
||||
add_definitions(-DBUILD_VERSION_STRING="${GIT_REV}")
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
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
|
||||
VERSION_STRING := `git describe --tags --dirty --always 2>/dev/null`
|
||||
|
||||
WARNFLAGS := -Wall
|
||||
WARNFLAGS := -Wall -pedantic
|
||||
|
||||
# Overridable CFLAGS
|
||||
CFLAGS ?= -O3 -flto -DNDEBUG
|
||||
@@ -209,23 +209,27 @@ checkdiff:
|
||||
# This target is used during development in order to prevent adding new issues
|
||||
# to the source code. All warnings are treated as errors in order to block the
|
||||
# compilation and make the continous integration infrastructure return failure.
|
||||
# The rationale for some of the flags is documented in the CMakeLists.
|
||||
|
||||
develop:
|
||||
$Qenv $(MAKE) WARNFLAGS="-Werror -Wall -Wextra -Wpedantic -Wno-type-limits \
|
||||
-Wno-sign-compare -Wvla -Wformat -Wformat-security -Wformat-overflow=2 \
|
||||
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused \
|
||||
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=4 \
|
||||
-Wstringop-overflow=4 -Walloc-zero -Wduplicated-cond \
|
||||
-Wfloat-equal -Wshadow -Wcast-qual -Wcast-align -Wlogical-op \
|
||||
-Wnested-externs -Wno-aggressive-loop-optimizations -Winline \
|
||||
-Wundef -Wstrict-prototypes -Wold-style-definition \
|
||||
-Wno-unknown-warning-option -Wno-tautological-constant-out-of-range-compare \
|
||||
$Qenv ${MAKE} WARNFLAGS="-Werror -Wextra \
|
||||
-Walloc-zero -Wcast-align -Wcast-qual -Wduplicated-branches -Wduplicated-cond \
|
||||
-Wfloat-equal -Winline -Wlogical-op -Wnested-externs -Wold-style-definition \
|
||||
-Wshift-overflow=2 \
|
||||
-Wstrict-overflow=5 -Wstrict-prototypes -Wundef -Wuninitialized -Wunused \
|
||||
-Wshadow \
|
||||
-Wnull-dereference -Wstringop-overflow=4 \
|
||||
-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=unreachable -fsanitize=vla-bound \
|
||||
-fsanitize=signed-integer-overflow -fsanitize=bounds \
|
||||
-fsanitize=object-size -fsanitize=bool -fsanitize=enum \
|
||||
-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.
|
||||
# This is not for Windows users!
|
||||
@@ -233,11 +237,11 @@ develop:
|
||||
# install instructions instead.
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
wine-shim:
|
||||
|
||||
@@ -73,10 +73,14 @@ The RGBDS source code file structure somewhat resembles the following:
|
||||
├── test/
|
||||
│ ├── ...
|
||||
│ └── run-tests.sh
|
||||
├── .clang-format
|
||||
├── CMakeLists.txt
|
||||
├── Makefile
|
||||
└── 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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
- ``.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
|
||||
----------
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step,
|
||||
void fstk_StopRept(void);
|
||||
bool fstk_Break(void);
|
||||
|
||||
void fstk_NewRecursionDepth(size_t newDepth);
|
||||
void fstk_Init(char const *mainPath, size_t maxDepth);
|
||||
|
||||
#endif /* RGBDS_ASM_FSTACK_H */
|
||||
|
||||
@@ -83,6 +83,7 @@ struct CaptureBody {
|
||||
size_t size;
|
||||
};
|
||||
|
||||
void lexer_CheckRecursionDepth(void);
|
||||
char const *lexer_GetFileName(void);
|
||||
uint32_t lexer_GetLineNo(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_FreeArgs(struct MacroArgs *args);
|
||||
char const *macro_GetArg(uint32_t i);
|
||||
char *macro_GetAllArgs(void);
|
||||
char const *macro_GetAllArgs(void);
|
||||
|
||||
uint32_t macro_GetUniqueID(void);
|
||||
char const *macro_GetUniqueIDStr(void);
|
||||
|
||||
@@ -10,9 +10,10 @@
|
||||
#define RGBDS_OPT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void opt_B(char chars[2]);
|
||||
void opt_G(char chars[4]);
|
||||
void opt_B(char const chars[2]);
|
||||
void opt_G(char const chars[4]);
|
||||
void opt_P(uint8_t fill);
|
||||
void opt_L(bool optimize);
|
||||
void opt_W(char const *flag);
|
||||
|
||||
@@ -28,7 +28,7 @@ struct Section {
|
||||
uint32_t size;
|
||||
uint32_t org;
|
||||
uint32_t bank;
|
||||
uint8_t align;
|
||||
uint8_t align; // Exactly as specified in `ALIGN[]`
|
||||
uint16_t alignOfs;
|
||||
struct Section *next;
|
||||
struct Patch *patches;
|
||||
|
||||
@@ -24,7 +24,7 @@ struct Options {
|
||||
bool mirror;
|
||||
bool unique;
|
||||
bool colorcurve;
|
||||
int trim;
|
||||
unsigned int trim;
|
||||
char *tilemapfile;
|
||||
bool tilemapout;
|
||||
char *attrmapfile;
|
||||
@@ -43,7 +43,7 @@ struct RGBColor {
|
||||
|
||||
struct ImageOptions {
|
||||
bool horizontal;
|
||||
int trim;
|
||||
unsigned int trim;
|
||||
char *tilemapfile;
|
||||
bool tilemapout;
|
||||
char *attrmapfile;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#define RGBDS_OBJECT_VERSION_STRING "RGB%1u"
|
||||
#define RGBDS_OBJECT_VERSION_NUMBER 9U
|
||||
#define RGBDS_OBJECT_REV 8U
|
||||
#define RGBDS_OBJECT_REV 9U
|
||||
|
||||
enum AssertionType {
|
||||
ASSERT_WARN,
|
||||
@@ -49,6 +49,7 @@ enum RPNCommand {
|
||||
|
||||
RPN_SHL = 0x40,
|
||||
RPN_SHR = 0x41,
|
||||
RPN_USHR = 0x42,
|
||||
|
||||
RPN_BANK_SYM = 0x50,
|
||||
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_shift_left(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 */
|
||||
|
||||
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
|
||||
#
|
||||
|
||||
configure_file(version.c _version.c ESCAPE_QUOTES)
|
||||
|
||||
set(common_src
|
||||
"error.c"
|
||||
"extern/getopt.c"
|
||||
"version.c"
|
||||
"_version.c"
|
||||
)
|
||||
|
||||
find_package(PkgConfig)
|
||||
|
||||
@@ -284,8 +284,9 @@ bool yywrap(void)
|
||||
*/
|
||||
static void newContext(struct FileStackNode *fileInfo)
|
||||
{
|
||||
if (++contextDepth >= maxRecursionDepth)
|
||||
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
|
||||
++contextDepth;
|
||||
fstk_NewRecursionDepth(maxRecursionDepth); // Only checks if the max depth was exceeded
|
||||
|
||||
struct Context *context = malloc(sizeof(*context));
|
||||
|
||||
if (!context)
|
||||
@@ -507,6 +508,13 @@ bool fstk_Break(void)
|
||||
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)
|
||||
{
|
||||
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},
|
||||
{"DI", T_Z80_DI},
|
||||
{"EI", T_Z80_EI},
|
||||
{"HALT", T_Z80_HALT},
|
||||
{"HALT✋", T_Z80_HALT},
|
||||
{"INC", T_Z80_INC},
|
||||
{"JP", T_Z80_JP},
|
||||
{"JR", T_Z80_JR},
|
||||
@@ -118,8 +118,9 @@ static struct KeywordMapping {
|
||||
{"LDD", T_Z80_LDD},
|
||||
{"LDIO", T_Z80_LDH},
|
||||
{"LDH", T_Z80_LDH},
|
||||
{"NOP", T_Z80_NOP},
|
||||
{"NOPE", T_Z80_NOP},
|
||||
{"OR", T_Z80_OR},
|
||||
{"OWO", T_OWO},
|
||||
{"POP", T_Z80_POP},
|
||||
{"PUSH", T_Z80_PUSH},
|
||||
{"RES", T_Z80_RES},
|
||||
@@ -140,7 +141,7 @@ static struct KeywordMapping {
|
||||
{"SLA", T_Z80_SLA},
|
||||
{"SRA", T_Z80_SRA},
|
||||
{"SRL", T_Z80_SRL},
|
||||
{"STOP", T_Z80_STOP},
|
||||
{"STOP!!🛑", T_Z80_STOP},
|
||||
{"SUB", T_Z80_SUB},
|
||||
{"SWAP", T_Z80_SWAP},
|
||||
{"XOR", T_Z80_XOR},
|
||||
@@ -148,24 +149,29 @@ static struct KeywordMapping {
|
||||
{"NZ", T_CC_NZ},
|
||||
{"Z", T_CC_Z},
|
||||
{"NC", T_CC_NC},
|
||||
/* Handled after as T_TOKEN_C */
|
||||
/* { "C", T_CC_C }, */
|
||||
{"C", T_CC_C},
|
||||
|
||||
{"AF", T_MODE_AF},
|
||||
{"BC", T_MODE_BC},
|
||||
{"DE", T_MODE_DE},
|
||||
{"HL", T_MODE_HL},
|
||||
{"•̀A•́)𝓕𝓾𝓬𝓴", T_MODE_AF},
|
||||
// {"BC", T_MODE_BC},
|
||||
// {"DE", T_MODE_DE},
|
||||
{"н∠(", T_MODE_HL_START},
|
||||
{"SP", T_MODE_SP},
|
||||
{"HLD", T_MODE_HL_DEC},
|
||||
{"HLI", T_MODE_HL_INC},
|
||||
{"н∠( ᐛ 」∠)_👁", T_MODE_HL_DEC},
|
||||
{"н∠( ᐛ 」∠)_👎", T_MODE_HL_INC},
|
||||
|
||||
{"A", T_TOKEN_A},
|
||||
{"B", T_TOKEN_B},
|
||||
{"C", T_TOKEN_C},
|
||||
{"D", T_TOKEN_D},
|
||||
{"E", T_TOKEN_E},
|
||||
{"H", T_TOKEN_H},
|
||||
{"L", T_TOKEN_L},
|
||||
// HACK: normally this is surrounded by parens, but this is annoying to special-case,
|
||||
// so we use cooperation from the parser.
|
||||
{"•̀A•́", T_TOKEN_A},
|
||||
// {"=B", T_TOKEN_B}, HACK: This begins with a non-identifier character, so we'll cheat
|
||||
{"♥(˘⌣˘", T_TOKEN_C}, // HACK: same for "C" after the space & closing paren
|
||||
// {";D", T_TOKEN_D}, HACK: also needs to be special-cased. God I feel dirty.
|
||||
{"(´ε`", 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},
|
||||
|
||||
@@ -400,16 +406,16 @@ uint32_t lexer_GetIFDepth(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));
|
||||
|
||||
new->ranIfBlock = false;
|
||||
new->reachedElseBlock = false;
|
||||
new->next = lexerState->ifStack;
|
||||
ifStack->ranIfBlock = false;
|
||||
ifStack->reachedElseBlock = false;
|
||||
ifStack->next = lexerState->ifStack;
|
||||
|
||||
lexerState->ifStack = new;
|
||||
lexerState->ifStack = ifStack;
|
||||
}
|
||||
|
||||
void lexer_DecIFDepth(void)
|
||||
@@ -578,16 +584,16 @@ struct KeywordDictNode {
|
||||
* In turn, this allows greatly simplifying checking an index into this array,
|
||||
* 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;
|
||||
/* 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 */
|
||||
static uint8_t dictIndex(char c)
|
||||
{
|
||||
/* Translate uppercase to lowercase (roughly) */
|
||||
if (c > 0x60)
|
||||
if (c > 0x60 && c < 0x80)
|
||||
c = c - ('a' - 'A');
|
||||
return c - ' ';
|
||||
}
|
||||
@@ -609,8 +615,9 @@ void lexer_Init(void)
|
||||
|
||||
/* Walk the dictionary, creating intermediate nodes for the keyword */
|
||||
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 */
|
||||
if (keywordDict[nodeID].children[*ptr - ' '] == 0) {
|
||||
if (keywordDict[nodeID].children[index] == 0) {
|
||||
/*
|
||||
* If this gets tripped up, set the size of keywordDict to
|
||||
* something high, compile with `-DPRINT_NODE_COUNT` (see below),
|
||||
@@ -619,10 +626,10 @@ void lexer_Init(void)
|
||||
assert(usedNodes < sizeof(keywordDict) / sizeof(*keywordDict));
|
||||
|
||||
/* There is no node at that location, grab one from the pool */
|
||||
keywordDict[nodeID].children[*ptr - ' '] = usedNodes;
|
||||
keywordDict[nodeID].children[index] = usedNodes;
|
||||
usedNodes++;
|
||||
}
|
||||
nodeID = keywordDict[nodeID].children[*ptr - ' '];
|
||||
nodeID = keywordDict[nodeID].children[index];
|
||||
}
|
||||
|
||||
/* This assumes that no two keywords have the same name */
|
||||
@@ -668,7 +675,26 @@ static void beginExpansion(char const *str, bool owned, char const *name)
|
||||
if (!size)
|
||||
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;
|
||||
|
||||
for (struct Expansion *exp = lexerState->expansions; exp; exp = exp->parent) {
|
||||
@@ -677,21 +703,6 @@ static void beginExpansion(char const *str, bool owned, char const *name)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
free(expansion->name);
|
||||
@@ -808,6 +819,19 @@ static char const *readMacroArg(char name)
|
||||
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 */
|
||||
static int peekInternal(uint8_t distance)
|
||||
{
|
||||
@@ -839,42 +863,31 @@ static int peekInternal(uint8_t distance)
|
||||
|
||||
/* Compute the index we'll start writing to */
|
||||
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 (writeIndex + target > LEXER_BUF_SIZE) {
|
||||
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
|
||||
// `nbCharsRead` cannot be negative, so it's fine to cast to `size_t`
|
||||
if ((size_t)nbCharsRead < nbExpectedChars)
|
||||
target -= nbReadChars;
|
||||
if (nbReadChars < nbExpectedChars)
|
||||
target = 0;
|
||||
}
|
||||
if (target != 0)
|
||||
readChars(target);
|
||||
|
||||
#undef readChars
|
||||
|
||||
lexerState->nbChars += totalCharsRead;
|
||||
lexerState->nbChars += readInternal(writeIndex, target);
|
||||
|
||||
/* If there aren't enough chars even after refilling, give up */
|
||||
if (lexerState->nbChars <= distance)
|
||||
return EOF;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
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)
|
||||
@@ -1768,6 +1785,10 @@ static int yylex_NORMAL(void)
|
||||
/* Ignore whitespace and comments */
|
||||
|
||||
case ';':
|
||||
if (peek() == 'D') {
|
||||
shiftChar();
|
||||
return T_TOKEN_D;
|
||||
}
|
||||
discardComment();
|
||||
/* fallthrough */
|
||||
case ' ':
|
||||
@@ -1788,8 +1809,6 @@ static int yylex_NORMAL(void)
|
||||
return T_LBRACK;
|
||||
case ']':
|
||||
return T_RBRACK;
|
||||
case '(':
|
||||
return T_LPAREN;
|
||||
case ')':
|
||||
return T_RPAREN;
|
||||
case ',':
|
||||
@@ -1857,9 +1876,14 @@ static int yylex_NORMAL(void)
|
||||
return T_OP_XOR;
|
||||
|
||||
case '=': /* Either assignment or EQ */
|
||||
if (peek() == '=') {
|
||||
switch (peek()) {
|
||||
case '=':
|
||||
shiftChar();
|
||||
return T_OP_LOGICEQU;
|
||||
case 'b':
|
||||
case 'B':
|
||||
shiftChar();
|
||||
return T_TOKEN_B;
|
||||
}
|
||||
return T_POP_EQUAL;
|
||||
|
||||
@@ -1888,18 +1912,23 @@ static int yylex_NORMAL(void)
|
||||
return T_OP_LOGICLT;
|
||||
}
|
||||
|
||||
case '>': /* Either >>=, GT, GTE, or right shift */
|
||||
case '>': /* Either >>=, GT, GTE, or either kind of right shift */
|
||||
switch (peek()) {
|
||||
case '=':
|
||||
shiftChar();
|
||||
return T_OP_LOGICGE;
|
||||
case '>':
|
||||
shiftChar();
|
||||
if (peek() == '=') {
|
||||
switch (peek()) {
|
||||
case '=':
|
||||
shiftChar();
|
||||
return T_POP_SHREQ;
|
||||
}
|
||||
case '>':
|
||||
shiftChar();
|
||||
return T_OP_USHR;
|
||||
default:
|
||||
return T_OP_SHR;
|
||||
}
|
||||
default:
|
||||
return T_OP_LOGICGT;
|
||||
}
|
||||
@@ -1993,6 +2022,12 @@ static int yylex_NORMAL(void)
|
||||
|
||||
/* Handle identifiers... or report garbage characters */
|
||||
|
||||
case '(':
|
||||
if (peek() != (unsigned char)"´"[0]) {
|
||||
return T_LPAREN;
|
||||
}
|
||||
// fallthrough
|
||||
|
||||
default:
|
||||
if (startsIdentifier(c)) {
|
||||
int tokenType = readIdentifier(c);
|
||||
|
||||
@@ -101,7 +101,7 @@ char const *macro_GetArg(uint32_t i)
|
||||
: macroArgs->args[realIndex];
|
||||
}
|
||||
|
||||
char *macro_GetAllArgs(void)
|
||||
char const *macro_GetAllArgs(void)
|
||||
{
|
||||
if (!macroArgs)
|
||||
return NULL;
|
||||
|
||||
@@ -142,6 +142,9 @@ static void print_usage(void)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#if YYDEBUG
|
||||
yydebug = 1;
|
||||
#endif
|
||||
int ch;
|
||||
char *ep;
|
||||
|
||||
@@ -241,7 +244,7 @@ int main(int argc, char *argv[])
|
||||
if (musl_optarg[0] == '\0' || *ep != '\0')
|
||||
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");
|
||||
|
||||
opt_P(fill);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
@@ -5,6 +6,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "asm/fstack.h"
|
||||
#include "asm/lexer.h"
|
||||
#include "asm/main.h"
|
||||
#include "asm/section.h"
|
||||
@@ -17,6 +19,7 @@ struct OptStackEntry {
|
||||
bool haltnop;
|
||||
bool optimizeLoads;
|
||||
bool warningsAreErrors;
|
||||
size_t maxRecursionDepth;
|
||||
// Don't be confused: we use the size of the **global variable** `warningStates`!
|
||||
enum WarningState warningStates[sizeof(warningStates)];
|
||||
struct OptStackEntry *next;
|
||||
@@ -24,12 +27,12 @@ struct OptStackEntry {
|
||||
|
||||
static struct OptStackEntry *stack = NULL;
|
||||
|
||||
void opt_B(char chars[2])
|
||||
void opt_B(char const chars[2])
|
||||
{
|
||||
lexer_SetBinDigits(chars);
|
||||
}
|
||||
|
||||
void opt_G(char chars[4])
|
||||
void opt_G(char const chars[4])
|
||||
{
|
||||
lexer_SetGfxDigits(chars);
|
||||
}
|
||||
@@ -39,6 +42,12 @@ void opt_P(uint8_t fill)
|
||||
fillByte = fill;
|
||||
}
|
||||
|
||||
void opt_R(size_t newDepth)
|
||||
{
|
||||
fstk_NewRecursionDepth(newDepth);
|
||||
lexer_CheckRecursionDepth();
|
||||
}
|
||||
|
||||
void opt_h(bool halt)
|
||||
{
|
||||
haltnop = halt;
|
||||
@@ -86,6 +95,29 @@ void opt_Parse(char *s)
|
||||
}
|
||||
break;
|
||||
|
||||
case 'r': {
|
||||
++s; // Skip 'r'
|
||||
while (isblank(*s))
|
||||
++s; // Skip leading whitespace
|
||||
|
||||
if (s[0] == '\0') {
|
||||
error("Missing argument to option 'r'\n");
|
||||
break;
|
||||
}
|
||||
|
||||
char *endptr;
|
||||
unsigned long newDepth = strtoul(s, &endptr, 10);
|
||||
|
||||
if (*endptr != '\0') {
|
||||
error("Invalid argument to option 'r' (\"%s\")\n", s);
|
||||
} else if (errno == ERANGE) {
|
||||
error("Argument to 'r' is out of range (\"%s\")\n", s);
|
||||
} else {
|
||||
opt_R(newDepth);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'h':
|
||||
if (s[1] == '\0')
|
||||
opt_h(false);
|
||||
|
||||
@@ -192,7 +192,7 @@ static uint32_t getsectid(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)
|
||||
{
|
||||
assert(patch->src->ID != -1);
|
||||
assert(patch->src->ID != (uint32_t)-1);
|
||||
putlong(patch->src->ID, f);
|
||||
putlong(patch->lineNo, f);
|
||||
putlong(patch->offset, f);
|
||||
@@ -264,7 +264,7 @@ static void writesymbol(struct Symbol const *sym, FILE *f)
|
||||
if (!sym_IsDefined(sym)) {
|
||||
putc(SYMTYPE_IMPORT, f);
|
||||
} else {
|
||||
assert(sym->src->ID != -1);
|
||||
assert(sym->src->ID != (uint32_t)-1);
|
||||
|
||||
putc(sym->isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, 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)
|
||||
{
|
||||
putlong(node->parent ? node->parent->ID : -1, f);
|
||||
putlong(node->parent ? node->parent->ID : (uint32_t)-1, f);
|
||||
putlong(node->lineNo, f);
|
||||
putc(node->type, f);
|
||||
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;
|
||||
}
|
||||
|
||||
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 newLen = strlen(new);
|
||||
size_t repLen = strlen(rep);
|
||||
size_t i = 0;
|
||||
|
||||
if (!oldLen) {
|
||||
@@ -240,8 +240,8 @@ static void strrpl(char *dest, size_t destLen, char const *src, char const *old,
|
||||
break;
|
||||
|
||||
// Copy the replacement substring
|
||||
memcpy(dest + i, new, newLen < destLen - i ? newLen : destLen - i);
|
||||
i += newLen;
|
||||
memcpy(dest + i, rep, repLen < destLen - i ? repLen : destLen - i);
|
||||
i += repLen;
|
||||
if (i >= destLen)
|
||||
break;
|
||||
|
||||
@@ -385,7 +385,7 @@ static void initDsArgList(struct DsArgList *args)
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
static size_t nextDsArgListIndex(struct DsArgList *args)
|
||||
static void appendDsArgList(struct DsArgList *args, const struct Expression *expr)
|
||||
{
|
||||
if (args->nbArgs == args->capacity) {
|
||||
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",
|
||||
strerror(errno));
|
||||
}
|
||||
return args->nbArgs++;
|
||||
args->args[args->nbArgs++] = *expr;
|
||||
}
|
||||
|
||||
static void freeDsArgList(struct DsArgList *args)
|
||||
@@ -534,7 +534,7 @@ enum {
|
||||
%token T_OP_LOGICNE "!=" T_OP_LOGICEQU "=="
|
||||
%token T_OP_ADD "+" T_OP_SUB "-"
|
||||
%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_NOT "~"
|
||||
%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_ADD T_OP_SUB
|
||||
%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
|
||||
|
||||
%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_DAA "daa" T_Z80_DEC "dec" T_Z80_DI "di"
|
||||
%token T_Z80_EI "ei"
|
||||
%token T_Z80_HALT "halt"
|
||||
%token T_Z80_HALT "halt✋"
|
||||
%token T_Z80_INC "inc"
|
||||
%token T_Z80_JP "jp" T_Z80_JR "jr"
|
||||
%token T_Z80_LD "ld"
|
||||
%token T_Z80_LDI "ldi"
|
||||
%token T_Z80_LDD "ldd"
|
||||
%token T_Z80_LDH "ldh"
|
||||
%token T_Z80_NOP "nop"
|
||||
%token T_Z80_NOP "nope"
|
||||
%token T_Z80_OR "or"
|
||||
%token T_OWO "owo"
|
||||
%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_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_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_SWAP "swap"
|
||||
%token T_Z80_XOR "xor"
|
||||
|
||||
%token T_TOKEN_A "a"
|
||||
%token T_TOKEN_B "b" T_TOKEN_C "c"
|
||||
%token T_TOKEN_D "d" T_TOKEN_E "e"
|
||||
%token T_TOKEN_H "h" T_TOKEN_L "l"
|
||||
%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_CC_NZ "nz" T_CC_Z "z" T_CC_NC "nc" // There is no T_CC_C, only T_TOKEN_C
|
||||
%token T_TOKEN_A "( •̀A•́)" T_TOKEN_F "𝓕𝓾𝓬𝓴"
|
||||
%token T_TOKEN_B "=B" T_TOKEN_C "♥(˘⌣˘ C)"
|
||||
%token T_TOKEN_D ";D" T_TOKEN_E "(´ε` )♡" T_TOKEN_E_HEART "(´ε` )♡"
|
||||
%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_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" T_CC_C "c"
|
||||
|
||||
%type <constValue> reg_r
|
||||
%type <constValue> reg_ss
|
||||
@@ -1144,14 +1145,10 @@ ds : T_POP_DS uconst { sect_Skip($2, true); }
|
||||
|
||||
ds_args : reloc_8bit {
|
||||
initDsArgList(&$$);
|
||||
size_t i = nextDsArgListIndex(&$$);
|
||||
|
||||
$$.args[i] = $1;
|
||||
appendDsArgList(&$$, &$1);
|
||||
}
|
||||
| ds_args T_COMMA reloc_8bit {
|
||||
size_t i = nextDsArgListIndex(&$1);
|
||||
|
||||
$1.args[i] = $3;
|
||||
appendDsArgList(&$1, &$3);
|
||||
$$ = $1;
|
||||
}
|
||||
;
|
||||
@@ -1481,6 +1478,9 @@ relocexpr_no_str : scoped_anon_id { rpn_Symbol(&$$, $1); }
|
||||
| relocexpr T_OP_SHR relocexpr {
|
||||
rpn_BinaryOp(RPN_SHR, &$$, &$1, &$3);
|
||||
}
|
||||
| relocexpr T_OP_USHR relocexpr {
|
||||
rpn_BinaryOp(RPN_USHR, &$$, &$1, &$3);
|
||||
}
|
||||
| relocexpr T_OP_MUL relocexpr {
|
||||
rpn_BinaryOp(RPN_MUL, &$$, &$1, &$3);
|
||||
}
|
||||
@@ -1770,6 +1770,7 @@ cpu_command : z80_adc
|
||||
| z80_sub
|
||||
| z80_swap
|
||||
| z80_xor
|
||||
| T_OWO { fatalerror("*BONK* go to horny jail\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_TOKEN_A
|
||||
T_MODE_A : T_LPAREN T_TOKEN_A 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_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
|
||||
;
|
||||
|
||||
@@ -2194,7 +2195,7 @@ T_MODE_D : T_TOKEN_D
|
||||
| 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
|
||||
;
|
||||
|
||||
@@ -2202,10 +2203,19 @@ T_MODE_H : T_TOKEN_H
|
||||
| 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_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;
|
||||
@@ -2215,7 +2225,7 @@ ccode_expr : ccode
|
||||
ccode : T_CC_NZ { $$ = CC_NZ; }
|
||||
| T_CC_Z { $$ = CC_Z; }
|
||||
| T_CC_NC { $$ = CC_NC; }
|
||||
| T_TOKEN_C { $$ = CC_C; }
|
||||
| T_CC_C { $$ = CC_C; }
|
||||
;
|
||||
|
||||
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; }
|
||||
| T_MODE_DE { $$ = REG_DE; }
|
||||
| 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; }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'\" e
|
||||
.\"
|
||||
.\" 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 Unary complement/plus/minus
|
||||
.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 Add/subtract
|
||||
.It Li != == <= >= < > Ta Comparison
|
||||
@@ -1384,6 +1387,12 @@ Alternatively, you can use
|
||||
to store a list of words (16-bit) or
|
||||
.Ic DL
|
||||
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
|
||||
Strings are handled a little specially: they first undergo charmap conversion (see
|
||||
.Sx Character maps ) ,
|
||||
@@ -1985,6 +1994,7 @@ block, all of them but the first one are ignored.
|
||||
.Ss Changing options while assembling
|
||||
.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.
|
||||
.Pq See Xr rgbasm 1 .
|
||||
.Pp
|
||||
.Ic OPT
|
||||
takes a comma-separated list of options as its argument:
|
||||
@@ -2000,8 +2010,10 @@ POPO
|
||||
LD [$FF88], A ; optimized to use LDH by default
|
||||
.Ed
|
||||
.Pp
|
||||
The options that OPT can modify are currently:
|
||||
.Cm b , g , p , h , L ,
|
||||
The options that
|
||||
.Ic OPT
|
||||
can modify are currently:
|
||||
.Cm b , g , p , r , h , L ,
|
||||
and
|
||||
.Cm W .
|
||||
The Boolean flag options
|
||||
|
||||
@@ -317,7 +317,7 @@ struct Symbol const *rpn_SymbolOf(struct Expression const *expr)
|
||||
{
|
||||
if (!rpn_isSymbol(expr))
|
||||
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)
|
||||
@@ -339,10 +339,51 @@ static bool isDiffConstant(struct Expression const *src1,
|
||||
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,
|
||||
const struct Expression *src1, const struct Expression *src2)
|
||||
{
|
||||
expr->isSymbol = false;
|
||||
int32_t constMaskVal;
|
||||
|
||||
/* First, check if the expression is known */
|
||||
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);
|
||||
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:
|
||||
expr->val = uleft * uright;
|
||||
break;
|
||||
@@ -477,6 +531,9 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
||||
|
||||
expr->val = sym_GetValue(symbol1) - sym_GetValue(symbol2);
|
||||
expr->isKnown = true;
|
||||
} else if (op == RPN_AND && (constMaskVal = tryConstMask(src1, src2)) != -1) {
|
||||
expr->val = constMaskVal;
|
||||
expr->isKnown = true;
|
||||
} else {
|
||||
/* If it's not known, start computing the RPN expression */
|
||||
|
||||
|
||||
@@ -760,7 +760,6 @@ void sym_Init(time_t now)
|
||||
now = 0;
|
||||
}
|
||||
|
||||
const struct tm *time_utc = gmtime(&now);
|
||||
const struct tm *time_local = localtime(&now);
|
||||
|
||||
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\"",
|
||||
time_local);
|
||||
|
||||
const struct tm *time_utc = gmtime(&now);
|
||||
|
||||
strftime(savedTIMESTAMP_ISO8601_UTC,
|
||||
sizeof(savedTIMESTAMP_ISO8601_UTC), "\"%Y-%m-%dT%H:%M:%SZ\"",
|
||||
time_utc);
|
||||
|
||||
@@ -1268,12 +1268,12 @@ do { \
|
||||
#define SPEC_H TRASH_HEADER_SUM
|
||||
#define SPEC_g FIX_GLOBAL_SUM
|
||||
#define SPEC_G TRASH_GLOBAL_SUM
|
||||
#define overrideSpec(new, bad) \
|
||||
#define overrideSpec(cur, bad) \
|
||||
do { \
|
||||
if (fixSpec & SPEC_##bad) \
|
||||
fprintf(stderr, \
|
||||
"warning: '" #new "' overriding '" #bad "' in fix spec\n"); \
|
||||
fixSpec = (fixSpec & ~SPEC_##bad) | SPEC_##new; \
|
||||
"warning: '" #cur "' overriding '" #bad "' in fix spec\n"); \
|
||||
fixSpec = (fixSpec & ~SPEC_##bad) | SPEC_##cur; \
|
||||
} while (0)
|
||||
case '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)
|
||||
{
|
||||
int x, y, byte;
|
||||
uint8_t index;
|
||||
|
||||
for (y = 0; y < raw_image->height; y++) {
|
||||
for (x = 0; x < raw_image->width; x++) {
|
||||
for (unsigned int y = 0; y < raw_image->height; y++) {
|
||||
for (unsigned int x = 0; x < raw_image->width; x++) {
|
||||
index = raw_image->data[y][x];
|
||||
index &= (1 << depth) - 1;
|
||||
|
||||
byte = y * depth
|
||||
unsigned int byte = y * depth
|
||||
+ x / 8 * raw_image->height / 8 * 8 * depth;
|
||||
gb->data[byte] |= (index & 1) << (7 - x % 8);
|
||||
if (depth == 2) {
|
||||
|
||||
@@ -95,6 +95,9 @@ void output_png_file(const struct Options *opts,
|
||||
if (!f)
|
||||
err("Opening output png file '%s' failed", outfile);
|
||||
|
||||
if (opts->debug)
|
||||
free(outfile);
|
||||
|
||||
img.png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
||||
NULL, NULL, NULL);
|
||||
if (!img.png)
|
||||
@@ -135,17 +138,13 @@ void output_png_file(const struct Options *opts,
|
||||
|
||||
png_destroy_write_struct(&img.png, &img.info);
|
||||
fclose(f);
|
||||
|
||||
if (opts->debug)
|
||||
free(outfile);
|
||||
}
|
||||
|
||||
void destroy_raw_image(struct RawIndexedImage **raw_image_ptr_ptr)
|
||||
{
|
||||
int y;
|
||||
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);
|
||||
|
||||
@@ -281,13 +281,12 @@ static void parseScrambleSpec(char const *spec)
|
||||
// Now, determine which region type this is
|
||||
enum ScrambledRegion region = 0;
|
||||
|
||||
while (region < SCRAMBLE_UNK) {
|
||||
for (; region < SCRAMBLE_UNK; region++) {
|
||||
// If the strings match (case-insensitively), we got it!
|
||||
// It's OK not to use `strncasecmp` because `regionName` is still
|
||||
// NUL-terminated, since the encompassing spec is.
|
||||
if (!strcasecmp(scrambleSpecs[region].name, regionName))
|
||||
// `strncasecmp` must be used here since `regionName` points
|
||||
// to the entire remaining argument.
|
||||
if (!strncasecmp(scrambleSpecs[region].name, regionName, regionNameLen))
|
||||
break;
|
||||
region++;
|
||||
}
|
||||
|
||||
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,
|
||||
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];
|
||||
|
||||
/* If the symbol is defined elsewhere... */
|
||||
@@ -265,6 +265,10 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
||||
value = popRPN();
|
||||
value = op_shift_right(popRPN(), value);
|
||||
break;
|
||||
case RPN_USHR:
|
||||
value = popRPN();
|
||||
value = op_shift_right_unsigned(popRPN(), value);
|
||||
break;
|
||||
|
||||
case RPN_BANK_SYM:
|
||||
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
|
||||
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 $40 Ta Li << operator
|
||||
.It Li $41 Ta Li >> operator
|
||||
.It Li $42 Ta Li >>> operator
|
||||
.It Li $50 Ta Li BANK(symbol) ,
|
||||
a
|
||||
.Ar LONG
|
||||
|
||||
@@ -12,6 +12,13 @@
|
||||
#include "helpers.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)
|
||||
{
|
||||
// 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...
|
||||
dl \1 + zero
|
||||
DEF expr EQUS STRRPL(STRRPL("\1 + zero)", "<< ", "<< ("), ">> ", ">> (")
|
||||
dl expr
|
||||
PURGE expr
|
||||
|
||||
; ...as well as the constexpr system
|
||||
result\@ equ \1
|
||||
println "\1 = {result\@}"
|
||||
println "\1 = ", (\1)
|
||||
endm
|
||||
|
||||
section "test", ROM0[0]
|
||||
@@ -23,5 +24,8 @@ section "test", ROM0[0]
|
||||
test -4 >> 2
|
||||
test -1 >> -9001
|
||||
|
||||
test $DEADBEEF >> 1
|
||||
test $DEADBEEF >>> 1
|
||||
|
||||
SECTION "Zero", ROM0[0]
|
||||
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
|
||||
warning: shift.asm(13) -> shift.asm::test(6): [-Wshift-amount]
|
||||
Shifting left by large amount 32
|
||||
warning: shift.asm(14) -> shift.asm::test(3): [-Wshift-amount]
|
||||
warning: shift.asm(15) -> shift.asm::test(8): [-Wshift-amount]
|
||||
Shifting left by large amount 9001
|
||||
warning: shift.asm(14) -> shift.asm::test(6): [-Wshift-amount]
|
||||
Shifting left by large amount 9001
|
||||
warning: shift.asm(16) -> shift.asm::test(3): [-Wshift-amount]
|
||||
warning: shift.asm(17) -> shift.asm::test(8): [-Wshift-amount]
|
||||
Shifting left by large amount 32
|
||||
warning: shift.asm(16) -> shift.asm::test(6): [-Wshift-amount]
|
||||
Shifting left by large amount 32
|
||||
warning: shift.asm(17) -> shift.asm::test(3): [-Wshift-amount]
|
||||
warning: shift.asm(18) -> shift.asm::test(8): [-Wshift-amount]
|
||||
Shifting left by negative amount -9001
|
||||
warning: shift.asm(17) -> shift.asm::test(6): [-Wshift-amount]
|
||||
Shifting left by negative amount -9001
|
||||
warning: shift.asm(19) -> shift.asm::test(3): [-Wshift]
|
||||
warning: shift.asm(20) -> shift.asm::test(8): [-Wshift]
|
||||
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
|
||||
warning: shift.asm(20) -> shift.asm::test(3): [-Wshift]
|
||||
Shifting right negative value -1
|
||||
warning: shift.asm(20) -> shift.asm::test(3): [-Wshift-amount]
|
||||
warning: shift.asm(21) -> shift.asm::test(8): [-Wshift-amount]
|
||||
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
|
||||
warning: shift.asm(20) -> shift.asm::test(6): [-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]
|
||||
warning: shift.asm(22) -> shift.asm::test(8): [-Wshift-amount]
|
||||
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
|
||||
warning: shift.asm(21) -> shift.asm::test(6): [-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]
|
||||
warning: shift.asm(25) -> shift.asm::test(8): [-Wshift-amount]
|
||||
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 >> 2 = $FFFFFFFF
|
||||
-1 >> -9001 = $0
|
||||
$DEADBEEF >> 1 = $EF56DF77
|
||||
$DEADBEEF >>> 1 = $6F56DF77
|
||||
|
||||
Binary file not shown.
@@ -1,11 +1,9 @@
|
||||
|
||||
SECTION "test", ROM0
|
||||
|
||||
ds 123
|
||||
|
||||
FloatingBase:
|
||||
assert WARN, FloatingBase & 0, "Worry about me, but not too much."
|
||||
assert FAIL, FloatingBase & 0, "Okay, this is getting serious!"
|
||||
assert FATAL, FloatingBase & 0, "It all ends now."
|
||||
assert FAIL, FloatingBase & 0, "Not even time to roll credits!"
|
||||
assert WARN, FloatingBase, "Worry about me, but not too much."
|
||||
assert FAIL, FloatingBase, "Okay, this is getting serious!"
|
||||
assert FATAL, FloatingBase, "It all ends now."
|
||||
assert FAIL, FloatingBase, "Not even time to roll credits!"
|
||||
assert WARN, 0, "Still can finish the film, though!"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
warning: assert.asm(7): Worry about me, but not too much.
|
||||
error: assert.asm(8): Okay, this is getting serious!
|
||||
FATAL: assert.asm(9): It all ends now.
|
||||
warning: assert.asm(5): Worry about me, but not too much.
|
||||
error: assert.asm(6): Okay, this is getting serious!
|
||||
FATAL: assert.asm(7): It all ends now.
|
||||
Linking aborted after 2 errors
|
||||
|
||||
@@ -22,23 +22,25 @@ done
|
||||
|
||||
# Test some significant external projects that use RGBDS
|
||||
# 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
|
||||
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
|
||||
pushd pokecrystal
|
||||
git fetch
|
||||
git checkout b8fc67848e1d5911204fa42bbd9b954fdec6228a
|
||||
git checkout a3e31d6463e6313aed12ebc733b3f772f2fc78d7
|
||||
make clean
|
||||
make -j4 compare RGBDS=../../
|
||||
popd
|
||||
|
||||
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
|
||||
pushd pokered
|
||||
git fetch
|
||||
git checkout 0af787ea6d42d6f9c16f952b46519ab94f356abb
|
||||
git checkout a75dd222709c92ae136d835ff2451391d5a88e45
|
||||
make clean
|
||||
make -j4 compare RGBDS=../../
|
||||
popd
|
||||
|
||||
Reference in New Issue
Block a user