mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Compare commits
149 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0759c98d91 | ||
|
|
402ffbf0c5 | ||
|
|
8191e5eb27 | ||
|
|
323738e7b8 | ||
|
|
a1d132cd35 | ||
|
|
e93d65d736 | ||
|
|
0f4d543aeb | ||
|
|
dab5f59ed9 | ||
|
|
22a6a82642 | ||
|
|
7b592eff8a | ||
|
|
4be81d9ffd | ||
|
|
55fbecee49 | ||
|
|
f36a3d5b2a | ||
|
|
c5e8e4ff83 | ||
|
|
ccc666c1e4 | ||
|
|
89eda89838 | ||
|
|
17b9838c8f | ||
|
|
889dd83798 | ||
|
|
38a372f25f | ||
|
|
2e6f5ac679 | ||
|
|
7a45fc68d9 | ||
|
|
1288737c0d | ||
|
|
5902306271 | ||
|
|
2fe4521a96 | ||
|
|
74673436a1 | ||
|
|
481748c279 | ||
|
|
9faa5c7a9e | ||
|
|
6fbb25c0da | ||
|
|
65fc42cce5 | ||
|
|
736b7727b6 | ||
|
|
fa920f8449 | ||
|
|
01aa56606f | ||
|
|
bddd5bc678 | ||
|
|
f6b7e39ce7 | ||
|
|
1363dd8f34 | ||
|
|
8214f0c09d | ||
|
|
ff7c46f5a6 | ||
|
|
b44f5825a5 | ||
|
|
631910bd67 | ||
|
|
37089ef940 | ||
|
|
4ef27a0d23 | ||
|
|
476ccc9f6b | ||
|
|
89dc14fcaf | ||
|
|
1ca2f12d24 | ||
|
|
20b2f5ee2f | ||
|
|
3cc67c48cf | ||
|
|
f9a04696f2 | ||
|
|
e0e8170fe6 | ||
|
|
9b40663d54 | ||
|
|
a517f900e4 | ||
|
|
350f40300c | ||
|
|
d3db5f0d76 | ||
|
|
7e3af4b3cd | ||
|
|
dfb3072381 | ||
|
|
02191135a0 | ||
|
|
dc2c97fe0c | ||
|
|
6068b565f5 | ||
|
|
a21cef7190 | ||
|
|
461ef6cea5 | ||
|
|
e05199ca1e | ||
|
|
12d82eb768 | ||
|
|
05becf3f4b | ||
|
|
3cc7981c82 | ||
|
|
8f287eeef9 | ||
|
|
ce05cb5683 | ||
|
|
17945a7377 | ||
|
|
3a1b47129e | ||
|
|
6ffa751090 | ||
|
|
c3641321d7 | ||
|
|
446173f0cb | ||
|
|
e27f381842 | ||
|
|
a3ee76dddd | ||
|
|
995265c549 | ||
|
|
03629b74d9 | ||
|
|
b069278e98 | ||
|
|
9738c88f95 | ||
|
|
a21ea30be0 | ||
|
|
64752da42d | ||
|
|
e3e18063c6 | ||
|
|
e400eac42b | ||
|
|
a6bf77718c | ||
|
|
c2787a9ea9 | ||
|
|
e33e6e2413 | ||
|
|
3cb56c5a2e | ||
|
|
91984cb7e7 | ||
|
|
b8d5dd1824 | ||
|
|
88b66f2941 | ||
|
|
3c7e59b9e1 | ||
|
|
c4471e3300 | ||
|
|
16111f46ef | ||
|
|
b019b03946 | ||
|
|
605acd24ba | ||
|
|
c8630eee95 | ||
|
|
4040555532 | ||
|
|
defb221c98 | ||
|
|
e7dc094e56 | ||
|
|
dfdb107105 | ||
|
|
9f598dfdb7 | ||
|
|
38110a833d | ||
|
|
484d15dbb2 | ||
|
|
1decf5d0d4 | ||
|
|
74e9de1b71 | ||
|
|
015d2b0830 | ||
|
|
c75a9539ba | ||
|
|
ca6149abcf | ||
|
|
b3120aea25 | ||
|
|
54e5bf0f0c | ||
|
|
847cae5b95 | ||
|
|
df15c97b6e | ||
|
|
0d97b58265 | ||
|
|
f7bc61e874 | ||
|
|
8d5a53f529 | ||
|
|
20f9492899 | ||
|
|
3cd1d46a1b | ||
|
|
88eceec257 | ||
|
|
d00ec024a2 | ||
|
|
0bcd53778a | ||
|
|
7592eaf42b | ||
|
|
12ed9e044a | ||
|
|
0a3af87aee | ||
|
|
4dee999f68 | ||
|
|
9a4941c794 | ||
|
|
2d0fd71159 | ||
|
|
327582be31 | ||
|
|
21aea281bd | ||
|
|
975f85260d | ||
|
|
f29d768989 | ||
|
|
9bd7ecad4c | ||
|
|
cc458a9693 | ||
|
|
d2bd9a2368 | ||
|
|
b909a5063a | ||
|
|
b2c1f6122e | ||
|
|
a761e98e18 | ||
|
|
e12e7b2acc | ||
|
|
f927c41abb | ||
|
|
249acace08 | ||
|
|
fa37922ca7 | ||
|
|
021990b8e0 | ||
|
|
540564694c | ||
|
|
8da4feb83c | ||
|
|
23f5e9dacc | ||
|
|
6ff9435e0a | ||
|
|
40006c6152 | ||
|
|
b256e4c2e3 | ||
|
|
a37a09c09c | ||
|
|
8ece231d8b | ||
|
|
e7de0745ad | ||
|
|
7af2d5dfe1 | ||
|
|
2f2f14bf80 |
@@ -67,3 +67,9 @@
|
||||
|
||||
# Don't complain when files are modified in 'include/asm'
|
||||
--ignore MODIFIED_INCLUDE_ASM
|
||||
|
||||
# Don't complain when bools are used in structs
|
||||
--ignore BOOL_MEMBER
|
||||
|
||||
# Don't complain about initializing statics (this is specific to the kernel)
|
||||
--ignore INITIALISED_STATIC
|
||||
|
||||
7
.dockerignore
Normal file
7
.dockerignore
Normal file
@@ -0,0 +1,7 @@
|
||||
# This file is part of RGBDS.
|
||||
#
|
||||
# Copyright (c) 2018-2019, Phil Smith and RGBDS contributors.
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
.git
|
||||
docs
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -6,6 +6,6 @@ rgbgfx
|
||||
*.exe
|
||||
.checkpatch-camelcase.*
|
||||
|
||||
test/pokecrystal/*
|
||||
test/pokered/*
|
||||
test/ucity/*
|
||||
test/pokecrystal
|
||||
test/pokered
|
||||
test/ucity
|
||||
|
||||
@@ -26,9 +26,6 @@ echo "Running checkpatch.pl..."
|
||||
fname=$(mktemp)
|
||||
rc=0
|
||||
|
||||
git remote set-branches --add origin develop
|
||||
git fetch
|
||||
|
||||
make CHECKPATCH=checkpatchdir/checkpatch.pl checkpatch > $fname
|
||||
|
||||
cat $fname
|
||||
|
||||
@@ -2,8 +2,8 @@ language: c
|
||||
sudo: required
|
||||
install:
|
||||
- ./.travis-deps.sh
|
||||
- make
|
||||
- sudo make install
|
||||
- make Q=
|
||||
- sudo make install Q=
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
@@ -65,9 +65,9 @@ copyright and the reference to the MIT License.
|
||||
|
||||
1. Fork this repository.
|
||||
|
||||
2. Checkout the ``develop`` branch.
|
||||
2. Checkout the ``master`` branch.
|
||||
|
||||
3. Create a new branch to work on. You could still work on ``develop``, but it's
|
||||
3. Create a new branch to work on. You could still work on ``master``, but it's
|
||||
easier that way.
|
||||
|
||||
4. Compile your changes with ``make develop`` instead of just ``make``. This
|
||||
@@ -88,9 +88,9 @@ copyright and the reference to the MIT License.
|
||||
code. By default, the Makefile expects the script (and associate files) to be
|
||||
located in ``../linux/scripts/``, but you can place them anywhere you like as
|
||||
long as you specify it when executing the command:
|
||||
``CHECKPATCH=../path/to/folder make checkpatch``.
|
||||
``make checkpatch CHECKPATCH=../path/to/folder``.
|
||||
|
||||
8. Create a pull request against the branch ``develop``.
|
||||
8. Create a pull request against the branch ``master``.
|
||||
|
||||
9. Be prepared to get some comments about your code and to modify it. Tip: Use
|
||||
``git rebase -i origin/develop`` to modify chains of commits.
|
||||
``git rebase -i origin/master`` to modify chains of commits.
|
||||
|
||||
@@ -22,7 +22,7 @@ Main contributors
|
||||
Other contributors
|
||||
------------------
|
||||
|
||||
- Ben10do
|
||||
- Ben Hetherington <dev@ben-h.uk>
|
||||
|
||||
- Björn Höhrmann <bjoern@hoehrmann.de>
|
||||
|
||||
@@ -30,14 +30,22 @@ Other contributors
|
||||
|
||||
- David Brotz <dbrotz007@gmail.com>
|
||||
|
||||
- Eldred Habert <eldredhabert0@gmail.com>
|
||||
|
||||
- The Musl C library <http://www.musl-libc.org>
|
||||
|
||||
- obskyr <powpowd@gmail.com>
|
||||
|
||||
- The OpenBSD Project <http://www.openbsd.org>
|
||||
|
||||
- Quint Guvernator <quint@guvernator.net>
|
||||
|
||||
- Sanqui <gsanky@gmail.com>
|
||||
|
||||
- YamaArashi <shadow962@live.com>
|
||||
|
||||
- yenatch <yenatch@gmail.com>
|
||||
|
||||
- phs <phil@philhsmith.com>
|
||||
|
||||
- jidoc01 <jidoc01@naver.com>
|
||||
|
||||
25
Dockerfile
Normal file
25
Dockerfile
Normal file
@@ -0,0 +1,25 @@
|
||||
# This file is part of RGBDS.
|
||||
#
|
||||
# Copyright (c) 2018-2019, Phil Smith and RGBDS contributors.
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
# docker build -t rgbds:vX.X.X-alpine
|
||||
FROM alpine:latest
|
||||
RUN apk add --update \
|
||||
build-base \
|
||||
byacc \
|
||||
flex \
|
||||
libpng-dev
|
||||
COPY . /rgbds
|
||||
WORKDIR /rgbds
|
||||
RUN make Q='' all
|
||||
|
||||
FROM alpine:latest
|
||||
RUN apk add --update \
|
||||
libpng
|
||||
COPY --from=0 \
|
||||
/rgbds/rgbasm \
|
||||
/rgbds/rgbfix \
|
||||
/rgbds/rgblink \
|
||||
/rgbds/rgbgfx \
|
||||
/bin/
|
||||
50
Makefile
50
Makefile
@@ -6,6 +6,9 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .h .l .y .c .o
|
||||
|
||||
# User-defined variables
|
||||
|
||||
Q := @
|
||||
@@ -33,6 +36,10 @@ CFLAGS := -g
|
||||
# Non-overridable CFLAGS
|
||||
REALCFLAGS := ${CFLAGS} ${WARNFLAGS} -std=c99 -D_POSIX_C_SOURCE=200809L \
|
||||
-Iinclude -DBUILD_VERSION_STRING=\"${VERSION_STRING}\"
|
||||
# Overridable LDFLAGS
|
||||
LDFLAGS :=
|
||||
# Non-overridable LDFLAGS
|
||||
REALLDFLAGS := ${LDFLAGS} ${WARNFLAGS}
|
||||
|
||||
YFLAGS :=
|
||||
LFLAGS := --nounistd
|
||||
@@ -57,12 +64,12 @@ rgbasm_obj := \
|
||||
src/asm/output.o \
|
||||
src/asm/rpn.o \
|
||||
src/asm/symbol.o \
|
||||
src/asm/util.o \
|
||||
src/extern/err.o \
|
||||
src/extern/utf8decoder.o \
|
||||
src/version.o
|
||||
|
||||
src/asm/asmy.h: src/asm/asmy.c
|
||||
src/asm/locallex.o src/asm/globlex.o src/asm/lexer.o src/asm/constexpr.o: src/asm/asmy.h
|
||||
src/asm/globlex.o src/asm/lexer.o src/asm/constexpr.o: src/asm/asmy.h
|
||||
|
||||
rgblink_obj := \
|
||||
src/link/assign.o \
|
||||
@@ -79,7 +86,6 @@ rgblink_obj := \
|
||||
src/extern/err.o \
|
||||
src/version.o
|
||||
|
||||
src/link/parser.h: src/link/parser.c
|
||||
src/link/lexer.o: src/link/parser.h
|
||||
|
||||
rgbfix_obj := \
|
||||
@@ -95,27 +101,32 @@ rgbgfx_obj := \
|
||||
src/version.o
|
||||
|
||||
rgbasm: ${rgbasm_obj}
|
||||
$Q${CC} ${REALCFLAGS} -o $@ ${rgbasm_obj} -lm
|
||||
$Q${CC} ${REALLDFLAGS} -o $@ ${rgbasm_obj} -lm
|
||||
|
||||
rgblink: ${rgblink_obj}
|
||||
$Q${CC} ${REALCFLAGS} -o $@ ${rgblink_obj}
|
||||
$Q${CC} ${REALLDFLAGS} -o $@ ${rgblink_obj}
|
||||
|
||||
rgbfix: ${rgbfix_obj}
|
||||
$Q${CC} ${REALCFLAGS} -o $@ ${rgbfix_obj}
|
||||
$Q${CC} ${REALLDFLAGS} -o $@ ${rgbfix_obj}
|
||||
|
||||
rgbgfx: ${rgbgfx_obj}
|
||||
$Q${CC} ${REALCFLAGS} ${PNGLDFLAGS} -o $@ ${rgbgfx_obj} ${PNGLDLIBS}
|
||||
$Q${CC} ${REALLDFLAGS} ${PNGLDFLAGS} -o $@ ${rgbgfx_obj} ${PNGLDLIBS}
|
||||
|
||||
# Rules to process files
|
||||
|
||||
# We want the yacc and lex invocations to pass through our rules
|
||||
.y.o:
|
||||
.l.o:
|
||||
|
||||
# yacc- and lex-generated C files have an accompanying header
|
||||
.c.h:
|
||||
$Qtouch $@
|
||||
|
||||
.y.c:
|
||||
$Q${YACC} -d ${YFLAGS} -o $@ $<
|
||||
|
||||
.l.o:
|
||||
$Q${RM} $*.c
|
||||
$Q${LEX} ${LFLAGS} -o $*.c $<
|
||||
$Q${CC} ${REALCFLAGS} -c -o $@ $*.c
|
||||
$Q${RM} $*.c
|
||||
.l.c:
|
||||
$Q${LEX} ${LFLAGS} -o $@ $<
|
||||
|
||||
.c.o:
|
||||
$Q${CC} ${REALCFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
||||
@@ -124,10 +135,11 @@ rgbgfx: ${rgbgfx_obj}
|
||||
# for the html documentation.
|
||||
|
||||
clean:
|
||||
$Q${RM} rgbasm rgbasm.exe ${rgbasm_obj}
|
||||
$Q${RM} rgblink rgblink.exe ${rgblink_obj}
|
||||
$Q${RM} rgbfix rgbfix.exe ${rgbfix_obj}
|
||||
$Q${RM} rgbgfx rgbgfx.exe ${rgbgfx_obj}
|
||||
$Q${RM} rgbasm rgbasm.exe
|
||||
$Q${RM} rgblink rgblink.exe
|
||||
$Q${RM} rgbfix rgbfix.exe
|
||||
$Q${RM} rgbgfx rgbgfx.exe
|
||||
$Qfind src/ -name "*.o" -exec rm {} \;
|
||||
$Q${RM} src/asm/asmy.c src/asm/asmy.h
|
||||
$Q${RM} src/link/lexer.c src/link/parser.c src/link/parser.h
|
||||
|
||||
@@ -169,11 +181,11 @@ checkcodebase:
|
||||
|
||||
# Target used to check the coding style of the patches from the upstream branch
|
||||
# to the HEAD. Runs checkpatch once for each commit between the current HEAD and
|
||||
# the first common commit between the HEAD and origin/develop. '.y' and '.l'
|
||||
# the first common commit between the HEAD and origin/master. '.y' and '.l'
|
||||
# files aren't checked, unfortunately...
|
||||
|
||||
checkpatch:
|
||||
$Qeval COMMON_COMMIT=$$(git merge-base HEAD origin/develop); \
|
||||
$Qeval COMMON_COMMIT=$$(git merge-base HEAD origin/master); \
|
||||
for commit in `git rev-list $$COMMON_COMMIT..HEAD`; do \
|
||||
echo "[*] Analyzing commit '$$commit'"; \
|
||||
git format-patch --stdout "$$commit~..$$commit" \
|
||||
@@ -202,7 +214,7 @@ wwwman:
|
||||
|
||||
develop:
|
||||
$Qenv make -j WARNFLAGS="-Werror -Wall -Wextra -Wpedantic \
|
||||
-Wno-sign-compare -Wchkp -Wformat=2 -Wformat-overflow=2 \
|
||||
-Wno-sign-compare -Wformat=2 -Wformat-overflow=2 \
|
||||
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused \
|
||||
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5 \
|
||||
-Wstringop-overflow=4 -Walloc-zero -Wduplicated-cond \
|
||||
|
||||
@@ -24,13 +24,16 @@ found in this repository.
|
||||
1.1 Windows
|
||||
~~~~~~~~~~~
|
||||
|
||||
Windows builds are available in the releases page on GitHub:
|
||||
Windows builds are available in the releases page on GitHub
|
||||
`here <https://github.com/rednex/rgbds/releases>`__.
|
||||
|
||||
Extract the zip and then add the executable directory to the path. For example:
|
||||
|
||||
::
|
||||
|
||||
https://github.com/rednex/rgbds/releases
|
||||
path %PATH%;C:\Programs\rgbds-0.3.8-win64\win64
|
||||
|
||||
Copy the ``.exe`` files to ``C:\Windows\`` or similar.
|
||||
Alternatively, the RGBDS executables can be simply dropped in the folder of the project they're used in.
|
||||
|
||||
If you require the latest version in development, it should be possible to
|
||||
compile RGBDS with MinGW or Cygwin by following the instructions to build it on
|
||||
|
||||
2760
docs/gbz80.7.html
2760
docs/gbz80.7.html
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- This is an automatically generated file. Do not edit.
|
||||
This file is part of RGBDS.
|
||||
|
||||
Copyright (c) 2010-2019, Anthony J. Bentley and RGBDS contributors.
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<style>
|
||||
table.head, table.foot { width: 100%; }
|
||||
td.head-rtitle, td.foot-os { text-align: right; }
|
||||
td.head-vol { text-align: center; }
|
||||
div.Pp { margin: 1ex 0ex; }
|
||||
</style>
|
||||
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
|
||||
<title>RGBASM(1)</title>
|
||||
</head>
|
||||
@@ -20,136 +21,122 @@
|
||||
</tr>
|
||||
</table>
|
||||
<div class="manual-text">
|
||||
<h1 class="Sh" title="Sh" id="NAME"><a class="selflink" href="#NAME">NAME</a></h1>
|
||||
<b class="Nm" title="Nm">rgbasm</b> — <span class="Nd" title="Nd">Game
|
||||
Boy assembler</span>
|
||||
<h1 class="Sh" title="Sh" id="SYNOPSIS"><a class="selflink" href="#SYNOPSIS">SYNOPSIS</a></h1>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
|
||||
<code class="Nm">rgbasm</code> —
|
||||
<div class="Nd">Game Boy assembler</div>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="SYNOPSIS"><a class="permalink" href="#SYNOPSIS">SYNOPSIS</a></h1>
|
||||
<table class="Nm">
|
||||
<tr>
|
||||
<td><b class="Nm" title="Nm">rgbasm</b></td>
|
||||
<td>[<span class="Op"><b class="Fl" title="Fl">-EhLVvw</b></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-b</b>
|
||||
<var class="Ar" title="Ar">chars</var></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-D</b>
|
||||
<var class="Ar" title="Ar">name</var>[<span class="Op">=<var class="Ar" title="Ar">value</var></span>]</span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-g</b>
|
||||
<var class="Ar" title="Ar">chars</var></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-i</b>
|
||||
<var class="Ar" title="Ar">path</var></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-M</b>
|
||||
<var class="Ar" title="Ar">dependfile</var></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-o</b>
|
||||
<var class="Ar" title="Ar">outfile</var></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-p</b>
|
||||
<var class="Ar" title="Ar">pad_value</var></span>]
|
||||
<var class="Ar" title="Ar">file</var></td>
|
||||
<td><code class="Nm">rgbasm</code></td>
|
||||
<td>[<code class="Fl">-EhLVvw</code>] [<code class="Fl">-b</code>
|
||||
<var class="Ar">chars</var>] [<code class="Fl">-D</code>
|
||||
<var class="Ar">name</var>[=<var class="Ar">value</var>]]
|
||||
[<code class="Fl">-g</code> <var class="Ar">chars</var>]
|
||||
[<code class="Fl">-i</code> <var class="Ar">path</var>]
|
||||
[<code class="Fl">-M</code> <var class="Ar">dependfile</var>]
|
||||
[<code class="Fl">-o</code> <var class="Ar">outfile</var>]
|
||||
[<code class="Fl">-p</code> <var class="Ar">pad_value</var>]
|
||||
[<code class="Fl">-r</code> <var class="Ar">recursion_depth</var>]
|
||||
<var class="Ar">file</var></td>
|
||||
</tr>
|
||||
</table>
|
||||
<h1 class="Sh" title="Sh" id="DESCRIPTION"><a class="selflink" href="#DESCRIPTION">DESCRIPTION</a></h1>
|
||||
The <b class="Nm" title="Nm">rgbasm</b> program creates an object file from an
|
||||
assembly source file. Its arguments are as follows:
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
|
||||
The <code class="Nm">rgbasm</code> program creates an object file from an
|
||||
assembly source file. The input <var class="Ar">file</var> can be a file path,
|
||||
or <code class="Cm">-</code> denoting <code class="Cm">stdin</code>. Its
|
||||
arguments are as follows:
|
||||
<dl class="Bl-tag">
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#b"><b class="Fl" title="Fl" id="b">-b</b></a>
|
||||
<var class="Ar" title="Ar">chars</var></dt>
|
||||
<dd class="It-tag">Change the two characters used for binary constants. The
|
||||
defaults are 01.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#D"><b class="Fl" title="Fl" id="D">-D</b></a>
|
||||
<var class="Ar" title="Ar">name</var>[<span class="Op">=<var class="Ar" title="Ar">value</var></span>]</dt>
|
||||
<dd class="It-tag">Add string symbol to the compiled source code. This is
|
||||
equivalent to <var class="Ar" title="Ar">name</var>
|
||||
<b class="Cm" title="Cm">EQUS</b>
|
||||
“<var class="Ar" title="Ar">value</var>” in code. If a value
|
||||
is not specified, a value of 1 is given.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#E"><b class="Fl" title="Fl" id="E">-E</b></a></dt>
|
||||
<dd class="It-tag">Export all labels, including unreferenced and local
|
||||
labels.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#g"><b class="Fl" title="Fl" id="g">-g</b></a>
|
||||
<var class="Ar" title="Ar">chars</var></dt>
|
||||
<dd class="It-tag">Change the four characters used for binary constants. The
|
||||
defaults are 0123.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#h"><b class="Fl" title="Fl" id="h">-h</b></a></dt>
|
||||
<dd class="It-tag">By default, <b class="Nm" title="Nm">rgbasm</b> inserts a
|
||||
‘nop’ instruction immediately after any ‘halt’
|
||||
instruction. The <b class="Fl" title="Fl">-h</b> option disables this
|
||||
behavior.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#i"><b class="Fl" title="Fl" id="i">-i</b></a>
|
||||
<var class="Ar" title="Ar">path</var></dt>
|
||||
<dd class="It-tag">Add an include path.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#L"><b class="Fl" title="Fl" id="L">-L</b></a></dt>
|
||||
<dd class="It-tag">Disable the optimization that turns loads of the form
|
||||
<b class="Sy" title="Sy">LD [$FF00+n8],A</b> into the opcode
|
||||
<b class="Sy" title="Sy">LDH [$FF00+n8],A</b> in order to have full
|
||||
control of the result in the final ROM.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#M"><b class="Fl" title="Fl" id="M">-M</b></a>
|
||||
<var class="Ar" title="Ar">dependfile</var></dt>
|
||||
<dd class="It-tag">Print <a class="Xr" title="Xr">make(1)</a> dependencies to
|
||||
<var class="Ar" title="Ar">dependfile</var>.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#o"><b class="Fl" title="Fl" id="o">-o</b></a>
|
||||
<var class="Ar" title="Ar">outfile</var></dt>
|
||||
<dd class="It-tag">Write an object file to the given filename.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#p"><b class="Fl" title="Fl" id="p">-p</b></a>
|
||||
<var class="Ar" title="Ar">pad_value</var></dt>
|
||||
<dd class="It-tag">When padding an image, pad with this value. The default is
|
||||
0x00.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#V"><b class="Fl" title="Fl" id="V">-V</b></a></dt>
|
||||
<dd class="It-tag">Print the version of the program and exit.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#v"><b class="Fl" title="Fl" id="v">-v</b></a></dt>
|
||||
<dd class="It-tag">Be verbose.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#w"><b class="Fl" title="Fl" id="w">-w</b></a></dt>
|
||||
<dd class="It-tag">Disable warning output.</dd>
|
||||
<dt><a class="permalink" href="#b"><code class="Fl" id="b">-b</code></a>
|
||||
<var class="Ar">chars</var></dt>
|
||||
<dd>Change the two characters used for binary constants. The defaults are
|
||||
01.</dd>
|
||||
<dt><a class="permalink" href="#D"><code class="Fl" id="D">-D</code></a>
|
||||
<var class="Ar">name</var>[=<var class="Ar">value</var>]</dt>
|
||||
<dd>Add string symbol to the compiled source code. This is equivalent to
|
||||
<var class="Ar">name</var> <code class="Cm">EQUS</code>
|
||||
“<var class="Ar">value</var>” in code. If a value is not
|
||||
specified, a value of 1 is given.</dd>
|
||||
<dt><a class="permalink" href="#E"><code class="Fl" id="E">-E</code></a></dt>
|
||||
<dd>Export all labels, including unreferenced and local labels.</dd>
|
||||
<dt><a class="permalink" href="#g"><code class="Fl" id="g">-g</code></a>
|
||||
<var class="Ar">chars</var></dt>
|
||||
<dd>Change the four characters used for binary constants. The defaults are
|
||||
0123.</dd>
|
||||
<dt><a class="permalink" href="#h"><code class="Fl" id="h">-h</code></a></dt>
|
||||
<dd>By default, <code class="Nm">rgbasm</code> inserts a ‘nop’
|
||||
instruction immediately after any ‘halt’ instruction. The
|
||||
<code class="Fl">-h</code> option disables this behavior.</dd>
|
||||
<dt><a class="permalink" href="#i"><code class="Fl" id="i">-i</code></a>
|
||||
<var class="Ar">path</var></dt>
|
||||
<dd>Add an include path.</dd>
|
||||
<dt><a class="permalink" href="#L"><code class="Fl" id="L">-L</code></a></dt>
|
||||
<dd>Disable the optimization that turns loads of the form <b class="Sy">LD
|
||||
[$FF00+n8],A</b> into the opcode <b class="Sy">LDH [$FF00+n8],A</b> in
|
||||
order to have full control of the result in the final ROM.</dd>
|
||||
<dt><a class="permalink" href="#M"><code class="Fl" id="M">-M</code></a>
|
||||
<var class="Ar">dependfile</var></dt>
|
||||
<dd>Print <a class="Xr">make(1)</a> dependencies to
|
||||
<var class="Ar">dependfile</var>.</dd>
|
||||
<dt><a class="permalink" href="#o"><code class="Fl" id="o">-o</code></a>
|
||||
<var class="Ar">outfile</var></dt>
|
||||
<dd>Write an object file to the given filename.</dd>
|
||||
<dt><a class="permalink" href="#p"><code class="Fl" id="p">-p</code></a>
|
||||
<var class="Ar">pad_value</var></dt>
|
||||
<dd>When padding an image, pad with this value. The default is 0x00.</dd>
|
||||
<dt><a class="permalink" href="#r"><code class="Fl" id="r">-r</code></a>
|
||||
<var class="Ar">recursion_depth</var></dt>
|
||||
<dd>Specifies the recursion depth at which RGBASM will assume being in an
|
||||
infinite loop.</dd>
|
||||
<dt><a class="permalink" href="#V"><code class="Fl" id="V">-V</code></a></dt>
|
||||
<dd>Print the version of the program and exit.</dd>
|
||||
<dt><a class="permalink" href="#v"><code class="Fl" id="v">-v</code></a></dt>
|
||||
<dd>Be verbose.</dd>
|
||||
<dt><a class="permalink" href="#w"><code class="Fl" id="w">-w</code></a></dt>
|
||||
<dd>Disable warning output.</dd>
|
||||
</dl>
|
||||
<h1 class="Sh" title="Sh" id="EXAMPLES"><a class="selflink" href="#EXAMPLES">EXAMPLES</a></h1>
|
||||
Assembling a basic source file is simple:
|
||||
<div class="Pp"></div>
|
||||
<div class="Bd" style="margin-left: 5.00ex;">
|
||||
<pre class="Li">
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="EXAMPLES"><a class="permalink" href="#EXAMPLES">EXAMPLES</a></h1>
|
||||
You can assemble a source file in two ways. Straight forward way:
|
||||
<div class="Bd Pp Bd-indent">
|
||||
<pre>
|
||||
$ rgbasm -o bar.o foo.asm
|
||||
</pre>
|
||||
</div>
|
||||
<div class="Pp"></div>
|
||||
The resulting object file is not yet a usable ROM image — it must first
|
||||
be run through <a class="Xr" title="Xr">rgblink(1)</a> and
|
||||
<a class="Xr" title="Xr">rgbfix(1)</a>.
|
||||
<h1 class="Sh" title="Sh" id="SEE_ALSO"><a class="selflink" href="#SEE_ALSO">SEE
|
||||
<p class="Pp">Pipes way:</p>
|
||||
<div class="Bd Pp Bd-indent">
|
||||
<pre>
|
||||
$ cat foo.asm | rgbasm -o bar.o -
|
||||
$ rgbasm -o bar.o - < foo.asm
|
||||
</pre>
|
||||
</div>
|
||||
<p class="Pp">The resulting object file is not yet a usable ROM image —
|
||||
it must first be run through <a class="Xr">rgblink(1)</a> and
|
||||
<a class="Xr">rgbfix(1)</a>.</p>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
|
||||
ALSO</a></h1>
|
||||
<a class="Xr" title="Xr">rgbasm(5)</a>, <a class="Xr" title="Xr">rgbfix(1)</a>,
|
||||
<a class="Xr" title="Xr">rgblink(1)</a>,
|
||||
<a class="Xr" title="Xr">rgbds(5)</a>, <a class="Xr" title="Xr">rgbds(7)</a>,
|
||||
<a class="Xr" title="Xr">gbz80(7)</a>
|
||||
<h1 class="Sh" title="Sh" id="HISTORY"><a class="selflink" href="#HISTORY">HISTORY</a></h1>
|
||||
<b class="Nm" title="Nm">rgbasm</b> was originally written by Carsten
|
||||
Sørensen as part of the ASMotor package, and was later packaged in
|
||||
RGBDS by Justin Lloyd. It is now maintained by a number of contributors at
|
||||
<a class="Lk" title="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.</div>
|
||||
<a class="Xr">rgbasm(5)</a>, <a class="Xr">rgbfix(1)</a>,
|
||||
<a class="Xr">rgblink(1)</a>, <a class="Xr">rgbds(5)</a>,
|
||||
<a class="Xr">rgbds(7)</a>, <a class="Xr">gbz80(7)</a>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
|
||||
<code class="Nm">rgbasm</code> was originally written by Carsten Sørensen
|
||||
as part of the ASMotor package, and was later packaged in RGBDS by Justin
|
||||
Lloyd. It is now maintained by a number of contributors at
|
||||
<a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.
|
||||
</section>
|
||||
</div>
|
||||
<table class="foot">
|
||||
<tr>
|
||||
<td class="foot-date">February 24, 2018</td>
|
||||
<td class="foot-date">July 8, 2019</td>
|
||||
<td class="foot-os">RGBDS Manual</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
2246
docs/rgbasm.5.html
2246
docs/rgbasm.5.html
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- This is an automatically generated file. Do not edit.
|
||||
This file is part of RGBDS.
|
||||
|
||||
Copyright (c) 2017-2018, Antonio Nino Diaz and RGBDS contributors.
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<style>
|
||||
table.head, table.foot { width: 100%; }
|
||||
td.head-rtitle, td.foot-os { text-align: right; }
|
||||
td.head-vol { text-align: center; }
|
||||
div.Pp { margin: 1ex 0ex; }
|
||||
</style>
|
||||
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
|
||||
<title>RGBDS(5)</title>
|
||||
</head>
|
||||
@@ -20,124 +21,127 @@
|
||||
</tr>
|
||||
</table>
|
||||
<div class="manual-text">
|
||||
<h1 class="Sh" title="Sh" id="NAME"><a class="selflink" href="#NAME">NAME</a></h1>
|
||||
<b class="Nm" title="Nm">rgbds</b> — <span class="Nd" title="Nd">object
|
||||
file format documentation</span>
|
||||
<h1 class="Sh" title="Sh" id="DESCRIPTION"><a class="selflink" href="#DESCRIPTION">DESCRIPTION</a></h1>
|
||||
This is the description of the object files used by
|
||||
<a class="Xr" title="Xr">rgbasm(1)</a> and
|
||||
<a class="Xr" title="Xr">rgblink(1)</a>. Please, note that the specifications
|
||||
may change. This toolchain is in development and new features may require
|
||||
adding more information to the current format, or modifying some fields, which
|
||||
would break compatibility with older versions.
|
||||
<h1 class="Sh" title="Sh" id="FILE_STRUCTURE"><a class="selflink" href="#FILE_STRUCTURE">FILE
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
|
||||
<code class="Nm">rgbds</code> —
|
||||
<div class="Nd">object file format documentation</div>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
|
||||
This is the description of the object files used by <a class="Xr">rgbasm(1)</a>
|
||||
and <a class="Xr">rgblink(1)</a>. Please, note that the specifications may
|
||||
change. This toolchain is in development and new features may require adding
|
||||
more information to the current format, or modifying some fields, which would
|
||||
break compatibility with older versions.
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="FILE_STRUCTURE"><a class="permalink" href="#FILE_STRUCTURE">FILE
|
||||
STRUCTURE</a></h1>
|
||||
The following types are used:
|
||||
<div class="Pp"></div>
|
||||
<var class="Ar" title="Ar">LONG</var> is a 32‐bit integer stored in
|
||||
little‐endian format (Intel). <var class="Ar" title="Ar">BYTE</var> is
|
||||
an 8‐bit integer. <var class="Ar" title="Ar">STRING</var> is a
|
||||
0‐terminated string of <var class="Ar" title="Ar">BYTE</var>.
|
||||
<div class="Pp"></div>
|
||||
<div class="Bd" style="margin-left: 0.00ex;">
|
||||
<pre class="Li">
|
||||
; Header
|
||||
|
||||
BYTE ID[4] ; "RGB6"
|
||||
LONG NumberOfSymbols ; The number of symbols used in this file
|
||||
LONG NumberOfSections ; The number of sections used in this file
|
||||
|
||||
; Symbols
|
||||
|
||||
REPT NumberOfSymbols ; Number of symbols defined in this object file.
|
||||
|
||||
STRING Name ; The name of this symbol. Local symbols are stored
|
||||
; as "Scope.Symbol".
|
||||
|
||||
BYTE Type ; 0 = LOCAL symbol only used in this file.
|
||||
; 1 = IMPORT this symbol from elsewhere (unused).
|
||||
; 2 = EXPORT this symbol to other objects.
|
||||
|
||||
IF Type != 1 ; If symbol is defined in this object file.
|
||||
|
||||
STRING FileName ; File where the symbol is defined.
|
||||
|
||||
LONG LineNum ; Line number in the file where the symbol is defined.
|
||||
|
||||
LONG SectionID ; The section number (of this object file) in which
|
||||
; this symbol is defined. If it doesn't belong to any
|
||||
; specific section (like a constant), this field has
|
||||
; the value -1.
|
||||
|
||||
LONG Value ; The symbols value. It's the offset into that
|
||||
; symbol's section.
|
||||
|
||||
ENDC
|
||||
|
||||
ENDR
|
||||
|
||||
; Sections
|
||||
|
||||
REPT NumberOfSections
|
||||
STRING Name ; Name of the section
|
||||
|
||||
LONG Size ; Size in bytes of this section
|
||||
|
||||
BYTE Type ; 0 = WRAM0
|
||||
; 1 = VRAM
|
||||
; 2 = ROMX
|
||||
; 3 = ROM0
|
||||
; 4 = HRAM
|
||||
; 5 = WRAMX
|
||||
; 6 = SRAM
|
||||
; 7 = OAM
|
||||
|
||||
LONG Org ; Address to fix this section at. -1 if the linker should
|
||||
; decide (floating address).
|
||||
|
||||
LONG Bank ; Bank to load this section into. -1 if the linker should
|
||||
; decide (floating bank). This field is only valid for ROMX,
|
||||
; VRAM, WRAMX and SRAM sections.
|
||||
|
||||
LONG Align ; Alignment of this section (expressed as number of low bits
|
||||
; to leave as 0). -1 if not defined.
|
||||
|
||||
IF (Type == ROMX) || (Type == ROM0) ; Sections that can contain data.
|
||||
|
||||
BYTE Data[Size] ; Raw data of the section.
|
||||
|
||||
LONG NumberOfPatches ; Number of patches to apply.
|
||||
|
||||
; These types of sections may have patches
|
||||
|
||||
REPT NumberOfPatches
|
||||
|
||||
STRING SourceFile ; Name of the source file (for printing error
|
||||
; messages).
|
||||
|
||||
LONG Line ; The line of the source file.
|
||||
|
||||
LONG Offset ; Offset into the section where patch should
|
||||
; be applied (in bytes).
|
||||
|
||||
BYTE Type ; 0 = BYTE patch.
|
||||
; 1 = little endian WORD patch.
|
||||
; 2 = little endian LONG patch.
|
||||
; 3 = JR offset value BYTE patch.
|
||||
|
||||
LONG RPNSize ; Size of the buffer with the RPN.
|
||||
; expression.
|
||||
|
||||
BYTE RPN[RPNSize] ; RPN expression. Definition below.
|
||||
|
||||
ENDR
|
||||
|
||||
ENDC
|
||||
|
||||
<p class="Pp"><var class="Ar">LONG</var> is a 32‐bit integer stored in
|
||||
little‐endian format (Intel). <var class="Ar">BYTE</var> is an
|
||||
8‐bit integer. <var class="Ar">STRING</var> is a 0‐terminated
|
||||
string of <var class="Ar">BYTE</var>.</p>
|
||||
<div class="Bd Pp">
|
||||
<pre>
|
||||
; Header
|
||||
|
||||
BYTE ID[4] ; "RGB6"
|
||||
LONG NumberOfSymbols ; The number of symbols used in this file
|
||||
LONG NumberOfSections ; The number of sections used in this file
|
||||
|
||||
; Symbols
|
||||
|
||||
REPT NumberOfSymbols ; Number of symbols defined in this object file.
|
||||
|
||||
STRING Name ; The name of this symbol. Local symbols are stored
|
||||
; as "Scope.Symbol".
|
||||
|
||||
BYTE Type ; 0 = LOCAL symbol only used in this file.
|
||||
; 1 = IMPORT this symbol from elsewhere
|
||||
; 2 = EXPORT this symbol to other objects.
|
||||
|
||||
IF Type != 1 ; If symbol is defined in this object file.
|
||||
|
||||
STRING FileName ; File where the symbol is defined.
|
||||
|
||||
LONG LineNum ; Line number in the file where the symbol is defined.
|
||||
|
||||
LONG SectionID ; The section number (of this object file) in which
|
||||
; this symbol is defined. If it doesn't belong to any
|
||||
; specific section (like a constant), this field has
|
||||
; the value -1.
|
||||
|
||||
LONG Value ; The symbols value. It's the offset into that
|
||||
; symbol's section.
|
||||
|
||||
ENDC
|
||||
|
||||
ENDR
|
||||
|
||||
; Sections
|
||||
|
||||
REPT NumberOfSections
|
||||
STRING Name ; Name of the section
|
||||
|
||||
LONG Size ; Size in bytes of this section
|
||||
|
||||
BYTE Type ; 0 = WRAM0
|
||||
; 1 = VRAM
|
||||
; 2 = ROMX
|
||||
; 3 = ROM0
|
||||
; 4 = HRAM
|
||||
; 5 = WRAMX
|
||||
; 6 = SRAM
|
||||
; 7 = OAM
|
||||
|
||||
LONG Org ; Address to fix this section at. -1 if the linker should
|
||||
; decide (floating address).
|
||||
|
||||
LONG Bank ; Bank to load this section into. -1 if the linker should
|
||||
; decide (floating bank). This field is only valid for ROMX,
|
||||
; VRAM, WRAMX and SRAM sections.
|
||||
|
||||
LONG Align ; Alignment of this section (expressed as number of low bits
|
||||
; to leave as 0). -1 if not defined.
|
||||
|
||||
IF (Type == ROMX) || (Type == ROM0) ; Sections that can contain data.
|
||||
|
||||
BYTE Data[Size] ; Raw data of the section.
|
||||
|
||||
LONG NumberOfPatches ; Number of patches to apply.
|
||||
|
||||
; These types of sections may have patches
|
||||
|
||||
REPT NumberOfPatches
|
||||
|
||||
STRING SourceFile ; Name of the source file (for printing error
|
||||
; messages).
|
||||
|
||||
LONG Line ; The line of the source file.
|
||||
|
||||
LONG Offset ; Offset into the section where patch should
|
||||
; be applied (in bytes).
|
||||
|
||||
BYTE Type ; 0 = BYTE patch.
|
||||
; 1 = little endian WORD patch.
|
||||
; 2 = little endian LONG patch.
|
||||
; 3 = JR offset value BYTE patch.
|
||||
|
||||
LONG RPNSize ; Size of the buffer with the RPN.
|
||||
; expression.
|
||||
|
||||
BYTE RPN[RPNSize] ; RPN expression. Definition below.
|
||||
|
||||
ENDR
|
||||
|
||||
ENDC
|
||||
|
||||
ENDR
|
||||
</pre>
|
||||
</div>
|
||||
<h2 class="Ss" title="Ss" id="RPN_DATA"><a class="selflink" href="#RPN_DATA">RPN
|
||||
<section class="Ss">
|
||||
<h2 class="Ss" id="RPN_DATA"><a class="permalink" href="#RPN_DATA">RPN
|
||||
DATA</a></h2>
|
||||
Expressions in the object file are stored as RPN. This is an expression of the
|
||||
form “2 5 +”. This will first push the value “2”
|
||||
@@ -146,159 +150,160 @@ Expressions in the object file are stored as RPN. This is an expression of the
|
||||
effectively replacing the two top arguments with their sum. In the RGB format,
|
||||
RPN expressions are stored as BYTEs with some bytes being special prefixes for
|
||||
integers and symbols.
|
||||
<table class="Bl-column" style="margin-left: 6.00ex;">
|
||||
<colgroup>
|
||||
<col style="width: 15.00ex;"/>
|
||||
<col style="min-width: 10.00ex;"/>
|
||||
</colgroup>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><b class="Sy" title="Sy">Value</b></td>
|
||||
<td class="It-column"><b class="Sy" title="Sy">Meaning</b></td>
|
||||
<table class="Bl-column Bd-indent">
|
||||
<tr>
|
||||
<td><b class="Sy">Value</b></td>
|
||||
<td><b class="Sy">Meaning</b></td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$00"><code class="Li" id="$00">$00</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#+_operator"><code class="Li" id="+_operator">+
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$00"><code class="Li" id="$00">$00</code></a></td>
|
||||
<td><a class="permalink" href="#+_operator"><code class="Li" id="+_operator">+
|
||||
operator</code></a></td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$01"><code class="Li" id="$01">$01</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#-_operator"><code class="Li" id="-_operator">-
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$01"><code class="Li" id="$01">$01</code></a></td>
|
||||
<td><a class="permalink" href="#-_operator"><code class="Li" id="-_operator">-
|
||||
operator</code></a></td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$02"><code class="Li" id="$02">$02</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#*_operator"><code class="Li" id="*_operator">*
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$02"><code class="Li" id="$02">$02</code></a></td>
|
||||
<td><a class="permalink" href="#*_operator"><code class="Li" id="*_operator">*
|
||||
operator</code></a></td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$03"><code class="Li" id="$03">$03</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#/_operator"><code class="Li" id="/_operator">/
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$03"><code class="Li" id="$03">$03</code></a></td>
|
||||
<td><a class="permalink" href="#/_operator"><code class="Li" id="/_operator">/
|
||||
operator</code></a></td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$04"><code class="Li" id="$04">$04</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#%_operator"><code class="Li" id="%_operator">%
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$04"><code class="Li" id="$04">$04</code></a></td>
|
||||
<td><a class="permalink" href="#__operator"><code class="Li" id="__operator">%
|
||||
operator</code></a></td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$05"><code class="Li" id="$05">$05</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#unary_-"><code class="Li" id="unary_-">unary
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$05"><code class="Li" id="$05">$05</code></a></td>
|
||||
<td><a class="permalink" href="#unary_-"><code class="Li" id="unary_-">unary
|
||||
-</code></a></td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$10"><code class="Li" id="$10">$10</code></a></td>
|
||||
<td class="It-column">|
|
||||
<a class="selflink" href="#operator"><code class="Li" id="operator">operator</code></a></td>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$10"><code class="Li" id="$10">$10</code></a></td>
|
||||
<td>|
|
||||
<a class="permalink" href="#operator"><code class="Li" id="operator">operator</code></a></td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$11"><code class="Li" id="$11">$11</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#&_operator"><code class="Li" id="&_operator">&
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$11"><code class="Li" id="$11">$11</code></a></td>
|
||||
<td><a class="permalink" href="#&_operator"><code class="Li" id="&_operator">&
|
||||
operator</code></a></td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$12"><code class="Li" id="$12">$12</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#^_operator"><code class="Li" id="^_operator">^
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$12"><code class="Li" id="$12">$12</code></a></td>
|
||||
<td><a class="permalink" href="#__operator_2"><code class="Li" id="__operator_2">^
|
||||
operator</code></a></td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$13"><code class="Li" id="$13">$13</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#unary_~"><code class="Li" id="unary_~">unary
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$13"><code class="Li" id="$13">$13</code></a></td>
|
||||
<td><a class="permalink" href="#unary_~"><code class="Li" id="unary_~">unary
|
||||
~</code></a></td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$21"><code class="Li" id="$21">$21</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#&&_comparison"><code class="Li" id="&&_comparison">&&
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$21"><code class="Li" id="$21">$21</code></a></td>
|
||||
<td><a class="permalink" href="#&&_comparison"><code class="Li" id="&&_comparison">&&
|
||||
comparison</code></a></td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$22"><code class="Li" id="$22">$22</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#||_comparison"><code class="Li" id="||_comparison">||
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$22"><code class="Li" id="$22">$22</code></a></td>
|
||||
<td><a class="permalink" href="#___comparison"><code class="Li" id="___comparison">||
|
||||
comparison</code></a></td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$23"><code class="Li" id="$23">$23</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#unary"><code class="Li" id="unary">unary</code></a>!</td>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$23"><code class="Li" id="$23">$23</code></a></td>
|
||||
<td><a class="permalink" href="#unary"><code class="Li" id="unary">unary</code></a>!</td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$30"><code class="Li" id="$30">$30</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#==_comparison"><code class="Li" id="==_comparison">==
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$30"><code class="Li" id="$30">$30</code></a></td>
|
||||
<td><a class="permalink" href="#==_comparison"><code class="Li" id="==_comparison">==
|
||||
comparison</code></a></td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$31"><code class="Li" id="$31">$31</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#!=_comparison"><code class="Li" id="!=_comparison">!=
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$31"><code class="Li" id="$31">$31</code></a></td>
|
||||
<td><a class="permalink" href="#!=_comparison"><code class="Li" id="!=_comparison">!=
|
||||
comparison</code></a></td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$32"><code class="Li" id="$32">$32</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#>_comparison"><code class="Li" id=">_comparison">>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$32"><code class="Li" id="$32">$32</code></a></td>
|
||||
<td><a class="permalink" href="#__comparison"><code class="Li" id="__comparison">>
|
||||
comparison</code></a></td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$33"><code class="Li" id="$33">$33</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#<_comparison"><code class="Li" id="<_comparison"><
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$33"><code class="Li" id="$33">$33</code></a></td>
|
||||
<td><a class="permalink" href="#__comparison_2"><code class="Li" id="__comparison_2"><
|
||||
comparison</code></a></td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$34"><code class="Li" id="$34">$34</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#>=_comparison"><code class="Li" id=">=_comparison">>=
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$34"><code class="Li" id="$34">$34</code></a></td>
|
||||
<td><a class="permalink" href="#_=_comparison"><code class="Li" id="_=_comparison">>=
|
||||
comparison</code></a></td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$35"><code class="Li" id="$35">$35</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#<=_comparison"><code class="Li" id="<=_comparison"><=
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$35"><code class="Li" id="$35">$35</code></a></td>
|
||||
<td><a class="permalink" href="#_=_comparison_2"><code class="Li" id="_=_comparison_2"><=
|
||||
comparison</code></a></td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$40"><code class="Li" id="$40">$40</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#<<_comparison"><code class="Li" id="<<_comparison"><<
|
||||
comparison</code></a></td>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$40"><code class="Li" id="$40">$40</code></a></td>
|
||||
<td><a class="permalink" href="#___operator"><code class="Li" id="___operator"><<
|
||||
operator</code></a></td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$41"><code class="Li" id="$41">$41</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#>>_comparison"><code class="Li" id=">>_comparison">>>
|
||||
comparison</code></a></td>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$41"><code class="Li" id="$41">$41</code></a></td>
|
||||
<td><a class="permalink" href="#___operator_2"><code class="Li" id="___operator_2">>>
|
||||
operator</code></a></td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$50"><code class="Li" id="$50">$50</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#BANK(symbol),"><code class="Li" id="BANK(symbol),">BANK(symbol),</code></a>
|
||||
a <var class="Ar" title="Ar">LONG</var> Symbol ID follows.</td>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$50"><code class="Li" id="$50">$50</code></a></td>
|
||||
<td><a class="permalink" href="#BANK(symbol),"><code class="Li" id="BANK(symbol),">BANK(symbol),</code></a>
|
||||
a <var class="Ar">LONG</var> Symbol ID follows.</td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$51"><code class="Li" id="$51">$51</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#BANK(section_name),"><code class="Li" id="BANK(section_name),">BANK(section_name),</code></a>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$51"><code class="Li" id="$51">$51</code></a></td>
|
||||
<td><a class="permalink" href="#BANK(section_name),"><code class="Li" id="BANK(section_name),">BANK(section_name),</code></a>
|
||||
a null-terminated string follows.</td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$52"><code class="Li" id="$52">$52</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#Current_BANK()"><code class="Li" id="Current_BANK()">Current
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$52"><code class="Li" id="$52">$52</code></a></td>
|
||||
<td><a class="permalink" href="#Current_BANK()"><code class="Li" id="Current_BANK()">Current
|
||||
BANK()</code></a>.</td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$60"><code class="Li" id="$60">$60</code></a></td>
|
||||
<td class="It-column"><a class="selflink" href="#HRAMCheck."><code class="Li" id="HRAMCheck.">HRAMCheck.</code></a>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$60"><code class="Li" id="$60">$60</code></a></td>
|
||||
<td><a class="permalink" href="#HRAMCheck."><code class="Li" id="HRAMCheck.">HRAMCheck.</code></a>
|
||||
Check if the value is in HRAM, AND it with 0xFF.</td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$80"><code class="Li" id="$80">$80</code></a></td>
|
||||
<td class="It-column"><var class="Ar" title="Ar">LONG</var> integer
|
||||
follows.</td>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$80"><code class="Li" id="$80">$80</code></a></td>
|
||||
<td><var class="Ar">LONG</var> integer follows.</td>
|
||||
</tr>
|
||||
<tr class="It-column">
|
||||
<td class="It-column"><a class="selflink" href="#$81"><code class="Li" id="$81">$81</code></a></td>
|
||||
<td class="It-column"><var class="Ar" title="Ar">LONG</var> Symbol ID
|
||||
follows.</td>
|
||||
<tr>
|
||||
<td><a class="permalink" href="#$81"><code class="Li" id="$81">$81</code></a></td>
|
||||
<td><var class="Ar">LONG</var> Symbol ID follows.</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h1 class="Sh" title="Sh" id="SEE_ALSO"><a class="selflink" href="#SEE_ALSO">SEE
|
||||
</section>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
|
||||
ALSO</a></h1>
|
||||
<a class="Xr" title="Xr">rgbasm(1)</a>, <a class="Xr" title="Xr">rgblink(1)</a>,
|
||||
<a class="Xr" title="Xr">rgbds(7)</a>, <a class="Xr" title="Xr">gbz80(7)</a>
|
||||
<h1 class="Sh" title="Sh" id="HISTORY"><a class="selflink" href="#HISTORY">HISTORY</a></h1>
|
||||
<b class="Nm" title="Nm">rgbds</b> was originally written by Carsten
|
||||
Sørensen as part of the ASMotor package, and was later packaged in
|
||||
RGBDS by Justin Lloyd. It is now maintained by a number of contributors at
|
||||
<a class="Lk" title="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.</div>
|
||||
<a class="Xr">rgbasm(1)</a>, <a class="Xr">rgblink(1)</a>,
|
||||
<a class="Xr">rgbds(7)</a>, <a class="Xr">gbz80(7)</a>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
|
||||
<code class="Nm">rgbds</code> was originally written by Carsten Sørensen
|
||||
as part of the ASMotor package, and was later packaged in RGBDS by Justin
|
||||
Lloyd. It is now maintained by a number of contributors at
|
||||
<a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.
|
||||
</section>
|
||||
</div>
|
||||
<table class="foot">
|
||||
<tr>
|
||||
<td class="foot-date">January 26, 2018</td>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- This is an automatically generated file. Do not edit.
|
||||
This file is part of RGBDS.
|
||||
|
||||
Copyright (c) 2010-2018, Anthony J. Bentley and RGBDS contributors.
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<style>
|
||||
table.head, table.foot { width: 100%; }
|
||||
td.head-rtitle, td.foot-os { text-align: right; }
|
||||
td.head-vol { text-align: center; }
|
||||
div.Pp { margin: 1ex 0ex; }
|
||||
</style>
|
||||
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
|
||||
<title>RGBDS(7)</title>
|
||||
</head>
|
||||
@@ -20,46 +21,52 @@
|
||||
</tr>
|
||||
</table>
|
||||
<div class="manual-text">
|
||||
<h1 class="Sh" title="Sh" id="NAME"><a class="selflink" href="#NAME">NAME</a></h1>
|
||||
<b class="Nm" title="Nm">rgbds</b> — <span class="Nd" title="Nd">Rednex
|
||||
Game Boy Development System</span>
|
||||
<h1 class="Sh" title="Sh" id="EXAMPLES"><a class="selflink" href="#EXAMPLES">EXAMPLES</a></h1>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
|
||||
<code class="Nm">rgbds</code> —
|
||||
<div class="Nd">Rednex Game Boy Development System</div>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="EXAMPLES"><a class="permalink" href="#EXAMPLES">EXAMPLES</a></h1>
|
||||
To get a working ROM image from a single assembly source file:
|
||||
<div class="Pp"></div>
|
||||
<div class="Bd" style="margin-left: 5.00ex;">
|
||||
<pre class="Li">
|
||||
$ rgbasm -o bar.o foo.asm
|
||||
$ rgblink -o baz.gb bar.o
|
||||
<div class="Bd Pp Bd-indent">
|
||||
<pre>
|
||||
$ rgbasm -o bar.o foo.asm
|
||||
$ rgblink -o baz.gb bar.o
|
||||
$ rgbfix -v -p 0 baz.gb
|
||||
</pre>
|
||||
</div>
|
||||
<h1 class="Sh" title="Sh" id="SEE_ALSO"><a class="selflink" href="#SEE_ALSO">SEE
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
|
||||
ALSO</a></h1>
|
||||
<a class="Xr" title="Xr">rgbasm(1)</a>, <a class="Xr" title="Xr">rgbfix(1)</a>,
|
||||
<a class="Xr" title="Xr">rgblink(1)</a>,
|
||||
<a class="Xr" title="Xr">rgbds(5)</a>, <a class="Xr" title="Xr">gbz80(7)</a>
|
||||
<h1 class="Sh" title="Sh" id="HISTORY"><a class="selflink" href="#HISTORY">HISTORY</a></h1>
|
||||
<a class="Xr">rgbasm(1)</a>, <a class="Xr">rgbfix(1)</a>,
|
||||
<a class="Xr">rgblink(1)</a>, <a class="Xr">rgbds(5)</a>,
|
||||
<a class="Xr">gbz80(7)</a>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
|
||||
<dl class="Bl-ohang">
|
||||
<dt class="It-ohang"></dt>
|
||||
<dd class="It-ohang">1997, Carsten Sørensen (AKA SurfSmurf) writes
|
||||
ASMotor as a general-purpose assembler/linker system for DOS/Win32.</dd>
|
||||
<dt class="It-ohang"></dt>
|
||||
<dd class="It-ohang">1999, Justin Lloyd (AKA Otaku no Zoku) adapts ASMotor to
|
||||
read and produce GBZ80 assembly/machine code, and releases this version as
|
||||
RGBDS.</dd>
|
||||
<dt class="It-ohang"></dt>
|
||||
<dd class="It-ohang">2009, Vegard Nossum adapts the code to be more UNIX-like
|
||||
and releases this version as rgbds-linux on GitHub.</dd>
|
||||
<dt class="It-ohang"></dt>
|
||||
<dd class="It-ohang">2010, Anthony J. Bentley forks that repository. The fork
|
||||
becomes the reference implementation of rgbds.</dd>
|
||||
<dt class="It-ohang"></dt>
|
||||
<dd class="It-ohang">2017, Bentley's repository is moved to a neutral name. It
|
||||
is now maintained by a number of contributors at
|
||||
<a class="Lk" title="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.</dd>
|
||||
<dt class="It-ohang"></dt>
|
||||
<dd class="It-ohang">2018, codebase relicensed under the MIT license.</dd>
|
||||
<dt></dt>
|
||||
<dd>1997, Carsten Sørensen (AKA SurfSmurf) writes ASMotor as a
|
||||
general-purpose assembler/linker system for DOS/Win32.</dd>
|
||||
<dt></dt>
|
||||
<dd>1999, Justin Lloyd (AKA Otaku no Zoku) adapts ASMotor to read and produce
|
||||
GBZ80 assembly/machine code, and releases this version as RGBDS.</dd>
|
||||
<dt></dt>
|
||||
<dd>2009, Vegard Nossum adapts the code to be more UNIX-like and releases this
|
||||
version as rgbds-linux on GitHub.</dd>
|
||||
<dt></dt>
|
||||
<dd>2010, Anthony J. Bentley forks that repository. The fork becomes the
|
||||
reference implementation of rgbds.</dd>
|
||||
<dt></dt>
|
||||
<dd>2017, Bentley's repository is moved to a neutral name. It is now
|
||||
maintained by a number of contributors at
|
||||
<a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.</dd>
|
||||
<dt></dt>
|
||||
<dd>2018, codebase relicensed under the MIT license.</dd>
|
||||
</dl>
|
||||
</section>
|
||||
</div>
|
||||
<table class="foot">
|
||||
<tr>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- This is an automatically generated file. Do not edit.
|
||||
This file is part of RGBDS.
|
||||
|
||||
Copyright (c) 2010-2017, Anthony J. Bentley and RGBDS contributors.
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<style>
|
||||
table.head, table.foot { width: 100%; }
|
||||
td.head-rtitle, td.foot-os { text-align: right; }
|
||||
td.head-vol { text-align: center; }
|
||||
div.Pp { margin: 1ex 0ex; }
|
||||
</style>
|
||||
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
|
||||
<title>RGBFIX(1)</title>
|
||||
</head>
|
||||
@@ -20,189 +21,158 @@
|
||||
</tr>
|
||||
</table>
|
||||
<div class="manual-text">
|
||||
<h1 class="Sh" title="Sh" id="NAME"><a class="selflink" href="#NAME">NAME</a></h1>
|
||||
<b class="Nm" title="Nm">rgbfix</b> — <span class="Nd" title="Nd">Game
|
||||
Boy checksum fixer</span>
|
||||
<h1 class="Sh" title="Sh" id="SYNOPSIS"><a class="selflink" href="#SYNOPSIS">SYNOPSIS</a></h1>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
|
||||
<code class="Nm">rgbfix</code> —
|
||||
<div class="Nd">Game Boy checksum fixer</div>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="SYNOPSIS"><a class="permalink" href="#SYNOPSIS">SYNOPSIS</a></h1>
|
||||
<table class="Nm">
|
||||
<tr>
|
||||
<td><b class="Nm" title="Nm">rgbfix</b></td>
|
||||
<td>[<span class="Op"><b class="Fl" title="Fl">-CcjsVv</b></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-f</b>
|
||||
<var class="Ar" title="Ar">fix_spec</var></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-i</b>
|
||||
<var class="Ar" title="Ar">game_id</var></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-k</b>
|
||||
<var class="Ar" title="Ar">licensee_str</var></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-l</b>
|
||||
<var class="Ar" title="Ar">licensee_id</var></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-m</b>
|
||||
<var class="Ar" title="Ar">mbc_type</var></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-n</b>
|
||||
<var class="Ar" title="Ar">rom_version</var></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-p</b>
|
||||
<var class="Ar" title="Ar">pad_value</var></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-r</b>
|
||||
<var class="Ar" title="Ar">ram_size</var></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-t</b>
|
||||
<var class="Ar" title="Ar">title_str</var></span>]
|
||||
<var class="Ar" title="Ar">file</var></td>
|
||||
<td><code class="Nm">rgbfix</code></td>
|
||||
<td>[<code class="Fl">-CcjsVv</code>] [<code class="Fl">-f</code>
|
||||
<var class="Ar">fix_spec</var>] [<code class="Fl">-i</code>
|
||||
<var class="Ar">game_id</var>] [<code class="Fl">-k</code>
|
||||
<var class="Ar">licensee_str</var>] [<code class="Fl">-l</code>
|
||||
<var class="Ar">licensee_id</var>] [<code class="Fl">-m</code>
|
||||
<var class="Ar">mbc_type</var>] [<code class="Fl">-n</code>
|
||||
<var class="Ar">rom_version</var>] [<code class="Fl">-p</code>
|
||||
<var class="Ar">pad_value</var>] [<code class="Fl">-r</code>
|
||||
<var class="Ar">ram_size</var>] [<code class="Fl">-t</code>
|
||||
<var class="Ar">title_str</var>] <var class="Ar">file</var></td>
|
||||
</tr>
|
||||
</table>
|
||||
<h1 class="Sh" title="Sh" id="DESCRIPTION"><a class="selflink" href="#DESCRIPTION">DESCRIPTION</a></h1>
|
||||
The <b class="Nm" title="Nm">rgbfix</b> program changes headers of Game Boy ROM
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
|
||||
The <code class="Nm">rgbfix</code> program changes headers of Game Boy ROM
|
||||
images. It also performs other filetype operations, such as truncation. The
|
||||
arguments are as follows:
|
||||
<dl class="Bl-tag">
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#C"><b class="Fl" title="Fl" id="C">-C</b></a></dt>
|
||||
<dd class="It-tag">Set the Game Boy Color–only flag:
|
||||
<i class="Ad">0x143</i> = 0xC0. If both this and the
|
||||
<b class="Fl" title="Fl">-c</b> flag are set, this takes precedence.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#c"><b class="Fl" title="Fl" id="c">-c</b></a></dt>
|
||||
<dd class="It-tag">Set the Game Boy Color–compatible flag:
|
||||
<i class="Ad">0x143</i> = 0x80. If both this and the
|
||||
<b class="Fl" title="Fl">-C</b> flag are set,
|
||||
<b class="Fl" title="Fl">-C</b> takes precedence.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#f"><b class="Fl" title="Fl" id="f">-f</b></a>
|
||||
<var class="Ar" title="Ar">fix_spec</var></dt>
|
||||
<dd class="It-tag">Fix certain header values that the Game Boy checks for
|
||||
correctness. Alternatively, intentionally trash these values by writing
|
||||
their binary inverse instead. <var class="Ar" title="Ar">fix_spec</var> is
|
||||
a string containing any combination of the following characters:
|
||||
<div class="Pp"></div>
|
||||
<dl class="Bl-tag Bl-compact" style="margin-left: 5.40ex;">
|
||||
<dt class="It-tag" style="margin-left: -5.40ex;"><a class="selflink" href="#l"><b class="Cm" title="Cm" id="l">l</b></a></dt>
|
||||
<dd class="It-tag">Fix the Nintendo logo
|
||||
(<i class="Ad">0x104</i>–<i class="Ad">0x133</i>).</dd>
|
||||
<dt class="It-tag" style="margin-left: -5.40ex;"><a class="selflink" href="#L"><b class="Cm" title="Cm" id="L">L</b></a></dt>
|
||||
<dd class="It-tag">Trash the Nintendo logo.</dd>
|
||||
<dt class="It-tag" style="margin-left: -5.40ex;"><a class="selflink" href="#h"><b class="Cm" title="Cm" id="h">h</b></a></dt>
|
||||
<dd class="It-tag">Fix the header checksum (<i class="Ad">0x14D</i>).</dd>
|
||||
<dt class="It-tag" style="margin-left: -5.40ex;"><a class="selflink" href="#H"><b class="Cm" title="Cm" id="H">H</b></a></dt>
|
||||
<dd class="It-tag">Trash the header checksum.</dd>
|
||||
<dt class="It-tag" style="margin-left: -5.40ex;"><a class="selflink" href="#g"><b class="Cm" title="Cm" id="g">g</b></a></dt>
|
||||
<dd class="It-tag">Fix the global checksum
|
||||
(<i class="Ad">0x14E</i>–<i class="Ad">0x14F</i>).</dd>
|
||||
<dt class="It-tag" style="margin-left: -5.40ex;"><a class="selflink" href="#G"><b class="Cm" title="Cm" id="G">G</b></a></dt>
|
||||
<dd class="It-tag">Trash the global checksum.</dd>
|
||||
<dt><a class="permalink" href="#C"><code class="Fl" id="C">-C</code></a></dt>
|
||||
<dd>Set the Game Boy Color–only flag: <span class="Ad">0x143</span> =
|
||||
0xC0. If both this and the <code class="Fl">-c</code> flag are set, this
|
||||
takes precedence.</dd>
|
||||
<dt><a class="permalink" href="#c"><code class="Fl" id="c">-c</code></a></dt>
|
||||
<dd>Set the Game Boy Color–compatible flag:
|
||||
<span class="Ad">0x143</span> = 0x80. If both this and the
|
||||
<code class="Fl">-C</code> flag are set, <code class="Fl">-C</code> takes
|
||||
precedence.</dd>
|
||||
<dt><a class="permalink" href="#f"><code class="Fl" id="f">-f</code></a>
|
||||
<var class="Ar">fix_spec</var></dt>
|
||||
<dd>Fix certain header values that the Game Boy checks for correctness.
|
||||
Alternatively, intentionally trash these values by writing their binary
|
||||
inverse instead. <var class="Ar">fix_spec</var> is a string containing any
|
||||
combination of the following characters:
|
||||
<p class="Pp"></p>
|
||||
<dl class="Bl-tag Bl-compact">
|
||||
<dt><a class="permalink" href="#l"><code class="Cm" id="l">l</code></a></dt>
|
||||
<dd>Fix the Nintendo logo
|
||||
(<span class="Ad">0x104</span>–<span class="Ad">0x133</span>).</dd>
|
||||
<dt><a class="permalink" href="#L"><code class="Cm" id="L">L</code></a></dt>
|
||||
<dd>Trash the Nintendo logo.</dd>
|
||||
<dt><a class="permalink" href="#h"><code class="Cm" id="h">h</code></a></dt>
|
||||
<dd>Fix the header checksum (<span class="Ad">0x14D</span>).</dd>
|
||||
<dt><a class="permalink" href="#H"><code class="Cm" id="H">H</code></a></dt>
|
||||
<dd>Trash the header checksum.</dd>
|
||||
<dt><a class="permalink" href="#g"><code class="Cm" id="g">g</code></a></dt>
|
||||
<dd>Fix the global checksum
|
||||
(<span class="Ad">0x14E</span>–<span class="Ad">0x14F</span>).</dd>
|
||||
<dt><a class="permalink" href="#G"><code class="Cm" id="G">G</code></a></dt>
|
||||
<dd>Trash the global checksum.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#i"><b class="Fl" title="Fl" id="i">-i</b></a>
|
||||
<var class="Ar" title="Ar">game_id</var></dt>
|
||||
<dd class="It-tag">Set the game ID string
|
||||
(<i class="Ad">0x13F</i>–<i class="Ad">0x142</i>) to a given string
|
||||
of exactly 4 characters. If both this and the title are set, the game ID
|
||||
will overwrite the overlapping portion of the title.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#j"><b class="Fl" title="Fl" id="j">-j</b></a></dt>
|
||||
<dd class="It-tag">Set the non-Japanese region flag: <i class="Ad">0x14A</i> =
|
||||
1.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#k"><b class="Fl" title="Fl" id="k">-k</b></a>
|
||||
<var class="Ar" title="Ar">licensee_str</var></dt>
|
||||
<dd class="It-tag">Set the new licensee string
|
||||
(<i class="Ad">0x144</i>–<i class="Ad">0x145</i>) to a given
|
||||
string, truncated to at most two characters.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#l"><b class="Fl" title="Fl" id="l">-l</b></a>
|
||||
<var class="Ar" title="Ar">licensee_id</var></dt>
|
||||
<dd class="It-tag">Set the old licensee code, <i class="Ad">0x14B</i>, to a
|
||||
given value from 0 to 0xFF. This value is deprecated and should be set to
|
||||
0x33 in all new software.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#m"><b class="Fl" title="Fl" id="m">-m</b></a>
|
||||
<var class="Ar" title="Ar">mbc_type</var></dt>
|
||||
<dd class="It-tag">Set the MBC type, <i class="Ad">0x147</i>, to a given value
|
||||
from 0 to 0xFF.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#n"><b class="Fl" title="Fl" id="n">-n</b></a>
|
||||
<var class="Ar" title="Ar">rom_version</var></dt>
|
||||
<dd class="It-tag">Set the ROM version, <i class="Ad">0x14C</i>, to a given
|
||||
value from 0 to 0xFF.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#p"><b class="Fl" title="Fl" id="p">-p</b></a>
|
||||
<var class="Ar" title="Ar">pad_value</var></dt>
|
||||
<dd class="It-tag">Pad the image to a valid size with a given pad value from 0
|
||||
to 0xFF. <b class="Nm" title="Nm">rgbfix</b> will automatically pick a
|
||||
size from 32KiB, 64KiB, 128KiB, ..., 8192KiB and give a warning
|
||||
thereafter. The cartridge size byte (<i class="Ad">0x148</i>) will be
|
||||
changed to reflect this new size.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#r"><b class="Fl" title="Fl" id="r">-r</b></a>
|
||||
<var class="Ar" title="Ar">ram_size</var></dt>
|
||||
<dd class="It-tag">Set the RAM size, <i class="Ad">0x149</i>, to a given value
|
||||
from 0 to 0xFF.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#s"><b class="Fl" title="Fl" id="s">-s</b></a></dt>
|
||||
<dd class="It-tag">Set the SGB flag: <i class="Ad">0x146</i> = 3.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#t"><b class="Fl" title="Fl" id="t">-t</b></a>
|
||||
<var class="Ar" title="Ar">title</var></dt>
|
||||
<dd class="It-tag">Set the title string
|
||||
(<i class="Ad">0x134</i>–<i class="Ad">0x143</i>) to a given
|
||||
string, truncated to at most 16 characters. It is recommended to use 15
|
||||
characters instead, to avoid clashing with the CGB flag
|
||||
(<b class="Fl" title="Fl">-c</b> or <b class="Fl" title="Fl">-C</b>). If
|
||||
both this and the game ID are set, the game ID will overwrite the
|
||||
overlapping portion of the title.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#V"><b class="Fl" title="Fl" id="V">-V</b></a></dt>
|
||||
<dd class="It-tag">Print the version of the program and exit.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#v"><b class="Fl" title="Fl" id="v">-v</b></a></dt>
|
||||
<dd class="It-tag">Equivalent to <b class="Fl" title="Fl">-f</b>
|
||||
<b class="Cm" title="Cm">lhg</b>.</dd>
|
||||
<dt><a class="permalink" href="#i"><code class="Fl" id="i">-i</code></a>
|
||||
<var class="Ar">game_id</var></dt>
|
||||
<dd>Set the game ID string
|
||||
(<span class="Ad">0x13F</span>–<span class="Ad">0x142</span>) to a
|
||||
given string of exactly 4 characters. If both this and the title are set,
|
||||
the game ID will overwrite the overlapping portion of the title.</dd>
|
||||
<dt><a class="permalink" href="#j"><code class="Fl" id="j">-j</code></a></dt>
|
||||
<dd>Set the non-Japanese region flag: <span class="Ad">0x14A</span> = 1.</dd>
|
||||
<dt><a class="permalink" href="#k"><code class="Fl" id="k">-k</code></a>
|
||||
<var class="Ar">licensee_str</var></dt>
|
||||
<dd>Set the new licensee string
|
||||
(<span class="Ad">0x144</span>–<span class="Ad">0x145</span>) to a
|
||||
given string, truncated to at most two characters.</dd>
|
||||
<dt><a class="permalink" href="#l_2"><code class="Fl" id="l_2">-l</code></a>
|
||||
<var class="Ar">licensee_id</var></dt>
|
||||
<dd>Set the old licensee code, <span class="Ad">0x14B</span>, to a given value
|
||||
from 0 to 0xFF. This value is deprecated and should be set to 0x33 in all
|
||||
new software.</dd>
|
||||
<dt><a class="permalink" href="#m"><code class="Fl" id="m">-m</code></a>
|
||||
<var class="Ar">mbc_type</var></dt>
|
||||
<dd>Set the MBC type, <span class="Ad">0x147</span>, to a given value from 0
|
||||
to 0xFF.</dd>
|
||||
<dt><a class="permalink" href="#n"><code class="Fl" id="n">-n</code></a>
|
||||
<var class="Ar">rom_version</var></dt>
|
||||
<dd>Set the ROM version, <span class="Ad">0x14C</span>, to a given value from
|
||||
0 to 0xFF.</dd>
|
||||
<dt><a class="permalink" href="#p"><code class="Fl" id="p">-p</code></a>
|
||||
<var class="Ar">pad_value</var></dt>
|
||||
<dd>Pad the image to a valid size with a given pad value from 0 to 0xFF.
|
||||
<code class="Nm">rgbfix</code> will automatically pick a size from 32KiB,
|
||||
64KiB, 128KiB, ..., 8192KiB and give a warning thereafter. The cartridge
|
||||
size byte (<span class="Ad">0x148</span>) will be changed to reflect this
|
||||
new size.</dd>
|
||||
<dt><a class="permalink" href="#r"><code class="Fl" id="r">-r</code></a>
|
||||
<var class="Ar">ram_size</var></dt>
|
||||
<dd>Set the RAM size, <span class="Ad">0x149</span>, to a given value from 0
|
||||
to 0xFF.</dd>
|
||||
<dt><a class="permalink" href="#s"><code class="Fl" id="s">-s</code></a></dt>
|
||||
<dd>Set the SGB flag: <span class="Ad">0x146</span> = 3.</dd>
|
||||
<dt><a class="permalink" href="#t"><code class="Fl" id="t">-t</code></a>
|
||||
<var class="Ar">title</var></dt>
|
||||
<dd>Set the title string
|
||||
(<span class="Ad">0x134</span>–<span class="Ad">0x143</span>) to a
|
||||
given string, truncated to at most 16 characters. It is recommended to use
|
||||
15 characters instead, to avoid clashing with the CGB flag
|
||||
(<code class="Fl">-c</code> or <code class="Fl">-C</code>). If both this
|
||||
and the game ID are set, the game ID will overwrite the overlapping
|
||||
portion of the title.</dd>
|
||||
<dt><a class="permalink" href="#V"><code class="Fl" id="V">-V</code></a></dt>
|
||||
<dd>Print the version of the program and exit.</dd>
|
||||
<dt><a class="permalink" href="#v"><code class="Fl" id="v">-v</code></a></dt>
|
||||
<dd>Equivalent to <code class="Fl">-f</code> <code class="Cm">lhg</code>.</dd>
|
||||
</dl>
|
||||
<h1 class="Sh" title="Sh" id="EXAMPLES"><a class="selflink" href="#EXAMPLES">EXAMPLES</a></h1>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="EXAMPLES"><a class="permalink" href="#EXAMPLES">EXAMPLES</a></h1>
|
||||
Most values in the ROM header are only cosmetic. The bare minimum requirements
|
||||
for a workable image are checksums, the Nintendo logo, and (if needed) the
|
||||
CGB/SGB flags. It is a good idea to pad the image to a valid size as well
|
||||
(“valid” meaning a multiple of 32KiB).
|
||||
<div class="Pp"></div>
|
||||
The following will make a plain, no-color Game Boy game without checking for a
|
||||
valid size:
|
||||
<div class="Pp"></div>
|
||||
<div class="D1">$ rgbfix -v foo.gb</div>
|
||||
<div class="Pp"></div>
|
||||
The following will make a SGB-enabled, color-enabled game with a title of
|
||||
“foobar”, and pad it to a multiple of 32KiB. (The Game Boy
|
||||
itself does not use the title, but some emulators or ROM managers might.)
|
||||
<div class="Pp"></div>
|
||||
<div class="D1">$ rgbfix -vcs -l 0x33 -p 0 -t foobar baz.gb</div>
|
||||
<div class="Pp"></div>
|
||||
The following will duplicate the header (sans global checksum) of the game
|
||||
“Survival Kids”:
|
||||
<div class="Pp"></div>
|
||||
<div class="D1">$ rgbfix -cjsv -k A4 -l 0x33 -m 0x1B -p 0xFF -r 3 -t
|
||||
<p class="Pp">The following will make a plain, no-color Game Boy game without
|
||||
checking for a valid size:</p>
|
||||
<p class="Pp"></p>
|
||||
<div class="Bd Bd-indent">$ rgbfix -v foo.gb</div>
|
||||
<p class="Pp">The following will make a SGB-enabled, color-enabled game with a
|
||||
title of “foobar”, and pad it to a multiple of 32KiB. (The
|
||||
Game Boy itself does not use the title, but some emulators or ROM managers
|
||||
might.)</p>
|
||||
<p class="Pp"></p>
|
||||
<div class="Bd Bd-indent">$ rgbfix -vcs -l 0x33 -p 0 -t foobar baz.gb</div>
|
||||
<p class="Pp">The following will duplicate the header (sans global checksum) of
|
||||
the game “Survival Kids”:</p>
|
||||
<p class="Pp"></p>
|
||||
<div class="Bd Bd-indent">$ rgbfix -cjsv -k A4 -l 0x33 -m 0x1B -p 0xFF -r 3 -t
|
||||
SURVIVALKIDAVKE SurvivalKids.gbc</div>
|
||||
<h1 class="Sh" title="Sh" id="SEE_ALSO"><a class="selflink" href="#SEE_ALSO">SEE
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
|
||||
ALSO</a></h1>
|
||||
<a class="Xr" title="Xr">rgbasm(1)</a>, <a class="Xr" title="Xr">rgblink(1)</a>,
|
||||
<a class="Xr" title="Xr">rgbds(7)</a>
|
||||
<h1 class="Sh" title="Sh" id="HISTORY"><a class="selflink" href="#HISTORY">HISTORY</a></h1>
|
||||
<b class="Nm" title="Nm">rgbfix</b> was originally released by Carsten
|
||||
<a class="Xr">rgbasm(1)</a>, <a class="Xr">rgblink(1)</a>,
|
||||
<a class="Xr">rgbds(7)</a>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
|
||||
<code class="Nm">rgbfix</code> was originally released by Carsten
|
||||
Sørensen as a standalone program called gbfix, and was later packaged
|
||||
in RGBDS by Justin Lloyd. It is now maintained by a number of contributors at
|
||||
<a class="Lk" title="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.</div>
|
||||
<a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.
|
||||
</section>
|
||||
</div>
|
||||
<table class="foot">
|
||||
<tr>
|
||||
<td class="foot-date">March 11, 2018</td>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- This is an automatically generated file. Do not edit.
|
||||
This file is part of RGBDS.
|
||||
|
||||
Copyright (c) 2013-2018, stag019 and RGBDS contributors.
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<style>
|
||||
table.head, table.foot { width: 100%; }
|
||||
td.head-rtitle, td.foot-os { text-align: right; }
|
||||
td.head-vol { text-align: center; }
|
||||
div.Pp { margin: 1ex 0ex; }
|
||||
</style>
|
||||
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
|
||||
<title>RGBGFX(1)</title>
|
||||
</head>
|
||||
@@ -20,151 +21,155 @@
|
||||
</tr>
|
||||
</table>
|
||||
<div class="manual-text">
|
||||
<h1 class="Sh" title="Sh" id="NAME"><a class="selflink" href="#NAME">NAME</a></h1>
|
||||
<b class="Nm" title="Nm">rgbgfx</b> — <span class="Nd" title="Nd">Game
|
||||
Boy graphics converter</span>
|
||||
<h1 class="Sh" title="Sh" id="SYNOPSIS"><a class="selflink" href="#SYNOPSIS">SYNOPSIS</a></h1>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
|
||||
<code class="Nm">rgbgfx</code> —
|
||||
<div class="Nd">Game Boy graphics converter</div>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="SYNOPSIS"><a class="permalink" href="#SYNOPSIS">SYNOPSIS</a></h1>
|
||||
<table class="Nm">
|
||||
<tr>
|
||||
<td><b class="Nm" title="Nm">rgbgfx</b></td>
|
||||
<td>[<span class="Op"><b class="Fl" title="Fl">-DfFhPTVv</b></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-o</b>
|
||||
<var class="Ar" title="Ar">outfile</var></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-d</b>
|
||||
<var class="Ar" title="Ar">depth</var></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-p</b>
|
||||
<var class="Ar" title="Ar">palfile</var></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-t</b>
|
||||
<var class="Ar" title="Ar">mapfile</var></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-x</b>
|
||||
<var class="Ar" title="Ar">tiles</var></span>]
|
||||
<var class="Ar" title="Ar">file</var></td>
|
||||
<td><code class="Nm">rgbgfx</code></td>
|
||||
<td>[<code class="Fl">-ADfFhmPTuVv</code>] [<code class="Fl">-o</code>
|
||||
<var class="Ar">outfile</var>] [<code class="Fl">-a</code>
|
||||
<var class="Ar">attrmap</var>] [<code class="Fl">-d</code>
|
||||
<var class="Ar">depth</var>] [<code class="Fl">-p</code>
|
||||
<var class="Ar">palfile</var>] [<code class="Fl">-t</code>
|
||||
<var class="Ar">tilemap</var>] [<code class="Fl">-x</code>
|
||||
<var class="Ar">tiles</var>] <var class="Ar">file</var></td>
|
||||
</tr>
|
||||
</table>
|
||||
<h1 class="Sh" title="Sh" id="DESCRIPTION"><a class="selflink" href="#DESCRIPTION">DESCRIPTION</a></h1>
|
||||
The <b class="Nm" title="Nm">rgbgfx</b> program converts PNG images into the
|
||||
Nintendo Game Boy's planar tile format.
|
||||
<div style="height: 1.00em;"> </div>
|
||||
The resulting colors and their palette indices are determined differently
|
||||
depending on the input PNG file:
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
|
||||
The <code class="Nm">rgbgfx</code> program converts PNG images into the Nintendo
|
||||
Game Boy's planar tile format.
|
||||
<p class="Pp">The resulting colors and their palette indices are determined
|
||||
differently depending on the input PNG file:</p>
|
||||
<ul class="Bl-dash">
|
||||
<li class="It-dash">If the file has an embedded palette, that palette's color
|
||||
and order are used.</li>
|
||||
<li class="It-dash">If not, and the image only contains shades of gray, rgbgfx
|
||||
maps them to the indices appropriate for each shade. Any undetermined
|
||||
indices are set to respective default shades of gray. For example: if the
|
||||
bit depth is 2 and the image contains light gray and black, they become
|
||||
the second and fourth colors - and the first and third colors get set to
|
||||
default white and dark gray. If the image has multiple shades that map to
|
||||
the same index, the palette is instead determined as if the image had
|
||||
color.</li>
|
||||
<li class="It-dash">If the image has color (or the grayscale method failed),
|
||||
the colors are sorted from lightest to darkest.</li>
|
||||
<li>If the file has an embedded palette, that palette's color and order are
|
||||
used.</li>
|
||||
<li>If not, and the image only contains shades of gray, rgbgfx maps them to
|
||||
the indices appropriate for each shade. Any undetermined indices are set
|
||||
to respective default shades of gray. For example: if the bit depth is 2
|
||||
and the image contains light gray and black, they become the second and
|
||||
fourth colors - and the first and third colors get set to default white
|
||||
and dark gray. If the image has multiple shades that map to the same
|
||||
index, the palette is instead determined as if the image had color.</li>
|
||||
<li>If the image has color (or the grayscale method failed), the colors are
|
||||
sorted from lightest to darkest.</li>
|
||||
</ul>
|
||||
<div style="height: 1.00em;"> </div>
|
||||
The input image may not contain more colors than the selected bit depth allows.
|
||||
Transparent pixels are set to palette index 0.
|
||||
<h1 class="Sh" title="Sh" id="ARGUMENTS"><a class="selflink" href="#ARGUMENTS">ARGUMENTS</a></h1>
|
||||
<p class="Pp">The input image may not contain more colors than the selected bit
|
||||
depth allows. Transparent pixels are set to palette index 0.</p>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="ARGUMENTS"><a class="permalink" href="#ARGUMENTS">ARGUMENTS</a></h1>
|
||||
<dl class="Bl-tag">
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#D"><b class="Fl" title="Fl" id="D">-D</b></a></dt>
|
||||
<dd class="It-tag">Debug features are enabled.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#f"><b class="Fl" title="Fl" id="f">-f</b></a></dt>
|
||||
<dd class="It-tag">Fix the input PNG file to be a correctly indexed
|
||||
image.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#F"><b class="Fl" title="Fl" id="F">-F</b></a></dt>
|
||||
<dd class="It-tag">Same as <b class="Fl" title="Fl">-f</b>, but additionally,
|
||||
the supplied command line parameters are saved within the PNG and will be
|
||||
loaded and automatically used next time.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#d"><b class="Fl" title="Fl" id="d">-d</b></a>
|
||||
<var class="Ar" title="Ar">depth</var></dt>
|
||||
<dd class="It-tag">The bit depth of the output image (either 1 or 2). By
|
||||
default, the bit depth is 2 (two bits per pixel).</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#h"><b class="Fl" title="Fl" id="h">-h</b></a></dt>
|
||||
<dd class="It-tag">Lay out tiles horizontally rather than vertically.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#o"><b class="Fl" title="Fl" id="o">-o</b></a>
|
||||
<var class="Ar" title="Ar">outfile</var></dt>
|
||||
<dd class="It-tag">The name of the output file.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#p"><b class="Fl" title="Fl" id="p">-p</b></a>
|
||||
<var class="Ar" title="Ar">palfile</var></dt>
|
||||
<dd class="It-tag">Output the image's palette in standard GBC palette format -
|
||||
bytes (8 bytes for two bits per pixel, 4 bytes for one bit per pixel)
|
||||
containing the RGB15 values in little-endian byte order. If the palette
|
||||
contains too few colors, the remaining entries are set to black.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#P"><b class="Fl" title="Fl" id="P">-P</b></a></dt>
|
||||
<dd class="It-tag">Same as <b class="Fl" title="Fl">-p</b>, but the palette
|
||||
file output name is made by taking the input PNG file's filename, removing
|
||||
the file extension, and appending <i class="Pa" title="Pa">.pal</i>.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#t"><b class="Fl" title="Fl" id="t">-t</b></a>
|
||||
<var class="Ar" title="Ar">mapfile</var></dt>
|
||||
<dd class="It-tag">If any tiles are the same, don't place the repeat tiles in
|
||||
the output file, and make a tilemap file.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#T"><b class="Fl" title="Fl" id="T">-T</b></a></dt>
|
||||
<dd class="It-tag">Same as <b class="Fl" title="Fl">-t</b>, but the tilemap
|
||||
file output name is made by taking the input filename, removing the file
|
||||
extension, and appending <i class="Pa" title="Pa">.tilemap</i>.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#u"><b class="Fl" title="Fl" id="u">-u</b></a></dt>
|
||||
<dd class="It-tag">Truncate repeated tiles. Useful with tilemaps.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#V"><b class="Fl" title="Fl" id="V">-V</b></a></dt>
|
||||
<dd class="It-tag">Print the version of the program and exit.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#v"><b class="Fl" title="Fl" id="v">-v</b></a></dt>
|
||||
<dd class="It-tag">Verbose. Print errors when the command line parameters and
|
||||
the parameters in the PNG file don't match.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#x"><b class="Fl" title="Fl" id="x">-x</b></a>
|
||||
<var class="Ar" title="Ar">tiles</var></dt>
|
||||
<dd class="It-tag">Trim the end of the output file by this many tiles.</dd>
|
||||
<dt><a class="permalink" href="#a"><code class="Fl" id="a">-a</code></a>
|
||||
<var class="Ar">attrmap</var></dt>
|
||||
<dd>Generate a file of tile mirroring attributes for OAM or (CGB-only)
|
||||
background tiles. For each tile in the input file, a byte is written
|
||||
representing the dimensions that the associated tile in the output file
|
||||
should be mirrored. Useful in combination with <code class="Fl">-m</code>
|
||||
to keep track the mirror direction of mirrored duplicate tiles.</dd>
|
||||
<dt><a class="permalink" href="#A"><code class="Fl" id="A">-A</code></a></dt>
|
||||
<dd>Same as <code class="Fl">-a</code>, but the attrmap file output name is
|
||||
made by taking the input filename, removing the file extension, and
|
||||
appending <span class="Pa">.attrmap</span>.</dd>
|
||||
<dt><a class="permalink" href="#C"><code class="Fl" id="C">-C</code></a></dt>
|
||||
<dd>Use the color curve of the Game Boy Color when generating palettes.</dd>
|
||||
<dt><a class="permalink" href="#D"><code class="Fl" id="D">-D</code></a></dt>
|
||||
<dd>Debug features are enabled.</dd>
|
||||
<dt><a class="permalink" href="#f"><code class="Fl" id="f">-f</code></a></dt>
|
||||
<dd>Fix the input PNG file to be a correctly indexed image.</dd>
|
||||
<dt><a class="permalink" href="#F"><code class="Fl" id="F">-F</code></a></dt>
|
||||
<dd>Same as <code class="Fl">-f</code>, but additionally, the supplied command
|
||||
line parameters are saved within the PNG and will be loaded and
|
||||
automatically used next time.</dd>
|
||||
<dt><a class="permalink" href="#d"><code class="Fl" id="d">-d</code></a>
|
||||
<var class="Ar">depth</var></dt>
|
||||
<dd>The bit depth of the output image (either 1 or 2). By default, the bit
|
||||
depth is 2 (two bits per pixel).</dd>
|
||||
<dt><a class="permalink" href="#h"><code class="Fl" id="h">-h</code></a></dt>
|
||||
<dd>Lay out tiles horizontally rather than vertically.</dd>
|
||||
<dt><a class="permalink" href="#m"><code class="Fl" id="m">-m</code></a></dt>
|
||||
<dd>Truncate tiles by checking for tiles that are mirrored versions of others
|
||||
and omitting these from the output file. Useful with tilemaps and attrmaps
|
||||
together to keep track of the duplicated tiles and the dimension mirrored.
|
||||
Tiles are checked for horizontal, vertical, and horizontal-vertical
|
||||
mirroring. Implies <code class="Fl">-u</code>.</dd>
|
||||
<dt><a class="permalink" href="#o"><code class="Fl" id="o">-o</code></a>
|
||||
<var class="Ar">outfile</var></dt>
|
||||
<dd>The name of the output file.</dd>
|
||||
<dt><a class="permalink" href="#p"><code class="Fl" id="p">-p</code></a>
|
||||
<var class="Ar">palfile</var></dt>
|
||||
<dd>Output the image's palette in standard GBC palette format - bytes (8 bytes
|
||||
for two bits per pixel, 4 bytes for one bit per pixel) containing the
|
||||
RGB15 values in little-endian byte order. If the palette contains too few
|
||||
colors, the remaining entries are set to black.</dd>
|
||||
<dt><a class="permalink" href="#P"><code class="Fl" id="P">-P</code></a></dt>
|
||||
<dd>Same as <code class="Fl">-p</code>, but the palette file output name is
|
||||
made by taking the input PNG file's filename, removing the file extension,
|
||||
and appending <span class="Pa">.pal</span>.</dd>
|
||||
<dt><a class="permalink" href="#t"><code class="Fl" id="t">-t</code></a>
|
||||
<var class="Ar">tilemap</var></dt>
|
||||
<dd>Generate a file of tile indices. For each tile in the input file, a byte
|
||||
is written representing the index of the associated tile in the output
|
||||
file. Useful in combination with <code class="Fl">-u</code> or
|
||||
<code class="Fl">-m</code> to keep track of duplicate tiles.</dd>
|
||||
<dt><a class="permalink" href="#T"><code class="Fl" id="T">-T</code></a></dt>
|
||||
<dd>Same as <code class="Fl">-t</code>, but the tilemap file output name is
|
||||
made by taking the input filename, removing the file extension, and
|
||||
appending <span class="Pa">.tilemap</span>.</dd>
|
||||
<dt><a class="permalink" href="#u"><code class="Fl" id="u">-u</code></a></dt>
|
||||
<dd>Truncate tiles by checking for tiles that are exact duplicates of others
|
||||
and omitting these from the output file. Useful with tilemaps to keep
|
||||
track of the duplicated tiles.</dd>
|
||||
<dt><a class="permalink" href="#V"><code class="Fl" id="V">-V</code></a></dt>
|
||||
<dd>Print the version of the program and exit.</dd>
|
||||
<dt><a class="permalink" href="#v"><code class="Fl" id="v">-v</code></a></dt>
|
||||
<dd>Verbose. Print errors when the command line parameters and the parameters
|
||||
in the PNG file don't match.</dd>
|
||||
<dt><a class="permalink" href="#x"><code class="Fl" id="x">-x</code></a>
|
||||
<var class="Ar">tiles</var></dt>
|
||||
<dd>Trim the end of the output file by this many tiles.</dd>
|
||||
</dl>
|
||||
<h1 class="Sh" title="Sh" id="EXAMPLES"><a class="selflink" href="#EXAMPLES">EXAMPLES</a></h1>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="EXAMPLES"><a class="permalink" href="#EXAMPLES">EXAMPLES</a></h1>
|
||||
The following will take a PNG file with a bit depth of 1, 2, or 8, and output
|
||||
planar 2bpp data:
|
||||
<div class="Pp"></div>
|
||||
<div class="D1">$ rgbgfx -o out.2bpp in.png</div>
|
||||
<div class="Pp"></div>
|
||||
The following creates a planar 2bpp file with only unique tiles, and its tilemap
|
||||
<i class="Pa" title="Pa">out.tilemap</i>:
|
||||
<div class="Pp"></div>
|
||||
<div class="D1">$ rgbgfx -T -u -o out.2bpp in.png</div>
|
||||
<div class="Pp"></div>
|
||||
The following will do nothing:
|
||||
<div class="Pp"></div>
|
||||
<div class="D1">$ rgbgfx in.png</div>
|
||||
<h1 class="Sh" title="Sh" id="SEE_ALSO"><a class="selflink" href="#SEE_ALSO">SEE
|
||||
<p class="Pp"></p>
|
||||
<div class="Bd Bd-indent">$ rgbgfx -o out.2bpp in.png</div>
|
||||
<p class="Pp">The following creates a planar 2bpp file with only unique tiles,
|
||||
and its tilemap <span class="Pa">out.tilemap</span>:</p>
|
||||
<p class="Pp"></p>
|
||||
<div class="Bd Bd-indent">$ rgbgfx -T -u -o out.2bpp in.png</div>
|
||||
<p class="Pp">The following creates a planar 2bpp file with only unique tiles
|
||||
(accounting for tile mirroring) and its associated tilemap
|
||||
<span class="Pa">out.tilemap</span> and attrmap
|
||||
<span class="Pa">out.attrmap</span>:</p>
|
||||
<p class="Pp"></p>
|
||||
<div class="Bd Bd-indent">$ rgbgfx -A -T -m -o out.2bpp in.png</div>
|
||||
<p class="Pp">The following will do nothing:</p>
|
||||
<p class="Pp"></p>
|
||||
<div class="Bd Bd-indent">$ rgbgfx in.png</div>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
|
||||
ALSO</a></h1>
|
||||
<a class="Xr" title="Xr">rgbds(7)</a>, <a class="Xr" title="Xr">rgbasm(1)</a>,
|
||||
<a class="Xr" title="Xr">rgblink(1)</a>,
|
||||
<a class="Xr" title="Xr">rgbfix(1)</a>, <a class="Xr" title="Xr">gbz80(7)</a>
|
||||
<h1 class="Sh" title="Sh" id="HISTORY"><a class="selflink" href="#HISTORY">HISTORY</a></h1>
|
||||
<b class="Nm" title="Nm">rgbgfx</b> was created by
|
||||
<span class="An" title="An">stag019</span> to be included in RGBDS. It is now
|
||||
maintained by a number of contributors at
|
||||
<a class="Lk" title="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.</div>
|
||||
<a class="Xr">rgbds(7)</a>, <a class="Xr">rgbasm(1)</a>,
|
||||
<a class="Xr">rgblink(1)</a>, <a class="Xr">rgbfix(1)</a>,
|
||||
<a class="Xr">gbz80(7)</a>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
|
||||
<code class="Nm">rgbgfx</code> was created by <span class="An">stag019</span> to
|
||||
be included in RGBDS. It is now maintained by a number of contributors at
|
||||
<a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.
|
||||
</section>
|
||||
</div>
|
||||
<table class="foot">
|
||||
<tr>
|
||||
<td class="foot-date">January 26, 2018</td>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- This is an automatically generated file. Do not edit.
|
||||
This file is part of RGBDS.
|
||||
|
||||
Copyright (c) 2010-2018, Anthony J. Bentley and RGBDS contributors.
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<style>
|
||||
table.head, table.foot { width: 100%; }
|
||||
td.head-rtitle, td.foot-os { text-align: right; }
|
||||
td.head-vol { text-align: center; }
|
||||
div.Pp { margin: 1ex 0ex; }
|
||||
</style>
|
||||
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
|
||||
<title>RGBLINK(1)</title>
|
||||
</head>
|
||||
@@ -20,137 +21,113 @@
|
||||
</tr>
|
||||
</table>
|
||||
<div class="manual-text">
|
||||
<h1 class="Sh" title="Sh" id="NAME"><a class="selflink" href="#NAME">NAME</a></h1>
|
||||
<b class="Nm" title="Nm">rgblink</b> — <span class="Nd" title="Nd">Game
|
||||
Boy linker</span>
|
||||
<h1 class="Sh" title="Sh" id="SYNOPSIS"><a class="selflink" href="#SYNOPSIS">SYNOPSIS</a></h1>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
|
||||
<code class="Nm">rgblink</code> —
|
||||
<div class="Nd">Game Boy linker</div>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="SYNOPSIS"><a class="permalink" href="#SYNOPSIS">SYNOPSIS</a></h1>
|
||||
<table class="Nm">
|
||||
<tr>
|
||||
<td><b class="Nm" title="Nm">rgblink</b></td>
|
||||
<td>[<span class="Op"><b class="Fl" title="Fl">-dtVw</b></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-m</b>
|
||||
<var class="Ar" title="Ar">mapfile</var></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-n</b>
|
||||
<var class="Ar" title="Ar">symfile</var></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-O</b>
|
||||
<var class="Ar" title="Ar">overlayfile</var></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-o</b>
|
||||
<var class="Ar" title="Ar">outfile</var></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-p</b>
|
||||
<var class="Ar" title="Ar">pad_value</var></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-s</b>
|
||||
<var class="Ar" title="Ar">symbol</var></span>]
|
||||
[<span class="Op"><b class="Fl" title="Fl">-l</b>
|
||||
<var class="Ar" title="Ar">linkerscript</var></span>]
|
||||
<var class="Ar" title="Ar">file ...</var></td>
|
||||
<td><code class="Nm">rgblink</code></td>
|
||||
<td>[<code class="Fl">-dtVw</code>] [<code class="Fl">-m</code>
|
||||
<var class="Ar">mapfile</var>] [<code class="Fl">-n</code>
|
||||
<var class="Ar">symfile</var>] [<code class="Fl">-O</code>
|
||||
<var class="Ar">overlayfile</var>] [<code class="Fl">-o</code>
|
||||
<var class="Ar">outfile</var>] [<code class="Fl">-p</code>
|
||||
<var class="Ar">pad_value</var>] [<code class="Fl">-s</code>
|
||||
<var class="Ar">symbol</var>] [<code class="Fl">-l</code>
|
||||
<var class="Ar">linkerscript</var>] <var class="Ar">file ...</var></td>
|
||||
</tr>
|
||||
</table>
|
||||
<h1 class="Sh" title="Sh" id="DESCRIPTION"><a class="selflink" href="#DESCRIPTION">DESCRIPTION</a></h1>
|
||||
The <b class="Nm" title="Nm">rgblink</b> program links objects created by
|
||||
<a class="Xr" title="Xr">rgbasm(1)</a> into a single Game Boy ROM file.
|
||||
<div class="Pp"></div>
|
||||
By default, ROM0 sections created by the assembler are placed in the 16KiB bank
|
||||
0, and ROMX sections are placed in any bank except bank 0. If your ROM will
|
||||
only be 32KiB, you can use the <b class="Fl" title="Fl">-t</b> option to
|
||||
override this.
|
||||
<div class="Pp"></div>
|
||||
Similarly, WRAM0 sections are placed in the first 4KiB of WRAM bank 0 and WRAMX
|
||||
sections are placed in any bank except bank 0. If your ROM doesn't use banked
|
||||
WRAM you can use option <b class="Fl" title="Fl">-w</b> option to override
|
||||
this.
|
||||
<div class="Pp"></div>
|
||||
Also, if your ROM is designed for DMG, you can make sure that you don't use any
|
||||
prohibited section by using the option <b class="Fl" title="Fl">-d</b>, which
|
||||
implies <b class="Fl" title="Fl">-w</b> but also prohibits the use of VRAM
|
||||
bank 1.
|
||||
<div class="Pp"></div>
|
||||
The arguments are as follows:
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
|
||||
The <code class="Nm">rgblink</code> program links objects created by
|
||||
<a class="Xr">rgbasm(1)</a> into a single Game Boy ROM file.
|
||||
<p class="Pp">By default, ROM0 sections created by the assembler are placed in
|
||||
the 16KiB bank 0, and ROMX sections are placed in any bank except bank 0. If
|
||||
your ROM will only be 32KiB, you can use the <code class="Fl">-t</code>
|
||||
option to override this.</p>
|
||||
<p class="Pp">Similarly, WRAM0 sections are placed in the first 4KiB of WRAM
|
||||
bank 0 and WRAMX sections are placed in any bank except bank 0. If your ROM
|
||||
doesn't use banked WRAM you can use option <code class="Fl">-w</code> option
|
||||
to override this.</p>
|
||||
<p class="Pp">Also, if your ROM is designed for DMG, you can make sure that you
|
||||
don't use any prohibited section by using the option
|
||||
<code class="Fl">-d</code>, which implies <code class="Fl">-w</code> but
|
||||
also prohibits the use of VRAM bank 1.</p>
|
||||
<p class="Pp">The arguments are as follows:</p>
|
||||
<dl class="Bl-tag">
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#m"><b class="Fl" title="Fl" id="m">-m</b></a>
|
||||
<var class="Ar" title="Ar">mapfile</var></dt>
|
||||
<dd class="It-tag">Write a mapfile to the given filename.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#n"><b class="Fl" title="Fl" id="n">-n</b></a>
|
||||
<var class="Ar" title="Ar">symfile</var></dt>
|
||||
<dd class="It-tag">Write a symbol file to the given filename.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#O"><b class="Fl" title="Fl" id="O">-O</b></a>
|
||||
<var class="Ar" title="Ar">overlayfile</var></dt>
|
||||
<dd class="It-tag">The ROM image to overlay sections over. When an overlay ROM
|
||||
is provided, all sections must be fixed. This may be used to patch an
|
||||
existing binary.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#o"><b class="Fl" title="Fl" id="o">-o</b></a>
|
||||
<var class="Ar" title="Ar">outfile</var></dt>
|
||||
<dd class="It-tag">Write ROM image to the given filename.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#p"><b class="Fl" title="Fl" id="p">-p</b></a>
|
||||
<var class="Ar" title="Ar">pad_value</var></dt>
|
||||
<dd class="It-tag">When padding an image, pad with this value. The default is
|
||||
0x00.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#s"><b class="Fl" title="Fl" id="s">-s</b></a>
|
||||
<var class="Ar" title="Ar">symbol</var></dt>
|
||||
<dd class="It-tag">???</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#w"><b class="Fl" title="Fl" id="w">-w</b></a></dt>
|
||||
<dd class="It-tag">Expand the WRAM0 section size from 4KiB to the full 8KiB
|
||||
assigned to WRAM and prohibit the use of WRAMX sections.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#d"><b class="Fl" title="Fl" id="d">-d</b></a></dt>
|
||||
<dd class="It-tag">Enable DMG mode. Prohibit the use of sections that doesn't
|
||||
exist on a DMG, such as WRAMX and VRAM bank 1. This option automatically
|
||||
enables <b class="Fl" title="Fl">-w</b>.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#t"><b class="Fl" title="Fl" id="t">-t</b></a></dt>
|
||||
<dd class="It-tag">Expand the ROM0 section size from 16KiB to the full 32KiB
|
||||
assigned to ROM and prohibit the use of ROMX sections. Useful for ROMs
|
||||
that fit in 32 KiB.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#l"><b class="Fl" title="Fl" id="l">-l</b></a>
|
||||
<var class="Ar" title="Ar">linkerscript</var></dt>
|
||||
<dd class="It-tag">Specify a linkerscript file that tells the linker how
|
||||
sections must be placed in the ROM. This file has priority over the
|
||||
attributes assigned in the source code, but they have to be consistent.
|
||||
See <a class="Xr" title="Xr">rgblink(5)</a> for more information about its
|
||||
format.</dd>
|
||||
<dt class="It-tag"> </dt>
|
||||
<dd class="It-tag"> </dd>
|
||||
<dt class="It-tag"><a class="selflink" href="#V"><b class="Fl" title="Fl" id="V">-V</b></a></dt>
|
||||
<dd class="It-tag">Print the version of the program and exit.</dd>
|
||||
<dt><a class="permalink" href="#m"><code class="Fl" id="m">-m</code></a>
|
||||
<var class="Ar">mapfile</var></dt>
|
||||
<dd>Write a mapfile to the given filename.</dd>
|
||||
<dt><a class="permalink" href="#n"><code class="Fl" id="n">-n</code></a>
|
||||
<var class="Ar">symfile</var></dt>
|
||||
<dd>Write a symbol file to the given filename.</dd>
|
||||
<dt><a class="permalink" href="#O"><code class="Fl" id="O">-O</code></a>
|
||||
<var class="Ar">overlayfile</var></dt>
|
||||
<dd>The ROM image to overlay sections over. When an overlay ROM is provided,
|
||||
all sections must be fixed. This may be used to patch an existing
|
||||
binary.</dd>
|
||||
<dt><a class="permalink" href="#o"><code class="Fl" id="o">-o</code></a>
|
||||
<var class="Ar">outfile</var></dt>
|
||||
<dd>Write ROM image to the given filename.</dd>
|
||||
<dt><a class="permalink" href="#p"><code class="Fl" id="p">-p</code></a>
|
||||
<var class="Ar">pad_value</var></dt>
|
||||
<dd>When padding an image, pad with this value. The default is 0x00.</dd>
|
||||
<dt><a class="permalink" href="#s"><code class="Fl" id="s">-s</code></a>
|
||||
<var class="Ar">symbol</var></dt>
|
||||
<dd>???</dd>
|
||||
<dt><a class="permalink" href="#w"><code class="Fl" id="w">-w</code></a></dt>
|
||||
<dd>Expand the WRAM0 section size from 4KiB to the full 8KiB assigned to WRAM
|
||||
and prohibit the use of WRAMX sections.</dd>
|
||||
<dt><a class="permalink" href="#d"><code class="Fl" id="d">-d</code></a></dt>
|
||||
<dd>Enable DMG mode. Prohibit the use of sections that doesn't exist on a DMG,
|
||||
such as WRAMX and VRAM bank 1. This option automatically enables
|
||||
<code class="Fl">-w</code>.</dd>
|
||||
<dt><a class="permalink" href="#t"><code class="Fl" id="t">-t</code></a></dt>
|
||||
<dd>Expand the ROM0 section size from 16KiB to the full 32KiB assigned to ROM
|
||||
and prohibit the use of ROMX sections. Useful for ROMs that fit in 32
|
||||
KiB.</dd>
|
||||
<dt><a class="permalink" href="#l"><code class="Fl" id="l">-l</code></a>
|
||||
<var class="Ar">linkerscript</var></dt>
|
||||
<dd>Specify a linkerscript file that tells the linker how sections must be
|
||||
placed in the ROM. This file has priority over the attributes assigned in
|
||||
the source code, but they have to be consistent. See
|
||||
<a class="Xr">rgblink(5)</a> for more information about its format.</dd>
|
||||
<dt><a class="permalink" href="#V"><code class="Fl" id="V">-V</code></a></dt>
|
||||
<dd>Print the version of the program and exit.</dd>
|
||||
</dl>
|
||||
<h1 class="Sh" title="Sh" id="EXAMPLES"><a class="selflink" href="#EXAMPLES">EXAMPLES</a></h1>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="EXAMPLES"><a class="permalink" href="#EXAMPLES">EXAMPLES</a></h1>
|
||||
All you need for a basic ROM is an object file, which can be made into a ROM
|
||||
image like so:
|
||||
<div class="Pp"></div>
|
||||
<div class="D1">$ rgblink -o bar.gb foo.o</div>
|
||||
<div class="Pp"></div>
|
||||
The resulting bar.gb will not have correct checksums (unless you put them in the
|
||||
assembly source). You should use <a class="Xr" title="Xr">rgbfix(1)</a> to fix
|
||||
these so that the program will actually run in a Game Boy:
|
||||
<div class="Pp"></div>
|
||||
<div class="D1">$ rgbfix -v bar.gb</div>
|
||||
<h1 class="Sh" title="Sh" id="SEE_ALSO"><a class="selflink" href="#SEE_ALSO">SEE
|
||||
<p class="Pp"></p>
|
||||
<div class="Bd Bd-indent">$ rgblink -o bar.gb foo.o</div>
|
||||
<p class="Pp">The resulting bar.gb will not have correct checksums (unless you
|
||||
put them in the assembly source). You should use <a class="Xr">rgbfix(1)</a>
|
||||
to fix these so that the program will actually run in a Game Boy:</p>
|
||||
<p class="Pp"></p>
|
||||
<div class="Bd Bd-indent">$ rgbfix -v bar.gb</div>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
|
||||
ALSO</a></h1>
|
||||
<a class="Xr" title="Xr">rgbasm(1)</a>, <a class="Xr" title="Xr">rgblink(5)</a>,
|
||||
<a class="Xr" title="Xr">rgbfix(1)</a>, <a class="Xr" title="Xr">rgbds(5)</a>,
|
||||
<a class="Xr" title="Xr">rgbds(7)</a>
|
||||
<h1 class="Sh" title="Sh" id="HISTORY"><a class="selflink" href="#HISTORY">HISTORY</a></h1>
|
||||
<b class="Nm" title="Nm">rgblink</b> was originally written by Carsten
|
||||
<a class="Xr">rgbasm(1)</a>, <a class="Xr">rgblink(5)</a>,
|
||||
<a class="Xr">rgbfix(1)</a>, <a class="Xr">rgbds(5)</a>,
|
||||
<a class="Xr">rgbds(7)</a>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
|
||||
<code class="Nm">rgblink</code> was originally written by Carsten
|
||||
Sørensen as part of the ASMotor package, and was later packaged in
|
||||
RGBDS by Justin Lloyd. It is now maintained by a number of contributors at
|
||||
<a class="Lk" title="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.</div>
|
||||
<a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.
|
||||
</section>
|
||||
</div>
|
||||
<table class="foot">
|
||||
<tr>
|
||||
<td class="foot-date">January 26, 2018</td>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- This is an automatically generated file. Do not edit.
|
||||
This file is part of RGBDS.
|
||||
|
||||
Copyright (c) 2017-2018, Antonio Nino Diaz and RGBDS contributors.
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<style>
|
||||
table.head, table.foot { width: 100%; }
|
||||
td.head-rtitle, td.foot-os { text-align: right; }
|
||||
td.head-vol { text-align: center; }
|
||||
div.Pp { margin: 1ex 0ex; }
|
||||
</style>
|
||||
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
|
||||
<title>RGBLINK(5)</title>
|
||||
</head>
|
||||
@@ -20,81 +21,80 @@
|
||||
</tr>
|
||||
</table>
|
||||
<div class="manual-text">
|
||||
<h1 class="Sh" title="Sh" id="NAME"><a class="selflink" href="#NAME">NAME</a></h1>
|
||||
<b class="Nm" title="Nm">rgblink</b> —
|
||||
<span class="Nd" title="Nd">linkerscript file format</span>
|
||||
<h1 class="Sh" title="Sh" id="DESCRIPTION"><a class="selflink" href="#DESCRIPTION">DESCRIPTION</a></h1>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
|
||||
<code class="Nm">rgblink</code> —
|
||||
<div class="Nd">linkerscript file format</div>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
|
||||
The linkerscript is an external file that allows the user to specify the order
|
||||
of sections without the need for doing so before assembling each object file.
|
||||
<div class="Pp"></div>
|
||||
The placement of sections specified in the linkerscript is done before the
|
||||
sections whose placement is defined in the source code.
|
||||
<div class="Pp"></div>
|
||||
A linkerscript consists on a series of banks followed by a list of sections and,
|
||||
optionally, commands. They can be lowercase or uppercase, it is ignored. Any
|
||||
line can contain a comment starting with
|
||||
‘<code class="Li">;</code>’ that ends at the end of the line:
|
||||
<div class="Pp"></div>
|
||||
<div class="Bd" style="margin-left: 5.00ex;">
|
||||
<pre class="Li">
|
||||
ROMX $F ; This is a comment
|
||||
"Functions to read array"
|
||||
ALIGN 8
|
||||
"Array aligned to 256 bytes"
|
||||
|
||||
WRAMX 2
|
||||
<p class="Pp">The placement of sections specified in the linkerscript is done
|
||||
before the sections whose placement is defined in the source code.</p>
|
||||
<p class="Pp">A linkerscript consists on a series of banks followed by a list of
|
||||
sections and, optionally, commands. They can be lowercase or uppercase, it
|
||||
is ignored. Any line can contain a comment starting with
|
||||
‘<code class="Li">;</code>’ that ends at the end of the
|
||||
line:</p>
|
||||
<div class="Bd Pp Bd-indent">
|
||||
<pre>
|
||||
ROMX $F ; This is a comment
|
||||
"Functions to read array"
|
||||
ALIGN 8
|
||||
"Array aligned to 256 bytes"
|
||||
|
||||
WRAMX 2
|
||||
"Some variables"
|
||||
</pre>
|
||||
</div>
|
||||
<div class="Pp"></div>
|
||||
Numbers can be in decimal or hexadecimal format (the prefix is
|
||||
‘<code class="Li">$</code>’). It is an error if any section name
|
||||
or command are found before setting a bank.
|
||||
<div class="Pp"></div>
|
||||
Files can be included by using the <var class="Ar" title="Ar">INCLUDE</var>
|
||||
keyword followed by a string with the path of the file that has to be
|
||||
included.
|
||||
<div class="Pp"></div>
|
||||
The possible bank types are: <b class="Sy" title="Sy">ROM0</b>,
|
||||
<b class="Sy" title="Sy">ROMX</b>, <b class="Sy" title="Sy">VRAM</b>,
|
||||
<b class="Sy" title="Sy">WRAM0</b>, <b class="Sy" title="Sy">WRAMX</b>,
|
||||
<b class="Sy" title="Sy">OAM</b> and <b class="Sy" title="Sy">HRAM</b>. Types
|
||||
<b class="Sy" title="Sy">ROMX</b>, <b class="Sy" title="Sy">VRAM</b>,
|
||||
<b class="Sy" title="Sy">WRAMX</b> and <b class="Sy" title="Sy">SRAM</b> are
|
||||
banked, which means that it is needed to specify a bank after the type.
|
||||
<div class="Pp"></div>
|
||||
When a new bank statement is found, sections found after it will be placed right
|
||||
from the beginning of that bank. If the linkerscript switches to a different
|
||||
bank and then it comes back to the previous one it will continue from the last
|
||||
address that was used.
|
||||
<div class="Pp"></div>
|
||||
The only two commands are <var class="Ar" title="Ar">ORG</var> and
|
||||
<var class="Ar" title="Ar">ALIGN</var>:
|
||||
<p class="Pp">Numbers can be in decimal or hexadecimal format (the prefix is
|
||||
‘<code class="Li">$</code>’). It is an error if any section
|
||||
name or command are found before setting a bank.</p>
|
||||
<p class="Pp">Files can be included by using the <var class="Ar">INCLUDE</var>
|
||||
keyword followed by a string with the path of the file that has to be
|
||||
included.</p>
|
||||
<p class="Pp">The possible bank types are: <b class="Sy">ROM0</b>,
|
||||
<b class="Sy">ROMX</b>, <b class="Sy">VRAM</b>, <b class="Sy">WRAM0</b>,
|
||||
<b class="Sy">WRAMX</b>, <b class="Sy">OAM</b> and <b class="Sy">HRAM</b>.
|
||||
Types <b class="Sy">ROMX</b>, <b class="Sy">VRAM</b>,
|
||||
<b class="Sy">WRAMX</b> and <b class="Sy">SRAM</b> are banked, which means
|
||||
that it is needed to specify a bank after the type.</p>
|
||||
<p class="Pp">When a new bank statement is found, sections found after it will
|
||||
be placed right from the beginning of that bank. If the linkerscript
|
||||
switches to a different bank and then it comes back to the previous one it
|
||||
will continue from the last address that was used.</p>
|
||||
<p class="Pp">The only two commands are <var class="Ar">ORG</var> and
|
||||
<var class="Ar">ALIGN</var>:</p>
|
||||
<ul class="Bl-bullet">
|
||||
<li class="It-bullet"><var class="Ar" title="Ar">ORG</var> sets the address in
|
||||
which new sections will be placed. It can not be lower than the current
|
||||
address.</li>
|
||||
<li class="It-bullet"><var class="Ar" title="Ar">ALIGN</var> will increase the
|
||||
address until it is aligned to the specified boundary (it tries to set to
|
||||
0 the number of bits specified after the command:
|
||||
<b class="Sy" title="Sy">ALIGN 8</b> will align to $100).</li>
|
||||
<li><var class="Ar">ORG</var> sets the address in which new sections will be
|
||||
placed. It can not be lower than the current address.</li>
|
||||
<li><var class="Ar">ALIGN</var> will increase the address until it is aligned
|
||||
to the specified boundary (it tries to set to 0 the number of bits
|
||||
specified after the command: <b class="Sy">ALIGN 8</b> will align to
|
||||
$100).</li>
|
||||
</ul>
|
||||
<div class="Pp"></div>
|
||||
Note: The bank, alignment, address and type of sections can be specified both in
|
||||
the source code and in the linkerscript. For a section to be able to be placed
|
||||
with the linkerscript the bank must be left unassigned in the source code or
|
||||
be the same as the one specified in the linkerscript. The address and
|
||||
alignment musn't be set.
|
||||
<h1 class="Sh" title="Sh" id="SEE_ALSO"><a class="selflink" href="#SEE_ALSO">SEE
|
||||
<p class="Pp">Note: The bank, alignment, address and type of sections can be
|
||||
specified both in the source code and in the linkerscript. For a section to
|
||||
be able to be placed with the linkerscript the bank must be left unassigned
|
||||
in the source code or be the same as the one specified in the linkerscript.
|
||||
The address and alignment musn't be set.</p>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
|
||||
ALSO</a></h1>
|
||||
<a class="Xr" title="Xr">rgbasm(1)</a>, <a class="Xr" title="Xr">rgblink(1)</a>,
|
||||
<a class="Xr" title="Xr">rgbfix(1)</a>, <a class="Xr" title="Xr">rgbds(5)</a>,
|
||||
<a class="Xr" title="Xr">rgbds(7)</a>
|
||||
<h1 class="Sh" title="Sh" id="HISTORY"><a class="selflink" href="#HISTORY">HISTORY</a></h1>
|
||||
<b class="Nm" title="Nm">rgblink</b> was originally written by Carsten
|
||||
<a class="Xr">rgbasm(1)</a>, <a class="Xr">rgblink(1)</a>,
|
||||
<a class="Xr">rgbfix(1)</a>, <a class="Xr">rgbds(5)</a>,
|
||||
<a class="Xr">rgbds(7)</a>
|
||||
</section>
|
||||
<section class="Sh">
|
||||
<h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
|
||||
<code class="Nm">rgblink</code> was originally written by Carsten
|
||||
Sørensen as part of the ASMotor package, and was later packaged in
|
||||
RGBDS by Justin Lloyd. It is now maintained by a number of contributors at
|
||||
<a class="Lk" title="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.</div>
|
||||
<a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.
|
||||
</section>
|
||||
</div>
|
||||
<table class="foot">
|
||||
<tr>
|
||||
<td class="foot-date">January 27, 2018</td>
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
extern int32_t nLineNo;
|
||||
extern uint32_t nTotalLines;
|
||||
extern uint32_t nPC;
|
||||
extern uint32_t nPass;
|
||||
extern uint32_t nIFDepth;
|
||||
extern bool skipElif;
|
||||
extern uint32_t nUnionDepth;
|
||||
@@ -40,6 +39,7 @@ extern struct sSymbol *tHashedSymbols[HASHSIZE];
|
||||
extern struct sSymbol *pPCSymbol;
|
||||
extern bool oDontExpandStrings;
|
||||
|
||||
size_t symvaluetostring(char *dest, size_t maxLength, char *sym);
|
||||
size_t symvaluetostring(char *dest, size_t maxLength, char *sym,
|
||||
const char *mode);
|
||||
|
||||
#endif /* RGBDS_ASM_ASM_H */
|
||||
|
||||
@@ -13,14 +13,30 @@
|
||||
|
||||
#define MAXCHARMAPS 512
|
||||
#define CHARMAPLENGTH 16
|
||||
#define MAXCHARNODES (MAXCHARMAPS * CHARMAPLENGTH + 1)
|
||||
|
||||
struct Charmap {
|
||||
int32_t count;
|
||||
char input[MAXCHARMAPS][CHARMAPLENGTH + 1];
|
||||
char output[MAXCHARMAPS];
|
||||
/*
|
||||
* A node for trie structure.
|
||||
*/
|
||||
struct Charnode {
|
||||
uint8_t code; /* the value in a key-value pair. */
|
||||
uint8_t isCode; /* has one if it's a code node, not just a bridge node. */
|
||||
struct Charnode *next[256]; /* each index representing the next possible character from its current state. */
|
||||
};
|
||||
|
||||
int32_t readUTF8Char(char *destination, char *source);
|
||||
struct Charmap {
|
||||
char name[MAXSYMLEN + 1];
|
||||
int32_t charCount; /* user-side count. */
|
||||
int32_t nodeCount; /* node-side count. */
|
||||
struct Charnode nodes[MAXCHARNODES]; /* first node is reserved for the root node in charmap. */
|
||||
struct Charmap *next; /* next charmap in hash table bucket */
|
||||
};
|
||||
|
||||
void charmap_InitMain(void);
|
||||
struct Charmap *charmap_New(const char *name, const char *baseName);
|
||||
void charmap_Set(const char *name);
|
||||
void charmap_Push(void);
|
||||
void charmap_Pop(void);
|
||||
int32_t charmap_Add(char *input, uint8_t output);
|
||||
int32_t charmap_Convert(char **input);
|
||||
|
||||
|
||||
@@ -33,15 +33,20 @@ struct sContext {
|
||||
char *pREPTBlock;
|
||||
uint32_t nREPTBlockCount;
|
||||
uint32_t nREPTBlockSize;
|
||||
int32_t nREPTBodyFirstLine;
|
||||
int32_t nREPTBodyLastLine;
|
||||
};
|
||||
|
||||
extern unsigned int nMaxRecursionDepth;
|
||||
|
||||
void fstk_RunInclude(char *tzFileName);
|
||||
void fstk_RunMacroArg(int32_t s);
|
||||
void fstk_Init(char *s);
|
||||
void fstk_Dump(void);
|
||||
void fstk_DumpStringExpansions(void);
|
||||
void fstk_AddIncludePath(char *s);
|
||||
uint32_t fstk_RunMacro(char *s);
|
||||
void fstk_RunRept(uint32_t count);
|
||||
void fstk_RunRept(uint32_t count, int32_t nReptLineNo);
|
||||
FILE *fstk_FindFile(char *fname, char **incPathUsed);
|
||||
int32_t fstk_GetLine(void);
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ struct yy_buffer_state {
|
||||
/* Address where the data is initially written after a safety margin */
|
||||
char *pBufferStart;
|
||||
char *pBuffer;
|
||||
uint32_t nBufferSize;
|
||||
size_t nBufferSize;
|
||||
uint32_t oAtLineStart;
|
||||
};
|
||||
|
||||
@@ -40,6 +40,13 @@ enum eLexerState {
|
||||
LEX_STATE_MACROARGS
|
||||
};
|
||||
|
||||
struct sStringExpansionPos {
|
||||
char *tzName;
|
||||
char *pBuffer;
|
||||
char *pBufferPos;
|
||||
struct sStringExpansionPos *pParent;
|
||||
};
|
||||
|
||||
#define INITIAL 0
|
||||
#define macroarg 3
|
||||
|
||||
@@ -62,14 +69,16 @@ void lex_FloatDeleteSecondRange(uint32_t id, uint16_t start, uint16_t end);
|
||||
void lex_Init(void);
|
||||
void lex_AddStrings(const struct sLexInitString *lex);
|
||||
void lex_SetBuffer(char *buffer, uint32_t len);
|
||||
void lex_BeginStringExpansion(const char *tzName);
|
||||
int yywrap(void);
|
||||
uint32_t yylex(void);
|
||||
int yylex(void);
|
||||
void yyunput(char c);
|
||||
void yyunputstr(char *s);
|
||||
void yyunputstr(const char *s);
|
||||
void yyskipbytes(uint32_t count);
|
||||
void yyunputbytes(uint32_t count);
|
||||
|
||||
extern YY_BUFFER_STATE pCurrentBuffer;
|
||||
extern struct sStringExpansionPos *pCurrentStringExpansion;
|
||||
|
||||
void upperstring(char *s);
|
||||
void lowerstring(char *s);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "helpers.h"
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ void out_AbsByteGroup(char *s, int32_t length);
|
||||
void out_RelByte(struct Expression *expr);
|
||||
void out_RelWord(struct Expression *expr);
|
||||
void out_PCRelByte(struct Expression *expr);
|
||||
void out_CheckErrors(void);
|
||||
void out_WriteObject(void);
|
||||
void out_Skip(int32_t skip);
|
||||
void out_BinaryFile(char *s);
|
||||
|
||||
@@ -11,17 +11,19 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define MAXRPNLEN 1048576
|
||||
|
||||
struct Expression {
|
||||
int32_t nVal;
|
||||
uint8_t tRPN[256];
|
||||
uint8_t *tRPN;
|
||||
uint32_t nRPNCapacity;
|
||||
uint32_t nRPNLength;
|
||||
uint32_t nRPNPatchSize;
|
||||
uint32_t nRPNOut;
|
||||
uint32_t isReloc;
|
||||
uint32_t isPCRel;
|
||||
};
|
||||
|
||||
uint32_t rpn_isReloc(const struct Expression *expr);
|
||||
uint32_t rpn_isPCRelative(const struct Expression *expr);
|
||||
void rpn_Symbol(struct Expression *expr, char *tzSym);
|
||||
void rpn_Number(struct Expression *expr, uint32_t i);
|
||||
void rpn_LOGNOT(struct Expression *expr, const struct Expression *src);
|
||||
@@ -69,7 +71,8 @@ uint16_t rpn_PopByte(struct Expression *expr);
|
||||
void rpn_BankSymbol(struct Expression *expr, char *tzSym);
|
||||
void rpn_BankSection(struct Expression *expr, char *tzSectionName);
|
||||
void rpn_BankSelf(struct Expression *expr);
|
||||
void rpn_Reset(struct Expression *expr);
|
||||
void rpn_Init(struct Expression *expr);
|
||||
void rpn_Free(struct Expression *expr);
|
||||
void rpn_CheckHRAM(struct Expression *expr, const struct Expression *src);
|
||||
|
||||
#endif /* RGBDS_ASM_RPN_H */
|
||||
|
||||
@@ -38,8 +38,8 @@ struct sSymbol {
|
||||
#define SYMF_SET 0x004
|
||||
/* Symbol should be exported */
|
||||
#define SYMF_EXPORT 0x008
|
||||
/* Symbol is imported, it's value is unknown */
|
||||
#define SYMF_IMPORT 0x010
|
||||
/* Symbol referenced in RPN expression */
|
||||
#define SYMF_REF 0x010
|
||||
/* Symbol is a local symbol */
|
||||
#define SYMF_LOCAL 0x020
|
||||
/* Symbol has been defined, not only referenced */
|
||||
@@ -51,10 +51,8 @@ struct sSymbol {
|
||||
/* Symbol has a constant value, will not be changed during linking */
|
||||
#define SYMF_CONST 0x200
|
||||
|
||||
uint32_t calchash(char *s);
|
||||
uint32_t sym_CalcHash(const char *s);
|
||||
void sym_SetExportAll(uint8_t set);
|
||||
void sym_PrepPass1(void);
|
||||
void sym_PrepPass2(void);
|
||||
void sym_AddLocalReloc(char *tzSym);
|
||||
void sym_AddReloc(char *tzSym);
|
||||
void sym_Export(char *tzSym);
|
||||
@@ -65,23 +63,21 @@ void sym_AddNewMacroArg(char *s);
|
||||
void sym_SaveCurrentMacroArgs(char *save[]);
|
||||
void sym_RestoreCurrentMacroArgs(char *save[]);
|
||||
void sym_UseNewMacroArgs(void);
|
||||
void sym_FreeCurrentMacroArgs(void);
|
||||
void sym_AddEqu(char *tzSym, int32_t value);
|
||||
void sym_AddSet(char *tzSym, int32_t value);
|
||||
void sym_Init(void);
|
||||
uint32_t sym_GetConstantValue(char *s);
|
||||
uint32_t sym_isConstant(char *s);
|
||||
struct sSymbol *sym_FindSymbol(char *tzName);
|
||||
void sym_Global(char *tzSym);
|
||||
char *sym_FindMacroArg(int32_t i);
|
||||
char *sym_GetStringValue(char *tzSym);
|
||||
void sym_UseCurrentMacroArgs(void);
|
||||
void sym_SetMacroArgID(uint32_t nMacroCount);
|
||||
uint32_t sym_isString(char *tzSym);
|
||||
void sym_AddMacro(char *tzSym);
|
||||
void sym_AddMacro(char *tzSym, int32_t nDefLineNo);
|
||||
void sym_Ref(char *tzSym);
|
||||
void sym_ShiftCurrentMacroArgs(void);
|
||||
void sym_AddString(char *tzSym, char *tzValue);
|
||||
uint32_t sym_GetValue(char *s);
|
||||
uint32_t sym_GetDefinedValue(char *s);
|
||||
uint32_t sym_isDefined(char *tzName);
|
||||
void sym_Purge(char *tzName);
|
||||
|
||||
17
include/asm/util.h
Normal file
17
include/asm/util.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* This file is part of RGBDS.
|
||||
*
|
||||
* Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef RGBDS_UTIL_H
|
||||
#define RGBDS_UTIL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t calchash(const char *s);
|
||||
int32_t readUTF8Char(char *dest, char *src);
|
||||
|
||||
#endif /* RGBDS_UTIL_H */
|
||||
@@ -12,14 +12,24 @@
|
||||
#include <stdint.h>
|
||||
#include "gfx/main.h"
|
||||
|
||||
#define XFLIP 0x40
|
||||
#define YFLIP 0x20
|
||||
|
||||
void raw_to_gb(const struct RawIndexedImage *raw_image, struct GBImage *gb);
|
||||
void output_file(const struct Options *opts, const struct GBImage *gb);
|
||||
int get_tile_index(uint8_t *tile, uint8_t **tiles, int num_tiles,
|
||||
int tile_size);
|
||||
void create_tilemap(const struct Options *opts, struct GBImage *gb,
|
||||
struct Tilemap *tilemap);
|
||||
uint8_t reverse_bits(uint8_t b);
|
||||
void xflip(uint8_t *tile, uint8_t *tile_xflip, int tile_size);
|
||||
void yflip(uint8_t *tile, uint8_t *tile_yflip, int tile_size);
|
||||
int get_mirrored_tile_index(uint8_t *tile, uint8_t **tiles, int num_tiles,
|
||||
int tile_size, int *flags);
|
||||
void create_mapfiles(const struct Options *opts, struct GBImage *gb,
|
||||
struct Mapfile *tilemap, struct Mapfile *attrmap);
|
||||
void output_tilemap_file(const struct Options *opts,
|
||||
const struct Tilemap *tilemap);
|
||||
const struct Mapfile *tilemap);
|
||||
void output_attrmap_file(const struct Options *opts,
|
||||
const struct Mapfile *attrmap);
|
||||
void output_palette_file(const struct Options *opts,
|
||||
const struct RawIndexedImage *raw_image);
|
||||
|
||||
|
||||
@@ -21,10 +21,14 @@ struct Options {
|
||||
bool hardfix;
|
||||
bool fix;
|
||||
bool horizontal;
|
||||
bool mirror;
|
||||
bool unique;
|
||||
bool colorcurve;
|
||||
int trim;
|
||||
char *mapfile;
|
||||
bool mapout;
|
||||
char *tilemapfile;
|
||||
bool tilemapout;
|
||||
char *attrmapfile;
|
||||
bool attrmapout;
|
||||
char *palfile;
|
||||
bool palout;
|
||||
char *outfile;
|
||||
@@ -40,8 +44,10 @@ struct RGBColor {
|
||||
struct ImageOptions {
|
||||
bool horizontal;
|
||||
int trim;
|
||||
char *mapfile;
|
||||
bool mapout;
|
||||
char *tilemapfile;
|
||||
bool tilemapout;
|
||||
char *attrmapfile;
|
||||
bool attrmapout;
|
||||
char *palfile;
|
||||
bool palout;
|
||||
};
|
||||
@@ -71,7 +77,7 @@ struct GBImage {
|
||||
int trim;
|
||||
};
|
||||
|
||||
struct Tilemap {
|
||||
struct Mapfile {
|
||||
uint8_t *data;
|
||||
int size;
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ void sym_Init(void);
|
||||
void sym_CreateSymbol(char *tzName, int32_t nValue, int32_t nBank,
|
||||
char *tzObjFileName, char *tzFileName,
|
||||
uint32_t nFileLine);
|
||||
int32_t sym_GetValue(char *tzName);
|
||||
int32_t sym_GetBank(char *tzName);
|
||||
int32_t sym_GetValue(struct sPatch *pPatch, char *tzName);
|
||||
int32_t sym_GetBank(struct sPatch *pPatch, char *tzName);
|
||||
|
||||
#endif /* RGBDS_LINK_SYMBOL_H */
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
#define PACKAGE_VERSION_MAJOR (0)
|
||||
#define PACKAGE_VERSION_MINOR (3)
|
||||
#define PACKAGE_VERSION_PATCH (8)
|
||||
#define PACKAGE_VERSION_PATCH (10)
|
||||
|
||||
const char *get_package_version_string(void);
|
||||
|
||||
|
||||
258
src/asm/asmy.y
258
src/asm/asmy.y
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of RGBDS.
|
||||
*
|
||||
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
|
||||
* Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
@@ -9,6 +9,7 @@
|
||||
%{
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -25,6 +26,9 @@
|
||||
#include "asm/output.h"
|
||||
#include "asm/rpn.h"
|
||||
#include "asm/symbol.h"
|
||||
#include "asm/util.h"
|
||||
|
||||
#include "extern/utf8decoder.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "linkdefs.h"
|
||||
@@ -73,7 +77,8 @@ static void bankrangecheck(char *name, uint32_t secttype, int32_t org,
|
||||
out_NewAbsSection(name, secttype, org, bank);
|
||||
}
|
||||
|
||||
size_t symvaluetostring(char *dest, size_t maxLength, char *sym)
|
||||
size_t symvaluetostring(char *dest, size_t maxLength, char *sym,
|
||||
const char *mode)
|
||||
{
|
||||
size_t length;
|
||||
|
||||
@@ -81,6 +86,9 @@ size_t symvaluetostring(char *dest, size_t maxLength, char *sym)
|
||||
char *src = sym_GetStringValue(sym);
|
||||
size_t i;
|
||||
|
||||
if (mode)
|
||||
yyerror("Print types are only allowed for numbers");
|
||||
|
||||
for (i = 0; src[i] != 0; i++) {
|
||||
if (i >= maxLength)
|
||||
fatalerror("Symbol value too long to fit buffer");
|
||||
@@ -92,8 +100,25 @@ size_t symvaluetostring(char *dest, size_t maxLength, char *sym)
|
||||
|
||||
} else {
|
||||
uint32_t value = sym_GetConstantValue(sym);
|
||||
int32_t fullLength = snprintf(dest, maxLength + 1, "$%X",
|
||||
value);
|
||||
int32_t fullLength;
|
||||
|
||||
/* Special cheat for binary */
|
||||
if (mode && !mode[0]) {
|
||||
char binary[33]; /* 32 bits + 1 terminator */
|
||||
char *write_ptr = binary + 32;
|
||||
fullLength = 0;
|
||||
binary[32] = 0;
|
||||
do {
|
||||
*(--write_ptr) = (value & 1) + '0';
|
||||
value >>= 1;
|
||||
fullLength++;
|
||||
} while(value);
|
||||
strncpy(dest, write_ptr, maxLength + 1);
|
||||
} else {
|
||||
fullLength = snprintf(dest, maxLength + 1,
|
||||
mode ? : "$%X",
|
||||
value);
|
||||
}
|
||||
|
||||
if (fullLength < 0) {
|
||||
fatalerror("snprintf encoding error");
|
||||
@@ -249,14 +274,13 @@ static void copymacro(void)
|
||||
src = pCurrentBuffer->pBuffer;
|
||||
ulNewMacroSize = len;
|
||||
|
||||
tzNewMacro = (char *)malloc(ulNewMacroSize+2);
|
||||
tzNewMacro = (char *)malloc(ulNewMacroSize + 1);
|
||||
if (tzNewMacro == NULL)
|
||||
fatalerror("Not enough memory for MACRO definition.");
|
||||
|
||||
uint32_t i;
|
||||
|
||||
tzNewMacro[ulNewMacroSize] = '\n';
|
||||
tzNewMacro[ulNewMacroSize+1] = 0;
|
||||
tzNewMacro[ulNewMacroSize] = 0;
|
||||
for (i = 0; i < ulNewMacroSize; i += 1) {
|
||||
tzNewMacro[i] = src[i];
|
||||
if (src[i] == '\n')
|
||||
@@ -266,16 +290,21 @@ static void copymacro(void)
|
||||
yyskipbytes(ulNewMacroSize + 4);
|
||||
}
|
||||
|
||||
static bool endsIf(char c)
|
||||
{
|
||||
return isWhiteSpace(c) || c == '(' || c == '{';
|
||||
}
|
||||
|
||||
static uint32_t isIf(char *s)
|
||||
{
|
||||
return (strncasecmp(s, "IF", 2) == 0)
|
||||
&& isWhiteSpace(s[-1]) && isWhiteSpace(s[2]);
|
||||
&& isWhiteSpace(s[-1]) && endsIf(s[2]);
|
||||
}
|
||||
|
||||
static uint32_t isElif(char *s)
|
||||
{
|
||||
return (strncasecmp(s, "ELIF", 4) == 0)
|
||||
&& isWhiteSpace(s[-1]) && isWhiteSpace(s[4]);
|
||||
&& isWhiteSpace(s[-1]) && endsIf(s[4]);
|
||||
}
|
||||
|
||||
static uint32_t isElse(char *s)
|
||||
@@ -324,20 +353,14 @@ static void if_skip_to_else(void)
|
||||
src++;
|
||||
}
|
||||
} else {
|
||||
switch (*src) {
|
||||
case '\\':
|
||||
src += 2;
|
||||
break;
|
||||
|
||||
case '\"':
|
||||
src += 2;
|
||||
if (*src == '\"') {
|
||||
inString = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
src++;
|
||||
break;
|
||||
} else if (*src == '\\') {
|
||||
/* Escaped quotes don't end the string */
|
||||
if (*++src != '\"')
|
||||
src--;
|
||||
}
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,21 +398,14 @@ static void if_skip_to_endc(void)
|
||||
src++;
|
||||
}
|
||||
} else {
|
||||
switch (*src) {
|
||||
|
||||
case '\\':
|
||||
src += 2;
|
||||
break;
|
||||
|
||||
case '\"':
|
||||
src++;
|
||||
if (*src == '\"') {
|
||||
inString = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
src++;
|
||||
break;
|
||||
} else if (*src == '\\') {
|
||||
/* Escaped quotes don't end the string */
|
||||
if (*++src != '\"')
|
||||
src--;
|
||||
}
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,6 +447,85 @@ static void updateUnion(void)
|
||||
pPCSymbol->nValue = unionStart[unionIndex];
|
||||
}
|
||||
|
||||
static size_t strlenUTF8(const char *s)
|
||||
{
|
||||
size_t len = 0;
|
||||
uint32_t state = 0;
|
||||
uint32_t codep = 0;
|
||||
|
||||
while (*s) {
|
||||
switch (decode(&state, &codep, (uint8_t)*s)) {
|
||||
case 1:
|
||||
fatalerror("STRLEN: Invalid UTF-8 character");
|
||||
break;
|
||||
case 0:
|
||||
len++;
|
||||
break;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
|
||||
/* Check for partial code point. */
|
||||
if (state != 0)
|
||||
fatalerror("STRLEN: Invalid UTF-8 character");
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static void strsubUTF8(char *dest, const char *src, uint32_t pos, uint32_t len)
|
||||
{
|
||||
size_t srcIndex = 0;
|
||||
size_t destIndex = 0;
|
||||
uint32_t state = 0;
|
||||
uint32_t codep = 0;
|
||||
uint32_t curPos = 1;
|
||||
uint32_t curLen = 0;
|
||||
|
||||
if (pos < 1) {
|
||||
warning("STRSUB: Position starts at 1");
|
||||
pos = 1;
|
||||
}
|
||||
|
||||
/* Advance to starting position in source string. */
|
||||
while (src[srcIndex] && curPos < pos) {
|
||||
switch (decode(&state, &codep, (uint8_t)src[srcIndex])) {
|
||||
case 1:
|
||||
fatalerror("STRSUB: Invalid UTF-8 character");
|
||||
break;
|
||||
case 0:
|
||||
curPos++;
|
||||
break;
|
||||
}
|
||||
srcIndex++;
|
||||
}
|
||||
|
||||
if (!src[srcIndex])
|
||||
warning("STRSUB: Position %lu is past the end of the string",
|
||||
(unsigned long)pos);
|
||||
|
||||
/* Copy from source to destination. */
|
||||
while (src[srcIndex] && destIndex < MAXSTRLEN && curLen < len) {
|
||||
switch (decode(&state, &codep, (uint8_t)src[srcIndex])) {
|
||||
case 1:
|
||||
fatalerror("STRSUB: Invalid UTF-8 character");
|
||||
break;
|
||||
case 0:
|
||||
curLen++;
|
||||
break;
|
||||
}
|
||||
dest[destIndex++] = src[srcIndex++];
|
||||
}
|
||||
|
||||
if (curLen < len)
|
||||
warning("STRSUB: Length too big: %lu", (unsigned long)len);
|
||||
|
||||
/* Check for partial code point. */
|
||||
if (state != 0)
|
||||
fatalerror("STRSUB: Invalid UTF-8 character");
|
||||
|
||||
dest[destIndex] = 0;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%union
|
||||
@@ -456,8 +551,8 @@ static void updateUnion(void)
|
||||
%token <tzString> T_STRING
|
||||
|
||||
%left <nConstValue> T_OP_LOGICNOT
|
||||
%left <nConstValue> T_OP_LOGICOR T_OP_LOGICAND T_OP_LOGICEQU
|
||||
%left <nConstValue> T_OP_LOGICGT T_OP_LOGICLT T_OP_LOGICGE T_OP_LOGICLE T_OP_LOGICNE
|
||||
%left <nConstValue> T_OP_LOGICOR T_OP_LOGICAND
|
||||
%left <nConstValue> T_OP_LOGICGT T_OP_LOGICLT T_OP_LOGICGE T_OP_LOGICLE T_OP_LOGICNE T_OP_LOGICEQU
|
||||
%left <nConstValue> T_OP_ADD T_OP_SUB
|
||||
%left <nConstValue> T_OP_OR T_OP_XOR T_OP_AND
|
||||
%left <nConstValue> T_OP_SHL T_OP_SHR
|
||||
@@ -510,6 +605,10 @@ static void updateUnion(void)
|
||||
%token T_POP_UNION T_POP_NEXTU T_POP_ENDU
|
||||
%token T_POP_INCBIN T_POP_REPT
|
||||
%token T_POP_CHARMAP
|
||||
%token T_POP_NEWCHARMAP
|
||||
%token T_POP_SETCHARMAP
|
||||
%token T_POP_PUSHC
|
||||
%token T_POP_POPC
|
||||
%token T_POP_SHIFT
|
||||
%token T_POP_ENDR
|
||||
%token T_POP_FAIL
|
||||
@@ -663,6 +762,10 @@ simple_pseudoop : include
|
||||
| endu
|
||||
| incbin
|
||||
| charmap
|
||||
| newcharmap
|
||||
| setcharmap
|
||||
| pushc
|
||||
| popc
|
||||
| rept
|
||||
| shift
|
||||
| fail
|
||||
@@ -712,15 +815,17 @@ shift : T_POP_SHIFT { sym_ShiftCurrentMacroArgs(); }
|
||||
|
||||
rept : T_POP_REPT uconst
|
||||
{
|
||||
uint32_t nDefinitionLineNo = nLineNo;
|
||||
copyrept();
|
||||
fstk_RunRept($2);
|
||||
fstk_RunRept($2, nDefinitionLineNo);
|
||||
}
|
||||
;
|
||||
|
||||
macrodef : T_LABEL ':' T_POP_MACRO
|
||||
{
|
||||
int32_t nDefinitionLineNo = nLineNo;
|
||||
copymacro();
|
||||
sym_AddMacro($1);
|
||||
sym_AddMacro($1, nDefinitionLineNo);
|
||||
}
|
||||
;
|
||||
|
||||
@@ -799,7 +904,7 @@ ds : T_POP_DS uconst
|
||||
;
|
||||
|
||||
db : T_POP_DB constlist_8bit_entry comma constlist_8bit {
|
||||
if ((nPass == 1) && (nListCountEmpty > 0)) {
|
||||
if (nListCountEmpty > 0) {
|
||||
warning("Empty entry in list of 8-bit elements (treated as 0).");
|
||||
}
|
||||
}
|
||||
@@ -807,7 +912,7 @@ db : T_POP_DB constlist_8bit_entry comma constlist_8bit {
|
||||
;
|
||||
|
||||
dw : T_POP_DW constlist_16bit_entry comma constlist_16bit {
|
||||
if ((nPass == 1) && (nListCountEmpty > 0)) {
|
||||
if (nListCountEmpty > 0) {
|
||||
warning("Empty entry in list of 16-bit elements (treated as 0).");
|
||||
}
|
||||
}
|
||||
@@ -815,7 +920,7 @@ dw : T_POP_DW constlist_16bit_entry comma constlist_16bit {
|
||||
;
|
||||
|
||||
dl : T_POP_DL constlist_32bit_entry comma constlist_32bit {
|
||||
if ((nPass == 1) && (nListCountEmpty > 0)) {
|
||||
if (nListCountEmpty > 0) {
|
||||
warning("Empty entry in list of 32-bit elements (treated as 0).");
|
||||
}
|
||||
}
|
||||
@@ -852,8 +957,7 @@ import_list_entry : T_ID
|
||||
* This is done automatically if the label isn't found
|
||||
* in the list of defined symbols.
|
||||
*/
|
||||
if (nPass == 1)
|
||||
warning("IMPORT is a deprecated keyword with no effect: %s", $1);
|
||||
warning("IMPORT is a deprecated keyword with no effect: %s", $1);
|
||||
}
|
||||
;
|
||||
|
||||
@@ -879,7 +983,7 @@ global_list : global_list_entry
|
||||
|
||||
global_list_entry : T_ID
|
||||
{
|
||||
sym_Global($1);
|
||||
sym_Export($1);
|
||||
}
|
||||
;
|
||||
|
||||
@@ -927,31 +1031,49 @@ charmap : T_POP_CHARMAP string comma string
|
||||
}
|
||||
;
|
||||
|
||||
newcharmap : T_POP_NEWCHARMAP T_ID
|
||||
{
|
||||
charmap_New($2, NULL);
|
||||
}
|
||||
| T_POP_NEWCHARMAP T_ID comma T_ID
|
||||
{
|
||||
charmap_New($2, $4);
|
||||
}
|
||||
;
|
||||
|
||||
setcharmap : T_POP_SETCHARMAP T_ID
|
||||
{
|
||||
charmap_Set($2);
|
||||
}
|
||||
;
|
||||
|
||||
pushc : T_POP_PUSHC { charmap_Push(); }
|
||||
;
|
||||
|
||||
popc : T_POP_POPC { charmap_Pop(); }
|
||||
;
|
||||
|
||||
printt : T_POP_PRINTT string
|
||||
{
|
||||
if (nPass == 1)
|
||||
printf("%s", $2);
|
||||
printf("%s", $2);
|
||||
}
|
||||
;
|
||||
|
||||
printv : T_POP_PRINTV const
|
||||
{
|
||||
if (nPass == 1)
|
||||
printf("$%X", constexpr_GetConstantValue(&$2));
|
||||
printf("$%X", constexpr_GetConstantValue(&$2));
|
||||
}
|
||||
;
|
||||
|
||||
printi : T_POP_PRINTI const
|
||||
{
|
||||
if (nPass == 1)
|
||||
printf("%d", constexpr_GetConstantValue(&$2));
|
||||
printf("%d", constexpr_GetConstantValue(&$2));
|
||||
}
|
||||
;
|
||||
|
||||
printf : T_POP_PRINTF const
|
||||
{
|
||||
if (nPass == 1)
|
||||
math_Print(constexpr_GetConstantValue(&$2));
|
||||
math_Print(constexpr_GetConstantValue(&$2));
|
||||
}
|
||||
;
|
||||
|
||||
@@ -1126,20 +1248,17 @@ relocconst : T_ID
|
||||
struct Expression sTemp, sOffset;
|
||||
|
||||
rpn_Symbol(&sTemp, $1);
|
||||
sTemp.nVal = sym_GetValue($1);
|
||||
|
||||
rpn_Number(&sOffset, nPCOffset);
|
||||
|
||||
rpn_SUB(&$$, &sTemp, &sOffset);
|
||||
} else {
|
||||
rpn_Symbol(&$$, $1);
|
||||
$$.nVal = sym_GetValue($1);
|
||||
}
|
||||
}
|
||||
| T_NUMBER
|
||||
{
|
||||
rpn_Number(&$$, $1);
|
||||
$$.nVal = $1;
|
||||
}
|
||||
| string
|
||||
{
|
||||
@@ -1149,7 +1268,6 @@ relocconst : T_ID
|
||||
|
||||
free(s);
|
||||
rpn_Number(&$$, r);
|
||||
$$.nVal = r;
|
||||
}
|
||||
| T_OP_LOGICNOT relocconst %prec NEG { rpn_LOGNOT(&$$, &$2); }
|
||||
| relocconst T_OP_LOGICOR relocconst { rpn_LOGOR(&$$, &$1, &$3); }
|
||||
@@ -1179,12 +1297,10 @@ relocconst : T_ID
|
||||
{
|
||||
/* '@' is also a T_ID, it is handled here. */
|
||||
rpn_BankSymbol(&$$, $3);
|
||||
$$.nVal = 0;
|
||||
}
|
||||
| T_OP_BANK '(' string ')'
|
||||
{
|
||||
rpn_BankSection(&$$, $3);
|
||||
$$.nVal = 0;
|
||||
}
|
||||
| T_OP_DEF {
|
||||
oDontExpandStrings = true;
|
||||
@@ -1260,7 +1376,7 @@ relocconst : T_ID
|
||||
else
|
||||
rpn_Number(&$$, 0);
|
||||
}
|
||||
| T_OP_STRLEN '(' string ')' { rpn_Number(&$$, strlen($3)); }
|
||||
| T_OP_STRLEN '(' string ')' { rpn_Number(&$$, strlenUTF8($3)); }
|
||||
| '(' relocconst ')' { $$ = $2; }
|
||||
;
|
||||
|
||||
@@ -1338,7 +1454,7 @@ const : T_ID { constexpr_Symbol(&$$, $1); }
|
||||
else
|
||||
constexpr_Number(&$$, 0);
|
||||
}
|
||||
| T_OP_STRLEN '(' string ')' { constexpr_Number(&$$, strlen($3)); }
|
||||
| T_OP_STRLEN '(' string ')' { constexpr_Number(&$$, strlenUTF8($3)); }
|
||||
| '(' const ')' { $$ = $2; }
|
||||
;
|
||||
|
||||
@@ -1349,14 +1465,7 @@ string : T_STRING
|
||||
}
|
||||
| T_OP_STRSUB '(' string comma uconst comma uconst ')'
|
||||
{
|
||||
uint32_t len = $7;
|
||||
if (len > MAXSTRLEN) {
|
||||
warning("STRSUB: Length too big: %u", len);
|
||||
len = MAXSTRLEN;
|
||||
}
|
||||
|
||||
if (snprintf($$, len + 1, "%s", $3 + $5 - 1) > MAXSTRLEN)
|
||||
warning("STRSUB: String too long '%s'", $$);
|
||||
strsubUTF8($$, $3, $5, $7);
|
||||
}
|
||||
| T_OP_STRCAT '(' string comma string ')'
|
||||
{
|
||||
@@ -1637,8 +1746,7 @@ z80_jp : T_Z80_JP const_16bit
|
||||
| T_Z80_JP T_MODE_HL_IND
|
||||
{
|
||||
out_AbsByte(0xE9);
|
||||
if (nPass == 1)
|
||||
warning("'JP [HL]' is obsolete, use 'JP HL' instead.");
|
||||
warning("'JP [HL]' is obsolete, use 'JP HL' instead.");
|
||||
}
|
||||
| T_Z80_JP T_MODE_HL
|
||||
{
|
||||
@@ -1665,8 +1773,7 @@ z80_ldi : T_Z80_LDI T_MODE_HL_IND comma T_MODE_A
|
||||
| T_Z80_LDI T_MODE_A comma T_MODE_HL
|
||||
{
|
||||
out_AbsByte(0x0A | (2 << 4));
|
||||
if (nPass == 1)
|
||||
warning("'LDI A,HL' is obsolete, use 'LDI A,[HL]' or 'LD A,[HL+] instead.");
|
||||
warning("'LDI A,HL' is obsolete, use 'LDI A,[HL]' or 'LD A,[HL+] instead.");
|
||||
}
|
||||
| T_Z80_LDI T_MODE_A comma T_MODE_HL_IND
|
||||
{
|
||||
@@ -1681,8 +1788,7 @@ z80_ldd : T_Z80_LDD T_MODE_HL_IND comma T_MODE_A
|
||||
| T_Z80_LDD T_MODE_A comma T_MODE_HL
|
||||
{
|
||||
out_AbsByte(0x0A | (3 << 4));
|
||||
if (nPass == 1)
|
||||
warning("'LDD A,HL' is obsolete, use 'LDD A,[HL]' or 'LD A,[HL-] instead.");
|
||||
warning("'LDD A,HL' is obsolete, use 'LDD A,[HL]' or 'LD A,[HL-] instead.");
|
||||
}
|
||||
| T_Z80_LDD T_MODE_A comma T_MODE_HL_IND
|
||||
{
|
||||
@@ -1772,6 +1878,7 @@ z80_ld_mem : T_Z80_LD op_mem_ind comma T_MODE_SP
|
||||
(!rpn_isReloc(&$2)) && ($2.nVal >= 0xFF00)) {
|
||||
out_AbsByte(0xE0);
|
||||
out_AbsByte($2.nVal & 0xFF);
|
||||
rpn_Free(&$2);
|
||||
} else {
|
||||
out_AbsByte(0xEA);
|
||||
out_RelWord(&$2);
|
||||
@@ -1826,12 +1933,14 @@ z80_ld_a : T_Z80_LD reg_r comma T_MODE_C_IND
|
||||
(!rpn_isReloc(&$4)) && ($4.nVal >= 0xFF00)) {
|
||||
out_AbsByte(0xF0);
|
||||
out_AbsByte($4.nVal & 0xFF);
|
||||
rpn_Free(&$4);
|
||||
} else {
|
||||
out_AbsByte(0xFA);
|
||||
out_RelWord(&$4);
|
||||
}
|
||||
} else {
|
||||
yyerror("Destination operand must be A");
|
||||
rpn_Free(&$4);
|
||||
}
|
||||
}
|
||||
;
|
||||
@@ -1964,6 +2073,7 @@ z80_rst : T_Z80_RST const_8bit
|
||||
yyerror("Invalid address $%x for RST", $2.nVal);
|
||||
else
|
||||
out_AbsByte(0xC7 | $2.nVal);
|
||||
rpn_Free(&$2);
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
@@ -15,134 +16,281 @@
|
||||
#include "asm/charmap.h"
|
||||
#include "asm/main.h"
|
||||
#include "asm/output.h"
|
||||
#include "asm/util.h"
|
||||
|
||||
#include "extern/utf8decoder.h"
|
||||
#define CHARMAP_HASH_SIZE (1 << 9)
|
||||
|
||||
struct Charmap globalCharmap = {0};
|
||||
struct CharmapStackEntry {
|
||||
struct Charmap *charmap;
|
||||
struct CharmapStackEntry *next;
|
||||
};
|
||||
|
||||
int32_t readUTF8Char(char *dest, char *src)
|
||||
static struct Charmap *tHashedCharmaps[CHARMAP_HASH_SIZE];
|
||||
|
||||
static struct Charmap *mainCharmap;
|
||||
static struct Charmap *currentCharmap;
|
||||
|
||||
struct CharmapStackEntry *charmapStack;
|
||||
|
||||
static void warnSectionCharmap(void)
|
||||
{
|
||||
uint32_t state;
|
||||
uint32_t codep;
|
||||
int32_t i;
|
||||
static bool warned = false;
|
||||
|
||||
for (i = 0, state = 0;; i++) {
|
||||
if (decode(&state, &codep, (uint8_t)src[i]) == 1)
|
||||
fatalerror("invalid UTF-8 character");
|
||||
if (warned)
|
||||
return;
|
||||
|
||||
dest[i] = src[i];
|
||||
warning("Using 'charmap' within a section when the current charmap is 'main' is deprecated");
|
||||
warned = true;
|
||||
}
|
||||
|
||||
if (state == 0) {
|
||||
dest[++i] = '\0';
|
||||
return i;
|
||||
static uint32_t charmap_CalcHash(const char *s)
|
||||
{
|
||||
return calchash(s) % CHARMAP_HASH_SIZE;
|
||||
}
|
||||
|
||||
static struct Charmap **charmap_Get(const char *name)
|
||||
{
|
||||
struct Charmap **ppCharmap = &tHashedCharmaps[charmap_CalcHash(name)];
|
||||
|
||||
while (*ppCharmap != NULL && strcmp((*ppCharmap)->name, name))
|
||||
ppCharmap = &(*ppCharmap)->next;
|
||||
|
||||
return ppCharmap;
|
||||
}
|
||||
|
||||
static void CopyNode(struct Charmap *dest,
|
||||
const struct Charmap *src,
|
||||
int nodeIdx)
|
||||
{
|
||||
dest->nodes[nodeIdx].code = src->nodes[nodeIdx].code;
|
||||
dest->nodes[nodeIdx].isCode = src->nodes[nodeIdx].isCode;
|
||||
for (int i = 0; i < 256; i++)
|
||||
if (src->nodes[nodeIdx].next[i])
|
||||
dest->nodes[nodeIdx].next[i] = dest->nodes +
|
||||
(src->nodes[nodeIdx].next[i] - src->nodes);
|
||||
}
|
||||
|
||||
struct Charmap *charmap_New(const char *name, const char *baseName)
|
||||
{
|
||||
struct Charmap *pBase = NULL;
|
||||
|
||||
if (baseName != NULL) {
|
||||
struct Charmap **ppBase = charmap_Get(baseName);
|
||||
|
||||
if (*ppBase == NULL) {
|
||||
yyerror("Base charmap '%s' doesn't exist", baseName);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pBase = *ppBase;
|
||||
}
|
||||
|
||||
struct Charmap **ppCharmap = charmap_Get(name);
|
||||
|
||||
if (*ppCharmap != NULL) {
|
||||
yyerror("Charmap '%s' already exists", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*ppCharmap = calloc(1, sizeof(struct Charmap));
|
||||
|
||||
if (*ppCharmap == NULL)
|
||||
fatalerror("Not enough memory for charmap");
|
||||
|
||||
struct Charmap *pCharmap = *ppCharmap;
|
||||
|
||||
snprintf(pCharmap->name, sizeof(pCharmap->name), "%s", name);
|
||||
|
||||
if (pBase != NULL) {
|
||||
pCharmap->charCount = pBase->charCount;
|
||||
pCharmap->nodeCount = pBase->nodeCount;
|
||||
|
||||
for (int i = 0; i < MAXCHARNODES; i++)
|
||||
CopyNode(pCharmap, pBase, i);
|
||||
}
|
||||
|
||||
currentCharmap = pCharmap;
|
||||
|
||||
return pCharmap;
|
||||
}
|
||||
|
||||
void charmap_Set(const char *name)
|
||||
{
|
||||
struct Charmap **ppCharmap = charmap_Get(name);
|
||||
|
||||
if (*ppCharmap == NULL) {
|
||||
yyerror("Charmap '%s' doesn't exist", name);
|
||||
return;
|
||||
}
|
||||
|
||||
currentCharmap = *ppCharmap;
|
||||
}
|
||||
|
||||
void charmap_Push(void)
|
||||
{
|
||||
struct CharmapStackEntry *stackEntry;
|
||||
|
||||
stackEntry = malloc(sizeof(struct CharmapStackEntry));
|
||||
if (stackEntry == NULL)
|
||||
fatalerror("No memory for charmap stack");
|
||||
|
||||
stackEntry->charmap = currentCharmap;
|
||||
stackEntry->next = charmapStack;
|
||||
|
||||
charmapStack = stackEntry;
|
||||
}
|
||||
|
||||
void charmap_Pop(void)
|
||||
{
|
||||
if (charmapStack == NULL)
|
||||
fatalerror("No entries in the charmap stack");
|
||||
|
||||
struct CharmapStackEntry *top = charmapStack;
|
||||
|
||||
currentCharmap = top->charmap;
|
||||
charmapStack = top->next;
|
||||
free(top);
|
||||
}
|
||||
|
||||
void charmap_InitMain(void)
|
||||
{
|
||||
mainCharmap = charmap_New("main", NULL);
|
||||
}
|
||||
|
||||
int32_t charmap_Add(char *input, uint8_t output)
|
||||
{
|
||||
int32_t i;
|
||||
size_t input_length;
|
||||
char temp1i[CHARMAPLENGTH + 1], temp2i[CHARMAPLENGTH + 1];
|
||||
char temp1o = 0, temp2o = 0;
|
||||
uint8_t v;
|
||||
|
||||
struct Charmap *charmap;
|
||||
struct Charmap *charmap;
|
||||
struct Charnode *curr_node, *temp_node;
|
||||
|
||||
if (pCurrentSection) {
|
||||
/*
|
||||
* If the user tries to define a character mapping inside a section
|
||||
* and the current global charmap is the "main" one, then a local
|
||||
* section charmap will be created or modified instead of the global
|
||||
* one. In other words, the local section charmap can override the
|
||||
* main global one, but not the others.
|
||||
*/
|
||||
if (pCurrentSection && currentCharmap == mainCharmap) {
|
||||
warnSectionCharmap();
|
||||
if (pCurrentSection->charmap) {
|
||||
charmap = pCurrentSection->charmap;
|
||||
} else {
|
||||
charmap = calloc(1, sizeof(struct Charmap));
|
||||
if (charmap == NULL)
|
||||
fatalerror("Not enough memory for charmap");
|
||||
|
||||
pCurrentSection->charmap = charmap;
|
||||
}
|
||||
} else {
|
||||
charmap = &globalCharmap;
|
||||
charmap = currentCharmap;
|
||||
}
|
||||
|
||||
if (nPass == 2)
|
||||
return charmap->count;
|
||||
|
||||
if (charmap->count > MAXCHARMAPS || strlen(input) > CHARMAPLENGTH)
|
||||
if (charmap->charCount >= MAXCHARMAPS || strlen(input) > CHARMAPLENGTH)
|
||||
return -1;
|
||||
|
||||
input_length = strlen(input);
|
||||
if (input_length > 1) {
|
||||
i = 0;
|
||||
while (i < charmap->count + 1) {
|
||||
if (input_length > strlen(charmap->input[i])) {
|
||||
memcpy(temp1i, charmap->input[i],
|
||||
CHARMAPLENGTH + 1);
|
||||
memcpy(charmap->input[i], input, input_length);
|
||||
temp1o = charmap->output[i];
|
||||
charmap->output[i] = output;
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
curr_node = &charmap->nodes[0];
|
||||
|
||||
for (i = 0; (v = (uint8_t)input[i]); i++) {
|
||||
if (curr_node->next[v]) {
|
||||
curr_node = curr_node->next[v];
|
||||
} else {
|
||||
temp_node = &charmap->nodes[charmap->nodeCount + 1];
|
||||
|
||||
curr_node->next[v] = temp_node;
|
||||
curr_node = temp_node;
|
||||
|
||||
++charmap->nodeCount;
|
||||
}
|
||||
while (i < charmap->count + 1) {
|
||||
memcpy(temp2i, charmap->input[i], CHARMAPLENGTH + 1);
|
||||
memcpy(charmap->input[i], temp1i, CHARMAPLENGTH + 1);
|
||||
memcpy(temp1i, temp2i, CHARMAPLENGTH + 1);
|
||||
temp2o = charmap->output[i];
|
||||
charmap->output[i] = temp1o;
|
||||
temp1o = temp2o;
|
||||
i++;
|
||||
}
|
||||
memcpy(charmap->input[charmap->count + 1], temp1i,
|
||||
CHARMAPLENGTH + 1);
|
||||
charmap->output[charmap->count + 1] = temp1o;
|
||||
} else {
|
||||
memcpy(charmap->input[charmap->count], input, input_length);
|
||||
charmap->output[charmap->count] = output;
|
||||
}
|
||||
return ++charmap->count;
|
||||
|
||||
/* prevent duplicated keys by accepting only first key-value pair. */
|
||||
if (curr_node->isCode)
|
||||
return charmap->charCount;
|
||||
|
||||
curr_node->code = output;
|
||||
curr_node->isCode = 1;
|
||||
|
||||
return ++charmap->charCount;
|
||||
}
|
||||
|
||||
int32_t charmap_Convert(char **input)
|
||||
{
|
||||
struct Charmap *charmap;
|
||||
struct Charmap *charmap;
|
||||
struct Charnode *charnode;
|
||||
|
||||
char outchar[CHARMAPLENGTH + 1];
|
||||
char *buffer;
|
||||
int32_t i, j, length;
|
||||
char *output;
|
||||
char outchar[8];
|
||||
|
||||
if (pCurrentSection && pCurrentSection->charmap)
|
||||
int32_t i, match, length;
|
||||
uint8_t v, foundCode;
|
||||
|
||||
/*
|
||||
* If there is a local section charmap and the current global charmap
|
||||
* is the "main" one, the local one is used. Otherwise, the global
|
||||
* one is used. In other words, the local section charmap can override
|
||||
* the main global one, but not the others.
|
||||
*/
|
||||
if (pCurrentSection &&
|
||||
pCurrentSection->charmap &&
|
||||
currentCharmap == mainCharmap)
|
||||
charmap = pCurrentSection->charmap;
|
||||
else
|
||||
charmap = &globalCharmap;
|
||||
charmap = currentCharmap;
|
||||
|
||||
buffer = malloc(strlen(*input));
|
||||
if (buffer == NULL)
|
||||
output = malloc(strlen(*input));
|
||||
if (output == NULL)
|
||||
fatalerror("Not enough memory for buffer");
|
||||
|
||||
length = 0;
|
||||
|
||||
while (**input) {
|
||||
j = 0;
|
||||
for (i = 0; i < charmap->count; i++) {
|
||||
j = strlen(charmap->input[i]);
|
||||
if (memcmp(*input, charmap->input[i], j) == 0) {
|
||||
outchar[0] = charmap->output[i];
|
||||
outchar[1] = 0;
|
||||
charnode = &charmap->nodes[0];
|
||||
|
||||
/*
|
||||
* find the longest valid match which has been registered in charmap.
|
||||
* note that there could be either multiple matches or no match.
|
||||
* and it possibly takes the longest match between them,
|
||||
* which means that it ignores partial matches shorter than the longest one.
|
||||
*/
|
||||
for (i = match = 0; (v = (*input)[i]);) {
|
||||
if (!charnode->next[v])
|
||||
break;
|
||||
|
||||
charnode = charnode->next[v];
|
||||
i++;
|
||||
|
||||
if (charnode->isCode) {
|
||||
match = i;
|
||||
foundCode = charnode->code;
|
||||
}
|
||||
j = 0;
|
||||
}
|
||||
|
||||
if (!j)
|
||||
j = readUTF8Char(outchar, *input);
|
||||
if (match) {
|
||||
output[length] = foundCode;
|
||||
|
||||
if (!outchar[0]) {
|
||||
buffer[length++] = 0;
|
||||
length += 1;
|
||||
} else {
|
||||
for (i = 0; outchar[i]; i++)
|
||||
buffer[length++] = outchar[i];
|
||||
/*
|
||||
* put a utf-8 character
|
||||
* if failed to find a match.
|
||||
*/
|
||||
match = readUTF8Char(outchar, *input);
|
||||
|
||||
if (match) {
|
||||
memcpy(output + length, *input, match);
|
||||
} else {
|
||||
output[length] = 0;
|
||||
match = 1;
|
||||
}
|
||||
|
||||
length += match;
|
||||
}
|
||||
*input += j;
|
||||
|
||||
*input += match;
|
||||
}
|
||||
*input = buffer;
|
||||
|
||||
*input = output;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ void constexpr_UnaryOp(struct ConstExpression *expr,
|
||||
result = value;
|
||||
break;
|
||||
case T_OP_SUB:
|
||||
result = -value;
|
||||
result = -(uint32_t)value;
|
||||
break;
|
||||
case T_OP_NOT:
|
||||
result = ~value;
|
||||
@@ -155,10 +155,10 @@ void constexpr_BinaryOp(struct ConstExpression *expr,
|
||||
result = value1 != value2;
|
||||
break;
|
||||
case T_OP_ADD:
|
||||
result = value1 + value2;
|
||||
result = (uint32_t)value1 + (uint32_t)value2;
|
||||
break;
|
||||
case T_OP_SUB:
|
||||
result = value1 - value2;
|
||||
result = (uint32_t)value1 - (uint32_t)value2;
|
||||
break;
|
||||
case T_OP_XOR:
|
||||
result = value1 ^ value2;
|
||||
@@ -181,7 +181,7 @@ void constexpr_BinaryOp(struct ConstExpression *expr,
|
||||
fatalerror("Shift by too big value: %d",
|
||||
value2);
|
||||
|
||||
result = value1 << value2;
|
||||
result = (uint32_t)value1 << value2;
|
||||
break;
|
||||
case T_OP_SHR:
|
||||
if (value2 < 0)
|
||||
@@ -194,17 +194,25 @@ void constexpr_BinaryOp(struct ConstExpression *expr,
|
||||
result = value1 >> value2;
|
||||
break;
|
||||
case T_OP_MUL:
|
||||
result = value1 * value2;
|
||||
result = (uint32_t)value1 * (uint32_t)value2;
|
||||
break;
|
||||
case T_OP_DIV:
|
||||
if (value2 == 0)
|
||||
fatalerror("Division by zero");
|
||||
result = value1 / value2;
|
||||
if (value1 == INT32_MIN && value2 == -1) {
|
||||
warning("Division of min value by -1");
|
||||
result = INT32_MIN;
|
||||
} else {
|
||||
result = value1 / value2;
|
||||
}
|
||||
break;
|
||||
case T_OP_MOD:
|
||||
if (value2 == 0)
|
||||
fatalerror("Division by zero");
|
||||
result = value1 % value2;
|
||||
if (value1 == INT32_MIN && value2 == -1)
|
||||
result = 0;
|
||||
else
|
||||
result = value1 % value2;
|
||||
break;
|
||||
case T_OP_FDIV:
|
||||
result = math_Div(value1, value2);
|
||||
|
||||
120
src/asm/fstack.c
120
src/asm/fstack.c
@@ -28,6 +28,8 @@
|
||||
#include "types.h"
|
||||
|
||||
static struct sContext *pFileStack;
|
||||
static unsigned int nFileStackDepth;
|
||||
unsigned int nMaxRecursionDepth;
|
||||
static struct sSymbol *pCurrentMacro;
|
||||
static YY_BUFFER_STATE CurrentFlexHandle;
|
||||
static FILE *pCurrentFile;
|
||||
@@ -40,6 +42,8 @@ static uint32_t nMacroCount;
|
||||
static char *pCurrentREPTBlock;
|
||||
static uint32_t nCurrentREPTBlockSize;
|
||||
static uint32_t nCurrentREPTBlockCount;
|
||||
static int32_t nCurrentREPTBodyFirstLine;
|
||||
static int32_t nCurrentREPTBodyLastLine;
|
||||
|
||||
uint32_t ulMacroReturnValue;
|
||||
|
||||
@@ -51,6 +55,8 @@ uint32_t ulMacroReturnValue;
|
||||
#define STAT_isMacroArg 2
|
||||
#define STAT_isREPTBlock 3
|
||||
|
||||
/* Max context stack size */
|
||||
|
||||
/*
|
||||
* Context push and pop
|
||||
*/
|
||||
@@ -58,6 +64,9 @@ static void pushcontext(void)
|
||||
{
|
||||
struct sContext **ppFileStack;
|
||||
|
||||
if (++nFileStackDepth > nMaxRecursionDepth)
|
||||
fatalerror("Recursion limit (%d) exceeded", nMaxRecursionDepth);
|
||||
|
||||
ppFileStack = &pFileStack;
|
||||
while (*ppFileStack)
|
||||
ppFileStack = &((*ppFileStack)->pNext);
|
||||
@@ -86,6 +95,8 @@ static void pushcontext(void)
|
||||
(*ppFileStack)->pREPTBlock = pCurrentREPTBlock;
|
||||
(*ppFileStack)->nREPTBlockSize = nCurrentREPTBlockSize;
|
||||
(*ppFileStack)->nREPTBlockCount = nCurrentREPTBlockCount;
|
||||
(*ppFileStack)->nREPTBodyFirstLine = nCurrentREPTBodyFirstLine;
|
||||
(*ppFileStack)->nREPTBodyLastLine = nCurrentREPTBodyLastLine;
|
||||
break;
|
||||
default:
|
||||
fatalerror("%s: Internal error.", __func__);
|
||||
@@ -100,6 +111,11 @@ static int32_t popcontext(void)
|
||||
|
||||
if (nCurrentStatus == STAT_isREPTBlock) {
|
||||
if (--nCurrentREPTBlockCount) {
|
||||
char *pREPTIterationWritePtr;
|
||||
unsigned long nREPTIterationNo;
|
||||
int nNbCharsWritten;
|
||||
int nNbCharsLeft;
|
||||
|
||||
yy_delete_buffer(CurrentFlexHandle);
|
||||
CurrentFlexHandle =
|
||||
yy_scan_bytes(pCurrentREPTBlock,
|
||||
@@ -108,6 +124,29 @@ static int32_t popcontext(void)
|
||||
sym_UseCurrentMacroArgs();
|
||||
sym_SetMacroArgID(nMacroCount++);
|
||||
sym_UseNewMacroArgs();
|
||||
|
||||
/* Increment REPT count in file path */
|
||||
pREPTIterationWritePtr =
|
||||
strrchr(tzCurrentFileName, '~') + 1;
|
||||
nREPTIterationNo =
|
||||
strtoul(pREPTIterationWritePtr, NULL, 10);
|
||||
nNbCharsLeft = sizeof(tzCurrentFileName)
|
||||
- (pREPTIterationWritePtr - tzCurrentFileName);
|
||||
nNbCharsWritten = snprintf(pREPTIterationWritePtr,
|
||||
nNbCharsLeft, "%lu",
|
||||
nREPTIterationNo + 1);
|
||||
if (nNbCharsWritten >= nNbCharsLeft) {
|
||||
/*
|
||||
* The string is probably corrupted somehow,
|
||||
* revert the change to avoid a bad error
|
||||
* output.
|
||||
*/
|
||||
sprintf(pREPTIterationWritePtr, "%lu",
|
||||
nREPTIterationNo);
|
||||
fatalerror("Cannot write REPT count to file path");
|
||||
}
|
||||
|
||||
nLineNo = nCurrentREPTBodyFirstLine;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -128,13 +167,9 @@ static int32_t popcontext(void)
|
||||
if (nCurrentStatus == STAT_isInclude)
|
||||
fclose(pCurrentFile);
|
||||
|
||||
if (nCurrentStatus == STAT_isMacro) {
|
||||
sym_FreeCurrentMacroArgs();
|
||||
nLineNo += 1;
|
||||
}
|
||||
|
||||
if (nCurrentStatus == STAT_isREPTBlock)
|
||||
nLineNo += 1;
|
||||
if (nCurrentStatus == STAT_isMacro
|
||||
|| nCurrentStatus == STAT_isREPTBlock)
|
||||
nLineNo++;
|
||||
|
||||
CurrentFlexHandle = pLastFile->FlexHandle;
|
||||
strcpy((char *)tzCurrentFileName, (char *)pLastFile->tzFileName);
|
||||
@@ -153,11 +188,16 @@ static int32_t popcontext(void)
|
||||
pCurrentREPTBlock = pLastFile->pREPTBlock;
|
||||
nCurrentREPTBlockSize = pLastFile->nREPTBlockSize;
|
||||
nCurrentREPTBlockCount = pLastFile->nREPTBlockCount;
|
||||
nCurrentREPTBodyFirstLine = pLastFile->nREPTBodyFirstLine;
|
||||
/* + 1 to account for the `ENDR` line */
|
||||
nLineNo = pLastFile->nREPTBodyLastLine + 1;
|
||||
break;
|
||||
default:
|
||||
fatalerror("%s: Internal error.", __func__);
|
||||
}
|
||||
|
||||
nFileStackDepth--;
|
||||
|
||||
free(*ppLastFile);
|
||||
*ppLastFile = NULL;
|
||||
yy_switch_to_buffer(CurrentFlexHandle);
|
||||
@@ -222,6 +262,20 @@ void fstk_Dump(void)
|
||||
fprintf(stderr, "%s(%d)", tzCurrentFileName, nLineNo);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump the string expansion stack to stderr
|
||||
*/
|
||||
void fstk_DumpStringExpansions(void)
|
||||
{
|
||||
const struct sStringExpansionPos *pExpansion = pCurrentStringExpansion;
|
||||
|
||||
while (pExpansion) {
|
||||
fprintf(stderr, "while expanding symbol \"%s\"\n",
|
||||
pExpansion->tzName);
|
||||
pExpansion = pExpansion->pParent;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Extra includepath stuff
|
||||
*/
|
||||
@@ -317,19 +371,23 @@ void fstk_RunInclude(char *tzFileName)
|
||||
uint32_t fstk_RunMacro(char *s)
|
||||
{
|
||||
struct sSymbol *sym = sym_FindMacro(s);
|
||||
int nPrintedChars;
|
||||
|
||||
if (sym == NULL)
|
||||
if (sym == NULL || sym->pMacro == NULL)
|
||||
return 0;
|
||||
|
||||
pushcontext();
|
||||
sym_SetMacroArgID(nMacroCount++);
|
||||
nLineNo = -1;
|
||||
/* Minus 1 because there is a newline at the beginning of the buffer */
|
||||
nLineNo = sym->nFileLine - 1;
|
||||
sym_UseNewMacroArgs();
|
||||
nCurrentStatus = STAT_isMacro;
|
||||
strcpy(tzCurrentFileName, s);
|
||||
|
||||
if (sym->pMacro == NULL)
|
||||
return 0;
|
||||
nPrintedChars = snprintf(tzCurrentFileName, _MAX_PATH + 1,
|
||||
"%s::%s", sym->tzFileName, s);
|
||||
if (nPrintedChars > _MAX_PATH) {
|
||||
popcontext();
|
||||
fatalerror("File name + macro name is too large to fit into buffer");
|
||||
}
|
||||
|
||||
pCurrentMacro = sym;
|
||||
CurrentFlexHandle = yy_scan_bytes(pCurrentMacro->pMacro,
|
||||
@@ -385,17 +443,30 @@ void fstk_RunString(char *s)
|
||||
/*
|
||||
* Set up a repeat block for parsing
|
||||
*/
|
||||
void fstk_RunRept(uint32_t count)
|
||||
void fstk_RunRept(uint32_t count, int32_t nReptLineNo)
|
||||
{
|
||||
if (count) {
|
||||
pushcontext();
|
||||
static const char *tzReptStr = "::REPT~1";
|
||||
|
||||
/* For error printing to make sense, fake nLineNo */
|
||||
nCurrentREPTBodyLastLine = nLineNo;
|
||||
nLineNo = nReptLineNo;
|
||||
sym_UseCurrentMacroArgs();
|
||||
pushcontext();
|
||||
sym_SetMacroArgID(nMacroCount++);
|
||||
sym_UseNewMacroArgs();
|
||||
nCurrentREPTBlockCount = count;
|
||||
nCurrentStatus = STAT_isREPTBlock;
|
||||
nCurrentREPTBlockSize = ulNewMacroSize;
|
||||
pCurrentREPTBlock = tzNewMacro;
|
||||
nCurrentREPTBodyFirstLine = nReptLineNo + 1;
|
||||
nLineNo = nReptLineNo;
|
||||
|
||||
if (strlen(tzCurrentFileName) + strlen(tzReptStr) > _MAX_PATH)
|
||||
fatalerror("Cannot append \"%s\" to file path",
|
||||
tzReptStr);
|
||||
strcat(tzCurrentFileName, tzReptStr);
|
||||
|
||||
CurrentFlexHandle =
|
||||
yy_scan_bytes(pCurrentREPTBlock, nCurrentREPTBlockSize);
|
||||
yy_switch_to_buffer(CurrentFlexHandle);
|
||||
@@ -405,23 +476,26 @@ void fstk_RunRept(uint32_t count)
|
||||
/*
|
||||
* Initialize the filestack routines
|
||||
*/
|
||||
void fstk_Init(char *s)
|
||||
void fstk_Init(char *pFileName)
|
||||
{
|
||||
char tzFileName[_MAX_PATH + 1];
|
||||
char tzSymFileName[_MAX_PATH + 1 + 2];
|
||||
|
||||
snprintf(tzSymFileName, sizeof(tzSymFileName), "\"%s\"", s);
|
||||
snprintf(tzSymFileName, sizeof(tzSymFileName), "\"%s\"", pFileName);
|
||||
sym_AddString("__FILE__", tzSymFileName);
|
||||
|
||||
strcpy(tzFileName, s);
|
||||
pFileStack = NULL;
|
||||
pCurrentFile = fopen(tzFileName, "rb");
|
||||
if (pCurrentFile == NULL)
|
||||
err(1, "Unable to open file '%s'", tzFileName);
|
||||
if (strcmp(pFileName, "-") == 0) {
|
||||
pCurrentFile = stdin;
|
||||
} else {
|
||||
pCurrentFile = fopen(pFileName, "rb");
|
||||
if (pCurrentFile == NULL)
|
||||
err(1, "Unable to open file '%s'", pFileName);
|
||||
}
|
||||
nFileStackDepth = 0;
|
||||
|
||||
nMacroCount = 0;
|
||||
nCurrentStatus = STAT_isInclude;
|
||||
snprintf(tzCurrentFileName, _MAX_PATH + 1, "%s", tzFileName);
|
||||
snprintf(tzCurrentFileName, _MAX_PATH + 1, "%s", pFileName);
|
||||
CurrentFlexHandle = yy_create_buffer(pCurrentFile);
|
||||
yy_switch_to_buffer(CurrentFlexHandle);
|
||||
nLineNo = 1;
|
||||
|
||||
@@ -71,8 +71,9 @@ typedef int32_t(*x2bin) (char ch);
|
||||
|
||||
static int32_t ascii2bin(char *s)
|
||||
{
|
||||
int32_t radix = 10;
|
||||
int32_t result = 0;
|
||||
char *start = s;
|
||||
uint32_t radix = 10;
|
||||
uint32_t result = 0;
|
||||
x2bin convertfunc = char2bin;
|
||||
|
||||
switch (*s) {
|
||||
@@ -101,6 +102,9 @@ static int32_t ascii2bin(char *s)
|
||||
break;
|
||||
}
|
||||
|
||||
const uint32_t max_q = UINT32_MAX / radix;
|
||||
const uint32_t max_r = UINT32_MAX % radix;
|
||||
|
||||
if (*s == '\0') {
|
||||
/*
|
||||
* There are no digits after the radix prefix
|
||||
@@ -108,15 +112,39 @@ static int32_t ascii2bin(char *s)
|
||||
*/
|
||||
yyerror("Invalid integer constant");
|
||||
} else if (radix == 4) {
|
||||
int32_t size = 0;
|
||||
int32_t c;
|
||||
|
||||
while (*s != '\0') {
|
||||
c = convertfunc(*s++);
|
||||
result = result * 2 + ((c & 2) << 7) + (c & 1);
|
||||
size++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extending a graphics constant longer than 8 pixels,
|
||||
* the Game Boy tile width, produces a nonsensical result.
|
||||
*/
|
||||
if (size > 8) {
|
||||
warning("Graphics constant '%s' is too long",
|
||||
start);
|
||||
}
|
||||
} else {
|
||||
while (*s != '\0')
|
||||
result = result * radix + convertfunc(*s++);
|
||||
bool overflow = false;
|
||||
|
||||
while (*s != '\0') {
|
||||
int32_t digit = convertfunc(*s++);
|
||||
|
||||
if (result > max_q
|
||||
|| (result == max_q && digit > max_r)) {
|
||||
overflow = true;
|
||||
}
|
||||
result = result * radix + digit;
|
||||
}
|
||||
|
||||
if (overflow)
|
||||
warning("Integer constant '%s' is too large",
|
||||
start);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -124,19 +152,19 @@ static int32_t ascii2bin(char *s)
|
||||
|
||||
uint32_t ParseFixedPoint(char *s, uint32_t size)
|
||||
{
|
||||
uint32_t i = 0, dot = 0;
|
||||
uint32_t i;
|
||||
uint32_t dot = 0;
|
||||
|
||||
while (size && dot != 2) {
|
||||
if (s[i] == '.')
|
||||
dot += 1;
|
||||
for (i = 0; i < size; i++) {
|
||||
if (s[i] == '.') {
|
||||
dot++;
|
||||
|
||||
if (dot < 2) {
|
||||
size -= 1;
|
||||
i += 1;
|
||||
if (dot == 2)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
yyunputbytes(size);
|
||||
yyskipbytes(i);
|
||||
|
||||
yylval.nConstValue = (int32_t)(atof(s) * 65536);
|
||||
|
||||
@@ -147,66 +175,124 @@ uint32_t ParseNumber(char *s, uint32_t size)
|
||||
{
|
||||
char dest[256];
|
||||
|
||||
if (size > 255)
|
||||
fatalerror("Number token too long");
|
||||
|
||||
strncpy(dest, s, size);
|
||||
dest[size] = 0;
|
||||
yylval.nConstValue = ascii2bin(dest);
|
||||
|
||||
yyskipbytes(size);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the symbol name ends before the end of the macro arg,
|
||||
* return a pointer to the rest of the macro arg.
|
||||
* Otherwise, return NULL.
|
||||
*/
|
||||
char *AppendMacroArg(char whichArg, char *dest, size_t *destIndex)
|
||||
{
|
||||
char *marg;
|
||||
|
||||
if (whichArg == '@')
|
||||
marg = sym_FindMacroArg(-1);
|
||||
else if (whichArg >= '1' && whichArg <= '9')
|
||||
marg = sym_FindMacroArg(whichArg - '0');
|
||||
else
|
||||
fatalerror("Invalid macro argument '\\%c' in symbol", whichArg);
|
||||
|
||||
if (!marg)
|
||||
fatalerror("Macro argument '\\%c' not defined", whichArg);
|
||||
|
||||
char ch;
|
||||
|
||||
while ((ch = *marg) != 0) {
|
||||
if ((ch >= 'a' && ch <= 'z')
|
||||
|| (ch >= 'A' && ch <= 'Z')
|
||||
|| (ch >= '0' && ch <= '9')
|
||||
|| ch == '_'
|
||||
|| ch == '@'
|
||||
|| ch == '#'
|
||||
|| ch == '.') {
|
||||
if (*destIndex >= MAXSYMLEN)
|
||||
fatalerror("Symbol too long");
|
||||
|
||||
dest[*destIndex] = ch;
|
||||
(*destIndex)++;
|
||||
} else {
|
||||
return marg;
|
||||
}
|
||||
|
||||
marg++;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t ParseSymbol(char *src, uint32_t size)
|
||||
{
|
||||
char dest[MAXSYMLEN + 1];
|
||||
int32_t copied = 0, size_backup = size;
|
||||
size_t srcIndex = 0;
|
||||
size_t destIndex = 0;
|
||||
char *rest = NULL;
|
||||
|
||||
while (size && copied < MAXSYMLEN) {
|
||||
if (*src == '\\') {
|
||||
char *marg;
|
||||
while (srcIndex < size) {
|
||||
char ch = src[srcIndex++];
|
||||
|
||||
src += 1;
|
||||
size -= 1;
|
||||
if (ch == '\\') {
|
||||
/*
|
||||
* We don't check if srcIndex is still less than size,
|
||||
* but that can only fail to be true when the
|
||||
* following char is neither '@' nor a digit.
|
||||
* In that case, AppendMacroArg() will catch the error.
|
||||
*/
|
||||
ch = src[srcIndex++];
|
||||
|
||||
if (*src == '@') {
|
||||
marg = sym_FindMacroArg(-1);
|
||||
} else if (*src >= '0' && *src <= '9') {
|
||||
marg = sym_FindMacroArg(*src - '0');
|
||||
} else {
|
||||
fatalerror("Malformed ID");
|
||||
return 0;
|
||||
}
|
||||
|
||||
src += 1;
|
||||
size -= 1;
|
||||
|
||||
if (marg) {
|
||||
while (*marg)
|
||||
dest[copied++] = *marg++;
|
||||
}
|
||||
rest = AppendMacroArg(ch, dest, &destIndex);
|
||||
/* If the symbol's end was in the middle of the token */
|
||||
if (rest)
|
||||
break;
|
||||
} else {
|
||||
dest[copied++] = *src++;
|
||||
size -= 1;
|
||||
if (destIndex >= MAXSYMLEN)
|
||||
fatalerror("Symbol too long");
|
||||
dest[destIndex++] = ch;
|
||||
}
|
||||
}
|
||||
|
||||
if (copied > MAXSYMLEN)
|
||||
fatalerror("Symbol too long");
|
||||
dest[destIndex] = 0;
|
||||
|
||||
dest[copied] = 0;
|
||||
/* Tell the lexer we read all bytes that we did */
|
||||
yyskipbytes(srcIndex);
|
||||
|
||||
/*
|
||||
* If an escape's expansion left some chars after the symbol's end,
|
||||
* such as the `::` in a `Backup\1` expanded to `BackupCamX::`,
|
||||
* put those into the buffer.
|
||||
* Note that this NEEDS to be done after the `yyskipbytes` above.
|
||||
*/
|
||||
if (rest)
|
||||
yyunputstr(rest);
|
||||
|
||||
/* If the symbol is an EQUS, expand it */
|
||||
if (!oDontExpandStrings && sym_isString(dest)) {
|
||||
char *s;
|
||||
|
||||
yyskipbytes(size_backup);
|
||||
lex_BeginStringExpansion(dest);
|
||||
|
||||
/* Feed the symbol's contents into the buffer */
|
||||
yyunputstr(s = sym_GetStringValue(dest));
|
||||
|
||||
/* Lines inserted this way shall not increase nLineNo */
|
||||
while (*s) {
|
||||
if (*s++ == '\n')
|
||||
nLineNo -= 1;
|
||||
nLineNo--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
strcpy(yylval.tzString, dest);
|
||||
strcpy(yylval.tzSym, dest);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -221,9 +307,9 @@ uint32_t PutMacroArg(char *src, uint32_t size)
|
||||
if (s != NULL)
|
||||
yyunputstr(s);
|
||||
else
|
||||
yyerror("Macro argument not defined");
|
||||
yyerror("Macro argument '\\%c' not defined", src[1]);
|
||||
} else {
|
||||
yyerror("Invalid macro argument");
|
||||
yyerror("Invalid macro argument '\\%c'", src[1]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -401,6 +487,10 @@ const struct sLexInitString lexer_strings[] = {
|
||||
|
||||
{"incbin", T_POP_INCBIN},
|
||||
{"charmap", T_POP_CHARMAP},
|
||||
{"newcharmap", T_POP_NEWCHARMAP},
|
||||
{"setcharmap", T_POP_SETCHARMAP},
|
||||
{"pushc", T_POP_PUSHC},
|
||||
{"popc", T_POP_POPC},
|
||||
|
||||
{"fail", T_POP_FAIL},
|
||||
{"warn", T_POP_WARN},
|
||||
|
||||
311
src/asm/lexer.c
311
src/asm/lexer.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of RGBDS.
|
||||
*
|
||||
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
|
||||
* Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
@@ -38,6 +38,8 @@ struct sLexString {
|
||||
|
||||
#define SAFETYMARGIN 1024
|
||||
|
||||
#define BOM_SIZE 3
|
||||
|
||||
struct sLexFloat tLexFloat[32];
|
||||
struct sLexString *tLexHash[LEXHASHSIZE];
|
||||
YY_BUFFER_STATE pCurrentBuffer;
|
||||
@@ -49,6 +51,12 @@ uint32_t tFloatingChars[256];
|
||||
uint32_t nFloating;
|
||||
enum eLexerState lexerstate = LEX_STATE_NORMAL;
|
||||
|
||||
struct sStringExpansionPos *pCurrentStringExpansion;
|
||||
static unsigned int nNbStringExpansions;
|
||||
|
||||
/* UTF-8 byte order mark */
|
||||
static const unsigned char bom[BOM_SIZE] = { 0xEF, 0xBB, 0xBF };
|
||||
|
||||
void upperstring(char *s)
|
||||
{
|
||||
while (*s) {
|
||||
@@ -83,17 +91,49 @@ void yyunput(char c)
|
||||
*(--pLexBuffer) = c;
|
||||
}
|
||||
|
||||
void yyunputstr(char *s)
|
||||
void yyunputstr(const char *s)
|
||||
{
|
||||
int32_t i, len;
|
||||
int32_t len;
|
||||
|
||||
len = strlen(s);
|
||||
|
||||
if (pLexBuffer - len < pLexBufferRealStart)
|
||||
/*
|
||||
* It would be undefined behavior to subtract `len` from pLexBuffer and
|
||||
* potentially have it point outside of pLexBufferRealStart's buffer,
|
||||
* this is why the check is done this way.
|
||||
* Refer to https://github.com/rednex/rgbds/pull/411#discussion_r319779797
|
||||
*/
|
||||
if (pLexBuffer - pLexBufferRealStart < len)
|
||||
fatalerror("Buffer safety margin exceeded");
|
||||
|
||||
for (i = len - 1; i >= 0; i--)
|
||||
*(--pLexBuffer) = s[i];
|
||||
pLexBuffer -= len;
|
||||
|
||||
memcpy(pLexBuffer, s, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Marks that a new string expansion with name `tzName` ends here
|
||||
* Enforces recursion depth
|
||||
*/
|
||||
void lex_BeginStringExpansion(const char *tzName)
|
||||
{
|
||||
if (++nNbStringExpansions > nMaxRecursionDepth)
|
||||
fatalerror("Recursion limit (%d) exceeded", nMaxRecursionDepth);
|
||||
|
||||
struct sStringExpansionPos *pNewStringExpansion =
|
||||
malloc(sizeof(*pNewStringExpansion));
|
||||
char *tzNewExpansionName = strdup(tzName);
|
||||
|
||||
if (!pNewStringExpansion || !tzNewExpansionName)
|
||||
fatalerror("Could not allocate memory to expand '%s'",
|
||||
tzName);
|
||||
|
||||
pNewStringExpansion->tzName = tzNewExpansionName;
|
||||
pNewStringExpansion->pBuffer = pLexBufferRealStart;
|
||||
pNewStringExpansion->pBufferPos = pLexBuffer;
|
||||
pNewStringExpansion->pParent = pCurrentStringExpansion;
|
||||
|
||||
pCurrentStringExpansion = pNewStringExpansion;
|
||||
}
|
||||
|
||||
void yy_switch_to_buffer(YY_BUFFER_STATE buf)
|
||||
@@ -118,13 +158,34 @@ void yy_delete_buffer(YY_BUFFER_STATE buf)
|
||||
* 2. The buffer is terminated with 0
|
||||
* 3. nBufferSize is the size without the terminator
|
||||
*/
|
||||
static void yy_buffer_append(YY_BUFFER_STATE buf, uint32_t capacity, char c)
|
||||
static void yy_buffer_append(YY_BUFFER_STATE buf, size_t capacity, char c)
|
||||
{
|
||||
assert(buf->pBuffer[buf->nBufferSize] == 0);
|
||||
assert(buf->pBufferStart[buf->nBufferSize] == 0);
|
||||
assert(buf->nBufferSize + 1 < capacity);
|
||||
|
||||
buf->pBuffer[buf->nBufferSize++] = c;
|
||||
buf->pBuffer[buf->nBufferSize] = 0;
|
||||
buf->pBufferStart[buf->nBufferSize++] = c;
|
||||
buf->pBufferStart[buf->nBufferSize] = 0;
|
||||
}
|
||||
|
||||
static void yy_buffer_append_newlines(YY_BUFFER_STATE buf, size_t capacity)
|
||||
{
|
||||
/* Add newline if file doesn't end with one */
|
||||
if (buf->nBufferSize == 0
|
||||
|| buf->pBufferStart[buf->nBufferSize - 1] != '\n')
|
||||
yy_buffer_append(buf, capacity, '\n');
|
||||
|
||||
/* Add newline if \ will eat the last newline */
|
||||
if (buf->nBufferSize >= 2) {
|
||||
size_t pos = buf->nBufferSize - 2;
|
||||
|
||||
/* Skip spaces and tabs */
|
||||
while (pos > 0 && (buf->pBufferStart[pos] == ' '
|
||||
|| buf->pBufferStart[pos] == '\t'))
|
||||
pos--;
|
||||
|
||||
if (buf->pBufferStart[pos] == '\\')
|
||||
yy_buffer_append(buf, capacity, '\n');
|
||||
}
|
||||
}
|
||||
|
||||
YY_BUFFER_STATE yy_scan_bytes(char *mem, uint32_t size)
|
||||
@@ -134,7 +195,9 @@ YY_BUFFER_STATE yy_scan_bytes(char *mem, uint32_t size)
|
||||
if (pBuffer == NULL)
|
||||
fatalerror("%s: Out of memory!", __func__);
|
||||
|
||||
pBuffer->pBufferRealStart = malloc(size + 1 + SAFETYMARGIN);
|
||||
size_t capacity = size + 3; /* space for 2 newlines and terminator */
|
||||
|
||||
pBuffer->pBufferRealStart = malloc(capacity + SAFETYMARGIN);
|
||||
|
||||
if (pBuffer->pBufferRealStart == NULL)
|
||||
fatalerror("%s: Out of memory for buffer!", __func__);
|
||||
@@ -142,9 +205,10 @@ YY_BUFFER_STATE yy_scan_bytes(char *mem, uint32_t size)
|
||||
pBuffer->pBufferStart = pBuffer->pBufferRealStart + SAFETYMARGIN;
|
||||
pBuffer->pBuffer = pBuffer->pBufferRealStart + SAFETYMARGIN;
|
||||
memcpy(pBuffer->pBuffer, mem, size);
|
||||
pBuffer->nBufferSize = size;
|
||||
pBuffer->oAtLineStart = 1;
|
||||
pBuffer->pBuffer[size] = 0;
|
||||
pBuffer->nBufferSize = size;
|
||||
yy_buffer_append_newlines(pBuffer, capacity);
|
||||
pBuffer->oAtLineStart = 1;
|
||||
|
||||
return pBuffer;
|
||||
}
|
||||
@@ -156,71 +220,119 @@ YY_BUFFER_STATE yy_create_buffer(FILE *f)
|
||||
if (pBuffer == NULL)
|
||||
fatalerror("%s: Out of memory!", __func__);
|
||||
|
||||
uint32_t size;
|
||||
size_t size = 0, capacity = -1;
|
||||
char *buf = NULL;
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
size = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
/*
|
||||
* Check if we can get the file size without implementation-defined
|
||||
* behavior:
|
||||
*
|
||||
* From ftell(3p):
|
||||
* [On error], ftell() and ftello() shall return −1, and set errno to
|
||||
* indicate the error.
|
||||
*
|
||||
* The ftell() and ftello() functions shall fail if: [...]
|
||||
* ESPIPE The file descriptor underlying stream is associated with a
|
||||
* pipe, FIFO, or socket.
|
||||
*
|
||||
* From fseek(3p):
|
||||
* The behavior of fseek() on devices which are incapable of seeking
|
||||
* is implementation-defined.
|
||||
*/
|
||||
if (ftell(f) != -1) {
|
||||
fseek(f, 0, SEEK_END);
|
||||
capacity = ftell(f);
|
||||
rewind(f);
|
||||
}
|
||||
|
||||
/* Give extra room for 2 newlines and terminator */
|
||||
uint32_t capacity = size + 3;
|
||||
// If ftell errored or the block above wasn't executed
|
||||
if (capacity == -1)
|
||||
capacity = 4096;
|
||||
// Handle 0-byte files gracefully
|
||||
else if (capacity == 0)
|
||||
capacity = 1;
|
||||
|
||||
pBuffer->pBufferRealStart = malloc(capacity + SAFETYMARGIN);
|
||||
while (!feof(f)) {
|
||||
if (buf == NULL || size >= capacity) {
|
||||
if (buf)
|
||||
capacity *= 2;
|
||||
/* Give extra room for 2 newlines and terminator */
|
||||
buf = realloc(buf, capacity + SAFETYMARGIN + 3);
|
||||
|
||||
if (pBuffer->pBufferRealStart == NULL)
|
||||
fatalerror("%s: Out of memory for buffer!", __func__);
|
||||
if (buf == NULL)
|
||||
fatalerror("%s: Out of memory for buffer!",
|
||||
__func__);
|
||||
}
|
||||
|
||||
pBuffer->pBufferStart = pBuffer->pBufferRealStart + SAFETYMARGIN;
|
||||
pBuffer->pBuffer = pBuffer->pBufferRealStart + SAFETYMARGIN;
|
||||
char *bufpos = buf + SAFETYMARGIN + size;
|
||||
size_t read_count = fread(bufpos, 1, capacity - size, f);
|
||||
|
||||
size = fread(pBuffer->pBuffer, sizeof(uint8_t), size, f);
|
||||
if (read_count == 0 && !feof(f))
|
||||
fatalerror("%s: fread error", __func__);
|
||||
|
||||
size += read_count;
|
||||
}
|
||||
|
||||
pBuffer->pBufferRealStart = buf;
|
||||
pBuffer->pBufferStart = buf + SAFETYMARGIN;
|
||||
pBuffer->pBuffer = buf + SAFETYMARGIN;
|
||||
pBuffer->pBuffer[size] = 0;
|
||||
pBuffer->nBufferSize = size;
|
||||
|
||||
/* This is added here to make the buffer scaling above easy to express,
|
||||
* while taking the newline space into account
|
||||
* for the yy_buffer_append_newlines() call below.
|
||||
*/
|
||||
capacity += 3;
|
||||
|
||||
/* Skip UTF-8 byte order mark. */
|
||||
if (pBuffer->nBufferSize >= BOM_SIZE
|
||||
&& !memcmp(pBuffer->pBuffer, bom, BOM_SIZE))
|
||||
pBuffer->pBuffer += BOM_SIZE;
|
||||
|
||||
/* Convert all line endings to LF and spaces */
|
||||
|
||||
char *mem = pBuffer->pBuffer;
|
||||
uint32_t instring = 0;
|
||||
int32_t lineCount = 0;
|
||||
|
||||
while (*mem) {
|
||||
if (*mem == '\"')
|
||||
instring = 1 - instring;
|
||||
|
||||
if ((mem[0] == '\\') && (mem[1] == '\"' || mem[1] == '\\')) {
|
||||
mem += 2;
|
||||
} else if (instring) {
|
||||
mem += 1;
|
||||
} else {
|
||||
/* LF CR and CR LF */
|
||||
if (((mem[0] == 10) && (mem[1] == 13))
|
||||
|| ((mem[0] == 13) && (mem[1] == 10))) {
|
||||
mem[0] = ' ';
|
||||
mem[1] = '\n';
|
||||
mem += 2;
|
||||
if (((mem[0] == '\n') && (mem[1] == '\r'))
|
||||
|| ((mem[0] == '\r') && (mem[1] == '\n'))) {
|
||||
*mem++ = ' ';
|
||||
*mem++ = '\n';
|
||||
lineCount++;
|
||||
/* LF and CR */
|
||||
} else if ((mem[0] == 10) || (mem[0] == 13)) {
|
||||
mem[0] = '\n';
|
||||
mem += 1;
|
||||
} else if ((mem[0] == '\n') || (mem[0] == '\r')) {
|
||||
*mem++ = '\n';
|
||||
lineCount++;
|
||||
} else {
|
||||
mem += 1;
|
||||
mem++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mem != pBuffer->pBuffer + size) {
|
||||
nLineNo = lineCount + 1;
|
||||
fatalerror("Found null character");
|
||||
}
|
||||
|
||||
/* Remove comments */
|
||||
|
||||
mem = pBuffer->pBuffer;
|
||||
instring = 0;
|
||||
bool instring = false;
|
||||
|
||||
while (*mem) {
|
||||
if (*mem == '\"')
|
||||
instring = 1 - instring;
|
||||
instring = !instring;
|
||||
|
||||
if ((mem[0] == '\\') && (mem[1] == '\"' || mem[1] == '\\')) {
|
||||
mem += 2;
|
||||
} else if (instring) {
|
||||
mem += 1;
|
||||
mem++;
|
||||
} else {
|
||||
/* Comments that start with ; anywhere in a line */
|
||||
if (*mem == ';') {
|
||||
@@ -228,31 +340,16 @@ YY_BUFFER_STATE yy_create_buffer(FILE *f)
|
||||
*mem++ = ' ';
|
||||
/* Comments that start with * at the start of a line */
|
||||
} else if ((mem[0] == '\n') && (mem[1] == '*')) {
|
||||
mem += 1;
|
||||
mem++;
|
||||
while (!((*mem == '\n') || (*mem == '\0')))
|
||||
*mem++ = ' ';
|
||||
} else {
|
||||
mem += 1;
|
||||
mem++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Add newline if file doesn't end with one */
|
||||
if (size == 0 || pBuffer->pBuffer[size - 1] != '\n')
|
||||
yy_buffer_append(pBuffer, capacity, '\n');
|
||||
|
||||
/* Add newline if \ will eat the last newline */
|
||||
if (pBuffer->nBufferSize >= 2) {
|
||||
size_t pos = pBuffer->nBufferSize - 2;
|
||||
|
||||
/* Skip spaces */
|
||||
while (pos > 0 && pBuffer->pBuffer[pos] == ' ')
|
||||
pos--;
|
||||
|
||||
if (pBuffer->pBuffer[pos] == '\\')
|
||||
yy_buffer_append(pBuffer, capacity, '\n');
|
||||
}
|
||||
|
||||
yy_buffer_append_newlines(pBuffer, capacity);
|
||||
pBuffer->oAtLineStart = 1;
|
||||
return pBuffer;
|
||||
}
|
||||
@@ -376,6 +473,9 @@ void lex_Init(void)
|
||||
|
||||
nLexMaxLength = 0;
|
||||
nFloating = 0;
|
||||
|
||||
pCurrentStringExpansion = NULL;
|
||||
nNbStringExpansions = 0;
|
||||
}
|
||||
|
||||
void lex_AddStrings(const struct sLexInitString *lex)
|
||||
@@ -428,17 +528,17 @@ void yylex_GetFloatMaskAndFloatLen(uint32_t *pnFloatMask, uint32_t *pnFloatLen)
|
||||
|
||||
char *s = pLexBuffer;
|
||||
uint32_t nOldFloatMask = 0;
|
||||
uint32_t nFloatMask = tFloatingFirstChar[(int32_t)*s];
|
||||
uint32_t nFloatMask = tFloatingFirstChar[(uint8_t)*s];
|
||||
|
||||
if (nFloatMask != 0) {
|
||||
s++;
|
||||
nOldFloatMask = nFloatMask;
|
||||
nFloatMask &= tFloatingSecondChar[(int32_t)*s];
|
||||
nFloatMask &= tFloatingSecondChar[(uint8_t)*s];
|
||||
|
||||
while (nFloatMask != 0) {
|
||||
s++;
|
||||
nOldFloatMask = nFloatMask;
|
||||
nFloatMask &= tFloatingChars[(int32_t)*s];
|
||||
nFloatMask &= tFloatingChars[(uint8_t)*s];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,6 +652,7 @@ size_t yylex_ReadBracketedSymbol(char *dest, size_t index)
|
||||
char ch;
|
||||
size_t i = 0;
|
||||
size_t length, maxLength;
|
||||
const char *mode = NULL;
|
||||
|
||||
for (ch = *pLexBuffer;
|
||||
ch != '}' && ch != '"' && ch != '\n';
|
||||
@@ -565,16 +666,42 @@ size_t yylex_ReadBracketedSymbol(char *dest, size_t index)
|
||||
i += length;
|
||||
else
|
||||
fatalerror("Illegal character escape '%c'", ch);
|
||||
} else if (ch == ':' && !mode) { /* Only grab 1st colon */
|
||||
/* Use a whitelist of modes, which does prevent the
|
||||
* use of some features such as precision,
|
||||
* but also avoids a security flaw
|
||||
*/
|
||||
const char *acceptedModes = "bxXd";
|
||||
/* Binary isn't natively supported,
|
||||
* so it's handled differently
|
||||
*/
|
||||
static const char * const formatSpecifiers[] = {
|
||||
"", "%x", "%X", "%d"
|
||||
};
|
||||
/* Prevent reading out of bounds! */
|
||||
const char *designatedMode;
|
||||
|
||||
if (i != 1)
|
||||
fatalerror("Print types are exactly 1 character long");
|
||||
|
||||
designatedMode = strchr(acceptedModes, sym[i - 1]);
|
||||
if (!designatedMode)
|
||||
fatalerror("Illegal print type '%c'",
|
||||
sym[i - 1]);
|
||||
mode = formatSpecifiers[designatedMode - acceptedModes];
|
||||
/* Begin writing the symbol again */
|
||||
i = 0;
|
||||
} else {
|
||||
yylex_SymbolWriteChar(sym, i++, ch);
|
||||
}
|
||||
}
|
||||
|
||||
/* Properly terminate the string */
|
||||
yylex_SymbolWriteChar(sym, i, 0);
|
||||
|
||||
/* It's assumed we're writing to a T_STRING */
|
||||
maxLength = MAXSTRLEN - index;
|
||||
length = symvaluetostring(&dest[index], maxLength, sym);
|
||||
length = symvaluetostring(&dest[index], maxLength, sym, mode);
|
||||
|
||||
if (*pLexBuffer == '}')
|
||||
pLexBuffer++;
|
||||
@@ -681,10 +808,10 @@ scanagain:
|
||||
* endings: "\r\n" is replaced by " \n" before the lexer has the
|
||||
* opportunity to see it.
|
||||
*/
|
||||
if (pLexBuffer[1] == ' ') {
|
||||
if (pLexBuffer[1] == ' ' || pLexBuffer[1] == '\t') {
|
||||
pLexBuffer += 2;
|
||||
while (1) {
|
||||
if (*pLexBuffer == ' ') {
|
||||
if (*pLexBuffer == ' ' || *pLexBuffer == '\t') {
|
||||
pLexBuffer++;
|
||||
} else if (*pLexBuffer == '\n') {
|
||||
pLexBuffer++;
|
||||
@@ -731,7 +858,9 @@ scanagain:
|
||||
return T_STRING;
|
||||
} else if (*pLexBuffer == '{') {
|
||||
pLexBuffer++;
|
||||
yylex_ReadBracketedSymbol(yylval.tzString, 0);
|
||||
size_t len = yylex_ReadBracketedSymbol(yylval.tzString,
|
||||
0);
|
||||
yylval.tzString[len] = 0;
|
||||
return T_STRING;
|
||||
}
|
||||
|
||||
@@ -740,10 +869,22 @@ scanagain:
|
||||
* numeric literal, string, or bracketed symbol, so just return
|
||||
* the ASCII character.
|
||||
*/
|
||||
if (*pLexBuffer == '\n')
|
||||
unsigned char ch = *pLexBuffer++;
|
||||
|
||||
if (ch == '\n')
|
||||
AtLineStart = 1;
|
||||
|
||||
return *pLexBuffer++;
|
||||
/*
|
||||
* Check for invalid unprintable characters.
|
||||
* They may not be readily apparent in a text editor,
|
||||
* so this is useful for identifying encoding problems.
|
||||
*/
|
||||
if (ch != 0
|
||||
&& ch != '\n'
|
||||
&& !(ch >= 0x20 && ch <= 0x7E))
|
||||
fatalerror("Found garbage character: 0x%02X", ch);
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
if (pLongestFixed == NULL || nFloatLen > pLongestFixed->nNameLength) {
|
||||
@@ -760,8 +901,6 @@ scanagain:
|
||||
goto scanagain;
|
||||
}
|
||||
|
||||
pLexBuffer += nFloatLen;
|
||||
|
||||
if (token->nToken == T_ID && linestart)
|
||||
return T_LABEL;
|
||||
else
|
||||
@@ -811,6 +950,7 @@ static uint32_t yylex_MACROARGS(void)
|
||||
ch = '}';
|
||||
break;
|
||||
case ' ':
|
||||
case '\t':
|
||||
/*
|
||||
* Look for line continuation character after a
|
||||
* series of spaces. This is also useful for
|
||||
@@ -819,7 +959,8 @@ static uint32_t yylex_MACROARGS(void)
|
||||
* opportunity to see it.
|
||||
*/
|
||||
while (1) {
|
||||
if (*pLexBuffer == ' ') {
|
||||
if (*pLexBuffer == ' '
|
||||
|| *pLexBuffer == '\t') {
|
||||
pLexBuffer++;
|
||||
} else if (*pLexBuffer == '\n') {
|
||||
pLexBuffer++;
|
||||
@@ -879,14 +1020,32 @@ static uint32_t yylex_MACROARGS(void)
|
||||
fatalerror("Internal error in %s", __func__);
|
||||
}
|
||||
|
||||
uint32_t yylex(void)
|
||||
int yylex(void)
|
||||
{
|
||||
int returnedChar;
|
||||
switch (lexerstate) {
|
||||
case LEX_STATE_NORMAL:
|
||||
return yylex_NORMAL();
|
||||
returnedChar = yylex_NORMAL();
|
||||
break;
|
||||
case LEX_STATE_MACROARGS:
|
||||
return yylex_MACROARGS();
|
||||
returnedChar = yylex_MACROARGS();
|
||||
break;
|
||||
default:
|
||||
fatalerror("%s: Internal error.", __func__);
|
||||
}
|
||||
|
||||
/* Check if string expansions were fully read */
|
||||
while (pCurrentStringExpansion
|
||||
&& pCurrentStringExpansion->pBuffer == pLexBufferRealStart
|
||||
&& pCurrentStringExpansion->pBufferPos <= pLexBuffer) {
|
||||
struct sStringExpansionPos *pParent =
|
||||
pCurrentStringExpansion->pParent;
|
||||
free(pCurrentStringExpansion->tzName);
|
||||
free(pCurrentStringExpansion);
|
||||
|
||||
pCurrentStringExpansion = pParent;
|
||||
nNbStringExpansions--;
|
||||
}
|
||||
|
||||
return returnedChar;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "asm/lexer.h"
|
||||
#include "asm/output.h"
|
||||
#include "asm/main.h"
|
||||
#include "asm/charmap.h"
|
||||
|
||||
#include "extern/err.h"
|
||||
|
||||
@@ -37,7 +38,7 @@ char **cldefines;
|
||||
|
||||
clock_t nStartClock, nEndClock;
|
||||
int32_t nLineNo;
|
||||
uint32_t nTotalLines, nPass, nPC, nIFDepth, nUnionDepth, nErrors;
|
||||
uint32_t nTotalLines, nPC, nIFDepth, nUnionDepth, nErrors;
|
||||
bool skipElif;
|
||||
uint32_t unionStart[128], unionSize[128];
|
||||
|
||||
@@ -238,12 +239,13 @@ static void opt_ParseDefines(void)
|
||||
*/
|
||||
void verror(const char *fmt, va_list args)
|
||||
{
|
||||
fprintf(stderr, "ERROR: ");
|
||||
fputs("ERROR: ", stderr);
|
||||
fstk_Dump();
|
||||
fprintf(stderr, ":\n ");
|
||||
fputs(":\n ", stderr);
|
||||
vfprintf(stderr, fmt, args);
|
||||
fprintf(stderr, "\n");
|
||||
nErrors += 1;
|
||||
fputc('\n', stderr);
|
||||
fstk_DumpStringExpansions();
|
||||
nErrors++;
|
||||
}
|
||||
|
||||
void yyerror(const char *fmt, ...)
|
||||
@@ -275,11 +277,12 @@ void warning(const char *fmt, ...)
|
||||
|
||||
va_start(args, fmt);
|
||||
|
||||
fprintf(stderr, "warning: ");
|
||||
fputs("warning: ", stderr);
|
||||
fstk_Dump();
|
||||
fprintf(stderr, ":\n ");
|
||||
fputs(":\n ", stderr);
|
||||
vfprintf(stderr, fmt, args);
|
||||
fprintf(stderr, "\n");
|
||||
fputc('\n', stderr);
|
||||
fstk_DumpStringExpansions();
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
@@ -288,7 +291,8 @@ static void print_usage(void)
|
||||
{
|
||||
printf(
|
||||
"usage: rgbasm [-EhLVvw] [-b chars] [-Dname[=value]] [-g chars] [-i path]\n"
|
||||
" [-M dependfile] [-o outfile] [-p pad_value] file.asm\n");
|
||||
" [-M dependfile] [-o outfile] [-p pad_value]\n"
|
||||
" [-r recursion_depth] file.asm\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -315,6 +319,8 @@ int main(int argc, char *argv[])
|
||||
|
||||
/* yydebug=1; */
|
||||
|
||||
nMaxRecursionDepth = 64;
|
||||
|
||||
DefaultOptions.gbgfx[0] = '0';
|
||||
DefaultOptions.gbgfx[1] = '1';
|
||||
DefaultOptions.gbgfx[2] = '2';
|
||||
@@ -332,7 +338,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
newopt = CurrentOptions;
|
||||
|
||||
while ((ch = getopt(argc, argv, "b:D:Eg:hi:LM:o:p:Vvw")) != -1) {
|
||||
while ((ch = getopt(argc, argv, "b:D:Eg:hi:LM:o:p:r:Vvw")) != -1) {
|
||||
switch (ch) {
|
||||
case 'b':
|
||||
if (strlen(optarg) == 2) {
|
||||
@@ -386,6 +392,12 @@ int main(int argc, char *argv[])
|
||||
errx(1, "Argument for option 'p' must be between 0 and 0xFF");
|
||||
|
||||
break;
|
||||
case 'r':
|
||||
nMaxRecursionDepth = strtoul(optarg, &ep, 0);
|
||||
|
||||
if (optarg[0] == '\0' || *ep != '\0')
|
||||
errx(1, "Invalid argument for option 'r'");
|
||||
break;
|
||||
case 'V':
|
||||
printf("rgbasm %s\n", get_package_version_string());
|
||||
exit(0);
|
||||
@@ -432,21 +444,18 @@ int main(int argc, char *argv[])
|
||||
skipElif = true;
|
||||
nUnionDepth = 0;
|
||||
nPC = 0;
|
||||
nPass = 1;
|
||||
nErrors = 0;
|
||||
sym_PrepPass1();
|
||||
sym_Init();
|
||||
sym_SetExportAll(CurrentOptions.exportall);
|
||||
fstk_Init(tzMainfile);
|
||||
opt_ParseDefines();
|
||||
|
||||
if (CurrentOptions.verbose)
|
||||
printf("Pass 1...\n");
|
||||
charmap_InitMain();
|
||||
|
||||
yy_set_state(LEX_STATE_NORMAL);
|
||||
opt_SetCurrentOptions(&DefaultOptions);
|
||||
|
||||
if (yyparse() != 0 || nErrors != 0)
|
||||
errx(1, "Assembly aborted in pass 1 (%ld errors)!", nErrors);
|
||||
errx(1, "Assembly aborted (%ld errors)!", nErrors);
|
||||
|
||||
if (nIFDepth != 0)
|
||||
errx(1, "Unterminated IF construct (%ld levels)!", nIFDepth);
|
||||
@@ -456,27 +465,6 @@ int main(int argc, char *argv[])
|
||||
nUnionDepth);
|
||||
}
|
||||
|
||||
nTotalLines = 0;
|
||||
nLineNo = 1;
|
||||
nIFDepth = 0;
|
||||
skipElif = true;
|
||||
nUnionDepth = 0;
|
||||
nPC = 0;
|
||||
nPass = 2;
|
||||
nErrors = 0;
|
||||
sym_PrepPass2();
|
||||
out_PrepPass2();
|
||||
fstk_Init(tzMainfile);
|
||||
yy_set_state(LEX_STATE_NORMAL);
|
||||
opt_SetCurrentOptions(&DefaultOptions);
|
||||
opt_ParseDefines();
|
||||
|
||||
if (CurrentOptions.verbose)
|
||||
printf("Pass 2...\n");
|
||||
|
||||
if (yyparse() != 0 || nErrors != 0)
|
||||
errx(1, "Assembly aborted in pass 2 (%ld errors)!", nErrors);
|
||||
|
||||
double timespent;
|
||||
|
||||
nEndClock = clock();
|
||||
@@ -491,6 +479,10 @@ int main(int argc, char *argv[])
|
||||
printf("(%d lines/minute)\n",
|
||||
(int)(60 / timespent * nTotalLines));
|
||||
}
|
||||
out_WriteObject();
|
||||
|
||||
out_CheckErrors();
|
||||
/* If no path specified, don't write file */
|
||||
if (tzObjectname != NULL)
|
||||
out_WriteObject();
|
||||
return 0;
|
||||
}
|
||||
|
||||
222
src/asm/output.c
222
src/asm/output.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of RGBDS.
|
||||
*
|
||||
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
|
||||
* Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
@@ -10,6 +10,7 @@
|
||||
* Outputs an objectfile
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
@@ -259,37 +260,37 @@ static void writesection(struct Section *pSect, FILE *f)
|
||||
*/
|
||||
static void writesymbol(struct sSymbol *pSym, FILE *f)
|
||||
{
|
||||
char symname[MAXSYMLEN * 2 + 1];
|
||||
uint32_t type;
|
||||
uint32_t offset;
|
||||
int32_t sectid;
|
||||
|
||||
if (pSym->nType & SYMF_IMPORT) {
|
||||
/* Symbol should be imported */
|
||||
strcpy(symname, pSym->tzName);
|
||||
offset = 0;
|
||||
sectid = -1;
|
||||
if (!(pSym->nType & SYMF_DEFINED)) {
|
||||
type = SYM_IMPORT;
|
||||
} else if (pSym->nType & SYMF_EXPORT) {
|
||||
type = SYM_EXPORT;
|
||||
} else {
|
||||
strcpy(symname, pSym->tzName);
|
||||
|
||||
if (pSym->nType & SYMF_EXPORT) {
|
||||
/* Symbol should be exported */
|
||||
type = SYM_EXPORT;
|
||||
offset = pSym->nValue;
|
||||
if (pSym->nType & SYMF_CONST)
|
||||
sectid = -1;
|
||||
else
|
||||
sectid = getsectid(pSym->pSection);
|
||||
} else {
|
||||
/* Symbol is local to this file */
|
||||
type = SYM_LOCAL;
|
||||
offset = pSym->nValue;
|
||||
sectid = getsectid(pSym->pSection);
|
||||
}
|
||||
type = SYM_LOCAL;
|
||||
}
|
||||
|
||||
fputstring(symname, f);
|
||||
switch (type) {
|
||||
case SYM_LOCAL:
|
||||
offset = pSym->nValue;
|
||||
sectid = getsectid(pSym->pSection);
|
||||
break;
|
||||
case SYM_IMPORT:
|
||||
offset = 0;
|
||||
sectid = -1;
|
||||
break;
|
||||
case SYM_EXPORT:
|
||||
offset = pSym->nValue;
|
||||
if (pSym->nType & SYMF_CONST)
|
||||
sectid = -1;
|
||||
else
|
||||
sectid = getsectid(pSym->pSection);
|
||||
break;
|
||||
}
|
||||
|
||||
fputstring(pSym->tzName, f);
|
||||
fputc(type, f);
|
||||
|
||||
if (type != SYM_IMPORT) {
|
||||
@@ -311,7 +312,7 @@ static uint32_t addsymbol(struct sSymbol *pSym)
|
||||
struct PatchSymbol *pPSym, **ppPSym;
|
||||
uint32_t hash;
|
||||
|
||||
hash = calchash(pSym->tzName);
|
||||
hash = sym_CalcHash(pSym->tzName);
|
||||
ppPSym = &(tHashedPatchSymbols[hash]);
|
||||
|
||||
while ((*ppPSym) != NULL) {
|
||||
@@ -383,10 +384,15 @@ void createpatch(uint32_t type, struct Expression *expr)
|
||||
{
|
||||
struct Patch *pPatch;
|
||||
uint16_t rpndata;
|
||||
uint8_t rpnexpr[2048];
|
||||
uint8_t *rpnexpr;
|
||||
char tzSym[512];
|
||||
uint32_t rpnptr = 0, symptr;
|
||||
|
||||
rpnexpr = malloc(expr->nRPNPatchSize);
|
||||
|
||||
if (rpnexpr == NULL)
|
||||
fatalerror("No memory for patch RPN expression");
|
||||
|
||||
pPatch = allocpatch();
|
||||
pPatch->nType = type;
|
||||
strcpy(pPatch->tzFilename, tzCurrentFileName);
|
||||
@@ -468,11 +474,10 @@ void createpatch(uint32_t type, struct Expression *expr)
|
||||
}
|
||||
}
|
||||
|
||||
pPatch->pRPN = malloc(rpnptr);
|
||||
if (pPatch->pRPN != NULL) {
|
||||
memcpy(pPatch->pRPN, rpnexpr, rpnptr);
|
||||
pPatch->nRPNSize = rpnptr;
|
||||
}
|
||||
assert(rpnptr == expr->nRPNPatchSize);
|
||||
|
||||
pPatch->pRPN = rpnexpr;
|
||||
pPatch->nRPNSize = rpnptr;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -507,8 +512,9 @@ static void checksectionoverflow(uint32_t delta_size)
|
||||
{
|
||||
uint32_t maxsize = getmaxsectionsize(pCurrentSection->nType,
|
||||
pCurrentSection->pzName);
|
||||
uint32_t new_size = pCurrentSection->nPC + delta_size;
|
||||
|
||||
if (pCurrentSection->nPC + delta_size > maxsize) {
|
||||
if (new_size > maxsize) {
|
||||
/*
|
||||
* This check is here to trap broken code that generates
|
||||
* sections that are too big and to prevent the assembler from
|
||||
@@ -516,8 +522,36 @@ static void checksectionoverflow(uint32_t delta_size)
|
||||
* memory.
|
||||
* The real check must be done at the linking stage.
|
||||
*/
|
||||
fatalerror("Section '%s' is too big (max size = 0x%X bytes).",
|
||||
pCurrentSection->pzName, maxsize);
|
||||
fatalerror("Section '%s' is too big (max size = 0x%X bytes, reached 0x%X).",
|
||||
pCurrentSection->pzName, maxsize, new_size);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for errors that could happen while writing an object file
|
||||
* This is important as out_WriteObject is skipped entirely when `-o` is omitted
|
||||
* Therefore, errors such as memory allocations still should be handled in
|
||||
* out_WriteObject and not here
|
||||
*/
|
||||
void out_CheckErrors(void)
|
||||
{
|
||||
/* Local symbols cannot be imported from elsewhere */
|
||||
struct PatchSymbol *pSym = pPatchSymbols;
|
||||
|
||||
while (pSym) {
|
||||
struct sSymbol *pSymbol = pSym->pSymbol;
|
||||
|
||||
if (!(pSymbol->nType & SYMF_DEFINED)
|
||||
&& pSymbol->nType & SYMF_LOCAL) {
|
||||
char *name = pSymbol->tzName;
|
||||
char *localPtr = strchr(name, '.');
|
||||
|
||||
if (localPtr)
|
||||
name = localPtr;
|
||||
errx(1, "%s(%u) : '%s' not defined",
|
||||
pSymbol->tzFileName, pSymbol->nFileLine, name);
|
||||
}
|
||||
pSym = pSym->pNext;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,20 +561,15 @@ static void checksectionoverflow(uint32_t delta_size)
|
||||
void out_WriteObject(void)
|
||||
{
|
||||
FILE *f;
|
||||
struct PatchSymbol *pSym;
|
||||
struct Section *pSect;
|
||||
|
||||
addexports();
|
||||
|
||||
/* If no path specified, don't write file */
|
||||
if (tzObjectname == NULL)
|
||||
return;
|
||||
|
||||
f = fopen(tzObjectname, "wb");
|
||||
if (f == NULL)
|
||||
fatalerror("Couldn't write file '%s'\n", tzObjectname);
|
||||
|
||||
struct PatchSymbol *pSym;
|
||||
struct Section *pSect;
|
||||
|
||||
fwrite(RGBDS_OBJECT_VERSION_STRING, 1,
|
||||
strlen(RGBDS_OBJECT_VERSION_STRING), f);
|
||||
|
||||
@@ -562,22 +591,6 @@ void out_WriteObject(void)
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare for pass #2
|
||||
*/
|
||||
void out_PrepPass2(void)
|
||||
{
|
||||
struct Section *pSect;
|
||||
|
||||
pSect = pSectionList;
|
||||
while (pSect) {
|
||||
pSect->nPC = 0;
|
||||
pSect = pSect->pNext;
|
||||
}
|
||||
pCurrentSection = NULL;
|
||||
pSectionStack = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the objectfilename
|
||||
*/
|
||||
@@ -586,10 +599,6 @@ void out_SetFileName(char *s)
|
||||
tzObjectname = s;
|
||||
if (CurrentOptions.verbose)
|
||||
printf("Output filename %s\n", s);
|
||||
|
||||
pSectionList = NULL;
|
||||
pCurrentSection = NULL;
|
||||
pPatchSymbols = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -636,7 +645,6 @@ struct Section *out_FindSection(char *pzName, uint32_t secttype, int32_t org,
|
||||
pSect->pNext = NULL;
|
||||
pSect->pPatches = NULL;
|
||||
pSect->charmap = NULL;
|
||||
pPatchSymbols = NULL;
|
||||
|
||||
/* It is only needed to allocate memory for ROM sections. */
|
||||
if (secttype == SECT_ROM0 || secttype == SECT_ROMX) {
|
||||
@@ -662,7 +670,7 @@ void out_SetCurrentSection(struct Section *pSect)
|
||||
fatalerror("Cannot change the section within a UNION");
|
||||
|
||||
pCurrentSection = pSect;
|
||||
nPC = pSect->nPC;
|
||||
nPC = (pSect != NULL) ? pSect->nPC : 0;
|
||||
|
||||
pPCSymbol->nValue = nPC;
|
||||
pPCSymbol->pSection = pCurrentSection;
|
||||
@@ -705,9 +713,7 @@ void out_AbsByteBypassCheck(int32_t b)
|
||||
{
|
||||
checksectionoverflow(1);
|
||||
b &= 0xFF;
|
||||
if (nPass == 2)
|
||||
pCurrentSection->tData[nPC] = b;
|
||||
|
||||
pCurrentSection->tData[nPC] = b;
|
||||
pCurrentSection->nPC += 1;
|
||||
nPC += 1;
|
||||
pPCSymbol->nValue += 1;
|
||||
@@ -772,17 +778,15 @@ void out_RelByte(struct Expression *expr)
|
||||
checkcodesection();
|
||||
checksectionoverflow(1);
|
||||
if (rpn_isReloc(expr)) {
|
||||
if (nPass == 2) {
|
||||
pCurrentSection->tData[nPC] = 0;
|
||||
createpatch(PATCH_BYTE, expr);
|
||||
}
|
||||
pCurrentSection->tData[nPC] = 0;
|
||||
createpatch(PATCH_BYTE, expr);
|
||||
pCurrentSection->nPC += 1;
|
||||
nPC += 1;
|
||||
pPCSymbol->nValue += 1;
|
||||
} else {
|
||||
out_AbsByte(expr->nVal);
|
||||
}
|
||||
rpn_Reset(expr);
|
||||
rpn_Free(expr);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -793,10 +797,8 @@ void out_AbsWord(int32_t b)
|
||||
checkcodesection();
|
||||
checksectionoverflow(2);
|
||||
b &= 0xFFFF;
|
||||
if (nPass == 2) {
|
||||
pCurrentSection->tData[nPC] = b & 0xFF;
|
||||
pCurrentSection->tData[nPC + 1] = b >> 8;
|
||||
}
|
||||
pCurrentSection->tData[nPC] = b & 0xFF;
|
||||
pCurrentSection->tData[nPC + 1] = b >> 8;
|
||||
pCurrentSection->nPC += 2;
|
||||
nPC += 2;
|
||||
pPCSymbol->nValue += 2;
|
||||
@@ -808,24 +810,19 @@ void out_AbsWord(int32_t b)
|
||||
*/
|
||||
void out_RelWord(struct Expression *expr)
|
||||
{
|
||||
uint32_t b;
|
||||
|
||||
checkcodesection();
|
||||
checksectionoverflow(2);
|
||||
b = expr->nVal & 0xFFFF;
|
||||
if (rpn_isReloc(expr)) {
|
||||
if (nPass == 2) {
|
||||
pCurrentSection->tData[nPC] = b & 0xFF;
|
||||
pCurrentSection->tData[nPC + 1] = b >> 8;
|
||||
createpatch(PATCH_WORD_L, expr);
|
||||
}
|
||||
pCurrentSection->tData[nPC] = 0;
|
||||
pCurrentSection->tData[nPC + 1] = 0;
|
||||
createpatch(PATCH_WORD_L, expr);
|
||||
pCurrentSection->nPC += 2;
|
||||
nPC += 2;
|
||||
pPCSymbol->nValue += 2;
|
||||
} else {
|
||||
out_AbsWord(expr->nVal);
|
||||
}
|
||||
rpn_Reset(expr);
|
||||
rpn_Free(expr);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -835,12 +832,10 @@ void out_AbsLong(int32_t b)
|
||||
{
|
||||
checkcodesection();
|
||||
checksectionoverflow(sizeof(int32_t));
|
||||
if (nPass == 2) {
|
||||
pCurrentSection->tData[nPC] = b & 0xFF;
|
||||
pCurrentSection->tData[nPC + 1] = b >> 8;
|
||||
pCurrentSection->tData[nPC + 2] = b >> 16;
|
||||
pCurrentSection->tData[nPC + 3] = b >> 24;
|
||||
}
|
||||
pCurrentSection->tData[nPC] = b & 0xFF;
|
||||
pCurrentSection->tData[nPC + 1] = b >> 8;
|
||||
pCurrentSection->tData[nPC + 2] = b >> 16;
|
||||
pCurrentSection->tData[nPC + 3] = b >> 24;
|
||||
pCurrentSection->nPC += 4;
|
||||
nPC += 4;
|
||||
pPCSymbol->nValue += 4;
|
||||
@@ -852,26 +847,21 @@ void out_AbsLong(int32_t b)
|
||||
*/
|
||||
void out_RelLong(struct Expression *expr)
|
||||
{
|
||||
int32_t b;
|
||||
|
||||
checkcodesection();
|
||||
checksectionoverflow(4);
|
||||
b = expr->nVal;
|
||||
if (rpn_isReloc(expr)) {
|
||||
if (nPass == 2) {
|
||||
pCurrentSection->tData[nPC] = b & 0xFF;
|
||||
pCurrentSection->tData[nPC + 1] = b >> 8;
|
||||
pCurrentSection->tData[nPC + 2] = b >> 16;
|
||||
pCurrentSection->tData[nPC + 3] = b >> 24;
|
||||
createpatch(PATCH_LONG_L, expr);
|
||||
}
|
||||
pCurrentSection->tData[nPC] = 0;
|
||||
pCurrentSection->tData[nPC + 1] = 0;
|
||||
pCurrentSection->tData[nPC + 2] = 0;
|
||||
pCurrentSection->tData[nPC + 3] = 0;
|
||||
createpatch(PATCH_LONG_L, expr);
|
||||
pCurrentSection->nPC += 4;
|
||||
nPC += 4;
|
||||
pPCSymbol->nValue += 4;
|
||||
} else {
|
||||
out_AbsLong(expr->nVal);
|
||||
}
|
||||
rpn_Reset(expr);
|
||||
rpn_Free(expr);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -884,15 +874,13 @@ void out_PCRelByte(struct Expression *expr)
|
||||
checksectionoverflow(1);
|
||||
|
||||
/* Always let the linker calculate the offset. */
|
||||
if (nPass == 2) {
|
||||
pCurrentSection->tData[nPC] = 0;
|
||||
createpatch(PATCH_BYTE_JR, expr);
|
||||
}
|
||||
pCurrentSection->tData[nPC] = 0;
|
||||
createpatch(PATCH_BYTE_JR, expr);
|
||||
pCurrentSection->nPC += 1;
|
||||
nPC += 1;
|
||||
pPCSymbol->nValue += 1;
|
||||
|
||||
rpn_Reset(expr);
|
||||
rpn_Free(expr);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -915,13 +903,12 @@ void out_BinaryFile(char *s)
|
||||
checkcodesection();
|
||||
checksectionoverflow(fsize);
|
||||
|
||||
if (nPass == 2) {
|
||||
int32_t dest = nPC;
|
||||
int32_t todo = fsize;
|
||||
int32_t dest = nPC;
|
||||
int32_t todo = fsize;
|
||||
|
||||
while (todo--)
|
||||
pCurrentSection->tData[dest++] = fgetc(f);
|
||||
|
||||
while (todo--)
|
||||
pCurrentSection->tData[dest++] = fgetc(f);
|
||||
}
|
||||
pCurrentSection->nPC += fsize;
|
||||
nPC += fsize;
|
||||
pPCSymbol->nValue += fsize;
|
||||
@@ -958,13 +945,12 @@ void out_BinaryFileSlice(char *s, int32_t start_pos, int32_t length)
|
||||
checkcodesection();
|
||||
checksectionoverflow(length);
|
||||
|
||||
if (nPass == 2) {
|
||||
int32_t dest = nPC;
|
||||
int32_t todo = length;
|
||||
int32_t dest = nPC;
|
||||
int32_t todo = length;
|
||||
|
||||
while (todo--)
|
||||
pCurrentSection->tData[dest++] = fgetc(f);
|
||||
|
||||
while (todo--)
|
||||
pCurrentSection->tData[dest++] = fgetc(f);
|
||||
}
|
||||
pCurrentSection->nPC += length;
|
||||
nPC += length;
|
||||
pPCSymbol->nValue += length;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
.\"
|
||||
.\" This file is part of RGBDS.
|
||||
.\"
|
||||
.\" Copyright (c) 2010-2018, Anthony J. Bentley and RGBDS contributors.
|
||||
.\" Copyright (c) 2010-2019, Anthony J. Bentley and RGBDS contributors.
|
||||
.\"
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd February 24, 2018
|
||||
.Dd July 8, 2019
|
||||
.Dt RGBASM 1
|
||||
.Os RGBDS Manual
|
||||
.Sh NAME
|
||||
@@ -21,11 +21,18 @@
|
||||
.Op Fl M Ar dependfile
|
||||
.Op Fl o Ar outfile
|
||||
.Op Fl p Ar pad_value
|
||||
.Op Fl r Ar recursion_depth
|
||||
.Ar file
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
program creates an object file from an assembly source file.
|
||||
The input
|
||||
.Ar file
|
||||
can be a file path, or
|
||||
.Cm \-
|
||||
denoting
|
||||
.Cm stdin .
|
||||
Its arguments are as follows:
|
||||
.Bl -tag -width Ds
|
||||
.It Fl b Ar chars
|
||||
@@ -71,6 +78,8 @@ Write an object file to the given filename.
|
||||
.It Fl p Ar pad_value
|
||||
When padding an image, pad with this value.
|
||||
The default is 0x00.
|
||||
.It Fl r Ar recursion_depth
|
||||
Specifies the recursion depth at which RGBASM will assume being in an infinite loop.
|
||||
.It Fl V
|
||||
Print the version of the program and exit.
|
||||
.It Fl v
|
||||
@@ -79,12 +88,20 @@ Be verbose.
|
||||
Disable warning output.
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
Assembling a basic source file is simple:
|
||||
You can assemble a source file in two ways.
|
||||
Straight forward way:
|
||||
.Pp
|
||||
.Bd -literal -offset indent
|
||||
$ rgbasm -o bar.o foo.asm
|
||||
.Ed
|
||||
.Pp
|
||||
Pipes way:
|
||||
.Pp
|
||||
.Bd -literal -offset indent
|
||||
$ cat foo.asm | rgbasm -o bar.o -
|
||||
$ rgbasm -o bar.o - < foo.asm
|
||||
.Ed
|
||||
.Pp
|
||||
The resulting object file is not yet a usable ROM image \(em it must first be
|
||||
run through
|
||||
.Xr rgblink 1
|
||||
|
||||
@@ -455,8 +455,7 @@ String equates can't be exported or imported.
|
||||
.Sy Important note :
|
||||
An EQUS can be expanded to a string that contains another EQUS
|
||||
and it will be expanded as well.
|
||||
This means that, if you aren't careful, you may trap the assembler into an
|
||||
infinite loop if there's a circular dependency in the expansions.
|
||||
If this creates an infinite loop, RGBASM will error out once a certain depth is reached. See the -r command-line option.
|
||||
Also, a MACRO can have inside an EQUS which references the same MACRO, which has
|
||||
the same problem.
|
||||
.Pp
|
||||
@@ -584,16 +583,15 @@ If you want to use the rest, you need to use the keyword
|
||||
.Ic SHIFT .
|
||||
.Pp
|
||||
Line continuations work as usual inside macros or lists of arguments of macros.
|
||||
Strings, however, are a bit trickier.
|
||||
The following example shows how to use strings as arguments for a macro:
|
||||
However, some characters need to be escaped, as in the following example:
|
||||
.Pp
|
||||
.Bd -literal -offset indent
|
||||
PrintMacro : MACRO
|
||||
PRINTT \[rs]1
|
||||
ENDM
|
||||
|
||||
PrintMacro STRCAT(\[rs]"Hello\[rs]"\[rs], \[rs]
|
||||
\[rs]" world\[rs]\[rs]n\[rs]")
|
||||
PrintMacro STRCAT("Hello"\[rs], \[rs]
|
||||
" world\[rs]\[rs]n")
|
||||
.Ed
|
||||
.Pp
|
||||
.Ic SHIFT
|
||||
@@ -1079,7 +1077,20 @@ within a string.
|
||||
This will examine the type of the symbol and insert its value accordingly.
|
||||
If symbol is a string symbol, the symbols value is simply copied.
|
||||
If it's a numeric symbol, the value is converted to hexadecimal notation and
|
||||
inserted as a string.
|
||||
inserted as a string with a dollar prepended.
|
||||
.Pp
|
||||
It's possible to change the way numeric symbols are converted by specifying
|
||||
a print type like so:
|
||||
.Sy {d:symbol}
|
||||
Valid print types are:
|
||||
.Bl -column -offset indent
|
||||
.It Sy Print type Ta Sy Format Ta Sy Example
|
||||
.It Li d Ta Decimal Ta 42
|
||||
.It Li x Ta Lowercase hexadecimal Ta 2a
|
||||
.It Li X Ta Uppercase hexadecimal Ta 2A
|
||||
.It Li b Ta Binary Ta 101010
|
||||
.Pp
|
||||
Note that print types should only be used with numeric values, not strings.
|
||||
.Pp
|
||||
HINT: The
|
||||
.Sy {symbol}
|
||||
@@ -1127,9 +1138,30 @@ CHARMAP "í", 20
|
||||
CHARMAP "A", 128
|
||||
.Ed
|
||||
.Pp
|
||||
It is possible to create multiple character maps and then switch between them
|
||||
as desired. This can be used to encode debug information in ASCII and use
|
||||
a different encoding for other purposes, for example. Initially, there is
|
||||
one character map called
|
||||
.Sy main
|
||||
and it is automatically selected as the current character map from the
|
||||
beginning. There is also a character map stack that can be used to save and
|
||||
restore which character map is currently active.
|
||||
.Bl -column "NEWCHARMAP name, basename"
|
||||
.It Sy Command Ta Sy Meaning
|
||||
.It Ic NEWCHARMAP Ar name Ta Creates a new, empty character map called
|
||||
.Ic name .
|
||||
.It Ic NEWCHARMAP Ar name , basename Ta Creates a new character map called
|
||||
. Ic name ,
|
||||
copied from character map
|
||||
.Ic basename .
|
||||
.It Ic SETCHARMAP Ar name Ta Switch to character map Ic name .
|
||||
.It Ic PUSHC Ta Push the current character map onto the stack.
|
||||
.It Ic POPC Ta Pop a character map off the stack and switch to it.
|
||||
.El
|
||||
.Pp
|
||||
.Sy Note:
|
||||
Character maps affect all strings in the file from the point in which they are
|
||||
defined.
|
||||
defined, until switching to a different character map.
|
||||
This means that any string that the code may want to print as debug information
|
||||
will also be affected by it.
|
||||
.Pp
|
||||
|
||||
237
src/asm/rpn.c
237
src/asm/rpn.c
@@ -10,6 +10,7 @@
|
||||
* Controls RPN expressions for objectfiles
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -24,12 +25,39 @@
|
||||
void mergetwoexpressions(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
*expr = *src1;
|
||||
memcpy(&(expr->tRPN[expr->nRPNLength]), src2->tRPN, src2->nRPNLength);
|
||||
assert(src1->tRPN != NULL && src2->tRPN != NULL);
|
||||
|
||||
expr->nRPNLength += src2->nRPNLength;
|
||||
expr->isReloc |= src2->isReloc;
|
||||
expr->isPCRel |= src2->isPCRel;
|
||||
if (src1->nRPNLength + src2->nRPNLength > MAXRPNLEN)
|
||||
fatalerror("RPN expression is too large");
|
||||
|
||||
uint32_t len = src1->nRPNLength + src2->nRPNLength;
|
||||
|
||||
expr->nVal = 0;
|
||||
expr->tRPN = src1->tRPN;
|
||||
|
||||
if (src1->nRPNCapacity >= len) {
|
||||
expr->nRPNCapacity = src1->nRPNCapacity;
|
||||
} else {
|
||||
uint32_t cap1 = src1->nRPNCapacity;
|
||||
uint32_t cap2 = src2->nRPNCapacity;
|
||||
uint32_t cap = (cap1 > cap2) ? cap1 : cap2;
|
||||
|
||||
if (len > cap)
|
||||
cap = (cap <= MAXRPNLEN / 2) ? cap * 2 : MAXRPNLEN;
|
||||
|
||||
expr->nRPNCapacity = cap;
|
||||
expr->tRPN = realloc(expr->tRPN, expr->nRPNCapacity);
|
||||
if (expr->tRPN == NULL)
|
||||
fatalerror("No memory for RPN expression");
|
||||
}
|
||||
|
||||
memcpy(expr->tRPN + src1->nRPNLength, src2->tRPN, src2->nRPNLength);
|
||||
free(src2->tRPN);
|
||||
|
||||
expr->nRPNLength = len;
|
||||
expr->nRPNPatchSize = src1->nRPNPatchSize + src2->nRPNPatchSize;
|
||||
expr->nRPNOut = 0;
|
||||
expr->isReloc = src1->isReloc || src2->isReloc;
|
||||
}
|
||||
|
||||
#define joinexpr() mergetwoexpressions(expr, src1, src2)
|
||||
@@ -37,20 +65,46 @@ void mergetwoexpressions(struct Expression *expr, const struct Expression *src1,
|
||||
/*
|
||||
* Add a byte to the RPN expression
|
||||
*/
|
||||
void pushbyte(struct Expression *expr, int b)
|
||||
void pushbyte(struct Expression *expr, uint8_t b)
|
||||
{
|
||||
expr->tRPN[expr->nRPNLength++] = b & 0xFF;
|
||||
if (expr->nRPNLength == expr->nRPNCapacity) {
|
||||
if (expr->nRPNCapacity == 0)
|
||||
expr->nRPNCapacity = 256;
|
||||
else if (expr->nRPNCapacity == MAXRPNLEN)
|
||||
fatalerror("RPN expression is too large");
|
||||
else if (expr->nRPNCapacity > MAXRPNLEN / 2)
|
||||
expr->nRPNCapacity = MAXRPNLEN;
|
||||
else
|
||||
expr->nRPNCapacity *= 2;
|
||||
expr->tRPN = realloc(expr->tRPN, expr->nRPNCapacity);
|
||||
|
||||
if (expr->tRPN == NULL)
|
||||
fatalerror("No memory for RPN expression");
|
||||
}
|
||||
|
||||
expr->tRPN[expr->nRPNLength++] = b;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset the RPN module
|
||||
* Init the RPN expression
|
||||
*/
|
||||
void rpn_Reset(struct Expression *expr)
|
||||
void rpn_Init(struct Expression *expr)
|
||||
{
|
||||
expr->tRPN = NULL;
|
||||
expr->nRPNCapacity = 0;
|
||||
expr->nRPNLength = 0;
|
||||
expr->nRPNPatchSize = 0;
|
||||
expr->nRPNOut = 0;
|
||||
expr->isReloc = 0;
|
||||
expr->isPCRel = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free the RPN expression
|
||||
*/
|
||||
void rpn_Free(struct Expression *expr)
|
||||
{
|
||||
free(expr->tRPN);
|
||||
rpn_Init(expr);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -72,45 +126,32 @@ uint32_t rpn_isReloc(const struct Expression *expr)
|
||||
return expr->isReloc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine if the current expression can be pc-relative
|
||||
*/
|
||||
uint32_t rpn_isPCRelative(const struct Expression *expr)
|
||||
{
|
||||
return expr->isPCRel;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add symbols, constants and operators to expression
|
||||
*/
|
||||
void rpn_Number(struct Expression *expr, uint32_t i)
|
||||
{
|
||||
rpn_Reset(expr);
|
||||
rpn_Init(expr);
|
||||
pushbyte(expr, RPN_CONST);
|
||||
pushbyte(expr, i);
|
||||
pushbyte(expr, i >> 8);
|
||||
pushbyte(expr, i >> 16);
|
||||
pushbyte(expr, i >> 24);
|
||||
expr->nVal = i;
|
||||
expr->nRPNPatchSize += 5;
|
||||
}
|
||||
|
||||
void rpn_Symbol(struct Expression *expr, char *tzSym)
|
||||
{
|
||||
if (!sym_isConstant(tzSym)) {
|
||||
const struct sSymbol *psym;
|
||||
|
||||
rpn_Reset(expr);
|
||||
|
||||
psym = sym_FindSymbol(tzSym);
|
||||
|
||||
if (psym == NULL || psym->pSection == pCurrentSection
|
||||
|| psym->pSection == NULL)
|
||||
expr->isPCRel = 1;
|
||||
rpn_Init(expr);
|
||||
sym_Ref(tzSym);
|
||||
expr->isReloc = 1;
|
||||
pushbyte(expr, RPN_SYM);
|
||||
while (*tzSym)
|
||||
pushbyte(expr, *tzSym++);
|
||||
pushbyte(expr, 0);
|
||||
expr->nRPNPatchSize += 5;
|
||||
} else {
|
||||
rpn_Number(expr, sym_GetConstantValue(tzSym));
|
||||
}
|
||||
@@ -118,7 +159,7 @@ void rpn_Symbol(struct Expression *expr, char *tzSym)
|
||||
|
||||
void rpn_BankSelf(struct Expression *expr)
|
||||
{
|
||||
rpn_Reset(expr);
|
||||
rpn_Init(expr);
|
||||
|
||||
/*
|
||||
* This symbol is not really relocatable, but this makes the assembler
|
||||
@@ -127,6 +168,7 @@ void rpn_BankSelf(struct Expression *expr)
|
||||
expr->isReloc = 1;
|
||||
|
||||
pushbyte(expr, RPN_BANK_SELF);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
void rpn_BankSymbol(struct Expression *expr, char *tzSym)
|
||||
@@ -138,19 +180,14 @@ void rpn_BankSymbol(struct Expression *expr, char *tzSym)
|
||||
}
|
||||
|
||||
if (!sym_isConstant(tzSym)) {
|
||||
rpn_Reset(expr);
|
||||
|
||||
/*
|
||||
* Check that the symbol exists by evaluating and discarding the
|
||||
* value.
|
||||
*/
|
||||
sym_GetValue(tzSym);
|
||||
|
||||
rpn_Init(expr);
|
||||
sym_Ref(tzSym);
|
||||
expr->isReloc = 1;
|
||||
pushbyte(expr, RPN_BANK_SYM);
|
||||
while (*tzSym)
|
||||
pushbyte(expr, *tzSym++);
|
||||
pushbyte(expr, 0);
|
||||
expr->nRPNPatchSize += 5;
|
||||
} else {
|
||||
yyerror("BANK argument must be a relocatable identifier");
|
||||
}
|
||||
@@ -158,7 +195,7 @@ void rpn_BankSymbol(struct Expression *expr, char *tzSym)
|
||||
|
||||
void rpn_BankSection(struct Expression *expr, char *tzSectionName)
|
||||
{
|
||||
rpn_Reset(expr);
|
||||
rpn_Init(expr);
|
||||
|
||||
/*
|
||||
* This symbol is not really relocatable, but this makes the assembler
|
||||
@@ -167,37 +204,48 @@ void rpn_BankSection(struct Expression *expr, char *tzSectionName)
|
||||
expr->isReloc = 1;
|
||||
|
||||
pushbyte(expr, RPN_BANK_SECT);
|
||||
while (*tzSectionName)
|
||||
expr->nRPNPatchSize++;
|
||||
|
||||
while (*tzSectionName) {
|
||||
pushbyte(expr, *tzSectionName++);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
pushbyte(expr, 0);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
void rpn_CheckHRAM(struct Expression *expr, const struct Expression *src)
|
||||
{
|
||||
*expr = *src;
|
||||
pushbyte(expr, RPN_HRAM);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
void rpn_LOGNOT(struct Expression *expr, const struct Expression *src)
|
||||
{
|
||||
*expr = *src;
|
||||
expr->nVal = !expr->nVal;
|
||||
pushbyte(expr, RPN_LOGUNNOT);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
void rpn_LOGOR(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
joinexpr();
|
||||
expr->nVal = (expr->nVal || src2->nVal);
|
||||
expr->nVal = (src1->nVal || src2->nVal);
|
||||
pushbyte(expr, RPN_LOGOR);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
void rpn_LOGAND(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
joinexpr();
|
||||
expr->nVal = (expr->nVal && src2->nVal);
|
||||
expr->nVal = (src1->nVal && src2->nVal);
|
||||
pushbyte(expr, RPN_LOGAND);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
void rpn_HIGH(struct Expression *expr, const struct Expression *src)
|
||||
@@ -221,6 +269,8 @@ void rpn_HIGH(struct Expression *expr, const struct Expression *src)
|
||||
pushbyte(expr, 0);
|
||||
|
||||
pushbyte(expr, RPN_AND);
|
||||
|
||||
expr->nRPNPatchSize += 12;
|
||||
}
|
||||
|
||||
void rpn_LOW(struct Expression *expr, const struct Expression *src)
|
||||
@@ -236,94 +286,107 @@ void rpn_LOW(struct Expression *expr, const struct Expression *src)
|
||||
pushbyte(expr, 0);
|
||||
|
||||
pushbyte(expr, RPN_AND);
|
||||
|
||||
expr->nRPNPatchSize += 6;
|
||||
}
|
||||
|
||||
void rpn_LOGEQU(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
joinexpr();
|
||||
expr->nVal = (expr->nVal == src2->nVal);
|
||||
expr->nVal = (src1->nVal == src2->nVal);
|
||||
pushbyte(expr, RPN_LOGEQ);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
void rpn_LOGGT(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
joinexpr();
|
||||
expr->nVal = (expr->nVal > src2->nVal);
|
||||
expr->nVal = (src1->nVal > src2->nVal);
|
||||
pushbyte(expr, RPN_LOGGT);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
void rpn_LOGLT(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
joinexpr();
|
||||
expr->nVal = (expr->nVal < src2->nVal);
|
||||
expr->nVal = (src1->nVal < src2->nVal);
|
||||
pushbyte(expr, RPN_LOGLT);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
void rpn_LOGGE(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
joinexpr();
|
||||
expr->nVal = (expr->nVal >= src2->nVal);
|
||||
expr->nVal = (src1->nVal >= src2->nVal);
|
||||
pushbyte(expr, RPN_LOGGE);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
void rpn_LOGLE(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
joinexpr();
|
||||
expr->nVal = (expr->nVal <= src2->nVal);
|
||||
expr->nVal = (src1->nVal <= src2->nVal);
|
||||
pushbyte(expr, RPN_LOGLE);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
void rpn_LOGNE(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
joinexpr();
|
||||
expr->nVal = (expr->nVal != src2->nVal);
|
||||
expr->nVal = (src1->nVal != src2->nVal);
|
||||
pushbyte(expr, RPN_LOGNE);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
void rpn_ADD(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
joinexpr();
|
||||
expr->nVal = (expr->nVal + src2->nVal);
|
||||
expr->nVal = ((uint32_t)src1->nVal + (uint32_t)src2->nVal);
|
||||
pushbyte(expr, RPN_ADD);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
void rpn_SUB(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
joinexpr();
|
||||
expr->nVal = (expr->nVal - src2->nVal);
|
||||
expr->nVal = ((uint32_t)src1->nVal - (uint32_t)src2->nVal);
|
||||
pushbyte(expr, RPN_SUB);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
void rpn_XOR(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
joinexpr();
|
||||
expr->nVal = (expr->nVal ^ src2->nVal);
|
||||
expr->nVal = (src1->nVal ^ src2->nVal);
|
||||
pushbyte(expr, RPN_XOR);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
void rpn_OR(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
joinexpr();
|
||||
expr->nVal = (expr->nVal | src2->nVal);
|
||||
expr->nVal = (src1->nVal | src2->nVal);
|
||||
pushbyte(expr, RPN_OR);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
void rpn_AND(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
joinexpr();
|
||||
expr->nVal = (expr->nVal & src2->nVal);
|
||||
expr->nVal = (src1->nVal & src2->nVal);
|
||||
pushbyte(expr, RPN_AND);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
void rpn_SHL(struct Expression *expr, const struct Expression *src1,
|
||||
@@ -331,66 +394,95 @@ void rpn_SHL(struct Expression *expr, const struct Expression *src1,
|
||||
{
|
||||
joinexpr();
|
||||
|
||||
if (src1->nVal < 0)
|
||||
warning("Left shift of negative value: %d", src1->nVal);
|
||||
if (!expr->isReloc) {
|
||||
if (src1->nVal < 0)
|
||||
warning("Left shift of negative value: %d", src1->nVal);
|
||||
|
||||
if (src2->nVal < 0)
|
||||
fatalerror("Shift by negative value: %d", src2->nVal);
|
||||
else if (src2->nVal >= 32)
|
||||
fatalerror("Shift by too big value: %d", src2->nVal);
|
||||
if (src2->nVal < 0)
|
||||
fatalerror("Shift by negative value: %d", src2->nVal);
|
||||
else if (src2->nVal >= 32)
|
||||
fatalerror("Shift by too big value: %d", src2->nVal);
|
||||
|
||||
expr->nVal = ((uint32_t)src1->nVal << src2->nVal);
|
||||
}
|
||||
|
||||
expr->nVal = (expr->nVal << src2->nVal);
|
||||
pushbyte(expr, RPN_SHL);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
void rpn_SHR(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
joinexpr();
|
||||
if (src2->nVal < 0)
|
||||
fatalerror("Shift by negative value: %d", src2->nVal);
|
||||
else if (src2->nVal >= 32)
|
||||
fatalerror("Shift by too big value: %d", src2->nVal);
|
||||
|
||||
expr->nVal = (expr->nVal >> src2->nVal);
|
||||
if (!expr->isReloc) {
|
||||
if (src2->nVal < 0)
|
||||
fatalerror("Shift by negative value: %d", src2->nVal);
|
||||
else if (src2->nVal >= 32)
|
||||
fatalerror("Shift by too big value: %d", src2->nVal);
|
||||
|
||||
expr->nVal = (src1->nVal >> src2->nVal);
|
||||
}
|
||||
|
||||
pushbyte(expr, RPN_SHR);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
void rpn_MUL(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
joinexpr();
|
||||
expr->nVal = (expr->nVal * src2->nVal);
|
||||
expr->nVal = ((uint32_t)src1->nVal * (uint32_t)src2->nVal);
|
||||
pushbyte(expr, RPN_MUL);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
void rpn_DIV(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
joinexpr();
|
||||
if (src2->nVal == 0)
|
||||
fatalerror("Division by zero");
|
||||
|
||||
expr->nVal = (expr->nVal / src2->nVal);
|
||||
if (!expr->isReloc) {
|
||||
if (src2->nVal == 0)
|
||||
fatalerror("Division by zero");
|
||||
|
||||
if (src1->nVal == INT32_MIN && src2->nVal == -1) {
|
||||
warning("Division of min value by -1");
|
||||
expr->nVal = INT32_MIN;
|
||||
} else {
|
||||
expr->nVal = (src1->nVal / src2->nVal);
|
||||
}
|
||||
}
|
||||
|
||||
pushbyte(expr, RPN_DIV);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
void rpn_MOD(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
joinexpr();
|
||||
if (src2->nVal == 0)
|
||||
fatalerror("Division by zero");
|
||||
|
||||
expr->nVal = (expr->nVal % src2->nVal);
|
||||
if (!expr->isReloc) {
|
||||
if (src2->nVal == 0)
|
||||
fatalerror("Division by zero");
|
||||
|
||||
if (src1->nVal == INT32_MIN && src2->nVal == -1)
|
||||
expr->nVal = 0;
|
||||
else
|
||||
expr->nVal = (src1->nVal % src2->nVal);
|
||||
}
|
||||
|
||||
pushbyte(expr, RPN_MOD);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
void rpn_UNNEG(struct Expression *expr, const struct Expression *src)
|
||||
{
|
||||
*expr = *src;
|
||||
expr->nVal = -expr->nVal;
|
||||
expr->nVal = -(uint32_t)expr->nVal;
|
||||
pushbyte(expr, RPN_UNSUB);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
void rpn_UNNOT(struct Expression *expr, const struct Expression *src)
|
||||
@@ -398,4 +490,5 @@ void rpn_UNNOT(struct Expression *expr, const struct Expression *src)
|
||||
*expr = *src;
|
||||
expr->nVal = ~expr->nVal;
|
||||
pushbyte(expr, RPN_UNNOT);
|
||||
expr->nRPNPatchSize++;
|
||||
}
|
||||
|
||||
526
src/asm/symbol.c
526
src/asm/symbol.c
@@ -22,6 +22,7 @@
|
||||
#include "asm/main.h"
|
||||
#include "asm/mymath.h"
|
||||
#include "asm/output.h"
|
||||
#include "asm/util.h"
|
||||
|
||||
#include "extern/err.h"
|
||||
|
||||
@@ -68,7 +69,7 @@ int32_t Callback_NARG(unused_ struct sSymbol *sym)
|
||||
uint32_t i = 0;
|
||||
|
||||
while (currentmacroargs[i] && i < MAXMACROARGS)
|
||||
i += 1;
|
||||
i++;
|
||||
|
||||
return i;
|
||||
}
|
||||
@@ -90,16 +91,11 @@ static int32_t getvaluefield(struct sSymbol *sym)
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the hash value for a string
|
||||
* Calculate the hash value for a symbol name
|
||||
*/
|
||||
uint32_t calchash(char *s)
|
||||
uint32_t sym_CalcHash(const char *s)
|
||||
{
|
||||
uint32_t hash = 5381;
|
||||
|
||||
while (*s != 0)
|
||||
hash = (hash * 33) ^ (*s++);
|
||||
|
||||
return hash % HASHSIZE;
|
||||
return calchash(s) % HASHSIZE;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -123,7 +119,7 @@ struct sSymbol *createsymbol(char *s)
|
||||
struct sSymbol **ppsym;
|
||||
uint32_t hash;
|
||||
|
||||
hash = calchash(s);
|
||||
hash = sym_CalcHash(s);
|
||||
ppsym = &(tHashedSymbols[hash]);
|
||||
|
||||
while ((*ppsym) != NULL)
|
||||
@@ -154,12 +150,15 @@ struct sSymbol *createsymbol(char *s)
|
||||
* Creates the full name of a local symbol in a given scope, by prepending
|
||||
* the name with the parent symbol's name.
|
||||
*/
|
||||
static size_t fullSymbolName(char *output, size_t outputSize, char *localName,
|
||||
const struct sSymbol *scope)
|
||||
static void fullSymbolName(char *output, size_t outputSize, char *localName,
|
||||
const struct sSymbol *scope)
|
||||
{
|
||||
const struct sSymbol *parent = scope->pScope ? scope->pScope : scope;
|
||||
int n = snprintf(output, outputSize, "%s%s", parent->tzName, localName);
|
||||
|
||||
return snprintf(output, outputSize, "%s%s", parent->tzName, localName);
|
||||
if (n >= (int)outputSize)
|
||||
fatalerror("Symbol name is too long: '%s%s'",
|
||||
parent->tzName, localName);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -184,7 +183,7 @@ struct sSymbol **findpsymbol(char *s, struct sSymbol *scope)
|
||||
s);
|
||||
}
|
||||
|
||||
hash = calchash(s);
|
||||
hash = sym_CalcHash(s);
|
||||
ppsym = &(tHashedSymbols[hash]);
|
||||
|
||||
while ((*ppsym) != NULL) {
|
||||
@@ -207,7 +206,7 @@ struct sSymbol *findsymbol(char *s, struct sSymbol *scope)
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a symbol by name and scope
|
||||
* Find a symbol by name, with automatically determined scope
|
||||
*/
|
||||
struct sSymbol *sym_FindSymbol(char *tzName)
|
||||
{
|
||||
@@ -256,14 +255,7 @@ void sym_Purge(char *tzName)
|
||||
*/
|
||||
uint32_t sym_isConstDefined(char *tzName)
|
||||
{
|
||||
struct sSymbol *psym, *pscope;
|
||||
|
||||
if (*tzName == '.')
|
||||
pscope = pScope;
|
||||
else
|
||||
pscope = NULL;
|
||||
|
||||
psym = findsymbol(tzName, pscope);
|
||||
struct sSymbol *psym = sym_FindSymbol(tzName);
|
||||
|
||||
if (psym && (psym->nType & SYMF_DEFINED)) {
|
||||
uint32_t mask = SYMF_EQU | SYMF_SET | SYMF_MACRO | SYMF_STRING;
|
||||
@@ -280,19 +272,9 @@ uint32_t sym_isConstDefined(char *tzName)
|
||||
|
||||
uint32_t sym_isDefined(char *tzName)
|
||||
{
|
||||
struct sSymbol *psym, *pscope;
|
||||
struct sSymbol *psym = sym_FindSymbol(tzName);
|
||||
|
||||
if (*tzName == '.')
|
||||
pscope = pScope;
|
||||
else
|
||||
pscope = NULL;
|
||||
|
||||
psym = findsymbol(tzName, pscope);
|
||||
|
||||
if (psym && (psym->nType & SYMF_DEFINED))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
return (psym && (psym->nType & SYMF_DEFINED));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -300,21 +282,9 @@ uint32_t sym_isDefined(char *tzName)
|
||||
*/
|
||||
uint32_t sym_isConstant(char *s)
|
||||
{
|
||||
struct sSymbol *psym, *pscope;
|
||||
struct sSymbol *psym = sym_FindSymbol(s);
|
||||
|
||||
if (*s == '.')
|
||||
pscope = pScope;
|
||||
else
|
||||
pscope = NULL;
|
||||
|
||||
psym = findsymbol(s, pscope);
|
||||
|
||||
if (psym != NULL) {
|
||||
if (psym->nType & SYMF_CONST)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return (psym && (psym->nType & SYMF_CONST));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -327,7 +297,7 @@ char *sym_GetStringValue(char *tzSym)
|
||||
if (pSym != NULL)
|
||||
return pSym->pMacro;
|
||||
|
||||
yyerror("Stringsymbol '%s' not defined", tzSym);
|
||||
yyerror("String symbol '%s' not defined", tzSym);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -337,14 +307,7 @@ char *sym_GetStringValue(char *tzSym)
|
||||
*/
|
||||
uint32_t sym_GetConstantValue(char *s)
|
||||
{
|
||||
struct sSymbol *psym, *pscope;
|
||||
|
||||
if (*s == '.')
|
||||
pscope = pScope;
|
||||
else
|
||||
pscope = NULL;
|
||||
|
||||
psym = findsymbol(s, pscope);
|
||||
struct sSymbol *psym = sym_FindSymbol(s);
|
||||
|
||||
if (psym != NULL) {
|
||||
if (psym->nType & SYMF_CONST)
|
||||
@@ -358,63 +321,12 @@ uint32_t sym_GetConstantValue(char *s)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a symbols value... "estimated" if not defined yet
|
||||
*/
|
||||
uint32_t sym_GetValue(char *s)
|
||||
{
|
||||
struct sSymbol *psym, *pscope;
|
||||
|
||||
if (*s == '.')
|
||||
pscope = pScope;
|
||||
else
|
||||
pscope = NULL;
|
||||
|
||||
psym = findsymbol(s, pscope);
|
||||
|
||||
if (psym != NULL) {
|
||||
if (psym->nType & SYMF_DEFINED) {
|
||||
if (psym->nType & (SYMF_MACRO | SYMF_STRING))
|
||||
yyerror("'%s' is a macro or string symbol", s);
|
||||
|
||||
return getvaluefield(psym);
|
||||
}
|
||||
|
||||
if (nPass == 2) {
|
||||
/*
|
||||
* Assume undefined symbols are imported from
|
||||
* somwehere else
|
||||
*/
|
||||
psym->nType |= SYMF_IMPORT;
|
||||
}
|
||||
|
||||
/* 0x80 seems like a good default value... */
|
||||
return 0x80;
|
||||
}
|
||||
|
||||
if (nPass == 1) {
|
||||
createsymbol(s);
|
||||
return 0x80;
|
||||
}
|
||||
|
||||
yyerror("'%s' not defined", s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a defined symbols value... aborts if not defined yet
|
||||
*/
|
||||
uint32_t sym_GetDefinedValue(char *s)
|
||||
{
|
||||
struct sSymbol *psym, *pscope;
|
||||
|
||||
if (*s == '.')
|
||||
pscope = pScope;
|
||||
else
|
||||
pscope = NULL;
|
||||
|
||||
psym = findsymbol(s, pscope);
|
||||
struct sSymbol *psym = sym_FindSymbol(s);
|
||||
|
||||
if (psym != NULL) {
|
||||
if ((psym->nType & SYMF_DEFINED)) {
|
||||
@@ -448,7 +360,7 @@ void sym_ShiftCurrentMacroArgs(void)
|
||||
int32_t i;
|
||||
|
||||
free(currentmacroargs[0]);
|
||||
for (i = 0; i < MAXMACROARGS - 1; i += 1)
|
||||
for (i = 0; i < MAXMACROARGS - 1; i++)
|
||||
currentmacroargs[i] = currentmacroargs[i + 1];
|
||||
|
||||
currentmacroargs[MAXMACROARGS - 1] = NULL;
|
||||
@@ -471,7 +383,8 @@ void sym_UseNewMacroArgs(void)
|
||||
{
|
||||
int32_t i;
|
||||
|
||||
for (i = 0; i <= MAXMACROARGS; i += 1) {
|
||||
for (i = 0; i <= MAXMACROARGS; i++) {
|
||||
free(currentmacroargs[i]);
|
||||
currentmacroargs[i] = newmacroargs[i];
|
||||
newmacroargs[i] = NULL;
|
||||
}
|
||||
@@ -481,25 +394,19 @@ void sym_SaveCurrentMacroArgs(char *save[])
|
||||
{
|
||||
int32_t i;
|
||||
|
||||
for (i = 0; i <= MAXMACROARGS; i += 1)
|
||||
for (i = 0; i <= MAXMACROARGS; i++) {
|
||||
save[i] = currentmacroargs[i];
|
||||
currentmacroargs[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void sym_RestoreCurrentMacroArgs(char *save[])
|
||||
{
|
||||
int32_t i;
|
||||
|
||||
for (i = 0; i <= MAXMACROARGS; i += 1)
|
||||
currentmacroargs[i] = save[i];
|
||||
}
|
||||
|
||||
void sym_FreeCurrentMacroArgs(void)
|
||||
{
|
||||
int32_t i;
|
||||
|
||||
for (i = 0; i <= MAXMACROARGS; i += 1) {
|
||||
for (i = 0; i <= MAXMACROARGS; i++) {
|
||||
free(currentmacroargs[i]);
|
||||
currentmacroargs[i] = NULL;
|
||||
currentmacroargs[i] = save[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -508,7 +415,7 @@ void sym_AddNewMacroArg(char *s)
|
||||
int32_t i = 0;
|
||||
|
||||
while (i < MAXMACROARGS && newmacroargs[i] != NULL)
|
||||
i += 1;
|
||||
i++;
|
||||
|
||||
if (i < MAXMACROARGS) {
|
||||
if (s)
|
||||
@@ -524,7 +431,7 @@ void sym_SetMacroArgID(uint32_t nMacroCount)
|
||||
{
|
||||
char s[256];
|
||||
|
||||
snprintf(s, sizeof(s), "_%u", nMacroCount);
|
||||
snprintf(s, sizeof(s) - 1, "_%u", nMacroCount);
|
||||
newmacroargs[MAXMACROARGS] = strdup(s);
|
||||
}
|
||||
|
||||
@@ -532,7 +439,7 @@ void sym_UseCurrentMacroArgs(void)
|
||||
{
|
||||
int32_t i;
|
||||
|
||||
for (i = 1; i <= MAXMACROARGS; i += 1)
|
||||
for (i = 1; i <= MAXMACROARGS; i++)
|
||||
sym_AddNewMacroArg(sym_FindMacroArg(i));
|
||||
}
|
||||
|
||||
@@ -544,30 +451,42 @@ struct sSymbol *sym_FindMacro(char *s)
|
||||
return findsymbol(s, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a symbol that will be non-relocatable and ensure that it
|
||||
* hasn't already been defined or referenced in a context that would
|
||||
* require that it be relocatable
|
||||
*/
|
||||
static struct sSymbol *createNonrelocSymbol(char *tzSym)
|
||||
{
|
||||
struct sSymbol *nsym = findsymbol(tzSym, NULL);
|
||||
|
||||
if (nsym != NULL) {
|
||||
if (nsym->nType & SYMF_DEFINED) {
|
||||
yyerror("'%s' already defined at %s(%u)",
|
||||
tzSym, nsym->tzFileName, nsym->nFileLine);
|
||||
} else if (nsym->nType & SYMF_REF) {
|
||||
yyerror("'%s' already referenced at %s(%u)",
|
||||
tzSym, nsym->tzFileName, nsym->nFileLine);
|
||||
}
|
||||
} else {
|
||||
nsym = createsymbol(tzSym);
|
||||
}
|
||||
|
||||
return nsym;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add an equated symbol
|
||||
*/
|
||||
void sym_AddEqu(char *tzSym, int32_t value)
|
||||
{
|
||||
if ((nPass == 1) || ((nPass == 2) && (sym_isDefined(tzSym) == 0))) {
|
||||
/* only add equated symbols in pass 1 */
|
||||
struct sSymbol *nsym = findsymbol(tzSym, NULL);
|
||||
struct sSymbol *nsym = createNonrelocSymbol(tzSym);
|
||||
|
||||
if (nsym != NULL) {
|
||||
if (nsym->nType & SYMF_DEFINED) {
|
||||
yyerror("'%s' already defined in %s(%d)", tzSym,
|
||||
nsym->tzFileName, nsym->nFileLine);
|
||||
}
|
||||
} else {
|
||||
nsym = createsymbol(tzSym);
|
||||
}
|
||||
|
||||
if (nsym) {
|
||||
nsym->nValue = value;
|
||||
nsym->nType |= SYMF_EQU | SYMF_DEFINED | SYMF_CONST;
|
||||
nsym->pScope = NULL;
|
||||
updateSymbolFilename(nsym);
|
||||
}
|
||||
if (nsym) {
|
||||
nsym->nValue = value;
|
||||
nsym->nType |= SYMF_EQU | SYMF_DEFINED | SYMF_CONST;
|
||||
nsym->pScope = NULL;
|
||||
updateSymbolFilename(nsym);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -585,16 +504,7 @@ void sym_AddEqu(char *tzSym, int32_t value)
|
||||
*/
|
||||
void sym_AddString(char *tzSym, char *tzValue)
|
||||
{
|
||||
struct sSymbol *nsym = findsymbol(tzSym, NULL);
|
||||
|
||||
if (nsym != NULL) {
|
||||
if (nsym->nType & SYMF_DEFINED) {
|
||||
yyerror("'%s' already defined in %s(%d)",
|
||||
tzSym, nsym->tzFileName, nsym->nFileLine);
|
||||
}
|
||||
} else {
|
||||
nsym = createsymbol(tzSym);
|
||||
}
|
||||
struct sSymbol *nsym = createNonrelocSymbol(tzSym);
|
||||
|
||||
if (nsym) {
|
||||
nsym->pMacro = malloc(strlen(tzValue) + 1);
|
||||
@@ -602,7 +512,7 @@ void sym_AddString(char *tzSym, char *tzValue)
|
||||
if (nsym->pMacro != NULL)
|
||||
strcpy(nsym->pMacro, tzValue);
|
||||
else
|
||||
fatalerror("No memory for stringequate");
|
||||
fatalerror("No memory for string equate");
|
||||
|
||||
nsym->nType |= SYMF_STRING | SYMF_DEFINED;
|
||||
nsym->ulMacroSize = strlen(tzValue);
|
||||
@@ -617,11 +527,7 @@ uint32_t sym_isString(char *tzSym)
|
||||
{
|
||||
const struct sSymbol *pSym = findsymbol(tzSym, NULL);
|
||||
|
||||
if (pSym != NULL) {
|
||||
if (pSym->nType & SYMF_STRING)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
return (pSym && (pSym->nType & SYMF_STRING));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -631,8 +537,25 @@ void sym_AddSet(char *tzSym, int32_t value)
|
||||
{
|
||||
struct sSymbol *nsym = findsymbol(tzSym, NULL);
|
||||
|
||||
if (nsym == NULL) {
|
||||
/* Symbol hasn been found, create */
|
||||
if (nsym != NULL) {
|
||||
if (nsym->nType & SYMF_DEFINED) {
|
||||
if (!(nsym->nType & SYMF_CONST))
|
||||
yyerror("'%s' already defined as non-constant at %s(%u)",
|
||||
tzSym,
|
||||
nsym->tzFileName,
|
||||
nsym->nFileLine);
|
||||
else if (!(nsym->nType & SYMF_SET))
|
||||
yyerror("'%s' already defined as constant at %s(%u)",
|
||||
tzSym,
|
||||
nsym->tzFileName,
|
||||
nsym->nFileLine);
|
||||
} else if (nsym->nType & SYMF_REF) {
|
||||
yyerror("'%s' already referenced at %s(%u)",
|
||||
tzSym,
|
||||
nsym->tzFileName,
|
||||
nsym->nFileLine);
|
||||
}
|
||||
} else {
|
||||
nsym = createsymbol(tzSym);
|
||||
}
|
||||
|
||||
@@ -650,9 +573,6 @@ void sym_AddSet(char *tzSym, int32_t value)
|
||||
void sym_AddLocalReloc(char *tzSym)
|
||||
{
|
||||
if (pScope) {
|
||||
if (strlen(tzSym) + strlen(pScope->tzName) > MAXSYMLEN)
|
||||
fatalerror("Symbol too long");
|
||||
|
||||
char fullname[MAXSYMLEN + 1];
|
||||
|
||||
fullSymbolName(fullname, sizeof(fullname), tzSym, pScope);
|
||||
@@ -669,59 +589,55 @@ void sym_AddLocalReloc(char *tzSym)
|
||||
void sym_AddReloc(char *tzSym)
|
||||
{
|
||||
struct sSymbol *scope = NULL;
|
||||
struct sSymbol *nsym;
|
||||
char *localPtr = strchr(tzSym, '.');
|
||||
|
||||
if ((nPass == 1)
|
||||
|| ((nPass == 2) && (sym_isDefined(tzSym) == 0))) {
|
||||
/* only add reloc symbols in pass 1 */
|
||||
struct sSymbol *nsym;
|
||||
char *localPtr = strchr(tzSym, '.');
|
||||
if (localPtr != NULL) {
|
||||
if (!pScope)
|
||||
fatalerror("Local label in main scope");
|
||||
|
||||
if (localPtr != NULL) {
|
||||
if (!pScope)
|
||||
fatalerror("Local label in main scope");
|
||||
struct sSymbol *parent = pScope->pScope ?
|
||||
pScope->pScope : pScope;
|
||||
uint32_t parentLen = localPtr - tzSym;
|
||||
|
||||
struct sSymbol *parent = pScope->pScope ?
|
||||
pScope->pScope : pScope;
|
||||
uint32_t parentLen = localPtr - tzSym;
|
||||
|
||||
if (strchr(localPtr + 1, '.') != NULL) {
|
||||
fatalerror("'%s' is a nonsensical reference to a nested local symbol",
|
||||
tzSym);
|
||||
} else if (strlen(parent->tzName) != parentLen
|
||||
|| strncmp(tzSym, parent->tzName, parentLen) != 0) {
|
||||
yyerror("Not currently in the scope of '%.*s'",
|
||||
parentLen, tzSym);
|
||||
}
|
||||
|
||||
scope = parent;
|
||||
if (strchr(localPtr + 1, '.') != NULL) {
|
||||
fatalerror("'%s' is a nonsensical reference to a nested local symbol",
|
||||
tzSym);
|
||||
} else if (strlen(parent->tzName) != parentLen
|
||||
|| strncmp(tzSym, parent->tzName, parentLen) != 0) {
|
||||
yyerror("Not currently in the scope of '%.*s'",
|
||||
parentLen, tzSym);
|
||||
}
|
||||
|
||||
nsym = findsymbol(tzSym, scope);
|
||||
|
||||
if (nsym != NULL) {
|
||||
if (nsym->nType & SYMF_DEFINED) {
|
||||
yyerror("'%s' already defined in %s(%d)", tzSym,
|
||||
nsym->tzFileName, nsym->nFileLine);
|
||||
}
|
||||
} else {
|
||||
nsym = createsymbol(tzSym);
|
||||
}
|
||||
|
||||
if (nsym) {
|
||||
nsym->nValue = nPC;
|
||||
nsym->nType |= SYMF_RELOC | SYMF_DEFINED;
|
||||
if (localPtr)
|
||||
nsym->nType |= SYMF_LOCAL;
|
||||
|
||||
if (exportall)
|
||||
nsym->nType |= SYMF_EXPORT;
|
||||
|
||||
nsym->pScope = scope;
|
||||
nsym->pSection = pCurrentSection;
|
||||
|
||||
updateSymbolFilename(nsym);
|
||||
}
|
||||
scope = parent;
|
||||
}
|
||||
|
||||
nsym = findsymbol(tzSym, scope);
|
||||
|
||||
if (nsym != NULL) {
|
||||
if (nsym->nType & SYMF_DEFINED) {
|
||||
yyerror("'%s' already defined in %s(%d)", tzSym,
|
||||
nsym->tzFileName, nsym->nFileLine);
|
||||
}
|
||||
} else {
|
||||
nsym = createsymbol(tzSym);
|
||||
}
|
||||
|
||||
if (nsym) {
|
||||
nsym->nValue = nPC;
|
||||
nsym->nType |= SYMF_RELOC | SYMF_DEFINED;
|
||||
if (localPtr)
|
||||
nsym->nType |= SYMF_LOCAL;
|
||||
|
||||
if (exportall)
|
||||
nsym->nType |= SYMF_EXPORT;
|
||||
|
||||
nsym->pScope = scope;
|
||||
nsym->pSection = pCurrentSection;
|
||||
|
||||
updateSymbolFilename(nsym);
|
||||
}
|
||||
|
||||
pScope = findsymbol(tzSym, scope);
|
||||
}
|
||||
|
||||
@@ -734,10 +650,6 @@ void sym_AddReloc(char *tzSym)
|
||||
*/
|
||||
int32_t sym_IsRelocDiffDefined(char *tzSym1, char *tzSym2)
|
||||
{
|
||||
/* Do nothing the first pass. */
|
||||
if (nPass != 2)
|
||||
return 1;
|
||||
|
||||
const struct sSymbol *nsym1 = sym_FindSymbol(tzSym1);
|
||||
const struct sSymbol *nsym2 = sym_FindSymbol(tzSym2);
|
||||
|
||||
@@ -781,148 +693,76 @@ int32_t sym_IsRelocDiffDefined(char *tzSym1, char *tzSym2)
|
||||
*/
|
||||
void sym_Export(char *tzSym)
|
||||
{
|
||||
if (nPass == 1) {
|
||||
/* only export symbols in pass 1 */
|
||||
struct sSymbol *nsym = sym_FindSymbol(tzSym);
|
||||
struct sSymbol *nsym = sym_FindSymbol(tzSym);
|
||||
|
||||
if (nsym == NULL)
|
||||
nsym = createsymbol(tzSym);
|
||||
if (nsym == NULL)
|
||||
nsym = createsymbol(tzSym);
|
||||
|
||||
if (nsym)
|
||||
nsym->nType |= SYMF_EXPORT;
|
||||
} else {
|
||||
const struct sSymbol *nsym = sym_FindSymbol(tzSym);
|
||||
|
||||
if (nsym != NULL) {
|
||||
if (nsym->nType & SYMF_DEFINED)
|
||||
return;
|
||||
}
|
||||
yyerror("'%s' not defined", tzSym);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Globalize a symbol (export if defined, import if not)
|
||||
*/
|
||||
void sym_Global(char *tzSym)
|
||||
{
|
||||
if (nPass == 2) {
|
||||
/* only globalize symbols in pass 2 */
|
||||
struct sSymbol *nsym = sym_FindSymbol(tzSym);
|
||||
|
||||
if ((nsym == NULL) || ((nsym->nType & SYMF_DEFINED) == 0)) {
|
||||
if (nsym == NULL)
|
||||
nsym = createsymbol(tzSym);
|
||||
|
||||
if (nsym)
|
||||
nsym->nType |= SYMF_IMPORT;
|
||||
} else {
|
||||
if (nsym)
|
||||
nsym->nType |= SYMF_EXPORT;
|
||||
}
|
||||
}
|
||||
if (nsym)
|
||||
nsym->nType |= SYMF_EXPORT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a macro definition
|
||||
*/
|
||||
void sym_AddMacro(char *tzSym)
|
||||
void sym_AddMacro(char *tzSym, int32_t nDefLineNo)
|
||||
{
|
||||
if ((nPass == 1) || ((nPass == 2) && (sym_isDefined(tzSym) == 0))) {
|
||||
/* only add macros in pass 1 */
|
||||
struct sSymbol *nsym;
|
||||
struct sSymbol *nsym = createNonrelocSymbol(tzSym);
|
||||
|
||||
nsym = findsymbol(tzSym, NULL);
|
||||
|
||||
if (nsym != NULL) {
|
||||
if (nsym->nType & SYMF_DEFINED) {
|
||||
yyerror("'%s' already defined in %s(%d)",
|
||||
tzSym, nsym->tzFileName,
|
||||
nsym->nFileLine);
|
||||
}
|
||||
} else {
|
||||
nsym = createsymbol(tzSym);
|
||||
}
|
||||
|
||||
if (nsym) {
|
||||
nsym->nValue = nPC;
|
||||
nsym->nType |= SYMF_MACRO | SYMF_DEFINED;
|
||||
nsym->pScope = NULL;
|
||||
nsym->ulMacroSize = ulNewMacroSize;
|
||||
nsym->pMacro = tzNewMacro;
|
||||
updateSymbolFilename(nsym);
|
||||
}
|
||||
if (nsym) {
|
||||
nsym->nType |= SYMF_MACRO | SYMF_DEFINED;
|
||||
nsym->pScope = NULL;
|
||||
nsym->ulMacroSize = ulNewMacroSize;
|
||||
nsym->pMacro = tzNewMacro;
|
||||
updateSymbolFilename(nsym);
|
||||
/*
|
||||
* The symbol is created at the line after the `endm`,
|
||||
* override this with the actual definition line
|
||||
*/
|
||||
nsym->nFileLine = nDefLineNo;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set whether to export all relocable symbols by default
|
||||
* Flag that a symbol is referenced in an RPN expression
|
||||
* and create it if it doesn't exist yet
|
||||
*/
|
||||
void sym_Ref(char *tzSym)
|
||||
{
|
||||
struct sSymbol *nsym = sym_FindSymbol(tzSym);
|
||||
|
||||
if (nsym == NULL) {
|
||||
char fullname[MAXSYMLEN + 1];
|
||||
int isLocal = 0;
|
||||
|
||||
if (*tzSym == '.') {
|
||||
if (!pScope)
|
||||
fatalerror("Local label reference '%s' in main scope",
|
||||
tzSym);
|
||||
fullSymbolName(fullname, sizeof(fullname), tzSym,
|
||||
pScope);
|
||||
tzSym = fullname;
|
||||
isLocal = 1;
|
||||
}
|
||||
|
||||
nsym = createsymbol(tzSym);
|
||||
|
||||
if (nsym && isLocal)
|
||||
nsym->nType |= SYMF_LOCAL;
|
||||
}
|
||||
|
||||
if (nsym)
|
||||
nsym->nType |= SYMF_REF;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set whether to export all relocatable symbols by default
|
||||
*/
|
||||
void sym_SetExportAll(uint8_t set)
|
||||
{
|
||||
exportall = set;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare for pass #1
|
||||
*/
|
||||
void sym_PrepPass1(void)
|
||||
{
|
||||
sym_Init();
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare for pass #2
|
||||
*/
|
||||
void sym_PrepPass2(void)
|
||||
{
|
||||
int32_t i;
|
||||
|
||||
for (i = 0; i < HASHSIZE; i += 1) {
|
||||
struct sSymbol **ppSym = &(tHashedSymbols[i]);
|
||||
|
||||
while (*ppSym) {
|
||||
uint32_t mask = SYMF_SET | SYMF_STRING | SYMF_EQU;
|
||||
|
||||
if ((*ppSym)->nType & mask) {
|
||||
struct sSymbol *pTemp;
|
||||
|
||||
pTemp = (*ppSym)->pNext;
|
||||
free(*ppSym);
|
||||
*ppSym = pTemp;
|
||||
} else {
|
||||
ppSym = &((*ppSym)->pNext);
|
||||
}
|
||||
}
|
||||
}
|
||||
pScope = NULL;
|
||||
pPCSymbol->nValue = 0;
|
||||
|
||||
sym_AddString("__TIME__", SavedTIME);
|
||||
sym_AddString("__DATE__", SavedDATE);
|
||||
sym_AddString("__ISO_8601_LOCAL__", SavedTIMESTAMP_ISO8601_LOCAL);
|
||||
sym_AddString("__ISO_8601_UTC__", SavedTIMESTAMP_ISO8601_UTC);
|
||||
sym_AddString("__UTC_DAY__", SavedDAY);
|
||||
sym_AddString("__UTC_MONTH__", SavedMONTH);
|
||||
sym_AddString("__UTC_YEAR__", SavedYEAR);
|
||||
sym_AddString("__UTC_HOUR__", SavedHOUR);
|
||||
sym_AddString("__UTC_MINUTE__", SavedMINUTE);
|
||||
sym_AddString("__UTC_SECOND__", SavedSECOND);
|
||||
sym_AddEqu("__RGBDS_MAJOR__", PACKAGE_VERSION_MAJOR);
|
||||
sym_AddEqu("__RGBDS_MINOR__", PACKAGE_VERSION_MINOR);
|
||||
sym_AddEqu("__RGBDS_PATCH__", PACKAGE_VERSION_PATCH);
|
||||
sym_AddSet("_RS", 0);
|
||||
|
||||
sym_AddEqu("_NARG", 0);
|
||||
p_NARGSymbol = findsymbol("_NARG", NULL);
|
||||
p_NARGSymbol->Callback = Callback_NARG;
|
||||
sym_AddEqu("__LINE__", 0);
|
||||
p__LINE__Symbol = findsymbol("__LINE__", NULL);
|
||||
p__LINE__Symbol->Callback = Callback__LINE__;
|
||||
|
||||
math_DefinePI();
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the symboltable
|
||||
*/
|
||||
@@ -931,12 +771,12 @@ void sym_Init(void)
|
||||
int32_t i;
|
||||
time_t now;
|
||||
|
||||
for (i = 0; i < MAXMACROARGS; i += 1) {
|
||||
for (i = 0; i < MAXMACROARGS; i++) {
|
||||
currentmacroargs[i] = NULL;
|
||||
newmacroargs[i] = NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < HASHSIZE; i += 1)
|
||||
for (i = 0; i < HASHSIZE; i++)
|
||||
tHashedSymbols[i] = NULL;
|
||||
|
||||
sym_AddReloc("@");
|
||||
@@ -948,6 +788,10 @@ void sym_Init(void)
|
||||
p__LINE__Symbol = findsymbol("__LINE__", NULL);
|
||||
p__LINE__Symbol->Callback = Callback__LINE__;
|
||||
|
||||
sym_AddEqu("__RGBDS_MAJOR__", PACKAGE_VERSION_MAJOR);
|
||||
sym_AddEqu("__RGBDS_MINOR__", PACKAGE_VERSION_MINOR);
|
||||
sym_AddEqu("__RGBDS_PATCH__", PACKAGE_VERSION_PATCH);
|
||||
|
||||
sym_AddSet("_RS", 0);
|
||||
|
||||
if (time(&now) != -1) {
|
||||
|
||||
46
src/asm/util.c
Normal file
46
src/asm/util.c
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* This file is part of RGBDS.
|
||||
*
|
||||
* Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "asm/main.h"
|
||||
#include "asm/util.h"
|
||||
|
||||
#include "extern/utf8decoder.h"
|
||||
|
||||
/*
|
||||
* Calculate the hash value for a string
|
||||
*/
|
||||
uint32_t calchash(const char *s)
|
||||
{
|
||||
uint32_t hash = 5381;
|
||||
|
||||
while (*s != 0)
|
||||
hash = (hash * 33) ^ (*s++);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
int32_t readUTF8Char(char *dest, char *src)
|
||||
{
|
||||
uint32_t state;
|
||||
uint32_t codep;
|
||||
int32_t i;
|
||||
|
||||
for (i = 0, state = 0;; i++) {
|
||||
if (decode(&state, &codep, (uint8_t)src[i]) == 1)
|
||||
fatalerror("invalid UTF-8 character");
|
||||
|
||||
dest[i] = src[i];
|
||||
|
||||
if (state == 0) {
|
||||
dest[++i] = '\0';
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -268,10 +268,7 @@ int main(int argc, char *argv[])
|
||||
* characters may conflict with the title.
|
||||
*/
|
||||
|
||||
int n = snprintf((char *)header + 0x34, 16, "%s", title);
|
||||
|
||||
for (int i = 16; i > n; i--)
|
||||
header[0x34 + i] = '\0';
|
||||
strncpy((char *)header + 0x34, title, 16);
|
||||
}
|
||||
|
||||
if (setid) {
|
||||
|
||||
189
src/gfx/gb.c
189
src/gfx/gb.c
@@ -85,8 +85,87 @@ int get_tile_index(uint8_t *tile, uint8_t **tiles, int num_tiles, int tile_size)
|
||||
return -1;
|
||||
}
|
||||
|
||||
void create_tilemap(const struct Options *opts, struct GBImage *gb,
|
||||
struct Tilemap *tilemap)
|
||||
uint8_t reverse_bits(uint8_t b)
|
||||
{
|
||||
uint8_t rev = 0;
|
||||
|
||||
rev |= (b & 0x80) >> 7;
|
||||
rev |= (b & 0x40) >> 5;
|
||||
rev |= (b & 0x20) >> 3;
|
||||
rev |= (b & 0x10) >> 1;
|
||||
rev |= (b & 0x08) << 1;
|
||||
rev |= (b & 0x04) << 3;
|
||||
rev |= (b & 0x02) << 5;
|
||||
rev |= (b & 0x01) << 7;
|
||||
return rev;
|
||||
}
|
||||
|
||||
void xflip(uint8_t *tile, uint8_t *tile_xflip, int tile_size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < tile_size; i++)
|
||||
tile_xflip[i] = reverse_bits(tile[i]);
|
||||
}
|
||||
|
||||
void yflip(uint8_t *tile, uint8_t *tile_yflip, int tile_size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < tile_size; i++)
|
||||
tile_yflip[i] = tile[(tile_size - i - 1) ^ (depth - 1)];
|
||||
}
|
||||
|
||||
/*
|
||||
* get_mirrored_tile_index looks for `tile` in tile array `tiles`, also
|
||||
* checking x-, y-, and xy-mirrored versions of `tile`. If one is found,
|
||||
* `*flags` is set according to the type of mirroring and the index of the
|
||||
* matched tile is returned. If no match is found, -1 is returned.
|
||||
*/
|
||||
int get_mirrored_tile_index(uint8_t *tile, uint8_t **tiles, int num_tiles,
|
||||
int tile_size, int *flags)
|
||||
{
|
||||
int index;
|
||||
uint8_t *tile_xflip;
|
||||
uint8_t *tile_yflip;
|
||||
|
||||
index = get_tile_index(tile, tiles, num_tiles, tile_size);
|
||||
if (index >= 0) {
|
||||
*flags = 0;
|
||||
return index;
|
||||
}
|
||||
|
||||
tile_yflip = malloc(tile_size);
|
||||
yflip(tile, tile_yflip, tile_size);
|
||||
index = get_tile_index(tile_yflip, tiles, num_tiles, tile_size);
|
||||
if (index >= 0) {
|
||||
*flags = YFLIP;
|
||||
free(tile_yflip);
|
||||
return index;
|
||||
}
|
||||
|
||||
tile_xflip = malloc(tile_size);
|
||||
xflip(tile, tile_xflip, tile_size);
|
||||
index = get_tile_index(tile_xflip, tiles, num_tiles, tile_size);
|
||||
if (index >= 0) {
|
||||
*flags = XFLIP;
|
||||
free(tile_yflip);
|
||||
free(tile_xflip);
|
||||
return index;
|
||||
}
|
||||
|
||||
yflip(tile_xflip, tile_yflip, tile_size);
|
||||
index = get_tile_index(tile_yflip, tiles, num_tiles, tile_size);
|
||||
if (index >= 0)
|
||||
*flags = XFLIP | YFLIP;
|
||||
|
||||
free(tile_yflip);
|
||||
free(tile_xflip);
|
||||
return index;
|
||||
}
|
||||
|
||||
void create_mapfiles(const struct Options *opts, struct GBImage *gb,
|
||||
struct Mapfile *tilemap, struct Mapfile *attrmap)
|
||||
{
|
||||
int i, j;
|
||||
int gb_i;
|
||||
@@ -94,6 +173,7 @@ void create_tilemap(const struct Options *opts, struct GBImage *gb,
|
||||
int max_tiles;
|
||||
int num_tiles;
|
||||
int index;
|
||||
int flags;
|
||||
int gb_size;
|
||||
uint8_t *tile;
|
||||
uint8_t **tiles;
|
||||
@@ -109,19 +189,33 @@ void create_tilemap(const struct Options *opts, struct GBImage *gb,
|
||||
tiles = calloc(max_tiles, sizeof(uint8_t *));
|
||||
num_tiles = 0;
|
||||
|
||||
tilemap->data = calloc(max_tiles, sizeof(uint8_t));
|
||||
tilemap->size = 0;
|
||||
if (*opts->tilemapfile) {
|
||||
tilemap->data = calloc(max_tiles, sizeof(uint8_t));
|
||||
tilemap->size = 0;
|
||||
}
|
||||
|
||||
if (*opts->attrmapfile) {
|
||||
attrmap->data = calloc(max_tiles, sizeof(uint8_t));
|
||||
attrmap->size = 0;
|
||||
}
|
||||
|
||||
|
||||
gb_i = 0;
|
||||
while (gb_i < gb_size) {
|
||||
flags = 0;
|
||||
tile = malloc(tile_size);
|
||||
for (i = 0; i < tile_size; i++) {
|
||||
tile[i] = gb->data[gb_i];
|
||||
gb_i++;
|
||||
}
|
||||
if (opts->unique) {
|
||||
index = get_tile_index(tile, tiles, num_tiles,
|
||||
tile_size);
|
||||
if (opts->mirror) {
|
||||
index = get_mirrored_tile_index(tile, tiles, num_tiles,
|
||||
tile_size, &flags);
|
||||
} else {
|
||||
index = get_tile_index(tile, tiles, num_tiles,
|
||||
tile_size);
|
||||
}
|
||||
if (index < 0) {
|
||||
index = num_tiles;
|
||||
tiles[num_tiles] = tile;
|
||||
@@ -132,8 +226,14 @@ void create_tilemap(const struct Options *opts, struct GBImage *gb,
|
||||
tiles[num_tiles] = tile;
|
||||
num_tiles++;
|
||||
}
|
||||
tilemap->data[tilemap->size] = index;
|
||||
tilemap->size++;
|
||||
if (*opts->tilemapfile) {
|
||||
tilemap->data[tilemap->size] = index;
|
||||
tilemap->size++;
|
||||
}
|
||||
if (*opts->attrmapfile) {
|
||||
attrmap->data[attrmap->size] = flags;
|
||||
attrmap->size++;
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->unique) {
|
||||
@@ -154,21 +254,61 @@ void create_tilemap(const struct Options *opts, struct GBImage *gb,
|
||||
}
|
||||
|
||||
void output_tilemap_file(const struct Options *opts,
|
||||
const struct Tilemap *tilemap)
|
||||
const struct Mapfile *tilemap)
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
f = fopen(opts->mapfile, "wb");
|
||||
f = fopen(opts->tilemapfile, "wb");
|
||||
if (!f)
|
||||
err(1, "Opening tilemap file '%s' failed", opts->mapfile);
|
||||
err(1, "Opening tilemap file '%s' failed", opts->tilemapfile);
|
||||
|
||||
fwrite(tilemap->data, 1, tilemap->size, f);
|
||||
fclose(f);
|
||||
|
||||
if (opts->mapout)
|
||||
free(opts->mapfile);
|
||||
if (opts->tilemapout)
|
||||
free(opts->tilemapfile);
|
||||
}
|
||||
|
||||
void output_attrmap_file(const struct Options *opts,
|
||||
const struct Mapfile *attrmap)
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
f = fopen(opts->attrmapfile, "wb");
|
||||
if (!f)
|
||||
err(1, "Opening attrmap file '%s' failed", opts->attrmapfile);
|
||||
|
||||
fwrite(attrmap->data, 1, attrmap->size, f);
|
||||
fclose(f);
|
||||
|
||||
if (opts->attrmapout)
|
||||
free(opts->attrmapfile);
|
||||
}
|
||||
|
||||
/*
|
||||
* based on the Gaussian-like curve used by SameBoy since commit
|
||||
* 65dd02cc52f531dbbd3a7e6014e99d5b24e71a4c (Oct 2017)
|
||||
* with ties resolved by comparing the difference of the squares.
|
||||
*/
|
||||
static int reverse_curve[] = {
|
||||
0, 0, 1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4,
|
||||
5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10,
|
||||
10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
|
||||
12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13,
|
||||
13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14,
|
||||
14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17,
|
||||
17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18,
|
||||
18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
|
||||
19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21,
|
||||
21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24,
|
||||
24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26,
|
||||
26, 27, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 30, 30, 31,
|
||||
};
|
||||
|
||||
void output_palette_file(const struct Options *opts,
|
||||
const struct RawIndexedImage *raw_image)
|
||||
{
|
||||
@@ -181,10 +321,25 @@ void output_palette_file(const struct Options *opts,
|
||||
err(1, "Opening palette file '%s' failed", opts->palfile);
|
||||
|
||||
for (i = 0; i < raw_image->num_colors; i++) {
|
||||
color =
|
||||
raw_image->palette[i].blue >> 3 << 10 |
|
||||
raw_image->palette[i].green >> 3 << 5 |
|
||||
raw_image->palette[i].red >> 3;
|
||||
int r = raw_image->palette[i].red;
|
||||
int g = raw_image->palette[i].green;
|
||||
int b = raw_image->palette[i].blue;
|
||||
|
||||
if (opts->colorcurve) {
|
||||
g = (g * 4 - b) / 3;
|
||||
if (g < 0)
|
||||
g = 0;
|
||||
|
||||
r = reverse_curve[r];
|
||||
g = reverse_curve[g];
|
||||
b = reverse_curve[b];
|
||||
} else {
|
||||
r >>= 3;
|
||||
g >>= 3;
|
||||
b >>= 3;
|
||||
}
|
||||
|
||||
color = b << 10 | g << 5 | r;
|
||||
cur_bytes[0] = color & 0xFF;
|
||||
cur_bytes[1] = color >> 8;
|
||||
fwrite(cur_bytes, 2, 1, f);
|
||||
|
||||
109
src/gfx/main.c
109
src/gfx/main.c
@@ -18,8 +18,8 @@
|
||||
static void print_usage(void)
|
||||
{
|
||||
printf(
|
||||
"usage: rgbgfx [-DFfhPTuVv] [-d #] [-o outfile] [-p palfile] [-t mapfile]\n"
|
||||
" [-x #] infile\n");
|
||||
"usage: rgbgfx [-ADFfhmPTuVv] [-o outfile] [-a attrmap] [-d #] [-p palfile]\n"
|
||||
" [-t tilemap] [-x #] infile\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -30,21 +30,32 @@ int main(int argc, char *argv[])
|
||||
struct ImageOptions png_options = {0};
|
||||
struct RawIndexedImage *raw_image;
|
||||
struct GBImage gb = {0};
|
||||
struct Tilemap tilemap = {0};
|
||||
struct Mapfile tilemap = {0};
|
||||
struct Mapfile attrmap = {0};
|
||||
char *ext;
|
||||
const char *errmsg = "Warning: The PNG's %s setting is not the same as the setting defined on the command line.";
|
||||
|
||||
if (argc == 1)
|
||||
print_usage();
|
||||
|
||||
opts.mapfile = "";
|
||||
opts.tilemapfile = "";
|
||||
opts.attrmapfile = "";
|
||||
opts.palfile = "";
|
||||
opts.outfile = "";
|
||||
|
||||
depth = 2;
|
||||
|
||||
while ((ch = getopt(argc, argv, "Dd:Ffho:Tt:uPp:Vvx:")) != -1) {
|
||||
while ((ch = getopt(argc, argv, "Aa:CDd:Ffhmo:Tt:uPp:Vvx:")) != -1) {
|
||||
switch (ch) {
|
||||
case 'A':
|
||||
opts.attrmapout = true;
|
||||
break;
|
||||
case 'a':
|
||||
opts.attrmapfile = optarg;
|
||||
break;
|
||||
case 'C':
|
||||
opts.colorcurve = true;
|
||||
break;
|
||||
case 'D':
|
||||
opts.debug = true;
|
||||
break;
|
||||
@@ -60,6 +71,10 @@ int main(int argc, char *argv[])
|
||||
case 'h':
|
||||
opts.horizontal = true;
|
||||
break;
|
||||
case 'm':
|
||||
opts.mirror = true;
|
||||
opts.unique = true;
|
||||
break;
|
||||
case 'o':
|
||||
opts.outfile = optarg;
|
||||
break;
|
||||
@@ -70,10 +85,10 @@ int main(int argc, char *argv[])
|
||||
opts.palfile = optarg;
|
||||
break;
|
||||
case 'T':
|
||||
opts.mapout = true;
|
||||
opts.tilemapout = true;
|
||||
break;
|
||||
case 't':
|
||||
opts.mapfile = optarg;
|
||||
opts.tilemapfile = optarg;
|
||||
break;
|
||||
case 'u':
|
||||
opts.unique = true;
|
||||
@@ -107,7 +122,8 @@ int main(int argc, char *argv[])
|
||||
|
||||
raw_image = input_png_file(&opts, &png_options);
|
||||
|
||||
png_options.mapfile = "";
|
||||
png_options.tilemapfile = "";
|
||||
png_options.attrmapfile = "";
|
||||
png_options.palfile = "";
|
||||
|
||||
if (png_options.horizontal != opts.horizontal) {
|
||||
@@ -148,25 +164,45 @@ int main(int argc, char *argv[])
|
||||
(raw_image->width / 8) * (raw_image->height / 8) - 1);
|
||||
}
|
||||
|
||||
if (strcmp(png_options.mapfile, opts.mapfile) != 0) {
|
||||
if (strcmp(png_options.tilemapfile, opts.tilemapfile) != 0) {
|
||||
if (opts.verbose)
|
||||
warnx(errmsg, "tilemap file");
|
||||
|
||||
if (opts.hardfix)
|
||||
png_options.mapfile = opts.mapfile;
|
||||
png_options.tilemapfile = opts.tilemapfile;
|
||||
}
|
||||
if (!*opts.mapfile)
|
||||
opts.mapfile = png_options.mapfile;
|
||||
if (!*opts.tilemapfile)
|
||||
opts.tilemapfile = png_options.tilemapfile;
|
||||
|
||||
if (png_options.mapout != opts.mapout) {
|
||||
if (png_options.tilemapout != opts.tilemapout) {
|
||||
if (opts.verbose)
|
||||
warnx(errmsg, "tilemap file");
|
||||
|
||||
if (opts.hardfix)
|
||||
png_options.mapout = opts.mapout;
|
||||
png_options.tilemapout = opts.tilemapout;
|
||||
}
|
||||
if (png_options.mapout)
|
||||
opts.mapout = png_options.mapout;
|
||||
if (png_options.tilemapout)
|
||||
opts.tilemapout = png_options.tilemapout;
|
||||
|
||||
if (strcmp(png_options.attrmapfile, opts.attrmapfile) != 0) {
|
||||
if (opts.verbose)
|
||||
warnx(errmsg, "attrmap file");
|
||||
|
||||
if (opts.hardfix)
|
||||
png_options.attrmapfile = opts.attrmapfile;
|
||||
}
|
||||
if (!*opts.attrmapfile)
|
||||
opts.attrmapfile = png_options.attrmapfile;
|
||||
|
||||
if (png_options.attrmapout != opts.attrmapout) {
|
||||
if (opts.verbose)
|
||||
warnx(errmsg, "attrmap file");
|
||||
|
||||
if (opts.hardfix)
|
||||
png_options.attrmapout = opts.attrmapout;
|
||||
}
|
||||
if (png_options.attrmapout)
|
||||
opts.attrmapout = png_options.attrmapout;
|
||||
|
||||
if (strcmp(png_options.palfile, opts.palfile) != 0) {
|
||||
if (opts.verbose)
|
||||
@@ -189,19 +225,35 @@ int main(int argc, char *argv[])
|
||||
if (png_options.palout)
|
||||
opts.palout = png_options.palout;
|
||||
|
||||
if (!*opts.mapfile && opts.mapout) {
|
||||
if (!*opts.tilemapfile && opts.tilemapout) {
|
||||
ext = strrchr(opts.infile, '.');
|
||||
|
||||
if (ext != NULL) {
|
||||
size = ext - opts.infile + 9;
|
||||
opts.mapfile = malloc(size);
|
||||
strncpy(opts.mapfile, opts.infile, size);
|
||||
*strrchr(opts.mapfile, '.') = '\0';
|
||||
strcat(opts.mapfile, ".tilemap");
|
||||
opts.tilemapfile = malloc(size);
|
||||
strncpy(opts.tilemapfile, opts.infile, size);
|
||||
*strrchr(opts.tilemapfile, '.') = '\0';
|
||||
strcat(opts.tilemapfile, ".tilemap");
|
||||
} else {
|
||||
opts.mapfile = malloc(strlen(opts.infile) + 9);
|
||||
strcpy(opts.mapfile, opts.infile);
|
||||
strcat(opts.mapfile, ".tilemap");
|
||||
opts.tilemapfile = malloc(strlen(opts.infile) + 9);
|
||||
strcpy(opts.tilemapfile, opts.infile);
|
||||
strcat(opts.tilemapfile, ".tilemap");
|
||||
}
|
||||
}
|
||||
|
||||
if (!*opts.attrmapfile && opts.attrmapout) {
|
||||
ext = strrchr(opts.infile, '.');
|
||||
|
||||
if (ext != NULL) {
|
||||
size = ext - opts.infile + 9;
|
||||
opts.attrmapfile = malloc(size);
|
||||
strncpy(opts.attrmapfile, opts.infile, size);
|
||||
*strrchr(opts.attrmapfile, '.') = '\0';
|
||||
strcat(opts.attrmapfile, ".attrmap");
|
||||
} else {
|
||||
opts.attrmapfile = malloc(strlen(opts.infile) + 9);
|
||||
strcpy(opts.attrmapfile, opts.infile);
|
||||
strcat(opts.attrmapfile, ".attrmap");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,17 +278,20 @@ int main(int argc, char *argv[])
|
||||
gb.trim = opts.trim;
|
||||
gb.horizontal = opts.horizontal;
|
||||
|
||||
if (*opts.outfile || *opts.mapfile) {
|
||||
if (*opts.outfile || *opts.tilemapfile || *opts.attrmapfile) {
|
||||
raw_to_gb(raw_image, &gb);
|
||||
create_tilemap(&opts, &gb, &tilemap);
|
||||
create_mapfiles(&opts, &gb, &tilemap, &attrmap);
|
||||
}
|
||||
|
||||
if (*opts.outfile)
|
||||
output_file(&opts, &gb);
|
||||
|
||||
if (*opts.mapfile)
|
||||
if (*opts.tilemapfile)
|
||||
output_tilemap_file(&opts, &tilemap);
|
||||
|
||||
if (*opts.attrmapfile)
|
||||
output_attrmap_file(&opts, &attrmap);
|
||||
|
||||
if (*opts.palfile)
|
||||
output_palette_file(&opts, raw_image);
|
||||
|
||||
|
||||
@@ -199,8 +199,8 @@ static struct RawIndexedImage *indexed_png_to_raw(struct PNGImage *img)
|
||||
raw_image = create_raw_image(img->width, img->height, colors);
|
||||
|
||||
/*
|
||||
* Transparent palette entries are removed, and the palette is collapsed.
|
||||
* Transparent pixels are then replaced with palette index 0.
|
||||
* Transparent palette entries are removed, and the palette is
|
||||
* collapsed. Transparent pixels are then replaced with palette index 0.
|
||||
* This way, an indexed PNG can contain transparent pixels in *addition*
|
||||
* to 4 normal colors.
|
||||
*/
|
||||
@@ -649,10 +649,16 @@ static void get_text(const struct PNGImage *img,
|
||||
png_options->trim = strtoul(text[i].text, NULL, 0);
|
||||
png_free_data(img->png, img->info, PNG_FREE_TEXT, i);
|
||||
} else if (strcmp(text[i].key, "t") == 0) {
|
||||
png_options->mapfile = text[i].text;
|
||||
png_options->tilemapfile = text[i].text;
|
||||
png_free_data(img->png, img->info, PNG_FREE_TEXT, i);
|
||||
} else if (strcmp(text[i].key, "T") == 0 && !*text[i].text) {
|
||||
png_options->mapout = true;
|
||||
png_options->tilemapout = true;
|
||||
png_free_data(img->png, img->info, PNG_FREE_TEXT, i);
|
||||
} else if (strcmp(text[i].key, "a") == 0) {
|
||||
png_options->attrmapfile = text[i].text;
|
||||
png_free_data(img->png, img->info, PNG_FREE_TEXT, i);
|
||||
} else if (strcmp(text[i].key, "A") == 0 && !*text[i].text) {
|
||||
png_options->attrmapout = true;
|
||||
png_free_data(img->png, img->info, PNG_FREE_TEXT, i);
|
||||
} else if (strcmp(text[i].key, "p") == 0) {
|
||||
png_options->palfile = text[i].text;
|
||||
@@ -699,18 +705,30 @@ static void set_text(const struct PNGImage *img,
|
||||
text[0].compression = PNG_TEXT_COMPRESSION_NONE;
|
||||
png_set_text(img->png, img->info, text, 1);
|
||||
}
|
||||
if (*png_options->mapfile) {
|
||||
if (*png_options->tilemapfile) {
|
||||
text[0].key = "t";
|
||||
text[0].text = "";
|
||||
text[0].compression = PNG_TEXT_COMPRESSION_NONE;
|
||||
png_set_text(img->png, img->info, text, 1);
|
||||
}
|
||||
if (png_options->mapout) {
|
||||
if (png_options->tilemapout) {
|
||||
text[0].key = "T";
|
||||
text[0].text = "";
|
||||
text[0].compression = PNG_TEXT_COMPRESSION_NONE;
|
||||
png_set_text(img->png, img->info, text, 1);
|
||||
}
|
||||
if (*png_options->attrmapfile) {
|
||||
text[0].key = "a";
|
||||
text[0].text = "";
|
||||
text[0].compression = PNG_TEXT_COMPRESSION_NONE;
|
||||
png_set_text(img->png, img->info, text, 1);
|
||||
}
|
||||
if (png_options->attrmapout) {
|
||||
text[0].key = "A";
|
||||
text[0].text = "";
|
||||
text[0].compression = PNG_TEXT_COMPRESSION_NONE;
|
||||
png_set_text(img->png, img->info, text, 1);
|
||||
}
|
||||
if (*png_options->palfile) {
|
||||
text[0].key = "p";
|
||||
text[0].text = "";
|
||||
|
||||
@@ -13,11 +13,12 @@
|
||||
.Nd Game Boy graphics converter
|
||||
.Sh SYNOPSIS
|
||||
.Nm rgbgfx
|
||||
.Op Fl DfFhPTVv
|
||||
.Op Fl ADfFhmPTuVv
|
||||
.Op Fl o Ar outfile
|
||||
.Op Fl a Ar attrmap
|
||||
.Op Fl d Ar depth
|
||||
.Op Fl p Ar palfile
|
||||
.Op Fl t Ar mapfile
|
||||
.Op Fl t Ar tilemap
|
||||
.Op Fl x Ar tiles
|
||||
.Ar file
|
||||
.Sh DESCRIPTION
|
||||
@@ -47,6 +48,21 @@ The input image may not contain more colors than the selected bit depth
|
||||
allows. Transparent pixels are set to palette index 0.
|
||||
.Sh ARGUMENTS
|
||||
.Bl -tag -width Ds
|
||||
.It Fl a Ar attrmap
|
||||
Generate a file of tile mirroring attributes for OAM or (CGB-only) background
|
||||
tiles. For each tile in the input file, a byte is written representing the
|
||||
dimensions that the associated tile in the output file should be mirrored.
|
||||
Useful in combination with
|
||||
.Fl m
|
||||
to keep track the mirror direction of mirrored duplicate tiles.
|
||||
.It Fl A
|
||||
Same as
|
||||
.Fl a ,
|
||||
but the attrmap file output name is made by taking the input filename, removing
|
||||
the file extension, and appending
|
||||
.Pa .attrmap .
|
||||
.It Fl C
|
||||
Use the color curve of the Game Boy Color when generating palettes.
|
||||
.It Fl D
|
||||
Debug features are enabled.
|
||||
.It Fl f
|
||||
@@ -61,6 +77,12 @@ The bit depth of the output image (either 1 or 2).
|
||||
By default, the bit depth is 2 (two bits per pixel).
|
||||
.It Fl h
|
||||
Lay out tiles horizontally rather than vertically.
|
||||
.It Fl m
|
||||
Truncate tiles by checking for tiles that are mirrored versions of others and
|
||||
omitting these from the output file. Useful with tilemaps and attrmaps together
|
||||
to keep track of the duplicated tiles and the dimension mirrored. Tiles are
|
||||
checked for horizontal, vertical, and horizontal-vertical mirroring. Implies
|
||||
.Fl u .
|
||||
.It Fl o Ar outfile
|
||||
The name of the output file.
|
||||
.It Fl p Ar palfile
|
||||
@@ -74,17 +96,24 @@ Same as
|
||||
but the palette file output name is made by taking the input PNG file's
|
||||
filename, removing the file extension, and appending
|
||||
.Pa .pal .
|
||||
.It Fl t Ar mapfile
|
||||
If any tiles are the same, don't place the repeat tiles in the output file, and
|
||||
make a tilemap file.
|
||||
.It Fl t Ar tilemap
|
||||
Generate a file of tile indices. For each tile in the input file, a byte is
|
||||
written representing the index of the associated tile in the output file.
|
||||
Useful in combination with
|
||||
.Fl u
|
||||
or
|
||||
.Fl m
|
||||
to keep track of duplicate tiles.
|
||||
.It Fl T
|
||||
Same as
|
||||
.Fl t ,
|
||||
but the tilemap file output name is made by taking the input filename,
|
||||
removing the file extension, and appending
|
||||
but the tilemap file output name is made by taking the input filename, removing
|
||||
the file extension, and appending
|
||||
.Pa .tilemap .
|
||||
.It Fl u
|
||||
Truncate repeated tiles. Useful with tilemaps.
|
||||
Truncate tiles by checking for tiles that are exact duplicates of others and
|
||||
omitting these from the output file. Useful with tilemaps to keep track of the
|
||||
duplicated tiles.
|
||||
.It Fl V
|
||||
Print the version of the program and exit.
|
||||
.It Fl v
|
||||
@@ -105,6 +134,14 @@ The following creates a planar 2bpp file with only unique tiles, and its tilemap
|
||||
.Pp
|
||||
.D1 $ rgbgfx -T -u -o out.2bpp in.png
|
||||
.Pp
|
||||
The following creates a planar 2bpp file with only unique tiles (accounting for
|
||||
tile mirroring) and its associated tilemap
|
||||
.Pa out.tilemap
|
||||
and attrmap
|
||||
.Pa out.attrmap :
|
||||
.Pp
|
||||
.D1 $ rgbgfx -A -T -m -o out.2bpp in.png
|
||||
.Pp
|
||||
The following will do nothing:
|
||||
.Pp
|
||||
.D1 $ rgbgfx in.png
|
||||
|
||||
2
src/link/.gitignore
vendored
2
src/link/.gitignore
vendored
@@ -1,2 +1,4 @@
|
||||
parser.c
|
||||
parser.h
|
||||
lexer.c
|
||||
lexer.h
|
||||
|
||||
@@ -155,6 +155,11 @@ int32_t area_Avail(int32_t bank)
|
||||
|
||||
int32_t area_doAlloc(struct sFreeArea *pArea, int32_t org, int32_t size)
|
||||
{
|
||||
if (size == 0) {
|
||||
/* 0-byte SECTIONs don't take any room, they can go anywhere */
|
||||
return org;
|
||||
}
|
||||
|
||||
if ((org >= pArea->nOrg)
|
||||
&& ((org + size) <= (pArea->nOrg + pArea->nSize))) {
|
||||
|
||||
@@ -695,22 +700,22 @@ void CreateSymbolTable(void)
|
||||
if ((tSymbol->Type == SYM_EXPORT) &&
|
||||
((tSymbol->pSection == pSect) ||
|
||||
(tSymbol->pSection == NULL))) {
|
||||
if (tSymbol->pSection == NULL)
|
||||
sym_CreateSymbol(
|
||||
tSymbol->pzName,
|
||||
tSymbol->nOffset,
|
||||
-1,
|
||||
tSymbol->pzObjFileName,
|
||||
tSymbol->pzFileName,
|
||||
tSymbol->nFileLine);
|
||||
else
|
||||
sym_CreateSymbol(
|
||||
tSymbol->pzName,
|
||||
pSect->nOrg + tSymbol->nOffset,
|
||||
pSect->nBank,
|
||||
tSymbol->pzObjFileName,
|
||||
tSymbol->pzFileName,
|
||||
tSymbol->nFileLine);
|
||||
if (tSymbol->pSection == NULL) {
|
||||
sym_CreateSymbol(tSymbol->pzName,
|
||||
tSymbol->nOffset,
|
||||
-1,
|
||||
tSymbol->pzObjFileName,
|
||||
tSymbol->pzFileName,
|
||||
tSymbol->nFileLine);
|
||||
} else {
|
||||
sym_CreateSymbol(tSymbol->pzName,
|
||||
pSect->nOrg +
|
||||
tSymbol->nOffset,
|
||||
pSect->nBank,
|
||||
tSymbol->pzObjFileName,
|
||||
tSymbol->pzFileName,
|
||||
tSymbol->nFileLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
pSect = pSect->pNext;
|
||||
|
||||
@@ -18,13 +18,18 @@
|
||||
#include "link/mylink.h"
|
||||
#include "link/symbol.h"
|
||||
|
||||
#define RPN_STACK_SIZE 256
|
||||
|
||||
static struct sSection *pCurrentSection;
|
||||
static int32_t rpnstack[256];
|
||||
static int32_t rpnstack[RPN_STACK_SIZE];
|
||||
static int32_t rpnp;
|
||||
int32_t nPC;
|
||||
|
||||
static void rpnpush(int32_t i)
|
||||
{
|
||||
if (rpnp >= RPN_STACK_SIZE)
|
||||
errx(1, "RPN stack overflow");
|
||||
|
||||
rpnstack[rpnp] = i;
|
||||
rpnp++;
|
||||
}
|
||||
@@ -35,13 +40,13 @@ static int32_t rpnpop(void)
|
||||
return rpnstack[rpnp];
|
||||
}
|
||||
|
||||
static int32_t getsymvalue(int32_t symid)
|
||||
static int32_t getsymvalue(struct sPatch *pPatch, int32_t symid)
|
||||
{
|
||||
const struct sSymbol *tSymbol = pCurrentSection->tSymbols[symid];
|
||||
|
||||
switch (tSymbol->Type) {
|
||||
case SYM_IMPORT:
|
||||
return sym_GetValue(tSymbol->pzName);
|
||||
return sym_GetValue(pPatch, tSymbol->pzName);
|
||||
|
||||
case SYM_EXPORT:
|
||||
case SYM_LOCAL:
|
||||
@@ -75,14 +80,14 @@ static int32_t getrealbankfrominternalbank(int32_t n)
|
||||
return n;
|
||||
}
|
||||
|
||||
static int32_t getsymbank(int32_t symid)
|
||||
static int32_t getsymbank(struct sPatch *pPatch, int32_t symid)
|
||||
{
|
||||
int32_t nBank;
|
||||
const struct sSymbol *tSymbol = pCurrentSection->tSymbols[symid];
|
||||
|
||||
switch (tSymbol->Type) {
|
||||
case SYM_IMPORT:
|
||||
nBank = sym_GetBank(tSymbol->pzName);
|
||||
nBank = sym_GetBank(pPatch, tSymbol->pzName);
|
||||
break;
|
||||
case SYM_EXPORT:
|
||||
case SYM_LOCAL:
|
||||
@@ -209,8 +214,8 @@ int32_t calcrpn(struct sPatch *pPatch)
|
||||
t |= (*rpn++) << 8;
|
||||
t |= (*rpn++) << 16;
|
||||
t |= (*rpn++) << 24;
|
||||
rpnpush(getsymvalue(t));
|
||||
pPatch->oRelocPatch |= (getsymbank(t) != -1);
|
||||
rpnpush(getsymvalue(pPatch, t));
|
||||
pPatch->oRelocPatch |= (getsymbank(pPatch, t) != -1);
|
||||
size -= 4;
|
||||
break;
|
||||
case RPN_BANK_SYM:
|
||||
@@ -219,7 +224,7 @@ int32_t calcrpn(struct sPatch *pPatch)
|
||||
t |= (*rpn++) << 8;
|
||||
t |= (*rpn++) << 16;
|
||||
t |= (*rpn++) << 24;
|
||||
rpnpush(getsymbank(t));
|
||||
rpnpush(getsymbank(pPatch, t));
|
||||
size -= 4;
|
||||
break;
|
||||
case RPN_BANK_SECT:
|
||||
@@ -229,7 +234,9 @@ int32_t calcrpn(struct sPatch *pPatch)
|
||||
struct sSection *pSection = GetSectionByName(name);
|
||||
|
||||
if (pSection == NULL) {
|
||||
errx(1, "Requested BANK() of section \"%s\", which was not found.\n",
|
||||
errx(1,
|
||||
"%s(%ld) : Requested BANK() of section \"%s\", which was not found.\n",
|
||||
pPatch->pzFilename, pPatch->nLineNo,
|
||||
name);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "link/main.h"
|
||||
#include "link/patch.h"
|
||||
#include "link/mylink.h"
|
||||
|
||||
#include "types.h"
|
||||
|
||||
@@ -53,7 +54,7 @@ void sym_Init(void)
|
||||
tHash[i] = NULL;
|
||||
}
|
||||
|
||||
int32_t sym_GetValue(char *tzName)
|
||||
int32_t sym_GetValue(struct sPatch *pPatch, char *tzName)
|
||||
{
|
||||
if (strcmp(tzName, "@") == 0)
|
||||
return nPC;
|
||||
@@ -68,10 +69,13 @@ int32_t sym_GetValue(char *tzName)
|
||||
return ((*ppSym)->nValue);
|
||||
}
|
||||
|
||||
errx(1, "Unknown symbol '%s'", tzName);
|
||||
errx(1,
|
||||
"%s(%ld) : Unknown symbol '%s'",
|
||||
pPatch->pzFilename, pPatch->nLineNo,
|
||||
tzName);
|
||||
}
|
||||
|
||||
int32_t sym_GetBank(char *tzName)
|
||||
int32_t sym_GetBank(struct sPatch *pPatch, char *tzName)
|
||||
{
|
||||
struct ISymbol **ppSym;
|
||||
|
||||
@@ -83,7 +87,10 @@ int32_t sym_GetBank(char *tzName)
|
||||
return ((*ppSym)->nBank);
|
||||
}
|
||||
|
||||
errx(1, "Unknown symbol '%s'", tzName);
|
||||
errx(1,
|
||||
"%s(%ld) : Unknown symbol '%s'",
|
||||
pPatch->pzFilename, pPatch->nLineNo,
|
||||
tzName);
|
||||
}
|
||||
|
||||
void sym_CreateSymbol(char *tzName, int32_t nValue, int32_t nBank,
|
||||
|
||||
@@ -47,7 +47,7 @@ REPT NumberOfSymbols ; Number of symbols defined in this object file.
|
||||
; as "Scope.Symbol".
|
||||
|
||||
BYTE Type ; 0 = LOCAL symbol only used in this file.
|
||||
; 1 = IMPORT this symbol from elsewhere (unused).
|
||||
; 1 = IMPORT this symbol from elsewhere
|
||||
; 2 = EXPORT this symbol to other objects.
|
||||
|
||||
IF Type != 1 ; If symbol is defined in this object file.
|
||||
@@ -164,8 +164,8 @@ special prefixes for integers and symbols.
|
||||
.It Li $33 Ta Li < comparison
|
||||
.It Li $34 Ta Li >= comparison
|
||||
.It Li $35 Ta Li <= comparison
|
||||
.It Li $40 Ta Li << comparison
|
||||
.It Li $41 Ta Li >> comparison
|
||||
.It Li $40 Ta Li << operator
|
||||
.It Li $41 Ta Li >> operator
|
||||
.It Li $50 Ta Li BANK(symbol),
|
||||
a
|
||||
.Ar LONG
|
||||
|
||||
20
test/asm/bracketed-symbols.asm
Normal file
20
test/asm/bracketed-symbols.asm
Normal file
@@ -0,0 +1,20 @@
|
||||
X = 42
|
||||
PRINTT "{X}\n"
|
||||
PRINTT "{x:X}\n"
|
||||
PRINTT "{X:X}\n"
|
||||
PRINTT "{d:X}\n"
|
||||
PRINTT "{b:X}\n"
|
||||
|
||||
Y equ 1337
|
||||
PRINTT "{b:Y}\n"
|
||||
|
||||
rsreset
|
||||
R rb 0
|
||||
PRINTT "{d:R}\n"
|
||||
|
||||
S equs "You can't format me!"
|
||||
PRINTT "{X:S}\n"
|
||||
|
||||
SECTION "Test", ROM0
|
||||
Label:
|
||||
PRINTT "{x:Label}\n"
|
||||
12
test/asm/bracketed-symbols.out
Normal file
12
test/asm/bracketed-symbols.out
Normal file
@@ -0,0 +1,12 @@
|
||||
ERROR: bracketed-symbols.asm(16):
|
||||
Print types are only allowed for numbers
|
||||
ERROR: bracketed-symbols.asm(20):
|
||||
Expression must have a constant value
|
||||
$2A
|
||||
2a
|
||||
2A
|
||||
42
|
||||
101010
|
||||
10100111001
|
||||
0
|
||||
You can't format me!
|
||||
11
test/asm/correct-line-number.asm
Normal file
11
test/asm/correct-line-number.asm
Normal file
@@ -0,0 +1,11 @@
|
||||
IF 0
|
||||
"\
|
||||
"
|
||||
ELIF 1
|
||||
WARN "Am I geting ahead of myself?"
|
||||
ELSE
|
||||
"\
|
||||
"
|
||||
ENDC
|
||||
|
||||
WARN "Hopefully not."
|
||||
4
test/asm/correct-line-number.out
Normal file
4
test/asm/correct-line-number.out
Normal file
@@ -0,0 +1,4 @@
|
||||
warning: correct-line-number.asm(5):
|
||||
Am I geting ahead of myself?
|
||||
warning: correct-line-number.asm(11):
|
||||
Hopefully not.
|
||||
@@ -1,4 +1,4 @@
|
||||
SECTION "sec", ROM0
|
||||
charmap "A", 1
|
||||
SECTION "sec", ROM0[0]
|
||||
_A_ EQU "A"
|
||||
db _A_
|
||||
|
||||
2
test/asm/equs-recursion.asm
Normal file
2
test/asm/equs-recursion.asm
Normal file
@@ -0,0 +1,2 @@
|
||||
recurse EQUS "recurse"
|
||||
recurse
|
||||
66
test/asm/equs-recursion.out
Normal file
66
test/asm/equs-recursion.out
Normal file
@@ -0,0 +1,66 @@
|
||||
ERROR: equs-recursion.asm(2):
|
||||
Recursion limit (64) exceeded
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
while expanding symbol "recurse"
|
||||
1
test/asm/garbage_char.asm
Normal file
1
test/asm/garbage_char.asm
Normal file
@@ -0,0 +1 @@
|
||||
x<EFBFBD>
|
||||
2
test/asm/garbage_char.out
Normal file
2
test/asm/garbage_char.out
Normal file
@@ -0,0 +1,2 @@
|
||||
ERROR: garbage_char.asm(1):
|
||||
Found garbage character: 0xFF
|
||||
1
test/asm/include-recursion.asm
Normal file
1
test/asm/include-recursion.asm
Normal file
@@ -0,0 +1 @@
|
||||
INCLUDE "include-recursion.asm"
|
||||
2
test/asm/include-recursion.out
Normal file
2
test/asm/include-recursion.out
Normal file
@@ -0,0 +1,2 @@
|
||||
ERROR: include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1):
|
||||
Recursion limit (64) exceeded
|
||||
45
test/asm/label-macro-arg.asm
Normal file
45
test/asm/label-macro-arg.asm
Normal file
@@ -0,0 +1,45 @@
|
||||
print: MACRO
|
||||
printv \1
|
||||
printt "\n"
|
||||
ENDM
|
||||
|
||||
|
||||
m1: MACRO
|
||||
x\1
|
||||
ENDM
|
||||
|
||||
S EQUS "y"
|
||||
S2 EQUS "yy"
|
||||
|
||||
m2: MACRO
|
||||
S\1
|
||||
ENDM
|
||||
|
||||
m1 = 5
|
||||
m2 = 6
|
||||
m1 x = 7
|
||||
m2 2 = 8
|
||||
|
||||
print x
|
||||
print y
|
||||
print xx
|
||||
print yy
|
||||
|
||||
|
||||
test_char: MACRO
|
||||
VAR_DEF equs "sizeof_\1something = 0"
|
||||
VAR_DEF
|
||||
sizeof_\1something = 1
|
||||
PURGE VAR_DEF
|
||||
|
||||
VAR_PRINT equs "printt \"sizeof_\1something equals {sizeof_\1something}\\n\""
|
||||
VAR_PRINT
|
||||
PURGE VAR_PRINT
|
||||
ENDM
|
||||
|
||||
test_char _
|
||||
test_char @
|
||||
test_char #
|
||||
test_char .
|
||||
|
||||
test_char :
|
||||
10
test/asm/label-macro-arg.out
Normal file
10
test/asm/label-macro-arg.out
Normal file
@@ -0,0 +1,10 @@
|
||||
ERROR: label-macro-arg.asm(45) -> label-macro-arg.asm::test_char(31):
|
||||
Macro 'something' not defined
|
||||
$5
|
||||
$6
|
||||
$7
|
||||
$8
|
||||
sizeof__something equals $1
|
||||
sizeof_@something equals $1
|
||||
sizeof_#something equals $1
|
||||
sizeof_.something equals $1
|
||||
@@ -1,3 +1,3 @@
|
||||
ERROR: label-redefinition.asm(7):
|
||||
'Sym' already defined in m(6)
|
||||
error: Assembly aborted in pass 1 (1 errors)!
|
||||
'Sym' already defined in label-redefinition.asm::m(6)
|
||||
error: Assembly aborted (1 errors)!
|
||||
|
||||
7
test/asm/line-continuation-macro.asm
Normal file
7
test/asm/line-continuation-macro.asm
Normal file
@@ -0,0 +1,7 @@
|
||||
m: MACRO
|
||||
ENDM
|
||||
|
||||
m2: MACRO
|
||||
m \ ENDM
|
||||
|
||||
m2
|
||||
0
test/asm/line-continuation-macro.out
Normal file
0
test/asm/line-continuation-macro.out
Normal file
8
test/asm/line-continuation-rept.asm
Normal file
8
test/asm/line-continuation-rept.asm
Normal file
@@ -0,0 +1,8 @@
|
||||
m: MACRO
|
||||
ENDM
|
||||
|
||||
REPT 1
|
||||
m ENDR
|
||||
|
||||
REPT 1
|
||||
m \ ENDR
|
||||
0
test/asm/line-continuation-rept.out
Normal file
0
test/asm/line-continuation-rept.out
Normal file
7
test/asm/line-continuation-whitespace.asm
Normal file
7
test/asm/line-continuation-whitespace.asm
Normal file
@@ -0,0 +1,7 @@
|
||||
; Test that \ followed by whitespace after a macro invocation at the end of the
|
||||
; file doesn't cause a segfault.
|
||||
|
||||
bar: MACRO
|
||||
ENDM
|
||||
|
||||
foo bar baz\
|
||||
0
test/asm/line-continuation-whitespace.out
Normal file
0
test/asm/line-continuation-whitespace.out
Normal file
@@ -1 +1,7 @@
|
||||
foo @bar\
|
||||
; Test that \ after a macro invocation at the end of the file doesn't
|
||||
; cause a segfault.
|
||||
|
||||
bar: MACRO
|
||||
ENDM
|
||||
|
||||
foo bar baz\
|
||||
@@ -1,2 +0,0 @@
|
||||
ERROR: line-continuation.asm(2) -> @(-1):
|
||||
Macro '@' not defined
|
||||
|
||||
3
test/asm/local-ref-without-parent.asm
Normal file
3
test/asm/local-ref-without-parent.asm
Normal file
@@ -0,0 +1,3 @@
|
||||
SECTION "sec", ROM0
|
||||
|
||||
dw .test
|
||||
2
test/asm/local-ref-without-parent.out
Normal file
2
test/asm/local-ref-without-parent.out
Normal file
@@ -0,0 +1,2 @@
|
||||
ERROR: local-ref-without-parent.asm(3):
|
||||
Local label reference '.test' in main scope
|
||||
2
test/asm/local-without-parent.asm
Normal file
2
test/asm/local-without-parent.asm
Normal file
@@ -0,0 +1,2 @@
|
||||
SECTION "Test", ROM0
|
||||
.test:
|
||||
2
test/asm/local-without-parent.out
Normal file
2
test/asm/local-without-parent.out
Normal file
@@ -0,0 +1,2 @@
|
||||
ERROR: local-without-parent.asm(2):
|
||||
Local label in main scope
|
||||
@@ -1,3 +1,3 @@
|
||||
ERROR: local-wrong-parent.asm(5):
|
||||
Not currently in the scope of 'WrongParent'
|
||||
error: Assembly aborted in pass 1 (1 errors)!
|
||||
error: Assembly aborted (1 errors)!
|
||||
|
||||
33
test/asm/long-rpn-expression.asm
Normal file
33
test/asm/long-rpn-expression.asm
Normal file
@@ -0,0 +1,33 @@
|
||||
SECTION "sec", ROM0
|
||||
|
||||
X0 EQUS "0"
|
||||
|
||||
m: MACRO
|
||||
\1 EQUS STRCAT("{X\2}", "+0")
|
||||
ENDM
|
||||
|
||||
n = 0
|
||||
|
||||
REPT $7E
|
||||
n1 = n + 1
|
||||
NSTR EQUS STRSUB("{n}", 2, STRLEN("{n}") - 1)
|
||||
N1STR EQUS STRSUB("{n1}", 2, STRLEN("{n1}") - 1)
|
||||
XN1 EQUS STRCAT("X", "{N1STR}")
|
||||
m XN1, {NSTR}
|
||||
PURGE NSTR, N1STR, XN1
|
||||
n = n + 1
|
||||
ENDR
|
||||
|
||||
; string of 127 zeros separated by plus signs
|
||||
X EQUS "{X7E}"
|
||||
|
||||
db x+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+\
|
||||
X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+\
|
||||
X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+\
|
||||
X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+\
|
||||
X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+\
|
||||
X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+\
|
||||
X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+\
|
||||
X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X
|
||||
|
||||
x db 0
|
||||
0
test/asm/long-rpn-expression.out
Normal file
0
test/asm/long-rpn-expression.out
Normal file
@@ -1,2 +1,2 @@
|
||||
ERROR: macro-@.asm(1) -> @(-1):
|
||||
ERROR: macro-@.asm(1):
|
||||
Macro '@' not defined
|
||||
|
||||
4
test/asm/macro-recursion.asm
Normal file
4
test/asm/macro-recursion.asm
Normal file
@@ -0,0 +1,4 @@
|
||||
recurse: MACRO
|
||||
recurse
|
||||
ENDM
|
||||
recurse
|
||||
2
test/asm/macro-recursion.out
Normal file
2
test/asm/macro-recursion.out
Normal file
@@ -0,0 +1,2 @@
|
||||
ERROR: macro-recursion.asm(4) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2):
|
||||
Recursion limit (64) exceeded
|
||||
104
test/asm/multiple-charmaps.asm
Normal file
104
test/asm/multiple-charmaps.asm
Normal file
@@ -0,0 +1,104 @@
|
||||
new_: MACRO
|
||||
IF _NARG > 1
|
||||
printt "newcharmap \1, \2\n"
|
||||
newcharmap \1, \2
|
||||
ELSE
|
||||
printt "newcharmap \1\n"
|
||||
newcharmap \1
|
||||
ENDC
|
||||
ENDM
|
||||
|
||||
set_: MACRO
|
||||
printt "setcharmap \1\n"
|
||||
setcharmap \1
|
||||
ENDM
|
||||
|
||||
push_: MACRO
|
||||
printt "pushc\n"
|
||||
pushc
|
||||
ENDM
|
||||
|
||||
pop_: MACRO
|
||||
printt "popc\n"
|
||||
popc
|
||||
ENDM
|
||||
|
||||
print: MACRO
|
||||
x = \1
|
||||
printt "{x}\n"
|
||||
ENDM
|
||||
|
||||
printt "main charmap\n"
|
||||
|
||||
charmap "ab", $0
|
||||
|
||||
print "ab"
|
||||
|
||||
new_ map1
|
||||
|
||||
print "ab"
|
||||
|
||||
new_ map2, main
|
||||
|
||||
print "ab"
|
||||
|
||||
set_ map1
|
||||
|
||||
print "ab"
|
||||
|
||||
new_ map3
|
||||
|
||||
charmap "ab", $1
|
||||
|
||||
print "ab"
|
||||
|
||||
new_ map4, map3
|
||||
|
||||
charmap "ab", $1
|
||||
charmap "cd", $2
|
||||
|
||||
print "ab"
|
||||
print "cd"
|
||||
|
||||
set_ map3
|
||||
|
||||
print "ab"
|
||||
print "cd"
|
||||
|
||||
set_ main
|
||||
|
||||
SECTION "sec0", ROM0
|
||||
|
||||
print "ab"
|
||||
|
||||
printt "override main charmap\n"
|
||||
charmap "ef", $3
|
||||
|
||||
print "ab"
|
||||
print "ef"
|
||||
|
||||
set_ map1
|
||||
|
||||
push_
|
||||
set_ map2
|
||||
push_
|
||||
|
||||
set_ map3
|
||||
|
||||
print "ab"
|
||||
print "cd"
|
||||
print "ef"
|
||||
|
||||
pop_
|
||||
|
||||
print "ab"
|
||||
|
||||
pop_
|
||||
|
||||
print "ab"
|
||||
|
||||
new_ map1
|
||||
|
||||
set_ map5
|
||||
|
||||
pop_
|
||||
44
test/asm/multiple-charmaps.out
Normal file
44
test/asm/multiple-charmaps.out
Normal file
@@ -0,0 +1,44 @@
|
||||
warning: multiple-charmaps.asm(75):
|
||||
Using 'charmap' within a section when the current charmap is 'main' is deprecated
|
||||
ERROR: multiple-charmaps.asm(100) -> multiple-charmaps.asm::new_(7):
|
||||
Charmap 'map1' already exists
|
||||
ERROR: multiple-charmaps.asm(102) -> multiple-charmaps.asm::set_(13):
|
||||
Charmap 'map5' doesn't exist
|
||||
ERROR: multiple-charmaps.asm(104) -> multiple-charmaps.asm::pop_(23):
|
||||
No entries in the charmap stack
|
||||
main charmap
|
||||
$0
|
||||
newcharmap map1
|
||||
$6162
|
||||
newcharmap map2, main
|
||||
$0
|
||||
setcharmap map1
|
||||
$6162
|
||||
newcharmap map3
|
||||
$1
|
||||
newcharmap map4, map3
|
||||
$1
|
||||
$2
|
||||
setcharmap map3
|
||||
$1
|
||||
$6364
|
||||
setcharmap main
|
||||
$0
|
||||
override main charmap
|
||||
$6162
|
||||
$3
|
||||
setcharmap map1
|
||||
pushc
|
||||
setcharmap map2
|
||||
pushc
|
||||
setcharmap map3
|
||||
$1
|
||||
$6364
|
||||
$6566
|
||||
popc
|
||||
$0
|
||||
popc
|
||||
$6162
|
||||
newcharmap map1
|
||||
setcharmap map5
|
||||
popc
|
||||
14
test/asm/narg-decreases-after-shift.asm
Normal file
14
test/asm/narg-decreases-after-shift.asm
Normal file
@@ -0,0 +1,14 @@
|
||||
testing: MACRO
|
||||
db _NARG
|
||||
shift
|
||||
db _NARG
|
||||
shift
|
||||
db _NARG
|
||||
shift
|
||||
db _NARG
|
||||
shift
|
||||
db _NARG
|
||||
ENDM
|
||||
|
||||
SECTION "Test output", ROM0[0]
|
||||
testing 1, 2, 3
|
||||
0
test/asm/narg-decreases-after-shift.out
Normal file
0
test/asm/narg-decreases-after-shift.out
Normal file
BIN
test/asm/narg-decreases-after-shift.out.bin
Normal file
BIN
test/asm/narg-decreases-after-shift.out.bin
Normal file
Binary file not shown.
18
test/asm/nested-if.asm
Normal file
18
test/asm/nested-if.asm
Normal file
@@ -0,0 +1,18 @@
|
||||
if 0
|
||||
if(1)
|
||||
endc
|
||||
if 1
|
||||
endc
|
||||
if{x}
|
||||
endc
|
||||
endc
|
||||
|
||||
if 1
|
||||
else
|
||||
if(1)
|
||||
endc
|
||||
if 1
|
||||
endc
|
||||
if{x}
|
||||
endc
|
||||
endc
|
||||
0
test/asm/nested-if.out
Normal file
0
test/asm/nested-if.out
Normal file
@@ -1,2 +1,2 @@
|
||||
ERROR: null-in-macro.asm(1):
|
||||
Unterminated MACRO definition.
|
||||
ERROR: null-in-macro.asm(2):
|
||||
Found null character
|
||||
|
||||
7
test/asm/operator-precedence.asm
Normal file
7
test/asm/operator-precedence.asm
Normal file
@@ -0,0 +1,7 @@
|
||||
print: MACRO
|
||||
printv \1
|
||||
printt "\n"
|
||||
ENDM
|
||||
|
||||
print 1 == 1 || 1 == 2
|
||||
print (1 == 1) || (1 == 2)
|
||||
2
test/asm/operator-precedence.out
Normal file
2
test/asm/operator-precedence.out
Normal file
@@ -0,0 +1,2 @@
|
||||
$1
|
||||
$1
|
||||
42
test/asm/overflow.asm
Normal file
42
test/asm/overflow.asm
Normal file
@@ -0,0 +1,42 @@
|
||||
SECTION "sec", ROM0
|
||||
|
||||
print_x: MACRO
|
||||
printv x
|
||||
printt "\n"
|
||||
ENDM
|
||||
|
||||
x = 2147483647
|
||||
x = x + 1
|
||||
dl 2147483647+1
|
||||
print_x
|
||||
|
||||
x = -2147483648
|
||||
x = x - 1
|
||||
dl -2147483648-1
|
||||
print_x
|
||||
|
||||
x = -2147483648
|
||||
x = x * -1
|
||||
dl -2147483648 * -1
|
||||
print_x
|
||||
|
||||
x = -2147483648
|
||||
x = x / -1
|
||||
dl -2147483648 / -1
|
||||
print_x
|
||||
|
||||
x = -2147483648
|
||||
x = x % -1
|
||||
dl -2147483648 % -1
|
||||
print_x
|
||||
|
||||
x = -1
|
||||
x = x << 1
|
||||
dl -1 << 1
|
||||
print_x
|
||||
|
||||
x = 4294967295
|
||||
x = 4294967296
|
||||
|
||||
x = `33333333
|
||||
x = `333333333
|
||||
18
test/asm/overflow.out
Normal file
18
test/asm/overflow.out
Normal file
@@ -0,0 +1,18 @@
|
||||
warning: overflow.asm(24):
|
||||
Division of min value by -1
|
||||
warning: overflow.asm(25):
|
||||
Division of min value by -1
|
||||
warning: overflow.asm(34):
|
||||
Left shift of negative value: -1
|
||||
warning: overflow.asm(35):
|
||||
Left shift of negative value: -1
|
||||
warning: overflow.asm(39):
|
||||
Integer constant '4294967296' is too large
|
||||
warning: overflow.asm(42):
|
||||
Graphics constant '`333333333' is too long
|
||||
$80000000
|
||||
$7FFFFFFF
|
||||
$80000000
|
||||
$80000000
|
||||
$0
|
||||
$FFFFFFFE
|
||||
1
test/asm/pops-no-pushed-sections.asm
Normal file
1
test/asm/pops-no-pushed-sections.asm
Normal file
@@ -0,0 +1 @@
|
||||
POPS
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user