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)
*.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
# This script was written by ISSOtm while looking at Arch Linux's PKGBUILD for
# the corresponding package. (And its dependencies)
# https://aur.archlinux.org/packages/mingw-w64-libpng/
set -e
pngver=1.6.37
_apngver=$pngver
_arch="$1"
## Install mingw-configure and mingw-env (both build dependencies)
install -m 755 .github/actions/mingw-env.sh /usr/bin/mingw-env
sed "s|@TRIPLE@|${_arch}|g" .github/actions/mingw-configure.sh > ${_arch}-configure
install -m 755 ${_arch}-configure /usr/bin/
arch="$1"
## Grab sources and check them
wget http://downloads.sourceforge.net/sourceforge/libpng/libpng-$pngver.tar.xz
wget http://downloads.sourceforge.net/project/apng/libpng/libpng16/libpng-$_apngver-apng.patch.gz
wget http://downloads.sourceforge.net/project/libpng/libpng16/$pngver/libpng-$pngver.tar.xz
wget http://downloads.sourceforge.net/project/apng/libpng/libpng16/libpng-$pngver-apng.patch.gz
sha256sum -c .github/actions/mingw-w64-libpng-dev.sha256sums
## Extract sources
## Extract sources and patch them
tar -xf libpng-$pngver.tar.xz
gunzip libpng-$_apngver-apng.patch.gz
gunzip libpng-$pngver-apng.patch.gz
# Patch in apng support
env -C libpng-$pngver patch -p0 ../libpng-$pngver-apng.patch
## Start building!
cd libpng-$pngver
# Patch in apng support
patch -p0 ../libpng-$_apngver-apng.patch
mkdir -p build-${_arch}
cd build-${_arch}
${_arch}-configure LDFLAGS=-static-libgcc
make
mkdir -p build
cd build
../libpng-$pngver/configure \
--host="$arch" --target="$arch" \
--prefix="/usr/$arch" \
--enable-shared --disable-static \
CPPFLAGS="-D_FORTIFY_SOURCE=2" \
CFLAGS="-O2 -pipe -fno-plt -fexceptions --param=ssp-buffer-size=4" \
LDFLAGS="-Wl,-O1,--sort-common,--as-needed -fstack-protector"
make -kj
make install

View File

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

View File

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

View File

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

View File

@@ -17,7 +17,7 @@ on:
jobs:
build:
if: github.repository_owner == 'gbdev'
runs-on: ubuntu-18.04
runs-on: ubuntu-22.04
steps:
- name: Checkout rgbds@master
uses: actions/checkout@v2
@@ -31,16 +31,10 @@ jobs:
repository: gbdev/rgbds-www
ref: master
path: rgbds-www
- name: Build and install mandoc + install groff
- name: Install groff and mandoc
run: |
sudo apt-get -qq update
sudo apt-get install -yq groff zlib1g-dev
wget 'http://mandoc.bsd.lv/snapshots/mandoc-1.14.6.tar.gz'
tar xf mandoc-1.14.6.tar.gz
cd mandoc-1.14.6
./configure
make
sudo make install
sudo apt-get install -yq groff mandoc
- name: Update pages
working-directory: rgbds/man
run: |

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -127,6 +127,8 @@ struct Symbol *sym_FindExactSymbol(char const *symName);
struct Symbol *sym_FindUnscopedSymbol(char const *symName);
// Find a symbol, possibly scoped, by name
struct Symbol *sym_FindScopedSymbol(char const *symName);
// Find a scoped symbol by name; do not return `@` or `_NARG` when they have no value
struct Symbol *sym_FindScopedValidSymbol(char const *symName);
struct Symbol const *sym_GetPC(void);
struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size);
struct Symbol *sym_Ref(char const *symName);

View File

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

View File

@@ -10,7 +10,7 @@
#ifndef RGBDS_LINK_SECTION_H
#define RGBDS_LINK_SECTION_H
// GUIDELINE: external code MUST NOT BE AWARE of the data structure used!!
// GUIDELINE: external code MUST NOT BE AWARE of the data structure used!
#include <stdint.h>
#include <stdbool.h>

View File

@@ -10,7 +10,7 @@
#ifndef RGBDS_LINK_SYMBOL_H
#define RGBDS_LINK_SYMBOL_H
// GUIDELINE: external code MUST NOT BE AWARE of the data structure used!!
// GUIDELINE: external code MUST NOT BE AWARE of the data structure used!
#include <stdint.h>

View File

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

View File

@@ -17,28 +17,28 @@
.Op Fl b Ar chars
.Op Fl D Ar name Ns Op = Ns Ar value
.Op Fl g Ar chars
.Op Fl i Ar path
.Op Fl I Ar path
.Op Fl M Ar depend_file
.Op Fl MG
.Op Fl MP
.Op Fl MT Ar target_file
.Op Fl MQ Ar target_file
.Op Fl o Ar out_file
.Op Fl P Ar include_file
.Op Fl p Ar pad_value
.Op Fl Q Ar fix_precision
.Op Fl r Ar recursion_depth
.Op Fl W Ar warning
.Ar
.Ar asmfile
.Sh DESCRIPTION
The
.Nm
program creates an RGB object file from an assembly source file.
The input
.Ar file
can be a file path, or
.Ar asmfile
can be a path to a file, or
.Cm \-
denoting
.Cm stdin .
to read from standard input.
.Pp
Note that options can be abbreviated as long as the abbreviation is unambiguous:
.Fl Fl verb
@@ -86,8 +86,20 @@ Disables inserting a
instruction immediately after any
.Ic halt
instruction.
.It Fl i Ar path , Fl Fl include Ar path
Add an include path.
.It Fl I Ar path , Fl Fl include Ar path
Add a new
.Dq include path ; Ar path
must point to a directory.
When a
.Ic INCLUDE
.Pq including the implicit one from Fl P
or
.Ic INCBIN
is attempted,
.Nm
first looks up the provided path from its working directory; if this fails, it tries again from each of the
.Dq include path
directories, in the order they were provided.
.It Fl L , Fl Fl preserve-ld
By default,
.Nm
@@ -116,6 +128,7 @@ This makes
.Nm
assume that missing files are auto-generated: when
.Ic INCLUDE
.Pq including the implicit one from Fl P
or
.Ic INCBIN
is attempted on a non-existent file, it is added as a dependency, then
@@ -146,6 +159,12 @@ characters, essentially
.Sq $ .
.It Fl o Ar out_file , Fl Fl output Ar out_file
Write an object file to the given filename.
.It Fl P Ar include_file , Fl Fl preinclude Ar include_file
Pre-include a file.
This acts as if a
.Ql Ic INCLUDE Qq Ar include_file
was read before the input
.Ar asmfile .
.It Fl p Ar pad_value , Fl Fl pad-value Ar pad_value
When padding an image, pad with this value.
The default is 0x00.
@@ -301,12 +320,19 @@ warns when an N-bit value's absolute value is 2**N or greater.
or just
.Fl Wtruncation
also warns when an N-bit value is less than -2**(N-1), which will not fit in two's complement encoding.
.It Fl Wunmapped-char
.It Fl Wunmapped-char=
Warn when a character goes through charmap conversion but has no defined mapping.
This warning is always disabled if the active charmap is empty, and/or is the default charmap
.Fl Wunmapped-char=0
or
.Fl Wunmapped-char
disables this warning.
.Fl Wunmapped-char=1
or just
.Fl Wunmapped-char
only warns if the active charmap is not empty.
.Fl Wunmapped-char=2
warns if the active charmap is empty, and/or is not the default charmap
.Sq main .
This warning is enabled by
.Fl Wall .
.It Fl Wno-user
Warn when the
.Ic WARN

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
.Xr gbz80 7 .
.Pp
It is strongly recommended to have some familiarity with the Game Boy hardware before reading this document.
It is advisable to have some familiarity with the Game Boy hardware before reading this document.
RGBDS is specifically targeted at the Game Boy, and thus a lot of its features tie directly to its concepts.
This document is not intended to be a Game Boy hardware reference.
.Pp
@@ -57,27 +57,25 @@ and ending with
.Ql */ .
It can be split across multiple lines, or occur in the middle of an expression:
.Bd -literal -offset indent
X = /* the value of x
DEF X = /* the value of x
should be 3 */ 3
.Ed
.Pp
Sometimes lines can be too long and it may be necessary to split them.
To do so, put a backslash at the end of the line:
.Bd -literal -offset indent
DB 1, 2, 3,\ \[rs]
4, 5, 6,\ \[rs]\ ;\ Put it before any comments
DB 1, 2, 3,\ \e
4, 5, 6,\ \e\ ;\ Put it before any comments
7, 8, 9
DB "Hello,\ \[rs]\ \ ;\ Space before the \[rs] is included
DB "Hello,\ \e\ \ ;\ Space before the \e is included
world!"\ \ \ \ \ \ \ \ \ \ \ ;\ Any leading space is included
.Ed
.Ss Symbol interpolation
A funky feature is
.Ql {symbol}
within a string, called
A funky feature is writing a symbol between
.Ql {braces} ,
called
.Dq symbol interpolation .
This will paste the contents of
.Ql symbol
as if they were part of the source file.
This will paste the symbol's contents as if they were part of the source file.
If it is a string symbol, its characters are simply inserted as-is.
If it is a numeric symbol, its value is converted to hexadecimal notation with a dollar sign
.Sq $
@@ -85,7 +83,7 @@ prepended.
.Pp
Symbol interpolations can be nested, too!
.Bd -literal -offset indent
DEF topic EQUS "life, the universe, and \[rs]"everything\[rs]""
DEF topic EQUS "life, the universe, and \e"everything\e""
DEF meaning EQUS "answer"
;\ Defines answer = 42
DEF {meaning} = 42
@@ -171,19 +169,19 @@ Examples:
.Bd -literal -offset indent
SECTION "Test", ROM0[2]
X: ;\ This works with labels **whose address is known**
Y = 3 ;\ This also works with variables
SUM equ X + Y ;\ And likewise with numeric constants
DEF Y = 3 ;\ This also works with variables
DEF SUM EQU X + Y ;\ And likewise with numeric constants
; Prints "%0010 + $3 == 5"
PRINTLN "{#05b:X} + {#x:Y} == {d:SUM}"
rsset 32
PERCENT rb 1 ;\ Same with offset constants
VALUE = 20
RESULT = MUL(20.0, 0.32)
DEF PERCENT rb 1 ;\ Same with offset constants
DEF VALUE = 20
DEF RESULT = MUL(20.0, 0.32)
; Prints "32% of 20 = 6.40"
PRINTLN "{d:PERCENT}% of {d:VALUE} = {f:RESULT}"
WHO equs STRLWR("WORLD")
DEF WHO EQUS STRLWR("WORLD")
; Prints "Hello world!"
PRINTLN "Hello {s:WHO}!"
.Ed
@@ -348,44 +346,53 @@ delim $$
delim off
.EN
.Pp
All of these fixed-point functions can take an optional final argument, which is the precision to use.
For example,
.Ql MUL(6.0q8, 7.0q8, 8)
will evaluate to
.Ql 42.0q8
no matter what value is set as the current
.Cm Q
option.
.Pp
The trigonometry functions (
.Ic SIN ,
.Ic COS ,
.Ic TAN ,
etc) are defined in terms of a circle divided into 65535.0 degrees.
etc) are defined in terms of a circle divided into 1.0 "turns" (equal to 2pi radians or 360 degrees).
.Pp
These functions are useful for automatic generation of various tables.
For example:
.Bd -literal -offset indent
; Generate a 256-byte sine table with values in the range [0, 128]
; (shifted and scaled from the range [-1.0, 1.0])
ANGLE = 0.0
; 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(ANGLE)) + 64.0) >> 16
ANGLE = ANGLE + 256.0 ; 256.0 = 65536 degrees / 256 entries
db MUL(64.0, SIN(turns) + 1.0) >> 16
DEF turns += 1.0 / 256
ENDR
.Ed
.Ss String expressions
The most basic string expression is any number of characters contained in double quotes
.Pq Ql \&"for instance" .
The backslash character
.Ql \[rs]
.Ql \e
is special in that it causes the character following it to be
.Dq escaped ,
meaning that it is treated differently from normal.
There are a number of escape sequences you can use within a string:
.Bl -column -offset indent "Qo \[rs]1 Qc \[en] Qo \[rs]9 Qc"
.Bl -column -offset indent "Qo \e1 Qc \[en] Qo \e9 Qc"
.It Sy String Ta Sy Meaning
.It Ql \[rs]\[rs] Ta Produces a backslash
.It Ql \[rs]" Ta Produces a double quote without terminating
.It Ql \[rs]{ Ta Curly bracket left
.It Ql \[rs]} Ta Curly bracket right
.It Ql \[rs]n Ta Newline ($0A)
.It Ql \[rs]r Ta Carriage return ($0D)
.It Ql \[rs]t Ta Tab ($09)
.It Qo \[rs]1 Qc \[en] Qo \[rs]9 Qc Ta Macro argument (Only in the body of a macro; see Sx Invoking macros )
.It Ql \[rs]# Ta All Dv _NARG No macro arguments, separated by commas (Only in the body of a macro)
.It Ql \[rs]@ Ta Label name suffix (Only in the body of a macro or a Ic REPT No block)
.It Ql \e\e Ta Produces a backslash
.It Ql \e" Ta Produces a double quote without terminating
.It Ql \e{ Ta Curly bracket left
.It Ql \e} Ta Curly bracket right
.It Ql \en Ta Newline ($0A)
.It Ql \er Ta Carriage return ($0D)
.It Ql \et Ta Tab ($09)
.It Qo \e1 Qc \[en] Qo \e9 Qc Ta Macro argument (Only in the body of a macro; see Sx Invoking macros )
.It Ql \e# Ta All Dv _NARG No macro arguments, separated by commas (Only in the body of a macro)
.It Ql \e@ Ta Label name suffix (Only in the body of a macro or a Ic REPT No block)
.El
(Note that some of those can be used outside of strings, when noted further in this document.)
.Pp
@@ -393,9 +400,9 @@ Multi-line strings are contained in triple quotes
.Pq Ql \&"\&"\&"for instance\&"\&"\&" .
Escape sequences work the same way in multi-line strings; however, literal newline
characters will be included as-is, without needing to escape them with
.Ql \[rs]r
.Ql \er
or
.Ql \[rs]n .
.Ql \en .
.Pp
The following functions operate on string expressions.
Most of them return a string, however some of these functions actually return an integer and can be used as part of an integer expression!
@@ -474,6 +481,11 @@ is a label, it returns the bank number the label is in.
The result may be constant if
.Nm
is able to compute it.
.It Fn SECTION symbol Ta Returns the name of the section that
.Ar symbol
is in.
.Ar symbol
must have been defined already.
.It Fn SIZEOF arg Ta Returns the size of the section named
.Ar arg .
The result is not constant, since only RGBLINK can compute its value.
@@ -1013,7 +1025,7 @@ DEF ARRAY_SIZE EQU 4
DEF COUNT = 2
DEF COUNT = 3
DEF COUNT = ARRAY_SIZE + COUNT
COUNT = COUNT*2
DEF COUNT *= 2
;\ COUNT now has the value 14
.Ed
.Pp
@@ -1066,7 +1078,7 @@ This can be used, for example, to update a constant using a macro, without makin
def NUM_ITEMS equ 0
MACRO add_item
redef NUM_ITEMS equ NUM_ITEMS + 1
def ITEM_{02x:NUM_ITEMS} equ \[rs]1
def ITEM_{02x:NUM_ITEMS} equ \e1
ENDM
add_item 1
add_item 4
@@ -1131,7 +1143,7 @@ will not expand string constants in their names.
DEF COUNTREG EQUS "[hl+]"
ld a,COUNTREG
DEF PLAYER_NAME EQUS "\[rs]"John\[rs]""
DEF PLAYER_NAME EQUS "\e"John\e""
db PLAYER_NAME
.Ed
.Pp
@@ -1143,7 +1155,7 @@ This will be interpreted as:
.Pp
String constants can also be used to define small one-line macros:
.Bd -literal -offset indent
DEF pusha EQUS "push af\[rs]npush bc\[rs]npush de\[rs]npush hl\[rs]n"
DEF pusha EQUS "push af\enpush bc\enpush de\enpush hl\en"
.Ed
.Pp
Note that colons
@@ -1252,18 +1264,18 @@ ENDM
But this will:
.Bd -literal -offset indent
MACRO outer
DEF definition EQUS "MACRO inner\[rs]nPRINTLN \[rs]"Hello!\[rs]"\[rs]nENDM"
DEF definition EQUS "MACRO inner\enPRINTLN \e"Hello!\e"\enENDM"
definition
PURGE definition
ENDM
.Ed
.Pp
Macro arguments support all the escape sequences of strings, as well as
.Ql \[rs],
.Ql \e,
to escape commas, as well as
.Ql \[rs](
.Ql \e(
and
.Ql \[rs])
.Ql \e)
to escape parentheses, since those otherwise separate and enclose arguments, respectively.
.Ss Exporting and importing symbols
Importing and exporting of symbols is a feature that is very useful when your project spans many source files and, for example, you need to jump to a routine defined in another file.
@@ -1324,7 +1336,7 @@ Note also that only exported symbols will appear in symbol and map files produce
.Ss Purging symbols
.Ic PURGE
allows you to completely remove a symbol from the symbol table as if it had never existed.
.Em USE WITH EXTREME CAUTION!!!
.Em USE WITH EXTREME CAUTION!
I can't stress this enough,
.Sy you seriously need to know what you are doing .
DON'T purge a symbol that you use in expressions the linker needs to calculate.
@@ -1343,8 +1355,6 @@ The following symbols are defined by the assembler:
.It Dv @ Ta Ic EQU Ta PC value (essentially, the current memory address)
.It Dv _RS Ta Ic = Ta _RS Counter
.It Dv _NARG Ta Ic EQU Ta Number of arguments passed to macro, updated by Ic SHIFT
.It Dv __LINE__ Ta Ic EQU Ta The current line number
.It Dv __FILE__ Ta Ic EQUS Ta The current filename
.It Dv __DATE__ Ta Ic EQUS Ta Today's date
.It Dv __TIME__ Ta Ic EQUS Ta The current time
.It Dv __ISO_8601_LOCAL__ Ta Ic EQUS Ta ISO 8601 timestamp (local)
@@ -1561,19 +1571,19 @@ ENDM
.Pp
This is fine, but only if you use the macro no more than once per scope.
To get around this problem, there is the escape sequence
.Ic \[rs]@
.Ic \e@
that expands to a unique string.
.Pp
.Ic \[rs]@
.Ic \e@
also works in
.Ic REPT
blocks.
.Bd -literal -offset indent
MACRO LoopyMacro
xor a,a
\&.loop\[rs]@ ld [hl+],a
\&.loop\e@ ld [hl+],a
dec c
jr nz,.loop\[rs]@
jr nz,.loop\e@
ENDM
.Ed
.Pp
@@ -1593,18 +1603,18 @@ which references the same macro, which has the same problem.
.Pp
It's possible to pass arguments to macros as well!
You retrieve the arguments by using the escape sequences
.Ic \[rs]1
.Ic \e1
through
.Ic \[rs]9 , \[rs]1
.Ic \e9 , \e1
being the first argument specified on the macro invocation.
.Bd -literal -offset indent
MACRO LoopyMacro
ld hl,\[rs]1
ld c,\[rs]2
ld hl,\e1
ld c,\e2
xor a,a
\&.loop\[rs]@ ld [hl+],a
\&.loop\e@ ld [hl+],a
dec c
jr nz,.loop\[rs]@
jr nz,.loop\e@
ENDM
.Ed
.Pp
@@ -1617,14 +1627,14 @@ LoopyMacro MyVars,54
Arguments are passed as string constants, although there's no need to enclose them in quotes.
Thus, an expression will not be evaluated first but kind of copy-pasted.
This means that it's probably a very good idea to use brackets around
.Ic \[rs]1
.Ic \e1
to
.Ic \[rs]9
.Ic \e9
if you perform further calculations on them.
For instance, consider the following:
.Bd -literal -offset indent
MACRO print_double
PRINTLN \[rs]1 * 2
PRINTLN \e1 * 2
ENDM
print_double 1 + 2
.Ed
@@ -1639,15 +1649,15 @@ Line continuations work as usual inside macros or lists of macro arguments.
However, some characters need to be escaped, as in the following example:
.Bd -literal -offset indent
MACRO PrintMacro1
PRINTLN STRCAT(\[rs]1)
PRINTLN STRCAT(\e1)
ENDM
PrintMacro1 "Hello "\[rs], \[rs]
PrintMacro1 "Hello "\e, \e
"world"
MACRO PrintMacro2
PRINT \[rs]1
PRINT \e1
ENDM
PrintMacro2 STRCAT("Hello ", \[rs]
"world\[rs]n")
PrintMacro2 STRCAT("Hello ", \e
"world\en")
.Ed
.Pp
The comma in
@@ -1657,34 +1667,34 @@ The comma in
.Ql PrintMacro2
does not need escaping because it is inside parentheses, similar to macro arguments in C.
The backslash in
.Ql \[rs]n
.Ql \en
also does not need escaping because string literals work as usual inside macro arguments.
.Pp
Since there are only nine digits, you can only access the first nine macro arguments like this.
To use the rest, you need to put the multi-digit argument number in angle brackets, like
.Ql \[rs]<10> .
.Ql \e<10> .
This bracketed syntax supports decimal numbers and numeric constant symbols.
For example,
.Ql \[rs]<_NARG>
.Ql \e<_NARG>
will get the last argument.
.Pp
Other macro arguments and symbol interpolations will be expanded inside the angle brackets.
For example, if
.Ql \[rs]1
.Ql \e1
is
.Ql 13 ,
then
.Ql \[rs]<\[rs]1>
.Ql \e<\e1>
will expand to
.Ql \[rs]<13> .
.Ql \e<13> .
Or if
.Ql v10 = 42
and
.Ql x = 10 ,
then
.Ql \[rs]<v{d:x}>
.Ql \e<v{d:x}>
will expand to
.Ql \[rs]<42> .
.Ql \e<42> .
.Pp
Another way to access more than nine macro arguments is the
.Ic SHIFT
@@ -1692,11 +1702,11 @@ command, a special command only available in macros.
It will shift the arguments by one to the left, and decrease
.Dv _NARG
by 1.
.Ic \[rs]1
.Ic \e1
will get the value of
.Ic \[rs]2 , \[rs]2
.Ic \e2 , \e2
will get the value of
.Ic \[rs]3 ,
.Ic \e3 ,
and so forth.
.Pp
.Ic SHIFT
@@ -1715,9 +1725,9 @@ and
commands print text and values to the standard output.
Useful for debugging macros, or wherever you may feel the need to tell yourself some important information.
.Bd -literal -offset indent
PRINT "Hello world!\[rs]n"
PRINT "Hello world!\en"
PRINTLN "Hello world!"
PRINT _NARG, " arguments\[rs]n"
PRINT _NARG, " arguments\en"
PRINTLN "sum: ", 2+3, " product: ", 2*3
PRINTLN "Line #", __LINE__
PRINTLN STRFMT("E = %f", 2.718)
@@ -1731,7 +1741,7 @@ For different formats, use
.Ic STRFMT .
.It Ic PRINTLN
prints out each of its comma-separated arguments, if any, followed by a line feed
.Pq Ql \[rs]n .
.Pq Ql \en .
.El
.Ss Automatically repeating blocks of code
Suppose you want to unroll a time consuming loop without copy-pasting it.
@@ -1755,17 +1765,16 @@ You can also use
.Ic REPT
to generate tables on the fly:
.Bd -literal -offset indent
; Generate a 256-byte sine table with values in the range [0, 128]
; (shifted and scaled from the range [-1.0, 1.0])
ANGLE = 0.0
REPT 256
db (MUL(64.0, SIN(ANGLE)) + 64.0) >> 16
ANGLE = ANGLE + 256.0 ; 256.0 = 65536 degrees / 256 entries
; Generate a table of square values from 0**2 = 0 to 100**2 = 10000
DEF x = 0
REPT 101
dw x * x
DEF x += 1
ENDR
.Ed
.Pp
As in macros, you can also use the escape sequence
.Ic \[rs]@ .
.Ic \e@ .
.Ic REPT
blocks can be nested.
.Pp
@@ -1853,7 +1862,7 @@ This will print:
Just like with
.Ic REPT
blocks, you can use the escape sequence
.Ic \[rs]@
.Ic \e@
inside of
.Ic FOR
blocks, and they can be nested.
@@ -1974,6 +1983,13 @@ calls infinitely (or until you run out of memory, whichever comes first).
.Bd -literal -offset indent
INCLUDE "irq.inc"
.Ed
.Pp
You may also implicitly
.Ic INCLUDE
a file before the source file with the
.Fl P
option of
.Xr rgbasm 1 .
.Ss Conditional assembling
The four commands
.Ic IF , ELIF , ELSE ,

View File

@@ -14,7 +14,6 @@
.Nd Game Boy graphics converter
.Sh SYNOPSIS
.Nm
.Op Fl r Ar stride
.Op Fl CmuVZ
.Op Fl v Op Fl v No ...
.Op Fl a Ar attrmap | Fl A
@@ -27,6 +26,7 @@
.Op Fl o Ar out_file
.Op Fl p Ar pal_file | Fl P
.Op Fl q Ar pal_map | Fl Q
.Op Fl r Ar stride
.Op Fl s Ar nb_colors
.Op Fl t Ar tilemap | Fl T
.Op Fl x Ar quantity
@@ -321,7 +321,22 @@ any command-line argument that begins with an at sign
.Pq Ql @
is interpreted as one.
The rest of the argument (without the @, that is) is interpreted as the path to a file, whose contents are interpreted as if given on the command line.
At-files can be stored right next to the corresponding image, for example.
At-files can be stored right next to the corresponding image, for example:
.Pp
.Dl $ rgbgfx -o image.2bpp -t image.tilemap @image.flags image.png
.Pp
This will read additional flags from file
.Ql image.flags ,
which could contains for example
.Ql -b 128
to specify a base offset for the image's tiles.
The above command could be generated from the following
.Xr make 1
rule, for example:
.Bd -literal -offset indent
%.2bpp %.tilemap: %.flags %.png
rgbgfx -o $*.2bpp -t $*.tilemap @$*.flags $*.png
.Ed
.Pp
Since the contents of at-files are interpreted by
.Nm ,
@@ -336,17 +351,19 @@ optionally preceded by whitespace, are considered comments and also ignored.
Each line can contain any number of arguments, which are separated by whitespace.
.Pq \&No quoting feature to prevent this is provided.
.Pp
Note that this special meaning given to arguments has less precedence than option arguments, and that the standard
Note that a leading
.Ql @
has no special meaning on option arguments, and that the standard
.Ql --
to stop option processing also disables at-file processing.
For example, the following command line processes
.Ql @tilesets/town.png ,
outputs tile data to
.Ql @tilesets/town.2bpp ,
and reads command-line options from
For example, the following command line reads command-line options from
.Ql tilesets/town.flags
then
.Ql tilesets.flags :
.Ql tilesets.flags ,
but processes
.Ql @tilesets/town.png
as the input image and outputs tile data to
.Ql @tilesets/town.2bpp :
.Pp
.Dl $ rgbgfx -o @tilesets/town.2bpp @tilesets/town.flags @tilesets.flags -- @tilesets/town.png
.Pp
@@ -357,11 +374,21 @@ can be used in an at-file (with identical semantics), it is only effective insid
.Sh PALETTE SPECIFICATION FORMATS
The following formats are supported:
.Bl -tag -width Ds
.It Sy act
.It Cm act
.Lk https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_pgfId-1070626 Adobe Photoshop color table .
.It Sy aco
.It Cm aco
.Lk https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_pgfId-1055819 Adobe Photoshop color swatch .
.It Sy psp
.It Cm gbc
A GBC palette memory dump, as emitted by
.Nm Fl p .
Useful to force several images to share the same palette.
.It Cm gpl
.Lk https://docs.gimp.org/2.10/en/gimp-concepts-palettes.html GIMP palette .
.It Cm hex
Plaintext lines of hexadecimal colors in
.Ql rrggbb
format.
.It Cm psp
.Lk https://www.selapa.net/swatches/colors/fileformats.php#psp_pal Paint Shop Pro palette .
.El
.Pp

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

View File

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

View File

@@ -27,7 +27,7 @@
struct Charnode {
bool isTerminal; // Whether there exists a mapping that ends here
uint8_t value; // If the above is true, its corresponding value
// This MUST be indexes and not pointers, because pointers get invalidated by `realloc`!!
// This MUST be indexes and not pointers, because pointers get invalidated by `realloc`!
size_t next[255]; // Indexes of where to go next, 0 = nowhere
};
@@ -249,11 +249,14 @@ size_t charmap_ConvertNext(char const **input, uint8_t **output)
if (output)
*output += codepointLen;
// Check if the character map is not the default "main" one, or if
// it has any mappings defined
if (strcmp(charmap->name, "main") || charmap->usedNodes > 1)
warning(WARNING_UNMAPPED_CHAR,
// Warn if this character is not mapped but any others are
if (charmap->usedNodes > 1)
warning(WARNING_UNMAPPED_CHAR_1,
"Unmapped character %s\n", printChar(firstChar));
else if (strcmp(charmap->name, DEFAULT_CHARMAP_NAME))
warning(WARNING_UNMAPPED_CHAR_2,
"Unmapped character %s not in " DEFAULT_CHARMAP_NAME
" charmap\n", printChar(firstChar));
return codepointLen;

View File

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

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 numLen = !!sign + !!prefix + len;
size_t numLen = (sign != 0) + (prefix != 0) + len;
size_t totalLen = fmt->width > numLen ? fmt->width : numLen;
if (totalLen > bufLen - 1) { // bufLen includes terminator

View File

@@ -19,6 +19,7 @@
#include "asm/main.h"
#include "asm/symbol.h"
#include "asm/warning.h"
#include "error.h"
#include "platform.h" // S_ISDIR (stat macro)
#define MAXINCPATHS 128
@@ -42,6 +43,8 @@ size_t maxRecursionDepth;
static unsigned int nbIncPaths = 0;
static char const *includePaths[MAXINCPATHS];
static const char *preIncludeName;
static const char *dumpNodeAndParents(struct FileStackNode const *node)
{
char const *name;
@@ -133,6 +136,15 @@ void fstk_AddIncludePath(char const *path)
includePaths[nbIncPaths++] = str;
}
void fstk_SetPreIncludeFile(char const *path)
{
if (preIncludeName)
warnx("Overriding pre-included filename %s", preIncludeName);
preIncludeName = path;
if (verbose)
printf("Pre-included filename %s\n", preIncludeName);
}
static void printDep(char const *path)
{
if (dependfile) {
@@ -274,11 +286,12 @@ bool yywrap(void)
lexer_SetState(contextStack->lexerState);
macro_SetUniqueID(contextStack->uniqueID);
return false;
}
// Make sure not to switch the lexer state before calling this, so the saved line no is correct.
// BE CAREFUL!! This modifies the file stack directly, you should have set up the file info first.
// BE CAREFUL! This modifies the file stack directly, you should have set up the file info first.
// Callers should set contextStack->lexerState after this so it is not NULL.
static void newContext(struct FileStackNode *fileInfo)
{
@@ -300,7 +313,7 @@ static void newContext(struct FileStackNode *fileInfo)
context->forName = NULL;
// Link new entry to its parent so it's reachable later
// ERRORS SHOULD NOT OCCUR AFTER THIS!!
// ERRORS SHOULD NOT OCCUR AFTER THIS!
context->parent = contextStack;
contextStack = context;
}
@@ -342,6 +355,41 @@ void fstk_RunInclude(char const *path)
contextStack->uniqueID = macro_UndefUniqueID();
}
// Similar to `fstk_RunInclude`, but not subject to `-MG`, and
// calling `lexer_SetState` instead of `lexer_SetStateAtEOL`.
static void runPreIncludeFile(void)
{
if (!preIncludeName)
return;
char *fullPath = NULL;
size_t size = 0;
if (!fstk_FindFile(preIncludeName, &fullPath, &size)) {
free(fullPath);
error("Unable to open included file '%s': %s\n", preIncludeName, strerror(errno));
return;
}
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + size);
if (!fileInfo) {
error("Failed to alloc file info for pre-include: %s\n", strerror(errno));
return;
}
fileInfo->node.type = NODE_FILE;
strcpy(fileInfo->name, fullPath);
free(fullPath);
newContext((struct FileStackNode *)fileInfo);
contextStack->lexerState = lexer_OpenFile(fileInfo->name);
if (!contextStack->lexerState)
fatalerror("Failed to set up lexer for file include\n");
lexer_SetState(contextStack->lexerState);
// We're back at top-level, so most things are reset
contextStack->uniqueID = macro_UndefUniqueID();
}
void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
{
struct Symbol *macro = sym_FindExactSymbol(macroName);
@@ -563,4 +611,6 @@ void fstk_Init(char const *mainPath, size_t maxDepth)
// Make sure that the default of 64 is OK, though
assert(DEPTH_LIMIT >= DEFAULT_MAX_DEPTH);
#undef DEPTH_LIMIT
runPreIncludeFile();
}

View File

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

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

View File

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

View File

@@ -550,6 +550,7 @@ enum {
%token T_OP_BANK "BANK"
%token T_OP_ALIGN "ALIGN"
%token T_OP_SIZEOF "SIZEOF" T_OP_STARTOF "STARTOF"
%token T_OP_SIN "SIN" T_OP_COS "COS" T_OP_TAN "TAN"
%token T_OP_ASIN "ASIN" T_OP_ACOS "ACOS" T_OP_ATAN "ATAN" T_OP_ATAN2 "ATAN2"
%token T_OP_FDIV "FDIV"
@@ -559,6 +560,7 @@ enum {
%token T_OP_LOG "LOG"
%token T_OP_ROUND "ROUND"
%token T_OP_CEIL "CEIL" T_OP_FLOOR "FLOOR"
%type <constValue> opt_q_arg
%token T_OP_HIGH "HIGH" T_OP_LOW "LOW"
%token T_OP_ISCONST "ISCONST"
@@ -1462,56 +1464,54 @@ relocexpr_no_str : scoped_anon_id { rpn_Symbol(&$$, $1); }
| T_OP_DEF {
lexer_ToggleStringExpansion(false);
} T_LPAREN scoped_anon_id T_RPAREN {
struct Symbol const *sym = sym_FindScopedSymbol($4);
rpn_Number(&$$, !!sym);
rpn_Number(&$$, sym_FindScopedValidSymbol($4) != NULL);
lexer_ToggleStringExpansion(true);
}
| T_OP_ROUND T_LPAREN const T_RPAREN {
rpn_Number(&$$, fix_Round($3));
| T_OP_ROUND T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Round($3, $4));
}
| T_OP_CEIL T_LPAREN const T_RPAREN {
rpn_Number(&$$, fix_Ceil($3));
| T_OP_CEIL T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Ceil($3, $4));
}
| T_OP_FLOOR T_LPAREN const T_RPAREN {
rpn_Number(&$$, fix_Floor($3));
| T_OP_FLOOR T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Floor($3, $4));
}
| T_OP_FDIV T_LPAREN const T_COMMA const T_RPAREN {
rpn_Number(&$$, fix_Div($3, $5));
| T_OP_FDIV T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Div($3, $5, $6));
}
| T_OP_FMUL T_LPAREN const T_COMMA const T_RPAREN {
rpn_Number(&$$, fix_Mul($3, $5));
| T_OP_FMUL T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Mul($3, $5, $6));
}
| T_OP_FMOD T_LPAREN const T_COMMA const T_RPAREN {
rpn_Number(&$$, fix_Mod($3, $5));
| T_OP_FMOD T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Mod($3, $5, $6));
}
| T_OP_POW T_LPAREN const T_COMMA const T_RPAREN {
rpn_Number(&$$, fix_Pow($3, $5));
| T_OP_POW T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Pow($3, $5, $6));
}
| T_OP_LOG T_LPAREN const T_COMMA const T_RPAREN {
rpn_Number(&$$, fix_Log($3, $5));
| T_OP_LOG T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Log($3, $5, $6));
}
| T_OP_SIN T_LPAREN const T_RPAREN {
rpn_Number(&$$, fix_Sin($3));
| T_OP_SIN T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Sin($3, $4));
}
| T_OP_COS T_LPAREN const T_RPAREN {
rpn_Number(&$$, fix_Cos($3));
| T_OP_COS T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Cos($3, $4));
}
| T_OP_TAN T_LPAREN const T_RPAREN {
rpn_Number(&$$, fix_Tan($3));
| T_OP_TAN T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_Tan($3, $4));
}
| T_OP_ASIN T_LPAREN const T_RPAREN {
rpn_Number(&$$, fix_ASin($3));
| T_OP_ASIN T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_ASin($3, $4));
}
| T_OP_ACOS T_LPAREN const T_RPAREN {
rpn_Number(&$$, fix_ACos($3));
| T_OP_ACOS T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_ACos($3, $4));
}
| T_OP_ATAN T_LPAREN const T_RPAREN {
rpn_Number(&$$, fix_ATan($3));
| T_OP_ATAN T_LPAREN const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_ATan($3, $4));
}
| T_OP_ATAN2 T_LPAREN const T_COMMA const T_RPAREN {
rpn_Number(&$$, fix_ATan2($3, $5));
| T_OP_ATAN2 T_LPAREN const T_COMMA const opt_q_arg T_RPAREN {
rpn_Number(&$$, fix_ATan2($3, $5, $6));
}
| T_OP_STRCMP T_LPAREN string T_COMMA string T_RPAREN {
rpn_Number(&$$, strcmp($3, $5));
@@ -1538,7 +1538,7 @@ relocexpr_no_str : scoped_anon_id { rpn_Symbol(&$$, $1); }
uconst : const {
$$ = $1;
if ($$ < 0)
fatalerror("Constant mustn't be negative: %d\n", $1);
fatalerror("Constant must not be negative: %d\n", $1);
}
;
@@ -1551,6 +1551,17 @@ const_no_str : relocexpr_no_str { $$ = rpn_GetConstVal(&$1); }
const_8bit : reloc_8bit { $$ = rpn_GetConstVal(&$1); }
;
opt_q_arg : %empty { $$ = fix_Precision(); }
| T_COMMA const {
if ($2 >= 1 && $2 <= 31) {
$$ = $2;
} else {
error("Fixed-point precision must be between 1 and 31\n");
$$ = fix_Precision();
}
}
;
string : T_STRING
| T_OP_STRSUB T_LPAREN string T_COMMA const T_COMMA uconst T_RPAREN {
size_t len = strlenUTF8($3);
@@ -1589,6 +1600,19 @@ string : T_STRING
strfmt($$, sizeof($$), $3.format, $3.nbArgs, $3.args);
freeStrFmtArgList(&$3);
}
| T_POP_SECTION T_LPAREN scoped_anon_id T_RPAREN {
struct Symbol *sym = sym_FindScopedValidSymbol($3);
if (!sym)
fatalerror("Unknown symbol \"%s\"\n", $3);
struct Section const *section = sym_GetSection(sym);
if (!section)
fatalerror("\"%s\" does not belong to any section\n", sym->name);
// Section names are capped by rgbasm's maximum string length,
// so this currently can't overflow.
strcpy($$, section->name);
}
;
strcat_args : string

View File

@@ -36,6 +36,7 @@ HashMap symbols;
static const char *labelScope; // Current section's label scope
static struct Symbol *PCSymbol;
static struct Symbol *_NARGSymbol;
static char savedTIME[256];
static char savedDATE[256];
static char savedTIMESTAMP_ISO8601_LOCAL[256];
@@ -78,12 +79,15 @@ static int32_t Callback_NARG(void)
static int32_t Callback__LINE__(void)
{
warning(WARNING_OBSOLETE, "`__LINE__` is deprecated\n");
return lexer_GetLineNo();
}
static char const *Callback__FILE__(void)
{
// FIXME: this is dangerous, and here's why this is CURRENTLY okay. It's still bad, fix it.
warning(WARNING_OBSOLETE, "`__FILE__` is deprecated\n");
// There are only two call sites for this; one copies the contents directly, the other is
// EQUS expansions, which cannot straddle file boundaries. So this should be fine.
static char *buf = NULL;
@@ -97,7 +101,7 @@ static char const *Callback__FILE__(void)
// Account for the extra backslash inserted below
if (fileName[i] == '"')
j++;
// Ensure there will be enough room; DO NOT PRINT ANYTHING ABOVE THIS!!
// Ensure there will be enough room; DO NOT PRINT ANYTHING ABOVE THIS!
if (j + 2 >= bufsize) { // Always keep room for 2 tail chars
bufsize = bufsize ? bufsize * 2 : 64;
buf = realloc(buf, bufsize);
@@ -247,6 +251,21 @@ struct Symbol *sym_FindScopedSymbol(char const *symName)
return sym_FindExactSymbol(symName);
}
struct Symbol *sym_FindScopedValidSymbol(char const *symName)
{
struct Symbol *sym = sym_FindScopedSymbol(symName);
// `@` has no value outside a section
if (sym == PCSymbol && !sect_GetSymbolSection()) {
return NULL;
}
// `_NARG` has no value outside a macro
if (sym == _NARGSymbol && !macro_GetCurrentArgs()) {
return NULL;
}
return sym;
}
struct Symbol const *sym_GetPC(void)
{
return PCSymbol;
@@ -260,7 +279,7 @@ static bool isReferenced(struct Symbol const *sym)
// Purge a symbol
void sym_Purge(char const *symName)
{
struct Symbol *sym = sym_FindScopedSymbol(symName);
struct Symbol *sym = sym_FindScopedValidSymbol(symName);
if (!sym) {
error("'%s' not defined\n", symName);
@@ -571,7 +590,7 @@ struct Symbol *sym_AddAnonLabel(void)
}
char name[MAXSYMLEN + 1];
sym_WriteAnonLabelName(name, 0, true); // The direction is important!!
sym_WriteAnonLabelName(name, 0, true); // The direction is important!
anonLabelID++;
return addLabel(name);
}
@@ -679,8 +698,10 @@ static struct Symbol *createBuiltinSymbol(char const *symName)
void sym_Init(time_t now)
{
PCSymbol = createBuiltinSymbol("@");
struct Symbol *_NARGSymbol = createBuiltinSymbol("_NARG");
_NARGSymbol = createBuiltinSymbol("_NARG");
// __LINE__ is deprecated
struct Symbol *__LINE__Symbol = createBuiltinSymbol("__LINE__");
// __FILE__ is deprecated
struct Symbol *__FILE__Symbol = createBuiltinSymbol("__FILE__");
PCSymbol->type = SYM_LABEL;

View File

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

View File

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

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);
};
// HACK: for compatibility with old versions, add unused colors if:
// - there is only one palette, and
// - only some of the first N colors are being used
if (palettes.size() == 1) {
Palette &palette = palettes[0];
// Build our candidate array of colors
decltype(palette.colors) colors{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX};
for (int i = 0; i < options.maxOpaqueColors(); ++i) {
colors[i + options.hasTransparentPixels] = pngToRgb(i).cgbColor();
}
// Check that the palette only uses those colors
if (std::all_of(palette.begin(), palette.end(), [&colors](uint16_t color) {
return std::find(colors.begin(), colors.end(), color) != colors.end();
})) {
if (palette.size() != options.maxOpaqueColors()) {
warning("Unused color in PNG embedded palette was re-added; please use `-c "
"embedded` to get this in future versions");
}
// Overwrite the palette, and return with that (it's already sorted)
palette.colors = colors;
return;
}
}
for (Palette &pal : palettes) {
std::sort(pal.begin(), pal.end(), [&](uint16_t lhs, uint16_t rhs) {
// Iterate through the PNG's palette, looking for either of the two

View File

@@ -16,6 +16,8 @@
#include <cstdio>
#include <cstring>
#include <fstream>
#include <limits>
#include <optional>
#include <ostream>
#include <streambuf>
#include <string>
@@ -53,7 +55,7 @@ constexpr uint8_t singleToHex(char c) {
template<typename Str> // Should be std::string or std::string_view
static void skipWhitespace(Str const &str, typename Str::size_type &pos) {
pos = std::min(str.find_first_not_of(" \t", pos), str.length());
pos = std::min(str.find_first_not_of(" \t"sv, pos), str.length());
}
void parseInlinePalSpec(char const * const rawArg) {
@@ -191,6 +193,15 @@ static T readBE(U const *bytes) {
return val;
}
template<typename T, typename U>
static T readLE(U const *bytes) {
T val = 0;
for (size_t i = 0; i < sizeof(val); ++i) {
val |= static_cast<uint8_t>(bytes[i]) << (i * 8);
}
return val;
}
/*
* **Appends** the first line read from `file` to the end of the provided `buffer`.
*/
@@ -217,13 +228,53 @@ static void readLine(std::filebuf &file, std::string &buffer) {
/*
* Parses the initial part of a string_view, advancing the "read index" as it does
*/
static uint16_t parseDec(std::string const &str, std::string::size_type &n) {
uint32_t value = 0; // Use a larger type to handle overflow more easily
for (auto end = std::min(str.length(), str.find_first_not_of("0123456789", n)); n < end; ++n) {
value = std::min<uint32_t>(value * 10 + (str[n] - '0'), UINT16_MAX);
template<typename U> // Should be uint*_t
static std::optional<U> parseDec(std::string const &str, std::string::size_type &n) {
std::string::size_type start = n;
uintmax_t value = 0; // Use a larger type to handle overflow more easily
for (auto end = std::min(str.length(), str.find_first_not_of("0123456789"sv, n)); n < end;
++n) {
value = std::min(value * 10 + (str[n] - '0'), (uintmax_t)std::numeric_limits<U>::max);
}
return value;
return n > start ? std::optional<U>{value} : std::nullopt;
}
static std::optional<Rgba> parseColor(std::string const &str, std::string::size_type &n,
uint16_t i) {
std::optional<uint8_t> r = parseDec<uint8_t>(str, n);
if (!r) {
error("Failed to parse color #%" PRIu16 " (\"%s\"): invalid red component", i + 1,
str.c_str());
return std::nullopt;
}
skipWhitespace(str, n);
if (n == str.length()) {
error("Failed to parse color #%" PRIu16 " (\"%s\"): missing green component", i + 1,
str.c_str());
return std::nullopt;
}
std::optional<uint8_t> g = parseDec<uint8_t>(str, n);
if (!g) {
error("Failed to parse color #%" PRIu16 " (\"%s\"): invalid green component", i + 1,
str.c_str());
return std::nullopt;
}
skipWhitespace(str, n);
if (n == str.length()) {
error("Failed to parse color #%" PRIu16 " (\"%s\"): missing blue component", i + 1,
str.c_str());
return std::nullopt;
}
std::optional<uint8_t> b = parseDec<uint8_t>(str, n);
if (!b) {
error("Failed to parse color #%" PRIu16 " (\"%s\"): invalid blue component", i + 1,
str.c_str());
return std::nullopt;
}
return std::optional<Rgba>{Rgba(*r, *g, *b, 0xFF)};
}
static void parsePSPFile(std::filebuf &file) {
@@ -246,41 +297,30 @@ static void parsePSPFile(std::filebuf &file) {
line.clear();
readLine(file, line);
std::string::size_type n = 0;
uint16_t nbColors = parseDec(line, n);
if (n != line.length()) {
std::optional<uint16_t> nbColors = parseDec<uint16_t>(line, n);
if (!nbColors || n != line.length()) {
error("Invalid \"number of colors\" line in PSP file (%s)", line.c_str());
return;
}
if (nbColors > options.nbColorsPerPal * options.nbPalettes) {
if (*nbColors > options.nbColorsPerPal * options.nbPalettes) {
warning("PSP file contains %" PRIu16 " colors, but there can only be %" PRIu16
"; ignoring extra",
nbColors, options.nbColorsPerPal * options.nbPalettes);
*nbColors, options.nbColorsPerPal * options.nbPalettes);
nbColors = options.nbColorsPerPal * options.nbPalettes;
}
options.palSpec.clear();
for (uint16_t i = 0; i < nbColors; ++i) {
for (uint16_t i = 0; i < *nbColors; ++i) {
line.clear();
readLine(file, line);
n = 0;
uint8_t r = parseDec(line, n);
skipWhitespace(line, n);
if (n == line.length()) {
error("Failed to parse color #%" PRIu16 " (\"%s\"): missing green component", i + 1,
line.c_str());
n = 0;
std::optional<Rgba> color = parseColor(line, n, i + 1);
if (!color) {
return;
}
uint8_t g = parseDec(line, n);
if (n == line.length()) {
error("Failed to parse color #%" PRIu16 " (\"%s\"): missing green component", i + 1,
line.c_str());
return;
}
skipWhitespace(line, n);
uint8_t b = parseDec(line, n);
if (n != line.length()) {
error("Failed to parse color #%" PRIu16
" (\"%s\"): trailing characters after blue component",
@@ -291,11 +331,98 @@ static void parsePSPFile(std::filebuf &file) {
if (i % options.nbColorsPerPal == 0) {
options.palSpec.emplace_back();
}
options.palSpec.back()[i % options.nbColorsPerPal] = Rgba(r, g, b, 0xFF);
options.palSpec.back()[i % options.nbColorsPerPal] = *color;
}
}
void parseACTFile(std::filebuf &file) {
static void parseGPLFile(std::filebuf &file) {
// https://gitlab.gnome.org/GNOME/gimp/-/blob/gimp-2-10/app/core/gimppalette-load.c#L39
std::string line;
readLine(file, line);
// FIXME: C++20 will allow `!line.starts_with` instead of `line.rfind` with 0
if (line.rfind("GIMP Palette", 0)) {
error("Palette file does not appear to be a GPL palette file");
return;
}
uint16_t nbColors = 0;
uint16_t maxNbColors = options.nbColorsPerPal * options.nbPalettes;
for (;;) {
line.clear();
readLine(file, line);
if (!line.length()) {
break;
}
// FIXME: C++20 will allow `line.starts_with` instead of `!line.rfind` with 0
if (!line.rfind("#", 0) || !line.rfind("Name:", 0) || !line.rfind("Column:", 0)) {
continue;
}
std::string::size_type n = 0;
std::optional<Rgba> color = parseColor(line, n, nbColors + 1);
if (!color) {
return;
}
++nbColors;
if (nbColors < maxNbColors) {
if (nbColors % options.nbColorsPerPal == 1) {
options.palSpec.emplace_back();
}
options.palSpec.back()[nbColors % options.nbColorsPerPal] = *color;
}
}
if (nbColors > maxNbColors) {
warning("GPL file contains %" PRIu16 " colors, but there can only be %" PRIu16
"; ignoring extra",
nbColors, maxNbColors);
}
}
static void parseHEXFile(std::filebuf &file) {
// https://lospec.com/palette-list/tag/gbc
uint16_t nbColors = 0;
uint16_t maxNbColors = options.nbColorsPerPal * options.nbPalettes;
for (;;) {
std::string line;
readLine(file, line);
if (!line.length()) {
break;
}
if (line.length() != 6
|| line.find_first_not_of("0123456789ABCDEFabcdef"sv) != std::string::npos) {
error("Failed to parse color #%" PRIu16 " (\"%s\"): invalid \"rrggbb\" line",
nbColors + 1, line.c_str());
return;
}
Rgba color =
Rgba(toHex(line[0], line[1]), toHex(line[2], line[3]), toHex(line[4], line[5]), 0xFF);
++nbColors;
if (nbColors < maxNbColors) {
if (nbColors % options.nbColorsPerPal == 1) {
options.palSpec.emplace_back();
}
options.palSpec.back()[nbColors % options.nbColorsPerPal] = color;
}
}
if (nbColors > maxNbColors) {
warning("HEX file contains %" PRIu16 " colors, but there can only be %" PRIu16
"; ignoring extra",
nbColors, maxNbColors);
}
}
static void parseACTFile(std::filebuf &file) {
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_pgfId-1070626
std::array<char, 772> buf;
@@ -344,8 +471,8 @@ void parseACTFile(std::filebuf &file) {
}
}
void parseACOFile(std::filebuf &file) {
// rhttps://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_pgfId-1055819
static void parseACOFile(std::filebuf &file) {
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_pgfId-1055819
// http://www.nomodes.com/aco.html
char buf[10];
@@ -412,6 +539,29 @@ void parseACOFile(std::filebuf &file) {
// `codecvt` can be used to convert from UTF-16 to UTF-8
}
static void parseGBCFile(std::filebuf &file) {
// This only needs to be able to read back files generated by `rgbgfx -p`
options.palSpec.clear();
for (;;) {
char buf[2 * 4];
auto len = file.sgetn(buf, sizeof(buf));
if (len == 0) {
break;
} else if (len != sizeof(buf)) {
error("GBC palette dump contains %zu 8-byte palette%s, plus %zu byte%s",
options.palSpec.size(), options.palSpec.size() == 1 ? "" : "s", len,
len == 1 ? "" : "s");
break;
}
options.palSpec.push_back({Rgba::fromCGBColor(readLE<uint16_t>(&buf[0])),
Rgba::fromCGBColor(readLE<uint16_t>(&buf[2])),
Rgba::fromCGBColor(readLE<uint16_t>(&buf[4])),
Rgba::fromCGBColor(readLE<uint16_t>(&buf[6]))});
}
}
void parseExternalPalSpec(char const *arg) {
// `fmt:path`, parse the file according to the given format
@@ -425,8 +575,11 @@ void parseExternalPalSpec(char const *arg) {
static std::array parsers{
std::tuple{"PSP", &parsePSPFile, std::ios::in },
std::tuple{"GPL", &parseGPLFile, std::ios::in },
std::tuple{"HEX", &parseHEXFile, std::ios::in },
std::tuple{"ACT", &parseACTFile, std::ios::binary},
std::tuple{"ACO", &parseACOFile, std::ios::binary},
std::tuple{"GBC", &parseGBCFile, std::ios::binary},
};
auto iter = std::find_if(parsers.begin(), parsers.end(),

View File

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

View File

@@ -459,7 +459,12 @@ static struct Section *getMainSection(struct Section *section)
void obj_ReadFile(char const *fileName, unsigned int fileID)
{
FILE *file = strcmp("-", fileName) ? fopen(fileName, "rb") : stdin;
FILE *file;
if (strcmp("-", fileName) != 0)
file = fopen(fileName, "rb");
else
file = fdopen(STDIN_FILENO, "rb"); // `stdin` is in text mode by default
if (!file)
err("Could not open file %s", fileName);

View File

@@ -17,6 +17,8 @@
#include "link/section.h"
#include "link/symbol.h"
#include "extern/utf8decoder.h"
#include "error.h"
#include "linkdefs.h"
#include "platform.h" // MIN_NB_ELMS
@@ -273,6 +275,55 @@ static struct SortedSection const **nextSection(struct SortedSection const **s1,
return (*s1)->section->org < (*s2)->section->org ? s1 : s2;
}
// Checks whether this character is legal as the first character of a symbol's name in a sym file
static bool canStartSymName(char c)
{
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_';
}
// Checks whether this character is legal in a symbol's name in a sym file
static bool isLegalForSymName(char c)
{
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
c == '_' || c == '@' || c == '#' || c == '$' || c == '.';
}
// Prints a symbol's name to `symFile`, assuming that the first character is legal.
// Illegal characters are UTF-8-decoded (errors are replaced by U+FFFD) and emitted as `\u`/`\U`.
static void printSymName(char const *name)
{
for (char const *ptr = name; *ptr != '\0'; ) {
char c = *ptr;
if (isLegalForSymName(c)) {
// Output legal ASCII characters as-is
fputc(c, symFile);
++ptr;
} else {
// Output illegal characters using Unicode escapes
// Decode the UTF-8 codepoint; or at least attempt to
uint32_t state = 0, codepoint;
do {
decode(&state, &codepoint, *ptr);
if (state == 1) {
// This sequence was invalid; emit a U+FFFD, and recover
codepoint = 0xFFFD;
// Skip continuation bytes
// A NUL byte does not qualify, so we're good
while ((*ptr & 0xC0) == 0x80)
++ptr;
break;
}
++ptr;
} while (state != 0);
fprintf(symFile, codepoint <= 0xFFFF ? "\\u%04" PRIx32 : "\\U%08" PRIx32,
codepoint);
}
}
}
// Comparator function for `qsort` to sort symbols
// Symbols are ordered by address, or else by original index for a stable sort
static int compareSymbols(void const *a, void const *b)
@@ -296,16 +347,22 @@ static void writeSymBank(struct SortedSections const *bankSections,
if (!symFile)
return;
#define forEachSortedSection(sect, ...) do { \
for (struct SortedSection const *ssp = bankSections->zeroLenSections; ssp; ssp = ssp->next) { \
for (struct Section const *sect = ssp->section; sect; sect = sect->nextu) \
__VA_ARGS__ \
} \
for (struct SortedSection const *ssp = bankSections->sections; ssp; ssp = ssp->next) { \
for (struct Section const *sect = ssp->section; sect; sect = sect->nextu) \
__VA_ARGS__ \
} \
} while (0)
uint32_t nbSymbols = 0;
for (struct SortedSection const *ptr = bankSections->zeroLenSections; ptr; ptr = ptr->next) {
for (struct Section const *sect = ptr->section; sect; sect = sect->nextu)
forEachSortedSection(sect, {
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)
return;
@@ -315,29 +372,21 @@ static void writeSymBank(struct SortedSections const *bankSections,
if (!symList)
err("Failed to allocate symbol list");
uint32_t idx = 0;
nbSymbols = 0;
for (struct SortedSection const *ptr = bankSections->zeroLenSections; ptr; ptr = ptr->next) {
for (struct Section const *sect = ptr->section; sect; sect = sect->nextu) {
forEachSortedSection(sect, {
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++;
if (!canStartSymName(sect->symbols[i]->name[0]))
// Don't output symbols that begin with an illegal character
continue;
symList[nbSymbols].idx = nbSymbols;
symList[nbSymbols].sym = sect->symbols[i];
symList[nbSymbols].addr = symList[nbSymbols].sym->offset + sect->org;
nbSymbols++;
}
}
}
for (struct SortedSection const *ptr = bankSections->sections; ptr; ptr = ptr->next) {
for (struct Section const *sect = ptr->section; sect; sect = sect->nextu) {
for (uint32_t i = 0; i < sect->nbSymbols; i++) {
symList[idx].idx = idx;
symList[idx].sym = sect->symbols[i];
symList[idx].addr = symList[idx].sym->offset + sect->org;
idx++;
}
}
}
assert(idx == nbSymbols);
});
#undef forEachSortedSection
qsort(symList, nbSymbols, sizeof(*symList), compareSymbols);
@@ -346,11 +395,13 @@ static void writeSymBank(struct SortedSections const *bankSections,
for (uint32_t i = 0; i < nbSymbols; i++) {
struct SortedSymbol *sym = &symList[i];
fprintf(symFile, "%02" PRIx32 ":%04" PRIx16 " %s\n",
symBank, sym->addr, sym->sym->name);
fprintf(symFile, "%02" PRIx32 ":%04" PRIx16 " ", symBank, sym->addr);
printSymName(sym->sym->name);
fputc('\n', symFile);
}
free(symList);
}
/*
@@ -384,20 +435,20 @@ static uint16_t writeMapBank(struct SortedSections const *sectList,
if (prevEndAddr < sect->org) {
uint16_t empty = sect->org - prevEndAddr;
fprintf(mapFile, " EMPTY: $%04" PRIx16 " byte%s\n", empty,
fprintf(mapFile, "\tEMPTY: $%04" PRIx16 " byte%s\n", empty,
empty == 1 ? "" : "s");
}
prevEndAddr = sect->org + sect->size;
if (sect->size != 0)
fprintf(mapFile, " SECTION: $%04" PRIx16 "-$%04x ($%04" PRIx16
fprintf(mapFile, "\tSECTION: $%04" PRIx16 "-$%04x ($%04" PRIx16
" byte%s) [\"%s\"]\n",
sect->org, prevEndAddr - 1,
sect->size, sect->size == 1 ? "" : "s",
sect->name);
else
fprintf(mapFile, " SECTION: $%04" PRIx16 " (0 bytes) [\"%s\"]\n",
fprintf(mapFile, "\tSECTION: $%04" PRIx16 " (0 bytes) [\"%s\"]\n",
sect->org, sect->name);
if (!noSymInMap) {
@@ -405,11 +456,12 @@ static uint16_t writeMapBank(struct SortedSections const *sectList,
while (sect) {
if (sect->modifier == SECTION_UNION)
fprintf(mapFile, " ; New union\n");
fprintf(mapFile, "\t\t; New union\n");
else if (sect->modifier == SECTION_FRAGMENT)
fprintf(mapFile, " ; New fragment\n");
fprintf(mapFile, "\t\t; New fragment\n");
for (size_t i = 0; i < sect->nbSymbols; i++)
fprintf(mapFile, " $%04" PRIx32 " = %s\n",
// "\tSECTION: $xxxx ..."
fprintf(mapFile, "\t $%04" PRIx32 " = %s\n",
sect->symbols[i]->offset + org,
sect->symbols[i]->name);
@@ -425,7 +477,7 @@ static uint16_t writeMapBank(struct SortedSections const *sectList,
if (prevEndAddr < bankEndAddr) {
uint16_t empty = bankEndAddr - prevEndAddr;
fprintf(mapFile, " EMPTY: $%04" PRIx16 " byte%s\n", empty,
fprintf(mapFile, "\tEMPTY: $%04" PRIx16 " byte%s\n", empty,
empty == 1 ? "" : "s");
}
@@ -434,7 +486,7 @@ static uint16_t writeMapBank(struct SortedSections const *sectList,
} else {
uint16_t slack = sectionTypeInfo[type].size - used;
fprintf(mapFile, " SLACK: $%04" PRIx16 " byte%s\n\n", slack,
fprintf(mapFile, "\tSLACK: $%04" PRIx16 " byte%s\n\n", slack,
slack == 1 ? "" : "s");
}
@@ -442,15 +494,15 @@ static uint16_t writeMapBank(struct SortedSections const *sectList,
}
/*
* Write the total used space by section type to the map file
* Write the total used and free space by section type to the map file
* @param usedMap The total used space by section type
*/
static void writeMapUsed(uint32_t usedMap[MIN_NB_ELMS(SECTTYPE_INVALID)])
static void writeMapSummary(uint32_t usedMap[MIN_NB_ELMS(SECTTYPE_INVALID)])
{
if (!mapFile)
return;
fputs("USED:\n", mapFile);
fputs("SUMMARY:\n", mapFile);
for (uint8_t i = 0; i < SECTTYPE_INVALID; i++) {
enum SectionType type = typeMap[i];
@@ -459,11 +511,18 @@ static void writeMapUsed(uint32_t usedMap[MIN_NB_ELMS(SECTTYPE_INVALID)])
if (type == SECTTYPE_VRAM || type == SECTTYPE_OAM)
continue;
if (sections[type].nbBanks > 0) {
fprintf(mapFile, " %s: $%04" PRIx32 " byte%s in %" PRIu32 " bank%s\n",
// Do not output unused section types
if (sections[type].nbBanks == 0)
continue;
fprintf(mapFile, "\t%s: %" PRId32 " byte%s used / %" PRId32 " free",
sectionTypeInfo[type].name, usedMap[type], usedMap[type] == 1 ? "" : "s",
sections[type].nbBanks, 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(mapFile);

View File

@@ -146,7 +146,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
isError = false;
// Be VERY careful with two `popRPN` in the same expression.
// C does not guarantee order of evaluation of operands!!
// C does not guarantee order of evaluation of operands!
// So, if there are two `popRPN` in the same expression, make
// sure the operation is commutative.
switch (command) {

View File

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

View File

@@ -1,57 +1,57 @@
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(7):
'__LINE__' already defined as constant at <builtin>
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(8):
'__LINE__' already defined as constant at <builtin>
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(11):
'__LINE__' already defined at <builtin>
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(12):
'__LINE__' already defined at <builtin>
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(16):
Built-in symbol '__LINE__' cannot be purged
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(17):
Built-in symbol '__LINE__' cannot be purged
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(20):
'__LINE__' already defined at <builtin>
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(21):
'__LINE__' already defined at <builtin>
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(24):
'__LINE__' already defined as constant at <builtin>
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(25):
'__LINE__' already defined as constant at <builtin>
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(28):
'__LINE__' already defined at <builtin>
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(29):
'__LINE__' already defined at <builtin>
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(32):
'__LINE__' already defined as constant at <builtin>
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(33):
'__LINE__' already defined as constant at <builtin>
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(36):
'__LINE__' already defined as non-EQUS at <builtin>
error: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(37):
'__LINE__' already defined as non-EQUS at <builtin>
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(7):
'__UTC_YEAR__' already defined as constant at <command-line>
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(8):
'__UTC_YEAR__' already defined as constant at <command-line>
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(11):
'__UTC_YEAR__' already defined at <command-line>
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(12):
'__UTC_YEAR__' already defined at <command-line>
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(16):
Built-in symbol '__FILE__' cannot be purged
Built-in symbol '__UTC_YEAR__' cannot be purged
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(17):
Built-in symbol '__FILE__' cannot be purged
Built-in symbol '__UTC_YEAR__' cannot be purged
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(20):
'__FILE__' already defined at <builtin>
'__UTC_YEAR__' already defined at <command-line>
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(21):
'__FILE__' already defined at <builtin>
'__UTC_YEAR__' already defined at <command-line>
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(24):
'__FILE__' already defined as constant at <builtin>
'__UTC_YEAR__' already defined as constant at <command-line>
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(25):
'__FILE__' already defined as constant at <builtin>
'__UTC_YEAR__' already defined as constant at <command-line>
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(28):
'__FILE__' already defined at <builtin>
'__UTC_YEAR__' already defined at <command-line>
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(29):
'__FILE__' already defined at <builtin>
'__UTC_YEAR__' already defined at <command-line>
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(32):
'__FILE__' already defined as constant at <builtin>
'__UTC_YEAR__' already defined as constant at <command-line>
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(33):
'__FILE__' already defined as constant at <builtin>
'__UTC_YEAR__' already defined as constant at <command-line>
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(36):
Built-in symbol '__FILE__' cannot be redefined
'__UTC_YEAR__' already defined as non-EQUS at <command-line>
error: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(37):
Built-in symbol '__FILE__' cannot be redefined
'__UTC_YEAR__' already defined as non-EQUS at <command-line>
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(16):
Built-in symbol '__ISO_8601_UTC__' cannot be purged
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(17):
Built-in symbol '__ISO_8601_UTC__' cannot be purged
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(20):
'__ISO_8601_UTC__' already defined at <command-line>
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(21):
'__ISO_8601_UTC__' already defined at <command-line>
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(24):
'__ISO_8601_UTC__' already defined as constant at <command-line>
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(25):
'__ISO_8601_UTC__' already defined as constant at <command-line>
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(28):
'__ISO_8601_UTC__' already defined at <command-line>
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(29):
'__ISO_8601_UTC__' already defined at <command-line>
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(32):
'__ISO_8601_UTC__' already defined as constant at <command-line>
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(33):
'__ISO_8601_UTC__' already defined as constant at <command-line>
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(36):
Built-in symbol '__ISO_8601_UTC__' cannot be redefined
error: builtin-overwrite.asm(44) -> builtin-overwrite.asm::tickle(37):
Built-in symbol '__ISO_8601_UTC__' cannot be redefined
error: Assembly aborted (28 errors)!

View File

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

View File

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

View File

@@ -1,5 +1,3 @@
error: compound-assignment.asm(36):
'__LINE__' already defined as constant at <builtin>
error: compound-assignment.asm(39):
Expected constant expression: 'UnDeFiNeD' is not constant at assembly time
error: Assembly aborted (2 errors)!
error: Assembly aborted (1 error)!

View File

@@ -35,5 +35,4 @@ $5
$14
$A
$64
$25
$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):
Argument for option 'Q' must be between 1 and 31
error: opt-Q.asm(18) -> opt-Q.asm::test(3):

View File

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

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

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

View File

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

View File

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

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

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

View File

@@ -1 +1,2 @@
$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
# Game Boy release date, 1989-04-21T12:34:56Z (for reproducible test results)
export SOURCE_DATE_EPOCH=609165296
o="$(mktemp)"
gb="$(mktemp)"
input="$(mktemp)"
@@ -52,20 +55,6 @@ else
rm -f version.asm
fi
# Add the quote test, except on Windows
if uname | grep -viq mingw; then
cat > quote\"file.asm <<EOF
WARN __FILE__
EOF
cat > quote\"file.out <<EOF
EOF
cat > quote\"file.err <<EOF
warning: quote"file.asm(1): [-Wuser]
quote"file.asm
while expanding symbol "__FILE__"
EOF
fi
# Check whether to use '.simple.err' files if they exist
# (rgbasm with pre-3.0 Bison just reports "syntax error")
$RGBASM -Weverything -o $o syntax-error.asm > $output 2> $errput
@@ -76,6 +65,11 @@ if ! diff --strip-trailing-cr syntax-error.err $errput; then
fi
for i in *.asm; do
flags=${i%.asm}.flags
RGBASMFLAGS=-Weverything
if [ -f $flags ]; then
RGBASMFLAGS="$(head -n 1 "$flags")" # Allow other lines to serve as comments
fi
for variant in '' '.pipe'; do
echo "${bold}${green}${i%.asm}${variant}...${rescolors}${resbold}"
desired_errname=${i%.asm}.err
@@ -83,7 +77,7 @@ for i in *.asm; do
desired_errname=${i%.asm}.simple.err
fi
if [ -z "$variant" ]; then
$RGBASM -Weverything -o $o $i > $output 2> $errput
$RGBASM $RGBASMFLAGS -o $o $i > $output 2> $errput
desired_output=${i%.asm}.out
desired_errput=$desired_errname
else
@@ -97,7 +91,7 @@ for i in *.asm; do
# stdin redirection makes the input an unseekable pipe - a scenario
# that's harder to deal with and was broken when the feature was
# first implemented.
cat $i | $RGBASM -Weverything -o $o - > $output 2> $errput
cat $i | $RGBASM $RGBASMFLAGS -o $o - > $output 2> $errput
# Use two otherwise unused files for temp storage
desired_output=$input

38
test/asm/trigonometry.asm Normal file
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
db "A" ; OK, default empty charmap
pushc
newcharmap custom
db "A" ; unmapped in non-default charmap
db "A" ; OK, unmapped in non-default empty charmap
pusho
opt Wunmapped-char=2
db "A" ; unmapped in non-default empty charmap
popo
charmap "C", $99
db "A" ; unmapped in non-empty charmap
popc
db "A" ; OK, default empty charmap again

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