Compare commits

...

35 Commits

Author SHA1 Message Date
Rangi
f90857032c Version 0.6.0 2022-10-02 19:08:13 -04:00
Rangi
1653a9a3f2 Use -flto=auto 2022-10-02 13:50:35 -04:00
Rangi
3c049983f1 Fixed-point functions can take specific precision (#1086) 2022-10-02 16:56:08 +02:00
Rangi
8553b61a94 Fixed-point values can use all 32-Q magnitude bits (#1085) 2022-10-02 11:08:38 +02:00
ISSOtm
ab12c474d2 Properly exclude GCC from macOS matrices 2022-10-02 02:50:38 -04:00
ISSOtm
8ccbd9dc36 Properly build and link against libpng
Doing it right this time.
Also bundling the newly required DLLs.
2022-10-02 02:50:38 -04:00
ISSOtm
b8307432b8 Fix use of bitwise OR instead of logical
Thanks, Clang!
2022-10-02 02:50:38 -04:00
ISSOtm
80a62a8a03 Update CI target OSes
Remove platforms deprecated by GitHub Actions
Add new platforms supported by the same
2022-10-02 02:50:38 -04:00
Rangi
bbe28faab4 Sort rgbgfx's -r option alphabetically 2022-10-01 21:22:39 -04:00
Rangi
106ad30e5a Allow fixed-point constants to have unsigned range (#1084)
For example with Q.4, $F0 is 15.0, which no longer warns
2022-10-01 23:32:34 +02:00
Rangi
a1107fc5cf Refactor !!x to x != 0
Also limit comments and docs to single "!"s
2022-10-01 14:09:02 -04:00
Rangi
969412af24 Parse HEX palettes (#1081)
Addresses one item of #1065
2022-10-01 12:45:00 -04:00
Eldred Habert
c10345f26d Comply with sym file spec (#1078)
Co-authored-by: Rangi <35663410+Rangi42@users.noreply.github.com>
2022-10-01 12:35:00 -04:00
ISSOtm
6fd5c94b27 Document gbc pal spec format
I *knew* I had forgotten something!
2022-10-01 10:48:40 +02:00
Rangi
ddb1d0b6aa Parse GPL palettes, and fix PSP palette parsing (#1080)
Addresses one item of #1065
2022-10-01 10:46:13 +02:00
Rangi
08545643cf Only define @ and _NARG when they have values (#1073)
Fixes #1069

Co-authored-by: Eldred Habert <eldredhabert0@gmail.com>
2022-10-01 01:04:03 +02:00
Rangi
140c6b169e Patch pokecrystal to use embedded palettes 2022-10-01 00:51:14 +02:00
Rangi
d86d24bdc1 Remove legacy support for generating a palette with unused colors
If you need an explicit set of colors, possibly including
unused ones, use `-c`.

Fixes #1062
2022-10-01 00:51:14 +02:00
Eldred Habert
a1a919579c Add support for GBC palette dumps to -c (#1075)
Fixes #1063
2022-09-30 17:09:28 -04:00
Rangi
a47da5f71f Deprecate __FILE__ and __LINE__ (#1072)
Unlike C, these constants are not convenient for logging in macros,
since they always report the same data (their location in the macro).

Fixes #1068
2022-09-30 19:48:30 +02:00
Rangi
68ad926279 Patch projects so CI will build (#1071)
Fixes #1070
2022-09-30 12:19:11 +02:00
Rangi
dec4133e84 SECTION(symbol) returns the name of a symbol's section (#1066)
Fixes #963

Co-authored-by: Eldred Habert <eldredhabert0@gmail.com>
2022-09-30 01:59:33 +02:00
Rangi
c35cb6ac32 Warning levels -Wunmapped-char=0/1/2 (#1061)
Fixes #1058
2022-09-29 18:14:04 -04:00
Rangi
023884d2b0 Redefine the trig functions to divide circles into 1.0 turns (#1060)
This makes their behavior consistent across Q settings

Fixes #1059
2022-09-29 10:57:29 +02:00
ISSOtm
3567faf395 Use backslash escape instead of "backwards slash" in man pages
The latter is in the "Lines" category, which seems inappropriate.
2022-09-26 09:45:25 +02:00
Eldred Habert
6502ed3919 Add -I as an alias for -i in rgbasm (#1056)
Co-authored-by: Rangi <35663410+Rangi42@users.noreply.github.com>
2022-09-26 03:42:30 -04:00
Rangi
b1a241233e Preserve Unix line endings for .bash and .flags files (#1054)
Fixes #955
2022-09-25 11:18:44 +02:00
Rangi
f88968ec20 Fix rgbasm -b and rgbasm -g (#1052)
Fixes #1051
2022-09-25 10:22:55 +02:00
Rangi
5ad8a8c958 Warn when a duplicate CLI argument overrides a previous one (#1053)
Fixes #1050
2022-09-25 10:04:30 +02:00
Rangi
2827374505 Use STD*_FILENO constants (#1055)
These are defined in platform.h, but not consistently used

Co-authored-by: Eldred Habert <eldredhabert0@gmail.com>
2022-09-25 10:02:53 +02:00
Rangi
b8385a50e3 Support -P/--preinclude to pre-INCLUDE a file (#1043)
Fixes #1041

Co-authored-by: ISSOtm <eldredhabert0@gmail.com>
2022-09-24 12:37:16 -04:00
ISSOtm
02923a67f3 Use tabs for indentation in map files
As requested by #1012
2022-09-24 12:58:48 +02:00
Rangi
f5b1990604 Document that symbol interpolation works outside of strings too 2022-09-22 01:15:17 -04:00
ISSOtm
0794da22bc Clarify at-files documentation 2022-09-13 08:34:41 +02:00
Rangi
6df75f7af3 Summarize used and free space at the end of the .map file
Fixes #1046
2022-09-12 23:16:09 +02:00
98 changed files with 1466 additions and 625 deletions

4
.gitattributes vendored
View File

@@ -1,2 +1,6 @@
# Shell scripts need Unix line endings (see https://github.com/gbdev/rgbds/issues/841) # Shell scripts need Unix line endings (see https://github.com/gbdev/rgbds/issues/841)
*.sh text eol=lf *.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

View File

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

View File

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

View File

@@ -1,44 +1,34 @@
#!/bin/sh #!/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 set -e
pngver=1.6.37 pngver=1.6.37
_apngver=$pngver arch="$1"
_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/
## Grab sources and check them ## Grab sources and check them
wget http://downloads.sourceforge.net/sourceforge/libpng/libpng-$pngver.tar.xz wget http://downloads.sourceforge.net/project/libpng/libpng16/$pngver/libpng-$pngver.tar.xz
wget http://downloads.sourceforge.net/project/apng/libpng/libpng16/libpng-$_apngver-apng.patch.gz wget http://downloads.sourceforge.net/project/apng/libpng/libpng16/libpng-$pngver-apng.patch.gz
sha256sum -c .github/actions/mingw-w64-libpng-dev.sha256sums sha256sum -c .github/actions/mingw-w64-libpng-dev.sha256sums
## Extract sources ## Extract sources and patch them
tar -xf libpng-$pngver.tar.xz 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! ## Start building!
cd libpng-$pngver mkdir -p build
# Patch in apng support cd build
patch -p0 ../libpng-$_apngver-apng.patch ../libpng-$pngver/configure \
--host="$arch" --target="$arch" \
mkdir -p build-${_arch} --prefix="/usr/$arch" \
cd build-${_arch} --enable-shared --disable-static \
${_arch}-configure LDFLAGS=-static-libgcc CPPFLAGS="-D_FORTIFY_SOURCE=2" \
make CFLAGS="-O2 -pipe -fno-plt -fexceptions --param=ssp-buffer-size=4" \
LDFLAGS="-Wl,-O1,--sort-common,--as-needed -fstack-protector"
make -kj
make install make install

View File

@@ -6,7 +6,7 @@ on:
jobs: jobs:
windows: windows:
runs-on: windows-2019 runs-on: windows-2022
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Get version from tag - name: Get version from tag

View File

@@ -7,7 +7,7 @@ on:
jobs: jobs:
build: build:
if: github.repository_owner == 'gbdev' if: github.repository_owner == 'gbdev'
runs-on: ubuntu-18.04 runs-on: ubuntu-22.04
steps: steps:
- name: Checkout rgbds@release - name: Checkout rgbds@release
uses: actions/checkout@v2 uses: actions/checkout@v2
@@ -18,17 +18,10 @@ jobs:
with: with:
repository: ${{ github.repository_owner }}/rgbds-www repository: ${{ github.repository_owner }}/rgbds-www
path: rgbds-www path: rgbds-www
# `-O toc` was added in 1.14.5, but the repos only have 1.14.4 - name: Install groff and mandoc
- name: Build and install mandoc + install groff
run: | run: |
sudo apt-get -qq update sudo apt-get -qq update
sudo apt-get install -yq groff zlib1g-dev sudo apt-get install -yq groff mandoc
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
- name: Update pages - name: Update pages
working-directory: rgbds/man working-directory: rgbds/man
run: | # The ref appears to be in the format "refs/tags/<version>", so strip that run: | # The ref appears to be in the format "refs/tags/<version>", so strip that

View File

@@ -7,14 +7,14 @@ jobs:
unix-testing: unix-testing:
strategy: strategy:
matrix: 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] cc: [gcc, clang]
buildsys: [make, cmake] buildsys: [make, cmake]
exclude: exclude:
# `gcc` is just an alias to `clang` on macOS, don't bother # `gcc` is just an alias to `clang` on macOS, don't bother
- os: macos-10.15 - os: macos-11
cc: gcc cc: gcc
- os: macos-11.0 - os: macos-12
cc: gcc cc: gcc
include: include:
- cc: gcc - cc: gcc
@@ -64,6 +64,7 @@ jobs:
strategy: strategy:
matrix: matrix:
bits: [32, 64] bits: [32, 64]
os: [windows-2019, windows-2022]
include: include:
- bits: 32 - bits: 32
arch: x86 arch: x86
@@ -72,7 +73,7 @@ jobs:
arch: x86_x64 arch: x86_x64
platform: x64 platform: x64
fail-fast: false fail-fast: false
runs-on: windows-2019 runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Get zlib, libpng and bison - name: Get zlib, libpng and bison
@@ -149,7 +150,6 @@ jobs:
strategy: strategy:
matrix: matrix:
bits: [32, 64] bits: [32, 64]
os: [ubuntu-18.04]
include: include:
- bits: 32 - bits: 32
arch: i686 arch: i686
@@ -158,7 +158,7 @@ jobs:
arch: x86-64 arch: x86-64
triplet: x86_64-w64-mingw32 triplet: x86_64-w64-mingw32
fail-fast: false fail-fast: false
runs-on: ${{ matrix.os }} runs-on: ubuntu-22.04
env: env:
DIST_DIR: win${{ matrix.bits }} DIST_DIR: win${{ matrix.bits }}
steps: steps:
@@ -168,8 +168,8 @@ jobs:
run: | run: |
./.github/actions/install_deps.sh ${{ matrix.os }} ./.github/actions/install_deps.sh ${{ matrix.os }}
- name: Install MinGW - name: Install MinGW
run: | run: | # dpkg-dev is apparently required for pkg-config for cross-building
sudo apt-get install {gcc,g++}-mingw-w64-${{ matrix.arch }} mingw-w64-tools libz-mingw-w64-dev 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 - name: Install libpng dev headers for MinGW
run: | run: |
sudo ./.github/actions/mingw-w64-libpng-dev.sh ${{ matrix.triplet }} sudo ./.github/actions/mingw-w64-libpng-dev.sh ${{ matrix.triplet }}
@@ -177,17 +177,12 @@ jobs:
run: | run: |
make mingw${{ matrix.bits }} -j Q= make mingw${{ matrix.bits }} -j Q=
- name: Package binaries - name: Package binaries
run: | run: | # DLL dependencies can be figured out using e.g. Dependency Walker
mkdir bins mkdir bins
mv rgbasm bins/rgbasm.exe mv -v rgb{asm,link,fix,gfx}.exe bins/
mv rgblink bins/rgblink.exe cp -v /usr/${{ matrix.triplet }}/lib/zlib1.dll bins
mv rgbfix bins/rgbfix.exe cp -v /usr/${{ matrix.triplet }}/bin/libpng16-16.dll bins
mv rgbgfx bins/rgbgfx.exe [ "${{ matrix.bits }}" -ne 32 ] || cp -v /usr/lib/gcc/${{ matrix.triplet }}/10-win32/lib{gcc_s_dw2-1,ssp-0,stdc++-6}.dll bins
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}
- name: Upload Windows binaries - name: Upload Windows binaries
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
@@ -205,9 +200,10 @@ jobs:
needs: windows-xbuild needs: windows-xbuild
strategy: strategy:
matrix: matrix:
os: [windows-2019, windows-2022]
bits: [32, 64] bits: [32, 64]
fail-fast: false fail-fast: false
runs-on: windows-2019 runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Retrieve binaries - name: Retrieve binaries

View File

@@ -17,7 +17,7 @@ on:
jobs: jobs:
build: build:
if: github.repository_owner == 'gbdev' if: github.repository_owner == 'gbdev'
runs-on: ubuntu-18.04 runs-on: ubuntu-22.04
steps: steps:
- name: Checkout rgbds@master - name: Checkout rgbds@master
uses: actions/checkout@v2 uses: actions/checkout@v2
@@ -31,16 +31,10 @@ jobs:
repository: gbdev/rgbds-www repository: gbdev/rgbds-www
ref: master ref: master
path: rgbds-www path: rgbds-www
- name: Build and install mandoc + install groff - name: Install groff and mandoc
run: | run: |
sudo apt-get -qq update sudo apt-get -qq update
sudo apt-get install -yq groff zlib1g-dev sudo apt-get install -yq groff mandoc
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
- name: Update pages - name: Update pages
working-directory: rgbds/man working-directory: rgbds/man
run: | run: |

View File

@@ -9,6 +9,8 @@
.SUFFIXES: .SUFFIXES:
.SUFFIXES: .h .y .c .cpp .o .SUFFIXES: .h .y .c .cpp .o
.PHONY: all clean install checkcodebase checkpatch checkdiff develop debug mingw32 mingw64 wine-shim dist
# User-defined variables # User-defined variables
Q := @ Q := @
@@ -33,8 +35,8 @@ VERSION_STRING := `git describe --tags --dirty --always 2>/dev/null`
WARNFLAGS := -Wall -pedantic WARNFLAGS := -Wall -pedantic
# Overridable CFLAGS # Overridable CFLAGS
CFLAGS ?= -O3 -flto -DNDEBUG CFLAGS ?= -O3 -flto=auto -DNDEBUG
CXXFLAGS ?= -O3 -flto -DNDEBUG CXXFLAGS ?= -O3 -flto=auto -DNDEBUG
# Non-overridable CFLAGS # Non-overridable CFLAGS
# _ISOC11_SOURCE is required on certain platforms to get C11 on top of the C99-based POSIX 2008 # _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 \ REALCFLAGS := ${CFLAGS} ${WARNFLAGS} -std=gnu11 -I include \
@@ -95,6 +97,7 @@ rgblink_obj := \
src/link/section.o \ src/link/section.o \
src/link/symbol.o \ src/link/symbol.o \
src/extern/getopt.o \ src/extern/getopt.o \
src/extern/utf8decoder.o \
src/error.o \ src/error.o \
src/hashmap.o \ src/hashmap.o \
src/linkdefs.o \ src/linkdefs.o \
@@ -249,6 +252,13 @@ develop:
CFLAGS="-ggdb3 -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls" \ CFLAGS="-ggdb3 -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls" \
CXXFLAGS="-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. # Targets for the project maintainer to easily create Windows exes.
# This is not for Windows users! # This is not for Windows users!
# If you're building on Windows with Cygwin or Mingw, just follow the Unix # If you're building on Windows with Cygwin or Mingw, just follow the Unix
@@ -257,12 +267,12 @@ develop:
mingw32: mingw32:
$Q${MAKE} all test/gfx/randtilegen test/gfx/rgbgfx_test \ $Q${MAKE} all test/gfx/randtilegen test/gfx/rgbgfx_test \
CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ \ 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: mingw64:
$Q${MAKE} all test/gfx/randtilegen test/gfx/rgbgfx_test \ $Q${MAKE} all test/gfx/randtilegen test/gfx/rgbgfx_test \
CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ \ 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: wine-shim:
$Qecho '#!/bin/bash' > rgbshim.sh $Qecho '#!/bin/bash' > rgbshim.sh

View File

@@ -35,9 +35,10 @@ _rgbasm_completions() {
[b]="binary-digits:unk" [b]="binary-digits:unk"
[D]="define:unk" [D]="define:unk"
[g]="gfx-chars:unk" [g]="gfx-chars:unk"
[i]="include:dir" [I]="include:dir"
[M]="dependfile:glob-*.mk *.d" [M]="dependfile:glob-*.mk *.d"
[o]="output:glob-*.o" [o]="output:glob-*.o"
[P]="preinclude:glob-*.asm *.inc"
[p]="pad-value:unk" [p]="pad-value:unk"
[Q]="q-precision:unk" [Q]="q-precision:unk"
[r]="recursion-depth:unk" [r]="recursion-depth:unk"

View File

@@ -29,7 +29,7 @@ _rgbasm_warnings() {
'user:Warn when executing the WARN built-in' 'user:Warn when executing the WARN built-in'
) )
# TODO: handle `no-` and `error=` somehow? # TODO: handle `no-` and `error=` somehow?
# TODO: handle `=0|1|2` levels for `numeric-string` and `truncation`? # TODO: handle `=0|1|2` levels for `numeric-string`, `truncation`, and `unmapped-char`?
_describe warning warnings _describe warning warnings
} }
@@ -48,13 +48,14 @@ local args=(
'(-b --binary-digits)'{-b,--binary-digits}'+[Change chars for binary constants]:digit spec:' '(-b --binary-digits)'{-b,--binary-digits}'+[Change chars for binary constants]:digit spec:'
'*'{-D,--define}'+[Define a string symbol]:name + value (default 1):' '*'{-D,--define}'+[Define a string symbol]:name + value (default 1):'
'(-g --gfx-chars)'{-g,--gfx-chars}'+[Change chars for gfx constants]:chars spec:' '(-g --gfx-chars)'{-g,--gfx-chars}'+[Change chars for gfx constants]:chars spec:'
'(-i --include)'{-i,--include}'+[Add an include directory]:include path:_files -/' '(-I --include)'{-I,--include}'+[Add an include directory]:include path:_files -/'
'(-M --dependfile)'{-M,--dependfile}"+[List deps in make format]:output file:_files -g '*.{d,mk}'" '(-M --dependfile)'{-M,--dependfile}"+[List deps in make format]:output file:_files -g '*.{d,mk}'"
-MG'[Assume missing files should be generated]' -MG'[Assume missing files should be generated]'
-MP'[Add phony targets to all deps]' -MP'[Add phony targets to all deps]'
'*'-MT"+[Add a target to the rules]:target:_files -g '*.{d,mk,o}'" '*'-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}'" '*'-MQ"+[Add a target to the rules]:target:_files -g '*.{d,mk,o}'"
'(-o --output)'{-o,--output}'+[Output file]:output file:_files' '(-o --output)'{-o,--output}'+[Output file]:output file:_files'
'(-P --preinclude)'{-P,--preinclude}"+[Pre-include a file]:include file:_files -g '*.{asm,inc}'"
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:' '(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
'(-Q --q-precision)'{-Q,--q-precision}'+[Set fixed-point precision]:precision:' '(-Q --q-precision)'{-Q,--q-precision}'+[Set fixed-point precision]:precision:'
'(-r --recursion-depth)'{-r,--recursion-depth}'+[Set maximum recursion depth]:depth:' '(-r --recursion-depth)'{-r,--recursion-depth}'+[Set maximum recursion depth]:depth:'

View File

@@ -11,6 +11,8 @@
#include <stdint.h> #include <stdint.h>
#define DEFAULT_CHARMAP_NAME "main"
struct Charmap *charmap_New(char const *name, char const *baseName); struct Charmap *charmap_New(char const *name, char const *baseName);
void charmap_Delete(struct Charmap *charmap); void charmap_Delete(struct Charmap *charmap);
void charmap_Set(char const *name); void charmap_Set(char const *name);

View File

@@ -13,22 +13,22 @@
extern uint8_t fixPrecision; extern uint8_t fixPrecision;
uint8_t fix_Precision(void);
double fix_PrecisionFactor(void); double fix_PrecisionFactor(void);
void fix_Print(int32_t i); int32_t fix_Sin(int32_t i, int32_t q);
int32_t fix_Sin(int32_t i); int32_t fix_Cos(int32_t i, int32_t q);
int32_t fix_Cos(int32_t i); int32_t fix_Tan(int32_t i, int32_t q);
int32_t fix_Tan(int32_t i); int32_t fix_ASin(int32_t i, int32_t q);
int32_t fix_ASin(int32_t i); int32_t fix_ACos(int32_t i, int32_t q);
int32_t fix_ACos(int32_t i); int32_t fix_ATan(int32_t i, int32_t q);
int32_t fix_ATan(int32_t i); int32_t fix_ATan2(int32_t i, int32_t j, int32_t q);
int32_t fix_ATan2(int32_t i, int32_t j); int32_t fix_Mul(int32_t i, int32_t j, int32_t q);
int32_t fix_Mul(int32_t i, int32_t j); int32_t fix_Mod(int32_t i, int32_t j, int32_t q);
int32_t fix_Mod(int32_t i, int32_t j); int32_t fix_Div(int32_t i, int32_t j, int32_t q);
int32_t fix_Div(int32_t i, int32_t j); int32_t fix_Pow(int32_t i, int32_t j, int32_t q);
int32_t fix_Pow(int32_t i, int32_t j); int32_t fix_Log(int32_t i, int32_t j, int32_t q);
int32_t fix_Log(int32_t i, int32_t j); int32_t fix_Round(int32_t i, int32_t q);
int32_t fix_Round(int32_t i); int32_t fix_Ceil(int32_t i, int32_t q);
int32_t fix_Ceil(int32_t i); int32_t fix_Floor(int32_t i, int32_t q);
int32_t fix_Floor(int32_t i);
#endif // RGBDS_ASM_FIXPOINT_H #endif // RGBDS_ASM_FIXPOINT_H

View File

@@ -58,6 +58,7 @@ struct FileStackNode *fstk_GetFileStack(void);
char const *fstk_GetFileName(void); char const *fstk_GetFileName(void);
void fstk_AddIncludePath(char const *s); void fstk_AddIncludePath(char const *s);
void fstk_SetPreIncludeFile(char const *s);
/* /*
* @param path The user-provided file name * @param path The user-provided file name
* @param fullPath The address of a pointer, which will be made to point at the full path * @param fullPath The address of a pointer, which will be made to point at the full path

View File

@@ -127,6 +127,8 @@ struct Symbol *sym_FindExactSymbol(char const *symName);
struct Symbol *sym_FindUnscopedSymbol(char const *symName); struct Symbol *sym_FindUnscopedSymbol(char const *symName);
// Find a symbol, possibly scoped, by name // Find a symbol, possibly scoped, by name
struct Symbol *sym_FindScopedSymbol(char const *symName); 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 const *sym_GetPC(void);
struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size); struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size);
struct Symbol *sym_Ref(char const *symName); struct Symbol *sym_Ref(char const *symName);

View File

@@ -36,7 +36,6 @@ enum WarningID {
WARNING_OBSOLETE, // Obsolete things WARNING_OBSOLETE, // Obsolete things
WARNING_SHIFT, // Shifting undefined behavior WARNING_SHIFT, // Shifting undefined behavior
WARNING_SHIFT_AMOUNT, // Strange shift amount WARNING_SHIFT_AMOUNT, // Strange shift amount
WARNING_UNMAPPED_CHAR, // Character without charmap entry
WARNING_USER, // User warnings WARNING_USER, // User warnings
NB_PLAIN_WARNINGS, NB_PLAIN_WARNINGS,
@@ -49,6 +48,9 @@ enum WarningID {
// Implicit truncation loses some bits // Implicit truncation loses some bits
WARNING_TRUNCATION_1, WARNING_TRUNCATION_1,
WARNING_TRUNCATION_2, WARNING_TRUNCATION_2,
// Character without charmap entry
WARNING_UNMAPPED_CHAR_1,
WARNING_UNMAPPED_CHAR_2,
NB_PLAIN_AND_PARAM_WARNINGS, NB_PLAIN_AND_PARAM_WARNINGS,
#define NB_PARAM_WARNINGS (NB_PLAIN_AND_PARAM_WARNINGS - PARAM_WARNINGS_START) #define NB_PARAM_WARNINGS (NB_PLAIN_AND_PARAM_WARNINGS - PARAM_WARNINGS_START)

View File

@@ -10,7 +10,7 @@
#ifndef RGBDS_LINK_SECTION_H #ifndef RGBDS_LINK_SECTION_H
#define 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 <stdint.h>
#include <stdbool.h> #include <stdbool.h>

View File

@@ -10,7 +10,7 @@
#ifndef RGBDS_LINK_SYMBOL_H #ifndef RGBDS_LINK_SYMBOL_H
#define 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> #include <stdint.h>

View File

@@ -16,7 +16,6 @@ extern "C" {
#define PACKAGE_VERSION_MAJOR 0 #define PACKAGE_VERSION_MAJOR 0
#define PACKAGE_VERSION_MINOR 6 #define PACKAGE_VERSION_MINOR 6
#define PACKAGE_VERSION_PATCH 0 #define PACKAGE_VERSION_PATCH 0
#define PACKAGE_VERSION_RC 2
char const *get_package_version_string(void); char const *get_package_version_string(void);

View File

@@ -17,28 +17,28 @@
.Op Fl b Ar chars .Op Fl b Ar chars
.Op Fl D Ar name Ns Op = Ns Ar value .Op Fl D Ar name Ns Op = Ns Ar value
.Op Fl g Ar chars .Op Fl g Ar chars
.Op Fl i Ar path .Op Fl I Ar path
.Op Fl M Ar depend_file .Op Fl M Ar depend_file
.Op Fl MG .Op Fl MG
.Op Fl MP .Op Fl MP
.Op Fl MT Ar target_file .Op Fl MT Ar target_file
.Op Fl MQ Ar target_file .Op Fl MQ Ar target_file
.Op Fl o Ar out_file .Op Fl o Ar out_file
.Op Fl P Ar include_file
.Op Fl p Ar pad_value .Op Fl p Ar pad_value
.Op Fl Q Ar fix_precision .Op Fl Q Ar fix_precision
.Op Fl r Ar recursion_depth .Op Fl r Ar recursion_depth
.Op Fl W Ar warning .Op Fl W Ar warning
.Ar .Ar asmfile
.Sh DESCRIPTION .Sh DESCRIPTION
The The
.Nm .Nm
program creates an RGB object file from an assembly source file. program creates an RGB object file from an assembly source file.
The input The input
.Ar file .Ar asmfile
can be a file path, or can be a path to a file, or
.Cm \- .Cm \-
denoting to read from standard input.
.Cm stdin .
.Pp .Pp
Note that options can be abbreviated as long as the abbreviation is unambiguous: Note that options can be abbreviated as long as the abbreviation is unambiguous:
.Fl Fl verb .Fl Fl verb
@@ -86,8 +86,20 @@ Disables inserting a
instruction immediately after any instruction immediately after any
.Ic halt .Ic halt
instruction. instruction.
.It Fl i Ar path , Fl Fl include Ar path .It Fl I Ar path , Fl Fl include Ar path
Add an include 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 .It Fl L , Fl Fl preserve-ld
By default, By default,
.Nm .Nm
@@ -116,6 +128,7 @@ This makes
.Nm .Nm
assume that missing files are auto-generated: when assume that missing files are auto-generated: when
.Ic INCLUDE .Ic INCLUDE
.Pq including the implicit one from Fl P
or or
.Ic INCBIN .Ic INCBIN
is attempted on a non-existent file, it is added as a dependency, then is attempted on a non-existent file, it is added as a dependency, then
@@ -146,6 +159,12 @@ characters, essentially
.Sq $ . .Sq $ .
.It Fl o Ar out_file , Fl Fl output Ar out_file .It Fl o Ar out_file , Fl Fl output Ar out_file
Write an object file to the given filename. 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 .It Fl p Ar pad_value , Fl Fl pad-value Ar pad_value
When padding an image, pad with this value. When padding an image, pad with this value.
The default is 0x00. The default is 0x00.
@@ -301,12 +320,19 @@ warns when an N-bit value's absolute value is 2**N or greater.
or just or just
.Fl Wtruncation .Fl Wtruncation
also warns when an N-bit value is less than -2**(N-1), which will not fit in two's complement encoding. 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. 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 . .Sq main .
This warning is enabled by
.Fl Wall .
.It Fl Wno-user .It Fl Wno-user
Warn when the Warn when the
.Ic WARN .Ic WARN

View File

@@ -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 The description of the instructions supported by the Game Boy CPU is in
.Xr gbz80 7 . .Xr gbz80 7 .
.Pp .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. 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. This document is not intended to be a Game Boy hardware reference.
.Pp .Pp
@@ -57,27 +57,25 @@ and ending with
.Ql */ . .Ql */ .
It can be split across multiple lines, or occur in the middle of an expression: It can be split across multiple lines, or occur in the middle of an expression:
.Bd -literal -offset indent .Bd -literal -offset indent
X = /* the value of x DEF X = /* the value of x
should be 3 */ 3 should be 3 */ 3
.Ed .Ed
.Pp .Pp
Sometimes lines can be too long and it may be necessary to split them. 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: To do so, put a backslash at the end of the line:
.Bd -literal -offset indent .Bd -literal -offset indent
DB 1, 2, 3,\ \[rs] DB 1, 2, 3,\ \e
4, 5, 6,\ \[rs]\ ;\ Put it before any comments 4, 5, 6,\ \e\ ;\ Put it before any comments
7, 8, 9 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 world!"\ \ \ \ \ \ \ \ \ \ \ ;\ Any leading space is included
.Ed .Ed
.Ss Symbol interpolation .Ss Symbol interpolation
A funky feature is A funky feature is writing a symbol between
.Ql {symbol} .Ql {braces} ,
within a string, called called
.Dq symbol interpolation . .Dq symbol interpolation .
This will paste the contents of This will paste the symbol's contents as if they were part of the source file.
.Ql symbol
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 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 If it is a numeric symbol, its value is converted to hexadecimal notation with a dollar sign
.Sq $ .Sq $
@@ -85,7 +83,7 @@ prepended.
.Pp .Pp
Symbol interpolations can be nested, too! Symbol interpolations can be nested, too!
.Bd -literal -offset indent .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" DEF meaning EQUS "answer"
;\ Defines answer = 42 ;\ Defines answer = 42
DEF {meaning} = 42 DEF {meaning} = 42
@@ -171,19 +169,19 @@ Examples:
.Bd -literal -offset indent .Bd -literal -offset indent
SECTION "Test", ROM0[2] SECTION "Test", ROM0[2]
X: ;\ This works with labels **whose address is known** X: ;\ This works with labels **whose address is known**
Y = 3 ;\ This also works with variables DEF Y = 3 ;\ This also works with variables
SUM equ X + Y ;\ And likewise with numeric constants DEF SUM EQU X + Y ;\ And likewise with numeric constants
; Prints "%0010 + $3 == 5" ; Prints "%0010 + $3 == 5"
PRINTLN "{#05b:X} + {#x:Y} == {d:SUM}" PRINTLN "{#05b:X} + {#x:Y} == {d:SUM}"
rsset 32 rsset 32
PERCENT rb 1 ;\ Same with offset constants DEF PERCENT rb 1 ;\ Same with offset constants
VALUE = 20 DEF VALUE = 20
RESULT = MUL(20.0, 0.32) DEF RESULT = MUL(20.0, 0.32)
; Prints "32% of 20 = 6.40" ; Prints "32% of 20 = 6.40"
PRINTLN "{d:PERCENT}% of {d:VALUE} = {f:RESULT}" PRINTLN "{d:PERCENT}% of {d:VALUE} = {f:RESULT}"
WHO equs STRLWR("WORLD") DEF WHO EQUS STRLWR("WORLD")
; Prints "Hello world!" ; Prints "Hello world!"
PRINTLN "Hello {s:WHO}!" PRINTLN "Hello {s:WHO}!"
.Ed .Ed
@@ -348,44 +346,53 @@ delim $$
delim off delim off
.EN .EN
.Pp .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 ( The trigonometry functions (
.Ic SIN , .Ic SIN ,
.Ic COS , .Ic COS ,
.Ic TAN , .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 .Pp
These functions are useful for automatic generation of various tables. These functions are useful for automatic generation of various tables.
For example: For example:
.Bd -literal -offset indent .Bd -literal -offset indent
; Generate a 256-byte sine table with values in the range [0, 128] ; Generate a table of sine values from sin(0.0) to sin(1.0), with
; (shifted and scaled from the range [-1.0, 1.0]) ; amplitude scaled from [-1.0, 1.0] to [0.0, 128.0]
ANGLE = 0.0 DEF turns = 0.0
REPT 256 REPT 256
db (MUL(64.0, SIN(ANGLE)) + 64.0) >> 16 db MUL(64.0, SIN(turns) + 1.0) >> 16
ANGLE = ANGLE + 256.0 ; 256.0 = 65536 degrees / 256 entries DEF turns += 1.0 / 256
ENDR ENDR
.Ed .Ed
.Ss String expressions .Ss String expressions
The most basic string expression is any number of characters contained in double quotes The most basic string expression is any number of characters contained in double quotes
.Pq Ql \&"for instance" . .Pq Ql \&"for instance" .
The backslash character The backslash character
.Ql \[rs] .Ql \e
is special in that it causes the character following it to be is special in that it causes the character following it to be
.Dq escaped , .Dq escaped ,
meaning that it is treated differently from normal. meaning that it is treated differently from normal.
There are a number of escape sequences you can use within a string: 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 Sy String Ta Sy Meaning
.It Ql \[rs]\[rs] Ta Produces a backslash .It Ql \e\e Ta Produces a backslash
.It Ql \[rs]" Ta Produces a double quote without terminating .It Ql \e" Ta Produces a double quote without terminating
.It Ql \[rs]{ Ta Curly bracket left .It Ql \e{ Ta Curly bracket left
.It Ql \[rs]} Ta Curly bracket right .It Ql \e} Ta Curly bracket right
.It Ql \[rs]n Ta Newline ($0A) .It Ql \en Ta Newline ($0A)
.It Ql \[rs]r Ta Carriage return ($0D) .It Ql \er Ta Carriage return ($0D)
.It Ql \[rs]t Ta Tab ($09) .It Ql \et 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 Qo \e1 Qc \[en] Qo \e9 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 \e# 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@ Ta Label name suffix (Only in the body of a macro or a Ic REPT No block)
.El .El
(Note that some of those can be used outside of strings, when noted further in this document.) (Note that some of those can be used outside of strings, when noted further in this document.)
.Pp .Pp
@@ -393,9 +400,9 @@ Multi-line strings are contained in triple quotes
.Pq Ql \&"\&"\&"for instance\&"\&"\&" . .Pq Ql \&"\&"\&"for instance\&"\&"\&" .
Escape sequences work the same way in multi-line strings; however, literal newline 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 characters will be included as-is, without needing to escape them with
.Ql \[rs]r .Ql \er
or or
.Ql \[rs]n . .Ql \en .
.Pp .Pp
The following functions operate on string expressions. 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! 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 The result may be constant if
.Nm .Nm
is able to compute it. 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 .It Fn SIZEOF arg Ta Returns the size of the section named
.Ar arg . .Ar arg .
The result is not constant, since only RGBLINK can compute its value. 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 = 2
DEF COUNT = 3 DEF COUNT = 3
DEF COUNT = ARRAY_SIZE + COUNT DEF COUNT = ARRAY_SIZE + COUNT
COUNT = COUNT*2 DEF COUNT *= 2
;\ COUNT now has the value 14 ;\ COUNT now has the value 14
.Ed .Ed
.Pp .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 def NUM_ITEMS equ 0
MACRO add_item MACRO add_item
redef NUM_ITEMS equ NUM_ITEMS + 1 redef NUM_ITEMS equ NUM_ITEMS + 1
def ITEM_{02x:NUM_ITEMS} equ \[rs]1 def ITEM_{02x:NUM_ITEMS} equ \e1
ENDM ENDM
add_item 1 add_item 1
add_item 4 add_item 4
@@ -1131,7 +1143,7 @@ will not expand string constants in their names.
DEF COUNTREG EQUS "[hl+]" DEF COUNTREG EQUS "[hl+]"
ld a,COUNTREG ld a,COUNTREG
DEF PLAYER_NAME EQUS "\[rs]"John\[rs]"" DEF PLAYER_NAME EQUS "\e"John\e""
db PLAYER_NAME db PLAYER_NAME
.Ed .Ed
.Pp .Pp
@@ -1143,7 +1155,7 @@ This will be interpreted as:
.Pp .Pp
String constants can also be used to define small one-line macros: String constants can also be used to define small one-line macros:
.Bd -literal -offset indent .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 .Ed
.Pp .Pp
Note that colons Note that colons
@@ -1252,18 +1264,18 @@ ENDM
But this will: But this will:
.Bd -literal -offset indent .Bd -literal -offset indent
MACRO outer 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 definition
PURGE definition PURGE definition
ENDM ENDM
.Ed .Ed
.Pp .Pp
Macro arguments support all the escape sequences of strings, as well as Macro arguments support all the escape sequences of strings, as well as
.Ql \[rs], .Ql \e,
to escape commas, as well as to escape commas, as well as
.Ql \[rs]( .Ql \e(
and and
.Ql \[rs]) .Ql \e)
to escape parentheses, since those otherwise separate and enclose arguments, respectively. to escape parentheses, since those otherwise separate and enclose arguments, respectively.
.Ss Exporting and importing symbols .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. 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 .Ss Purging symbols
.Ic PURGE .Ic PURGE
allows you to completely remove a symbol from the symbol table as if it had never existed. 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, I can't stress this enough,
.Sy you seriously need to know what you are doing . .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. 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 @ Ta Ic EQU Ta PC value (essentially, the current memory address)
.It Dv _RS Ta Ic = Ta _RS Counter .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 _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 __DATE__ Ta Ic EQUS Ta Today's date
.It Dv __TIME__ Ta Ic EQUS Ta The current time .It Dv __TIME__ Ta Ic EQUS Ta The current time
.It Dv __ISO_8601_LOCAL__ Ta Ic EQUS Ta ISO 8601 timestamp (local) .It Dv __ISO_8601_LOCAL__ Ta Ic EQUS Ta ISO 8601 timestamp (local)
@@ -1561,19 +1571,19 @@ ENDM
.Pp .Pp
This is fine, but only if you use the macro no more than once per scope. 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 To get around this problem, there is the escape sequence
.Ic \[rs]@ .Ic \e@
that expands to a unique string. that expands to a unique string.
.Pp .Pp
.Ic \[rs]@ .Ic \e@
also works in also works in
.Ic REPT .Ic REPT
blocks. blocks.
.Bd -literal -offset indent .Bd -literal -offset indent
MACRO LoopyMacro MACRO LoopyMacro
xor a,a xor a,a
\&.loop\[rs]@ ld [hl+],a \&.loop\e@ ld [hl+],a
dec c dec c
jr nz,.loop\[rs]@ jr nz,.loop\e@
ENDM ENDM
.Ed .Ed
.Pp .Pp
@@ -1593,18 +1603,18 @@ which references the same macro, which has the same problem.
.Pp .Pp
It's possible to pass arguments to macros as well! It's possible to pass arguments to macros as well!
You retrieve the arguments by using the escape sequences You retrieve the arguments by using the escape sequences
.Ic \[rs]1 .Ic \e1
through through
.Ic \[rs]9 , \[rs]1 .Ic \e9 , \e1
being the first argument specified on the macro invocation. being the first argument specified on the macro invocation.
.Bd -literal -offset indent .Bd -literal -offset indent
MACRO LoopyMacro MACRO LoopyMacro
ld hl,\[rs]1 ld hl,\e1
ld c,\[rs]2 ld c,\e2
xor a,a xor a,a
\&.loop\[rs]@ ld [hl+],a \&.loop\e@ ld [hl+],a
dec c dec c
jr nz,.loop\[rs]@ jr nz,.loop\e@
ENDM ENDM
.Ed .Ed
.Pp .Pp
@@ -1617,14 +1627,14 @@ LoopyMacro MyVars,54
Arguments are passed as string constants, although there's no need to enclose them in quotes. 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. 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 This means that it's probably a very good idea to use brackets around
.Ic \[rs]1 .Ic \e1
to to
.Ic \[rs]9 .Ic \e9
if you perform further calculations on them. if you perform further calculations on them.
For instance, consider the following: For instance, consider the following:
.Bd -literal -offset indent .Bd -literal -offset indent
MACRO print_double MACRO print_double
PRINTLN \[rs]1 * 2 PRINTLN \e1 * 2
ENDM ENDM
print_double 1 + 2 print_double 1 + 2
.Ed .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: However, some characters need to be escaped, as in the following example:
.Bd -literal -offset indent .Bd -literal -offset indent
MACRO PrintMacro1 MACRO PrintMacro1
PRINTLN STRCAT(\[rs]1) PRINTLN STRCAT(\e1)
ENDM ENDM
PrintMacro1 "Hello "\[rs], \[rs] PrintMacro1 "Hello "\e, \e
"world" "world"
MACRO PrintMacro2 MACRO PrintMacro2
PRINT \[rs]1 PRINT \e1
ENDM ENDM
PrintMacro2 STRCAT("Hello ", \[rs] PrintMacro2 STRCAT("Hello ", \e
"world\[rs]n") "world\en")
.Ed .Ed
.Pp .Pp
The comma in The comma in
@@ -1657,34 +1667,34 @@ The comma in
.Ql PrintMacro2 .Ql PrintMacro2
does not need escaping because it is inside parentheses, similar to macro arguments in C. does not need escaping because it is inside parentheses, similar to macro arguments in C.
The backslash in The backslash in
.Ql \[rs]n .Ql \en
also does not need escaping because string literals work as usual inside macro arguments. also does not need escaping because string literals work as usual inside macro arguments.
.Pp .Pp
Since there are only nine digits, you can only access the first nine macro arguments like this. 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 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. This bracketed syntax supports decimal numbers and numeric constant symbols.
For example, For example,
.Ql \[rs]<_NARG> .Ql \e<_NARG>
will get the last argument. will get the last argument.
.Pp .Pp
Other macro arguments and symbol interpolations will be expanded inside the angle brackets. Other macro arguments and symbol interpolations will be expanded inside the angle brackets.
For example, if For example, if
.Ql \[rs]1 .Ql \e1
is is
.Ql 13 , .Ql 13 ,
then then
.Ql \[rs]<\[rs]1> .Ql \e<\e1>
will expand to will expand to
.Ql \[rs]<13> . .Ql \e<13> .
Or if Or if
.Ql v10 = 42 .Ql v10 = 42
and and
.Ql x = 10 , .Ql x = 10 ,
then then
.Ql \[rs]<v{d:x}> .Ql \e<v{d:x}>
will expand to will expand to
.Ql \[rs]<42> . .Ql \e<42> .
.Pp .Pp
Another way to access more than nine macro arguments is the Another way to access more than nine macro arguments is the
.Ic SHIFT .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 It will shift the arguments by one to the left, and decrease
.Dv _NARG .Dv _NARG
by 1. by 1.
.Ic \[rs]1 .Ic \e1
will get the value of will get the value of
.Ic \[rs]2 , \[rs]2 .Ic \e2 , \e2
will get the value of will get the value of
.Ic \[rs]3 , .Ic \e3 ,
and so forth. and so forth.
.Pp .Pp
.Ic SHIFT .Ic SHIFT
@@ -1715,9 +1725,9 @@ and
commands print text and values to the standard output. 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. Useful for debugging macros, or wherever you may feel the need to tell yourself some important information.
.Bd -literal -offset indent .Bd -literal -offset indent
PRINT "Hello world!\[rs]n" PRINT "Hello world!\en"
PRINTLN "Hello world!" PRINTLN "Hello world!"
PRINT _NARG, " arguments\[rs]n" PRINT _NARG, " arguments\en"
PRINTLN "sum: ", 2+3, " product: ", 2*3 PRINTLN "sum: ", 2+3, " product: ", 2*3
PRINTLN "Line #", __LINE__ PRINTLN "Line #", __LINE__
PRINTLN STRFMT("E = %f", 2.718) PRINTLN STRFMT("E = %f", 2.718)
@@ -1731,7 +1741,7 @@ For different formats, use
.Ic STRFMT . .Ic STRFMT .
.It Ic PRINTLN .It Ic PRINTLN
prints out each of its comma-separated arguments, if any, followed by a line feed prints out each of its comma-separated arguments, if any, followed by a line feed
.Pq Ql \[rs]n . .Pq Ql \en .
.El .El
.Ss Automatically repeating blocks of code .Ss Automatically repeating blocks of code
Suppose you want to unroll a time consuming loop without copy-pasting it. Suppose you want to unroll a time consuming loop without copy-pasting it.
@@ -1755,17 +1765,16 @@ You can also use
.Ic REPT .Ic REPT
to generate tables on the fly: to generate tables on the fly:
.Bd -literal -offset indent .Bd -literal -offset indent
; Generate a 256-byte sine table with values in the range [0, 128] ; Generate a table of square values from 0**2 = 0 to 100**2 = 10000
; (shifted and scaled from the range [-1.0, 1.0]) DEF x = 0
ANGLE = 0.0 REPT 101
REPT 256 dw x * x
db (MUL(64.0, SIN(ANGLE)) + 64.0) >> 16 DEF x += 1
ANGLE = ANGLE + 256.0 ; 256.0 = 65536 degrees / 256 entries ENDR
ENDR
.Ed .Ed
.Pp .Pp
As in macros, you can also use the escape sequence As in macros, you can also use the escape sequence
.Ic \[rs]@ . .Ic \e@ .
.Ic REPT .Ic REPT
blocks can be nested. blocks can be nested.
.Pp .Pp
@@ -1853,7 +1862,7 @@ This will print:
Just like with Just like with
.Ic REPT .Ic REPT
blocks, you can use the escape sequence blocks, you can use the escape sequence
.Ic \[rs]@ .Ic \e@
inside of inside of
.Ic FOR .Ic FOR
blocks, and they can be nested. 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 .Bd -literal -offset indent
INCLUDE "irq.inc" INCLUDE "irq.inc"
.Ed .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 .Ss Conditional assembling
The four commands The four commands
.Ic IF , ELIF , ELSE , .Ic IF , ELIF , ELSE ,

View File

@@ -14,7 +14,6 @@
.Nd Game Boy graphics converter .Nd Game Boy graphics converter
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl r Ar stride
.Op Fl CmuVZ .Op Fl CmuVZ
.Op Fl v Op Fl v No ... .Op Fl v Op Fl v No ...
.Op Fl a Ar attrmap | Fl A .Op Fl a Ar attrmap | Fl A
@@ -27,6 +26,7 @@
.Op Fl o Ar out_file .Op Fl o Ar out_file
.Op Fl p Ar pal_file | Fl P .Op Fl p Ar pal_file | Fl P
.Op Fl q Ar pal_map | Fl Q .Op Fl q Ar pal_map | Fl Q
.Op Fl r Ar stride
.Op Fl s Ar nb_colors .Op Fl s Ar nb_colors
.Op Fl t Ar tilemap | Fl T .Op Fl t Ar tilemap | Fl T
.Op Fl x Ar quantity .Op Fl x Ar quantity
@@ -321,7 +321,22 @@ any command-line argument that begins with an at sign
.Pq Ql @ .Pq Ql @
is interpreted as one. 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. 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 .Pp
Since the contents of at-files are interpreted by Since the contents of at-files are interpreted by
.Nm , .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. Each line can contain any number of arguments, which are separated by whitespace.
.Pq \&No quoting feature to prevent this is provided. .Pq \&No quoting feature to prevent this is provided.
.Pp .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 -- .Ql --
to stop option processing also disables at-file processing. to stop option processing also disables at-file processing.
For example, the following command line processes For example, the following command line reads command-line options from
.Ql @tilesets/town.png ,
outputs tile data to
.Ql @tilesets/town.2bpp ,
and reads command-line options from
.Ql tilesets/town.flags .Ql tilesets/town.flags
then 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 .Pp
.Dl $ rgbgfx -o @tilesets/town.2bpp @tilesets/town.flags @tilesets.flags -- @tilesets/town.png .Dl $ rgbgfx -o @tilesets/town.2bpp @tilesets/town.flags @tilesets.flags -- @tilesets/town.png
.Pp .Pp
@@ -357,11 +374,21 @@ can be used in an at-file (with identical semantics), it is only effective insid
.Sh PALETTE SPECIFICATION FORMATS .Sh PALETTE SPECIFICATION FORMATS
The following formats are supported: The following formats are supported:
.Bl -tag -width Ds .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 . .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 . .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 . .Lk https://www.selapa.net/swatches/colors/fileformats.php#psp_pal Paint Shop Pro palette .
.El .El
.Pp .Pp

View File

@@ -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 Section names in double quotes support the same character escape sequences as strings in
.Xr rgbasm 5 , .Xr rgbasm 5 ,
specifically specifically
.Ql \[rs]\[rs] , .Ql \e\e ,
.Ql \[rs]" , .Ql \e" ,
.Ql \[rs]n , .Ql \en ,
.Ql \[rs]r , .Ql \er ,
and and
.Ql \[rs]t . .Ql \et .
Other backslash escape sequences in Other backslash escape sequences in
.Xr rgbasm 5 .Xr rgbasm 5
are only relevant to assembly code and do not apply in section names. are only relevant to assembly code and do not apply in section names.

View File

@@ -84,6 +84,7 @@ set(rgblink_src
"link/sdas_obj.c" "link/sdas_obj.c"
"link/section.c" "link/section.c"
"link/symbol.c" "link/symbol.c"
"extern/utf8decoder.c"
"hashmap.c" "hashmap.c"
"linkdefs.c" "linkdefs.c"
"opmath.c" "opmath.c"

View File

@@ -27,7 +27,7 @@
struct Charnode { struct Charnode {
bool isTerminal; // Whether there exists a mapping that ends here bool isTerminal; // Whether there exists a mapping that ends here
uint8_t value; // If the above is true, its corresponding value 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 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) if (output)
*output += codepointLen; *output += codepointLen;
// Check if the character map is not the default "main" one, or if // Warn if this character is not mapped but any others are
// it has any mappings defined if (charmap->usedNodes > 1)
if (strcmp(charmap->name, "main") || charmap->usedNodes > 1) warning(WARNING_UNMAPPED_CHAR_1,
warning(WARNING_UNMAPPED_CHAR,
"Unmapped character %s\n", printChar(firstChar)); "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; return codepointLen;

View File

@@ -11,7 +11,6 @@
#include <inttypes.h> #include <inttypes.h>
#include <math.h> #include <math.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include "asm/fixpoint.h" #include "asm/fixpoint.h"
#include "asm/symbol.h" #include "asm/symbol.h"
@@ -21,105 +20,96 @@
#define M_PI 3.14159265358979323846 #define M_PI 3.14159265358979323846
#endif #endif
#define fix2double(i) ((double)((i) / fix_PrecisionFactor())) #define fix2double(i, q) ((double)((i) / pow(2.0, q)))
#define double2fix(d) ((int32_t)round((d) * fix_PrecisionFactor())) #define double2fix(d, q) ((int32_t)round((d) * pow(2.0, q)))
// pi*2 radians == 2**fixPrecision fixed-point "degrees" // 2*pi radians == 1 turn
#define fdeg2rad(f) ((f) * (M_PI * 2) / fix_PrecisionFactor()) #define turn2rad(f) ((f) * (M_PI * 2))
#define rad2fdeg(r) ((r) * fix_PrecisionFactor() / (M_PI * 2)) #define rad2turn(r) ((r) / (M_PI * 2))
uint8_t fixPrecision; uint8_t fixPrecision;
uint8_t fix_Precision(void)
{
return fixPrecision;
}
double fix_PrecisionFactor(void) double fix_PrecisionFactor(void)
{ {
return pow(2.0, fixPrecision); 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; return double2fix(sin(turn2rad(fix2double(i, q))), q);
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);
} }
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))); return double2fix(floor(fix2double(i, q)), q);
}
int32_t fix_Floor(int32_t i)
{
return double2fix(floor(fix2double(i)));
} }

View File

@@ -249,7 +249,7 @@ void fmt_PrintNumber(char *buf, size_t bufLen, struct FormatSpec const *fmt, uin
} }
size_t len = strlen(valueBuf); 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; size_t totalLen = fmt->width > numLen ? fmt->width : numLen;
if (totalLen > bufLen - 1) { // bufLen includes terminator if (totalLen > bufLen - 1) { // bufLen includes terminator

View File

@@ -19,6 +19,7 @@
#include "asm/main.h" #include "asm/main.h"
#include "asm/symbol.h" #include "asm/symbol.h"
#include "asm/warning.h" #include "asm/warning.h"
#include "error.h"
#include "platform.h" // S_ISDIR (stat macro) #include "platform.h" // S_ISDIR (stat macro)
#define MAXINCPATHS 128 #define MAXINCPATHS 128
@@ -42,6 +43,8 @@ size_t maxRecursionDepth;
static unsigned int nbIncPaths = 0; static unsigned int nbIncPaths = 0;
static char const *includePaths[MAXINCPATHS]; static char const *includePaths[MAXINCPATHS];
static const char *preIncludeName;
static const char *dumpNodeAndParents(struct FileStackNode const *node) static const char *dumpNodeAndParents(struct FileStackNode const *node)
{ {
char const *name; char const *name;
@@ -133,6 +136,15 @@ void fstk_AddIncludePath(char const *path)
includePaths[nbIncPaths++] = str; 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) static void printDep(char const *path)
{ {
if (dependfile) { if (dependfile) {
@@ -274,11 +286,12 @@ bool yywrap(void)
lexer_SetState(contextStack->lexerState); lexer_SetState(contextStack->lexerState);
macro_SetUniqueID(contextStack->uniqueID); macro_SetUniqueID(contextStack->uniqueID);
return false; return false;
} }
// Make sure not to switch the lexer state before calling this, so the saved line no is correct. // 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. // Callers should set contextStack->lexerState after this so it is not NULL.
static void newContext(struct FileStackNode *fileInfo) static void newContext(struct FileStackNode *fileInfo)
{ {
@@ -300,7 +313,7 @@ static void newContext(struct FileStackNode *fileInfo)
context->forName = NULL; context->forName = NULL;
// Link new entry to its parent so it's reachable later // 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; context->parent = contextStack;
contextStack = context; contextStack = context;
} }
@@ -342,6 +355,41 @@ void fstk_RunInclude(char const *path)
contextStack->uniqueID = macro_UndefUniqueID(); 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) void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
{ {
struct Symbol *macro = sym_FindExactSymbol(macroName); 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 // Make sure that the default of 64 is OK, though
assert(DEPTH_LIMIT >= DEFAULT_MAX_DEPTH); assert(DEPTH_LIMIT >= DEFAULT_MAX_DEPTH);
#undef DEPTH_LIMIT #undef DEPTH_LIMIT
runPreIncludeFile();
} }

View File

@@ -733,7 +733,7 @@ static uint32_t readBracketedMacroArgNum(void)
} }
symName[i] = '\0'; symName[i] = '\0';
struct Symbol const *sym = sym_FindScopedSymbol(symName); struct Symbol const *sym = sym_FindScopedValidSymbol(symName);
if (!sym) { if (!sym) {
error("Bracketed symbol \"%s\" does not exist\n", symName); error("Bracketed symbol \"%s\" does not exist\n", symName);
@@ -1179,7 +1179,7 @@ static uint32_t readFractionalPart(uint32_t integer)
precision = fixPrecision; 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"); warning(WARNING_LARGE_CONSTANT, "Magnitude of fixed-point constant is too large\n");
// Cast to unsigned avoids undefined overflow behavior // Cast to unsigned avoids undefined overflow behavior
@@ -1400,7 +1400,7 @@ static char const *readInterpolation(size_t depth)
static char buf[MAXSTRLEN + 1]; static char buf[MAXSTRLEN + 1];
struct Symbol const *sym = sym_FindScopedSymbol(symName); struct Symbol const *sym = sym_FindScopedValidSymbol(symName);
if (!sym) { if (!sym) {
error("Interpolated symbol \"%s\" does not exist\n", symName); error("Interpolated symbol \"%s\" does not exist\n", symName);

View File

@@ -53,11 +53,11 @@ char const *__asan_default_options(void) { return "detect_leaks=0"; }
// Unfortunately, macOS still ships 2.3, which is from 2008... // Unfortunately, macOS still ships 2.3, which is from 2008...
int yyparse(void); int yyparse(void);
FILE * dependfile; FILE *dependfile = NULL;
bool generatedMissingIncludes; bool generatedMissingIncludes = false;
bool failedOnMissingInclude; bool failedOnMissingInclude = false;
bool generatePhonyDeps; bool generatePhonyDeps = false;
char *targetFileName; char *targetFileName = NULL;
bool haltnop; bool haltnop;
bool warnOnHaltNop; bool warnOnHaltNop;
@@ -87,7 +87,7 @@ static char *make_escape(char const *str)
} }
// Short options // 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 // Variables for the long-only options
static int depType; // Variants of `-M` static int depType; // Variants of `-M`
@@ -107,7 +107,7 @@ static struct option const longopts[] = {
{ "gfx-chars", required_argument, NULL, 'g' }, { "gfx-chars", required_argument, NULL, 'g' },
{ "nop-after-halt", no_argument, NULL, 'H' }, { "nop-after-halt", no_argument, NULL, 'H' },
{ "halt-without-nop", 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' }, { "preserve-ld", no_argument, NULL, 'L' },
{ "auto-ldh", no_argument, NULL, 'l' }, { "auto-ldh", no_argument, NULL, 'l' },
{ "dependfile", required_argument, NULL, 'M' }, { "dependfile", required_argument, NULL, 'M' },
@@ -116,6 +116,7 @@ static struct option const longopts[] = {
{ "MT", required_argument, &depType, 'T' }, { "MT", required_argument, &depType, 'T' },
{ "MQ", required_argument, &depType, 'Q' }, { "MQ", required_argument, &depType, 'Q' },
{ "output", required_argument, NULL, 'o' }, { "output", required_argument, NULL, 'o' },
{ "preinclude", required_argument, NULL, 'P' },
{ "pad-value", required_argument, NULL, 'p' }, { "pad-value", required_argument, NULL, 'p' },
{ "q-precision", required_argument, NULL, 'Q' }, { "q-precision", required_argument, NULL, 'Q' },
{ "recursion-depth", required_argument, NULL, 'r' }, { "recursion-depth", required_argument, NULL, 'r' },
@@ -128,10 +129,10 @@ static struct option const longopts[] = {
static void print_usage(void) static void print_usage(void)
{ {
fputs( 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" " [-M depend_file] [-MG] [-MP] [-MT target_file] [-MQ target_file]\n"
" [-o out_file] [-p pad_value] [-Q precision] [-r depth]\n" " [-o out_file] [-P include_file] [-p pad_value] [-Q precision]\n"
" [-W warning] <file>\n" " [-r depth] [-W warning] <file>\n"
"Useful options:\n" "Useful options:\n"
" -E, --export-all export all labels\n" " -E, --export-all export all labels\n"
" -M, --dependfile <path> set the output dependency file\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 main(int argc, char *argv[])
{ {
int ch;
char *ep;
time_t now = time(NULL); time_t now = time(NULL);
char const *sourceDateEpoch = getenv("SOURCE_DATE_EPOCH"); char const *sourceDateEpoch = getenv("SOURCE_DATE_EPOCH");
@@ -158,18 +156,11 @@ int main(int argc, char *argv[])
if (sourceDateEpoch) if (sourceDateEpoch)
now = (time_t)strtoul(sourceDateEpoch, NULL, 0); now = (time_t)strtoul(sourceDateEpoch, NULL, 0);
dependfile = NULL;
// Perform some init for below // Perform some init for below
sym_Init(now); sym_Init(now);
// Set defaults // Set defaults
generatePhonyDeps = false;
generatedMissingIncludes = false;
failedOnMissingInclude = false;
targetFileName = NULL;
opt_B("01"); opt_B("01");
opt_G("0123"); opt_G("0123");
opt_P(0); opt_P(0);
@@ -182,13 +173,16 @@ int main(int argc, char *argv[])
warnings = true; warnings = true;
sym_SetExportAll(false); sym_SetExportAll(false);
uint32_t maxDepth = DEFAULT_MAX_DEPTH; uint32_t maxDepth = DEFAULT_MAX_DEPTH;
char *dependFileName = NULL;
size_t targetFileNameLen = 0; size_t targetFileNameLen = 0;
int ch;
char *ep;
while ((ch = musl_getopt_long_only(argc, argv, optstring, longopts, NULL)) != -1) { while ((ch = musl_getopt_long_only(argc, argv, optstring, longopts, NULL)) != -1) {
switch (ch) { switch (ch) {
case 'b': case 'b':
if (strlen(musl_optarg) == 2) if (strlen(musl_optarg) == 2)
opt_B(&musl_optarg[1]); opt_B(musl_optarg);
else else
errx("Must specify exactly 2 characters for option 'b'"); errx("Must specify exactly 2 characters for option 'b'");
break; break;
@@ -210,7 +204,7 @@ int main(int argc, char *argv[])
case 'g': case 'g':
if (strlen(musl_optarg) == 4) if (strlen(musl_optarg) == 4)
opt_G(&musl_optarg[1]); opt_G(musl_optarg);
else else
errx("Must specify exactly 4 characters for option 'g'"); errx("Must specify exactly 4 characters for option 'g'");
break; break;
@@ -226,6 +220,10 @@ int main(int argc, char *argv[])
haltnop = false; haltnop = false;
break; 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': case 'i':
fstk_AddIncludePath(musl_optarg); fstk_AddIncludePath(musl_optarg);
break; break;
@@ -242,18 +240,27 @@ int main(int argc, char *argv[])
break; break;
case 'M': case 'M':
if (!strcmp("-", musl_optarg)) if (dependfile)
warnx("Overriding dependfile %s", dependFileName);
if (!strcmp("-", musl_optarg)) {
dependfile = stdout; dependfile = stdout;
else dependFileName = "<stdout>";
} else {
dependfile = fopen(musl_optarg, "w"); dependfile = fopen(musl_optarg, "w");
dependFileName = musl_optarg;
}
if (dependfile == NULL) if (dependfile == NULL)
err("Could not open dependfile %s", musl_optarg); err("Could not open dependfile %s", dependFileName);
break; break;
case 'o': case 'o':
out_SetFileName(musl_optarg); out_SetFileName(musl_optarg);
break; break;
case 'P':
fstk_SetPreIncludeFile(musl_optarg);
break;
unsigned long padByte; unsigned long padByte;
case 'p': case 'p':
padByte = strtoul(musl_optarg, &ep, 0); padByte = strtoul(musl_optarg, &ep, 0);
@@ -371,7 +378,7 @@ int main(int argc, char *argv[])
fprintf(dependfile, "%s: %s\n", targetFileName, mainFileName); 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 // Init lexer and file stack, providing file info
lexer_Init(); lexer_Init();

View File

@@ -492,7 +492,7 @@ void out_WriteObject(void)
if (strcmp(objectName, "-") != 0) if (strcmp(objectName, "-") != 0)
f = fopen(objectName, "wb"); f = fopen(objectName, "wb");
else else
f = fdopen(1, "wb"); f = fdopen(STDOUT_FILENO, "wb");
if (!f) if (!f)
err("Couldn't write file '%s'", objectName); err("Couldn't write file '%s'", objectName);
@@ -540,7 +540,9 @@ void out_WriteObject(void)
// Set the objectfilename // Set the objectfilename
void out_SetFileName(char *s) void out_SetFileName(char *s)
{ {
if (objectName)
warnx("Overriding output filename %s", objectName);
objectName = s; objectName = s;
if (verbose) if (verbose)
printf("Output filename %s\n", s); printf("Output filename %s\n", objectName);
} }

View File

@@ -550,6 +550,7 @@ enum {
%token T_OP_BANK "BANK" %token T_OP_BANK "BANK"
%token T_OP_ALIGN "ALIGN" %token T_OP_ALIGN "ALIGN"
%token T_OP_SIZEOF "SIZEOF" T_OP_STARTOF "STARTOF" %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_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_ASIN "ASIN" T_OP_ACOS "ACOS" T_OP_ATAN "ATAN" T_OP_ATAN2 "ATAN2"
%token T_OP_FDIV "FDIV" %token T_OP_FDIV "FDIV"
@@ -559,6 +560,7 @@ enum {
%token T_OP_LOG "LOG" %token T_OP_LOG "LOG"
%token T_OP_ROUND "ROUND" %token T_OP_ROUND "ROUND"
%token T_OP_CEIL "CEIL" T_OP_FLOOR "FLOOR" %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_HIGH "HIGH" T_OP_LOW "LOW"
%token T_OP_ISCONST "ISCONST" %token T_OP_ISCONST "ISCONST"
@@ -1462,56 +1464,54 @@ relocexpr_no_str : scoped_anon_id { rpn_Symbol(&$$, $1); }
| T_OP_DEF { | T_OP_DEF {
lexer_ToggleStringExpansion(false); lexer_ToggleStringExpansion(false);
} T_LPAREN scoped_anon_id T_RPAREN { } T_LPAREN scoped_anon_id T_RPAREN {
struct Symbol const *sym = sym_FindScopedSymbol($4); rpn_Number(&$$, sym_FindScopedValidSymbol($4) != NULL);
rpn_Number(&$$, !!sym);
lexer_ToggleStringExpansion(true); lexer_ToggleStringExpansion(true);
} }
| T_OP_ROUND T_LPAREN const T_RPAREN { | T_OP_ROUND T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Round($3)); rpn_Number(&$$, fix_Round($3, $4));
} }
| T_OP_CEIL T_LPAREN const T_RPAREN { | T_OP_CEIL T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Ceil($3)); rpn_Number(&$$, fix_Ceil($3, $4));
} }
| T_OP_FLOOR T_LPAREN const T_RPAREN { | T_OP_FLOOR T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Floor($3)); rpn_Number(&$$, fix_Floor($3, $4));
} }
| T_OP_FDIV T_LPAREN const T_COMMA const T_RPAREN { | T_OP_FDIV T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Div($3, $5)); rpn_Number(&$$, fix_Div($3, $5, $6));
} }
| T_OP_FMUL T_LPAREN const T_COMMA const T_RPAREN { | T_OP_FMUL T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Mul($3, $5)); rpn_Number(&$$, fix_Mul($3, $5, $6));
} }
| T_OP_FMOD T_LPAREN const T_COMMA const T_RPAREN { | T_OP_FMOD T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Mod($3, $5)); rpn_Number(&$$, fix_Mod($3, $5, $6));
} }
| T_OP_POW T_LPAREN const T_COMMA const T_RPAREN { | T_OP_POW T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Pow($3, $5)); rpn_Number(&$$, fix_Pow($3, $5, $6));
} }
| T_OP_LOG T_LPAREN const T_COMMA const T_RPAREN { | T_OP_LOG T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Log($3, $5)); rpn_Number(&$$, fix_Log($3, $5, $6));
} }
| T_OP_SIN T_LPAREN const T_RPAREN { | T_OP_SIN T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Sin($3)); rpn_Number(&$$, fix_Sin($3, $4));
} }
| T_OP_COS T_LPAREN const T_RPAREN { | T_OP_COS T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Cos($3)); rpn_Number(&$$, fix_Cos($3, $4));
} }
| T_OP_TAN T_LPAREN const T_RPAREN { | T_OP_TAN T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Tan($3)); rpn_Number(&$$, fix_Tan($3, $4));
} }
| T_OP_ASIN T_LPAREN const T_RPAREN { | T_OP_ASIN T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_ASin($3)); rpn_Number(&$$, fix_ASin($3, $4));
} }
| T_OP_ACOS T_LPAREN const T_RPAREN { | T_OP_ACOS T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_ACos($3)); rpn_Number(&$$, fix_ACos($3, $4));
} }
| T_OP_ATAN T_LPAREN const T_RPAREN { | T_OP_ATAN T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_ATan($3)); rpn_Number(&$$, fix_ATan($3, $4));
} }
| T_OP_ATAN2 T_LPAREN const T_COMMA const T_RPAREN { | T_OP_ATAN2 T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_ATan2($3, $5)); rpn_Number(&$$, fix_ATan2($3, $5, $6));
} }
| T_OP_STRCMP T_LPAREN string T_COMMA string T_RPAREN { | T_OP_STRCMP T_LPAREN string T_COMMA string T_RPAREN {
rpn_Number(&$$, strcmp($3, $5)); rpn_Number(&$$, strcmp($3, $5));
@@ -1538,7 +1538,7 @@ relocexpr_no_str : scoped_anon_id { rpn_Symbol(&$$, $1); }
uconst : const { uconst : const {
$$ = $1; $$ = $1;
if ($$ < 0) 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); } 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 string : T_STRING
| T_OP_STRSUB T_LPAREN string T_COMMA const T_COMMA uconst T_RPAREN { | T_OP_STRSUB T_LPAREN string T_COMMA const T_COMMA uconst T_RPAREN {
size_t len = strlenUTF8($3); size_t len = strlenUTF8($3);
@@ -1589,6 +1600,19 @@ string : T_STRING
strfmt($$, sizeof($$), $3.format, $3.nbArgs, $3.args); strfmt($$, sizeof($$), $3.format, $3.nbArgs, $3.args);
freeStrFmtArgList(&$3); 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 strcat_args : string

View File

@@ -36,6 +36,7 @@ HashMap symbols;
static const char *labelScope; // Current section's label scope static const char *labelScope; // Current section's label scope
static struct Symbol *PCSymbol; static struct Symbol *PCSymbol;
static struct Symbol *_NARGSymbol;
static char savedTIME[256]; static char savedTIME[256];
static char savedDATE[256]; static char savedDATE[256];
static char savedTIMESTAMP_ISO8601_LOCAL[256]; static char savedTIMESTAMP_ISO8601_LOCAL[256];
@@ -78,12 +79,15 @@ static int32_t Callback_NARG(void)
static int32_t Callback__LINE__(void) static int32_t Callback__LINE__(void)
{ {
warning(WARNING_OBSOLETE, "`__LINE__` is deprecated\n");
return lexer_GetLineNo(); return lexer_GetLineNo();
} }
static char const *Callback__FILE__(void) 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 // 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. // EQUS expansions, which cannot straddle file boundaries. So this should be fine.
static char *buf = NULL; static char *buf = NULL;
@@ -97,7 +101,7 @@ static char const *Callback__FILE__(void)
// Account for the extra backslash inserted below // Account for the extra backslash inserted below
if (fileName[i] == '"') if (fileName[i] == '"')
j++; 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 if (j + 2 >= bufsize) { // Always keep room for 2 tail chars
bufsize = bufsize ? bufsize * 2 : 64; bufsize = bufsize ? bufsize * 2 : 64;
buf = realloc(buf, bufsize); buf = realloc(buf, bufsize);
@@ -247,6 +251,21 @@ struct Symbol *sym_FindScopedSymbol(char const *symName)
return sym_FindExactSymbol(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) struct Symbol const *sym_GetPC(void)
{ {
return PCSymbol; return PCSymbol;
@@ -260,7 +279,7 @@ static bool isReferenced(struct Symbol const *sym)
// Purge a symbol // Purge a symbol
void sym_Purge(char const *symName) void sym_Purge(char const *symName)
{ {
struct Symbol *sym = sym_FindScopedSymbol(symName); struct Symbol *sym = sym_FindScopedValidSymbol(symName);
if (!sym) { if (!sym) {
error("'%s' not defined\n", symName); error("'%s' not defined\n", symName);
@@ -571,7 +590,7 @@ struct Symbol *sym_AddAnonLabel(void)
} }
char name[MAXSYMLEN + 1]; char name[MAXSYMLEN + 1];
sym_WriteAnonLabelName(name, 0, true); // The direction is important!! sym_WriteAnonLabelName(name, 0, true); // The direction is important!
anonLabelID++; anonLabelID++;
return addLabel(name); return addLabel(name);
} }
@@ -679,8 +698,10 @@ static struct Symbol *createBuiltinSymbol(char const *symName)
void sym_Init(time_t now) void sym_Init(time_t now)
{ {
PCSymbol = createBuiltinSymbol("@"); PCSymbol = createBuiltinSymbol("@");
struct Symbol *_NARGSymbol = createBuiltinSymbol("_NARG"); _NARGSymbol = createBuiltinSymbol("_NARG");
// __LINE__ is deprecated
struct Symbol *__LINE__Symbol = createBuiltinSymbol("__LINE__"); struct Symbol *__LINE__Symbol = createBuiltinSymbol("__LINE__");
// __FILE__ is deprecated
struct Symbol *__FILE__Symbol = createBuiltinSymbol("__FILE__"); struct Symbol *__FILE__Symbol = createBuiltinSymbol("__FILE__");
PCSymbol->type = SYM_LABEL; PCSymbol->type = SYM_LABEL;

View File

@@ -38,13 +38,14 @@ static const enum WarningState defaultWarnings[ARRAY_SIZE(warningStates)] = {
[WARNING_OBSOLETE] = WARNING_ENABLED, [WARNING_OBSOLETE] = WARNING_ENABLED,
[WARNING_SHIFT] = WARNING_DISABLED, [WARNING_SHIFT] = WARNING_DISABLED,
[WARNING_SHIFT_AMOUNT] = WARNING_DISABLED, [WARNING_SHIFT_AMOUNT] = WARNING_DISABLED,
[WARNING_UNMAPPED_CHAR] = WARNING_ENABLED,
[WARNING_USER] = WARNING_ENABLED, [WARNING_USER] = WARNING_ENABLED,
[WARNING_NUMERIC_STRING_1] = WARNING_ENABLED, [WARNING_NUMERIC_STRING_1] = WARNING_ENABLED,
[WARNING_NUMERIC_STRING_2] = WARNING_DISABLED, [WARNING_NUMERIC_STRING_2] = WARNING_DISABLED,
[WARNING_TRUNCATION_1] = WARNING_ENABLED, [WARNING_TRUNCATION_1] = WARNING_ENABLED,
[WARNING_TRUNCATION_2] = WARNING_DISABLED, [WARNING_TRUNCATION_2] = WARNING_DISABLED,
[WARNING_UNMAPPED_CHAR_1] = WARNING_ENABLED,
[WARNING_UNMAPPED_CHAR_2] = WARNING_DISABLED,
}; };
enum WarningState warningStates[ARRAY_SIZE(warningStates)]; enum WarningState warningStates[ARRAY_SIZE(warningStates)];
@@ -86,7 +87,6 @@ static const char * const warningFlags[NB_WARNINGS] = {
"obsolete", "obsolete",
"shift", "shift",
"shift-amount", "shift-amount",
"unmapped-char",
"user", "user",
// Parametric warnings // Parametric warnings
@@ -94,6 +94,8 @@ static const char * const warningFlags[NB_WARNINGS] = {
"numeric-string", "numeric-string",
"truncation", "truncation",
"truncation", "truncation",
"unmapped-char",
"unmapped-char",
// Meta warnings // Meta warnings
"all", "all",
@@ -108,6 +110,7 @@ static const struct {
} paramWarnings[] = { } paramWarnings[] = {
{ "numeric-string", 2, 1 }, { "numeric-string", 2, 1 },
{ "truncation", 2, 2 }, { "truncation", 2, 2 },
{ "unmapped-char", 2, 1 },
}; };
static bool tryProcessParamWarning(char const *flag, uint8_t param, enum WarningState state) 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_LONG_STR,
WARNING_NESTED_COMMENT, WARNING_NESTED_COMMENT,
WARNING_OBSOLETE, WARNING_OBSOLETE,
WARNING_UNMAPPED_CHAR,
WARNING_NUMERIC_STRING_1, WARNING_NUMERIC_STRING_1,
WARNING_UNMAPPED_CHAR_1,
META_WARNING_DONE META_WARNING_DONE
}; };
@@ -176,6 +179,8 @@ static uint8_t const _wextraCommands[] = {
WARNING_NUMERIC_STRING_2, WARNING_NUMERIC_STRING_2,
WARNING_TRUNCATION_1, WARNING_TRUNCATION_1,
WARNING_TRUNCATION_2, WARNING_TRUNCATION_2,
WARNING_UNMAPPED_CHAR_1,
WARNING_UNMAPPED_CHAR_2,
META_WARNING_DONE META_WARNING_DONE
}; };
@@ -194,11 +199,12 @@ static uint8_t const _weverythingCommands[] = {
WARNING_OBSOLETE, WARNING_OBSOLETE,
WARNING_SHIFT, WARNING_SHIFT,
WARNING_SHIFT_AMOUNT, WARNING_SHIFT_AMOUNT,
WARNING_UNMAPPED_CHAR,
WARNING_NUMERIC_STRING_1, WARNING_NUMERIC_STRING_1,
WARNING_NUMERIC_STRING_2, WARNING_NUMERIC_STRING_2,
WARNING_TRUNCATION_1, WARNING_TRUNCATION_1,
WARNING_TRUNCATION_2, WARNING_TRUNCATION_2,
WARNING_UNMAPPED_CHAR_1,
WARNING_UNMAPPED_CHAR_2,
// WARNING_USER, // WARNING_USER,
META_WARNING_DONE META_WARNING_DONE
}; };

View File

@@ -337,6 +337,8 @@ static char *parseArgv(int argc, char **argv, bool &autoAttrmap, bool &autoTilem
break; break;
case 'a': case 'a':
autoAttrmap = false; autoAttrmap = false;
if (!options.attrmap.empty())
warning("Overriding attrmap file %s", options.attrmap.c_str());
options.attrmap = musl_optarg; options.attrmap = musl_optarg;
break; break;
case 'b': case 'b':
@@ -479,6 +481,8 @@ static char *parseArgv(int argc, char **argv, bool &autoAttrmap, bool &autoTilem
} }
break; break;
case 'o': case 'o':
if (!options.output.empty())
warning("Overriding tile data file %s", options.output.c_str());
options.output = musl_optarg; options.output = musl_optarg;
break; break;
case 'P': case 'P':
@@ -486,6 +490,8 @@ static char *parseArgv(int argc, char **argv, bool &autoAttrmap, bool &autoTilem
break; break;
case 'p': case 'p':
autoPalettes = false; autoPalettes = false;
if (!options.palettes.empty())
warning("Overriding palettes file %s", options.palettes.c_str());
options.palettes = musl_optarg; options.palettes = musl_optarg;
break; break;
case 'Q': case 'Q':
@@ -493,6 +499,8 @@ static char *parseArgv(int argc, char **argv, bool &autoAttrmap, bool &autoTilem
break; break;
case 'q': case 'q':
autoPalmap = false; autoPalmap = false;
if (!options.palmap.empty())
warning("Overriding palette map file %s", options.palmap.c_str());
options.palmap = musl_optarg; options.palmap = musl_optarg;
break; break;
case 'r': case 'r':
@@ -520,6 +528,8 @@ static char *parseArgv(int argc, char **argv, bool &autoAttrmap, bool &autoTilem
break; break;
case 't': case 't':
autoTilemap = false; autoTilemap = false;
if (!options.tilemap.empty())
warning("Overriding tilemap file %s", options.tilemap.c_str());
options.tilemap = musl_optarg; options.tilemap = musl_optarg;
break; break;
case 'V': case 'V':

View File

@@ -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); 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) { for (Palette &pal : palettes) {
std::sort(pal.begin(), pal.end(), [&](uint16_t lhs, uint16_t rhs) { std::sort(pal.begin(), pal.end(), [&](uint16_t lhs, uint16_t rhs) {
// Iterate through the PNG's palette, looking for either of the two // Iterate through the PNG's palette, looking for either of the two

View File

@@ -16,6 +16,8 @@
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <fstream> #include <fstream>
#include <limits>
#include <optional>
#include <ostream> #include <ostream>
#include <streambuf> #include <streambuf>
#include <string> #include <string>
@@ -53,7 +55,7 @@ constexpr uint8_t singleToHex(char c) {
template<typename Str> // Should be std::string or std::string_view template<typename Str> // Should be std::string or std::string_view
static void skipWhitespace(Str const &str, typename Str::size_type &pos) { 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) { void parseInlinePalSpec(char const * const rawArg) {
@@ -191,6 +193,15 @@ static T readBE(U const *bytes) {
return val; 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`. * **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 * 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) { template<typename U> // Should be uint*_t
uint32_t value = 0; // Use a larger type to handle overflow more easily static std::optional<U> parseDec(std::string const &str, std::string::size_type &n) {
for (auto end = std::min(str.length(), str.find_first_not_of("0123456789", n)); n < end; ++n) { std::string::size_type start = n;
value = std::min<uint32_t>(value * 10 + (str[n] - '0'), UINT16_MAX);
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) { static void parsePSPFile(std::filebuf &file) {
@@ -246,41 +297,30 @@ static void parsePSPFile(std::filebuf &file) {
line.clear(); line.clear();
readLine(file, line); readLine(file, line);
std::string::size_type n = 0; std::string::size_type n = 0;
uint16_t nbColors = parseDec(line, n); std::optional<uint16_t> nbColors = parseDec<uint16_t>(line, n);
if (n != line.length()) { if (!nbColors || n != line.length()) {
error("Invalid \"number of colors\" line in PSP file (%s)", line.c_str()); error("Invalid \"number of colors\" line in PSP file (%s)", line.c_str());
return; 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 warning("PSP file contains %" PRIu16 " colors, but there can only be %" PRIu16
"; ignoring extra", "; ignoring extra",
nbColors, options.nbColorsPerPal * options.nbPalettes); *nbColors, options.nbColorsPerPal * options.nbPalettes);
nbColors = options.nbColorsPerPal * options.nbPalettes; nbColors = options.nbColorsPerPal * options.nbPalettes;
} }
options.palSpec.clear(); options.palSpec.clear();
for (uint16_t i = 0; i < nbColors; ++i) { for (uint16_t i = 0; i < *nbColors; ++i) {
line.clear(); line.clear();
readLine(file, line); readLine(file, line);
n = 0;
uint8_t r = parseDec(line, n); n = 0;
skipWhitespace(line, n); std::optional<Rgba> color = parseColor(line, n, i + 1);
if (n == line.length()) { if (!color) {
error("Failed to parse color #%" PRIu16 " (\"%s\"): missing green component", i + 1,
line.c_str());
return; 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()) { if (n != line.length()) {
error("Failed to parse color #%" PRIu16 error("Failed to parse color #%" PRIu16
" (\"%s\"): trailing characters after blue component", " (\"%s\"): trailing characters after blue component",
@@ -291,11 +331,98 @@ static void parsePSPFile(std::filebuf &file) {
if (i % options.nbColorsPerPal == 0) { if (i % options.nbColorsPerPal == 0) {
options.palSpec.emplace_back(); 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 // https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_pgfId-1070626
std::array<char, 772> buf; std::array<char, 772> buf;
@@ -344,8 +471,8 @@ void parseACTFile(std::filebuf &file) {
} }
} }
void parseACOFile(std::filebuf &file) { static void parseACOFile(std::filebuf &file) {
// rhttps://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_pgfId-1055819 // https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_pgfId-1055819
// http://www.nomodes.com/aco.html // http://www.nomodes.com/aco.html
char buf[10]; char buf[10];
@@ -412,6 +539,29 @@ void parseACOFile(std::filebuf &file) {
// `codecvt` can be used to convert from UTF-16 to UTF-8 // `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) { void parseExternalPalSpec(char const *arg) {
// `fmt:path`, parse the file according to the given format // `fmt:path`, parse the file according to the given format
@@ -425,8 +575,11 @@ void parseExternalPalSpec(char const *arg) {
static std::array parsers{ static std::array parsers{
std::tuple{"PSP", &parsePSPFile, std::ios::in }, 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{"ACT", &parseACTFile, std::ios::binary},
std::tuple{"ACO", &parseACOFile, 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(), auto iter = std::find_if(parsers.begin(), parsers.end(),

View File

@@ -154,9 +154,9 @@ FILE *openFile(char const *fileName, char const *mode)
if (strcmp(fileName, "-") != 0) if (strcmp(fileName, "-") != 0)
file = fopen(fileName, mode); file = fopen(fileName, mode);
else if (mode[0] == 'r') else if (mode[0] == 'r')
file = fdopen(0, mode); file = fdopen(STDIN_FILENO, mode);
else else
file = fdopen(1, mode); file = fdopen(STDOUT_FILENO, mode);
if (!file) if (!file)
err("Could not open file \"%s\"", fileName); err("Could not open file \"%s\"", fileName);
@@ -369,21 +369,31 @@ int main(int argc, char *argv[])
isWRA0Mode = true; isWRA0Mode = true;
break; break;
case 'l': case 'l':
if (linkerScriptName)
warnx("Overriding linkerscript %s", musl_optarg);
linkerScriptName = musl_optarg; linkerScriptName = musl_optarg;
break; break;
case 'M': case 'M':
noSymInMap = true; noSymInMap = true;
break; break;
case 'm': case 'm':
if (mapFileName)
warnx("Overriding mapfile %s", musl_optarg);
mapFileName = musl_optarg; mapFileName = musl_optarg;
break; break;
case 'n': case 'n':
if (symFileName)
warnx("Overriding symfile %s", musl_optarg);
symFileName = musl_optarg; symFileName = musl_optarg;
break; break;
case 'O': case 'O':
if (overlayFileName)
warnx("Overriding overlay file %s", musl_optarg);
overlayFileName = musl_optarg; overlayFileName = musl_optarg;
break; break;
case 'o': case 'o':
if (outputFileName)
warnx("Overriding output file %s", musl_optarg);
outputFileName = musl_optarg; outputFileName = musl_optarg;
break; break;
case 'p': case 'p':

View File

@@ -459,7 +459,12 @@ static struct Section *getMainSection(struct Section *section)
void obj_ReadFile(char const *fileName, unsigned int fileID) 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) if (!file)
err("Could not open file %s", fileName); err("Could not open file %s", fileName);

View File

@@ -17,6 +17,8 @@
#include "link/section.h" #include "link/section.h"
#include "link/symbol.h" #include "link/symbol.h"
#include "extern/utf8decoder.h"
#include "error.h" #include "error.h"
#include "linkdefs.h" #include "linkdefs.h"
#include "platform.h" // MIN_NB_ELMS #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; 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 // Comparator function for `qsort` to sort symbols
// Symbols are ordered by address, or else by original index for a stable sort // Symbols are ordered by address, or else by original index for a stable sort
static int compareSymbols(void const *a, void const *b) static int compareSymbols(void const *a, void const *b)
@@ -296,16 +347,22 @@ static void writeSymBank(struct SortedSections const *bankSections,
if (!symFile) if (!symFile)
return; 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; uint32_t nbSymbols = 0;
for (struct SortedSection const *ptr = bankSections->zeroLenSections; ptr; ptr = ptr->next) { forEachSortedSection(sect, {
for (struct Section const *sect = ptr->section; sect; sect = sect->nextu)
nbSymbols += sect->nbSymbols; 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;
}
if (!nbSymbols) if (!nbSymbols)
return; return;
@@ -315,29 +372,21 @@ static void writeSymBank(struct SortedSections const *bankSections,
if (!symList) if (!symList)
err("Failed to allocate symbol list"); err("Failed to allocate symbol list");
uint32_t idx = 0; nbSymbols = 0;
for (struct SortedSection const *ptr = bankSections->zeroLenSections; ptr; ptr = ptr->next) { forEachSortedSection(sect, {
for (struct Section const *sect = ptr->section; sect; sect = sect->nextu) {
for (uint32_t i = 0; i < sect->nbSymbols; i++) { for (uint32_t i = 0; i < sect->nbSymbols; i++) {
symList[idx].idx = idx; if (!canStartSymName(sect->symbols[i]->name[0]))
symList[idx].sym = sect->symbols[i]; // Don't output symbols that begin with an illegal character
symList[idx].addr = symList[idx].sym->offset + sect->org; continue;
idx++; 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) { #undef forEachSortedSection
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);
qsort(symList, nbSymbols, sizeof(*symList), compareSymbols); 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++) { for (uint32_t i = 0; i < nbSymbols; i++) {
struct SortedSymbol *sym = &symList[i]; struct SortedSymbol *sym = &symList[i];
fprintf(symFile, "%02" PRIx32 ":%04" PRIx16 " %s\n", fprintf(symFile, "%02" PRIx32 ":%04" PRIx16 " ", symBank, sym->addr);
symBank, sym->addr, sym->sym->name); printSymName(sym->sym->name);
fputc('\n', symFile);
} }
free(symList); free(symList);
} }
/* /*
@@ -384,20 +435,20 @@ static uint16_t writeMapBank(struct SortedSections const *sectList,
if (prevEndAddr < sect->org) { if (prevEndAddr < sect->org) {
uint16_t empty = sect->org - prevEndAddr; 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"); empty == 1 ? "" : "s");
} }
prevEndAddr = sect->org + sect->size; prevEndAddr = sect->org + sect->size;
if (sect->size != 0) if (sect->size != 0)
fprintf(mapFile, " SECTION: $%04" PRIx16 "-$%04x ($%04" PRIx16 fprintf(mapFile, "\tSECTION: $%04" PRIx16 "-$%04x ($%04" PRIx16
" byte%s) [\"%s\"]\n", " byte%s) [\"%s\"]\n",
sect->org, prevEndAddr - 1, sect->org, prevEndAddr - 1,
sect->size, sect->size == 1 ? "" : "s", sect->size, sect->size == 1 ? "" : "s",
sect->name); sect->name);
else else
fprintf(mapFile, " SECTION: $%04" PRIx16 " (0 bytes) [\"%s\"]\n", fprintf(mapFile, "\tSECTION: $%04" PRIx16 " (0 bytes) [\"%s\"]\n",
sect->org, sect->name); sect->org, sect->name);
if (!noSymInMap) { if (!noSymInMap) {
@@ -405,11 +456,12 @@ static uint16_t writeMapBank(struct SortedSections const *sectList,
while (sect) { while (sect) {
if (sect->modifier == SECTION_UNION) if (sect->modifier == SECTION_UNION)
fprintf(mapFile, " ; New union\n"); fprintf(mapFile, "\t\t; New union\n");
else if (sect->modifier == SECTION_FRAGMENT) 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++) 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]->offset + org,
sect->symbols[i]->name); sect->symbols[i]->name);
@@ -425,7 +477,7 @@ static uint16_t writeMapBank(struct SortedSections const *sectList,
if (prevEndAddr < bankEndAddr) { if (prevEndAddr < bankEndAddr) {
uint16_t empty = bankEndAddr - prevEndAddr; 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"); empty == 1 ? "" : "s");
} }
@@ -434,7 +486,7 @@ static uint16_t writeMapBank(struct SortedSections const *sectList,
} else { } else {
uint16_t slack = sectionTypeInfo[type].size - used; 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"); 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 * @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) if (!mapFile)
return; return;
fputs("USED:\n", mapFile); fputs("SUMMARY:\n", mapFile);
for (uint8_t i = 0; i < SECTTYPE_INVALID; i++) { for (uint8_t i = 0; i < SECTTYPE_INVALID; i++) {
enum SectionType type = typeMap[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) if (type == SECTTYPE_VRAM || type == SECTTYPE_OAM)
continue; continue;
if (sections[type].nbBanks > 0) { // Do not output unused section types
fprintf(mapFile, " %s: $%04" PRIx32 " byte%s in %" PRIu32 " bank%s\n", 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", sectionTypeInfo[type].name, usedMap[type], usedMap[type] == 1 ? "" : "s",
sections[type].nbBanks, sections[type].nbBanks == 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(symFile);
closeFile(mapFile); closeFile(mapFile);

View File

@@ -146,7 +146,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
isError = false; isError = false;
// Be VERY careful with two `popRPN` in the same expression. // 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 // So, if there are two `popRPN` in the same expression, make
// sure the operation is commutative. // sure the operation is commutative.
switch (command) { switch (command) {

View File

@@ -39,5 +39,6 @@ PRINTLN \1
endm endm
; Representative numeric and string builtins ; Representative numeric and string builtins
tickle __LINE__, 1 ; (SOURCE_DATE_EPOCH in test.sh makes this reproducible)
tickle __FILE__, 0 tickle __UTC_YEAR__, 1
tickle __ISO_8601_UTC__, 0

View File

@@ -1,57 +1,57 @@
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(7): error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(7):
'__LINE__' already defined as constant at <builtin> '__UTC_YEAR__' already defined as constant at <command-line>
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(8): error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(8):
'__LINE__' already defined as constant at <builtin> '__UTC_YEAR__' already defined as constant at <command-line>
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(11): error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(11):
'__LINE__' already defined at <builtin> '__UTC_YEAR__' already defined at <command-line>
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(12): error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(12):
'__LINE__' already defined at <builtin> '__UTC_YEAR__' already defined at <command-line>
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(16): 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): 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): 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): 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): 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): 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): 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): 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): 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): 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): 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): 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)! error: Assembly aborted (28 errors)!

View File

@@ -1,14 +1,14 @@
$9 $7C5
$D $7C5
$12 $7C5
$16 $7C5
$1A $7C5
$1E $7C5
$22 $7C5
$26 $7C5
builtin-overwrite.asm 1989-04-21T12:34:56Z
builtin-overwrite.asm 1989-04-21T12:34:56Z
builtin-overwrite.asm 1989-04-21T12:34:56Z
builtin-overwrite.asm 1989-04-21T12:34:56Z
builtin-overwrite.asm 1989-04-21T12:34:56Z
builtin-overwrite.asm 1989-04-21T12:34:56Z

View File

@@ -33,8 +33,5 @@ endm
_RS += 100 _RS += 100
println _RS println _RS
__LINE__ *= 200
println __LINE__
UnDeFiNeD ^= 300 UnDeFiNeD ^= 300
println UnDeFiNeD println UnDeFiNeD

View File

@@ -1,5 +1,3 @@
error: compound-assignment.asm(36): 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 Expected constant expression: 'UnDeFiNeD' is not constant at assembly time
error: Assembly aborted (2 errors)! error: Assembly aborted (1 error)!

View File

@@ -35,5 +35,4 @@ $5
$14 $14
$A $A
$64 $64
$25
$0 $0

View File

@@ -0,0 +1,2 @@
warning: file-sym.asm(1): [-Wobsolete]
`__FILE__` is deprecated

View 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

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

View 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

View 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

View 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

View File

@@ -1,2 +0,0 @@
if "{@}"
endc

View File

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

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

View File

@@ -0,0 +1,2 @@
warning: long-section-name.asm(1): [-Wlong-string]
String constant too long

View 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

View File

@@ -1 +0,0 @@
PRINTLN "{_NARG}"

View File

@@ -1,3 +0,0 @@
error: narg-nosect.asm(1):
_NARG does not make sense outside of a macro
error: Assembly aborted (1 error)!

View File

@@ -1 +0,0 @@
$0

View File

@@ -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): error: opt-Q.asm(17) -> opt-Q.asm::test(3):
Argument for option 'Q' must be between 1 and 31 Argument for option 'Q' must be between 1 and 31
error: opt-Q.asm(18) -> opt-Q.asm::test(3): error: opt-Q.asm(18) -> opt-Q.asm::test(3):

View File

@@ -1,2 +1,4 @@
PRINTLN %Oo_Oo_Oo
OPT b.X OPT b.X
PRINTLN %..X.X.X. PRINTLN %..X._X.X.

1
test/asm/opt-b.flags Normal file
View File

@@ -0,0 +1 @@
-Weverything -b oO

View File

@@ -1 +1,2 @@
$2A $2A
$2A

View File

@@ -1,2 +1,4 @@
PRINTLN `pqpq_rsrs
OPT g.x0X OPT g.x0X
PRINTLN `.x.x0X0X PRINTLN `.x.x_0X0X

1
test/asm/opt-g.flags Normal file
View File

@@ -0,0 +1 @@
-Weverything -g pqrs

View File

@@ -1 +1,2 @@
$F55 $F55
$F55

View File

@@ -1,5 +0,0 @@
IF DEF(@)
PRINTLN "defined"
ELSE
PRINTLN "not defined"
ENDC

View File

@@ -1 +0,0 @@
defined

3
test/asm/preinclude.asm Normal file
View 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
View File

@@ -0,0 +1,4 @@
warning: preinclude.asm(0) -> preinclude.inc(1): [-Wuser]
pre-included file
warning: preinclude.asm(1): [-Wuser]
main file

View File

@@ -0,0 +1 @@
-Weverything -P preinclude.inc

11
test/asm/preinclude.inc Normal file
View 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
View File

@@ -0,0 +1,7 @@
rept 3
rept 3
rept 3
for 0/3
for 1/3
for 2/3
12 + 34 = 46

View File

@@ -0,0 +1,7 @@
SECTION "test", ROM0
Label:
println SECTION(Label) ; OK
DEF Value EQU 42
println SECTION(Value) ; not OK

View File

@@ -0,0 +1,2 @@
FATAL: section-name-invalid.asm(7):
"Value" does not belong to any section

View File

@@ -0,0 +1 @@
test

19
test/asm/section-name.asm Normal file
View 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

View File

View File

@@ -0,0 +1,8 @@
aaa
aaa
bbb
bbb
bbb
ccc
ccc
ddd

Binary file not shown.

View File

@@ -2,6 +2,9 @@
export LC_ALL=C export LC_ALL=C
# Game Boy release date, 1989-04-21T12:34:56Z (for reproducible test results)
export SOURCE_DATE_EPOCH=609165296
o="$(mktemp)" o="$(mktemp)"
gb="$(mktemp)" gb="$(mktemp)"
input="$(mktemp)" input="$(mktemp)"
@@ -52,20 +55,6 @@ else
rm -f version.asm rm -f version.asm
fi 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 # Check whether to use '.simple.err' files if they exist
# (rgbasm with pre-3.0 Bison just reports "syntax error") # (rgbasm with pre-3.0 Bison just reports "syntax error")
$RGBASM -Weverything -o $o syntax-error.asm > $output 2> $errput $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 fi
for i in *.asm; do 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 for variant in '' '.pipe'; do
echo "${bold}${green}${i%.asm}${variant}...${rescolors}${resbold}" echo "${bold}${green}${i%.asm}${variant}...${rescolors}${resbold}"
desired_errname=${i%.asm}.err desired_errname=${i%.asm}.err
@@ -83,7 +77,7 @@ for i in *.asm; do
desired_errname=${i%.asm}.simple.err desired_errname=${i%.asm}.simple.err
fi fi
if [ -z "$variant" ]; then 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_output=${i%.asm}.out
desired_errput=$desired_errname desired_errput=$desired_errname
else else
@@ -97,7 +91,7 @@ for i in *.asm; do
# stdin redirection makes the input an unseekable pipe - a scenario # stdin redirection makes the input an unseekable pipe - a scenario
# that's harder to deal with and was broken when the feature was # that's harder to deal with and was broken when the feature was
# first implemented. # 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 # Use two otherwise unused files for temp storage
desired_output=$input desired_output=$input

38
test/asm/trigonometry.asm Normal file
View 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

View File

View File

Binary file not shown.

View 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

View 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)!

View File

@@ -0,0 +1,8 @@
$0
?
$0
?
$42
$42!
$3
$3!

View File

@@ -1,10 +1,18 @@
opt Wunmapped-char=1 ; non-default empty charmaps can have unmapped chars
SECTION "test", ROM0 SECTION "test", ROM0
db "A" ; OK, default empty charmap db "A" ; OK, default empty charmap
pushc pushc
newcharmap custom 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 popc
db "A" ; OK, default empty charmap again db "A" ; OK, default empty charmap again

View File

@@ -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' Unmapped character 'A'
warning: unmapped-char.asm(13): [-Wunmapped-char] warning: unmapped-char.asm(21): [-Wunmapped-char]
Unmapped character 'A' Unmapped character 'A'

View File

@@ -1 +1 @@
AAAAA AAAAAAA

View 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

View File

@@ -25,32 +25,20 @@ done
# When updating subprojects, change the commit being checked out, and set the `shallow-since` # 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. # to the day before, to reduce the amount of refs being transferred and thus speed up CI.
if [ ! -d pokecrystal ]; then test_downstream() { # owner/repo shallow-since commit make-target
git clone https://github.com/pret/pokecrystal.git --shallow-since=2022-03-12 --single-branch if [ ! -d ${1##*/} ]; then
fi git clone https://github.com/$1.git --shallow-since=$2 --single-branch
pushd pokecrystal fi
git fetch pushd ${1##*/}
git checkout a3e31d6463e6313aed12ebc733b3f772f2fc78d7 git checkout -f $3
make clean if [ -f ../patches/${1##*/}.patch ]; then
make -j4 compare RGBDS=../../ git apply --ignore-whitespace ../patches/${1##*/}.patch
popd fi
make clean
make -j4 $4 RGBDS=../../
popd
}
if [ ! -d pokered ]; then test_downstream pret/pokecrystal 2022-09-29 70a3ec1accb6de1c1c273470af0ddfa2edc1b0a9 compare
git clone https://github.com/pret/pokered.git --shallow-since=2022-03-07 --single-branch test_downstream pret/pokered 2022-09-29 2b52ceb718b55dce038db24d177715ae4281d065 compare
fi test_downstream AntonioND/ucity 2022-04-20 d8878233da7a6569f09f87b144cb5bf140146a0f ''
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