Compare commits

...

32 Commits

Author SHA1 Message Date
ISSOtm
bffe7eb4de Implement new instructions
"Who are you? What are you doing in my commit history?"
~ https://xkcd.com/163/
2022-04-01 12:46:52 +02:00
ISSOtm
cd454d2e9a Hack in new register syntax
Oh my god I want to die x_x
2022-04-01 12:46:52 +02:00
ISSOtm
c814a616d6 Port Gan's work to Mandoc format
Hopefully I didn't forget anything
2022-04-01 12:46:52 +02:00
ISSOtm
e6ae1992fe Update zlib downloaded for generating releases 2022-04-01 12:46:44 +02:00
ISSOtm
d9b46cdec9 Update CI builds to zlib 1.2.12
"Due to the bug fixes, any installations of 1.2.11 should be replaced with 1.2.12."
2022-03-28 09:10:04 +02:00
ISSOtm
570cd62b81 Update subproject commits and shallow dates
Added a comment to explain what to update those to, too
2022-03-13 14:43:27 +01:00
ISSOtm
a4ead0c25f Freshen version number when building with CMake
Fixes #979
2022-03-11 09:21:08 +01:00
ISSOtm
da66eeb40e Clean up lint warnings in deps install script
Also use a simpler way to remove the version number, lol
2022-03-10 00:06:32 +01:00
ISSOtm
0c4f1f8334 Add a shebang to the deps install script
Duh.
2022-03-10 00:03:54 +01:00
ISSOtm
af70d555fc Remove signed check on unsigned variable 2022-03-09 08:40:49 +01:00
Eldred Habert
e07bd92314 Have the eqn preprocessor run on rgbasm(5)
This is required for the built-in functions (L315-337), otherwise the math syntax doesn't get processed.

This magic line is detected by most `man` impls:
https://www.gnu.org/software/groff/manual/html_node/Preprocessors-in-man-pages.html
2022-03-08 22:11:49 +01:00
ISSOtm
9169028e57 Add .clang-format
Should not be followed for C files, to avoid mucking `blame`s,
but will be applied during the gradual C++ rollout, since that
will likely touch most lines in the codebase anyway.
2022-02-28 01:52:54 +01:00
ISSOtm
7dd8ba37f1 Allow changing recursion depth limit at runtime 2022-02-05 20:32:56 +01:00
Eldred Habert
6842c831fd Allow binary AND to be sometimes constant (#976) 2022-02-05 14:17:57 -05:00
Eldred Habert
6b903059fe Document endianness of dw and dl (#972) 2022-02-05 14:12:35 -05:00
ISSOtm
eb5af70d79 Add unsigned right shift operator 2022-02-05 20:12:15 +01:00
ISSOtm
cf19879281 Enable make develop on all Unix platforms
We are actually already doing it, so simplify the code
2022-02-05 20:11:25 +01:00
ISSOtm
ac59ecf3c0 Enable -Wsign-compare and fix the warnings 2022-02-05 20:11:25 +01:00
ISSOtm
72b677a8d7 Enable "debug" optimizations in make develop
Enhances some warnings as well as the sanitizers (Clang especially complained about it)
The `-f*` flags are to get better stack traces out of the sanitizers, as recommended
by Clang's docs: https://clang.llvm.org/docs/AddressSanitizer.html#usage

GCC's docs claim that these optimizations should not hinder the debugging
experience, and Clang's don't mention optimization flags at all.
2022-02-05 20:11:25 +01:00
ISSOtm
bbae9966e9 Fix string constness issues in RGBASM 2022-02-05 20:11:25 +01:00
ISSOtm
b3304ae1ac Add more warnings to develop
As suggested by #969
2022-02-05 20:11:25 +01:00
ISSOtm
a48801a675 Trim down develop warning list and add comments
Remove already-enabled warnings as pointed out by #969
Comments added to the CMakeLists because the Makefile format does not really allow them
"Plain" list also sorted alphabetically, the rest (somewhat) thematically
2022-02-05 20:11:25 +01:00
ISSOtm
7dc81a64d3 Make recursive Make invocations properly recurse flags 2022-02-05 20:11:25 +01:00
ISSOtm
3afa6b5a5a Use pedantic warnings in Makefile default config as well
We aim to be standard-conformant, and the CMakeLists already does it
2022-02-05 20:11:25 +01:00
ISSOtm
9b49f788e4 Add missing header in opt.h 2022-02-05 13:37:40 +01:00
ISSOtm
acc31feaa1 Fix "shift" test to actually test RGBLINK 2022-02-05 11:39:10 +01:00
Rangi
4ed5ba7508 Finish using time_utc before initializing time_local
gmtime and localtime may share the same static internal struct tm,
so they cannot both be initialized and then used.
2022-01-27 22:32:34 -05:00
Rangi
20a26599a3 Simplify allocating multiple ds args
Dealing with indexes is only necessary for `strfmt`
2021-12-01 20:24:31 -05:00
Rangi
7bdfc9da23 Really don't use new as a variable name 2021-11-30 17:06:36 -05:00
Rangi
d073cffa74 Don't use new as a variable name
It conflicts with the C++ keyword
2021-11-24 22:48:28 -05:00
Rangi
8435a29c4e Turn the readChars macro into a readInternal function
This macro was only used twice, and the second usage did
some unnecessary work.
2021-11-25 00:26:23 +01:00
Eievui
b7fe78cad8 Fix improperly terminated region name check (#953) 2021-11-24 16:00:54 -05:00
49 changed files with 962 additions and 595 deletions

99
.clang-format Normal file
View 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

View File

@@ -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

View File

@@ -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`

View File

@@ -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`

View File

@@ -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
ERROR_QUIET)
string(STRIP "${GIT_REV}" 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)
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)

View File

@@ -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:

View File

@@ -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
----------

View File

@@ -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 */

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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,

View File

@@ -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
View File

@@ -0,0 +1,2 @@
# Generated by CMake
/.version.c

View File

@@ -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)

View File

@@ -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);

View File

@@ -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,28 +675,32 @@ static void beginExpansion(char const *str, bool owned, char const *name)
if (!size)
return;
if (name) {
size_t depth = 0;
if (name)
lexer_CheckRecursionDepth();
for (struct Expansion *exp = lexerState->expansions; exp; exp = exp->parent) {
if (depth++ >= maxRecursionDepth)
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
}
}
struct Expansion *exp = malloc(sizeof(*exp));
struct Expansion *new = malloc(sizeof(*new));
if (!new)
if (!exp)
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;
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 = new;
lexerState->expansions = exp;
}
void lexer_CheckRecursionDepth(void)
{
size_t depth = 0;
for (struct Expansion *exp = lexerState->expansions; exp; exp = exp->parent) {
if (depth++ >= maxRecursionDepth)
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
}
}
static void freeExpansion(struct Expansion *expansion)
@@ -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;
}
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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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; }

View File

@@ -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

View File

@@ -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 */

View File

@@ -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);

View File

@@ -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);

File diff suppressed because it is too large Load Diff

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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)

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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
View 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
View 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
View File

@@ -0,0 +1,7 @@
$0
$A
$A
$0
$A
$0
$0

13
test/asm/opt-r.asm Normal file
View 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
View 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
View File

View 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:

View File

@@ -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

View File

@@ -10,3 +10,5 @@
-4 >> 1 = $FFFFFFFE
-4 >> 2 = $FFFFFFFF
-1 >> -9001 = $0
$DEADBEEF >> 1 = $EF56DF77
$DEADBEEF >>> 1 = $6F56DF77

Binary file not shown.

View File

@@ -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!"

View File

@@ -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

View File

@@ -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