mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 02:02:06 +00:00
Compare commits
35 Commits
v0.6.0-rc2
...
v0.6.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f90857032c | ||
|
|
1653a9a3f2 | ||
|
|
3c049983f1 | ||
|
|
8553b61a94 | ||
|
|
ab12c474d2 | ||
|
|
8ccbd9dc36 | ||
|
|
b8307432b8 | ||
|
|
80a62a8a03 | ||
|
|
bbe28faab4 | ||
|
|
106ad30e5a | ||
|
|
a1107fc5cf | ||
|
|
969412af24 | ||
|
|
c10345f26d | ||
|
|
6fd5c94b27 | ||
|
|
ddb1d0b6aa | ||
|
|
08545643cf | ||
|
|
140c6b169e | ||
|
|
d86d24bdc1 | ||
|
|
a1a919579c | ||
|
|
a47da5f71f | ||
|
|
68ad926279 | ||
|
|
dec4133e84 | ||
|
|
c35cb6ac32 | ||
|
|
023884d2b0 | ||
|
|
3567faf395 | ||
|
|
6502ed3919 | ||
|
|
b1a241233e | ||
|
|
f88968ec20 | ||
|
|
5ad8a8c958 | ||
|
|
2827374505 | ||
|
|
b8385a50e3 | ||
|
|
02923a67f3 | ||
|
|
f5b1990604 | ||
|
|
0794da22bc | ||
|
|
6df75f7af3 |
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -1,2 +1,6 @@
|
||||
# Shell scripts need Unix line endings (see https://github.com/gbdev/rgbds/issues/841)
|
||||
*.sh text eol=lf
|
||||
*.bash text eol=lf
|
||||
|
||||
# Flags also need Unix line endings (see https://github.com/gbdev/rgbds/issues/955)
|
||||
*.flags text eol=lf
|
||||
|
||||
17
.github/actions/mingw-configure.sh
vendored
17
.github/actions/mingw-configure.sh
vendored
@@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
source mingw-env @TRIPLE@
|
||||
echo LAST IS: $last
|
||||
|
||||
# check if last arg is a path to configure, else use parent
|
||||
for last; do true; done
|
||||
if test -x "${last}/configure"; then
|
||||
config_path="$last"
|
||||
else
|
||||
config_path=".."
|
||||
fi
|
||||
|
||||
${config_path}/configure \
|
||||
--host=@TRIPLE@ --target=@TRIPLE@ --build="$CHOST" \
|
||||
--prefix=/usr/@TRIPLE@ --libdir=/usr/@TRIPLE@/lib --includedir=/usr/@TRIPLE@/include \
|
||||
--enable-shared --enable-static "$@"
|
||||
16
.github/actions/mingw-env.sh
vendored
16
.github/actions/mingw-env.sh
vendored
@@ -1,16 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
_arch=$1
|
||||
|
||||
default_mingw_pp_flags="-D_FORTIFY_SOURCE=2"
|
||||
default_mingw_compiler_flags="$default_mingw_pp_flags -O2 -pipe -fno-plt -fexceptions --param=ssp-buffer-size=4"
|
||||
default_mingw_linker_flags="-Wl,-O1,--sort-common,--as-needed -fstack-protector"
|
||||
|
||||
export CPPFLAGS="${MINGW_CPPFLAGS:-$default_mingw_pp_flags $CPPFLAGS}"
|
||||
export CFLAGS="${MINGW_CFLAGS:-$default_mingw_compiler_flags $CFLAGS}"
|
||||
export CXXFLAGS="${MINGW_CXXFLAGS:-$default_mingw_compiler_flags $CXXFLAGS}"
|
||||
export LDFLAGS="${MINGW_LDFLAGS:-$default_mingw_linker_flags $LDFLAGS}"
|
||||
|
||||
mingw_prefix=/usr/${_arch}
|
||||
export PKG_CONFIG_SYSROOT_DIR="${mingw_prefix}"
|
||||
export PKG_CONFIG_LIBDIR="${mingw_prefix}/lib/pkgconfig:${mingw_prefix}/share/pkgconfig"
|
||||
44
.github/actions/mingw-w64-libpng-dev.sh
vendored
44
.github/actions/mingw-w64-libpng-dev.sh
vendored
@@ -1,44 +1,34 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script was written by ISSOtm while looking at Arch Linux's PKGBUILD for
|
||||
# the corresponding package. (And its dependencies)
|
||||
# https://aur.archlinux.org/packages/mingw-w64-libpng/
|
||||
|
||||
set -e
|
||||
|
||||
pngver=1.6.37
|
||||
_apngver=$pngver
|
||||
_arch="$1"
|
||||
|
||||
|
||||
## Install mingw-configure and mingw-env (both build dependencies)
|
||||
|
||||
install -m 755 .github/actions/mingw-env.sh /usr/bin/mingw-env
|
||||
|
||||
sed "s|@TRIPLE@|${_arch}|g" .github/actions/mingw-configure.sh > ${_arch}-configure
|
||||
install -m 755 ${_arch}-configure /usr/bin/
|
||||
|
||||
arch="$1"
|
||||
|
||||
## Grab sources and check them
|
||||
|
||||
wget http://downloads.sourceforge.net/sourceforge/libpng/libpng-$pngver.tar.xz
|
||||
wget http://downloads.sourceforge.net/project/apng/libpng/libpng16/libpng-$_apngver-apng.patch.gz
|
||||
wget http://downloads.sourceforge.net/project/libpng/libpng16/$pngver/libpng-$pngver.tar.xz
|
||||
wget http://downloads.sourceforge.net/project/apng/libpng/libpng16/libpng-$pngver-apng.patch.gz
|
||||
sha256sum -c .github/actions/mingw-w64-libpng-dev.sha256sums
|
||||
|
||||
## Extract sources
|
||||
## Extract sources and patch them
|
||||
|
||||
tar -xf libpng-$pngver.tar.xz
|
||||
gunzip libpng-$_apngver-apng.patch.gz
|
||||
gunzip libpng-$pngver-apng.patch.gz
|
||||
|
||||
# Patch in apng support
|
||||
env -C libpng-$pngver patch -p0 ../libpng-$pngver-apng.patch
|
||||
|
||||
## Start building!
|
||||
|
||||
cd libpng-$pngver
|
||||
# Patch in apng support
|
||||
patch -p0 ../libpng-$_apngver-apng.patch
|
||||
|
||||
mkdir -p build-${_arch}
|
||||
cd build-${_arch}
|
||||
${_arch}-configure LDFLAGS=-static-libgcc
|
||||
make
|
||||
mkdir -p build
|
||||
cd build
|
||||
../libpng-$pngver/configure \
|
||||
--host="$arch" --target="$arch" \
|
||||
--prefix="/usr/$arch" \
|
||||
--enable-shared --disable-static \
|
||||
CPPFLAGS="-D_FORTIFY_SOURCE=2" \
|
||||
CFLAGS="-O2 -pipe -fno-plt -fexceptions --param=ssp-buffer-size=4" \
|
||||
LDFLAGS="-Wl,-O1,--sort-common,--as-needed -fstack-protector"
|
||||
make -kj
|
||||
make install
|
||||
|
||||
@@ -6,7 +6,7 @@ on:
|
||||
|
||||
jobs:
|
||||
windows:
|
||||
runs-on: windows-2019
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Get version from tag
|
||||
|
||||
13
.github/workflows/create-release-docs.yml
vendored
13
.github/workflows/create-release-docs.yml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
if: github.repository_owner == 'gbdev'
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout rgbds@release
|
||||
uses: actions/checkout@v2
|
||||
@@ -18,17 +18,10 @@ jobs:
|
||||
with:
|
||||
repository: ${{ github.repository_owner }}/rgbds-www
|
||||
path: rgbds-www
|
||||
# `-O toc` was added in 1.14.5, but the repos only have 1.14.4
|
||||
- name: Build and install mandoc + install groff
|
||||
- name: Install groff and mandoc
|
||||
run: |
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get install -yq groff zlib1g-dev
|
||||
wget 'http://mandoc.bsd.lv/snapshots/mandoc-1.14.6.tar.gz'
|
||||
tar xf mandoc-1.14.6.tar.gz
|
||||
cd mandoc-1.14.6
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
sudo apt-get install -yq groff mandoc
|
||||
- name: Update pages
|
||||
working-directory: rgbds/man
|
||||
run: | # The ref appears to be in the format "refs/tags/<version>", so strip that
|
||||
|
||||
34
.github/workflows/testing.yml
vendored
34
.github/workflows/testing.yml
vendored
@@ -7,14 +7,14 @@ jobs:
|
||||
unix-testing:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04, ubuntu-18.04, macos-11.0, macos-10.15]
|
||||
os: [ubuntu-20.04, ubuntu-22.04, macos-11, macos-12]
|
||||
cc: [gcc, clang]
|
||||
buildsys: [make, cmake]
|
||||
exclude:
|
||||
# `gcc` is just an alias to `clang` on macOS, don't bother
|
||||
- os: macos-10.15
|
||||
- os: macos-11
|
||||
cc: gcc
|
||||
- os: macos-11.0
|
||||
- os: macos-12
|
||||
cc: gcc
|
||||
include:
|
||||
- cc: gcc
|
||||
@@ -64,6 +64,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
bits: [32, 64]
|
||||
os: [windows-2019, windows-2022]
|
||||
include:
|
||||
- bits: 32
|
||||
arch: x86
|
||||
@@ -72,7 +73,7 @@ jobs:
|
||||
arch: x86_x64
|
||||
platform: x64
|
||||
fail-fast: false
|
||||
runs-on: windows-2019
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Get zlib, libpng and bison
|
||||
@@ -149,7 +150,6 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
bits: [32, 64]
|
||||
os: [ubuntu-18.04]
|
||||
include:
|
||||
- bits: 32
|
||||
arch: i686
|
||||
@@ -158,7 +158,7 @@ jobs:
|
||||
arch: x86-64
|
||||
triplet: x86_64-w64-mingw32
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.os }}
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
DIST_DIR: win${{ matrix.bits }}
|
||||
steps:
|
||||
@@ -168,8 +168,8 @@ jobs:
|
||||
run: |
|
||||
./.github/actions/install_deps.sh ${{ matrix.os }}
|
||||
- name: Install MinGW
|
||||
run: |
|
||||
sudo apt-get install {gcc,g++}-mingw-w64-${{ matrix.arch }} mingw-w64-tools libz-mingw-w64-dev
|
||||
run: | # dpkg-dev is apparently required for pkg-config for cross-building
|
||||
sudo apt-get install {gcc,g++}-mingw-w64-${{ matrix.arch }}-win32 mingw-w64-tools libz-mingw-w64-dev dpkg-dev
|
||||
- name: Install libpng dev headers for MinGW
|
||||
run: |
|
||||
sudo ./.github/actions/mingw-w64-libpng-dev.sh ${{ matrix.triplet }}
|
||||
@@ -177,17 +177,12 @@ jobs:
|
||||
run: |
|
||||
make mingw${{ matrix.bits }} -j Q=
|
||||
- name: Package binaries
|
||||
run: |
|
||||
run: | # DLL dependencies can be figured out using e.g. Dependency Walker
|
||||
mkdir bins
|
||||
mv rgbasm bins/rgbasm.exe
|
||||
mv rgblink bins/rgblink.exe
|
||||
mv rgbfix bins/rgbfix.exe
|
||||
mv rgbgfx bins/rgbgfx.exe
|
||||
cp /usr/${{ matrix.triplet }}/lib/zlib1.dll bins
|
||||
cp /usr/${{ matrix.triplet }}/bin/libpng16-16.dll bins
|
||||
if [ ${{ matrix.bits }} -eq 32 ]; then cp /usr/lib/gcc/${{ matrix.triplet }}/7.3-win32/lib{gcc_s_sjlj-1,stdc++-6}.dll bins; fi
|
||||
mv test/gfx/randtilegen{,.exe}
|
||||
mv test/gfx/rgbgfx_test{,.exe}
|
||||
mv -v rgb{asm,link,fix,gfx}.exe bins/
|
||||
cp -v /usr/${{ matrix.triplet }}/lib/zlib1.dll bins
|
||||
cp -v /usr/${{ matrix.triplet }}/bin/libpng16-16.dll bins
|
||||
[ "${{ matrix.bits }}" -ne 32 ] || cp -v /usr/lib/gcc/${{ matrix.triplet }}/10-win32/lib{gcc_s_dw2-1,ssp-0,stdc++-6}.dll bins
|
||||
- name: Upload Windows binaries
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
@@ -205,9 +200,10 @@ jobs:
|
||||
needs: windows-xbuild
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-2019, windows-2022]
|
||||
bits: [32, 64]
|
||||
fail-fast: false
|
||||
runs-on: windows-2019
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Retrieve binaries
|
||||
|
||||
12
.github/workflows/update-master-docs.yml
vendored
12
.github/workflows/update-master-docs.yml
vendored
@@ -17,7 +17,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
if: github.repository_owner == 'gbdev'
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout rgbds@master
|
||||
uses: actions/checkout@v2
|
||||
@@ -31,16 +31,10 @@ jobs:
|
||||
repository: gbdev/rgbds-www
|
||||
ref: master
|
||||
path: rgbds-www
|
||||
- name: Build and install mandoc + install groff
|
||||
- name: Install groff and mandoc
|
||||
run: |
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get install -yq groff zlib1g-dev
|
||||
wget 'http://mandoc.bsd.lv/snapshots/mandoc-1.14.6.tar.gz'
|
||||
tar xf mandoc-1.14.6.tar.gz
|
||||
cd mandoc-1.14.6
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
sudo apt-get install -yq groff mandoc
|
||||
- name: Update pages
|
||||
working-directory: rgbds/man
|
||||
run: |
|
||||
|
||||
18
Makefile
18
Makefile
@@ -9,6 +9,8 @@
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .h .y .c .cpp .o
|
||||
|
||||
.PHONY: all clean install checkcodebase checkpatch checkdiff develop debug mingw32 mingw64 wine-shim dist
|
||||
|
||||
# User-defined variables
|
||||
|
||||
Q := @
|
||||
@@ -33,8 +35,8 @@ VERSION_STRING := `git describe --tags --dirty --always 2>/dev/null`
|
||||
WARNFLAGS := -Wall -pedantic
|
||||
|
||||
# Overridable CFLAGS
|
||||
CFLAGS ?= -O3 -flto -DNDEBUG
|
||||
CXXFLAGS ?= -O3 -flto -DNDEBUG
|
||||
CFLAGS ?= -O3 -flto=auto -DNDEBUG
|
||||
CXXFLAGS ?= -O3 -flto=auto -DNDEBUG
|
||||
# Non-overridable CFLAGS
|
||||
# _ISOC11_SOURCE is required on certain platforms to get C11 on top of the C99-based POSIX 2008
|
||||
REALCFLAGS := ${CFLAGS} ${WARNFLAGS} -std=gnu11 -I include \
|
||||
@@ -95,6 +97,7 @@ rgblink_obj := \
|
||||
src/link/section.o \
|
||||
src/link/symbol.o \
|
||||
src/extern/getopt.o \
|
||||
src/extern/utf8decoder.o \
|
||||
src/error.o \
|
||||
src/hashmap.o \
|
||||
src/linkdefs.o \
|
||||
@@ -249,6 +252,13 @@ develop:
|
||||
CFLAGS="-ggdb3 -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls" \
|
||||
CXXFLAGS="-ggdb3 -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls"
|
||||
|
||||
# This target is used during development in order to more easily debug with gdb.
|
||||
|
||||
debug:
|
||||
$Qenv ${MAKE} \
|
||||
CFLAGS="-ggdb3 -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls" \
|
||||
CXXFLAGS="-ggdb3 -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls"
|
||||
|
||||
# Targets for the project maintainer to easily create Windows exes.
|
||||
# This is not for Windows users!
|
||||
# If you're building on Windows with Cygwin or Mingw, just follow the Unix
|
||||
@@ -257,12 +267,12 @@ develop:
|
||||
mingw32:
|
||||
$Q${MAKE} all test/gfx/randtilegen test/gfx/rgbgfx_test \
|
||||
CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ \
|
||||
BISON=bison PKG_CONFIG=i686-w64-mingw32-pkg-config -j
|
||||
BISON=bison PKG_CONFIG="PKG_CONFIG_SYSROOT_DIR=/usr/i686-w64-mingw32 pkg-config"
|
||||
|
||||
mingw64:
|
||||
$Q${MAKE} all test/gfx/randtilegen test/gfx/rgbgfx_test \
|
||||
CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ \
|
||||
BISON=bison PKG_CONFIG=x86_64-w64-mingw32-pkg-config -j
|
||||
BISON=bison PKG_CONFIG="PKG_CONFIG_SYSROOT_DIR=/usr/x86_64-w64-mingw32 pkg-config"
|
||||
|
||||
wine-shim:
|
||||
$Qecho '#!/bin/bash' > rgbshim.sh
|
||||
|
||||
@@ -35,9 +35,10 @@ _rgbasm_completions() {
|
||||
[b]="binary-digits:unk"
|
||||
[D]="define:unk"
|
||||
[g]="gfx-chars:unk"
|
||||
[i]="include:dir"
|
||||
[I]="include:dir"
|
||||
[M]="dependfile:glob-*.mk *.d"
|
||||
[o]="output:glob-*.o"
|
||||
[P]="preinclude:glob-*.asm *.inc"
|
||||
[p]="pad-value:unk"
|
||||
[Q]="q-precision:unk"
|
||||
[r]="recursion-depth:unk"
|
||||
|
||||
@@ -29,7 +29,7 @@ _rgbasm_warnings() {
|
||||
'user:Warn when executing the WARN built-in'
|
||||
)
|
||||
# TODO: handle `no-` and `error=` somehow?
|
||||
# TODO: handle `=0|1|2` levels for `numeric-string` and `truncation`?
|
||||
# TODO: handle `=0|1|2` levels for `numeric-string`, `truncation`, and `unmapped-char`?
|
||||
_describe warning warnings
|
||||
}
|
||||
|
||||
@@ -48,13 +48,14 @@ local args=(
|
||||
'(-b --binary-digits)'{-b,--binary-digits}'+[Change chars for binary constants]:digit spec:'
|
||||
'*'{-D,--define}'+[Define a string symbol]:name + value (default 1):'
|
||||
'(-g --gfx-chars)'{-g,--gfx-chars}'+[Change chars for gfx constants]:chars spec:'
|
||||
'(-i --include)'{-i,--include}'+[Add an include directory]:include path:_files -/'
|
||||
'(-I --include)'{-I,--include}'+[Add an include directory]:include path:_files -/'
|
||||
'(-M --dependfile)'{-M,--dependfile}"+[List deps in make format]:output file:_files -g '*.{d,mk}'"
|
||||
-MG'[Assume missing files should be generated]'
|
||||
-MP'[Add phony targets to all deps]'
|
||||
'*'-MT"+[Add a target to the rules]:target:_files -g '*.{d,mk,o}'"
|
||||
'*'-MQ"+[Add a target to the rules]:target:_files -g '*.{d,mk,o}'"
|
||||
'(-o --output)'{-o,--output}'+[Output file]:output file:_files'
|
||||
'(-P --preinclude)'{-P,--preinclude}"+[Pre-include a file]:include file:_files -g '*.{asm,inc}'"
|
||||
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
|
||||
'(-Q --q-precision)'{-Q,--q-precision}'+[Set fixed-point precision]:precision:'
|
||||
'(-r --recursion-depth)'{-r,--recursion-depth}'+[Set maximum recursion depth]:depth:'
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define DEFAULT_CHARMAP_NAME "main"
|
||||
|
||||
struct Charmap *charmap_New(char const *name, char const *baseName);
|
||||
void charmap_Delete(struct Charmap *charmap);
|
||||
void charmap_Set(char const *name);
|
||||
|
||||
@@ -13,22 +13,22 @@
|
||||
|
||||
extern uint8_t fixPrecision;
|
||||
|
||||
uint8_t fix_Precision(void);
|
||||
double fix_PrecisionFactor(void);
|
||||
void fix_Print(int32_t i);
|
||||
int32_t fix_Sin(int32_t i);
|
||||
int32_t fix_Cos(int32_t i);
|
||||
int32_t fix_Tan(int32_t i);
|
||||
int32_t fix_ASin(int32_t i);
|
||||
int32_t fix_ACos(int32_t i);
|
||||
int32_t fix_ATan(int32_t i);
|
||||
int32_t fix_ATan2(int32_t i, int32_t j);
|
||||
int32_t fix_Mul(int32_t i, int32_t j);
|
||||
int32_t fix_Mod(int32_t i, int32_t j);
|
||||
int32_t fix_Div(int32_t i, int32_t j);
|
||||
int32_t fix_Pow(int32_t i, int32_t j);
|
||||
int32_t fix_Log(int32_t i, int32_t j);
|
||||
int32_t fix_Round(int32_t i);
|
||||
int32_t fix_Ceil(int32_t i);
|
||||
int32_t fix_Floor(int32_t i);
|
||||
int32_t fix_Sin(int32_t i, int32_t q);
|
||||
int32_t fix_Cos(int32_t i, int32_t q);
|
||||
int32_t fix_Tan(int32_t i, int32_t q);
|
||||
int32_t fix_ASin(int32_t i, int32_t q);
|
||||
int32_t fix_ACos(int32_t i, int32_t q);
|
||||
int32_t fix_ATan(int32_t i, int32_t q);
|
||||
int32_t fix_ATan2(int32_t i, int32_t j, int32_t q);
|
||||
int32_t fix_Mul(int32_t i, int32_t j, int32_t q);
|
||||
int32_t fix_Mod(int32_t i, int32_t j, int32_t q);
|
||||
int32_t fix_Div(int32_t i, int32_t j, int32_t q);
|
||||
int32_t fix_Pow(int32_t i, int32_t j, int32_t q);
|
||||
int32_t fix_Log(int32_t i, int32_t j, int32_t q);
|
||||
int32_t fix_Round(int32_t i, int32_t q);
|
||||
int32_t fix_Ceil(int32_t i, int32_t q);
|
||||
int32_t fix_Floor(int32_t i, int32_t q);
|
||||
|
||||
#endif // RGBDS_ASM_FIXPOINT_H
|
||||
|
||||
@@ -58,6 +58,7 @@ struct FileStackNode *fstk_GetFileStack(void);
|
||||
char const *fstk_GetFileName(void);
|
||||
|
||||
void fstk_AddIncludePath(char const *s);
|
||||
void fstk_SetPreIncludeFile(char const *s);
|
||||
/*
|
||||
* @param path The user-provided file name
|
||||
* @param fullPath The address of a pointer, which will be made to point at the full path
|
||||
|
||||
@@ -127,6 +127,8 @@ struct Symbol *sym_FindExactSymbol(char const *symName);
|
||||
struct Symbol *sym_FindUnscopedSymbol(char const *symName);
|
||||
// Find a symbol, possibly scoped, by name
|
||||
struct Symbol *sym_FindScopedSymbol(char const *symName);
|
||||
// Find a scoped symbol by name; do not return `@` or `_NARG` when they have no value
|
||||
struct Symbol *sym_FindScopedValidSymbol(char const *symName);
|
||||
struct Symbol const *sym_GetPC(void);
|
||||
struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size);
|
||||
struct Symbol *sym_Ref(char const *symName);
|
||||
|
||||
@@ -36,7 +36,6 @@ enum WarningID {
|
||||
WARNING_OBSOLETE, // Obsolete things
|
||||
WARNING_SHIFT, // Shifting undefined behavior
|
||||
WARNING_SHIFT_AMOUNT, // Strange shift amount
|
||||
WARNING_UNMAPPED_CHAR, // Character without charmap entry
|
||||
WARNING_USER, // User warnings
|
||||
|
||||
NB_PLAIN_WARNINGS,
|
||||
@@ -49,6 +48,9 @@ enum WarningID {
|
||||
// Implicit truncation loses some bits
|
||||
WARNING_TRUNCATION_1,
|
||||
WARNING_TRUNCATION_2,
|
||||
// Character without charmap entry
|
||||
WARNING_UNMAPPED_CHAR_1,
|
||||
WARNING_UNMAPPED_CHAR_2,
|
||||
|
||||
NB_PLAIN_AND_PARAM_WARNINGS,
|
||||
#define NB_PARAM_WARNINGS (NB_PLAIN_AND_PARAM_WARNINGS - PARAM_WARNINGS_START)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#ifndef RGBDS_LINK_SECTION_H
|
||||
#define RGBDS_LINK_SECTION_H
|
||||
|
||||
// GUIDELINE: external code MUST NOT BE AWARE of the data structure used!!
|
||||
// GUIDELINE: external code MUST NOT BE AWARE of the data structure used!
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#ifndef RGBDS_LINK_SYMBOL_H
|
||||
#define RGBDS_LINK_SYMBOL_H
|
||||
|
||||
// GUIDELINE: external code MUST NOT BE AWARE of the data structure used!!
|
||||
// GUIDELINE: external code MUST NOT BE AWARE of the data structure used!
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ extern "C" {
|
||||
#define PACKAGE_VERSION_MAJOR 0
|
||||
#define PACKAGE_VERSION_MINOR 6
|
||||
#define PACKAGE_VERSION_PATCH 0
|
||||
#define PACKAGE_VERSION_RC 2
|
||||
|
||||
char const *get_package_version_string(void);
|
||||
|
||||
|
||||
50
man/rgbasm.1
50
man/rgbasm.1
@@ -17,28 +17,28 @@
|
||||
.Op Fl b Ar chars
|
||||
.Op Fl D Ar name Ns Op = Ns Ar value
|
||||
.Op Fl g Ar chars
|
||||
.Op Fl i Ar path
|
||||
.Op Fl I Ar path
|
||||
.Op Fl M Ar depend_file
|
||||
.Op Fl MG
|
||||
.Op Fl MP
|
||||
.Op Fl MT Ar target_file
|
||||
.Op Fl MQ Ar target_file
|
||||
.Op Fl o Ar out_file
|
||||
.Op Fl P Ar include_file
|
||||
.Op Fl p Ar pad_value
|
||||
.Op Fl Q Ar fix_precision
|
||||
.Op Fl r Ar recursion_depth
|
||||
.Op Fl W Ar warning
|
||||
.Ar
|
||||
.Ar asmfile
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
program creates an RGB object file from an assembly source file.
|
||||
The input
|
||||
.Ar file
|
||||
can be a file path, or
|
||||
.Ar asmfile
|
||||
can be a path to a file, or
|
||||
.Cm \-
|
||||
denoting
|
||||
.Cm stdin .
|
||||
to read from standard input.
|
||||
.Pp
|
||||
Note that options can be abbreviated as long as the abbreviation is unambiguous:
|
||||
.Fl Fl verb
|
||||
@@ -86,8 +86,20 @@ Disables inserting a
|
||||
instruction immediately after any
|
||||
.Ic halt
|
||||
instruction.
|
||||
.It Fl i Ar path , Fl Fl include Ar path
|
||||
Add an include path.
|
||||
.It Fl I Ar path , Fl Fl include Ar path
|
||||
Add a new
|
||||
.Dq include path ; Ar path
|
||||
must point to a directory.
|
||||
When a
|
||||
.Ic INCLUDE
|
||||
.Pq including the implicit one from Fl P
|
||||
or
|
||||
.Ic INCBIN
|
||||
is attempted,
|
||||
.Nm
|
||||
first looks up the provided path from its working directory; if this fails, it tries again from each of the
|
||||
.Dq include path
|
||||
directories, in the order they were provided.
|
||||
.It Fl L , Fl Fl preserve-ld
|
||||
By default,
|
||||
.Nm
|
||||
@@ -116,6 +128,7 @@ This makes
|
||||
.Nm
|
||||
assume that missing files are auto-generated: when
|
||||
.Ic INCLUDE
|
||||
.Pq including the implicit one from Fl P
|
||||
or
|
||||
.Ic INCBIN
|
||||
is attempted on a non-existent file, it is added as a dependency, then
|
||||
@@ -146,6 +159,12 @@ characters, essentially
|
||||
.Sq $ .
|
||||
.It Fl o Ar out_file , Fl Fl output Ar out_file
|
||||
Write an object file to the given filename.
|
||||
.It Fl P Ar include_file , Fl Fl preinclude Ar include_file
|
||||
Pre-include a file.
|
||||
This acts as if a
|
||||
.Ql Ic INCLUDE Qq Ar include_file
|
||||
was read before the input
|
||||
.Ar asmfile .
|
||||
.It Fl p Ar pad_value , Fl Fl pad-value Ar pad_value
|
||||
When padding an image, pad with this value.
|
||||
The default is 0x00.
|
||||
@@ -301,12 +320,19 @@ warns when an N-bit value's absolute value is 2**N or greater.
|
||||
or just
|
||||
.Fl Wtruncation
|
||||
also warns when an N-bit value is less than -2**(N-1), which will not fit in two's complement encoding.
|
||||
.It Fl Wunmapped-char
|
||||
.It Fl Wunmapped-char=
|
||||
Warn when a character goes through charmap conversion but has no defined mapping.
|
||||
This warning is always disabled if the active charmap is empty, and/or is the default charmap
|
||||
.Fl Wunmapped-char=0
|
||||
or
|
||||
.Fl Wunmapped-char
|
||||
disables this warning.
|
||||
.Fl Wunmapped-char=1
|
||||
or just
|
||||
.Fl Wunmapped-char
|
||||
only warns if the active charmap is not empty.
|
||||
.Fl Wunmapped-char=2
|
||||
warns if the active charmap is empty, and/or is not the default charmap
|
||||
.Sq main .
|
||||
This warning is enabled by
|
||||
.Fl Wall .
|
||||
.It Fl Wno-user
|
||||
Warn when the
|
||||
.Ic WARN
|
||||
|
||||
204
man/rgbasm.5
204
man/rgbasm.5
@@ -18,7 +18,7 @@ This is the full description of the language used by
|
||||
The description of the instructions supported by the Game Boy CPU is in
|
||||
.Xr gbz80 7 .
|
||||
.Pp
|
||||
It is strongly recommended to have some familiarity with the Game Boy hardware before reading this document.
|
||||
It is advisable to have some familiarity with the Game Boy hardware before reading this document.
|
||||
RGBDS is specifically targeted at the Game Boy, and thus a lot of its features tie directly to its concepts.
|
||||
This document is not intended to be a Game Boy hardware reference.
|
||||
.Pp
|
||||
@@ -57,27 +57,25 @@ and ending with
|
||||
.Ql */ .
|
||||
It can be split across multiple lines, or occur in the middle of an expression:
|
||||
.Bd -literal -offset indent
|
||||
X = /* the value of x
|
||||
should be 3 */ 3
|
||||
DEF X = /* the value of x
|
||||
should be 3 */ 3
|
||||
.Ed
|
||||
.Pp
|
||||
Sometimes lines can be too long and it may be necessary to split them.
|
||||
To do so, put a backslash at the end of the line:
|
||||
.Bd -literal -offset indent
|
||||
DB 1, 2, 3,\ \[rs]
|
||||
4, 5, 6,\ \[rs]\ ;\ Put it before any comments
|
||||
DB 1, 2, 3,\ \e
|
||||
4, 5, 6,\ \e\ ;\ Put it before any comments
|
||||
7, 8, 9
|
||||
DB "Hello,\ \[rs]\ \ ;\ Space before the \[rs] is included
|
||||
DB "Hello,\ \e\ \ ;\ Space before the \e is included
|
||||
world!"\ \ \ \ \ \ \ \ \ \ \ ;\ Any leading space is included
|
||||
.Ed
|
||||
.Ss Symbol interpolation
|
||||
A funky feature is
|
||||
.Ql {symbol}
|
||||
within a string, called
|
||||
A funky feature is writing a symbol between
|
||||
.Ql {braces} ,
|
||||
called
|
||||
.Dq symbol interpolation .
|
||||
This will paste the contents of
|
||||
.Ql symbol
|
||||
as if they were part of the source file.
|
||||
This will paste the symbol's contents as if they were part of the source file.
|
||||
If it is a string symbol, its characters are simply inserted as-is.
|
||||
If it is a numeric symbol, its value is converted to hexadecimal notation with a dollar sign
|
||||
.Sq $
|
||||
@@ -85,7 +83,7 @@ prepended.
|
||||
.Pp
|
||||
Symbol interpolations can be nested, too!
|
||||
.Bd -literal -offset indent
|
||||
DEF topic EQUS "life, the universe, and \[rs]"everything\[rs]""
|
||||
DEF topic EQUS "life, the universe, and \e"everything\e""
|
||||
DEF meaning EQUS "answer"
|
||||
;\ Defines answer = 42
|
||||
DEF {meaning} = 42
|
||||
@@ -170,20 +168,20 @@ Valid print types are:
|
||||
Examples:
|
||||
.Bd -literal -offset indent
|
||||
SECTION "Test", ROM0[2]
|
||||
X: ;\ This works with labels **whose address is known**
|
||||
Y = 3 ;\ This also works with variables
|
||||
SUM equ X + Y ;\ And likewise with numeric constants
|
||||
X: ;\ This works with labels **whose address is known**
|
||||
DEF Y = 3 ;\ This also works with variables
|
||||
DEF SUM EQU X + Y ;\ And likewise with numeric constants
|
||||
; Prints "%0010 + $3 == 5"
|
||||
PRINTLN "{#05b:X} + {#x:Y} == {d:SUM}"
|
||||
|
||||
rsset 32
|
||||
PERCENT rb 1 ;\ Same with offset constants
|
||||
VALUE = 20
|
||||
RESULT = MUL(20.0, 0.32)
|
||||
DEF PERCENT rb 1 ;\ Same with offset constants
|
||||
DEF VALUE = 20
|
||||
DEF RESULT = MUL(20.0, 0.32)
|
||||
; Prints "32% of 20 = 6.40"
|
||||
PRINTLN "{d:PERCENT}% of {d:VALUE} = {f:RESULT}"
|
||||
|
||||
WHO equs STRLWR("WORLD")
|
||||
DEF WHO EQUS STRLWR("WORLD")
|
||||
; Prints "Hello world!"
|
||||
PRINTLN "Hello {s:WHO}!"
|
||||
.Ed
|
||||
@@ -348,44 +346,53 @@ delim $$
|
||||
delim off
|
||||
.EN
|
||||
.Pp
|
||||
All of these fixed-point functions can take an optional final argument, which is the precision to use.
|
||||
For example,
|
||||
.Ql MUL(6.0q8, 7.0q8, 8)
|
||||
will evaluate to
|
||||
.Ql 42.0q8
|
||||
no matter what value is set as the current
|
||||
.Cm Q
|
||||
option.
|
||||
.Pp
|
||||
The trigonometry functions (
|
||||
.Ic SIN ,
|
||||
.Ic COS ,
|
||||
.Ic TAN ,
|
||||
etc) are defined in terms of a circle divided into 65535.0 degrees.
|
||||
etc) are defined in terms of a circle divided into 1.0 "turns" (equal to 2pi radians or 360 degrees).
|
||||
.Pp
|
||||
These functions are useful for automatic generation of various tables.
|
||||
For example:
|
||||
.Bd -literal -offset indent
|
||||
; Generate a 256-byte sine table with values in the range [0, 128]
|
||||
; (shifted and scaled from the range [-1.0, 1.0])
|
||||
ANGLE = 0.0
|
||||
REPT 256
|
||||
db (MUL(64.0, SIN(ANGLE)) + 64.0) >> 16
|
||||
ANGLE = ANGLE + 256.0 ; 256.0 = 65536 degrees / 256 entries
|
||||
ENDR
|
||||
; Generate a table of sine values from sin(0.0) to sin(1.0), with
|
||||
; amplitude scaled from [-1.0, 1.0] to [0.0, 128.0]
|
||||
DEF turns = 0.0
|
||||
REPT 256
|
||||
db MUL(64.0, SIN(turns) + 1.0) >> 16
|
||||
DEF turns += 1.0 / 256
|
||||
ENDR
|
||||
.Ed
|
||||
.Ss String expressions
|
||||
The most basic string expression is any number of characters contained in double quotes
|
||||
.Pq Ql \&"for instance" .
|
||||
The backslash character
|
||||
.Ql \[rs]
|
||||
.Ql \e
|
||||
is special in that it causes the character following it to be
|
||||
.Dq escaped ,
|
||||
meaning that it is treated differently from normal.
|
||||
There are a number of escape sequences you can use within a string:
|
||||
.Bl -column -offset indent "Qo \[rs]1 Qc \[en] Qo \[rs]9 Qc"
|
||||
.Bl -column -offset indent "Qo \e1 Qc \[en] Qo \e9 Qc"
|
||||
.It Sy String Ta Sy Meaning
|
||||
.It Ql \[rs]\[rs] Ta Produces a backslash
|
||||
.It Ql \[rs]" Ta Produces a double quote without terminating
|
||||
.It Ql \[rs]{ Ta Curly bracket left
|
||||
.It Ql \[rs]} Ta Curly bracket right
|
||||
.It Ql \[rs]n Ta Newline ($0A)
|
||||
.It Ql \[rs]r Ta Carriage return ($0D)
|
||||
.It Ql \[rs]t Ta Tab ($09)
|
||||
.It Qo \[rs]1 Qc \[en] Qo \[rs]9 Qc Ta Macro argument (Only in the body of a macro; see Sx Invoking macros )
|
||||
.It Ql \[rs]# Ta All Dv _NARG No macro arguments, separated by commas (Only in the body of a macro)
|
||||
.It Ql \[rs]@ Ta Label name suffix (Only in the body of a macro or a Ic REPT No block)
|
||||
.It Ql \e\e Ta Produces a backslash
|
||||
.It Ql \e" Ta Produces a double quote without terminating
|
||||
.It Ql \e{ Ta Curly bracket left
|
||||
.It Ql \e} Ta Curly bracket right
|
||||
.It Ql \en Ta Newline ($0A)
|
||||
.It Ql \er Ta Carriage return ($0D)
|
||||
.It Ql \et Ta Tab ($09)
|
||||
.It Qo \e1 Qc \[en] Qo \e9 Qc Ta Macro argument (Only in the body of a macro; see Sx Invoking macros )
|
||||
.It Ql \e# Ta All Dv _NARG No macro arguments, separated by commas (Only in the body of a macro)
|
||||
.It Ql \e@ Ta Label name suffix (Only in the body of a macro or a Ic REPT No block)
|
||||
.El
|
||||
(Note that some of those can be used outside of strings, when noted further in this document.)
|
||||
.Pp
|
||||
@@ -393,9 +400,9 @@ Multi-line strings are contained in triple quotes
|
||||
.Pq Ql \&"\&"\&"for instance\&"\&"\&" .
|
||||
Escape sequences work the same way in multi-line strings; however, literal newline
|
||||
characters will be included as-is, without needing to escape them with
|
||||
.Ql \[rs]r
|
||||
.Ql \er
|
||||
or
|
||||
.Ql \[rs]n .
|
||||
.Ql \en .
|
||||
.Pp
|
||||
The following functions operate on string expressions.
|
||||
Most of them return a string, however some of these functions actually return an integer and can be used as part of an integer expression!
|
||||
@@ -474,6 +481,11 @@ is a label, it returns the bank number the label is in.
|
||||
The result may be constant if
|
||||
.Nm
|
||||
is able to compute it.
|
||||
.It Fn SECTION symbol Ta Returns the name of the section that
|
||||
.Ar symbol
|
||||
is in.
|
||||
.Ar symbol
|
||||
must have been defined already.
|
||||
.It Fn SIZEOF arg Ta Returns the size of the section named
|
||||
.Ar arg .
|
||||
The result is not constant, since only RGBLINK can compute its value.
|
||||
@@ -1013,7 +1025,7 @@ DEF ARRAY_SIZE EQU 4
|
||||
DEF COUNT = 2
|
||||
DEF COUNT = 3
|
||||
DEF COUNT = ARRAY_SIZE + COUNT
|
||||
COUNT = COUNT*2
|
||||
DEF COUNT *= 2
|
||||
;\ COUNT now has the value 14
|
||||
.Ed
|
||||
.Pp
|
||||
@@ -1066,7 +1078,7 @@ This can be used, for example, to update a constant using a macro, without makin
|
||||
def NUM_ITEMS equ 0
|
||||
MACRO add_item
|
||||
redef NUM_ITEMS equ NUM_ITEMS + 1
|
||||
def ITEM_{02x:NUM_ITEMS} equ \[rs]1
|
||||
def ITEM_{02x:NUM_ITEMS} equ \e1
|
||||
ENDM
|
||||
add_item 1
|
||||
add_item 4
|
||||
@@ -1131,7 +1143,7 @@ will not expand string constants in their names.
|
||||
DEF COUNTREG EQUS "[hl+]"
|
||||
ld a,COUNTREG
|
||||
|
||||
DEF PLAYER_NAME EQUS "\[rs]"John\[rs]""
|
||||
DEF PLAYER_NAME EQUS "\e"John\e""
|
||||
db PLAYER_NAME
|
||||
.Ed
|
||||
.Pp
|
||||
@@ -1143,7 +1155,7 @@ This will be interpreted as:
|
||||
.Pp
|
||||
String constants can also be used to define small one-line macros:
|
||||
.Bd -literal -offset indent
|
||||
DEF pusha EQUS "push af\[rs]npush bc\[rs]npush de\[rs]npush hl\[rs]n"
|
||||
DEF pusha EQUS "push af\enpush bc\enpush de\enpush hl\en"
|
||||
.Ed
|
||||
.Pp
|
||||
Note that colons
|
||||
@@ -1252,18 +1264,18 @@ ENDM
|
||||
But this will:
|
||||
.Bd -literal -offset indent
|
||||
MACRO outer
|
||||
DEF definition EQUS "MACRO inner\[rs]nPRINTLN \[rs]"Hello!\[rs]"\[rs]nENDM"
|
||||
DEF definition EQUS "MACRO inner\enPRINTLN \e"Hello!\e"\enENDM"
|
||||
definition
|
||||
PURGE definition
|
||||
ENDM
|
||||
.Ed
|
||||
.Pp
|
||||
Macro arguments support all the escape sequences of strings, as well as
|
||||
.Ql \[rs],
|
||||
.Ql \e,
|
||||
to escape commas, as well as
|
||||
.Ql \[rs](
|
||||
.Ql \e(
|
||||
and
|
||||
.Ql \[rs])
|
||||
.Ql \e)
|
||||
to escape parentheses, since those otherwise separate and enclose arguments, respectively.
|
||||
.Ss Exporting and importing symbols
|
||||
Importing and exporting of symbols is a feature that is very useful when your project spans many source files and, for example, you need to jump to a routine defined in another file.
|
||||
@@ -1324,7 +1336,7 @@ Note also that only exported symbols will appear in symbol and map files produce
|
||||
.Ss Purging symbols
|
||||
.Ic PURGE
|
||||
allows you to completely remove a symbol from the symbol table as if it had never existed.
|
||||
.Em USE WITH EXTREME CAUTION!!!
|
||||
.Em USE WITH EXTREME CAUTION!
|
||||
I can't stress this enough,
|
||||
.Sy you seriously need to know what you are doing .
|
||||
DON'T purge a symbol that you use in expressions the linker needs to calculate.
|
||||
@@ -1343,8 +1355,6 @@ The following symbols are defined by the assembler:
|
||||
.It Dv @ Ta Ic EQU Ta PC value (essentially, the current memory address)
|
||||
.It Dv _RS Ta Ic = Ta _RS Counter
|
||||
.It Dv _NARG Ta Ic EQU Ta Number of arguments passed to macro, updated by Ic SHIFT
|
||||
.It Dv __LINE__ Ta Ic EQU Ta The current line number
|
||||
.It Dv __FILE__ Ta Ic EQUS Ta The current filename
|
||||
.It Dv __DATE__ Ta Ic EQUS Ta Today's date
|
||||
.It Dv __TIME__ Ta Ic EQUS Ta The current time
|
||||
.It Dv __ISO_8601_LOCAL__ Ta Ic EQUS Ta ISO 8601 timestamp (local)
|
||||
@@ -1561,19 +1571,19 @@ ENDM
|
||||
.Pp
|
||||
This is fine, but only if you use the macro no more than once per scope.
|
||||
To get around this problem, there is the escape sequence
|
||||
.Ic \[rs]@
|
||||
.Ic \e@
|
||||
that expands to a unique string.
|
||||
.Pp
|
||||
.Ic \[rs]@
|
||||
.Ic \e@
|
||||
also works in
|
||||
.Ic REPT
|
||||
blocks.
|
||||
.Bd -literal -offset indent
|
||||
MACRO LoopyMacro
|
||||
xor a,a
|
||||
\&.loop\[rs]@ ld [hl+],a
|
||||
\&.loop\e@ ld [hl+],a
|
||||
dec c
|
||||
jr nz,.loop\[rs]@
|
||||
jr nz,.loop\e@
|
||||
ENDM
|
||||
.Ed
|
||||
.Pp
|
||||
@@ -1593,18 +1603,18 @@ which references the same macro, which has the same problem.
|
||||
.Pp
|
||||
It's possible to pass arguments to macros as well!
|
||||
You retrieve the arguments by using the escape sequences
|
||||
.Ic \[rs]1
|
||||
.Ic \e1
|
||||
through
|
||||
.Ic \[rs]9 , \[rs]1
|
||||
.Ic \e9 , \e1
|
||||
being the first argument specified on the macro invocation.
|
||||
.Bd -literal -offset indent
|
||||
MACRO LoopyMacro
|
||||
ld hl,\[rs]1
|
||||
ld c,\[rs]2
|
||||
ld hl,\e1
|
||||
ld c,\e2
|
||||
xor a,a
|
||||
\&.loop\[rs]@ ld [hl+],a
|
||||
\&.loop\e@ ld [hl+],a
|
||||
dec c
|
||||
jr nz,.loop\[rs]@
|
||||
jr nz,.loop\e@
|
||||
ENDM
|
||||
.Ed
|
||||
.Pp
|
||||
@@ -1617,14 +1627,14 @@ LoopyMacro MyVars,54
|
||||
Arguments are passed as string constants, although there's no need to enclose them in quotes.
|
||||
Thus, an expression will not be evaluated first but kind of copy-pasted.
|
||||
This means that it's probably a very good idea to use brackets around
|
||||
.Ic \[rs]1
|
||||
.Ic \e1
|
||||
to
|
||||
.Ic \[rs]9
|
||||
.Ic \e9
|
||||
if you perform further calculations on them.
|
||||
For instance, consider the following:
|
||||
.Bd -literal -offset indent
|
||||
MACRO print_double
|
||||
PRINTLN \[rs]1 * 2
|
||||
PRINTLN \e1 * 2
|
||||
ENDM
|
||||
print_double 1 + 2
|
||||
.Ed
|
||||
@@ -1639,15 +1649,15 @@ Line continuations work as usual inside macros or lists of macro arguments.
|
||||
However, some characters need to be escaped, as in the following example:
|
||||
.Bd -literal -offset indent
|
||||
MACRO PrintMacro1
|
||||
PRINTLN STRCAT(\[rs]1)
|
||||
PRINTLN STRCAT(\e1)
|
||||
ENDM
|
||||
PrintMacro1 "Hello "\[rs], \[rs]
|
||||
PrintMacro1 "Hello "\e, \e
|
||||
"world"
|
||||
MACRO PrintMacro2
|
||||
PRINT \[rs]1
|
||||
PRINT \e1
|
||||
ENDM
|
||||
PrintMacro2 STRCAT("Hello ", \[rs]
|
||||
"world\[rs]n")
|
||||
PrintMacro2 STRCAT("Hello ", \e
|
||||
"world\en")
|
||||
.Ed
|
||||
.Pp
|
||||
The comma in
|
||||
@@ -1657,34 +1667,34 @@ The comma in
|
||||
.Ql PrintMacro2
|
||||
does not need escaping because it is inside parentheses, similar to macro arguments in C.
|
||||
The backslash in
|
||||
.Ql \[rs]n
|
||||
.Ql \en
|
||||
also does not need escaping because string literals work as usual inside macro arguments.
|
||||
.Pp
|
||||
Since there are only nine digits, you can only access the first nine macro arguments like this.
|
||||
To use the rest, you need to put the multi-digit argument number in angle brackets, like
|
||||
.Ql \[rs]<10> .
|
||||
.Ql \e<10> .
|
||||
This bracketed syntax supports decimal numbers and numeric constant symbols.
|
||||
For example,
|
||||
.Ql \[rs]<_NARG>
|
||||
.Ql \e<_NARG>
|
||||
will get the last argument.
|
||||
.Pp
|
||||
Other macro arguments and symbol interpolations will be expanded inside the angle brackets.
|
||||
For example, if
|
||||
.Ql \[rs]1
|
||||
.Ql \e1
|
||||
is
|
||||
.Ql 13 ,
|
||||
then
|
||||
.Ql \[rs]<\[rs]1>
|
||||
.Ql \e<\e1>
|
||||
will expand to
|
||||
.Ql \[rs]<13> .
|
||||
.Ql \e<13> .
|
||||
Or if
|
||||
.Ql v10 = 42
|
||||
and
|
||||
.Ql x = 10 ,
|
||||
then
|
||||
.Ql \[rs]<v{d:x}>
|
||||
.Ql \e<v{d:x}>
|
||||
will expand to
|
||||
.Ql \[rs]<42> .
|
||||
.Ql \e<42> .
|
||||
.Pp
|
||||
Another way to access more than nine macro arguments is the
|
||||
.Ic SHIFT
|
||||
@@ -1692,11 +1702,11 @@ command, a special command only available in macros.
|
||||
It will shift the arguments by one to the left, and decrease
|
||||
.Dv _NARG
|
||||
by 1.
|
||||
.Ic \[rs]1
|
||||
.Ic \e1
|
||||
will get the value of
|
||||
.Ic \[rs]2 , \[rs]2
|
||||
.Ic \e2 , \e2
|
||||
will get the value of
|
||||
.Ic \[rs]3 ,
|
||||
.Ic \e3 ,
|
||||
and so forth.
|
||||
.Pp
|
||||
.Ic SHIFT
|
||||
@@ -1715,9 +1725,9 @@ and
|
||||
commands print text and values to the standard output.
|
||||
Useful for debugging macros, or wherever you may feel the need to tell yourself some important information.
|
||||
.Bd -literal -offset indent
|
||||
PRINT "Hello world!\[rs]n"
|
||||
PRINT "Hello world!\en"
|
||||
PRINTLN "Hello world!"
|
||||
PRINT _NARG, " arguments\[rs]n"
|
||||
PRINT _NARG, " arguments\en"
|
||||
PRINTLN "sum: ", 2+3, " product: ", 2*3
|
||||
PRINTLN "Line #", __LINE__
|
||||
PRINTLN STRFMT("E = %f", 2.718)
|
||||
@@ -1731,7 +1741,7 @@ For different formats, use
|
||||
.Ic STRFMT .
|
||||
.It Ic PRINTLN
|
||||
prints out each of its comma-separated arguments, if any, followed by a line feed
|
||||
.Pq Ql \[rs]n .
|
||||
.Pq Ql \en .
|
||||
.El
|
||||
.Ss Automatically repeating blocks of code
|
||||
Suppose you want to unroll a time consuming loop without copy-pasting it.
|
||||
@@ -1755,17 +1765,16 @@ You can also use
|
||||
.Ic REPT
|
||||
to generate tables on the fly:
|
||||
.Bd -literal -offset indent
|
||||
; Generate a 256-byte sine table with values in the range [0, 128]
|
||||
; (shifted and scaled from the range [-1.0, 1.0])
|
||||
ANGLE = 0.0
|
||||
REPT 256
|
||||
db (MUL(64.0, SIN(ANGLE)) + 64.0) >> 16
|
||||
ANGLE = ANGLE + 256.0 ; 256.0 = 65536 degrees / 256 entries
|
||||
ENDR
|
||||
; Generate a table of square values from 0**2 = 0 to 100**2 = 10000
|
||||
DEF x = 0
|
||||
REPT 101
|
||||
dw x * x
|
||||
DEF x += 1
|
||||
ENDR
|
||||
.Ed
|
||||
.Pp
|
||||
As in macros, you can also use the escape sequence
|
||||
.Ic \[rs]@ .
|
||||
.Ic \e@ .
|
||||
.Ic REPT
|
||||
blocks can be nested.
|
||||
.Pp
|
||||
@@ -1853,7 +1862,7 @@ This will print:
|
||||
Just like with
|
||||
.Ic REPT
|
||||
blocks, you can use the escape sequence
|
||||
.Ic \[rs]@
|
||||
.Ic \e@
|
||||
inside of
|
||||
.Ic FOR
|
||||
blocks, and they can be nested.
|
||||
@@ -1974,6 +1983,13 @@ calls infinitely (or until you run out of memory, whichever comes first).
|
||||
.Bd -literal -offset indent
|
||||
INCLUDE "irq.inc"
|
||||
.Ed
|
||||
.Pp
|
||||
You may also implicitly
|
||||
.Ic INCLUDE
|
||||
a file before the source file with the
|
||||
.Fl P
|
||||
option of
|
||||
.Xr rgbasm 1 .
|
||||
.Ss Conditional assembling
|
||||
The four commands
|
||||
.Ic IF , ELIF , ELSE ,
|
||||
|
||||
51
man/rgbgfx.1
51
man/rgbgfx.1
@@ -14,7 +14,6 @@
|
||||
.Nd Game Boy graphics converter
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl r Ar stride
|
||||
.Op Fl CmuVZ
|
||||
.Op Fl v Op Fl v No ...
|
||||
.Op Fl a Ar attrmap | Fl A
|
||||
@@ -27,6 +26,7 @@
|
||||
.Op Fl o Ar out_file
|
||||
.Op Fl p Ar pal_file | Fl P
|
||||
.Op Fl q Ar pal_map | Fl Q
|
||||
.Op Fl r Ar stride
|
||||
.Op Fl s Ar nb_colors
|
||||
.Op Fl t Ar tilemap | Fl T
|
||||
.Op Fl x Ar quantity
|
||||
@@ -321,7 +321,22 @@ any command-line argument that begins with an at sign
|
||||
.Pq Ql @
|
||||
is interpreted as one.
|
||||
The rest of the argument (without the @, that is) is interpreted as the path to a file, whose contents are interpreted as if given on the command line.
|
||||
At-files can be stored right next to the corresponding image, for example.
|
||||
At-files can be stored right next to the corresponding image, for example:
|
||||
.Pp
|
||||
.Dl $ rgbgfx -o image.2bpp -t image.tilemap @image.flags image.png
|
||||
.Pp
|
||||
This will read additional flags from file
|
||||
.Ql image.flags ,
|
||||
which could contains for example
|
||||
.Ql -b 128
|
||||
to specify a base offset for the image's tiles.
|
||||
The above command could be generated from the following
|
||||
.Xr make 1
|
||||
rule, for example:
|
||||
.Bd -literal -offset indent
|
||||
%.2bpp %.tilemap: %.flags %.png
|
||||
rgbgfx -o $*.2bpp -t $*.tilemap @$*.flags $*.png
|
||||
.Ed
|
||||
.Pp
|
||||
Since the contents of at-files are interpreted by
|
||||
.Nm ,
|
||||
@@ -336,17 +351,19 @@ optionally preceded by whitespace, are considered comments and also ignored.
|
||||
Each line can contain any number of arguments, which are separated by whitespace.
|
||||
.Pq \&No quoting feature to prevent this is provided.
|
||||
.Pp
|
||||
Note that this special meaning given to arguments has less precedence than option arguments, and that the standard
|
||||
Note that a leading
|
||||
.Ql @
|
||||
has no special meaning on option arguments, and that the standard
|
||||
.Ql --
|
||||
to stop option processing also disables at-file processing.
|
||||
For example, the following command line processes
|
||||
.Ql @tilesets/town.png ,
|
||||
outputs tile data to
|
||||
.Ql @tilesets/town.2bpp ,
|
||||
and reads command-line options from
|
||||
For example, the following command line reads command-line options from
|
||||
.Ql tilesets/town.flags
|
||||
then
|
||||
.Ql tilesets.flags :
|
||||
.Ql tilesets.flags ,
|
||||
but processes
|
||||
.Ql @tilesets/town.png
|
||||
as the input image and outputs tile data to
|
||||
.Ql @tilesets/town.2bpp :
|
||||
.Pp
|
||||
.Dl $ rgbgfx -o @tilesets/town.2bpp @tilesets/town.flags @tilesets.flags -- @tilesets/town.png
|
||||
.Pp
|
||||
@@ -357,11 +374,21 @@ can be used in an at-file (with identical semantics), it is only effective insid
|
||||
.Sh PALETTE SPECIFICATION FORMATS
|
||||
The following formats are supported:
|
||||
.Bl -tag -width Ds
|
||||
.It Sy act
|
||||
.It Cm act
|
||||
.Lk https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_pgfId-1070626 Adobe Photoshop color table .
|
||||
.It Sy aco
|
||||
.It Cm aco
|
||||
.Lk https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_pgfId-1055819 Adobe Photoshop color swatch .
|
||||
.It Sy psp
|
||||
.It Cm gbc
|
||||
A GBC palette memory dump, as emitted by
|
||||
.Nm Fl p .
|
||||
Useful to force several images to share the same palette.
|
||||
.It Cm gpl
|
||||
.Lk https://docs.gimp.org/2.10/en/gimp-concepts-palettes.html GIMP palette .
|
||||
.It Cm hex
|
||||
Plaintext lines of hexadecimal colors in
|
||||
.Ql rrggbb
|
||||
format.
|
||||
.It Cm psp
|
||||
.Lk https://www.selapa.net/swatches/colors/fileformats.php#psp_pal Paint Shop Pro palette .
|
||||
.El
|
||||
.Pp
|
||||
|
||||
@@ -54,12 +54,12 @@ it is needed to specify a bank number after the type.
|
||||
Section names in double quotes support the same character escape sequences as strings in
|
||||
.Xr rgbasm 5 ,
|
||||
specifically
|
||||
.Ql \[rs]\[rs] ,
|
||||
.Ql \[rs]" ,
|
||||
.Ql \[rs]n ,
|
||||
.Ql \[rs]r ,
|
||||
.Ql \e\e ,
|
||||
.Ql \e" ,
|
||||
.Ql \en ,
|
||||
.Ql \er ,
|
||||
and
|
||||
.Ql \[rs]t .
|
||||
.Ql \et .
|
||||
Other backslash escape sequences in
|
||||
.Xr rgbasm 5
|
||||
are only relevant to assembly code and do not apply in section names.
|
||||
|
||||
@@ -84,6 +84,7 @@ set(rgblink_src
|
||||
"link/sdas_obj.c"
|
||||
"link/section.c"
|
||||
"link/symbol.c"
|
||||
"extern/utf8decoder.c"
|
||||
"hashmap.c"
|
||||
"linkdefs.c"
|
||||
"opmath.c"
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
struct Charnode {
|
||||
bool isTerminal; // Whether there exists a mapping that ends here
|
||||
uint8_t value; // If the above is true, its corresponding value
|
||||
// This MUST be indexes and not pointers, because pointers get invalidated by `realloc`!!
|
||||
// This MUST be indexes and not pointers, because pointers get invalidated by `realloc`!
|
||||
size_t next[255]; // Indexes of where to go next, 0 = nowhere
|
||||
};
|
||||
|
||||
@@ -249,11 +249,14 @@ size_t charmap_ConvertNext(char const **input, uint8_t **output)
|
||||
if (output)
|
||||
*output += codepointLen;
|
||||
|
||||
// Check if the character map is not the default "main" one, or if
|
||||
// it has any mappings defined
|
||||
if (strcmp(charmap->name, "main") || charmap->usedNodes > 1)
|
||||
warning(WARNING_UNMAPPED_CHAR,
|
||||
// Warn if this character is not mapped but any others are
|
||||
if (charmap->usedNodes > 1)
|
||||
warning(WARNING_UNMAPPED_CHAR_1,
|
||||
"Unmapped character %s\n", printChar(firstChar));
|
||||
else if (strcmp(charmap->name, DEFAULT_CHARMAP_NAME))
|
||||
warning(WARNING_UNMAPPED_CHAR_2,
|
||||
"Unmapped character %s not in " DEFAULT_CHARMAP_NAME
|
||||
" charmap\n", printChar(firstChar));
|
||||
|
||||
return codepointLen;
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "asm/fixpoint.h"
|
||||
#include "asm/symbol.h"
|
||||
@@ -21,105 +20,96 @@
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
#define fix2double(i) ((double)((i) / fix_PrecisionFactor()))
|
||||
#define double2fix(d) ((int32_t)round((d) * fix_PrecisionFactor()))
|
||||
#define fix2double(i, q) ((double)((i) / pow(2.0, q)))
|
||||
#define double2fix(d, q) ((int32_t)round((d) * pow(2.0, q)))
|
||||
|
||||
// pi*2 radians == 2**fixPrecision fixed-point "degrees"
|
||||
#define fdeg2rad(f) ((f) * (M_PI * 2) / fix_PrecisionFactor())
|
||||
#define rad2fdeg(r) ((r) * fix_PrecisionFactor() / (M_PI * 2))
|
||||
// 2*pi radians == 1 turn
|
||||
#define turn2rad(f) ((f) * (M_PI * 2))
|
||||
#define rad2turn(r) ((r) / (M_PI * 2))
|
||||
|
||||
uint8_t fixPrecision;
|
||||
|
||||
uint8_t fix_Precision(void)
|
||||
{
|
||||
return fixPrecision;
|
||||
}
|
||||
|
||||
double fix_PrecisionFactor(void)
|
||||
{
|
||||
return pow(2.0, fixPrecision);
|
||||
}
|
||||
|
||||
void fix_Print(int32_t i)
|
||||
int32_t fix_Sin(int32_t i, int32_t q)
|
||||
{
|
||||
uint32_t u = i;
|
||||
char const *sign = "";
|
||||
|
||||
if (i < 0) {
|
||||
u = -u;
|
||||
sign = "-";
|
||||
}
|
||||
|
||||
printf("%s%" PRIu32 ".%05" PRIu32, sign, u >> fixPrecision,
|
||||
((uint32_t)(fix2double(u) * 100000 + 0.5)) % 100000);
|
||||
return double2fix(sin(turn2rad(fix2double(i, q))), q);
|
||||
}
|
||||
|
||||
int32_t fix_Sin(int32_t i)
|
||||
int32_t fix_Cos(int32_t i, int32_t q)
|
||||
{
|
||||
return double2fix(sin(fdeg2rad(fix2double(i))));
|
||||
return double2fix(cos(turn2rad(fix2double(i, q))), q);
|
||||
}
|
||||
|
||||
int32_t fix_Cos(int32_t i)
|
||||
int32_t fix_Tan(int32_t i, int32_t q)
|
||||
{
|
||||
return double2fix(cos(fdeg2rad(fix2double(i))));
|
||||
return double2fix(tan(turn2rad(fix2double(i, q))), q);
|
||||
}
|
||||
|
||||
int32_t fix_Tan(int32_t i)
|
||||
int32_t fix_ASin(int32_t i, int32_t q)
|
||||
{
|
||||
return double2fix(tan(fdeg2rad(fix2double(i))));
|
||||
return double2fix(rad2turn(asin(fix2double(i, q))), q);
|
||||
}
|
||||
|
||||
int32_t fix_ASin(int32_t i)
|
||||
int32_t fix_ACos(int32_t i, int32_t q)
|
||||
{
|
||||
return double2fix(rad2fdeg(asin(fix2double(i))));
|
||||
return double2fix(rad2turn(acos(fix2double(i, q))), q);
|
||||
}
|
||||
|
||||
int32_t fix_ACos(int32_t i)
|
||||
int32_t fix_ATan(int32_t i, int32_t q)
|
||||
{
|
||||
return double2fix(rad2fdeg(acos(fix2double(i))));
|
||||
return double2fix(rad2turn(atan(fix2double(i, q))), q);
|
||||
}
|
||||
|
||||
int32_t fix_ATan(int32_t i)
|
||||
int32_t fix_ATan2(int32_t i, int32_t j, int32_t q)
|
||||
{
|
||||
return double2fix(rad2fdeg(atan(fix2double(i))));
|
||||
return double2fix(rad2turn(atan2(fix2double(i, q), fix2double(j, q))), q);
|
||||
}
|
||||
|
||||
int32_t fix_ATan2(int32_t i, int32_t j)
|
||||
int32_t fix_Mul(int32_t i, int32_t j, int32_t q)
|
||||
{
|
||||
return double2fix(rad2fdeg(atan2(fix2double(i), fix2double(j))));
|
||||
return double2fix(fix2double(i, q) * fix2double(j, q), q);
|
||||
}
|
||||
|
||||
int32_t fix_Mul(int32_t i, int32_t j)
|
||||
int32_t fix_Div(int32_t i, int32_t j, int32_t q)
|
||||
{
|
||||
return double2fix(fix2double(i) * fix2double(j));
|
||||
return double2fix(fix2double(i, q) / fix2double(j, q), q);
|
||||
}
|
||||
|
||||
int32_t fix_Div(int32_t i, int32_t j)
|
||||
int32_t fix_Mod(int32_t i, int32_t j, int32_t q)
|
||||
{
|
||||
return double2fix(fix2double(i) / fix2double(j));
|
||||
return double2fix(fmod(fix2double(i, q), fix2double(j, q)), q);
|
||||
}
|
||||
|
||||
int32_t fix_Mod(int32_t i, int32_t j)
|
||||
int32_t fix_Pow(int32_t i, int32_t j, int32_t q)
|
||||
{
|
||||
return double2fix(fmod(fix2double(i), fix2double(j)));
|
||||
return double2fix(pow(fix2double(i, q), fix2double(j, q)), q);
|
||||
}
|
||||
|
||||
int32_t fix_Pow(int32_t i, int32_t j)
|
||||
int32_t fix_Log(int32_t i, int32_t j, int32_t q)
|
||||
{
|
||||
return double2fix(pow(fix2double(i), fix2double(j)));
|
||||
return double2fix(log(fix2double(i, q)) / log(fix2double(j, q)), q);
|
||||
}
|
||||
|
||||
int32_t fix_Log(int32_t i, int32_t j)
|
||||
int32_t fix_Round(int32_t i, int32_t q)
|
||||
{
|
||||
return double2fix(log(fix2double(i)) / log(fix2double(j)));
|
||||
return double2fix(round(fix2double(i, q)), q);
|
||||
}
|
||||
|
||||
int32_t fix_Round(int32_t i)
|
||||
int32_t fix_Ceil(int32_t i, int32_t q)
|
||||
{
|
||||
return double2fix(round(fix2double(i)));
|
||||
return double2fix(ceil(fix2double(i, q)), q);
|
||||
}
|
||||
|
||||
int32_t fix_Ceil(int32_t i)
|
||||
int32_t fix_Floor(int32_t i, int32_t q)
|
||||
{
|
||||
return double2fix(ceil(fix2double(i)));
|
||||
}
|
||||
|
||||
int32_t fix_Floor(int32_t i)
|
||||
{
|
||||
return double2fix(floor(fix2double(i)));
|
||||
return double2fix(floor(fix2double(i, q)), q);
|
||||
}
|
||||
|
||||
@@ -249,7 +249,7 @@ void fmt_PrintNumber(char *buf, size_t bufLen, struct FormatSpec const *fmt, uin
|
||||
}
|
||||
|
||||
size_t len = strlen(valueBuf);
|
||||
size_t numLen = !!sign + !!prefix + len;
|
||||
size_t numLen = (sign != 0) + (prefix != 0) + len;
|
||||
size_t totalLen = fmt->width > numLen ? fmt->width : numLen;
|
||||
|
||||
if (totalLen > bufLen - 1) { // bufLen includes terminator
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "asm/main.h"
|
||||
#include "asm/symbol.h"
|
||||
#include "asm/warning.h"
|
||||
#include "error.h"
|
||||
#include "platform.h" // S_ISDIR (stat macro)
|
||||
|
||||
#define MAXINCPATHS 128
|
||||
@@ -42,6 +43,8 @@ size_t maxRecursionDepth;
|
||||
static unsigned int nbIncPaths = 0;
|
||||
static char const *includePaths[MAXINCPATHS];
|
||||
|
||||
static const char *preIncludeName;
|
||||
|
||||
static const char *dumpNodeAndParents(struct FileStackNode const *node)
|
||||
{
|
||||
char const *name;
|
||||
@@ -133,6 +136,15 @@ void fstk_AddIncludePath(char const *path)
|
||||
includePaths[nbIncPaths++] = str;
|
||||
}
|
||||
|
||||
void fstk_SetPreIncludeFile(char const *path)
|
||||
{
|
||||
if (preIncludeName)
|
||||
warnx("Overriding pre-included filename %s", preIncludeName);
|
||||
preIncludeName = path;
|
||||
if (verbose)
|
||||
printf("Pre-included filename %s\n", preIncludeName);
|
||||
}
|
||||
|
||||
static void printDep(char const *path)
|
||||
{
|
||||
if (dependfile) {
|
||||
@@ -274,11 +286,12 @@ bool yywrap(void)
|
||||
|
||||
lexer_SetState(contextStack->lexerState);
|
||||
macro_SetUniqueID(contextStack->uniqueID);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure not to switch the lexer state before calling this, so the saved line no is correct.
|
||||
// BE CAREFUL!! This modifies the file stack directly, you should have set up the file info first.
|
||||
// BE CAREFUL! This modifies the file stack directly, you should have set up the file info first.
|
||||
// Callers should set contextStack->lexerState after this so it is not NULL.
|
||||
static void newContext(struct FileStackNode *fileInfo)
|
||||
{
|
||||
@@ -300,7 +313,7 @@ static void newContext(struct FileStackNode *fileInfo)
|
||||
context->forName = NULL;
|
||||
|
||||
// Link new entry to its parent so it's reachable later
|
||||
// ERRORS SHOULD NOT OCCUR AFTER THIS!!
|
||||
// ERRORS SHOULD NOT OCCUR AFTER THIS!
|
||||
context->parent = contextStack;
|
||||
contextStack = context;
|
||||
}
|
||||
@@ -342,6 +355,41 @@ void fstk_RunInclude(char const *path)
|
||||
contextStack->uniqueID = macro_UndefUniqueID();
|
||||
}
|
||||
|
||||
// Similar to `fstk_RunInclude`, but not subject to `-MG`, and
|
||||
// calling `lexer_SetState` instead of `lexer_SetStateAtEOL`.
|
||||
static void runPreIncludeFile(void)
|
||||
{
|
||||
if (!preIncludeName)
|
||||
return;
|
||||
|
||||
char *fullPath = NULL;
|
||||
size_t size = 0;
|
||||
|
||||
if (!fstk_FindFile(preIncludeName, &fullPath, &size)) {
|
||||
free(fullPath);
|
||||
error("Unable to open included file '%s': %s\n", preIncludeName, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + size);
|
||||
|
||||
if (!fileInfo) {
|
||||
error("Failed to alloc file info for pre-include: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
fileInfo->node.type = NODE_FILE;
|
||||
strcpy(fileInfo->name, fullPath);
|
||||
free(fullPath);
|
||||
|
||||
newContext((struct FileStackNode *)fileInfo);
|
||||
contextStack->lexerState = lexer_OpenFile(fileInfo->name);
|
||||
if (!contextStack->lexerState)
|
||||
fatalerror("Failed to set up lexer for file include\n");
|
||||
lexer_SetState(contextStack->lexerState);
|
||||
// We're back at top-level, so most things are reset
|
||||
contextStack->uniqueID = macro_UndefUniqueID();
|
||||
}
|
||||
|
||||
void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
|
||||
{
|
||||
struct Symbol *macro = sym_FindExactSymbol(macroName);
|
||||
@@ -563,4 +611,6 @@ void fstk_Init(char const *mainPath, size_t maxDepth)
|
||||
// Make sure that the default of 64 is OK, though
|
||||
assert(DEPTH_LIMIT >= DEFAULT_MAX_DEPTH);
|
||||
#undef DEPTH_LIMIT
|
||||
|
||||
runPreIncludeFile();
|
||||
}
|
||||
|
||||
@@ -733,7 +733,7 @@ static uint32_t readBracketedMacroArgNum(void)
|
||||
}
|
||||
symName[i] = '\0';
|
||||
|
||||
struct Symbol const *sym = sym_FindScopedSymbol(symName);
|
||||
struct Symbol const *sym = sym_FindScopedValidSymbol(symName);
|
||||
|
||||
if (!sym) {
|
||||
error("Bracketed symbol \"%s\" does not exist\n", symName);
|
||||
@@ -1179,7 +1179,7 @@ static uint32_t readFractionalPart(uint32_t integer)
|
||||
precision = fixPrecision;
|
||||
}
|
||||
|
||||
if (integer >= ((uint32_t)1 << (precision - 1)))
|
||||
if (integer >= ((uint64_t)1 << (32 - precision)))
|
||||
warning(WARNING_LARGE_CONSTANT, "Magnitude of fixed-point constant is too large\n");
|
||||
|
||||
// Cast to unsigned avoids undefined overflow behavior
|
||||
@@ -1400,7 +1400,7 @@ static char const *readInterpolation(size_t depth)
|
||||
|
||||
static char buf[MAXSTRLEN + 1];
|
||||
|
||||
struct Symbol const *sym = sym_FindScopedSymbol(symName);
|
||||
struct Symbol const *sym = sym_FindScopedValidSymbol(symName);
|
||||
|
||||
if (!sym) {
|
||||
error("Interpolated symbol \"%s\" does not exist\n", symName);
|
||||
|
||||
@@ -53,11 +53,11 @@ char const *__asan_default_options(void) { return "detect_leaks=0"; }
|
||||
// Unfortunately, macOS still ships 2.3, which is from 2008...
|
||||
int yyparse(void);
|
||||
|
||||
FILE * dependfile;
|
||||
bool generatedMissingIncludes;
|
||||
bool failedOnMissingInclude;
|
||||
bool generatePhonyDeps;
|
||||
char *targetFileName;
|
||||
FILE *dependfile = NULL;
|
||||
bool generatedMissingIncludes = false;
|
||||
bool failedOnMissingInclude = false;
|
||||
bool generatePhonyDeps = false;
|
||||
char *targetFileName = NULL;
|
||||
|
||||
bool haltnop;
|
||||
bool warnOnHaltNop;
|
||||
@@ -87,7 +87,7 @@ static char *make_escape(char const *str)
|
||||
}
|
||||
|
||||
// Short options
|
||||
static const char *optstring = "b:D:Eg:Hhi:LlM:o:p:Q:r:VvW:w";
|
||||
static const char *optstring = "b:D:Eg:Hhi:I:LlM:o:P:p:Q:r:VvW:w";
|
||||
|
||||
// Variables for the long-only options
|
||||
static int depType; // Variants of `-M`
|
||||
@@ -107,7 +107,7 @@ static struct option const longopts[] = {
|
||||
{ "gfx-chars", required_argument, NULL, 'g' },
|
||||
{ "nop-after-halt", no_argument, NULL, 'H' },
|
||||
{ "halt-without-nop", no_argument, NULL, 'h' },
|
||||
{ "include", required_argument, NULL, 'i' },
|
||||
{ "include", required_argument, NULL, 'I' },
|
||||
{ "preserve-ld", no_argument, NULL, 'L' },
|
||||
{ "auto-ldh", no_argument, NULL, 'l' },
|
||||
{ "dependfile", required_argument, NULL, 'M' },
|
||||
@@ -116,6 +116,7 @@ static struct option const longopts[] = {
|
||||
{ "MT", required_argument, &depType, 'T' },
|
||||
{ "MQ", required_argument, &depType, 'Q' },
|
||||
{ "output", required_argument, NULL, 'o' },
|
||||
{ "preinclude", required_argument, NULL, 'P' },
|
||||
{ "pad-value", required_argument, NULL, 'p' },
|
||||
{ "q-precision", required_argument, NULL, 'Q' },
|
||||
{ "recursion-depth", required_argument, NULL, 'r' },
|
||||
@@ -128,10 +129,10 @@ static struct option const longopts[] = {
|
||||
static void print_usage(void)
|
||||
{
|
||||
fputs(
|
||||
"Usage: rgbasm [-EHhLlVvw] [-b chars] [-D name[=value]] [-g chars] [-i path]\n"
|
||||
"Usage: rgbasm [-EHhLlVvw] [-b chars] [-D name[=value]] [-g chars] [-I path]\n"
|
||||
" [-M depend_file] [-MG] [-MP] [-MT target_file] [-MQ target_file]\n"
|
||||
" [-o out_file] [-p pad_value] [-Q precision] [-r depth]\n"
|
||||
" [-W warning] <file>\n"
|
||||
" [-o out_file] [-P include_file] [-p pad_value] [-Q precision]\n"
|
||||
" [-r depth] [-W warning] <file>\n"
|
||||
"Useful options:\n"
|
||||
" -E, --export-all export all labels\n"
|
||||
" -M, --dependfile <path> set the output dependency file\n"
|
||||
@@ -147,9 +148,6 @@ static void print_usage(void)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ch;
|
||||
char *ep;
|
||||
|
||||
time_t now = time(NULL);
|
||||
char const *sourceDateEpoch = getenv("SOURCE_DATE_EPOCH");
|
||||
|
||||
@@ -158,18 +156,11 @@ int main(int argc, char *argv[])
|
||||
if (sourceDateEpoch)
|
||||
now = (time_t)strtoul(sourceDateEpoch, NULL, 0);
|
||||
|
||||
dependfile = NULL;
|
||||
|
||||
// Perform some init for below
|
||||
sym_Init(now);
|
||||
|
||||
// Set defaults
|
||||
|
||||
generatePhonyDeps = false;
|
||||
generatedMissingIncludes = false;
|
||||
failedOnMissingInclude = false;
|
||||
targetFileName = NULL;
|
||||
|
||||
opt_B("01");
|
||||
opt_G("0123");
|
||||
opt_P(0);
|
||||
@@ -182,13 +173,16 @@ int main(int argc, char *argv[])
|
||||
warnings = true;
|
||||
sym_SetExportAll(false);
|
||||
uint32_t maxDepth = DEFAULT_MAX_DEPTH;
|
||||
char *dependFileName = NULL;
|
||||
size_t targetFileNameLen = 0;
|
||||
|
||||
int ch;
|
||||
char *ep;
|
||||
while ((ch = musl_getopt_long_only(argc, argv, optstring, longopts, NULL)) != -1) {
|
||||
switch (ch) {
|
||||
case 'b':
|
||||
if (strlen(musl_optarg) == 2)
|
||||
opt_B(&musl_optarg[1]);
|
||||
opt_B(musl_optarg);
|
||||
else
|
||||
errx("Must specify exactly 2 characters for option 'b'");
|
||||
break;
|
||||
@@ -210,7 +204,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
case 'g':
|
||||
if (strlen(musl_optarg) == 4)
|
||||
opt_G(&musl_optarg[1]);
|
||||
opt_G(musl_optarg);
|
||||
else
|
||||
errx("Must specify exactly 4 characters for option 'g'");
|
||||
break;
|
||||
@@ -226,6 +220,10 @@ int main(int argc, char *argv[])
|
||||
haltnop = false;
|
||||
break;
|
||||
|
||||
// `-i` was the only short option for `--include` until `-I` was
|
||||
// introduced to better match the `-I dir` option of gcc and clang.
|
||||
// `-i` is now undocumented but still supported for now.
|
||||
case 'I':
|
||||
case 'i':
|
||||
fstk_AddIncludePath(musl_optarg);
|
||||
break;
|
||||
@@ -242,18 +240,27 @@ int main(int argc, char *argv[])
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
if (!strcmp("-", musl_optarg))
|
||||
if (dependfile)
|
||||
warnx("Overriding dependfile %s", dependFileName);
|
||||
if (!strcmp("-", musl_optarg)) {
|
||||
dependfile = stdout;
|
||||
else
|
||||
dependFileName = "<stdout>";
|
||||
} else {
|
||||
dependfile = fopen(musl_optarg, "w");
|
||||
dependFileName = musl_optarg;
|
||||
}
|
||||
if (dependfile == NULL)
|
||||
err("Could not open dependfile %s", musl_optarg);
|
||||
err("Could not open dependfile %s", dependFileName);
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
out_SetFileName(musl_optarg);
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
fstk_SetPreIncludeFile(musl_optarg);
|
||||
break;
|
||||
|
||||
unsigned long padByte;
|
||||
case 'p':
|
||||
padByte = strtoul(musl_optarg, &ep, 0);
|
||||
@@ -371,7 +378,7 @@ int main(int argc, char *argv[])
|
||||
fprintf(dependfile, "%s: %s\n", targetFileName, mainFileName);
|
||||
}
|
||||
|
||||
charmap_New("main", NULL);
|
||||
charmap_New(DEFAULT_CHARMAP_NAME, NULL);
|
||||
|
||||
// Init lexer and file stack, providing file info
|
||||
lexer_Init();
|
||||
|
||||
@@ -492,7 +492,7 @@ void out_WriteObject(void)
|
||||
if (strcmp(objectName, "-") != 0)
|
||||
f = fopen(objectName, "wb");
|
||||
else
|
||||
f = fdopen(1, "wb");
|
||||
f = fdopen(STDOUT_FILENO, "wb");
|
||||
|
||||
if (!f)
|
||||
err("Couldn't write file '%s'", objectName);
|
||||
@@ -540,7 +540,9 @@ void out_WriteObject(void)
|
||||
// Set the objectfilename
|
||||
void out_SetFileName(char *s)
|
||||
{
|
||||
if (objectName)
|
||||
warnx("Overriding output filename %s", objectName);
|
||||
objectName = s;
|
||||
if (verbose)
|
||||
printf("Output filename %s\n", s);
|
||||
printf("Output filename %s\n", objectName);
|
||||
}
|
||||
|
||||
@@ -550,6 +550,7 @@ enum {
|
||||
%token T_OP_BANK "BANK"
|
||||
%token T_OP_ALIGN "ALIGN"
|
||||
%token T_OP_SIZEOF "SIZEOF" T_OP_STARTOF "STARTOF"
|
||||
|
||||
%token T_OP_SIN "SIN" T_OP_COS "COS" T_OP_TAN "TAN"
|
||||
%token T_OP_ASIN "ASIN" T_OP_ACOS "ACOS" T_OP_ATAN "ATAN" T_OP_ATAN2 "ATAN2"
|
||||
%token T_OP_FDIV "FDIV"
|
||||
@@ -559,6 +560,7 @@ enum {
|
||||
%token T_OP_LOG "LOG"
|
||||
%token T_OP_ROUND "ROUND"
|
||||
%token T_OP_CEIL "CEIL" T_OP_FLOOR "FLOOR"
|
||||
%type <constValue> opt_q_arg
|
||||
|
||||
%token T_OP_HIGH "HIGH" T_OP_LOW "LOW"
|
||||
%token T_OP_ISCONST "ISCONST"
|
||||
@@ -1462,56 +1464,54 @@ relocexpr_no_str : scoped_anon_id { rpn_Symbol(&$$, $1); }
|
||||
| T_OP_DEF {
|
||||
lexer_ToggleStringExpansion(false);
|
||||
} T_LPAREN scoped_anon_id T_RPAREN {
|
||||
struct Symbol const *sym = sym_FindScopedSymbol($4);
|
||||
|
||||
rpn_Number(&$$, !!sym);
|
||||
rpn_Number(&$$, sym_FindScopedValidSymbol($4) != NULL);
|
||||
|
||||
lexer_ToggleStringExpansion(true);
|
||||
}
|
||||
| T_OP_ROUND T_LPAREN const T_RPAREN {
|
||||
rpn_Number(&$$, fix_Round($3));
|
||||
| T_OP_ROUND T_LPAREN const opt_q_arg T_RPAREN {
|
||||
rpn_Number(&$$, fix_Round($3, $4));
|
||||
}
|
||||
| T_OP_CEIL T_LPAREN const T_RPAREN {
|
||||
rpn_Number(&$$, fix_Ceil($3));
|
||||
| T_OP_CEIL T_LPAREN const opt_q_arg T_RPAREN {
|
||||
rpn_Number(&$$, fix_Ceil($3, $4));
|
||||
}
|
||||
| T_OP_FLOOR T_LPAREN const T_RPAREN {
|
||||
rpn_Number(&$$, fix_Floor($3));
|
||||
| T_OP_FLOOR T_LPAREN const opt_q_arg T_RPAREN {
|
||||
rpn_Number(&$$, fix_Floor($3, $4));
|
||||
}
|
||||
| T_OP_FDIV T_LPAREN const T_COMMA const T_RPAREN {
|
||||
rpn_Number(&$$, fix_Div($3, $5));
|
||||
| T_OP_FDIV T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
|
||||
rpn_Number(&$$, fix_Div($3, $5, $6));
|
||||
}
|
||||
| T_OP_FMUL T_LPAREN const T_COMMA const T_RPAREN {
|
||||
rpn_Number(&$$, fix_Mul($3, $5));
|
||||
| T_OP_FMUL T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
|
||||
rpn_Number(&$$, fix_Mul($3, $5, $6));
|
||||
}
|
||||
| T_OP_FMOD T_LPAREN const T_COMMA const T_RPAREN {
|
||||
rpn_Number(&$$, fix_Mod($3, $5));
|
||||
| T_OP_FMOD T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
|
||||
rpn_Number(&$$, fix_Mod($3, $5, $6));
|
||||
}
|
||||
| T_OP_POW T_LPAREN const T_COMMA const T_RPAREN {
|
||||
rpn_Number(&$$, fix_Pow($3, $5));
|
||||
| T_OP_POW T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
|
||||
rpn_Number(&$$, fix_Pow($3, $5, $6));
|
||||
}
|
||||
| T_OP_LOG T_LPAREN const T_COMMA const T_RPAREN {
|
||||
rpn_Number(&$$, fix_Log($3, $5));
|
||||
| T_OP_LOG T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
|
||||
rpn_Number(&$$, fix_Log($3, $5, $6));
|
||||
}
|
||||
| T_OP_SIN T_LPAREN const T_RPAREN {
|
||||
rpn_Number(&$$, fix_Sin($3));
|
||||
| T_OP_SIN T_LPAREN const opt_q_arg T_RPAREN {
|
||||
rpn_Number(&$$, fix_Sin($3, $4));
|
||||
}
|
||||
| T_OP_COS T_LPAREN const T_RPAREN {
|
||||
rpn_Number(&$$, fix_Cos($3));
|
||||
| T_OP_COS T_LPAREN const opt_q_arg T_RPAREN {
|
||||
rpn_Number(&$$, fix_Cos($3, $4));
|
||||
}
|
||||
| T_OP_TAN T_LPAREN const T_RPAREN {
|
||||
rpn_Number(&$$, fix_Tan($3));
|
||||
| T_OP_TAN T_LPAREN const opt_q_arg T_RPAREN {
|
||||
rpn_Number(&$$, fix_Tan($3, $4));
|
||||
}
|
||||
| T_OP_ASIN T_LPAREN const T_RPAREN {
|
||||
rpn_Number(&$$, fix_ASin($3));
|
||||
| T_OP_ASIN T_LPAREN const opt_q_arg T_RPAREN {
|
||||
rpn_Number(&$$, fix_ASin($3, $4));
|
||||
}
|
||||
| T_OP_ACOS T_LPAREN const T_RPAREN {
|
||||
rpn_Number(&$$, fix_ACos($3));
|
||||
| T_OP_ACOS T_LPAREN const opt_q_arg T_RPAREN {
|
||||
rpn_Number(&$$, fix_ACos($3, $4));
|
||||
}
|
||||
| T_OP_ATAN T_LPAREN const T_RPAREN {
|
||||
rpn_Number(&$$, fix_ATan($3));
|
||||
| T_OP_ATAN T_LPAREN const opt_q_arg T_RPAREN {
|
||||
rpn_Number(&$$, fix_ATan($3, $4));
|
||||
}
|
||||
| T_OP_ATAN2 T_LPAREN const T_COMMA const T_RPAREN {
|
||||
rpn_Number(&$$, fix_ATan2($3, $5));
|
||||
| T_OP_ATAN2 T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
|
||||
rpn_Number(&$$, fix_ATan2($3, $5, $6));
|
||||
}
|
||||
| T_OP_STRCMP T_LPAREN string T_COMMA string T_RPAREN {
|
||||
rpn_Number(&$$, strcmp($3, $5));
|
||||
@@ -1538,7 +1538,7 @@ relocexpr_no_str : scoped_anon_id { rpn_Symbol(&$$, $1); }
|
||||
uconst : const {
|
||||
$$ = $1;
|
||||
if ($$ < 0)
|
||||
fatalerror("Constant mustn't be negative: %d\n", $1);
|
||||
fatalerror("Constant must not be negative: %d\n", $1);
|
||||
}
|
||||
;
|
||||
|
||||
@@ -1551,6 +1551,17 @@ const_no_str : relocexpr_no_str { $$ = rpn_GetConstVal(&$1); }
|
||||
const_8bit : reloc_8bit { $$ = rpn_GetConstVal(&$1); }
|
||||
;
|
||||
|
||||
opt_q_arg : %empty { $$ = fix_Precision(); }
|
||||
| T_COMMA const {
|
||||
if ($2 >= 1 && $2 <= 31) {
|
||||
$$ = $2;
|
||||
} else {
|
||||
error("Fixed-point precision must be between 1 and 31\n");
|
||||
$$ = fix_Precision();
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
string : T_STRING
|
||||
| T_OP_STRSUB T_LPAREN string T_COMMA const T_COMMA uconst T_RPAREN {
|
||||
size_t len = strlenUTF8($3);
|
||||
@@ -1589,6 +1600,19 @@ string : T_STRING
|
||||
strfmt($$, sizeof($$), $3.format, $3.nbArgs, $3.args);
|
||||
freeStrFmtArgList(&$3);
|
||||
}
|
||||
| T_POP_SECTION T_LPAREN scoped_anon_id T_RPAREN {
|
||||
struct Symbol *sym = sym_FindScopedValidSymbol($3);
|
||||
|
||||
if (!sym)
|
||||
fatalerror("Unknown symbol \"%s\"\n", $3);
|
||||
struct Section const *section = sym_GetSection(sym);
|
||||
|
||||
if (!section)
|
||||
fatalerror("\"%s\" does not belong to any section\n", sym->name);
|
||||
// Section names are capped by rgbasm's maximum string length,
|
||||
// so this currently can't overflow.
|
||||
strcpy($$, section->name);
|
||||
}
|
||||
;
|
||||
|
||||
strcat_args : string
|
||||
|
||||
@@ -36,6 +36,7 @@ HashMap symbols;
|
||||
|
||||
static const char *labelScope; // Current section's label scope
|
||||
static struct Symbol *PCSymbol;
|
||||
static struct Symbol *_NARGSymbol;
|
||||
static char savedTIME[256];
|
||||
static char savedDATE[256];
|
||||
static char savedTIMESTAMP_ISO8601_LOCAL[256];
|
||||
@@ -78,12 +79,15 @@ static int32_t Callback_NARG(void)
|
||||
|
||||
static int32_t Callback__LINE__(void)
|
||||
{
|
||||
warning(WARNING_OBSOLETE, "`__LINE__` is deprecated\n");
|
||||
|
||||
return lexer_GetLineNo();
|
||||
}
|
||||
|
||||
static char const *Callback__FILE__(void)
|
||||
{
|
||||
// FIXME: this is dangerous, and here's why this is CURRENTLY okay. It's still bad, fix it.
|
||||
warning(WARNING_OBSOLETE, "`__FILE__` is deprecated\n");
|
||||
|
||||
// There are only two call sites for this; one copies the contents directly, the other is
|
||||
// EQUS expansions, which cannot straddle file boundaries. So this should be fine.
|
||||
static char *buf = NULL;
|
||||
@@ -97,7 +101,7 @@ static char const *Callback__FILE__(void)
|
||||
// Account for the extra backslash inserted below
|
||||
if (fileName[i] == '"')
|
||||
j++;
|
||||
// Ensure there will be enough room; DO NOT PRINT ANYTHING ABOVE THIS!!
|
||||
// Ensure there will be enough room; DO NOT PRINT ANYTHING ABOVE THIS!
|
||||
if (j + 2 >= bufsize) { // Always keep room for 2 tail chars
|
||||
bufsize = bufsize ? bufsize * 2 : 64;
|
||||
buf = realloc(buf, bufsize);
|
||||
@@ -247,6 +251,21 @@ struct Symbol *sym_FindScopedSymbol(char const *symName)
|
||||
return sym_FindExactSymbol(symName);
|
||||
}
|
||||
|
||||
struct Symbol *sym_FindScopedValidSymbol(char const *symName)
|
||||
{
|
||||
struct Symbol *sym = sym_FindScopedSymbol(symName);
|
||||
|
||||
// `@` has no value outside a section
|
||||
if (sym == PCSymbol && !sect_GetSymbolSection()) {
|
||||
return NULL;
|
||||
}
|
||||
// `_NARG` has no value outside a macro
|
||||
if (sym == _NARGSymbol && !macro_GetCurrentArgs()) {
|
||||
return NULL;
|
||||
}
|
||||
return sym;
|
||||
}
|
||||
|
||||
struct Symbol const *sym_GetPC(void)
|
||||
{
|
||||
return PCSymbol;
|
||||
@@ -260,7 +279,7 @@ static bool isReferenced(struct Symbol const *sym)
|
||||
// Purge a symbol
|
||||
void sym_Purge(char const *symName)
|
||||
{
|
||||
struct Symbol *sym = sym_FindScopedSymbol(symName);
|
||||
struct Symbol *sym = sym_FindScopedValidSymbol(symName);
|
||||
|
||||
if (!sym) {
|
||||
error("'%s' not defined\n", symName);
|
||||
@@ -571,7 +590,7 @@ struct Symbol *sym_AddAnonLabel(void)
|
||||
}
|
||||
char name[MAXSYMLEN + 1];
|
||||
|
||||
sym_WriteAnonLabelName(name, 0, true); // The direction is important!!
|
||||
sym_WriteAnonLabelName(name, 0, true); // The direction is important!
|
||||
anonLabelID++;
|
||||
return addLabel(name);
|
||||
}
|
||||
@@ -679,8 +698,10 @@ static struct Symbol *createBuiltinSymbol(char const *symName)
|
||||
void sym_Init(time_t now)
|
||||
{
|
||||
PCSymbol = createBuiltinSymbol("@");
|
||||
struct Symbol *_NARGSymbol = createBuiltinSymbol("_NARG");
|
||||
_NARGSymbol = createBuiltinSymbol("_NARG");
|
||||
// __LINE__ is deprecated
|
||||
struct Symbol *__LINE__Symbol = createBuiltinSymbol("__LINE__");
|
||||
// __FILE__ is deprecated
|
||||
struct Symbol *__FILE__Symbol = createBuiltinSymbol("__FILE__");
|
||||
|
||||
PCSymbol->type = SYM_LABEL;
|
||||
|
||||
@@ -38,13 +38,14 @@ static const enum WarningState defaultWarnings[ARRAY_SIZE(warningStates)] = {
|
||||
[WARNING_OBSOLETE] = WARNING_ENABLED,
|
||||
[WARNING_SHIFT] = WARNING_DISABLED,
|
||||
[WARNING_SHIFT_AMOUNT] = WARNING_DISABLED,
|
||||
[WARNING_UNMAPPED_CHAR] = WARNING_ENABLED,
|
||||
[WARNING_USER] = WARNING_ENABLED,
|
||||
|
||||
[WARNING_NUMERIC_STRING_1] = WARNING_ENABLED,
|
||||
[WARNING_NUMERIC_STRING_2] = WARNING_DISABLED,
|
||||
[WARNING_TRUNCATION_1] = WARNING_ENABLED,
|
||||
[WARNING_TRUNCATION_2] = WARNING_DISABLED,
|
||||
[WARNING_UNMAPPED_CHAR_1] = WARNING_ENABLED,
|
||||
[WARNING_UNMAPPED_CHAR_2] = WARNING_DISABLED,
|
||||
};
|
||||
|
||||
enum WarningState warningStates[ARRAY_SIZE(warningStates)];
|
||||
@@ -86,7 +87,6 @@ static const char * const warningFlags[NB_WARNINGS] = {
|
||||
"obsolete",
|
||||
"shift",
|
||||
"shift-amount",
|
||||
"unmapped-char",
|
||||
"user",
|
||||
|
||||
// Parametric warnings
|
||||
@@ -94,6 +94,8 @@ static const char * const warningFlags[NB_WARNINGS] = {
|
||||
"numeric-string",
|
||||
"truncation",
|
||||
"truncation",
|
||||
"unmapped-char",
|
||||
"unmapped-char",
|
||||
|
||||
// Meta warnings
|
||||
"all",
|
||||
@@ -108,6 +110,7 @@ static const struct {
|
||||
} paramWarnings[] = {
|
||||
{ "numeric-string", 2, 1 },
|
||||
{ "truncation", 2, 2 },
|
||||
{ "unmapped-char", 2, 1 },
|
||||
};
|
||||
|
||||
static bool tryProcessParamWarning(char const *flag, uint8_t param, enum WarningState state)
|
||||
@@ -162,8 +165,8 @@ static uint8_t const _wallCommands[] = {
|
||||
WARNING_LONG_STR,
|
||||
WARNING_NESTED_COMMENT,
|
||||
WARNING_OBSOLETE,
|
||||
WARNING_UNMAPPED_CHAR,
|
||||
WARNING_NUMERIC_STRING_1,
|
||||
WARNING_UNMAPPED_CHAR_1,
|
||||
META_WARNING_DONE
|
||||
};
|
||||
|
||||
@@ -176,6 +179,8 @@ static uint8_t const _wextraCommands[] = {
|
||||
WARNING_NUMERIC_STRING_2,
|
||||
WARNING_TRUNCATION_1,
|
||||
WARNING_TRUNCATION_2,
|
||||
WARNING_UNMAPPED_CHAR_1,
|
||||
WARNING_UNMAPPED_CHAR_2,
|
||||
META_WARNING_DONE
|
||||
};
|
||||
|
||||
@@ -194,11 +199,12 @@ static uint8_t const _weverythingCommands[] = {
|
||||
WARNING_OBSOLETE,
|
||||
WARNING_SHIFT,
|
||||
WARNING_SHIFT_AMOUNT,
|
||||
WARNING_UNMAPPED_CHAR,
|
||||
WARNING_NUMERIC_STRING_1,
|
||||
WARNING_NUMERIC_STRING_2,
|
||||
WARNING_TRUNCATION_1,
|
||||
WARNING_TRUNCATION_2,
|
||||
WARNING_UNMAPPED_CHAR_1,
|
||||
WARNING_UNMAPPED_CHAR_2,
|
||||
// WARNING_USER,
|
||||
META_WARNING_DONE
|
||||
};
|
||||
|
||||
@@ -337,6 +337,8 @@ static char *parseArgv(int argc, char **argv, bool &autoAttrmap, bool &autoTilem
|
||||
break;
|
||||
case 'a':
|
||||
autoAttrmap = false;
|
||||
if (!options.attrmap.empty())
|
||||
warning("Overriding attrmap file %s", options.attrmap.c_str());
|
||||
options.attrmap = musl_optarg;
|
||||
break;
|
||||
case 'b':
|
||||
@@ -479,6 +481,8 @@ static char *parseArgv(int argc, char **argv, bool &autoAttrmap, bool &autoTilem
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
if (!options.output.empty())
|
||||
warning("Overriding tile data file %s", options.output.c_str());
|
||||
options.output = musl_optarg;
|
||||
break;
|
||||
case 'P':
|
||||
@@ -486,6 +490,8 @@ static char *parseArgv(int argc, char **argv, bool &autoAttrmap, bool &autoTilem
|
||||
break;
|
||||
case 'p':
|
||||
autoPalettes = false;
|
||||
if (!options.palettes.empty())
|
||||
warning("Overriding palettes file %s", options.palettes.c_str());
|
||||
options.palettes = musl_optarg;
|
||||
break;
|
||||
case 'Q':
|
||||
@@ -493,6 +499,8 @@ static char *parseArgv(int argc, char **argv, bool &autoAttrmap, bool &autoTilem
|
||||
break;
|
||||
case 'q':
|
||||
autoPalmap = false;
|
||||
if (!options.palmap.empty())
|
||||
warning("Overriding palette map file %s", options.palmap.c_str());
|
||||
options.palmap = musl_optarg;
|
||||
break;
|
||||
case 'r':
|
||||
@@ -520,6 +528,8 @@ static char *parseArgv(int argc, char **argv, bool &autoAttrmap, bool &autoTilem
|
||||
break;
|
||||
case 't':
|
||||
autoTilemap = false;
|
||||
if (!options.tilemap.empty())
|
||||
warning("Overriding tilemap file %s", options.tilemap.c_str());
|
||||
options.tilemap = musl_optarg;
|
||||
break;
|
||||
case 'V':
|
||||
|
||||
@@ -28,31 +28,6 @@ void indexed(std::vector<Palette> &palettes, int palSize, png_color const *palRG
|
||||
return Rgba(c.red, c.green, c.blue, palAlpha ? palAlpha[index] : 0xFF);
|
||||
};
|
||||
|
||||
// HACK: for compatibility with old versions, add unused colors if:
|
||||
// - there is only one palette, and
|
||||
// - only some of the first N colors are being used
|
||||
if (palettes.size() == 1) {
|
||||
Palette &palette = palettes[0];
|
||||
// Build our candidate array of colors
|
||||
decltype(palette.colors) colors{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX};
|
||||
for (int i = 0; i < options.maxOpaqueColors(); ++i) {
|
||||
colors[i + options.hasTransparentPixels] = pngToRgb(i).cgbColor();
|
||||
}
|
||||
|
||||
// Check that the palette only uses those colors
|
||||
if (std::all_of(palette.begin(), palette.end(), [&colors](uint16_t color) {
|
||||
return std::find(colors.begin(), colors.end(), color) != colors.end();
|
||||
})) {
|
||||
if (palette.size() != options.maxOpaqueColors()) {
|
||||
warning("Unused color in PNG embedded palette was re-added; please use `-c "
|
||||
"embedded` to get this in future versions");
|
||||
}
|
||||
// Overwrite the palette, and return with that (it's already sorted)
|
||||
palette.colors = colors;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Palette &pal : palettes) {
|
||||
std::sort(pal.begin(), pal.end(), [&](uint16_t lhs, uint16_t rhs) {
|
||||
// Iterate through the PNG's palette, looking for either of the two
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <streambuf>
|
||||
#include <string>
|
||||
@@ -53,7 +55,7 @@ constexpr uint8_t singleToHex(char c) {
|
||||
|
||||
template<typename Str> // Should be std::string or std::string_view
|
||||
static void skipWhitespace(Str const &str, typename Str::size_type &pos) {
|
||||
pos = std::min(str.find_first_not_of(" \t", pos), str.length());
|
||||
pos = std::min(str.find_first_not_of(" \t"sv, pos), str.length());
|
||||
}
|
||||
|
||||
void parseInlinePalSpec(char const * const rawArg) {
|
||||
@@ -191,6 +193,15 @@ static T readBE(U const *bytes) {
|
||||
return val;
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
static T readLE(U const *bytes) {
|
||||
T val = 0;
|
||||
for (size_t i = 0; i < sizeof(val); ++i) {
|
||||
val |= static_cast<uint8_t>(bytes[i]) << (i * 8);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* **Appends** the first line read from `file` to the end of the provided `buffer`.
|
||||
*/
|
||||
@@ -217,13 +228,53 @@ static void readLine(std::filebuf &file, std::string &buffer) {
|
||||
/*
|
||||
* Parses the initial part of a string_view, advancing the "read index" as it does
|
||||
*/
|
||||
static uint16_t parseDec(std::string const &str, std::string::size_type &n) {
|
||||
uint32_t value = 0; // Use a larger type to handle overflow more easily
|
||||
for (auto end = std::min(str.length(), str.find_first_not_of("0123456789", n)); n < end; ++n) {
|
||||
value = std::min<uint32_t>(value * 10 + (str[n] - '0'), UINT16_MAX);
|
||||
template<typename U> // Should be uint*_t
|
||||
static std::optional<U> parseDec(std::string const &str, std::string::size_type &n) {
|
||||
std::string::size_type start = n;
|
||||
|
||||
uintmax_t value = 0; // Use a larger type to handle overflow more easily
|
||||
for (auto end = std::min(str.length(), str.find_first_not_of("0123456789"sv, n)); n < end;
|
||||
++n) {
|
||||
value = std::min(value * 10 + (str[n] - '0'), (uintmax_t)std::numeric_limits<U>::max);
|
||||
}
|
||||
|
||||
return value;
|
||||
return n > start ? std::optional<U>{value} : std::nullopt;
|
||||
}
|
||||
|
||||
static std::optional<Rgba> parseColor(std::string const &str, std::string::size_type &n,
|
||||
uint16_t i) {
|
||||
std::optional<uint8_t> r = parseDec<uint8_t>(str, n);
|
||||
if (!r) {
|
||||
error("Failed to parse color #%" PRIu16 " (\"%s\"): invalid red component", i + 1,
|
||||
str.c_str());
|
||||
return std::nullopt;
|
||||
}
|
||||
skipWhitespace(str, n);
|
||||
if (n == str.length()) {
|
||||
error("Failed to parse color #%" PRIu16 " (\"%s\"): missing green component", i + 1,
|
||||
str.c_str());
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<uint8_t> g = parseDec<uint8_t>(str, n);
|
||||
if (!g) {
|
||||
error("Failed to parse color #%" PRIu16 " (\"%s\"): invalid green component", i + 1,
|
||||
str.c_str());
|
||||
return std::nullopt;
|
||||
}
|
||||
skipWhitespace(str, n);
|
||||
if (n == str.length()) {
|
||||
error("Failed to parse color #%" PRIu16 " (\"%s\"): missing blue component", i + 1,
|
||||
str.c_str());
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<uint8_t> b = parseDec<uint8_t>(str, n);
|
||||
if (!b) {
|
||||
error("Failed to parse color #%" PRIu16 " (\"%s\"): invalid blue component", i + 1,
|
||||
str.c_str());
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return std::optional<Rgba>{Rgba(*r, *g, *b, 0xFF)};
|
||||
}
|
||||
|
||||
static void parsePSPFile(std::filebuf &file) {
|
||||
@@ -246,41 +297,30 @@ static void parsePSPFile(std::filebuf &file) {
|
||||
line.clear();
|
||||
readLine(file, line);
|
||||
std::string::size_type n = 0;
|
||||
uint16_t nbColors = parseDec(line, n);
|
||||
if (n != line.length()) {
|
||||
std::optional<uint16_t> nbColors = parseDec<uint16_t>(line, n);
|
||||
if (!nbColors || n != line.length()) {
|
||||
error("Invalid \"number of colors\" line in PSP file (%s)", line.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (nbColors > options.nbColorsPerPal * options.nbPalettes) {
|
||||
if (*nbColors > options.nbColorsPerPal * options.nbPalettes) {
|
||||
warning("PSP file contains %" PRIu16 " colors, but there can only be %" PRIu16
|
||||
"; ignoring extra",
|
||||
nbColors, options.nbColorsPerPal * options.nbPalettes);
|
||||
*nbColors, options.nbColorsPerPal * options.nbPalettes);
|
||||
nbColors = options.nbColorsPerPal * options.nbPalettes;
|
||||
}
|
||||
|
||||
options.palSpec.clear();
|
||||
|
||||
for (uint16_t i = 0; i < nbColors; ++i) {
|
||||
for (uint16_t i = 0; i < *nbColors; ++i) {
|
||||
line.clear();
|
||||
readLine(file, line);
|
||||
n = 0;
|
||||
|
||||
uint8_t r = parseDec(line, n);
|
||||
skipWhitespace(line, n);
|
||||
if (n == line.length()) {
|
||||
error("Failed to parse color #%" PRIu16 " (\"%s\"): missing green component", i + 1,
|
||||
line.c_str());
|
||||
n = 0;
|
||||
std::optional<Rgba> color = parseColor(line, n, i + 1);
|
||||
if (!color) {
|
||||
return;
|
||||
}
|
||||
uint8_t g = parseDec(line, n);
|
||||
if (n == line.length()) {
|
||||
error("Failed to parse color #%" PRIu16 " (\"%s\"): missing green component", i + 1,
|
||||
line.c_str());
|
||||
return;
|
||||
}
|
||||
skipWhitespace(line, n);
|
||||
uint8_t b = parseDec(line, n);
|
||||
if (n != line.length()) {
|
||||
error("Failed to parse color #%" PRIu16
|
||||
" (\"%s\"): trailing characters after blue component",
|
||||
@@ -291,11 +331,98 @@ static void parsePSPFile(std::filebuf &file) {
|
||||
if (i % options.nbColorsPerPal == 0) {
|
||||
options.palSpec.emplace_back();
|
||||
}
|
||||
options.palSpec.back()[i % options.nbColorsPerPal] = Rgba(r, g, b, 0xFF);
|
||||
options.palSpec.back()[i % options.nbColorsPerPal] = *color;
|
||||
}
|
||||
}
|
||||
|
||||
void parseACTFile(std::filebuf &file) {
|
||||
static void parseGPLFile(std::filebuf &file) {
|
||||
// https://gitlab.gnome.org/GNOME/gimp/-/blob/gimp-2-10/app/core/gimppalette-load.c#L39
|
||||
|
||||
std::string line;
|
||||
readLine(file, line);
|
||||
// FIXME: C++20 will allow `!line.starts_with` instead of `line.rfind` with 0
|
||||
if (line.rfind("GIMP Palette", 0)) {
|
||||
error("Palette file does not appear to be a GPL palette file");
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t nbColors = 0;
|
||||
uint16_t maxNbColors = options.nbColorsPerPal * options.nbPalettes;
|
||||
|
||||
for (;;) {
|
||||
line.clear();
|
||||
readLine(file, line);
|
||||
if (!line.length()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// FIXME: C++20 will allow `line.starts_with` instead of `!line.rfind` with 0
|
||||
if (!line.rfind("#", 0) || !line.rfind("Name:", 0) || !line.rfind("Column:", 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string::size_type n = 0;
|
||||
std::optional<Rgba> color = parseColor(line, n, nbColors + 1);
|
||||
if (!color) {
|
||||
return;
|
||||
}
|
||||
|
||||
++nbColors;
|
||||
if (nbColors < maxNbColors) {
|
||||
if (nbColors % options.nbColorsPerPal == 1) {
|
||||
options.palSpec.emplace_back();
|
||||
}
|
||||
options.palSpec.back()[nbColors % options.nbColorsPerPal] = *color;
|
||||
}
|
||||
}
|
||||
|
||||
if (nbColors > maxNbColors) {
|
||||
warning("GPL file contains %" PRIu16 " colors, but there can only be %" PRIu16
|
||||
"; ignoring extra",
|
||||
nbColors, maxNbColors);
|
||||
}
|
||||
}
|
||||
|
||||
static void parseHEXFile(std::filebuf &file) {
|
||||
// https://lospec.com/palette-list/tag/gbc
|
||||
|
||||
uint16_t nbColors = 0;
|
||||
uint16_t maxNbColors = options.nbColorsPerPal * options.nbPalettes;
|
||||
|
||||
for (;;) {
|
||||
std::string line;
|
||||
readLine(file, line);
|
||||
if (!line.length()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (line.length() != 6
|
||||
|| line.find_first_not_of("0123456789ABCDEFabcdef"sv) != std::string::npos) {
|
||||
error("Failed to parse color #%" PRIu16 " (\"%s\"): invalid \"rrggbb\" line",
|
||||
nbColors + 1, line.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
Rgba color =
|
||||
Rgba(toHex(line[0], line[1]), toHex(line[2], line[3]), toHex(line[4], line[5]), 0xFF);
|
||||
|
||||
++nbColors;
|
||||
if (nbColors < maxNbColors) {
|
||||
if (nbColors % options.nbColorsPerPal == 1) {
|
||||
options.palSpec.emplace_back();
|
||||
}
|
||||
options.palSpec.back()[nbColors % options.nbColorsPerPal] = color;
|
||||
}
|
||||
}
|
||||
|
||||
if (nbColors > maxNbColors) {
|
||||
warning("HEX file contains %" PRIu16 " colors, but there can only be %" PRIu16
|
||||
"; ignoring extra",
|
||||
nbColors, maxNbColors);
|
||||
}
|
||||
}
|
||||
|
||||
static void parseACTFile(std::filebuf &file) {
|
||||
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_pgfId-1070626
|
||||
|
||||
std::array<char, 772> buf;
|
||||
@@ -344,8 +471,8 @@ void parseACTFile(std::filebuf &file) {
|
||||
}
|
||||
}
|
||||
|
||||
void parseACOFile(std::filebuf &file) {
|
||||
// rhttps://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_pgfId-1055819
|
||||
static void parseACOFile(std::filebuf &file) {
|
||||
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_pgfId-1055819
|
||||
// http://www.nomodes.com/aco.html
|
||||
|
||||
char buf[10];
|
||||
@@ -412,6 +539,29 @@ void parseACOFile(std::filebuf &file) {
|
||||
// `codecvt` can be used to convert from UTF-16 to UTF-8
|
||||
}
|
||||
|
||||
static void parseGBCFile(std::filebuf &file) {
|
||||
// This only needs to be able to read back files generated by `rgbgfx -p`
|
||||
options.palSpec.clear();
|
||||
|
||||
for (;;) {
|
||||
char buf[2 * 4];
|
||||
auto len = file.sgetn(buf, sizeof(buf));
|
||||
if (len == 0) {
|
||||
break;
|
||||
} else if (len != sizeof(buf)) {
|
||||
error("GBC palette dump contains %zu 8-byte palette%s, plus %zu byte%s",
|
||||
options.palSpec.size(), options.palSpec.size() == 1 ? "" : "s", len,
|
||||
len == 1 ? "" : "s");
|
||||
break;
|
||||
}
|
||||
|
||||
options.palSpec.push_back({Rgba::fromCGBColor(readLE<uint16_t>(&buf[0])),
|
||||
Rgba::fromCGBColor(readLE<uint16_t>(&buf[2])),
|
||||
Rgba::fromCGBColor(readLE<uint16_t>(&buf[4])),
|
||||
Rgba::fromCGBColor(readLE<uint16_t>(&buf[6]))});
|
||||
}
|
||||
}
|
||||
|
||||
void parseExternalPalSpec(char const *arg) {
|
||||
// `fmt:path`, parse the file according to the given format
|
||||
|
||||
@@ -425,8 +575,11 @@ void parseExternalPalSpec(char const *arg) {
|
||||
|
||||
static std::array parsers{
|
||||
std::tuple{"PSP", &parsePSPFile, std::ios::in },
|
||||
std::tuple{"GPL", &parseGPLFile, std::ios::in },
|
||||
std::tuple{"HEX", &parseHEXFile, std::ios::in },
|
||||
std::tuple{"ACT", &parseACTFile, std::ios::binary},
|
||||
std::tuple{"ACO", &parseACOFile, std::ios::binary},
|
||||
std::tuple{"GBC", &parseGBCFile, std::ios::binary},
|
||||
};
|
||||
|
||||
auto iter = std::find_if(parsers.begin(), parsers.end(),
|
||||
|
||||
@@ -997,7 +997,7 @@ void process() {
|
||||
* protoPalettes.erase(protoPalettes.begin() + i);
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
*/
|
||||
[[fallthrough]];
|
||||
|
||||
case ProtoPalette::THEY_BIGGER:
|
||||
|
||||
@@ -154,9 +154,9 @@ FILE *openFile(char const *fileName, char const *mode)
|
||||
if (strcmp(fileName, "-") != 0)
|
||||
file = fopen(fileName, mode);
|
||||
else if (mode[0] == 'r')
|
||||
file = fdopen(0, mode);
|
||||
file = fdopen(STDIN_FILENO, mode);
|
||||
else
|
||||
file = fdopen(1, mode);
|
||||
file = fdopen(STDOUT_FILENO, mode);
|
||||
|
||||
if (!file)
|
||||
err("Could not open file \"%s\"", fileName);
|
||||
@@ -369,21 +369,31 @@ int main(int argc, char *argv[])
|
||||
isWRA0Mode = true;
|
||||
break;
|
||||
case 'l':
|
||||
if (linkerScriptName)
|
||||
warnx("Overriding linkerscript %s", musl_optarg);
|
||||
linkerScriptName = musl_optarg;
|
||||
break;
|
||||
case 'M':
|
||||
noSymInMap = true;
|
||||
break;
|
||||
case 'm':
|
||||
if (mapFileName)
|
||||
warnx("Overriding mapfile %s", musl_optarg);
|
||||
mapFileName = musl_optarg;
|
||||
break;
|
||||
case 'n':
|
||||
if (symFileName)
|
||||
warnx("Overriding symfile %s", musl_optarg);
|
||||
symFileName = musl_optarg;
|
||||
break;
|
||||
case 'O':
|
||||
if (overlayFileName)
|
||||
warnx("Overriding overlay file %s", musl_optarg);
|
||||
overlayFileName = musl_optarg;
|
||||
break;
|
||||
case 'o':
|
||||
if (outputFileName)
|
||||
warnx("Overriding output file %s", musl_optarg);
|
||||
outputFileName = musl_optarg;
|
||||
break;
|
||||
case 'p':
|
||||
|
||||
@@ -459,7 +459,12 @@ static struct Section *getMainSection(struct Section *section)
|
||||
|
||||
void obj_ReadFile(char const *fileName, unsigned int fileID)
|
||||
{
|
||||
FILE *file = strcmp("-", fileName) ? fopen(fileName, "rb") : stdin;
|
||||
FILE *file;
|
||||
|
||||
if (strcmp("-", fileName) != 0)
|
||||
file = fopen(fileName, "rb");
|
||||
else
|
||||
file = fdopen(STDIN_FILENO, "rb"); // `stdin` is in text mode by default
|
||||
|
||||
if (!file)
|
||||
err("Could not open file %s", fileName);
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#include "link/section.h"
|
||||
#include "link/symbol.h"
|
||||
|
||||
#include "extern/utf8decoder.h"
|
||||
|
||||
#include "error.h"
|
||||
#include "linkdefs.h"
|
||||
#include "platform.h" // MIN_NB_ELMS
|
||||
@@ -273,6 +275,55 @@ static struct SortedSection const **nextSection(struct SortedSection const **s1,
|
||||
return (*s1)->section->org < (*s2)->section->org ? s1 : s2;
|
||||
}
|
||||
|
||||
// Checks whether this character is legal as the first character of a symbol's name in a sym file
|
||||
static bool canStartSymName(char c)
|
||||
{
|
||||
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_';
|
||||
}
|
||||
|
||||
// Checks whether this character is legal in a symbol's name in a sym file
|
||||
static bool isLegalForSymName(char c)
|
||||
{
|
||||
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
|
||||
c == '_' || c == '@' || c == '#' || c == '$' || c == '.';
|
||||
}
|
||||
|
||||
// Prints a symbol's name to `symFile`, assuming that the first character is legal.
|
||||
// Illegal characters are UTF-8-decoded (errors are replaced by U+FFFD) and emitted as `\u`/`\U`.
|
||||
static void printSymName(char const *name)
|
||||
{
|
||||
for (char const *ptr = name; *ptr != '\0'; ) {
|
||||
char c = *ptr;
|
||||
|
||||
if (isLegalForSymName(c)) {
|
||||
// Output legal ASCII characters as-is
|
||||
fputc(c, symFile);
|
||||
++ptr;
|
||||
} else {
|
||||
// Output illegal characters using Unicode escapes
|
||||
// Decode the UTF-8 codepoint; or at least attempt to
|
||||
uint32_t state = 0, codepoint;
|
||||
|
||||
do {
|
||||
decode(&state, &codepoint, *ptr);
|
||||
if (state == 1) {
|
||||
// This sequence was invalid; emit a U+FFFD, and recover
|
||||
codepoint = 0xFFFD;
|
||||
// Skip continuation bytes
|
||||
// A NUL byte does not qualify, so we're good
|
||||
while ((*ptr & 0xC0) == 0x80)
|
||||
++ptr;
|
||||
break;
|
||||
}
|
||||
++ptr;
|
||||
} while (state != 0);
|
||||
|
||||
fprintf(symFile, codepoint <= 0xFFFF ? "\\u%04" PRIx32 : "\\U%08" PRIx32,
|
||||
codepoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Comparator function for `qsort` to sort symbols
|
||||
// Symbols are ordered by address, or else by original index for a stable sort
|
||||
static int compareSymbols(void const *a, void const *b)
|
||||
@@ -296,16 +347,22 @@ static void writeSymBank(struct SortedSections const *bankSections,
|
||||
if (!symFile)
|
||||
return;
|
||||
|
||||
#define forEachSortedSection(sect, ...) do { \
|
||||
for (struct SortedSection const *ssp = bankSections->zeroLenSections; ssp; ssp = ssp->next) { \
|
||||
for (struct Section const *sect = ssp->section; sect; sect = sect->nextu) \
|
||||
__VA_ARGS__ \
|
||||
} \
|
||||
for (struct SortedSection const *ssp = bankSections->sections; ssp; ssp = ssp->next) { \
|
||||
for (struct Section const *sect = ssp->section; sect; sect = sect->nextu) \
|
||||
__VA_ARGS__ \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
uint32_t nbSymbols = 0;
|
||||
|
||||
for (struct SortedSection const *ptr = bankSections->zeroLenSections; ptr; ptr = ptr->next) {
|
||||
for (struct Section const *sect = ptr->section; sect; sect = sect->nextu)
|
||||
nbSymbols += sect->nbSymbols;
|
||||
}
|
||||
for (struct SortedSection const *ptr = bankSections->sections; ptr; ptr = ptr->next) {
|
||||
for (struct Section const *sect = ptr->section; sect; sect = sect->nextu)
|
||||
nbSymbols += sect->nbSymbols;
|
||||
}
|
||||
forEachSortedSection(sect, {
|
||||
nbSymbols += sect->nbSymbols;
|
||||
});
|
||||
|
||||
if (!nbSymbols)
|
||||
return;
|
||||
@@ -315,29 +372,21 @@ static void writeSymBank(struct SortedSections const *bankSections,
|
||||
if (!symList)
|
||||
err("Failed to allocate symbol list");
|
||||
|
||||
uint32_t idx = 0;
|
||||
nbSymbols = 0;
|
||||
|
||||
for (struct SortedSection const *ptr = bankSections->zeroLenSections; ptr; ptr = ptr->next) {
|
||||
for (struct Section const *sect = ptr->section; sect; sect = sect->nextu) {
|
||||
for (uint32_t i = 0; i < sect->nbSymbols; i++) {
|
||||
symList[idx].idx = idx;
|
||||
symList[idx].sym = sect->symbols[i];
|
||||
symList[idx].addr = symList[idx].sym->offset + sect->org;
|
||||
idx++;
|
||||
}
|
||||
forEachSortedSection(sect, {
|
||||
for (uint32_t i = 0; i < sect->nbSymbols; i++) {
|
||||
if (!canStartSymName(sect->symbols[i]->name[0]))
|
||||
// Don't output symbols that begin with an illegal character
|
||||
continue;
|
||||
symList[nbSymbols].idx = nbSymbols;
|
||||
symList[nbSymbols].sym = sect->symbols[i];
|
||||
symList[nbSymbols].addr = symList[nbSymbols].sym->offset + sect->org;
|
||||
nbSymbols++;
|
||||
}
|
||||
}
|
||||
for (struct SortedSection const *ptr = bankSections->sections; ptr; ptr = ptr->next) {
|
||||
for (struct Section const *sect = ptr->section; sect; sect = sect->nextu) {
|
||||
for (uint32_t i = 0; i < sect->nbSymbols; i++) {
|
||||
symList[idx].idx = idx;
|
||||
symList[idx].sym = sect->symbols[i];
|
||||
symList[idx].addr = symList[idx].sym->offset + sect->org;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(idx == nbSymbols);
|
||||
});
|
||||
|
||||
#undef forEachSortedSection
|
||||
|
||||
qsort(symList, nbSymbols, sizeof(*symList), compareSymbols);
|
||||
|
||||
@@ -346,11 +395,13 @@ static void writeSymBank(struct SortedSections const *bankSections,
|
||||
for (uint32_t i = 0; i < nbSymbols; i++) {
|
||||
struct SortedSymbol *sym = &symList[i];
|
||||
|
||||
fprintf(symFile, "%02" PRIx32 ":%04" PRIx16 " %s\n",
|
||||
symBank, sym->addr, sym->sym->name);
|
||||
fprintf(symFile, "%02" PRIx32 ":%04" PRIx16 " ", symBank, sym->addr);
|
||||
printSymName(sym->sym->name);
|
||||
fputc('\n', symFile);
|
||||
}
|
||||
|
||||
free(symList);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -384,20 +435,20 @@ static uint16_t writeMapBank(struct SortedSections const *sectList,
|
||||
if (prevEndAddr < sect->org) {
|
||||
uint16_t empty = sect->org - prevEndAddr;
|
||||
|
||||
fprintf(mapFile, " EMPTY: $%04" PRIx16 " byte%s\n", empty,
|
||||
fprintf(mapFile, "\tEMPTY: $%04" PRIx16 " byte%s\n", empty,
|
||||
empty == 1 ? "" : "s");
|
||||
}
|
||||
|
||||
prevEndAddr = sect->org + sect->size;
|
||||
|
||||
if (sect->size != 0)
|
||||
fprintf(mapFile, " SECTION: $%04" PRIx16 "-$%04x ($%04" PRIx16
|
||||
fprintf(mapFile, "\tSECTION: $%04" PRIx16 "-$%04x ($%04" PRIx16
|
||||
" byte%s) [\"%s\"]\n",
|
||||
sect->org, prevEndAddr - 1,
|
||||
sect->size, sect->size == 1 ? "" : "s",
|
||||
sect->name);
|
||||
else
|
||||
fprintf(mapFile, " SECTION: $%04" PRIx16 " (0 bytes) [\"%s\"]\n",
|
||||
fprintf(mapFile, "\tSECTION: $%04" PRIx16 " (0 bytes) [\"%s\"]\n",
|
||||
sect->org, sect->name);
|
||||
|
||||
if (!noSymInMap) {
|
||||
@@ -405,11 +456,12 @@ static uint16_t writeMapBank(struct SortedSections const *sectList,
|
||||
|
||||
while (sect) {
|
||||
if (sect->modifier == SECTION_UNION)
|
||||
fprintf(mapFile, " ; New union\n");
|
||||
fprintf(mapFile, "\t\t; New union\n");
|
||||
else if (sect->modifier == SECTION_FRAGMENT)
|
||||
fprintf(mapFile, " ; New fragment\n");
|
||||
fprintf(mapFile, "\t\t; New fragment\n");
|
||||
for (size_t i = 0; i < sect->nbSymbols; i++)
|
||||
fprintf(mapFile, " $%04" PRIx32 " = %s\n",
|
||||
// "\tSECTION: $xxxx ..."
|
||||
fprintf(mapFile, "\t $%04" PRIx32 " = %s\n",
|
||||
sect->symbols[i]->offset + org,
|
||||
sect->symbols[i]->name);
|
||||
|
||||
@@ -425,7 +477,7 @@ static uint16_t writeMapBank(struct SortedSections const *sectList,
|
||||
if (prevEndAddr < bankEndAddr) {
|
||||
uint16_t empty = bankEndAddr - prevEndAddr;
|
||||
|
||||
fprintf(mapFile, " EMPTY: $%04" PRIx16 " byte%s\n", empty,
|
||||
fprintf(mapFile, "\tEMPTY: $%04" PRIx16 " byte%s\n", empty,
|
||||
empty == 1 ? "" : "s");
|
||||
}
|
||||
|
||||
@@ -434,7 +486,7 @@ static uint16_t writeMapBank(struct SortedSections const *sectList,
|
||||
} else {
|
||||
uint16_t slack = sectionTypeInfo[type].size - used;
|
||||
|
||||
fprintf(mapFile, " SLACK: $%04" PRIx16 " byte%s\n\n", slack,
|
||||
fprintf(mapFile, "\tSLACK: $%04" PRIx16 " byte%s\n\n", slack,
|
||||
slack == 1 ? "" : "s");
|
||||
}
|
||||
|
||||
@@ -442,15 +494,15 @@ static uint16_t writeMapBank(struct SortedSections const *sectList,
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the total used space by section type to the map file
|
||||
* Write the total used and free space by section type to the map file
|
||||
* @param usedMap The total used space by section type
|
||||
*/
|
||||
static void writeMapUsed(uint32_t usedMap[MIN_NB_ELMS(SECTTYPE_INVALID)])
|
||||
static void writeMapSummary(uint32_t usedMap[MIN_NB_ELMS(SECTTYPE_INVALID)])
|
||||
{
|
||||
if (!mapFile)
|
||||
return;
|
||||
|
||||
fputs("USED:\n", mapFile);
|
||||
fputs("SUMMARY:\n", mapFile);
|
||||
|
||||
for (uint8_t i = 0; i < SECTTYPE_INVALID; i++) {
|
||||
enum SectionType type = typeMap[i];
|
||||
@@ -459,11 +511,18 @@ static void writeMapUsed(uint32_t usedMap[MIN_NB_ELMS(SECTTYPE_INVALID)])
|
||||
if (type == SECTTYPE_VRAM || type == SECTTYPE_OAM)
|
||||
continue;
|
||||
|
||||
if (sections[type].nbBanks > 0) {
|
||||
fprintf(mapFile, " %s: $%04" PRIx32 " byte%s in %" PRIu32 " bank%s\n",
|
||||
sectionTypeInfo[type].name, usedMap[type], usedMap[type] == 1 ? "" : "s",
|
||||
sections[type].nbBanks, sections[type].nbBanks == 1 ? "" : "s");
|
||||
}
|
||||
// Do not output unused section types
|
||||
if (sections[type].nbBanks == 0)
|
||||
continue;
|
||||
|
||||
fprintf(mapFile, "\t%s: %" PRId32 " byte%s used / %" PRId32 " free",
|
||||
sectionTypeInfo[type].name, usedMap[type], usedMap[type] == 1 ? "" : "s",
|
||||
sections[type].nbBanks * sectionTypeInfo[type].size - usedMap[type]);
|
||||
if (sectionTypeInfo[type].firstBank != sectionTypeInfo[type].lastBank ||
|
||||
sections[type].nbBanks > 1)
|
||||
fprintf(mapFile, " in %d bank%s", sections[type].nbBanks,
|
||||
sections[type].nbBanks == 1 ? "" : "s");
|
||||
fputc('\n', mapFile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,7 +551,7 @@ static void writeSymAndMap(void)
|
||||
}
|
||||
}
|
||||
|
||||
writeMapUsed(usedMap);
|
||||
writeMapSummary(usedMap);
|
||||
|
||||
closeFile(symFile);
|
||||
closeFile(mapFile);
|
||||
|
||||
@@ -146,7 +146,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
||||
isError = false;
|
||||
|
||||
// Be VERY careful with two `popRPN` in the same expression.
|
||||
// C does not guarantee order of evaluation of operands!!
|
||||
// C does not guarantee order of evaluation of operands!
|
||||
// So, if there are two `popRPN` in the same expression, make
|
||||
// sure the operation is commutative.
|
||||
switch (command) {
|
||||
|
||||
@@ -39,5 +39,6 @@ PRINTLN \1
|
||||
endm
|
||||
|
||||
; Representative numeric and string builtins
|
||||
tickle __LINE__, 1
|
||||
tickle __FILE__, 0
|
||||
; (SOURCE_DATE_EPOCH in test.sh makes this reproducible)
|
||||
tickle __UTC_YEAR__, 1
|
||||
tickle __ISO_8601_UTC__, 0
|
||||
|
||||
@@ -1,57 +1,57 @@
|
||||
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(7):
|
||||
'__LINE__' already defined as constant at <builtin>
|
||||
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(8):
|
||||
'__LINE__' already defined as constant at <builtin>
|
||||
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(11):
|
||||
'__LINE__' already defined at <builtin>
|
||||
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(12):
|
||||
'__LINE__' already defined at <builtin>
|
||||
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(16):
|
||||
Built-in symbol '__LINE__' cannot be purged
|
||||
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(17):
|
||||
Built-in symbol '__LINE__' cannot be purged
|
||||
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(20):
|
||||
'__LINE__' already defined at <builtin>
|
||||
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(21):
|
||||
'__LINE__' already defined at <builtin>
|
||||
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(24):
|
||||
'__LINE__' already defined as constant at <builtin>
|
||||
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(25):
|
||||
'__LINE__' already defined as constant at <builtin>
|
||||
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(28):
|
||||
'__LINE__' already defined at <builtin>
|
||||
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(29):
|
||||
'__LINE__' already defined at <builtin>
|
||||
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(32):
|
||||
'__LINE__' already defined as constant at <builtin>
|
||||
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(33):
|
||||
'__LINE__' already defined as constant at <builtin>
|
||||
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(36):
|
||||
'__LINE__' already defined as non-EQUS at <builtin>
|
||||
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(37):
|
||||
'__LINE__' already defined as non-EQUS at <builtin>
|
||||
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(7):
|
||||
'__UTC_YEAR__' already defined as constant at <command-line>
|
||||
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(8):
|
||||
'__UTC_YEAR__' already defined as constant at <command-line>
|
||||
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(11):
|
||||
'__UTC_YEAR__' already defined at <command-line>
|
||||
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(12):
|
||||
'__UTC_YEAR__' already defined at <command-line>
|
||||
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(16):
|
||||
Built-in symbol '__FILE__' cannot be purged
|
||||
Built-in symbol '__UTC_YEAR__' cannot be purged
|
||||
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(17):
|
||||
Built-in symbol '__FILE__' cannot be purged
|
||||
Built-in symbol '__UTC_YEAR__' cannot be purged
|
||||
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(20):
|
||||
'__FILE__' already defined at <builtin>
|
||||
'__UTC_YEAR__' already defined at <command-line>
|
||||
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(21):
|
||||
'__FILE__' already defined at <builtin>
|
||||
'__UTC_YEAR__' already defined at <command-line>
|
||||
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(24):
|
||||
'__FILE__' already defined as constant at <builtin>
|
||||
'__UTC_YEAR__' already defined as constant at <command-line>
|
||||
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(25):
|
||||
'__FILE__' already defined as constant at <builtin>
|
||||
'__UTC_YEAR__' already defined as constant at <command-line>
|
||||
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(28):
|
||||
'__FILE__' already defined at <builtin>
|
||||
'__UTC_YEAR__' already defined at <command-line>
|
||||
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(29):
|
||||
'__FILE__' already defined at <builtin>
|
||||
'__UTC_YEAR__' already defined at <command-line>
|
||||
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(32):
|
||||
'__FILE__' already defined as constant at <builtin>
|
||||
'__UTC_YEAR__' already defined as constant at <command-line>
|
||||
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(33):
|
||||
'__FILE__' already defined as constant at <builtin>
|
||||
'__UTC_YEAR__' already defined as constant at <command-line>
|
||||
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(36):
|
||||
Built-in symbol '__FILE__' cannot be redefined
|
||||
'__UTC_YEAR__' already defined as non-EQUS at <command-line>
|
||||
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(37):
|
||||
Built-in symbol '__FILE__' cannot be redefined
|
||||
'__UTC_YEAR__' already defined as non-EQUS at <command-line>
|
||||
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(16):
|
||||
Built-in symbol '__ISO_8601_UTC__' cannot be purged
|
||||
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(17):
|
||||
Built-in symbol '__ISO_8601_UTC__' cannot be purged
|
||||
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(20):
|
||||
'__ISO_8601_UTC__' already defined at <command-line>
|
||||
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(21):
|
||||
'__ISO_8601_UTC__' already defined at <command-line>
|
||||
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(24):
|
||||
'__ISO_8601_UTC__' already defined as constant at <command-line>
|
||||
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(25):
|
||||
'__ISO_8601_UTC__' already defined as constant at <command-line>
|
||||
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(28):
|
||||
'__ISO_8601_UTC__' already defined at <command-line>
|
||||
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(29):
|
||||
'__ISO_8601_UTC__' already defined at <command-line>
|
||||
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(32):
|
||||
'__ISO_8601_UTC__' already defined as constant at <command-line>
|
||||
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(33):
|
||||
'__ISO_8601_UTC__' already defined as constant at <command-line>
|
||||
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(36):
|
||||
Built-in symbol '__ISO_8601_UTC__' cannot be redefined
|
||||
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(37):
|
||||
Built-in symbol '__ISO_8601_UTC__' cannot be redefined
|
||||
error: Assembly aborted (28 errors)!
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
$9
|
||||
$D
|
||||
$12
|
||||
$16
|
||||
$1A
|
||||
$1E
|
||||
$22
|
||||
$26
|
||||
builtin-overwrite.asm
|
||||
builtin-overwrite.asm
|
||||
builtin-overwrite.asm
|
||||
builtin-overwrite.asm
|
||||
builtin-overwrite.asm
|
||||
builtin-overwrite.asm
|
||||
$7C5
|
||||
$7C5
|
||||
$7C5
|
||||
$7C5
|
||||
$7C5
|
||||
$7C5
|
||||
$7C5
|
||||
$7C5
|
||||
1989-04-21T12:34:56Z
|
||||
1989-04-21T12:34:56Z
|
||||
1989-04-21T12:34:56Z
|
||||
1989-04-21T12:34:56Z
|
||||
1989-04-21T12:34:56Z
|
||||
1989-04-21T12:34:56Z
|
||||
|
||||
@@ -33,8 +33,5 @@ endm
|
||||
_RS += 100
|
||||
println _RS
|
||||
|
||||
__LINE__ *= 200
|
||||
println __LINE__
|
||||
|
||||
UnDeFiNeD ^= 300
|
||||
println UnDeFiNeD
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
error: compound-assignment.asm(36):
|
||||
'__LINE__' already defined as constant at <builtin>
|
||||
error: compound-assignment.asm(39):
|
||||
Expected constant expression: 'UnDeFiNeD' is not constant at assembly time
|
||||
error: Assembly aborted (2 errors)!
|
||||
error: Assembly aborted (1 error)!
|
||||
|
||||
@@ -35,5 +35,4 @@ $5
|
||||
$14
|
||||
$A
|
||||
$64
|
||||
$25
|
||||
$0
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
warning: file-sym.asm(1): [-Wobsolete]
|
||||
`__FILE__` is deprecated
|
||||
|
||||
19
test/asm/fixed-point-magnitude.asm
Normal file
19
test/asm/fixed-point-magnitude.asm
Normal file
@@ -0,0 +1,19 @@
|
||||
for precision, 1, 32
|
||||
opt Q{d:precision}
|
||||
def magnitude = 32 - precision
|
||||
|
||||
def maxInt = 1 << magnitude - 1
|
||||
redef defMaxValue equs "def maxValue = {d:maxInt}.0"
|
||||
{defMaxValue}
|
||||
println "Q.{2d:precision}: max ok = 1 << {2d:magnitude} - 1 = {11d:maxInt}.0 = {#09x:maxValue}"
|
||||
|
||||
def minBadInt = maxInt + 1
|
||||
redef defMinBadValue equs "def minBadValue = {d:minBadInt}.0"
|
||||
{defMinBadValue}
|
||||
println "Q.{2d:precision}: min bad = 1 << {2d:magnitude} = {11d:minBadInt}.0 = {#09x:minBadValue}"
|
||||
|
||||
def worseInt = minBadInt + 42
|
||||
redef defWorseValue equs "def worseValue = {d:worseInt}.0"
|
||||
{defWorseValue}
|
||||
println "Q.{2d:precision}: worse = 1 << {2d:magnitude} + 42 = {11d:worseInt}.0 = {#09x:worseValue}"
|
||||
endr
|
||||
183
test/asm/fixed-point-magnitude.err
Normal file
183
test/asm/fixed-point-magnitude.err
Normal file
@@ -0,0 +1,183 @@
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~1(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = -2147483648.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~2(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 1073741824.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~2(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 1073741866.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~3(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 536870912.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~3(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 536870954.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~4(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 268435456.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~4(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 268435498.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~5(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 134217728.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~5(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 134217770.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~6(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 67108864.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~6(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 67108906.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~7(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 33554432.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~7(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 33554474.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~8(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 16777216.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~8(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 16777258.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~9(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 8388608.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~9(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 8388650.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~10(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 4194304.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~10(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 4194346.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~11(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 2097152.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~11(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 2097194.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~12(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 1048576.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~12(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 1048618.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~13(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 524288.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~13(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 524330.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~14(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 262144.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~14(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 262186.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~15(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 131072.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~15(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 131114.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~16(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 65536.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~16(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 65578.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~17(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 32768.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~17(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 32810.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~18(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 16384.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~18(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 16426.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~19(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 8192.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~19(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 8234.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~20(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 4096.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~20(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 4138.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~21(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 2048.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~21(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 2090.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~22(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 1024.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~22(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 1066.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~23(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 512.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~23(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 554.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~24(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 256.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~24(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 298.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~25(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 128.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~25(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 170.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~26(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 64.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~26(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 106.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~27(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 32.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~27(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 74.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~28(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 16.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~28(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 58.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~29(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 8.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~29(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 50.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~30(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 4.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~30(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 46.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~31(12): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def minBadValue = 2.0"
|
||||
warning: fixed-point-magnitude.asm(1) -> fixed-point-magnitude.asm::REPT~31(17): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
while expanding symbol "def worseValue = 44.0"
|
||||
93
test/asm/fixed-point-magnitude.out
Normal file
93
test/asm/fixed-point-magnitude.out
Normal file
@@ -0,0 +1,93 @@
|
||||
Q. 1: max ok = 1 << 31 - 1 = 2147483647.0 = $fffffffe
|
||||
Q. 1: min bad = 1 << 31 = -2147483648.0 = $00000000
|
||||
Q. 1: worse = 1 << 31 + 42 = -2147483606.0 = $00000054
|
||||
Q. 2: max ok = 1 << 30 - 1 = 1073741823.0 = $fffffffc
|
||||
Q. 2: min bad = 1 << 30 = 1073741824.0 = $00000000
|
||||
Q. 2: worse = 1 << 30 + 42 = 1073741866.0 = $000000a8
|
||||
Q. 3: max ok = 1 << 29 - 1 = 536870911.0 = $fffffff8
|
||||
Q. 3: min bad = 1 << 29 = 536870912.0 = $00000000
|
||||
Q. 3: worse = 1 << 29 + 42 = 536870954.0 = $00000150
|
||||
Q. 4: max ok = 1 << 28 - 1 = 268435455.0 = $fffffff0
|
||||
Q. 4: min bad = 1 << 28 = 268435456.0 = $00000000
|
||||
Q. 4: worse = 1 << 28 + 42 = 268435498.0 = $000002a0
|
||||
Q. 5: max ok = 1 << 27 - 1 = 134217727.0 = $ffffffe0
|
||||
Q. 5: min bad = 1 << 27 = 134217728.0 = $00000000
|
||||
Q. 5: worse = 1 << 27 + 42 = 134217770.0 = $00000540
|
||||
Q. 6: max ok = 1 << 26 - 1 = 67108863.0 = $ffffffc0
|
||||
Q. 6: min bad = 1 << 26 = 67108864.0 = $00000000
|
||||
Q. 6: worse = 1 << 26 + 42 = 67108906.0 = $00000a80
|
||||
Q. 7: max ok = 1 << 25 - 1 = 33554431.0 = $ffffff80
|
||||
Q. 7: min bad = 1 << 25 = 33554432.0 = $00000000
|
||||
Q. 7: worse = 1 << 25 + 42 = 33554474.0 = $00001500
|
||||
Q. 8: max ok = 1 << 24 - 1 = 16777215.0 = $ffffff00
|
||||
Q. 8: min bad = 1 << 24 = 16777216.0 = $00000000
|
||||
Q. 8: worse = 1 << 24 + 42 = 16777258.0 = $00002a00
|
||||
Q. 9: max ok = 1 << 23 - 1 = 8388607.0 = $fffffe00
|
||||
Q. 9: min bad = 1 << 23 = 8388608.0 = $00000000
|
||||
Q. 9: worse = 1 << 23 + 42 = 8388650.0 = $00005400
|
||||
Q.10: max ok = 1 << 22 - 1 = 4194303.0 = $fffffc00
|
||||
Q.10: min bad = 1 << 22 = 4194304.0 = $00000000
|
||||
Q.10: worse = 1 << 22 + 42 = 4194346.0 = $0000a800
|
||||
Q.11: max ok = 1 << 21 - 1 = 2097151.0 = $fffff800
|
||||
Q.11: min bad = 1 << 21 = 2097152.0 = $00000000
|
||||
Q.11: worse = 1 << 21 + 42 = 2097194.0 = $00015000
|
||||
Q.12: max ok = 1 << 20 - 1 = 1048575.0 = $fffff000
|
||||
Q.12: min bad = 1 << 20 = 1048576.0 = $00000000
|
||||
Q.12: worse = 1 << 20 + 42 = 1048618.0 = $0002a000
|
||||
Q.13: max ok = 1 << 19 - 1 = 524287.0 = $ffffe000
|
||||
Q.13: min bad = 1 << 19 = 524288.0 = $00000000
|
||||
Q.13: worse = 1 << 19 + 42 = 524330.0 = $00054000
|
||||
Q.14: max ok = 1 << 18 - 1 = 262143.0 = $ffffc000
|
||||
Q.14: min bad = 1 << 18 = 262144.0 = $00000000
|
||||
Q.14: worse = 1 << 18 + 42 = 262186.0 = $000a8000
|
||||
Q.15: max ok = 1 << 17 - 1 = 131071.0 = $ffff8000
|
||||
Q.15: min bad = 1 << 17 = 131072.0 = $00000000
|
||||
Q.15: worse = 1 << 17 + 42 = 131114.0 = $00150000
|
||||
Q.16: max ok = 1 << 16 - 1 = 65535.0 = $ffff0000
|
||||
Q.16: min bad = 1 << 16 = 65536.0 = $00000000
|
||||
Q.16: worse = 1 << 16 + 42 = 65578.0 = $002a0000
|
||||
Q.17: max ok = 1 << 15 - 1 = 32767.0 = $fffe0000
|
||||
Q.17: min bad = 1 << 15 = 32768.0 = $00000000
|
||||
Q.17: worse = 1 << 15 + 42 = 32810.0 = $00540000
|
||||
Q.18: max ok = 1 << 14 - 1 = 16383.0 = $fffc0000
|
||||
Q.18: min bad = 1 << 14 = 16384.0 = $00000000
|
||||
Q.18: worse = 1 << 14 + 42 = 16426.0 = $00a80000
|
||||
Q.19: max ok = 1 << 13 - 1 = 8191.0 = $fff80000
|
||||
Q.19: min bad = 1 << 13 = 8192.0 = $00000000
|
||||
Q.19: worse = 1 << 13 + 42 = 8234.0 = $01500000
|
||||
Q.20: max ok = 1 << 12 - 1 = 4095.0 = $fff00000
|
||||
Q.20: min bad = 1 << 12 = 4096.0 = $00000000
|
||||
Q.20: worse = 1 << 12 + 42 = 4138.0 = $02a00000
|
||||
Q.21: max ok = 1 << 11 - 1 = 2047.0 = $ffe00000
|
||||
Q.21: min bad = 1 << 11 = 2048.0 = $00000000
|
||||
Q.21: worse = 1 << 11 + 42 = 2090.0 = $05400000
|
||||
Q.22: max ok = 1 << 10 - 1 = 1023.0 = $ffc00000
|
||||
Q.22: min bad = 1 << 10 = 1024.0 = $00000000
|
||||
Q.22: worse = 1 << 10 + 42 = 1066.0 = $0a800000
|
||||
Q.23: max ok = 1 << 9 - 1 = 511.0 = $ff800000
|
||||
Q.23: min bad = 1 << 9 = 512.0 = $00000000
|
||||
Q.23: worse = 1 << 9 + 42 = 554.0 = $15000000
|
||||
Q.24: max ok = 1 << 8 - 1 = 255.0 = $ff000000
|
||||
Q.24: min bad = 1 << 8 = 256.0 = $00000000
|
||||
Q.24: worse = 1 << 8 + 42 = 298.0 = $2a000000
|
||||
Q.25: max ok = 1 << 7 - 1 = 127.0 = $fe000000
|
||||
Q.25: min bad = 1 << 7 = 128.0 = $00000000
|
||||
Q.25: worse = 1 << 7 + 42 = 170.0 = $54000000
|
||||
Q.26: max ok = 1 << 6 - 1 = 63.0 = $fc000000
|
||||
Q.26: min bad = 1 << 6 = 64.0 = $00000000
|
||||
Q.26: worse = 1 << 6 + 42 = 106.0 = $a8000000
|
||||
Q.27: max ok = 1 << 5 - 1 = 31.0 = $f8000000
|
||||
Q.27: min bad = 1 << 5 = 32.0 = $00000000
|
||||
Q.27: worse = 1 << 5 + 42 = 74.0 = $50000000
|
||||
Q.28: max ok = 1 << 4 - 1 = 15.0 = $f0000000
|
||||
Q.28: min bad = 1 << 4 = 16.0 = $00000000
|
||||
Q.28: worse = 1 << 4 + 42 = 58.0 = $a0000000
|
||||
Q.29: max ok = 1 << 3 - 1 = 7.0 = $e0000000
|
||||
Q.29: min bad = 1 << 3 = 8.0 = $00000000
|
||||
Q.29: worse = 1 << 3 + 42 = 50.0 = $40000000
|
||||
Q.30: max ok = 1 << 2 - 1 = 3.0 = $c0000000
|
||||
Q.30: min bad = 1 << 2 = 4.0 = $00000000
|
||||
Q.30: worse = 1 << 2 + 42 = 46.0 = $80000000
|
||||
Q.31: max ok = 1 << 1 - 1 = 1.0 = $80000000
|
||||
Q.31: min bad = 1 << 1 = 2.0 = $00000000
|
||||
Q.31: worse = 1 << 1 + 42 = 44.0 = $00000000
|
||||
27
test/asm/fixed-point-specific.asm
Normal file
27
test/asm/fixed-point-specific.asm
Normal file
@@ -0,0 +1,27 @@
|
||||
MACRO compare
|
||||
print "\3: "
|
||||
if _NARG == 4
|
||||
def v1 = \3(\4q\1, \1)
|
||||
def v2 = \3(\4q\2, \2)
|
||||
elif _NARG == 5
|
||||
def v1 = \3(\4q\1, \5q\1, \1)
|
||||
def v2 = \3(\4q\2, \5q\2, \2)
|
||||
endc
|
||||
opt Q\1
|
||||
print "{.4f:v1} == "
|
||||
opt Q\2
|
||||
println "{.4f:v2}"
|
||||
ENDM
|
||||
|
||||
compare 8, 16, mul, 6.0, 7.0
|
||||
compare 12, 24, div, 115.625, 9.25
|
||||
compare 7, 14, pow, 3.5, 5.5
|
||||
|
||||
compare 4, 8, sin, 0.25
|
||||
compare 5, 9, cos, 0.75
|
||||
compare 6, 10, asin, 1.0
|
||||
compare 7, 11, acos, 0.0
|
||||
|
||||
compare 3, 6, round, 1.75
|
||||
compare 10, 20, ceil, 123.4
|
||||
compare 13, 17, floor, 567.8
|
||||
10
test/asm/fixed-point-specific.out
Normal file
10
test/asm/fixed-point-specific.out
Normal file
@@ -0,0 +1,10 @@
|
||||
mul: 42.0000 == 42.0000
|
||||
div: 12.5000 == 12.5000
|
||||
pow: 982.5938 == 982.5943
|
||||
sin: 1.0000 == 1.0000
|
||||
cos: 0.0000 == 0.0000
|
||||
asin: 0.2500 == 0.2500
|
||||
acos: 0.2500 == 0.2500
|
||||
round: 2.0000 == 2.0000
|
||||
ceil: 124.0000 == 124.0000
|
||||
floor: 567.0000 == 567.0000
|
||||
@@ -1,2 +0,0 @@
|
||||
if "{@}"
|
||||
endc
|
||||
@@ -1,5 +0,0 @@
|
||||
error: if@-no-sect.asm(1):
|
||||
PC has no value outside a section
|
||||
warning: if@-no-sect.asm(1): [-Wnumeric-string]
|
||||
Treating 2-character string as a number
|
||||
error: Assembly aborted (1 error)!
|
||||
2
test/asm/long-section-name.asm
Normal file
2
test/asm/long-section-name.asm
Normal file
@@ -0,0 +1,2 @@
|
||||
SECTION "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit", ROM0[0]
|
||||
println "This is section ", SECTION(@)
|
||||
2
test/asm/long-section-name.err
Normal file
2
test/asm/long-section-name.err
Normal file
@@ -0,0 +1,2 @@
|
||||
warning: long-section-name.asm(1): [-Wlong-string]
|
||||
String constant too long
|
||||
1
test/asm/long-section-name.out
Normal file
1
test/asm/long-section-name.out
Normal file
@@ -0,0 +1 @@
|
||||
This is section Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor i
|
||||
@@ -1 +0,0 @@
|
||||
PRINTLN "{_NARG}"
|
||||
@@ -1,3 +0,0 @@
|
||||
error: narg-nosect.asm(1):
|
||||
_NARG does not make sense outside of a macro
|
||||
error: Assembly aborted (1 error)!
|
||||
@@ -1 +0,0 @@
|
||||
$0
|
||||
@@ -1,5 +1,3 @@
|
||||
warning: opt-Q.asm(10) -> opt-Q.asm::REPT~1(12) -> opt-Q.asm::test(5): [-Wlarge-constant]
|
||||
Magnitude of fixed-point constant is too large
|
||||
error: opt-Q.asm(17) -> opt-Q.asm::test(3):
|
||||
Argument for option 'Q' must be between 1 and 31
|
||||
error: opt-Q.asm(18) -> opt-Q.asm::test(3):
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
PRINTLN %Oo_Oo_Oo
|
||||
|
||||
OPT b.X
|
||||
PRINTLN %..X.X.X.
|
||||
PRINTLN %..X._X.X.
|
||||
|
||||
1
test/asm/opt-b.flags
Normal file
1
test/asm/opt-b.flags
Normal file
@@ -0,0 +1 @@
|
||||
-Weverything -b oO
|
||||
@@ -1 +1,2 @@
|
||||
$2A
|
||||
$2A
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
PRINTLN `pqpq_rsrs
|
||||
|
||||
OPT g.x0X
|
||||
PRINTLN `.x.x0X0X
|
||||
PRINTLN `.x.x_0X0X
|
||||
|
||||
1
test/asm/opt-g.flags
Normal file
1
test/asm/opt-g.flags
Normal file
@@ -0,0 +1 @@
|
||||
-Weverything -g pqrs
|
||||
@@ -1 +1,2 @@
|
||||
$F55
|
||||
$F55
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
IF DEF(@)
|
||||
PRINTLN "defined"
|
||||
ELSE
|
||||
PRINTLN "not defined"
|
||||
ENDC
|
||||
@@ -1 +0,0 @@
|
||||
defined
|
||||
3
test/asm/preinclude.asm
Normal file
3
test/asm/preinclude.asm
Normal file
@@ -0,0 +1,3 @@
|
||||
warn "main file"
|
||||
def v3 = v1 + v2
|
||||
println "{d:v1} + {d:v2} = {d:v3}"
|
||||
4
test/asm/preinclude.err
Normal file
4
test/asm/preinclude.err
Normal file
@@ -0,0 +1,4 @@
|
||||
warning: preinclude.asm(0) -> preinclude.inc(1): [-Wuser]
|
||||
pre-included file
|
||||
warning: preinclude.asm(1): [-Wuser]
|
||||
main file
|
||||
1
test/asm/preinclude.flags
Normal file
1
test/asm/preinclude.flags
Normal file
@@ -0,0 +1 @@
|
||||
-Weverything -P preinclude.inc
|
||||
11
test/asm/preinclude.inc
Normal file
11
test/asm/preinclude.inc
Normal file
@@ -0,0 +1,11 @@
|
||||
warn "pre-included file"
|
||||
|
||||
def v1 = 12
|
||||
rept 3
|
||||
println "rept 3"
|
||||
endr
|
||||
|
||||
def v2 = 34
|
||||
for i, 3
|
||||
println "for {d:i}/3"
|
||||
endr
|
||||
7
test/asm/preinclude.out
Normal file
7
test/asm/preinclude.out
Normal file
@@ -0,0 +1,7 @@
|
||||
rept 3
|
||||
rept 3
|
||||
rept 3
|
||||
for 0/3
|
||||
for 1/3
|
||||
for 2/3
|
||||
12 + 34 = 46
|
||||
7
test/asm/section-name-invalid.asm
Normal file
7
test/asm/section-name-invalid.asm
Normal file
@@ -0,0 +1,7 @@
|
||||
SECTION "test", ROM0
|
||||
|
||||
Label:
|
||||
println SECTION(Label) ; OK
|
||||
|
||||
DEF Value EQU 42
|
||||
println SECTION(Value) ; not OK
|
||||
2
test/asm/section-name-invalid.err
Normal file
2
test/asm/section-name-invalid.err
Normal file
@@ -0,0 +1,2 @@
|
||||
FATAL: section-name-invalid.asm(7):
|
||||
"Value" does not belong to any section
|
||||
1
test/asm/section-name-invalid.out
Normal file
1
test/asm/section-name-invalid.out
Normal file
@@ -0,0 +1 @@
|
||||
test
|
||||
19
test/asm/section-name.asm
Normal file
19
test/asm/section-name.asm
Normal file
@@ -0,0 +1,19 @@
|
||||
SECTION "aaa", ROM0[5]
|
||||
println SECTION(@)
|
||||
Label1: println SECTION(Label1)
|
||||
dw STARTOF(SECTION(@))
|
||||
|
||||
SECTION UNION "bbb", WRAM0
|
||||
println SECTION(@)
|
||||
Label2:
|
||||
.local1: println SECTION(Label2.local1)
|
||||
.local2: println SECTION(.local2)
|
||||
|
||||
SECTION FRAGMENT "ccc", HRAM
|
||||
println SECTION(@)
|
||||
: println SECTION(:-)
|
||||
|
||||
PUSHS
|
||||
SECTION "ddd", ROMX
|
||||
println SECTION(@)
|
||||
POPS
|
||||
0
test/asm/section-name.err
Normal file
0
test/asm/section-name.err
Normal file
8
test/asm/section-name.out
Normal file
8
test/asm/section-name.out
Normal file
@@ -0,0 +1,8 @@
|
||||
aaa
|
||||
aaa
|
||||
bbb
|
||||
bbb
|
||||
bbb
|
||||
ccc
|
||||
ccc
|
||||
ddd
|
||||
BIN
test/asm/section-name.out.bin
Normal file
BIN
test/asm/section-name.out.bin
Normal file
Binary file not shown.
@@ -2,6 +2,9 @@
|
||||
|
||||
export LC_ALL=C
|
||||
|
||||
# Game Boy release date, 1989-04-21T12:34:56Z (for reproducible test results)
|
||||
export SOURCE_DATE_EPOCH=609165296
|
||||
|
||||
o="$(mktemp)"
|
||||
gb="$(mktemp)"
|
||||
input="$(mktemp)"
|
||||
@@ -52,20 +55,6 @@ else
|
||||
rm -f version.asm
|
||||
fi
|
||||
|
||||
# Add the quote test, except on Windows
|
||||
if uname | grep -viq mingw; then
|
||||
cat > quote\"file.asm <<EOF
|
||||
WARN __FILE__
|
||||
EOF
|
||||
cat > quote\"file.out <<EOF
|
||||
EOF
|
||||
cat > quote\"file.err <<EOF
|
||||
warning: quote"file.asm(1): [-Wuser]
|
||||
quote"file.asm
|
||||
while expanding symbol "__FILE__"
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Check whether to use '.simple.err' files if they exist
|
||||
# (rgbasm with pre-3.0 Bison just reports "syntax error")
|
||||
$RGBASM -Weverything -o $o syntax-error.asm > $output 2> $errput
|
||||
@@ -76,6 +65,11 @@ if ! diff --strip-trailing-cr syntax-error.err $errput; then
|
||||
fi
|
||||
|
||||
for i in *.asm; do
|
||||
flags=${i%.asm}.flags
|
||||
RGBASMFLAGS=-Weverything
|
||||
if [ -f $flags ]; then
|
||||
RGBASMFLAGS="$(head -n 1 "$flags")" # Allow other lines to serve as comments
|
||||
fi
|
||||
for variant in '' '.pipe'; do
|
||||
echo "${bold}${green}${i%.asm}${variant}...${rescolors}${resbold}"
|
||||
desired_errname=${i%.asm}.err
|
||||
@@ -83,7 +77,7 @@ for i in *.asm; do
|
||||
desired_errname=${i%.asm}.simple.err
|
||||
fi
|
||||
if [ -z "$variant" ]; then
|
||||
$RGBASM -Weverything -o $o $i > $output 2> $errput
|
||||
$RGBASM $RGBASMFLAGS -o $o $i > $output 2> $errput
|
||||
desired_output=${i%.asm}.out
|
||||
desired_errput=$desired_errname
|
||||
else
|
||||
@@ -97,7 +91,7 @@ for i in *.asm; do
|
||||
# stdin redirection makes the input an unseekable pipe - a scenario
|
||||
# that's harder to deal with and was broken when the feature was
|
||||
# first implemented.
|
||||
cat $i | $RGBASM -Weverything -o $o - > $output 2> $errput
|
||||
cat $i | $RGBASM $RGBASMFLAGS -o $o - > $output 2> $errput
|
||||
|
||||
# Use two otherwise unused files for temp storage
|
||||
desired_output=$input
|
||||
|
||||
38
test/asm/trigonometry.asm
Normal file
38
test/asm/trigonometry.asm
Normal file
@@ -0,0 +1,38 @@
|
||||
for Q, 2, 31
|
||||
OPT Q.{d:Q}
|
||||
|
||||
assert sin(0.25) == 1.0
|
||||
assert asin(1.0) == 0.25
|
||||
|
||||
assert sin(0.0) == 0.0
|
||||
assert asin(0.0) == 0.0
|
||||
|
||||
assert cos(0.0) == 1.0
|
||||
assert acos(1.0) == 0.0
|
||||
|
||||
if Q > 2 ; can't represent 0.125 in Q.2
|
||||
assert tan(0.125) == 1.0
|
||||
assert atan(1.0) == 0.125
|
||||
else
|
||||
assert tan(0.0) == 0.0
|
||||
assert atan(0.0) == 0.0
|
||||
endc
|
||||
endr
|
||||
|
||||
SECTION "sine", ROM0[0]
|
||||
OPT Q.16
|
||||
; Generate a table of sine values from sin(0.0) to sin(1.0), with
|
||||
; amplitude scaled from [-1.0, 1.0] to [0.0, 128.0]
|
||||
DEF turns = 0.0
|
||||
REPT 256
|
||||
db MUL(64.0, SIN(turns) + 1.0) >> 16
|
||||
DEF turns += 1.0 / 256
|
||||
ENDR
|
||||
|
||||
|
||||
SECTION "cosine", ROM0[256]
|
||||
OPT Q.8
|
||||
; 32 samples of cos(x) from x=0 to x=pi radians=0.5 turns
|
||||
for x, 0.0, 0.5, 0.5 / 32
|
||||
dw cos(x)
|
||||
endr
|
||||
0
test/asm/trigonometry.err
Normal file
0
test/asm/trigonometry.err
Normal file
0
test/asm/trigonometry.out
Normal file
0
test/asm/trigonometry.out
Normal file
BIN
test/asm/trigonometry.out.bin
Normal file
BIN
test/asm/trigonometry.out.bin
Normal file
Binary file not shown.
21
test/asm/undefined-builtins.asm
Normal file
21
test/asm/undefined-builtins.asm
Normal file
@@ -0,0 +1,21 @@
|
||||
; not inside a section
|
||||
assert !DEF(@)
|
||||
println @
|
||||
println "{@}?"
|
||||
|
||||
; not inside a macro
|
||||
assert !DEF(_NARG)
|
||||
println _NARG
|
||||
println "{_NARG}?"
|
||||
|
||||
SECTION "s", ROM0[$42]
|
||||
assert DEF(@)
|
||||
println @
|
||||
println "{@}!"
|
||||
|
||||
MACRO m
|
||||
assert DEF(_NARG)
|
||||
println _NARG
|
||||
println "{_NARG}!"
|
||||
ENDM
|
||||
m 1, 2, 3
|
||||
9
test/asm/undefined-builtins.err
Normal file
9
test/asm/undefined-builtins.err
Normal file
@@ -0,0 +1,9 @@
|
||||
error: undefined-builtins.asm(3):
|
||||
PC has no value outside a section
|
||||
error: undefined-builtins.asm(4):
|
||||
Interpolated symbol "@" does not exist
|
||||
error: undefined-builtins.asm(8):
|
||||
_NARG does not make sense outside of a macro
|
||||
error: undefined-builtins.asm(9):
|
||||
Interpolated symbol "_NARG" does not exist
|
||||
error: Assembly aborted (4 errors)!
|
||||
8
test/asm/undefined-builtins.out
Normal file
8
test/asm/undefined-builtins.out
Normal file
@@ -0,0 +1,8 @@
|
||||
$0
|
||||
?
|
||||
$0
|
||||
?
|
||||
$42
|
||||
$42!
|
||||
$3
|
||||
$3!
|
||||
@@ -1,10 +1,18 @@
|
||||
opt Wunmapped-char=1 ; non-default empty charmaps can have unmapped chars
|
||||
|
||||
SECTION "test", ROM0
|
||||
|
||||
db "A" ; OK, default empty charmap
|
||||
|
||||
pushc
|
||||
newcharmap custom
|
||||
db "A" ; unmapped in non-default charmap
|
||||
db "A" ; OK, unmapped in non-default empty charmap
|
||||
pusho
|
||||
opt Wunmapped-char=2
|
||||
db "A" ; unmapped in non-default empty charmap
|
||||
popo
|
||||
charmap "C", $99
|
||||
db "A" ; unmapped in non-empty charmap
|
||||
popc
|
||||
|
||||
db "A" ; OK, default empty charmap again
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
warning: unmapped-char.asm(7): [-Wunmapped-char]
|
||||
warning: unmapped-char.asm(12): [-Wunmapped-char]
|
||||
Unmapped character 'A' not in main charmap
|
||||
warning: unmapped-char.asm(15): [-Wunmapped-char]
|
||||
Unmapped character 'A'
|
||||
warning: unmapped-char.asm(13): [-Wunmapped-char]
|
||||
warning: unmapped-char.asm(21): [-Wunmapped-char]
|
||||
Unmapped character 'A'
|
||||
|
||||
@@ -1 +1 @@
|
||||
AAAAA
|
||||
AAAAAAA
|
||||
41
test/patches/pokecrystal.patch
Normal file
41
test/patches/pokecrystal.patch
Normal file
@@ -0,0 +1,41 @@
|
||||
diff --git a/Makefile b/Makefile
|
||||
index bf54eb4ce..d75f86aae 100644
|
||||
--- a/Makefile
|
||||
+++ b/Makefile
|
||||
@@ -104,7 +104,7 @@ tools:
|
||||
$(MAKE) -C tools/
|
||||
|
||||
|
||||
-RGBASMFLAGS = -L -Weverything -Wnumeric-string=2 -Wtruncation=1
|
||||
+RGBASMFLAGS = -L -Weverything -Wnumeric-string=2 -Wtruncation=1 -Q8
|
||||
# Create a sym/map for debug purposes if `make` run with `DEBUG=1`
|
||||
ifeq ($(DEBUG),1)
|
||||
RGBASMFLAGS += -E
|
||||
@@ -208,9 +208,11 @@ gfx/pokemon/girafarig/front.animated.tilemap: gfx/pokemon/girafarig/front.2bpp g
|
||||
|
||||
### Misc file-specific graphics rules
|
||||
|
||||
-gfx/pokemon/%/back.2bpp: rgbgfx += -h
|
||||
+gfx/pokemon/%/back.2bpp: rgbgfx += -h -c embedded
|
||||
+gfx/pokemon/%/front.2bpp: rgbgfx += -c embedded
|
||||
+gfx/pokemon/unown_%/front.2bpp: rgbgfx =
|
||||
|
||||
-gfx/trainers/%.2bpp: rgbgfx += -h
|
||||
+gfx/trainers/%.2bpp: rgbgfx += -h -c embedded
|
||||
|
||||
gfx/pokemon/egg/unused_front.2bpp: rgbgfx += -h
|
||||
|
||||
diff --git a/macros/data.asm b/macros/data.asm
|
||||
index c2686c9f4..4dac70f3a 100644
|
||||
--- a/macros/data.asm
|
||||
+++ b/macros/data.asm
|
||||
@@ -97,7 +97,7 @@ MACRO sine_table
|
||||
; \1 samples of sin(x) from x=0 to x<32768 (pi radians)
|
||||
DEF x = 0
|
||||
rept \1
|
||||
- dw (sin(x) + (sin(x) & $ff)) >> 8 ; round up
|
||||
- DEF x += DIV(32768, \1) ; a circle has 65536 "degrees"
|
||||
+ dw sin(x)
|
||||
+ DEF x += 0.5 / (\1)
|
||||
endr
|
||||
ENDM
|
||||
@@ -25,32 +25,20 @@ done
|
||||
# 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=2022-03-12 --single-branch
|
||||
fi
|
||||
pushd pokecrystal
|
||||
git fetch
|
||||
git checkout a3e31d6463e6313aed12ebc733b3f772f2fc78d7
|
||||
make clean
|
||||
make -j4 compare RGBDS=../../
|
||||
popd
|
||||
test_downstream() { # owner/repo shallow-since commit make-target
|
||||
if [ ! -d ${1##*/} ]; then
|
||||
git clone https://github.com/$1.git --shallow-since=$2 --single-branch
|
||||
fi
|
||||
pushd ${1##*/}
|
||||
git checkout -f $3
|
||||
if [ -f ../patches/${1##*/}.patch ]; then
|
||||
git apply --ignore-whitespace ../patches/${1##*/}.patch
|
||||
fi
|
||||
make clean
|
||||
make -j4 $4 RGBDS=../../
|
||||
popd
|
||||
}
|
||||
|
||||
if [ ! -d pokered ]; then
|
||||
git clone https://github.com/pret/pokered.git --shallow-since=2022-03-07 --single-branch
|
||||
fi
|
||||
pushd pokered
|
||||
git fetch
|
||||
git checkout a75dd222709c92ae136d835ff2451391d5a88e45
|
||||
make clean
|
||||
make -j4 compare RGBDS=../../
|
||||
popd
|
||||
|
||||
if [ ! -d ucity ]; then
|
||||
git clone https://github.com/AntonioND/ucity.git --shallow-since=2020-11-01 --single-branch
|
||||
fi
|
||||
pushd ucity
|
||||
git fetch
|
||||
git checkout d8878233da7a6569f09f87b144cb5bf140146a0f
|
||||
make clean
|
||||
make -j4 RGBDS=../../
|
||||
popd
|
||||
test_downstream pret/pokecrystal 2022-09-29 70a3ec1accb6de1c1c273470af0ddfa2edc1b0a9 compare
|
||||
test_downstream pret/pokered 2022-09-29 2b52ceb718b55dce038db24d177715ae4281d065 compare
|
||||
test_downstream AntonioND/ucity 2022-04-20 d8878233da7a6569f09f87b144cb5bf140146a0f ''
|
||||
|
||||
Reference in New Issue
Block a user