Compare commits

..

88 Commits

Author SHA1 Message Date
ISSOtm
8c0adb63a1 Sync release version in CMakeLists 2020-07-21 23:54:36 +02:00
ISSOtm
f5b0c701a6 Add install hint to CMakeLists 2020-07-21 23:41:56 +02:00
ISSOtm
874bd92604 Only require libpng 1.2 in CMakeLists
This version is sufficient for rgbgfx to function properly, according to testing.
The security problems are not ours to decide, however!
2020-07-21 23:23:52 +02:00
ISSOtm
69a8c62863 Add install directives to CMakeLists
Otherwise `make install` or `cmake --install` does not work
2020-07-21 23:17:33 +02:00
ISSOtm
5481af5093 Release 0.4.1 2020-07-21 23:11:06 +02:00
ISSOtm
6355d0598b Regenerate docs for 0.4.1 2020-07-21 23:10:14 +02:00
Eldred Habert
1145d10996 Merge pull request #543 from ISSOtm/ubuntu20
Add Ubuntu 20.04 to CI
2020-07-21 22:24:28 +02:00
ISSOtm
32231e3f7e Add Ubuntu 20.04 to CI 2020-07-21 22:15:56 +02:00
Eldred Habert
29314f76f7 Merge pull request #533 from JL2210/platform-fixes
Add platform-specific fixes file (only for MSVC, currently)
2020-07-21 22:11:25 +02:00
ISSOtm
8e92383fa3 Enable -Wobsolete by default
The fact that deprecations were common and quickly acted upon was raised by
some users
2020-07-21 21:06:48 +02:00
James Larrowe
e51701acaa Add platform-specific fixes file
Create a new file, platform.h, for platform-specific hacks

for MSVC, this includes defining strncasecmp to _stricmp and
strdup to _strdup, among other things like defining missing
stat macros

Change some things not supported in MSVC, like _Static_assert,
to their counterparts (in this case, static_assert)

Replace usage of VLAs with malloc and free

Update getopt_long and use the getopt implementation from musl
on Windows.

Use comments to show which functions from platform.h are being used
2020-07-21 14:24:22 -04:00
ISSOtm
0793e9effe Fix develop compilation error
This bug slipped by because my pre-commit hook for testing was broken...
2020-07-21 20:10:28 +02:00
ISSOtm
c25b0be085 Document that -t and -w convert section types
This is also a fairly overdue doc update
2020-07-21 20:05:26 +02:00
ISSOtm
1f2f797cb9 Add section fragments
Fixes #517, and hopefully enables RGBDS as a SDCC back-end
2020-07-21 19:56:46 +02:00
ISSOtm
aca00e4fce Document MP etc. in rgbasm(1)
This is actually kind of overdue...
2020-07-21 15:55:56 +02:00
Eldred Habert
cf80293d9b Merge pull request #540 from daid/patch-1
Make the local variables of getopt static.
2020-07-21 13:11:30 +02:00
daid
6d53753c66 Make the local variables of getopt static.
While compiling for empscripten there is a duplicate symbol conflict with these two variables. And as they are only used locally, and do not need a symbol exported they can just be static.
2020-07-21 10:21:33 +02:00
Eldred Habert
79f293c3d7 Merge pull request #529 from JL2210/cmake
Add CMake build system
2020-07-20 16:02:47 +02:00
Eldred Habert
8c765883fb Merge pull request #536 from braydenm303/patch-1
Fix error in documentation about unary not
2020-07-20 15:11:29 +02:00
Brayden Morris
83aa456d05 Fix error in documentation about unary not 2020-07-13 09:32:43 -06:00
James R Larrowe
819c36943e Add CMake build system
This should hopefully work torwards compatibility with more systems.
I've tried to make this as general as possible but some small assumptions
about the compiler are made. I've also tried to recreate the build process
as closely as possible, but I had to change some things slightly to work
with CMake (version strings, mainly).

For now, it doesn't allow in-source builds, as that could overwrite the
Makefile.

This adds:

- Support for more build systems
- Automatic dependency generation
- Performance gains (especially when using i.e. Ninja)

Defaults to Release build.
2020-07-03 15:10:18 -04:00
Eldred Habert
8b60efa149 Merge pull request #530 from JL2210/fix-clang
Fix Clang warning in linkdefs
2020-06-27 22:07:19 +02:00
James Larrowe
240fca0861 Fix Clang warning in linkdefs
This one's really stupid; printing an int with %hhu gives a warning
even though a regular unsigned char is promoted to an int before
printing anyway. Silence it so the build with Clang works.

This is a regression introduced in 5c24de3
2020-06-23 23:29:49 -04:00
Eldred Habert
f996186fae Merge pull request #526 from JL2210/fix-bank-index
Fix indexing of banks array, fixes #525
2020-06-21 12:44:17 +02:00
James Larrowe
69a41d8ef9 Fix indexing of banks array
When ROMX bank 1 is given, the banks array is indexed with an index
of 1 rather than an index of zero.
2020-06-17 13:33:48 -04:00
Eldred Habert
b958820bce Merge pull request #523 from RandomBananazz/patch-1
Fixed error in POP AF instruction reference
2020-05-27 12:45:44 +02:00
Jason Yuan
7624bd524c Fixed error in POP AF instruction reference
The "imaginary" equivalent instructions put the instructions in the wrong order (inc sp first).
2020-05-26 23:29:06 -04:00
Eldred Habert
663f0ca3b0 Merge pull request #522 from JL2210/djgpp
Fix DJGPP build
2020-05-21 22:37:24 +02:00
James Larrowe
f88d9e728d Fix DJGPP build
GCC with the -std=c11 defines __STRICT_ANSI__. DJGPP checks if
__STRICT_ANSI__ is defined and if so doesn't define some things
mandated by POSIX such as struct stat, PATH_MAX, and others.
The -std=gnu11 option does not define this macro, so use it instead.

_DEFAULT_SOURCE isn't needed as no GNU nor BSD-specific functions
are used. Remove it.

Fix the last two occurrences of incorrect format specifiers for standard
fixed-width integer types.
2020-05-19 19:21:16 -04:00
ISSOtm
71fa62c9d1 Fix incorrectly spaced unary ! in RPN documentation 2020-05-13 01:20:34 +02:00
ISSOtm
5c6069dbe9 Add NULL and overflow checks to macro args 2020-05-13 01:17:58 +02:00
ISSOtm
d517d2d6b4 Change macro arg allocation to geometric growth array 2020-05-13 00:22:56 +02:00
ISSOtm
80218fa109 Fix array overflow on invalid macro arg evaluation
`macro_GetArg` had not been changed after the previous commit; however,
the old code relied on the `macroArgs->args` array being at least
`MAXMACROARGS` entries large (which was the case until the last commit).
The boundary against which it checked would have better been written as
`sizeof(macroArgs->args)/sizeof(macroArgs->args[0])`, I guess, but what's
done is done.
2020-05-12 16:44:23 +02:00
Eldred Habert
fee8a58b77 Merge pull request #521 from aaaaaa123456789/master
Support over 256 macro arguments
2020-05-12 15:46:42 +02:00
aaaaaa123456789
fd52a6f046 Add a test for >256 macro arguments 2020-05-12 07:22:42 -03:00
aaaaaa123456789
89fb372326 Set max macro arguments to 99,999 2020-05-12 06:46:17 -03:00
aaaaaa123456789
a828f82414 Remove fixed-size array for macro arguments 2020-05-12 06:46:07 -03:00
Eldred Habert
4d0d6664d7 Merge pull request #520 from JL2210/inttypes-stdint
Use inttypes for stdint types
2020-05-07 17:31:06 +02:00
James Larrowe
5c24de3dc4 Use inttypes for stdint types
This should help make RGBDS portable to systems with 16-bit integers,
like DOS.

For kicks, use the macros for 16-bit and 8-bit integers.

Fix other miscellaneous things, like #include ordering and other
printf-format related things.

Reduce repitition in math.c while I'm there.
2020-05-07 11:10:20 -04:00
ISSOtm
363458c3bc Fix semi-unused variable in lexer.c 2020-05-06 19:55:37 +02:00
ISSOtm
b299f6fb3b Fix uninitialized memory use with -MT and -MQ
This didn't break unless the first uninitialized byte was non-zero,
which happened to be the case on someone's Windows machine.

Would it be worth it setting up Valgrind in CI?
2020-05-06 19:19:10 +02:00
Eldred Habert
645473e336 Merge pull request #518 from JL2210/warn-parameterless-dx
Add empty data directive warning
2020-05-06 16:25:03 +02:00
James Larrowe
bdf397bba7 Add empty data directive warning
Fixes #516
2020-05-06 09:50:28 -04:00
ISSOtm
781a65ee49 Fix hashmap collisions sometimes hanging deletion 2020-05-03 19:13:12 +02:00
ISSOtm
c135e2c6a0 Fix local sym names not being expanded by PURGE
And an additional bug that broke the attached test
2020-05-03 19:06:48 +02:00
ISSOtm
12693081c9 Use colons for hour symbols 2020-04-28 13:05:06 +02:00
Eldred Habert
7c22954fd5 Merge pull request #513 from JL2210/disable-padding-option
Add option to disable padding in rgblink
2020-04-27 11:05:55 +02:00
ISSOtm
8958e352df Update documentation for new -x option 2020-04-27 11:04:29 +02:00
ISSOtm
573003113b Fix 0-byte sections incorrectly printed in map files
Fixes #515
2020-04-17 11:38:28 +02:00
JL2210
8b1351fc3e Add option to disable padding in rgblink
Fixes #307

RGBFIX can handle padding, so there's no reason why
we can't add an option to disable padding in rgblink.

Signed-off-by: JL2210 <larrowe.semaj11@gmail.com>
2020-04-15 09:34:51 -04:00
Eldred Habert
106ef895ee Merge pull request #514 from JL2210/fix-gfx-gb-leak
Add error checking and fix memory leak in gfx/gb.c
2020-04-14 21:31:12 +02:00
JL2210
7f9bd12f76 Add error checking and fix memory leak in gfx/gb.c
It's possible that tile could be leaked, so free it.

Fix sizeof convention and check the result of malloc.

Signed-off-by: JL2210 <larrowe.semaj11@gmail.com>
2020-04-13 22:02:24 -04:00
ISSOtm
5fe3a0adb6 Remove non-OPT options from Options struct 2020-04-13 17:15:00 +02:00
Eldred Habert
cdb4c5f553 Merge pull request #509 from JL2210/zero-alloc-use-fix-3
Fix use of zero-allocated memory
2020-04-13 02:50:58 +02:00
ISSOtm
57639f3765 Change comment style and use errx instead of err 2020-04-13 02:50:11 +02:00
Eldred Habert
9bec983923 Merge pull request #511 from JL2210/memory-errors
Fix memory errors in makepng
2020-04-13 02:39:17 +02:00
ISSOtm
2220f19fa7 Fix use-after-free with include in linker scripts
Fixes #510, and further proves that you *really* should not entrust memory
ownership management to humans :P
2020-04-13 02:36:19 +02:00
JL2210
2a734ecba2 Fix memory errors
This includes not checking the result of malloc and
using the wrong type in sizeof. Only the latter was
caught by scan-build.

Also use more idiomatic sizeof().

Signed-off-by: JL2210 <larrowe.semaj11@gmail.com>
2020-04-12 19:45:37 -04:00
JL2210
d21015e34a Fix use of zero-allocated memory
It's possible that the unsigned integer may overflow to zero,
and then we might use zero-allocated memory.

This is incredibly unlikely, and I would even go so far as to say
that this is a false positive. Fix it anyway, to silence this warning:

src/link/patch.c:92:24: warning: Use of zero-allocated memory
        stack.buf[stack.size] = value;
        ~~~~~~~~~~~~~~~~~~~~~ ^

Deal with overflow, and check for zero to get rid of the warning.

Signed-off-by: JL2210 <larrowe.semaj11@gmail.com>
2020-04-12 19:36:47 -04:00
ISSOtm
023a3c037f Properly handle missing symbols
Fixes #512
2020-04-12 22:52:32 +02:00
ISSOtm
828edb7403 Remove spurious error from div by zero test 2020-04-12 01:05:06 +02:00
ISSOtm
a034ce0478 Deprecate '*' for comments 2020-04-09 17:57:15 +02:00
Eldred Habert
d6cd5823e3 Merge pull request #508 from JL2210/mod-by-zero-fix-2
Fix modulo by zero
2020-04-09 14:52:12 +02:00
JL2210
5dd941b311 Fix modulo by zero
Yet another case caught by scan-build:

src/link/patch.c:188:21: warning: Division by zero
                        value = popRPN() % value;
                                ~~~~~~~~~^~~~~~~

Just copy over the code from the division case, with a few modifications.

Signed-off-by: JL2210 <larrowe.semaj11@gmail.com>
2020-04-09 08:50:23 -04:00
ISSOtm
f9f27d6f5a Clean up symbol system
Get rid of Hungarian notation
Improve encapsulation (the rest of the world should not touch PC directly)
2020-04-09 10:42:37 +02:00
Eldred Habert
5ea8490e2b Merge pull request #507 from JL2210/null-pointer-fix-1
Fix possible null pointer dereference
2020-04-08 23:35:21 +02:00
JL2210
5863cd10b8 Fix possible null pointer dereference
It's possible that if the FILE passed to yy_create_buffer is at the
end-of file, there may be a null pointer dereference.

This should hopefully fix that.

Found with clang-tools' scan-build:

src/asm/lexer.c:281:25: warning: Array access (via field 'pBuffer')
 results in a null pointer dereference
        pBuffer->pBuffer[size] = 0;
                 ~~~~~~~       ^
1 warning generated.

Signed-off-by: JL2210 <larrowe.semaj11@gmail.com>
2020-04-08 17:30:43 -04:00
ISSOtm
40f8e33e6c Fix periods not being accepted as second char of label names 2020-04-08 15:14:07 +02:00
ISSOtm
0157ba63d3 Document whitespace before local labels 2020-04-08 15:08:29 +02:00
ISSOtm
9e3d8b22cb Document new intra-section align
Also sneak in two code style fixes forgotten in last commit
2020-04-08 15:01:36 +02:00
ISSOtm
665412c073 Implement mid-section alignment directive
Fixes #254.
2020-04-08 12:29:00 +02:00
ISSOtm
2b0c34ecb5 Fix a few code style errors 2020-04-08 00:44:41 +02:00
ISSOtm
b0ec8468e6 Allow specifying offset in addition to alignment 2020-04-08 00:40:41 +02:00
ISSOtm
e82ad21704 Use a single byte for alignment 2020-04-07 21:19:09 +02:00
ISSOtm
e098bf47ba Allow references to be overridden by constant symbols
RGBLINK is capable of handling it now.
Though it'd be ideal for RGBASM to directly catch it.

Fixes #496.
2020-04-07 20:57:20 +02:00
ISSOtm
190678107b Prevent RGBLINK from crashing when getting the bank of a constant 2020-04-07 20:41:29 +02:00
ISSOtm
9f82fa4cf7 Fix BANK(@) outside sections causing crashes 2020-04-07 15:51:17 +02:00
Eldred Habert
562835308b Merge pull request #504 from runlevel5/gcc10-fix
Fix multiple definitions for GCC10
2020-04-07 15:18:23 +02:00
ISSOtm
927c65e863 Fix incorrect PC in LOAD blocks at link time 2020-04-07 14:44:51 +02:00
ISSOtm
5b6c1569a4 Make failure to open file a fatal error 2020-04-07 11:36:44 +02:00
Trung Lê
65121e6d5d Fix multiple definitions for GCC10 2020-04-07 14:48:30 +10:00
ISSOtm
82e0e4ffaf Make some RGBLINK errors non-fatal 2020-04-06 00:48:10 +02:00
ISSOtm
ffb199a26a Avoid Useless Use of backticks in rgblink testing 2020-04-06 00:44:59 +02:00
ISSOtm
175933d2b1 Remove old scripts for updating references for tests
I much rather prefer correcting everything by hand, and gauging
whether the change is good on a case by case basis
2020-04-06 00:41:37 +02:00
Eldred Habert
c86e6fef0a Merge pull request #502 from JL2210/more-conventional-permissions
Use more conventional permissions and man-page directory
2020-04-06 00:25:33 +02:00
JL2210
1e3ce2a36f Use more conventional permissions and man-page directory
As 444 and 555 seem to be used for no apparent reason, use the more
conventional 644 and 755.

/man is typically unused or a symlink to /share/man now, so just use
/share/man.

Signed-off-by: JL2210 <larrowe.semaj11@gmail.com>
2020-04-05 17:02:12 -04:00
ISSOtm
153915dc2f Improve portability of new make dist 2020-04-04 14:48:15 +02:00
ISSOtm
ced38bc6ee Add new Makefile target for release tarballs 2020-04-04 14:23:28 +02:00
119 changed files with 2955 additions and 1039 deletions

View File

@@ -5,7 +5,7 @@ jobs:
unix-testing: unix-testing:
strategy: strategy:
matrix: matrix:
os: [ubuntu-18.04, ubuntu-16.04, macos-10.15] os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04, macos-10.15]
cc: [gcc, clang] cc: [gcc, clang]
include: include:
- os: ubuntu-18.04 - os: ubuntu-18.04

3
.gitignore vendored
View File

@@ -6,6 +6,9 @@ rgbshim.sh
*.o *.o
*.exe *.exe
.checkpatch-camelcase.* .checkpatch-camelcase.*
CMakeCache.txt
CMakeFiles
cmake_install.cmake
test/pokecrystal test/pokecrystal
test/pokered test/pokered

71
CMakeLists.txt Normal file
View File

@@ -0,0 +1,71 @@
#
# This file is part of RGBDS.
#
# Copyright (c) 2020 RGBDS contributors.
#
# SPDX-License-Identifier: MIT
#
cmake_minimum_required(VERSION 2.8.8)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
set(RGBDS_VER 0.4.1)
set(RGBDS_DESC "Rednex Game Boy Development System")
if(CMAKE_VERSION VERSION_LESS 3.0)
project(rgbds C)
set(PROJECT_VERSION "${RGBDS_VER}")
else()
if(CMAKE_VERSION VERSION_LESS 3.9)
project(rgbds VERSION "${RGBDS_VER}"
LANGUAGES C)
else()
project(rgbds VERSION "${RGBDS_VER}"
DESCRIPTION "${RGBDS_DESC}"
LANGUAGES C)
endif()
endif()
if(CMAKE_VERSION VERSION_LESS 3.9)
set(PROJECT_DESCRIPTION "${RGBDS_DESC}")
endif()
set(DEFAULT_BUILD_TYPE "Release")
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}")
endif()
# get real path of source and binary directories
get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH)
get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH)
# reject in-source builds, may conflict with Makefile
if(srcdir STREQUAL bindir)
message("RGBDS should not be built in the source directory.")
message("Instead, create a separate build directory and specify to CMake the path to the source directory.")
message(FATAL_ERROR "Terminating configuration")
endif()
find_package(PNG 1.2 REQUIRED)
find_package(BISON REQUIRED)
find_package(FLEX)
include_directories("${PROJECT_SOURCE_DIR}/include")
if(MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W1 /MP -D_CRT_SECURE_NO_WARNINGS")
else()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -pedantic")
endif()
if(CMAKE_VERSION VERSION_LESS 3.12)
add_definitions(-DBUILD_VERSION_STRING="${PROJECT_VERSION}")
else()
add_compile_definitions(BUILD_VERSION_STRING="${PROJECT_VERSION}")
endif()
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED True)
add_subdirectory(src)

View File

@@ -14,10 +14,10 @@
Q := @ Q := @
PREFIX := /usr/local PREFIX := /usr/local
bindir := ${PREFIX}/bin bindir := ${PREFIX}/bin
mandir := ${PREFIX}/man mandir := ${PREFIX}/share/man
STRIP := -s STRIP := -s
BINMODE := 555 BINMODE := 755
MANMODE := 444 MANMODE := 644
CHECKPATCH := ../linux/scripts/checkpatch.pl CHECKPATCH := ../linux/scripts/checkpatch.pl
# Other variables # Other variables
@@ -34,8 +34,8 @@ WARNFLAGS := -Wall
# Overridable CFLAGS # Overridable CFLAGS
CFLAGS := -O3 CFLAGS := -O3
# Non-overridable CFLAGS # Non-overridable CFLAGS
REALCFLAGS := ${CFLAGS} ${WARNFLAGS} -std=c11 -D_POSIX_C_SOURCE=200809L \ REALCFLAGS := ${CFLAGS} ${WARNFLAGS} -std=gnu11 -D_POSIX_C_SOURCE=200809L \
-D_DEFAULT_SOURCE -Iinclude -Iinclude
# Overridable LDFLAGS # Overridable LDFLAGS
LDFLAGS := LDFLAGS :=
# Non-overridable LDFLAGS # Non-overridable LDFLAGS
@@ -247,3 +247,10 @@ wine-shim:
$Qln -s rgbshim.sh rgblink $Qln -s rgbshim.sh rgblink
$Qln -s rgbshim.sh rgbfix $Qln -s rgbshim.sh rgbfix
$Qln -s rgbshim.sh rgbgfx $Qln -s rgbshim.sh rgbgfx
# Target for the project maintainer to produce distributable release tarballs
# of the source code.
dist:
$Qgit ls-files | sed s~^~$${PWD##*/}/~ \
| tar -czf rgbds-`git describe --tags | cut -c 2-`.tar.gz -C .. -T -

View File

@@ -147,7 +147,7 @@ This is the complete list of user-defined variables:
``${PREFIX}/bin``. ``${PREFIX}/bin``.
- ``mandir``: Location where the manpages will be installed. Defaults to - ``mandir``: Location where the manpages will be installed. Defaults to
``${PREFIX}/man``. ``${PREFIX}/share/man``.
- ``DESTDIR``: This is prepended to all paths during the installation. It is - ``DESTDIR``: This is prepended to all paths during the installation. It is
mainly used for packaging. mainly used for packaging.
@@ -158,9 +158,9 @@ This is the complete list of user-defined variables:
- ``STRIP``: Whether to strip the installed binaries of debug symbols or not. - ``STRIP``: Whether to strip the installed binaries of debug symbols or not.
Defaults to ``-s``. Defaults to ``-s``.
- ``BINMODE``: Permissions of the installed binaries. Defaults to ``555``. - ``BINMODE``: Permissions of the installed binaries. Defaults to ``755``.
- ``MANMODE``: Permissions of the installed manpages. Defaults to ``444``. - ``MANMODE``: Permissions of the installed manpages. Defaults to ``644``.
- ``CHECKPATCH``: Path of the script ``checkpatch.pl`` of the Linux kernel. - ``CHECKPATCH``: Path of the script ``checkpatch.pl`` of the Linux kernel.
Defaults to ``../linux/scripts/checkpatch.pl``. Defaults to ``../linux/scripts/checkpatch.pl``.

View File

@@ -1064,10 +1064,10 @@ Pop register <b class="Sy">AF</b> from the stack. This is roughly equivalent to
the following <i class="Em">imaginary</i> instructions: the following <i class="Em">imaginary</i> instructions:
<div class="Bd Pp Bd-indent"> <div class="Bd Pp Bd-indent">
<pre> <pre>
ld f, [sp] ; See below for individual flags
inc sp inc sp
ld a, [sp] ld a, [sp]
inc sp inc sp
ld f, [sp] ; See below for individual flags
</pre> </pre>
</div> </div>
<p class="Pp">Cycles: 3</p> <p class="Pp">Cycles: 3</p>

View File

@@ -39,6 +39,9 @@
[<code class="Fl"><a href="#g">-g</a></code> <var class="Ar">chars</var>] [<code class="Fl"><a href="#g">-g</a></code> <var class="Ar">chars</var>]
[<code class="Fl"><a href="#i">-i</a></code> <var class="Ar">path</var>] [<code class="Fl"><a href="#i">-i</a></code> <var class="Ar">path</var>]
[<code class="Fl"><a href="#M">-M</a></code> <var class="Ar">depend_file</var>] [<code class="Fl"><a href="#M">-M</a></code> <var class="Ar">depend_file</var>]
[<code class="Fl"><a href="#M">-M</a><a href="#G">G</a></code>] [<code class="Fl"><a href="#M">-M</a><a href="#P">P</a></code>]
[<code class="Fl"><a href="#M">-M</a><a href="#T">T</a></code> <var class="Ar">target_file</var>]
[<code class="Fl"><a href="#M">-M</a><a href="#Q">Q</a></code> <var class="Ar">target_file</var>]
[<code class="Fl"><a href="#o">-o</a></code> <var class="Ar">out_file</var>] [<code class="Fl"><a href="#o">-o</a></code> <var class="Ar">out_file</var>]
[<code class="Fl"><a href="#p">-p</a></code> <var class="Ar">pad_value</var>] [<code class="Fl"><a href="#p">-p</a></code> <var class="Ar">pad_value</var>]
[<code class="Fl"><a href="#r">-r</a></code> <var class="Ar">recursion_depth</var>] [<code class="Fl"><a href="#r">-r</a></code> <var class="Ar">recursion_depth</var>]
@@ -107,6 +110,35 @@ The <code class="Nm">rgbasm</code> program creates an RGB object file from an
<var class="Ar">depend_file</var></dt> <var class="Ar">depend_file</var></dt>
<dd>Print <a class="Xr">make(1)</a> dependencies to <dd>Print <a class="Xr">make(1)</a> dependencies to
<var class="Ar">depend_file</var>.</dd> <var class="Ar">depend_file</var>.</dd>
<dt><a class="permalink" href="#MG"><code class="Fl" id="MG">-MG</code></a></dt>
<dd>To be used in conjunction with <code class="Fl">-M</code>. This makes
<code class="Nm">rgbasm</code> assume that missing files are
auto-generated: when <code class="Ic">INCLUDE</code> or
<code class="Ic">INCBIN</code> is attempted on a non-existent file, it is
added as a dependency, then <code class="Nm">rgbasm</code> exits normally
instead of erroring out. This feature is used in automatic updating of
makefiles.</dd>
<dt><a class="permalink" href="#MP"><code class="Fl" id="MP">-MP</code></a></dt>
<dd>When enabled, this causes a phony target to be added for each dependency
other than the main file. This prevents <a class="Xr">make(1)</a> from
erroring out when dependency files are deleted.</dd>
<dt><a class="permalink" href="#MT"><code class="Fl" id="MT">-MT</code></a>
<var class="Ar">target_file</var></dt>
<dd>Add a target to the rules emitted by <code class="Fl">-M</code>. The exact
string provided will be written, including spaces and special characters.
<div class="Bd Bd-indent"><code class="Li"><code class="Fl">-MT</code>
<code class="Fl">-fileA</code> <code class="Fl">-MT</code>
<code class="Fl">-fileB</code></code></div>
is equivalent to
<div class="Bd Bd-indent"><code class="Li"><code class="Fl">-MT</code>
<code class="Fl">-'fileA</code>
<code class="Fl">-fileB'</code>.</code></div>
If neither this nor <code class="Fl">-MQ</code> is specified, the output
file name is used.</dd>
<dt><a class="permalink" href="#MQ"><code class="Fl" id="MQ">-MQ</code></a>
<var class="Ar">target_file</var></dt>
<dd>Same as <code class="Fl">-MT</code>, but additionally escapes any special
<a class="Xr">make(1)</a> characters, essentially &#x2018;$&#x2019;.</dd>
<dt><a class="permalink" href="#o"><code class="Fl" id="o">-o</code></a> <dt><a class="permalink" href="#o"><code class="Fl" id="o">-o</code></a>
<var class="Ar">out_file</var>, <var class="Ar">out_file</var>,
<code class="Fl">--output</code> <code class="Fl">--output</code>
@@ -200,10 +232,10 @@ Warnings are diagnostic messages that indicate possibly erroneous behavior that
<dt><a class="permalink" href="#Wlong-string"><code class="Fl" id="Wlong-string">-Wlong-string</code></a></dt> <dt><a class="permalink" href="#Wlong-string"><code class="Fl" id="Wlong-string">-Wlong-string</code></a></dt>
<dd>Warn when a string too long to fit in internal buffers is encountered. <dd>Warn when a string too long to fit in internal buffers is encountered.
This warning is enabled by <code class="Fl">-Wall</code>.</dd> This warning is enabled by <code class="Fl">-Wall</code>.</dd>
<dt><a class="permalink" href="#Wobsolete"><code class="Fl" id="Wobsolete">-Wobsolete</code></a></dt> <dt><a class="permalink" href="#Wno-obsolete"><code class="Fl" id="Wno-obsolete">-Wno-obsolete</code></a></dt>
<dd>Warn when obsolete constructs such as the <code class="Ic">jp [hl]</code> <dd>Warn when obsolete constructs such as the <code class="Ic">jp [hl]</code>
instruction or <code class="Ic">HOME</code> section type are encountered. instruction or <code class="Ic">HOME</code> section type are
This warning is enabled by <code class="Fl">-Wextra</code>.</dd> encountered.</dd>
<dt><a class="permalink" href="#Wshift"><code class="Fl" id="Wshift">-Wshift</code></a></dt> <dt><a class="permalink" href="#Wshift"><code class="Fl" id="Wshift">-Wshift</code></a></dt>
<dd>Warn when shifting right a negative value. Use a division by 2^N <dd>Warn when shifting right a negative value. Use a division by 2^N
instead.</dd> instead.</dd>

View File

@@ -232,7 +232,7 @@ A great number of operators you can use in expressions are available (listed
<p class="Pp">Unlike in a lot of languages, and for technical reasons, <p class="Pp">Unlike in a lot of languages, and for technical reasons,
<code class="Nm">rgbasm</code> still evaluates both operands of <code class="Nm">rgbasm</code> still evaluates both operands of
&#x2018;&amp;&amp;&#x2019; and &#x2018;||&#x2019;.</p> &#x2018;&amp;&amp;&#x2019; and &#x2018;||&#x2019;.</p>
<p class="Pp">! returns 1 if the operand was 0, and 1 otherwise.</p> <p class="Pp">! returns 1 if the operand was 0, and 0 otherwise.</p>
</section> </section>
<section class="Ss"> <section class="Ss">
<h2 class="Ss" id="Fixed__u2010_point_Expressions"><a class="permalink" href="#Fixed__u2010_point_Expressions">Fixed&#x2010;point <h2 class="Ss" id="Fixed__u2010_point_Expressions"><a class="permalink" href="#Fixed__u2010_point_Expressions">Fixed&#x2010;point
@@ -639,12 +639,9 @@ Before you can start writing code, you must define a section. This tells the
<var class="Ar">type</var>[<var class="Ar">addr</var>], <var class="Ar">type</var>[<var class="Ar">addr</var>],
<var class="Ar">options</var></code></div> <var class="Ar">options</var></code></div>
<p class="Pp"><var class="Ar">name</var> is a string enclosed in double quotes, <p class="Pp"><var class="Ar">name</var> is a string enclosed in double quotes,
and can be a new name or the name of an existing section. All sections and can be a new name or the name of an existing section. If the type
assembled at the same time that have the same name are considered to be the doesn't match, an error occurs. All other sections must have a unique name,
same section, and their code is put together in the object file generated by even in different source files, or the linker will treat it as an error.</p>
the assembler. If the type doesn't match, an error occurs. All other
sections must have a unique name, even in different source files, or the
linker will treat it as an error.</p>
<p class="Pp">Possible section <var class="Ar">type</var>s are as follows:</p> <p class="Pp">Possible section <var class="Ar">type</var>s are as follows:</p>
<dl class="Bl-tag"> <dl class="Bl-tag">
<dt><a class="permalink" href="#ROM0"><code class="Ic" id="ROM0">ROM0</code></a></dt> <dt><a class="permalink" href="#ROM0"><code class="Ic" id="ROM0">ROM0</code></a></dt>
@@ -703,11 +700,17 @@ Before you can start writing code, you must define a section. This tells the
<dd>Specify which <var class="Ar">bank</var> for the linker to place the <dd>Specify which <var class="Ar">bank</var> for the linker to place the
section in. See above for possible values for <var class="Ar">bank</var>, section in. See above for possible values for <var class="Ar">bank</var>,
depending on <var class="Ar">type</var>.</dd> depending on <var class="Ar">type</var>.</dd>
<dt><a class="permalink" href="#ALIGN"><code class="Ic" id="ALIGN">ALIGN</code></a>[<var class="Ar">align</var>]</dt> <dt><a class="permalink" href="#ALIGN"><code class="Ic" id="ALIGN">ALIGN</code></a>[<var class="Ar">align</var>,
<var class="Ar">offset</var>]</dt>
<dd>Place the section at an address whose <var class="Ar">align</var> <dd>Place the section at an address whose <var class="Ar">align</var>
least&#x2010;significant bits are zero. This option can be used with least&#x2010;significant bits are equal to <var class="Ar">offset</var>.
<var class="Ar">addr</var>, as long as they don't contradict (Note that <code class="Ic">ALIGN</code>[<var class="Ar">align</var>] is a
eachother.</dd> shorthand for <code class="Ic">ALIGN</code>[<var class="Ar">align</var>,
<span class="No">0</span>]). This option can be used with
[<var class="Ar">addr</var>], as long as they don't contradict eachother.
It's also possible to request alignment in the middle of a section, see
<a class="Sx" href="#Requesting_alignment">Requesting alignment</a>
below.</dd>
</dl> </dl>
<p class="Pp">If [<var class="Ar">addr</var>] is not specified, the section is <p class="Pp">If [<var class="Ar">addr</var>] is not specified, the section is
considered &#x201C;floating&#x201D;; the linker will automatically calculate considered &#x201C;floating&#x201D;; the linker will automatically calculate
@@ -724,7 +727,7 @@ Before you can start writing code, you must define a section. This tells the
<li> <li>
<div class="Bd Pp Bd-indent"> <div class="Bd Pp Bd-indent">
<pre> <pre>
SECTION &quot;CoolStuff&quot;,ROMX SECTION &quot;Cool Stuff&quot;,ROMX
</pre> </pre>
</div> </div>
This switches to the section called &#x201C;CoolStuff&#x201D;, creating it This switches to the section called &#x201C;CoolStuff&#x201D;, creating it
@@ -733,14 +736,14 @@ SECTION &quot;CoolStuff&quot;,ROMX
<li>If it is needed, the the base address of the section can be specified: <li>If it is needed, the the base address of the section can be specified:
<div class="Bd Pp Bd-indent"> <div class="Bd Pp Bd-indent">
<pre> <pre>
SECTION &quot;CoolStuff&quot;,ROMX[$4567] SECTION &quot;Cool Stuff&quot;,ROMX[$4567]
</pre> </pre>
</div> </div>
</li> </li>
<li>An example with a fixed bank: <li>An example with a fixed bank:
<div class="Bd Pp Bd-indent"> <div class="Bd Pp Bd-indent">
<pre> <pre>
SECTION &quot;CoolStuff&quot;,ROMX[$4567],BANK[3] SECTION &quot;Cool Stuff&quot;,ROMX[$4567],BANK[3]
</pre> </pre>
</div> </div>
</li> </li>
@@ -748,7 +751,7 @@ SECTION &quot;CoolStuff&quot;,ROMX[$4567],BANK[3]
within the bank, that's also possible: within the bank, that's also possible:
<div class="Bd Pp Bd-indent"> <div class="Bd Pp Bd-indent">
<pre> <pre>
SECTION &quot;CoolStuff&quot;,ROMX,BANK[7] SECTION &quot;Cool Stuff&quot;,ROMX,BANK[7]
</pre> </pre>
</div> </div>
</li> </li>
@@ -848,7 +851,8 @@ When you're tight on RAM, you may want to define overlapping blocks of
several times per <code class="Nm">rgbasm</code> invocation, and across several times per <code class="Nm">rgbasm</code> invocation, and across
several invocations. Different declarations are treated and merged several invocations. Different declarations are treated and merged
identically whether within the same invocation, or different ones.</li> identically whether within the same invocation, or different ones.</li>
<li>A section cannot be declared both as unionized or non-unionized.</li> <li>If one section has been declared as unionized, all sections with the same
name must be declared unionized as well.</li>
<li>All declarations must have the same type. For example, even if <li>All declarations must have the same type. For example, even if
<a class="Xr" href="rgblink.1.html">rgblink(1)</a>'s <code class="Fl">-w</code> flag is used, <a class="Xr" href="rgblink.1.html">rgblink(1)</a>'s <code class="Fl">-w</code> flag is used,
<code class="Ic">WRAM0</code> and <code class="Ic">WRAMX</code> types are <code class="Ic">WRAM0</code> and <code class="Ic">WRAMX</code> types are
@@ -865,6 +869,43 @@ When you're tight on RAM, you may want to define overlapping blocks of
<a class="Sx" href="#Unions">Unions</a>. Similarly, the size of an unionized <a class="Sx" href="#Unions">Unions</a>. Similarly, the size of an unionized
section is the largest of all its declarations.</p> section is the largest of all its declarations.</p>
</section> </section>
<section class="Ss">
<h2 class="Ss" id="Section_Fragments"><a class="permalink" href="#Section_Fragments">Section
Fragments</a></h2>
Section fragments are sections with a small twist: when several of the same name
are encountered, they are concatenated instead of producing an error. This
works within the same file (paralleling the behavior &quot;plain&quot;
sections has in previous versions), but also across object files. However,
similarly to <a class="Sx" href="#Unionized_Sections">Unionized Sections</a>,
some rules must be followed:
<ul class="Bl-bullet Bd-indent">
<li>If one section has been declared as fragment, all sections with the same
name must be declared fragments as well.</li>
<li>All declarations must have the same type. For example, even if
<a class="Xr" href="rgblink.1.html">rgblink(1)</a>'s <code class="Fl">-w</code> flag is used,
<code class="Ic">WRAM0</code> and <code class="Ic">WRAMX</code> types are
still considered different.</li>
<li>Different constraints (alignment, bank, etc.) can be specified for each
unionized section declaration, but they must all be compatible. For
example, alignment must be compatible with any fixed address, all
specified banks must be the same, etc.</li>
<li>A section fragment may not be unionized; after all, that wouldn't make
much sense.</li>
</ul>
<p class="Pp">When RGBASM merges two fragments, the one encountered later is
appended to the one encountered earlier.</p>
<p class="Pp">When RGBLINK merges two fragments, the one whose file was
specified last is appended to the one whose file was specified first. For
example, assuming &#x2018;<code class="Li">bar.o</code>&#x2019;,
&#x2018;<code class="Li">baz.o</code>&#x2019;, and
&#x2018;<code class="Li">foo.o</code>&#x2019; all contain a fragment with
the same name, the command</p>
<div class="Bd Bd-indent"><code class="Li">rgblink -o rom.gb baz.o foo.o
bar.o</code></div>
would produce the fragment from &#x2018;<code class="Li">baz.o</code>&#x2019;
first, followed by the one from &#x2018;<code class="Li">foo.o</code>&#x2019;,
and the one from &#x2018;<code class="Li">bar.o</code>&#x2019; last.
</section>
</section> </section>
<section class="Sh"> <section class="Sh">
<h1 class="Sh" id="SYMBOLS"><a class="permalink" href="#SYMBOLS">SYMBOLS</a></h1> <h1 class="Sh" id="SYMBOLS"><a class="permalink" href="#SYMBOLS">SYMBOLS</a></h1>
@@ -919,6 +960,8 @@ ThisWillBeExported.too::
&#x2018;<code class="Li">.local:</code>&#x2019;. If the former notation &#x2018;<code class="Li">.local:</code>&#x2019;. If the former notation
is used, then &#x2018;<code class="Li">scope</code>&#x2019; must be the is used, then &#x2018;<code class="Li">scope</code>&#x2019; must be the
actual current scope.</p> actual current scope.</p>
<p class="Pp">Local labels may have whitespace before their declaration as
the only exception to the rule.</p>
<p class="Pp">A label's location (and thus value) is usually not determined <p class="Pp">A label's location (and thus value) is usually not determined
until the linking stage, so labels usually cannot be used as constants. until the linking stage, so labels usually cannot be used as constants.
However, if the section in which the label is declared has a fixed base However, if the section in which the label is declared has a fixed base
@@ -1665,6 +1708,23 @@ DW `00112233
number of entries is limited only by the amount of memory in your number of entries is limited only by the amount of memory in your
machine.</p> machine.</p>
</section> </section>
<section class="Ss">
<h2 class="Ss" id="Requesting_alignment"><a class="permalink" href="#Requesting_alignment">Requesting
alignment</a></h2>
While <code class="Ic">ALIGN</code> as presented in
<a class="Sx" href="#SECTIONS">SECTIONS</a> is often useful as-is, sometimes
you instead want a particular piece of data (or code) in the middle of the
section to be aligned. This is made easier through the use of mid-section
<code class="Ic">align</code> <var class="Ar">align</var>,
<var class="Ar">offset</var>. It will alter the section's attributes to ensure
that the location the <code class="Ic">align</code> directive is at, has its
<var class="Ar">align</var> lower bits equal to <var class="Ar">offset</var>.
<p class="Pp">If the constraint cannot be met (for example because the section
is fixed at an incompatible address), and error is produced. Note that
<code class="Ic">align</code> <var class="Ar">align</var> is a shorthand for
<code class="Ic">align</code> <var class="Ar">align</var>,
<span class="No">0</span>.</p>
</section>
</section> </section>
<section class="Sh"> <section class="Sh">
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE <h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE

View File

@@ -107,8 +107,10 @@ REPT NumberOfSections
; decide (floating bank). This field is only valid for ROMX, ; decide (floating bank). This field is only valid for ROMX,
; VRAM, WRAMX and SRAM sections. ; VRAM, WRAMX and SRAM sections.
LONG Align ; Alignment of this section, expressed as 1 &lt;&lt; align. 1 if BYTE Align ; Alignment of this section, as N bits. 0 when not specified.
; not specified.
LONG Ofs ; Offset relative to the alignment specified above.
; Must be below 1 &lt;&lt; Align.
IF (Type == ROMX) || (Type == ROM0) ; Sections that can contain data. IF (Type == ROMX) || (Type == ROM0) ; Sections that can contain data.
@@ -116,8 +118,6 @@ REPT NumberOfSections
LONG NumberOfPatches ; Number of patches to apply. LONG NumberOfPatches ; Number of patches to apply.
; These types of sections may have patches
REPT NumberOfPatches REPT NumberOfPatches
STRING SourceFile ; Name of the source file (for printing error STRING SourceFile ; Name of the source file (for printing error
@@ -126,6 +126,16 @@ REPT NumberOfSections
LONG Offset ; Offset into the section where patch should LONG Offset ; Offset into the section where patch should
; be applied (in bytes). ; be applied (in bytes).
LONG PCSectionID ; Index within the file of the section in which
; PC is located.
; This is usually the same section that the
; patch should be applied into, except e.g.
; with LOAD blocks.
LONG PCOffset ; PC's offset into the above section.
; Used because the section may be floating, so
; PC's value is not known to RGBASM.
BYTE Type ; 0 = BYTE patch. BYTE Type ; 0 = BYTE patch.
; 1 = little endian WORD patch. ; 1 = little endian WORD patch.
; 2 = little endian LONG patch. ; 2 = little endian LONG patch.
@@ -152,6 +162,13 @@ REPT NumberOfAssertions
LONG Offset ; Offset into the section where the assertion is located. LONG Offset ; Offset into the section where the assertion is located.
LONG SectionID ; Index within the file of the section in which PC is
; located, or -1 if defined outside a section.
LONG PCOffset ; PC's offset into the above section.
; Used because the section may be floating, so PC's value
; is not known to RGBASM.
BYTE Type ; 0 = Prints the message but allows linking to continue BYTE Type ; 0 = Prints the message but allows linking to continue
; 1 = Prints the message and evaluates other assertions, ; 1 = Prints the message and evaluates other assertions,
; but linking fails afterwards ; but linking fails afterwards
@@ -161,10 +178,6 @@ REPT NumberOfAssertions
BYTE RPN[RPNSize] ; RPN expression, same as patches. Assert fails if == 0. BYTE RPN[RPNSize] ; RPN expression, same as patches. Assert fails if == 0.
LONG SectionID ; The section number (of this object file) in which this
; assert is defined. If it doesn't belong to any specific
; section (like a constant), this field has the value -1.
STRING Message ; A message displayed when the assert fails. If set to STRING Message ; A message displayed when the assert fails. If set to
; the empty string, a generic message is printed instead. ; the empty string, a generic message is printed instead.
@@ -248,7 +261,7 @@ Expressions in the object file are stored as RPN. This is an expression of the
</tr> </tr>
<tr> <tr>
<td><a class="permalink" href="#$23"><code class="Li" id="$23">$23</code></a></td> <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> <td><a class="permalink" href="#unary__!"><code class="Li" id="unary__!">unary&#x00A0;!</code></a></td>
</tr> </tr>
<tr> <tr>
<td><a class="permalink" href="#$30"><code class="Li" id="$30">$30</code></a></td> <td><a class="permalink" href="#$30"><code class="Li" id="$30">$30</code></a></td>

View File

@@ -33,7 +33,7 @@
<table class="Nm"> <table class="Nm">
<tr> <tr>
<td><code class="Nm">rgblink</code></td> <td><code class="Nm">rgblink</code></td>
<td>[<code class="Fl"><a href="#d">-d</a><a href="#t">t</a><a href="#V">V</a><a href="#v">v</a><a href="#w">w</a></code>] [<code class="Fl"><a href="#l">-l</a></code> <td>[<code class="Fl"><a href="#d">-d</a><a href="#t">t</a><a href="#V">V</a><a href="#v">v</a><a href="#w">w</a><a href="#x">x</a></code>] [<code class="Fl"><a href="#l">-l</a></code>
<var class="Ar">linker_script</var>] [<code class="Fl"><a href="#m">-m</a></code> <var class="Ar">linker_script</var>] [<code class="Fl"><a href="#m">-m</a></code>
<var class="Ar">map_file</var>] [<code class="Fl"><a href="#n">-n</a></code> <var class="Ar">map_file</var>] [<code class="Fl"><a href="#n">-n</a></code>
<var class="Ar">sym_file</var>] [<code class="Fl"><a href="#O">-O</a></code> <var class="Ar">sym_file</var>] [<code class="Fl"><a href="#O">-O</a></code>
@@ -71,7 +71,7 @@ The <code class="Nm">rgblink</code> program links RGB object files, typically
<dt><a class="permalink" href="#d"><code class="Fl" id="d">-d</code></a>, <dt><a class="permalink" href="#d"><code class="Fl" id="d">-d</code></a>,
<code class="Fl">--dmg</code></dt> <code class="Fl">--dmg</code></dt>
<dd>Enable DMG mode. Prohibit the use of sections that doesn't exist on a DMG, <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 such as VRAM bank 1. This option automatically enables
<code class="Fl">-w</code>.</dd> <code class="Fl">-w</code>.</dd>
<dt><a class="permalink" href="#l"><code class="Fl" id="l">-l</code></a> <dt><a class="permalink" href="#l"><code class="Fl" id="l">-l</code></a>
<var class="Ar">linker_script,</var> <var class="Ar">linker_script,</var>
@@ -122,7 +122,8 @@ The <code class="Nm">rgblink</code> program links RGB object files, typically
<dt><a class="permalink" href="#t"><code class="Fl" id="t">-t</code></a>, <dt><a class="permalink" href="#t"><code class="Fl" id="t">-t</code></a>,
<code class="Fl">--tiny</code></dt> <code class="Fl">--tiny</code></dt>
<dd>Expand the ROM0 section size from 16 KiB to the full 32 KiB assigned to <dd>Expand the ROM0 section size from 16 KiB to the full 32 KiB assigned to
ROM and prohibit the use of ROMX sections. Useful for ROMs that fit in 32 ROM. ROMX sections that are fixed to a bank other than 1 become errors,
other ROMX sections are treated as ROM0. Useful for ROMs that fit in 32
KiB.</dd> KiB.</dd>
<dt><a class="permalink" href="#V"><code class="Fl" id="V">-V</code></a>, <dt><a class="permalink" href="#V"><code class="Fl" id="V">-V</code></a>,
<code class="Fl">--version</code></dt> <code class="Fl">--version</code></dt>
@@ -133,7 +134,15 @@ The <code class="Nm">rgblink</code> program links RGB object files, typically
<dt><a class="permalink" href="#w"><code class="Fl" id="w">-w</code></a>, <dt><a class="permalink" href="#w"><code class="Fl" id="w">-w</code></a>,
<code class="Fl">--wramx</code></dt> <code class="Fl">--wramx</code></dt>
<dd>Expand the WRAM0 section size from 4 KiB to the full 8 KiB assigned to <dd>Expand the WRAM0 section size from 4 KiB to the full 8 KiB assigned to
WRAM and prohibit the use of WRAMX sections.</dd> WRAM. WRAMX sections that are fixed to a bank other than 1 become errors,
other WRAMX sections are treated as WRAM0.</dd>
<dt><a class="permalink" href="#x"><code class="Fl" id="x">-x</code></a>,
<code class="Fl">--nopad</code></dt>
<dd>Disables padding the end of the final file. This option automatically
enables <code class="Fl">-t</code>. You can use this when not not making a
ROM. When making a ROM, be careful that not using this is not a
replacement for <a class="Xr" href="rgbfix.1.html">rgbfix(1)</a>'s <code class="Fl">-p</code>
option!</dd>
</dl> </dl>
</section> </section>
<section class="Sh"> <section class="Sh">

View File

@@ -22,7 +22,7 @@
#include "asm/symbol.h" #include "asm/symbol.h"
#define MAXUNIONS 128 #define MAXUNIONS 128
#define MAXMACROARGS 256 #define MAXMACROARGS 99999
#define MAXINCPATHS 128 #define MAXINCPATHS 128
extern int32_t nLineNo; extern int32_t nLineNo;
@@ -34,7 +34,6 @@ extern uint32_t unionStart[MAXUNIONS];
extern uint32_t unionSize[MAXUNIONS]; extern uint32_t unionSize[MAXUNIONS];
extern char tzCurrentFileName[_MAX_PATH + 1]; extern char tzCurrentFileName[_MAX_PATH + 1];
extern struct Section *pCurrentSection; extern struct Section *pCurrentSection;
extern struct sSymbol *pPCSymbol;
extern bool oDontExpandStrings; extern bool oDontExpandStrings;
size_t symvaluetostring(char *dest, size_t maxLength, char *sym, size_t symvaluetostring(char *dest, size_t maxLength, char *sym,

View File

@@ -25,7 +25,7 @@ struct MacroArgs;
struct sContext { struct sContext {
YY_BUFFER_STATE FlexHandle; YY_BUFFER_STATE FlexHandle;
struct sSymbol *pMacro; struct Symbol const *pMacro;
struct sContext *pNext; struct sContext *pNext;
char tzFileName[_MAX_PATH + 1]; char tzFileName[_MAX_PATH + 1];
struct MacroArgs *macroArgs; struct MacroArgs *macroArgs;
@@ -48,7 +48,7 @@ void fstk_Dump(void);
void fstk_DumpToStr(char *buf, size_t len); void fstk_DumpToStr(char *buf, size_t len);
void fstk_DumpStringExpansions(void); void fstk_DumpStringExpansions(void);
void fstk_AddIncludePath(char *s); void fstk_AddIncludePath(char *s);
bool fstk_RunMacro(char *s, struct MacroArgs *args); void fstk_RunMacro(char *s, struct MacroArgs *args);
void fstk_RunRept(uint32_t count, int32_t nReptLineNo); void fstk_RunRept(uint32_t count, int32_t nReptLineNo);
FILE *fstk_FindFile(char const *fname, char **incPathUsed); FILE *fstk_FindFile(char const *fname, char **incPathUsed);
int32_t fstk_GetLine(void); int32_t fstk_GetLine(void);

View File

@@ -56,7 +56,7 @@ void setup_lexer(void);
void yy_set_state(enum eLexerState i); void yy_set_state(enum eLexerState i);
YY_BUFFER_STATE yy_create_buffer(FILE *f); YY_BUFFER_STATE yy_create_buffer(FILE *f);
YY_BUFFER_STATE yy_scan_bytes(char *mem, uint32_t size); YY_BUFFER_STATE yy_scan_bytes(char const *mem, uint32_t size);
void yy_delete_buffer(YY_BUFFER_STATE buf); void yy_delete_buffer(YY_BUFFER_STATE buf);
void yy_switch_to_buffer(YY_BUFFER_STATE buf); void yy_switch_to_buffer(YY_BUFFER_STATE buf);
uint32_t lex_FloatAlloc(const struct sLexFloat *tok); uint32_t lex_FloatAlloc(const struct sLexFloat *tok);

View File

@@ -20,7 +20,7 @@ struct MacroArgs;
struct MacroArgs *macro_GetCurrentArgs(void); struct MacroArgs *macro_GetCurrentArgs(void);
struct MacroArgs *macro_NewArgs(void); struct MacroArgs *macro_NewArgs(void);
void macro_AppendArg(struct MacroArgs *args, char *s); void macro_AppendArg(struct MacroArgs **args, char *s);
void macro_UseNewArgs(struct MacroArgs *args); void macro_UseNewArgs(struct MacroArgs *args);
void macro_FreeArgs(struct MacroArgs *args); void macro_FreeArgs(struct MacroArgs *args);
char const *macro_GetArg(uint32_t i); char const *macro_GetArg(uint32_t i);

View File

@@ -18,12 +18,7 @@
struct sOptions { struct sOptions {
char binary[2]; char binary[2];
char gbgfx[4]; char gbgfx[4];
bool exportall;
int32_t fillchar; int32_t fillchar;
bool haltnop;
bool optimizeloads;
bool verbose;
bool warnings; /* True to enable warnings, false to disable them. */
}; };
extern char *tzNewMacro; extern char *tzNewMacro;
@@ -35,6 +30,10 @@ extern uint32_t curOffset; /* Offset into the current section */
extern struct sOptions DefaultOptions; extern struct sOptions DefaultOptions;
extern struct sOptions CurrentOptions; extern struct sOptions CurrentOptions;
extern bool haltnop;
extern bool optimizeloads;
extern bool verbose;
extern bool warnings; /* True to enable warnings, false to disable them. */
extern FILE *dependfile; extern FILE *dependfile;
extern char *tzTargetFileName; extern char *tzTargetFileName;

View File

@@ -10,6 +10,7 @@
#define RGBDS_SECTION_H #define RGBDS_SECTION_H
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include "linkdefs.h" #include "linkdefs.h"
@@ -18,11 +19,12 @@ struct Expression;
struct Section { struct Section {
char *pzName; char *pzName;
enum SectionType nType; enum SectionType nType;
bool isUnion; enum SectionModifier modifier;
uint32_t size; uint32_t size;
uint32_t nOrg; uint32_t nOrg;
uint32_t nBank; uint32_t nBank;
uint32_t nAlign; uint8_t nAlign;
uint16_t alignOfs;
struct Section *pNext; struct Section *pNext;
struct Patch *pPatches; struct Patch *pPatches;
uint8_t *tData; uint8_t *tData;
@@ -30,22 +32,25 @@ struct Section {
struct SectionSpec { struct SectionSpec {
uint32_t bank; uint32_t bank;
uint32_t alignment; uint8_t alignment;
uint16_t alignOfs;
}; };
struct Section *out_FindSectionByName(const char *pzName); struct Section *out_FindSectionByName(const char *pzName);
void out_NewSection(char const *pzName, uint32_t secttype, uint32_t org, void out_NewSection(char const *pzName, uint32_t secttype, uint32_t org,
struct SectionSpec const *attributes, bool isUnion); struct SectionSpec const *attributes,
enum SectionModifier mod);
void out_SetLoadSection(char const *name, uint32_t secttype, uint32_t org, void out_SetLoadSection(char const *name, uint32_t secttype, uint32_t org,
struct SectionSpec const *attributes); struct SectionSpec const *attributes);
void out_EndLoadSection(void); void out_EndLoadSection(void);
struct Section *sect_GetSymbolSection(void); struct Section *sect_GetSymbolSection(void);
uint32_t sect_GetOutputOffset(void); uint32_t sect_GetOutputOffset(void);
void sect_AlignPC(uint8_t alignment, uint16_t offset);
void out_AbsByte(uint8_t b); void out_AbsByte(uint8_t b);
void out_AbsByteGroup(uint8_t const *s, int32_t length); void out_AbsByteGroup(uint8_t const *s, int32_t length);
void out_Skip(int32_t skip); void out_Skip(int32_t skip, bool ds);
void out_String(char const *s); void out_String(char const *s);
void out_RelByte(struct Expression *expr); void out_RelByte(struct Expression *expr);
void out_RelBytes(struct Expression *expr, uint32_t n); void out_RelBytes(struct Expression *expr, uint32_t n);

View File

@@ -29,53 +29,70 @@ enum SymbolType {
SYM_REF // Forward reference to a label SYM_REF // Forward reference to a label
}; };
struct sSymbol { struct Symbol {
char tzName[MAXSYMLEN + 1]; char name[MAXSYMLEN + 1];
enum SymbolType type; enum SymbolType type;
bool isExported; /* Whether the symbol is to be exported */ bool isExported; /* Whether the symbol is to be exported */
bool isBuiltin; /* Whether the symbol is a built-in */ bool isBuiltin; /* Whether the symbol is a built-in */
struct sSymbol *pScope; struct Symbol const *scope;
struct Section *pSection; struct Section *section;
int32_t nValue; char fileName[_MAX_PATH + 1]; /* File where the symbol was defined. */
uint32_t ulMacroSize; uint32_t fileLine; /* Line where the symbol was defined. */
char *pMacro;
int32_t (*Callback)(struct sSymbol const *self); union {
char tzFileName[_MAX_PATH + 1]; /* File where the symbol was defined. */ struct { /* If sym_IsNumeric */
uint32_t nFileLine; /* Line where the symbol was defined. */ int32_t value;
int32_t (*callback)(void);
};
struct { /* For SYM_MACRO */
uint32_t macroSize;
char *macro;
};
};
uint32_t ID; /* ID of the symbol in the object file (-1 if none) */ uint32_t ID; /* ID of the symbol in the object file (-1 if none) */
struct sSymbol *next; /* Next object to output in the object file */ struct Symbol *next; /* Next object to output in the object file */
}; };
static inline bool sym_IsDefined(struct sSymbol const *sym) bool sym_IsPC(struct Symbol const *sym);
static inline bool sym_IsDefined(struct Symbol const *sym)
{ {
return sym->type != SYM_REF; return sym->type != SYM_REF;
} }
static inline bool sym_IsConstant(struct sSymbol const *sym) static inline struct Section *sym_GetSection(struct Symbol const *sym)
{ {
return sym->type == SYM_EQU || sym->type == SYM_SET return sym_IsPC(sym) ? sect_GetSymbolSection() : sym->section;
|| (sym->type == SYM_LABEL && sym->pSection
&& sym->pSection->nOrg != -1);
} }
static inline bool sym_IsNumeric(struct sSymbol const *sym) static inline bool sym_IsConstant(struct Symbol const *sym)
{
if (sym->type == SYM_LABEL) {
struct Section const *sect = sym_GetSection(sym);
return sect && sect->nOrg != -1;
}
return sym->type == SYM_EQU || sym->type == SYM_SET;
}
static inline bool sym_IsNumeric(struct Symbol const *sym)
{ {
return sym->type == SYM_LABEL || sym->type == SYM_EQU return sym->type == SYM_LABEL || sym->type == SYM_EQU
|| sym->type == SYM_SET; || sym->type == SYM_SET;
} }
static inline bool sym_IsLabel(struct sSymbol const *sym) static inline bool sym_IsLabel(struct Symbol const *sym)
{ {
return sym->type == SYM_LABEL || sym->type == SYM_REF; return sym->type == SYM_LABEL || sym->type == SYM_REF;
} }
static inline bool sym_IsLocal(struct sSymbol const *sym) static inline bool sym_IsLocal(struct Symbol const *sym)
{ {
return sym_IsLabel(sym) && strchr(sym->tzName, '.'); return sym_IsLabel(sym) && strchr(sym->name, '.');
} }
static inline bool sym_IsExported(struct sSymbol const *sym) static inline bool sym_IsExported(struct Symbol const *sym)
{ {
return sym->isExported; return sym->isExported;
} }
@@ -83,33 +100,32 @@ static inline bool sym_IsExported(struct sSymbol const *sym)
/* /*
* Get a string equate's value * Get a string equate's value
*/ */
static inline char *sym_GetStringValue(struct sSymbol const *sym) static inline char const *sym_GetStringValue(struct Symbol const *sym)
{ {
return sym->pMacro; return sym->macro;
} }
void sym_ForEach(void (*func)(struct sSymbol *, void *), void *arg); void sym_ForEach(void (*func)(struct Symbol *, void *), void *arg);
int32_t sym_GetValue(struct sSymbol const *sym); int32_t sym_GetValue(struct Symbol const *sym);
void sym_SetExportAll(bool set); void sym_SetExportAll(bool set);
struct sSymbol *sym_AddLocalReloc(char const *tzSym); struct Symbol *sym_AddLocalReloc(char const *symName);
struct sSymbol *sym_AddReloc(char const *tzSym); struct Symbol *sym_AddReloc(char const *symName);
void sym_Export(char const *tzSym); void sym_Export(char const *symName);
struct sSymbol *sym_FindMacro(char const *s); struct Symbol *sym_AddEqu(char const *symName, int32_t value);
struct sSymbol *sym_AddEqu(char const *tzSym, int32_t value); struct Symbol *sym_AddSet(char const *symName, int32_t value);
struct sSymbol *sym_AddSet(char const *tzSym, int32_t value); uint32_t sym_GetPCValue(void);
void sym_Init(void);
uint32_t sym_GetConstantValue(char const *s); uint32_t sym_GetConstantValue(char const *s);
struct sSymbol *sym_FindSymbol(char const *tzName); struct Symbol *sym_FindSymbol(char const *symName);
char *sym_GetStringValue(struct sSymbol const *sym); struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo);
struct sSymbol *sym_AddMacro(char const *tzSym, int32_t nDefLineNo); struct Symbol *sym_Ref(char const *symName);
struct sSymbol *sym_Ref(char const *tzSym); struct Symbol *sym_AddString(char const *symName, char const *value);
struct sSymbol *sym_AddString(char const *tzSym, char const *tzValue);
uint32_t sym_GetDefinedValue(char const *s); uint32_t sym_GetDefinedValue(char const *s);
void sym_Purge(char const *tzName); void sym_Purge(char const *symName);
void sym_Init(void);
/* Functions to save and restore the current symbol scope. */ /* Functions to save and restore the current symbol scope. */
struct sSymbol *sym_GetCurrentSymbolScope(void); struct Symbol *sym_GetCurrentSymbolScope(void);
void sym_SetCurrentSymbolScope(struct sSymbol *pNewScope); void sym_SetCurrentSymbolScope(struct Symbol *newScope);
#endif /* RGBDS_SYMBOL_H */ #endif /* RGBDS_SYMBOL_H */

View File

@@ -17,6 +17,7 @@ enum WarningID {
WARNING_ASSERT, WARNING_ASSERT,
WARNING_BUILTIN_ARG, WARNING_BUILTIN_ARG,
WARNING_DIV, WARNING_DIV,
WARNING_EMPTY_DATA_DIRECTIVE,
WARNING_EMPTY_ENTRY, WARNING_EMPTY_ENTRY,
WARNING_LARGE_CONSTANT, WARNING_LARGE_CONSTANT,
WARNING_LONG_STR, WARNING_LONG_STR,

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright © 2005-2019 Rich Felker, et al. * Copyright © 2005-2020 Rich Felker, et al.
* *
* Permission is hereby granted, free of charge, to any person obtaining * Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the * a copy of this software and associated documentation files (the
@@ -23,8 +23,8 @@
/* This implementation was taken from musl and modified for RGBDS */ /* This implementation was taken from musl and modified for RGBDS */
#ifndef _GETOPT_H #ifndef RGBDS_EXTERN_GETOPT_H
#define _GETOPT_H #define RGBDS_EXTERN_GETOPT_H
extern char *optarg; extern char *optarg;
extern int optind, opterr, optopt, optreset; extern int optind, opterr, optopt, optreset;

View File

@@ -83,7 +83,7 @@ struct Mapfile {
int size; int size;
}; };
int depth, colors; extern int depth, colors;
#include "gfx/makepng.h" #include "gfx/makepng.h"
#include "gfx/gb.h" #include "gfx/gb.h"

View File

@@ -11,10 +11,11 @@
#define RGBDS_LINK_HASHMAP_H #define RGBDS_LINK_HASHMAP_H
#include <assert.h> #include <assert.h>
#include <stdbool.h>
#define HASH_NB_BITS 32 #define HASH_NB_BITS 32
#define HALF_HASH_NB_BITS 16 #define HALF_HASH_NB_BITS 16
_Static_assert(HALF_HASH_NB_BITS * 2 == HASH_NB_BITS, ""); static_assert(HALF_HASH_NB_BITS * 2 == HASH_NB_BITS, "");
#define HASHMAP_NB_BUCKETS (1 << HALF_HASH_NB_BITS) #define HASHMAP_NB_BUCKETS (1 << HALF_HASH_NB_BITS)
/* HashMapEntry is internal, please do not attempt to use it */ /* HashMapEntry is internal, please do not attempt to use it */

View File

@@ -14,9 +14,11 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include "helpers.h"
/* Variables related to CLI options */ /* Variables related to CLI options */
extern bool isDmgMode; extern bool isDmgMode;
extern char const *linkerScriptName; extern char *linkerScriptName;
extern char const *mapFileName; extern char const *mapFileName;
extern char const *symFileName; extern char const *symFileName;
extern char const *overlayFileName; extern char const *overlayFileName;
@@ -25,6 +27,7 @@ extern uint8_t padValue;
extern bool is32kMode; extern bool is32kMode;
extern bool beVerbose; extern bool beVerbose;
extern bool isWRA0Mode; extern bool isWRA0Mode;
extern bool disablePadding;
/* Helper macro for printing verbose-mode messages */ /* Helper macro for printing verbose-mode messages */
#define verbosePrint(...) do { \ #define verbosePrint(...) do { \
@@ -32,13 +35,17 @@ extern bool isWRA0Mode;
fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, __VA_ARGS__); \
} while (0) } while (0)
void error(char const *fmt, ...);
noreturn_ void fatal(char const *fmt, ...);
/** /**
* Opens a file if specified, and aborts on error. * Opens a file if specified, and aborts on error.
* @param fileName The name of the file to open; if NULL, no file will be opened * @param fileName The name of the file to open; if NULL, no file will be opened
* @param mode The mode to open the file with * @param mode The mode to open the file with
* @return A pointer to a valid FILE structure, or NULL if fileName was NULL * @return A pointer to a valid FILE structure, or NULL if fileName was NULL
*/ */
FILE * openFile(char const *fileName, char const *mode); FILE *openFile(char const *fileName, char const *mode);
#define closeFile(file) do { \ #define closeFile(file) do { \
FILE *tmp = file; \ FILE *tmp = file; \

View File

@@ -20,13 +20,12 @@
struct Assertion { struct Assertion {
struct Patch patch; struct Patch patch;
// enum AssertionType type; The `patch`'s field is instead re-used // enum AssertionType type; The `patch`'s field is instead re-used
struct Section *section;
char *message; char *message;
/* /*
* This would be redundant with `.section->fileSymbols`... but * This would be redundant with `.section->fileSymbols`... but
* `section` is sometimes NULL! * `section` is sometimes NULL!
*/ */
struct Symbol ** fileSymbols; struct Symbol **fileSymbols;
struct Assertion *next; struct Assertion *next;
}; };

View File

@@ -19,6 +19,8 @@
#include "linkdefs.h" #include "linkdefs.h"
struct Section;
struct AttachedSymbol { struct AttachedSymbol {
struct Symbol *symbol; struct Symbol *symbol;
struct AttachedSymbol *next; struct AttachedSymbol *next;
@@ -27,23 +29,29 @@ struct AttachedSymbol {
struct Patch { struct Patch {
char *fileName; char *fileName;
int32_t offset; int32_t offset;
uint32_t pcSectionID;
uint32_t pcOffset;
enum PatchType type; enum PatchType type;
int32_t rpnSize; int32_t rpnSize;
uint8_t *rpnExpression; uint8_t *rpnExpression;
struct Section const *pcSection;
}; };
struct Section { struct Section {
/* Info contained in the object files */ /* Info contained in the object files */
char *name; char *name;
uint16_t size; uint16_t size;
uint16_t offset;
enum SectionType type; enum SectionType type;
bool isUnion; enum SectionModifier modifier;
bool isAddressFixed; bool isAddressFixed;
uint16_t org; uint16_t org;
bool isBankFixed; bool isBankFixed;
uint32_t bank; uint32_t bank;
bool isAlignFixed; bool isAlignFixed;
uint16_t alignMask; uint16_t alignMask;
uint16_t alignOfs;
uint8_t *data; /* Array of size `size`*/ uint8_t *data; /* Array of size `size`*/
uint32_t nbPatches; uint32_t nbPatches;
struct Patch *patches; struct Patch *patches;

View File

@@ -12,9 +12,9 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#define RGBDS_OBJECT_VERSION_STRING "RGB%1hhu" #define RGBDS_OBJECT_VERSION_STRING "RGB%1u"
#define RGBDS_OBJECT_VERSION_NUMBER (uint8_t)9 #define RGBDS_OBJECT_VERSION_NUMBER 9U
#define RGBDS_OBJECT_REV 3 #define RGBDS_OBJECT_REV 5U
enum AssertionType { enum AssertionType {
ASSERT_WARN, ASSERT_WARN,
@@ -73,6 +73,14 @@ enum SectionType {
SECTTYPE_INVALID SECTTYPE_INVALID
}; };
enum SectionModifier {
SECTION_NORMAL,
SECTION_UNION,
SECTION_FRAGMENT
};
extern char const * const sectionModNames[];
/** /**
* Tells whether a section has data in its object file definition, * Tells whether a section has data in its object file definition,
* depending on type. * depending on type.

35
include/platform.h Normal file
View File

@@ -0,0 +1,35 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 2020 RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
/* platform-specific hacks */
#ifndef RGBDS_PLATFORM_H
#define RGBDS_PLATFORM_H
/* MSVC doesn't have strncasecmp, use a suitable replacement */
#ifdef _MSC_VER
# include <string.h>
# define strncasecmp _strnicmp
#else
# include <strings.h>
#endif
/* MSVC has deprecated strdup in favor of _strdup */
#ifdef _MSC_VER
# define strdup _strdup
#endif
/* MSVC prefixes the names of S_* macros with underscores,
and doesn't define any S_IS* macros. Define them ourselves */
#ifdef _MSC_VER
# define S_IFMT _S_IFMT
# define S_IFDIR _S_IFDIR
# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
#endif
#endif /* RGBDS_PLATFORM_H */

View File

@@ -11,7 +11,7 @@
#define PACKAGE_VERSION_MAJOR (0) #define PACKAGE_VERSION_MAJOR (0)
#define PACKAGE_VERSION_MINOR (4) #define PACKAGE_VERSION_MINOR (4)
#define PACKAGE_VERSION_PATCH (0) #define PACKAGE_VERSION_PATCH (1)
const char *get_package_version_string(void); const char *get_package_version_string(void);

100
src/CMakeLists.txt Normal file
View File

@@ -0,0 +1,100 @@
#
# This file is part of RGBDS.
#
# Copyright (c) 2020 RGBDS contributors.
#
# SPDX-License-Identifier: MIT
#
set(common_src
"extern/err.c"
"extern/getopt.c"
"version.c"
)
BISON_TARGET(ASMy "asm/asmy.y"
"${PROJECT_SOURCE_DIR}/src/asm/asmy.c"
DEFINES_FILE "${PROJECT_SOURCE_DIR}/src/asm/asmy.h"
)
# Lexer is not present yet
if(False) # FLEX_FOUND
FLEX_TARGET(Lexer "asm/lexer.l"
"${PROJECT_SOURCE_DIR}/src/asm/lexer.c"
)
ADD_FLEX_BISON_DEPENDENCY(Lexer ASMy)
set(Lexer_SOURCE "${FLEX_Lexer_OUTPUTS}")
else()
set(Lexer_SOURCE "asm/lexer.c")
endif()
set(rgbasm_src
"${BISON_ASMy_OUTPUT_SOURCE}"
"${Lexer_SOURCE}"
"asm/charmap.c"
"asm/fstack.c"
"asm/globlex.c"
"asm/macro.c"
"asm/main.c"
"asm/math.c"
"asm/output.c"
"asm/rpn.c"
"asm/section.c"
"asm/symbol.c"
"asm/util.c"
"asm/warning.c"
"extern/utf8decoder.c"
"hashmap.c"
"linkdefs.c"
)
set(rgbfix_src
"fix/main.c"
)
set(rgbgfx_src
"gfx/gb.c"
"gfx/main.c"
"gfx/makepng.c"
)
set(rgblink_src
"link/assign.c"
"link/main.c"
"link/object.c"
"link/output.c"
"link/patch.c"
"link/script.c"
"link/section.c"
"link/symbol.c"
"hashmap.c"
"linkdefs.c"
)
foreach(PROG "asm" "fix" "gfx" "link")
add_executable(rgb${PROG}
${rgb${PROG}_src}
${common_src}
)
install(TARGETS rgb${PROG} RUNTIME DESTINATION bin)
endforeach()
if(CMAKE_VERSION VERSION_LESS 2.8.12)
add_definitions(${PNG_DEFINITIONS})
include_directories(${PNG_INCLUDE_DIRS})
target_link_libraries(rgbgfx ${PNG_LIBRARIES})
else()
target_compile_definitions(rgbgfx PRIVATE ${PNG_DEFINITIONS})
target_include_directories(rgbgfx PRIVATE ${PNG_INCLUDE_DIRS})
target_link_libraries(rgbgfx PRIVATE ${PNG_LIBRARIES})
endif()
include(CheckLibraryExists)
check_library_exists("m" "sin" "" HAS_LIBM)
if(HAS_LIBM)
if(CMAKE_VERSION VERSION_LESS 2.8.12)
target_link_libraries(rgbasm LINK_PRIVATE "m")
else()
target_link_libraries(rgbasm PRIVATE "m")
endif()
endif()

View File

@@ -9,12 +9,12 @@
%{ %{
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <inttypes.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <strings.h>
#include "asm/asm.h" #include "asm/asm.h"
#include "asm/charmap.h" #include "asm/charmap.h"
@@ -33,6 +33,7 @@
#include "extern/utf8decoder.h" #include "extern/utf8decoder.h"
#include "linkdefs.h" #include "linkdefs.h"
#include "platform.h" // strncasecmp, strdup
uint32_t nListCountEmpty; uint32_t nListCountEmpty;
char *tzNewMacro; char *tzNewMacro;
@@ -43,7 +44,7 @@ size_t symvaluetostring(char *dest, size_t maxLength, char *symName,
const char *mode) const char *mode)
{ {
size_t length; size_t length;
struct sSymbol *sym = sym_FindSymbol(symName); struct Symbol *sym = sym_FindSymbol(symName);
if (sym && sym->type == SYM_EQUS) { if (sym && sym->type == SYM_EQUS) {
char const *src = sym_GetStringValue(sym); char const *src = sym_GetStringValue(sym);
@@ -79,7 +80,7 @@ size_t symvaluetostring(char *dest, size_t maxLength, char *symName,
strncpy(dest, write_ptr, maxLength + 1); strncpy(dest, write_ptr, maxLength + 1);
} else { } else {
fullLength = snprintf(dest, maxLength + 1, fullLength = snprintf(dest, maxLength + 1,
mode ? mode : "$%X", mode ? mode : "$%" PRIX32,
value); value);
} }
@@ -495,6 +496,7 @@ static void strsubUTF8(char *dest, const char *src, uint32_t pos, uint32_t len)
char tzString[MAXSTRLEN + 1]; char tzString[MAXSTRLEN + 1];
struct Expression sVal; struct Expression sVal;
int32_t nConstValue; int32_t nConstValue;
enum SectionModifier sectMod;
struct SectionSpec sectSpec; struct SectionSpec sectSpec;
struct MacroArgs *macroArg; struct MacroArgs *macroArg;
enum AssertionType assertType; enum AssertionType assertType;
@@ -569,7 +571,7 @@ static void strsubUTF8(char *dest, const char *src, uint32_t pos, uint32_t len)
%token T_POP_IF T_POP_ELIF T_POP_ELSE T_POP_ENDC %token T_POP_IF T_POP_ELIF T_POP_ELSE T_POP_ENDC
%token T_POP_EXPORT T_POP_GLOBAL T_POP_XDEF %token T_POP_EXPORT T_POP_GLOBAL T_POP_XDEF
%token T_POP_DB T_POP_DS T_POP_DW T_POP_DL %token T_POP_DB T_POP_DS T_POP_DW T_POP_DL
%token T_POP_SECTION %token T_POP_SECTION T_POP_FRAGMENT
%token T_POP_RB %token T_POP_RB
%token T_POP_RW %token T_POP_RW
%token T_POP_RL %token T_POP_RL
@@ -599,7 +601,7 @@ static void strsubUTF8(char *dest, const char *src, uint32_t pos, uint32_t len)
%token T_SECT_WRAM0 T_SECT_VRAM T_SECT_ROMX T_SECT_ROM0 T_SECT_HRAM %token T_SECT_WRAM0 T_SECT_VRAM T_SECT_ROMX T_SECT_ROM0 T_SECT_HRAM
%token T_SECT_WRAMX T_SECT_SRAM T_SECT_OAM %token T_SECT_WRAMX T_SECT_SRAM T_SECT_OAM
%type <nConstValue> sectunion %type <sectMod> sectmod
%type <macroArg> macroargs %type <macroArg> macroargs
%token T_Z80_ADC T_Z80_ADD T_Z80_AND %token T_Z80_ADC T_Z80_ADD T_Z80_AND
@@ -710,8 +712,7 @@ macro : T_ID {
yy_set_state(LEX_STATE_MACROARGS); yy_set_state(LEX_STATE_MACROARGS);
} macroargs { } macroargs {
yy_set_state(LEX_STATE_NORMAL); yy_set_state(LEX_STATE_NORMAL);
if (!fstk_RunMacro($1, $3)) fstk_RunMacro($1, $3);
fatalerror("Macro '%s' not defined", $1);
} }
; ;
@@ -720,10 +721,10 @@ macroargs : /* empty */ {
} }
| T_STRING { | T_STRING {
$$ = macro_NewArgs(); $$ = macro_NewArgs();
macro_AppendArg($$, strdup($1)); macro_AppendArg(&($$), strdup($1));
} }
| macroargs ',' T_STRING { | macroargs ',' T_STRING {
macro_AppendArg($$, strdup($3)); macro_AppendArg(&($$), strdup($3));
} }
; ;
@@ -774,6 +775,26 @@ simple_pseudoop : include
| popo | popo
| pusho | pusho
| opt | opt
| align
;
align : T_OP_ALIGN uconst {
if ($2 > 16)
yyerror("Alignment must be between 0 and 16, not %u",
$2);
else
sect_AlignPC($2, 0);
}
| T_OP_ALIGN uconst ',' uconst {
if ($2 > 16)
yyerror("Alignment must be between 0 and 16, not %u",
$2);
else if ($4 >= 1 << $2)
yyerror("Offset must be between 0 and %u, not %u",
(1 << $2) - 1, $4);
else
sect_AlignPC($2, $4);
}
; ;
opt : T_POP_OPT { opt : T_POP_OPT {
@@ -976,7 +997,7 @@ endu : T_POP_ENDU {
} }
; ;
ds : T_POP_DS uconst { out_Skip($2); } ds : T_POP_DS uconst { out_Skip($2, true); }
| T_POP_DS uconst ',' reloc_8bit { | T_POP_DS uconst ',' reloc_8bit {
out_RelBytes(&$4, $2); out_RelBytes(&$4, $2);
} }
@@ -1088,10 +1109,10 @@ popc : T_POP_POPC { charmap_Pop(); }
printt : T_POP_PRINTT string { printf("%s", $2); } printt : T_POP_PRINTT string { printf("%s", $2); }
; ;
printv : T_POP_PRINTV const { printf("$%X", $2); } printv : T_POP_PRINTV const { printf("$%" PRIX32, $2); }
; ;
printi : T_POP_PRINTI const { printf("%d", $2); } printi : T_POP_PRINTI const { printf("%" PRId32, $2); }
; ;
printf : T_POP_PRINTF const { math_Print($2); } printf : T_POP_PRINTF const { math_Print($2); }
@@ -1170,7 +1191,7 @@ constlist_8bit : constlist_8bit_entry
; ;
constlist_8bit_entry : /* empty */ { constlist_8bit_entry : /* empty */ {
out_Skip(1); out_Skip(1, false);
nListCountEmpty++; nListCountEmpty++;
} }
| reloc_8bit_no_str { out_RelByte(&$1); } | reloc_8bit_no_str { out_RelByte(&$1); }
@@ -1188,7 +1209,7 @@ constlist_16bit : constlist_16bit_entry
; ;
constlist_16bit_entry : /* empty */ { constlist_16bit_entry : /* empty */ {
out_Skip(2); out_Skip(2, false);
nListCountEmpty++; nListCountEmpty++;
} }
| reloc_16bit { out_RelWord(&$1); } | reloc_16bit { out_RelWord(&$1); }
@@ -1199,7 +1220,7 @@ constlist_32bit : constlist_32bit_entry
; ;
constlist_32bit_entry : /* empty */ { constlist_32bit_entry : /* empty */ {
out_Skip(4); out_Skip(4, false);
nListCountEmpty++; nListCountEmpty++;
} }
| relocexpr { out_RelLong(&$1); } | relocexpr { out_RelLong(&$1); }
@@ -1314,7 +1335,7 @@ relocexpr_no_str : scoped_id { rpn_Symbol(&$$, $1); }
| T_OP_DEF { | T_OP_DEF {
oDontExpandStrings = true; oDontExpandStrings = true;
} '(' scoped_id ')' { } '(' scoped_id ')' {
struct sSymbol const *sym = sym_FindSymbol($4); struct Symbol const *sym = sym_FindSymbol($4);
rpn_Number(&$$, !!sym); rpn_Number(&$$, !!sym);
@@ -1418,13 +1439,15 @@ string : T_STRING {
} }
; ;
section : T_POP_SECTION sectunion string ',' sectiontype sectorg sectattrs { section : T_POP_SECTION sectmod string ',' sectiontype sectorg sectattrs {
out_NewSection($3, $5, $6, &$7, $2); out_NewSection($3, $5, $6, &$7, $2);
} }
; ;
sectunion : /* empty */ { $$ = false; } sectmod : /* empty */ { $$ = SECTION_NORMAL; }
| T_POP_UNION { $$ = true; } | T_POP_UNION { $$ = SECTION_UNION; }
| T_POP_FRAGMENT{ $$ = SECTION_FRAGMENT; }
;
sectiontype : T_SECT_WRAM0 { $$ = SECTTYPE_WRAM0; } sectiontype : T_SECT_WRAM0 { $$ = SECTTYPE_WRAM0; }
| T_SECT_VRAM { $$ = SECTTYPE_VRAM; } | T_SECT_VRAM { $$ = SECTTYPE_VRAM; }
@@ -1449,15 +1472,29 @@ sectorg : /* empty */ { $$ = -1; }
sectattrs : /* empty */ { sectattrs : /* empty */ {
$$.alignment = 0; $$.alignment = 0;
$$.alignOfs = 0;
$$.bank = -1; $$.bank = -1;
} }
| sectattrs ',' T_OP_ALIGN '[' uconst ']' { | sectattrs ',' T_OP_ALIGN '[' uconst ']' {
if ($5 < 0 || $5 > 16) if ($5 > 16)
yyerror("Alignment must be between 0 and 16 bits, not %u", yyerror("Alignment must be between 0 and 16, not %u",
$5); $5);
else else
$$.alignment = $5; $$.alignment = $5;
} }
| sectattrs ',' T_OP_ALIGN '[' uconst ',' uconst ']' {
if ($5 > 16) {
yyerror("Alignment must be between 0 and 16, not %u",
$5);
} else {
$$.alignment = $5;
if ($7 >= 1 << $$.alignment)
yyerror("Alignment offset must not be greater than alignment (%u < %u)",
$7, 1 << $$.alignment);
else
$$.alignOfs = $7;
}
}
| sectattrs ',' T_OP_BANK '[' uconst ']' { | sectattrs ',' T_OP_BANK '[' uconst ']' {
/* We cannot check the validity of this now */ /* We cannot check the validity of this now */
$$.bank = $5; $$.bank = $5;
@@ -1584,7 +1621,7 @@ z80_ei : T_Z80_EI { out_AbsByte(0xFB); }
z80_halt : T_Z80_HALT { z80_halt : T_Z80_HALT {
out_AbsByte(0x76); out_AbsByte(0x76);
if (CurrentOptions.haltnop) if (haltnop)
out_AbsByte(0x00); out_AbsByte(0x00);
} }
; ;
@@ -1684,8 +1721,8 @@ z80_ld_mem : T_Z80_LD op_mem_ind ',' T_MODE_SP {
out_RelWord(&$2); out_RelWord(&$2);
} }
| T_Z80_LD op_mem_ind ',' T_MODE_A { | T_Z80_LD op_mem_ind ',' T_MODE_A {
if (CurrentOptions.optimizeloads && if (optimizeloads && rpn_isKnown(&$2)
(rpn_isKnown(&$2)) && ($2.nVal >= 0xFF00)) { && $2.nVal >= 0xFF00) {
out_AbsByte(0xE0); out_AbsByte(0xE0);
out_AbsByte($2.nVal & 0xFF); out_AbsByte($2.nVal & 0xFF);
rpn_Free(&$2); rpn_Free(&$2);
@@ -1732,8 +1769,8 @@ z80_ld_a : T_Z80_LD reg_r ',' T_MODE_C_IND {
} }
| T_Z80_LD reg_r ',' op_mem_ind { | T_Z80_LD reg_r ',' op_mem_ind {
if ($2 == REG_A) { if ($2 == REG_A) {
if (CurrentOptions.optimizeloads && if (optimizeloads && rpn_isKnown(&$4)
(rpn_isKnown(&$4)) && ($4.nVal >= 0xFF00)) { && $4.nVal >= 0xFF00) {
out_AbsByte(0xF0); out_AbsByte(0xF0);
out_AbsByte($4.nVal & 0xFF); out_AbsByte($4.nVal & 0xFF);
rpn_Free(&$4); rpn_Free(&$4);

View File

@@ -10,15 +10,15 @@
* FileStack routines * FileStack routines
*/ */
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h> #include <errno.h>
#include <inttypes.h>
#include <limits.h> #include <limits.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <sys/stat.h>
#include <sys/types.h>
#include "asm/fstack.h" #include "asm/fstack.h"
#include "asm/lexer.h" #include "asm/lexer.h"
@@ -29,12 +29,13 @@
#include "extern/err.h" #include "extern/err.h"
#include "platform.h" // S_ISDIR (stat macro)
#include "types.h" #include "types.h"
static struct sContext *pFileStack; static struct sContext *pFileStack;
static unsigned int nFileStackDepth; static unsigned int nFileStackDepth;
unsigned int nMaxRecursionDepth; unsigned int nMaxRecursionDepth;
static struct sSymbol *pCurrentMacro; static struct Symbol const *pCurrentMacro;
static YY_BUFFER_STATE CurrentFlexHandle; static YY_BUFFER_STATE CurrentFlexHandle;
static FILE *pCurrentFile; static FILE *pCurrentFile;
static uint32_t nCurrentStatus; static uint32_t nCurrentStatus;
@@ -69,7 +70,7 @@ static void pushcontext(void)
struct sContext **ppFileStack; struct sContext **ppFileStack;
if (++nFileStackDepth > nMaxRecursionDepth) if (++nFileStackDepth > nMaxRecursionDepth)
fatalerror("Recursion limit (%d) exceeded", nMaxRecursionDepth); fatalerror("Recursion limit (%u) exceeded", nMaxRecursionDepth);
ppFileStack = &pFileStack; ppFileStack = &pFileStack;
while (*ppFileStack) while (*ppFileStack)
@@ -271,12 +272,12 @@ void fstk_Dump(void)
pLastFile = pFileStack; pLastFile = pFileStack;
while (pLastFile) { while (pLastFile) {
fprintf(stderr, "%s(%d) -> ", pLastFile->tzFileName, fprintf(stderr, "%s(%" PRId32 ") -> ", pLastFile->tzFileName,
pLastFile->nLine); pLastFile->nLine);
pLastFile = pLastFile->pNext; pLastFile = pLastFile->pNext;
} }
fprintf(stderr, "%s(%d)", tzCurrentFileName, nLineNo); fprintf(stderr, "%s(%" PRId32 ")", tzCurrentFileName, nLineNo);
} }
void fstk_DumpToStr(char *buf, size_t buflen) void fstk_DumpToStr(char *buf, size_t buflen)
@@ -286,7 +287,7 @@ void fstk_DumpToStr(char *buf, size_t buflen)
size_t len = buflen; size_t len = buflen;
while (pLastFile) { while (pLastFile) {
retcode = snprintf(&buf[buflen - len], len, "%s(%d) -> ", retcode = snprintf(&buf[buflen - len], len, "%s(%" PRId32 ") -> ",
pLastFile->tzFileName, pLastFile->nLine); pLastFile->tzFileName, pLastFile->nLine);
if (retcode < 0) if (retcode < 0)
fatalerror("Failed to dump file stack to string: %s", fatalerror("Failed to dump file stack to string: %s",
@@ -298,8 +299,8 @@ void fstk_DumpToStr(char *buf, size_t buflen)
pLastFile = pLastFile->pNext; pLastFile = pLastFile->pNext;
} }
retcode = snprintf(&buf[buflen - len], len, "%s(%d)", tzCurrentFileName, retcode = snprintf(&buf[buflen - len], len, "%s(%" PRId32 ")",
nLineNo); tzCurrentFileName, nLineNo);
if (retcode < 0) if (retcode < 0)
fatalerror("Failed to dump file stack to string: %s", fatalerror("Failed to dump file stack to string: %s",
strerror(errno)); strerror(errno));
@@ -434,7 +435,7 @@ void fstk_RunInclude(char *tzFileName)
nCurrentStatus = STAT_isInclude; nCurrentStatus = STAT_isInclude;
snprintf(tzCurrentFileName, sizeof(tzCurrentFileName), "%s%s", snprintf(tzCurrentFileName, sizeof(tzCurrentFileName), "%s%s",
incPathUsed, tzFileName); incPathUsed, tzFileName);
if (CurrentOptions.verbose) if (verbose)
printf("Assembling %s\n", tzCurrentFileName); printf("Assembling %s\n", tzCurrentFileName);
pCurrentFile = f; pCurrentFile = f;
CurrentFlexHandle = yy_create_buffer(pCurrentFile); CurrentFlexHandle = yy_create_buffer(pCurrentFile);
@@ -449,52 +450,38 @@ void fstk_RunInclude(char *tzFileName)
/* /*
* Set up a macro for parsing * Set up a macro for parsing
*/ */
bool fstk_RunMacro(char *s, struct MacroArgs *args) void fstk_RunMacro(char *s, struct MacroArgs *args)
{ {
struct sSymbol *sym = sym_FindMacro(s); struct Symbol const *sym = sym_FindSymbol(s);
int nPrintedChars; int nPrintedChars;
if (sym == NULL || sym->pMacro == NULL) if (sym == NULL) {
return false; yyerror("Macro \"%s\" not defined", s);
return;
}
if (sym->type != SYM_MACRO) {
yyerror("\"%s\" is not a macro", s);
return;
}
pushcontext(); pushcontext();
macro_SetUniqueID(nMacroCount++); macro_SetUniqueID(nMacroCount++);
/* Minus 1 because there is a newline at the beginning of the buffer */ /* Minus 1 because there is a newline at the beginning of the buffer */
nLineNo = sym->nFileLine - 1; nLineNo = sym->fileLine - 1;
macro_UseNewArgs(args); macro_UseNewArgs(args);
nCurrentStatus = STAT_isMacro; nCurrentStatus = STAT_isMacro;
nPrintedChars = snprintf(tzCurrentFileName, _MAX_PATH + 1, nPrintedChars = snprintf(tzCurrentFileName, _MAX_PATH + 1,
"%s::%s", sym->tzFileName, s); "%s::%s", sym->fileName, s);
if (nPrintedChars > _MAX_PATH) { if (nPrintedChars > _MAX_PATH) {
popcontext(); popcontext();
fatalerror("File name + macro name is too large to fit into buffer"); fatalerror("File name + macro name is too large to fit into buffer");
} }
pCurrentMacro = sym; pCurrentMacro = sym;
CurrentFlexHandle = yy_scan_bytes(pCurrentMacro->pMacro, /* TODO: why is `strlen` being used when there's a macro size field? */
strlen(pCurrentMacro->pMacro)); CurrentFlexHandle = yy_scan_bytes(pCurrentMacro->macro,
strlen(pCurrentMacro->macro));
yy_switch_to_buffer(CurrentFlexHandle); yy_switch_to_buffer(CurrentFlexHandle);
return true;
}
/*
* Set up a stringequate for parsing
*/
void fstk_RunString(char *s)
{
const struct sSymbol *pSym = sym_FindSymbol(s);
if (pSym != NULL) {
pushcontext();
nCurrentStatus = STAT_isMacroArg;
strcpy(tzCurrentFileName, s);
CurrentFlexHandle =
yy_scan_bytes(pSym->pMacro, strlen(pSym->pMacro));
yy_switch_to_buffer(CurrentFlexHandle);
} else {
yyerror("No such string symbol '%s'", s);
}
} }
/* /*
@@ -544,7 +531,7 @@ void fstk_Init(char *pFileName)
} else { } else {
pCurrentFile = fopen(pFileName, "rb"); pCurrentFile = fopen(pFileName, "rb");
if (pCurrentFile == NULL) if (pCurrentFile == NULL)
yyerror("Unable to open file '%s': %s", pFileName, fatalerror("Unable to open file '%s': %s", pFileName,
strerror(errno)); strerror(errno));
} }
nFileStackDepth = 0; nFileStackDepth = 0;

View File

@@ -277,10 +277,10 @@ uint32_t ParseSymbol(char *src, uint32_t size)
/* If the symbol is an EQUS, expand it */ /* If the symbol is an EQUS, expand it */
if (!oDontExpandStrings) { if (!oDontExpandStrings) {
struct sSymbol const *sym = sym_FindSymbol(dest); struct Symbol const *sym = sym_FindSymbol(dest);
if (sym && sym->type == SYM_EQUS) { if (sym && sym->type == SYM_EQUS) {
char *s; char const *s;
lex_BeginStringExpansion(dest); lex_BeginStringExpansion(dest);
@@ -442,6 +442,7 @@ const struct sLexInitString lexer_strings[] = {
{"def", T_OP_DEF}, {"def", T_OP_DEF},
{"fragment", T_POP_FRAGMENT},
{"bank", T_OP_BANK}, {"bank", T_OP_BANK},
{"align", T_OP_ALIGN}, {"align", T_OP_ALIGN},
@@ -654,6 +655,7 @@ void setup_lexer(void)
lex_FloatAddFirstRange(id, 'a', 'z'); lex_FloatAddFirstRange(id, 'a', 'z');
lex_FloatAddFirstRange(id, 'A', 'Z'); lex_FloatAddFirstRange(id, 'A', 'Z');
lex_FloatAddFirstRange(id, '_', '_'); lex_FloatAddFirstRange(id, '_', '_');
lex_FloatAddSecondRange(id, '.', '.');
lex_FloatAddSecondRange(id, 'a', 'z'); lex_FloatAddSecondRange(id, 'a', 'z');
lex_FloatAddSecondRange(id, 'A', 'Z'); lex_FloatAddSecondRange(id, 'A', 'Z');
lex_FloatAddSecondRange(id, '0', '9'); lex_FloatAddSecondRange(id, '0', '9');

View File

@@ -7,12 +7,12 @@
*/ */
#include <assert.h> #include <assert.h>
#include <ctype.h>
#include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <strings.h>
#include <ctype.h>
#include "asm/asm.h" #include "asm/asm.h"
#include "asm/fstack.h" #include "asm/fstack.h"
@@ -26,6 +26,7 @@
#include "extern/err.h" #include "extern/err.h"
#include "asmy.h" #include "asmy.h"
#include "platform.h" // strncasecmp, strdup
struct sLexString { struct sLexString {
char *tzName; char *tzName;
@@ -120,7 +121,7 @@ void yyunputstr(const char *s)
void lex_BeginStringExpansion(const char *tzName) void lex_BeginStringExpansion(const char *tzName)
{ {
if (++nNbStringExpansions > nMaxRecursionDepth) if (++nNbStringExpansions > nMaxRecursionDepth)
fatalerror("Recursion limit (%d) exceeded", nMaxRecursionDepth); fatalerror("Recursion limit (%u) exceeded", nMaxRecursionDepth);
struct sStringExpansionPos *pNewStringExpansion = struct sStringExpansionPos *pNewStringExpansion =
malloc(sizeof(*pNewStringExpansion)); malloc(sizeof(*pNewStringExpansion));
@@ -190,7 +191,7 @@ static void yy_buffer_append_newlines(YY_BUFFER_STATE buf, size_t capacity)
} }
} }
YY_BUFFER_STATE yy_scan_bytes(char *mem, uint32_t size) YY_BUFFER_STATE yy_scan_bytes(char const *mem, uint32_t size)
{ {
YY_BUFFER_STATE pBuffer = malloc(sizeof(struct yy_buffer_state)); YY_BUFFER_STATE pBuffer = malloc(sizeof(struct yy_buffer_state));
@@ -254,7 +255,7 @@ YY_BUFFER_STATE yy_create_buffer(FILE *f)
else if (capacity == 0) else if (capacity == 0)
capacity = 1; capacity = 1;
while (!feof(f)) { do {
if (buf == NULL || size >= capacity) { if (buf == NULL || size >= capacity) {
if (buf) if (buf)
capacity *= 2; capacity *= 2;
@@ -273,7 +274,7 @@ YY_BUFFER_STATE yy_create_buffer(FILE *f)
fatalerror("%s: fread error", __func__); fatalerror("%s: fread error", __func__);
size += read_count; size += read_count;
} } while (!feof(f));
pBuffer->pBufferRealStart = buf; pBuffer->pBufferRealStart = buf;
pBuffer->pBufferStart = buf + SAFETYMARGIN; pBuffer->pBufferStart = buf + SAFETYMARGIN;
@@ -342,6 +343,7 @@ YY_BUFFER_STATE yy_create_buffer(FILE *f)
*mem++ = ' '; *mem++ = ' ';
/* Comments that start with * at the start of a line */ /* Comments that start with * at the start of a line */
} else if ((mem[0] == '\n') && (mem[1] == '*')) { } else if ((mem[0] == '\n') && (mem[1] == '*')) {
warning(WARNING_OBSOLETE, "'*' is deprecated for comments, please use ';' instead");
mem++; mem++;
while (!((*mem == '\n') || (*mem == '\0'))) while (!((*mem == '\n') || (*mem == '\0')))
*mem++ = ' '; *mem++ = ' ';
@@ -370,7 +372,7 @@ uint32_t lex_FloatAlloc(const struct sLexFloat *token)
bool lex_CheckCharacterRange(uint16_t start, uint16_t end) bool lex_CheckCharacterRange(uint16_t start, uint16_t end)
{ {
if (start > end || start < 1 || end > 127) { if (start > end || start < 1 || end > 127) {
yyerror("Invalid character range (start: %u, end: %u)", yyerror("Invalid character range (start: %" PRIu16 ", end: %" PRIu16 ")",
start, end); start, end);
return false; return false;
} }
@@ -486,9 +488,9 @@ void lex_AddStrings(const struct sLexInitString *lex)
{ {
while (lex->tzName) { while (lex->tzName) {
struct sLexString **ppHash; struct sLexString **ppHash;
uint32_t hash; uint32_t hash = lexcalchash(lex->tzName);
ppHash = &tLexHash[hash = lexcalchash(lex->tzName)]; ppHash = &tLexHash[hash];
while (*ppHash) while (*ppHash)
ppHash = &((*ppHash)->pNext); ppHash = &((*ppHash)->pNext);
@@ -671,7 +673,7 @@ size_t yylex_ReadBracketedSymbol(char *dest, size_t index)
* so it's handled differently * so it's handled differently
*/ */
static const char * const formatSpecifiers[] = { static const char * const formatSpecifiers[] = {
"", "%x", "%X", "%d" "", "%" PRIx32, "%" PRIX32, "%" PRId32
}; };
/* Prevent reading out of bounds! */ /* Prevent reading out of bounds! */
const char *designatedMode; const char *designatedMode;

View File

@@ -1,5 +1,7 @@
#include <assert.h> #include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -8,14 +10,25 @@
#include "asm/macro.h" #include "asm/macro.h"
#include "asm/warning.h" #include "asm/warning.h"
/*
* Your average macro invocation does not go past the tens, but some go further
* This ensures that sane and slightly insane invocations suffer no penalties,
* and the rest is insane and thus will assume responsibility.
* Additionally, ~300 bytes (on x64) of memory per level of nesting has been
* deemed reasonable. (Halve that on x86.)
*/
#define INITIAL_ARG_SIZE 32
struct MacroArgs { struct MacroArgs {
char *args[MAXMACROARGS];
unsigned int nbArgs; unsigned int nbArgs;
unsigned int shift; unsigned int shift;
unsigned int capacity;
char *args[];
}; };
static struct MacroArgs defaultArgs = { .nbArgs = 0, .shift = 0 }; #define SIZEOF_ARGS(nbArgs) (sizeof(struct MacroArgs) + \
static struct MacroArgs *macroArgs = &defaultArgs; sizeof(((struct MacroArgs){0}).args[0]) * (nbArgs))
static struct MacroArgs *macroArgs = NULL;
static uint32_t uniqueID = -1; static uint32_t uniqueID = -1;
/* /*
* The initialization is somewhat harmful, since it is never used, but it * The initialization is somewhat harmful, since it is never used, but it
@@ -32,19 +45,34 @@ struct MacroArgs *macro_GetCurrentArgs(void)
struct MacroArgs *macro_NewArgs(void) struct MacroArgs *macro_NewArgs(void)
{ {
struct MacroArgs *args = malloc(sizeof(*args)); struct MacroArgs *args = malloc(SIZEOF_ARGS(INITIAL_ARG_SIZE));
if (!args)
fatalerror("Unable to register macro arguments: %s", strerror(errno));
args->nbArgs = 0; args->nbArgs = 0;
args->shift = 0; args->shift = 0;
args->capacity = INITIAL_ARG_SIZE;
return args; return args;
} }
void macro_AppendArg(struct MacroArgs *args, char *s) void macro_AppendArg(struct MacroArgs **argPtr, char *s)
{ {
if (args->nbArgs == MAXMACROARGS) #define macArgs (*argPtr)
if (macArgs->nbArgs == MAXMACROARGS)
yyerror("A maximum of " EXPAND_AND_STR(MAXMACROARGS) yyerror("A maximum of " EXPAND_AND_STR(MAXMACROARGS)
" arguments is allowed"); " arguments is allowed");
args->args[args->nbArgs++] = s; if (macArgs->nbArgs >= macArgs->capacity) {
macArgs->capacity *= 2;
/* Check that overflow didn't roll us back */
if (macArgs->capacity <= macArgs->nbArgs)
fatalerror("Failed to add new macro argument: possible capacity overflow");
macArgs = realloc(macArgs, SIZEOF_ARGS(macArgs->capacity));
if (!macArgs)
fatalerror("Error adding new macro argument: %s", strerror(errno));
}
macArgs->args[macArgs->nbArgs++] = s;
#undef macArgs
} }
void macro_UseNewArgs(struct MacroArgs *args) void macro_UseNewArgs(struct MacroArgs *args)
@@ -62,7 +90,8 @@ char const *macro_GetArg(uint32_t i)
{ {
uint32_t realIndex = i + macroArgs->shift - 1; uint32_t realIndex = i + macroArgs->shift - 1;
return realIndex >= MAXMACROARGS ? NULL : macroArgs->args[realIndex]; return realIndex >= macroArgs->nbArgs ? NULL
: macroArgs->args[realIndex];
} }
uint32_t macro_GetUniqueID(void) uint32_t macro_GetUniqueID(void)
@@ -82,7 +111,7 @@ void macro_SetUniqueID(uint32_t id)
uniqueIDPtr = NULL; uniqueIDPtr = NULL;
} else { } else {
/* The buffer is guaranteed to be the correct size */ /* The buffer is guaranteed to be the correct size */
sprintf(uniqueIDBuf, "_%u", id); sprintf(uniqueIDBuf, "_%" PRIu32, id);
uniqueIDPtr = uniqueIDBuf; uniqueIDPtr = uniqueIDBuf;
} }
} }

View File

@@ -63,6 +63,10 @@ char *tzTargetFileName;
struct sOptions DefaultOptions; struct sOptions DefaultOptions;
struct sOptions CurrentOptions; struct sOptions CurrentOptions;
bool haltnop;
bool optimizeloads;
bool verbose;
bool warnings; /* True to enable warnings, false to disable them. */
struct sOptionStackEntry { struct sOptionStackEntry {
struct sOptions Options; struct sOptions Options;
@@ -163,7 +167,7 @@ void opt_Parse(char *s)
/* fallthrough */ /* fallthrough */
case 'p': case 'p':
if (strlen(&s[1]) <= 2) { if (strlen(&s[1]) <= 2) {
int32_t result; int result;
unsigned int fillchar; unsigned int fillchar;
result = sscanf(&s[1], "%x", &fillchar); result = sscanf(&s[1], "%x", &fillchar);
@@ -360,12 +364,12 @@ int main(int argc, char *argv[])
DefaultOptions.gbgfx[3] = '3'; DefaultOptions.gbgfx[3] = '3';
DefaultOptions.binary[0] = '0'; DefaultOptions.binary[0] = '0';
DefaultOptions.binary[1] = '1'; DefaultOptions.binary[1] = '1';
DefaultOptions.exportall = false;
DefaultOptions.fillchar = 0; DefaultOptions.fillchar = 0;
DefaultOptions.optimizeloads = true; optimizeloads = true;
DefaultOptions.haltnop = true; haltnop = true;
DefaultOptions.verbose = false; verbose = false;
DefaultOptions.warnings = true; warnings = true;
bool exportall = false;
opt_SetCurrentOptions(&DefaultOptions); opt_SetCurrentOptions(&DefaultOptions);
@@ -386,7 +390,7 @@ int main(int argc, char *argv[])
opt_AddDefine(optarg); opt_AddDefine(optarg);
break; break;
case 'E': case 'E':
newopt.exportall = true; exportall = true;
break; break;
case 'g': case 'g':
if (strlen(optarg) == 4) { if (strlen(optarg) == 4) {
@@ -399,13 +403,13 @@ int main(int argc, char *argv[])
} }
break; break;
case 'h': case 'h':
newopt.haltnop = false; haltnop = false;
break; break;
case 'i': case 'i':
fstk_AddIncludePath(optarg); fstk_AddIncludePath(optarg);
break; break;
case 'L': case 'L':
newopt.optimizeloads = false; optimizeloads = false;
break; break;
case 'M': case 'M':
if (!strcmp("-", optarg)) if (!strcmp("-", optarg))
@@ -439,18 +443,17 @@ int main(int argc, char *argv[])
printf("rgbasm %s\n", get_package_version_string()); printf("rgbasm %s\n", get_package_version_string());
exit(0); exit(0);
case 'v': case 'v':
newopt.verbose = true; verbose = true;
break; break;
case 'W': case 'W':
processWarningFlag(optarg); processWarningFlag(optarg);
break; break;
case 'w': case 'w':
newopt.warnings = false; warnings = false;
break; break;
/* Long-only options */ /* Long-only options */
case 0: case 0:
if (depType) {
switch (depType) { switch (depType) {
case 'G': case 'G':
oGeneratedMissingIncludes = true; oGeneratedMissingIncludes = true;
@@ -468,9 +471,16 @@ int main(int argc, char *argv[])
ep = make_escape(ep); ep = make_escape(ep);
nTargetFileNameLen += strlen(ep) + 1; nTargetFileNameLen += strlen(ep) + 1;
if (!tzTargetFileName) {
/* On first alloc, make an empty str */
tzTargetFileName =
malloc(nTargetFileNameLen + 1);
*tzTargetFileName = '\0';
} else {
tzTargetFileName = tzTargetFileName =
realloc(tzTargetFileName, realloc(tzTargetFileName,
nTargetFileNameLen + 1); nTargetFileNameLen + 1);
}
if (tzTargetFileName == NULL) if (tzTargetFileName == NULL)
err(1, "Cannot append new file to target file list"); err(1, "Cannot append new file to target file list");
strcat(tzTargetFileName, ep); strcat(tzTargetFileName, ep);
@@ -482,7 +492,6 @@ int main(int argc, char *argv[])
*ptr = '\0'; *ptr = '\0';
break; break;
} }
}
break; break;
/* Unrecognized options */ /* Unrecognized options */
@@ -510,7 +519,7 @@ int main(int argc, char *argv[])
setup_lexer(); setup_lexer();
if (CurrentOptions.verbose) if (verbose)
printf("Assembling %s\n", tzMainfile); printf("Assembling %s\n", tzMainfile);
if (dependfile) { if (dependfile) {
@@ -528,7 +537,7 @@ int main(int argc, char *argv[])
skipElif = true; skipElif = true;
nUnionDepth = 0; nUnionDepth = 0;
sym_Init(); sym_Init();
sym_SetExportAll(CurrentOptions.exportall); sym_SetExportAll(exportall);
fstk_Init(tzMainfile); fstk_Init(tzMainfile);
opt_ParseDefines(); opt_ParseDefines();
charmap_InitMain(); charmap_InitMain();
@@ -542,10 +551,11 @@ int main(int argc, char *argv[])
fclose(dependfile); fclose(dependfile);
if (nIFDepth != 0) if (nIFDepth != 0)
errx(1, "Unterminated IF construct (%u levels)!", nIFDepth); errx(1, "Unterminated IF construct (%" PRIu32 " levels)!",
nIFDepth);
if (nUnionDepth != 0) { if (nUnionDepth != 0) {
errx(1, "Unterminated UNION construct (%u levels)!", errx(1, "Unterminated UNION construct (%" PRIu32 " levels)!",
nUnionDepth); nUnionDepth);
} }
@@ -554,9 +564,10 @@ int main(int argc, char *argv[])
nEndClock = clock(); nEndClock = clock();
timespent = ((double)(nEndClock - nStartClock)) timespent = ((double)(nEndClock - nStartClock))
/ (double)CLOCKS_PER_SEC; / (double)CLOCKS_PER_SEC;
if (CurrentOptions.verbose) { if (verbose) {
printf("Success! %u lines in %d.%02d seconds ", nTotalLines, printf("Success! %" PRIu32 " lines in %d.%02d seconds ",
(int)timespent, ((int)(timespent * 100.0)) % 100); nTotalLines, (int)timespent,
((int)(timespent * 100.0)) % 100);
if (timespent < FLT_MIN_EXP) if (timespent < FLT_MIN_EXP)
printf("(INFINITY lines/minute)\n"); printf("(INFINITY lines/minute)\n");
else else

View File

@@ -10,6 +10,7 @@
* Fixedpoint math routines * Fixedpoint math routines
*/ */
#include <inttypes.h>
#include <math.h> #include <math.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
@@ -37,12 +38,16 @@ void math_DefinePI(void)
*/ */
void math_Print(int32_t i) void math_Print(int32_t i)
{ {
if (i >= 0) uint32_t u = i;
printf("%d.%05d", i >> 16, const char *sign = "";
((int32_t)(fx2double(i) * 100000 + 0.5)) % 100000);
else if (i < 0) {
printf("-%d.%05d", (-i) >> 16, u = -u;
((int32_t)(fx2double(-i) * 100000 + 0.5)) % 100000); sign = "-";
}
printf("%s%" PRIu32 ".%05" PRIu32, sign, u >> 16,
((uint32_t)(fx2double(u) * 100000 + 0.5)) % 100000);
} }
/* /*

View File

@@ -30,10 +30,13 @@
#include "extern/err.h" #include "extern/err.h"
#include "linkdefs.h" #include "linkdefs.h"
#include "platform.h" // strdup
struct Patch { struct Patch {
char tzFilename[_MAX_PATH + 1]; char tzFilename[_MAX_PATH + 1];
uint32_t nOffset; uint32_t nOffset;
struct Section *pcSection;
uint32_t pcOffset;
uint8_t nType; uint8_t nType;
uint32_t nRPNSize; uint32_t nRPNSize;
uint8_t *pRPN; uint8_t *pRPN;
@@ -53,8 +56,8 @@ char *tzObjectname;
struct Section *pSectionList, *pCurrentSection; struct Section *pSectionList, *pCurrentSection;
/* Linked list of symbols to put in the object file */ /* Linked list of symbols to put in the object file */
static struct sSymbol *objectSymbols = NULL; static struct Symbol *objectSymbols = NULL;
static struct sSymbol **objectSymbolsTail = &objectSymbols; static struct Symbol **objectSymbolsTail = &objectSymbols;
static uint32_t nbSymbols = 0; /* Length of the above list */ static uint32_t nbSymbols = 0; /* Length of the above list */
static struct Assertion *assertions = NULL; static struct Assertion *assertions = NULL;
@@ -79,16 +82,13 @@ static uint32_t countsections(void)
/* /*
* Count the number of patches used in this object * Count the number of patches used in this object
*/ */
static uint32_t countpatches(struct Section *pSect) static uint32_t countpatches(struct Section const *pSect)
{ {
struct Patch *pPatch;
uint32_t r = 0; uint32_t r = 0;
pPatch = pSect->pPatches; for (struct Patch const *patch = pSect->pPatches; patch != NULL;
while (pPatch) { patch = patch->pNext)
r++; r++;
pPatch = pPatch->pNext;
}
return r; return r;
} }
@@ -132,9 +132,9 @@ static void fputstring(char const *s, FILE *f)
/* /*
* Return a section's ID * Return a section's ID
*/ */
static uint32_t getsectid(struct Section *pSect) static uint32_t getsectid(struct Section const *pSect)
{ {
struct Section *sec; struct Section const *sec;
uint32_t ID = 0; uint32_t ID = 0;
sec = pSectionList; sec = pSectionList;
@@ -149,13 +149,20 @@ static uint32_t getsectid(struct Section *pSect)
fatalerror("Unknown section '%s'", pSect->pzName); fatalerror("Unknown section '%s'", pSect->pzName);
} }
static uint32_t getSectIDIfAny(struct Section const *sect)
{
return sect ? getsectid(sect) : -1;
}
/* /*
* Write a patch to a file * Write a patch to a file
*/ */
static void writepatch(struct Patch *pPatch, FILE *f) static void writepatch(struct Patch const *pPatch, FILE *f)
{ {
fputstring(pPatch->tzFilename, f); fputstring(pPatch->tzFilename, f);
fputlong(pPatch->nOffset, f); fputlong(pPatch->nOffset, f);
fputlong(getSectIDIfAny(pPatch->pcSection), f);
fputlong(pPatch->pcOffset, f);
fputc(pPatch->nType, f); fputc(pPatch->nType, f);
fputlong(pPatch->nRPNSize, f); fputlong(pPatch->nRPNSize, f);
fwrite(pPatch->pRPN, 1, pPatch->nRPNSize, f); fwrite(pPatch->pRPN, 1, pPatch->nRPNSize, f);
@@ -164,46 +171,46 @@ static void writepatch(struct Patch *pPatch, FILE *f)
/* /*
* Write a section to a file * Write a section to a file
*/ */
static void writesection(struct Section *pSect, FILE *f) static void writesection(struct Section const *pSect, FILE *f)
{ {
fputstring(pSect->pzName, f); fputstring(pSect->pzName, f);
fputlong(pSect->size, f); fputlong(pSect->size, f);
fputc(pSect->nType | pSect->isUnion << 7, f); bool isUnion = pSect->modifier == SECTION_UNION;
bool isFragment = pSect->modifier == SECTION_FRAGMENT;
fputc(pSect->nType | isUnion << 7 | isFragment << 6, f);
fputlong(pSect->nOrg, f); fputlong(pSect->nOrg, f);
fputlong(pSect->nBank, f); fputlong(pSect->nBank, f);
fputlong(pSect->nAlign, f); fputc(pSect->nAlign, f);
fputlong(pSect->alignOfs, f);
if (sect_HasData(pSect->nType)) { if (sect_HasData(pSect->nType)) {
struct Patch *pPatch;
fwrite(pSect->tData, 1, pSect->size, f); fwrite(pSect->tData, 1, pSect->size, f);
fputlong(countpatches(pSect), f); fputlong(countpatches(pSect), f);
pPatch = pSect->pPatches; for (struct Patch const *patch = pSect->pPatches; patch != NULL;
while (pPatch) { patch = patch->pNext)
writepatch(pPatch, f); writepatch(patch, f);
pPatch = pPatch->pNext;
}
} }
} }
/* /*
* Write a symbol to a file * Write a symbol to a file
*/ */
static void writesymbol(struct sSymbol const *pSym, FILE *f) static void writesymbol(struct Symbol const *sym, FILE *f)
{ {
fputstring(pSym->tzName, f); fputstring(sym->name, f);
if (!sym_IsDefined(pSym)) { if (!sym_IsDefined(sym)) {
fputc(SYMTYPE_IMPORT, f); fputc(SYMTYPE_IMPORT, f);
} else { } else {
fputc(pSym->isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, f); fputc(sym->isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, f);
fputstring(pSym->tzFileName, f); fputstring(sym->fileName, f);
fputlong(pSym->nFileLine, f); fputlong(sym->fileLine, f);
fputlong(pSym->pSection ? getsectid(pSym->pSection) : -1, f); fputlong(getSectIDIfAny(sym_GetSection(sym)), f);
fputlong(pSym->nValue, f); fputlong(sym->value, f);
} }
} }
@@ -211,7 +218,7 @@ static void writesymbol(struct sSymbol const *pSym, FILE *f)
* Returns a symbol's ID within the object file * Returns a symbol's ID within the object file
* If the symbol does not have one, one is assigned by registering the symbol * If the symbol does not have one, one is assigned by registering the symbol
*/ */
static uint32_t getSymbolID(struct sSymbol *sym) static uint32_t getSymbolID(struct Symbol *sym)
{ {
if (sym->ID == -1) { if (sym->ID == -1) {
sym->ID = nbSymbols++; sym->ID = nbSymbols++;
@@ -244,7 +251,7 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
{ {
for (unsigned int i = -1; (tzSym[++i] = popbyte()); ) for (unsigned int i = -1; (tzSym[++i] = popbyte()); )
; ;
struct sSymbol *sym = sym_FindSymbol(tzSym); struct Symbol *sym = sym_FindSymbol(tzSym);
uint32_t value; uint32_t value;
if (sym_IsConstant(sym)) { if (sym_IsConstant(sym)) {
@@ -264,7 +271,7 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
{ {
for (unsigned int i = -1; (tzSym[++i] = popbyte()); ) for (unsigned int i = -1; (tzSym[++i] = popbyte()); )
; ;
struct sSymbol *sym = sym_FindSymbol(tzSym); struct Symbol *sym = sym_FindSymbol(tzSym);
uint32_t value = getSymbolID(sym); uint32_t value = getSymbolID(sym);
writebyte(RPN_BANK_SYM); writebyte(RPN_BANK_SYM);
@@ -300,9 +307,7 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
static struct Patch *allocpatch(uint32_t type, struct Expression const *expr, static struct Patch *allocpatch(uint32_t type, struct Expression const *expr,
uint32_t ofs) uint32_t ofs)
{ {
struct Patch *pPatch; struct Patch *pPatch = malloc(sizeof(struct Patch));
pPatch = malloc(sizeof(struct Patch));
if (!pPatch) if (!pPatch)
fatalerror("No memory for patch: %s", strerror(errno)); fatalerror("No memory for patch: %s", strerror(errno));
@@ -316,6 +321,8 @@ static struct Patch *allocpatch(uint32_t type, struct Expression const *expr,
pPatch->nType = type; pPatch->nType = type;
fstk_DumpToStr(pPatch->tzFilename, sizeof(pPatch->tzFilename)); fstk_DumpToStr(pPatch->tzFilename, sizeof(pPatch->tzFilename));
pPatch->nOffset = ofs; pPatch->nOffset = ofs;
pPatch->pcSection = sect_GetSymbolSection();
pPatch->pcOffset = curOffset;
writerpn(pPatch->pRPN, &pPatch->nRPNSize, expr->tRPN, expr->nRPNLength); writerpn(pPatch->pRPN, &pPatch->nRPNSize, expr->tRPN, expr->nRPNLength);
assert(pPatch->nRPNSize == expr->nRPNPatchSize); assert(pPatch->nRPNSize == expr->nRPNPatchSize);
@@ -346,7 +353,6 @@ bool out_CreateAssert(enum AssertionType type, struct Expression const *expr,
return false; return false;
assertion->patch = allocpatch(type, expr, ofs); assertion->patch = allocpatch(type, expr, ofs);
assertion->section = pCurrentSection;
assertion->message = strdup(message); assertion->message = strdup(message);
if (!assertion->message) { if (!assertion->message) {
free(assertion); free(assertion);
@@ -362,14 +368,13 @@ bool out_CreateAssert(enum AssertionType type, struct Expression const *expr,
static void writeassert(struct Assertion *assert, FILE *f) static void writeassert(struct Assertion *assert, FILE *f)
{ {
writepatch(assert->patch, f); writepatch(assert->patch, f);
fputlong(assert->section ? getsectid(assert->section) : -1, f);
fputstring(assert->message, f); fputstring(assert->message, f);
} }
static void registerExportedSymbol(struct sSymbol *symbol, void *arg) static void registerExportedSymbol(struct Symbol *symbol, void *arg)
{ {
(void)arg; (void)arg;
if (symbol->isExported && symbol->ID == -1) { if (sym_IsExported(symbol) && symbol->ID == -1) {
*objectSymbolsTail = symbol; *objectSymbolsTail = symbol;
objectSymbolsTail = &symbol->next; objectSymbolsTail = &symbol->next;
nbSymbols++; nbSymbols++;
@@ -381,11 +386,8 @@ static void registerExportedSymbol(struct sSymbol *symbol, void *arg)
*/ */
void out_WriteObject(void) void out_WriteObject(void)
{ {
FILE *f; FILE *f = fopen(tzObjectname, "wb");
struct Section *pSect;
struct Assertion *assert = assertions;
f = fopen(tzObjectname, "wb");
if (!f) if (!f)
err(1, "Couldn't write file '%s'", tzObjectname); err(1, "Couldn't write file '%s'", tzObjectname);
@@ -398,21 +400,16 @@ void out_WriteObject(void)
fputlong(nbSymbols, f); fputlong(nbSymbols, f);
fputlong(countsections(), f); fputlong(countsections(), f);
for (struct sSymbol const *sym = objectSymbols; sym; sym = sym->next) { for (struct Symbol const *sym = objectSymbols; sym; sym = sym->next)
writesymbol(sym, f); writesymbol(sym, f);
}
pSect = pSectionList; for (struct Section *sect = pSectionList; sect; sect = sect->pNext)
while (pSect) { writesection(sect, f);
writesection(pSect, f);
pSect = pSect->pNext;
}
fputlong(countasserts(), f); fputlong(countasserts(), f);
while (assert) { for (struct Assertion *assert = assertions; assert;
assert = assert->next)
writeassert(assert, f); writeassert(assert, f);
assert = assert->next;
}
fclose(f); fclose(f);
} }
@@ -423,6 +420,6 @@ void out_WriteObject(void)
void out_SetFileName(char *s) void out_SetFileName(char *s)
{ {
tzObjectname = s; tzObjectname = s;
if (CurrentOptions.verbose) if (verbose)
printf("Output filename %s\n", s); printf("Output filename %s\n", s);
} }

View File

@@ -19,6 +19,10 @@
.Op Fl g Ar chars .Op Fl g Ar chars
.Op Fl i Ar path .Op Fl i Ar path
.Op Fl M Ar depend_file .Op Fl M Ar depend_file
.Op Fl MG
.Op Fl MP
.Op Fl MT Ar target_file
.Op Fl MQ Ar target_file
.Op Fl o Ar out_file .Op Fl o Ar out_file
.Op Fl p Ar pad_value .Op Fl p Ar pad_value
.Op Fl r Ar recursion_depth .Op Fl r Ar recursion_depth
@@ -86,6 +90,41 @@ Print
.Xr make 1 .Xr make 1
dependencies to dependencies to
.Ar depend_file . .Ar depend_file .
.It Fl MG
To be used in conjunction with
.Fl M .
This makes
.Nm
assume that missing files are auto-generated: when
.Ic INCLUDE
or
.Ic INCBIN
is attempted on a non-existent file, it is added as a dependency, then
.Nm
exits normally instead of erroring out.
This feature is used in automatic updating of makefiles.
.It Fl MP
When enabled, this causes a phony target to be added for each dependency other than the main file.
This prevents
.Xr make 1
from erroring out when dependency files are deleted.
.It Fl MT Ar target_file
Add a target to the rules emitted by
.Fl M .
The exact string provided will be written, including spaces and special characters.
.Dl Fl MT fileA Fl MT fileB
is equivalent to
.Dl Fl MT 'fileA fileB' .
If neither this nor
.Fl MQ
is specified, the output file name is used.
.It Fl MQ Ar target_file
Same as
.Fl MT ,
but additionally escapes any special
.Xr make 1
characters, essentially
.Sq $ .
.It Fl o Ar out_file , Fl Fl output Ar out_file .It Fl o Ar out_file , Fl Fl output Ar out_file
Write an object file to the given filename. Write an object file to the given filename.
.It Fl p Ar pad_value , Fl Fl pad-value Ar pad_value .It Fl p Ar pad_value , Fl Fl pad-value Ar pad_value
@@ -180,14 +219,12 @@ This warning is enabled by
Warn when a string too long to fit in internal buffers is encountered. Warn when a string too long to fit in internal buffers is encountered.
This warning is enabled by This warning is enabled by
.Fl Wall . .Fl Wall .
.It Fl Wobsolete .It Fl Wno-obsolete
Warn when obsolete constructs such as the Warn when obsolete constructs such as the
.Ic jp [hl] .Ic jp [hl]
instruction or instruction or
.Ic HOME .Ic HOME
section type are encountered. section type are encountered.
This warning is enabled by
.Fl Wextra .
.It Fl Wshift .It Fl Wshift
Warn when shifting right a negative value. Warn when shifting right a negative value.
Use a division by 2^N instead. Use a division by 2^N instead.

View File

@@ -165,7 +165,7 @@ still evaluates both operands of
and and
.Sq || . .Sq || .
.Pp .Pp
! returns 1 if the operand was 0, and 1 otherwise. ! returns 1 if the operand was 0, and 0 otherwise.
.Ss Fixedpoint Expressions .Ss Fixedpoint Expressions
.Pp .Pp
Fixed-point numbers are basically normal (32-bit) integers, which count 65536th's instead of entire units, offering better precision than integers but limiting the range of values. Fixed-point numbers are basically normal (32-bit) integers, which count 65536th's instead of entire units, offering better precision than integers but limiting the range of values.
@@ -367,7 +367,6 @@ This tells the assembler what kind of information follows and, if it is code, wh
.Pp .Pp
.Ar name .Ar name
is a string enclosed in double quotes, and can be a new name or the name of an existing section. is a string enclosed in double quotes, and can be a new name or the name of an existing section.
All sections assembled at the same time that have the same name are considered to be the same section, and their code is put together in the object file generated by the assembler.
If the type doesn't match, an error occurs. If the type doesn't match, an error occurs.
All other sections must have a unique name, even in different source files, or the linker will treat it as an error. All other sections must have a unique name, even in different source files, or the linker will treat it as an error.
.Pp .Pp
@@ -492,13 +491,21 @@ See above for possible values for
.Ar bank , .Ar bank ,
depending on depending on
.Ar type . .Ar type .
.It Ic ALIGN Ns Bq Ar align .It Ic ALIGN Ns Bq Ar align , offset
Place the section at an address whose Place the section at an address whose
.Ar align .Ar align
leastsignificant bits are zero. leastsignificant bits are equal to
.Ar offset .
(Note that
.Ic ALIGN Ns Bq Ar align
is a shorthand for
.Ic ALIGN Ns Bq Ar align , No 0 ) .
This option can be used with This option can be used with
.Ar addr , .Bq Ar addr ,
as long as they don't contradict eachother. as long as they don't contradict eachother.
It's also possible to request alignment in the middle of a section, see
.Sx Requesting alignment
below.
.El .El
.Pp .Pp
If If
@@ -520,7 +527,7 @@ Section examples:
.Bl -item .Bl -item
.It .It
.Bd -literal -offset indent .Bd -literal -offset indent
SECTION "CoolStuff",ROMX SECTION "Cool Stuff",ROMX
.Ed .Ed
This switches to the section called This switches to the section called
.Dq CoolStuff , .Dq CoolStuff ,
@@ -530,17 +537,17 @@ Code and data may follow.
.It .It
If it is needed, the the base address of the section can be specified: If it is needed, the the base address of the section can be specified:
.Bd -literal -offset indent .Bd -literal -offset indent
SECTION "CoolStuff",ROMX[$4567] SECTION "Cool Stuff",ROMX[$4567]
.Ed .Ed
.It .It
An example with a fixed bank: An example with a fixed bank:
.Bd -literal -offset indent .Bd -literal -offset indent
SECTION "CoolStuff",ROMX[$4567],BANK[3] SECTION "Cool Stuff",ROMX[$4567],BANK[3]
.Ed .Ed
.It .It
And if you want to force only the section's bank, and not its position within the bank, that's also possible: And if you want to force only the section's bank, and not its position within the bank, that's also possible:
.Bd -literal -offset indent .Bd -literal -offset indent
SECTION "CoolStuff",ROMX,BANK[7] SECTION "Cool Stuff",ROMX,BANK[7]
.Ed .Ed
.It .It
Alignment examples: Alignment examples:
@@ -647,7 +654,7 @@ The same unionized section (= having the same name) can be declared several time
invocation, and across several invocations. invocation, and across several invocations.
Different declarations are treated and merged identically whether within the same invocation, or different ones. Different declarations are treated and merged identically whether within the same invocation, or different ones.
.It .It
A section cannot be declared both as unionized or non-unionized. If one section has been declared as unionized, all sections with the same name must be declared unionized as well.
.It .It
All declarations must have the same type. All declarations must have the same type.
For example, even if For example, even if
@@ -671,6 +678,49 @@ or
Different declarations of the same unionized section are not appended, but instead overlaid on top of eachother, just like Different declarations of the same unionized section are not appended, but instead overlaid on top of eachother, just like
.Sx Unions . .Sx Unions .
Similarly, the size of an unionized section is the largest of all its declarations. Similarly, the size of an unionized section is the largest of all its declarations.
.Ss Section Fragments
Section fragments are sections with a small twist: when several of the same name are encountered, they are concatenated instead of producing an error.
This works within the same file (paralleling the behavior "plain" sections has in previous versions), but also across object files.
However, similarly to
.Sx Unionized Sections ,
some rules must be followed:
.Bl -bullet -offset indent
.It
If one section has been declared as fragment, all sections with the same name must be declared fragments as well.
.It
All declarations must have the same type.
For example, even if
.Xr rgblink 1 Ap s
.Fl w
flag is used,
.Ic WRAM0
and
.Ic WRAMX
types are still considered different.
.It
Different constraints (alignment, bank, etc.) can be specified for each unionized section declaration, but they must all be compatible.
For example, alignment must be compatible with any fixed address, all specified banks must be the same, etc.
.It
A section fragment may not be unionized; after all, that wouldn't make much sense.
.El
.Pp
When RGBASM merges two fragments, the one encountered later is appended to the one encountered earlier.
.Pp
When RGBLINK merges two fragments, the one whose file was specified last is appended to the one whose file was specified first.
For example, assuming
.Ql bar.o ,
.Ql baz.o ,
and
.Ql foo.o
all contain a fragment with the same name, the command
.Dl rgblink -o rom.gb baz.o foo.o bar.o
would produce the fragment from
.Ql baz.o
first, followed by the one from
.Ql foo.o ,
and the one from
.Ql bar.o
last.
.Sh SYMBOLS .Sh SYMBOLS
.Pp .Pp
RGBDS supports several types of symbols: RGBDS supports several types of symbols:
@@ -731,6 +781,8 @@ If the former notation is used, then
.Ql scope .Ql scope
must be the actual current scope. must be the actual current scope.
.Pp .Pp
Local labels may have whitespace before their declaration as the only exception to the rule.
.Pp
A label's location (and thus value) is usually not determined until the linking stage, so labels usually cannot be used as constants. A label's location (and thus value) is usually not determined until the linking stage, so labels usually cannot be used as constants.
However, if the section in which the label is declared has a fixed base address, its value is known at assembly time. However, if the section in which the label is declared has a fixed base address, its value is known at assembly time.
.Pp .Pp
@@ -1460,10 +1512,29 @@ provide the interface to the option stack.
will push the current set of options on the option stack. will push the current set of options on the option stack.
.Ic POPO .Ic POPO
can then later be used to restore them. can then later be used to restore them.
Useful if you want to change some options in an include file and you don't want Useful if you want to change some options in an include file and you don't want to destroy the options set by the program that included your file.
to destroy the options set by the program that included your file. The stack's number of entries is limited only by the amount of memory in your machine.
The stack's number of entries is limited only by the amount of memory in your .Ss Requesting alignment
machine. .Pp
While
.Ic ALIGN
as presented in
.Sx SECTIONS
is often useful as-is, sometimes you instead want a particular piece of data (or code) in the middle of the section to be aligned.
This is made easier through the use of mid-section
.Ic align Ar align , offset .
It will alter the section's attributes to ensure that the location the
.Ic align
directive is at, has its
.Ar align
lower bits equal to
.Ar offset .
.Pp
If the constraint cannot be met (for example because the section is fixed at an incompatible address), and error is produced.
Note that
.Ic align Ar align
is a shorthand for
.Ic align Ar align , No 0 .
.Sh SEE ALSO .Sh SEE ALSO
.Xr rgbasm 1 , .Xr rgbasm 1 ,
.Xr rgblink 1 , .Xr rgblink 1 ,

View File

@@ -12,6 +12,7 @@
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <inttypes.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@@ -50,8 +51,8 @@ static uint8_t *reserveSpace(struct Expression *expr, uint32_t size)
* To avoid generating humongous object files, cap the * To avoid generating humongous object files, cap the
* size of RPN expressions * size of RPN expressions
*/ */
fatalerror("RPN expression cannot grow larger than %d bytes", fatalerror("RPN expression cannot grow larger than %lu bytes",
MAXRPNLEN); (unsigned long)MAXRPNLEN);
else if (expr->nRPNCapacity > MAXRPNLEN / 2) else if (expr->nRPNCapacity > MAXRPNLEN / 2)
expr->nRPNCapacity = MAXRPNLEN; expr->nRPNCapacity = MAXRPNLEN;
else else
@@ -104,9 +105,9 @@ void rpn_Number(struct Expression *expr, uint32_t i)
void rpn_Symbol(struct Expression *expr, char *tzSym) void rpn_Symbol(struct Expression *expr, char *tzSym)
{ {
struct sSymbol *sym = sym_FindSymbol(tzSym); struct Symbol *sym = sym_FindSymbol(tzSym);
if (sym == pPCSymbol && !pPCSymbol->pSection) { if (sym_IsPC(sym) && !sect_GetSymbolSection()) {
yyerror("PC has no value outside a section"); yyerror("PC has no value outside a section");
rpn_Number(expr, 0); rpn_Number(expr, 0);
} else if (!sym || !sym_IsConstant(sym)) { } else if (!sym || !sym_IsConstant(sym)) {
@@ -114,9 +115,9 @@ void rpn_Symbol(struct Expression *expr, char *tzSym)
expr->isSymbol = true; expr->isSymbol = true;
sym_Ref(tzSym); sym_Ref(tzSym);
makeUnknown(expr, strcmp(tzSym, "@") makeUnknown(expr, sym_IsPC(sym)
? "'%s' is not constant at assembly time" ? "PC is not constant at assembly time"
: "PC is not constant at assembly time", : "'%s' is not constant at assembly time",
tzSym); tzSym);
expr->nRPNPatchSize += 5; /* 1-byte opcode + 4-byte symbol ID */ expr->nRPNPatchSize += 5; /* 1-byte opcode + 4-byte symbol ID */
@@ -126,7 +127,7 @@ void rpn_Symbol(struct Expression *expr, char *tzSym)
memcpy(ptr, tzSym, nameLen); memcpy(ptr, tzSym, nameLen);
/* RGBLINK assumes PC is at the byte being computed... */ /* RGBLINK assumes PC is at the byte being computed... */
if (sym == pPCSymbol && nPCOffset) { if (sym_IsPC(sym) && nPCOffset) {
struct Expression pc = *expr, offset; struct Expression pc = *expr, offset;
rpn_Number(&offset, nPCOffset); rpn_Number(&offset, nPCOffset);
@@ -143,7 +144,10 @@ void rpn_BankSelf(struct Expression *expr)
{ {
rpn_Init(expr); rpn_Init(expr);
if (pCurrentSection->nBank == -1) { if (!pCurrentSection) {
yyerror("PC has no bank outside a section");
expr->nVal = 1;
} else if (pCurrentSection->nBank == -1) {
makeUnknown(expr, "Current section's bank is not known"); makeUnknown(expr, "Current section's bank is not known");
expr->nRPNPatchSize++; expr->nRPNPatchSize++;
*reserveSpace(expr, 1) = RPN_BANK_SELF; *reserveSpace(expr, 1) = RPN_BANK_SELF;
@@ -154,10 +158,10 @@ void rpn_BankSelf(struct Expression *expr)
void rpn_BankSymbol(struct Expression *expr, char const *tzSym) void rpn_BankSymbol(struct Expression *expr, char const *tzSym)
{ {
struct sSymbol const *sym = sym_FindSymbol(tzSym); struct Symbol const *sym = sym_FindSymbol(tzSym);
/* The @ symbol is treated differently. */ /* The @ symbol is treated differently. */
if (sym == pPCSymbol) { if (sym_IsPC(sym)) {
rpn_BankSelf(expr); rpn_BankSelf(expr);
return; return;
} }
@@ -167,12 +171,13 @@ void rpn_BankSymbol(struct Expression *expr, char const *tzSym)
yyerror("BANK argument must be a label"); yyerror("BANK argument must be a label");
} else { } else {
sym_Ref(tzSym); sym_Ref(tzSym);
if (!sym)
/* If the symbol didn't exist, `sym_Ref` created it */ /* If the symbol didn't exist, `sym_Ref` created it */
struct sSymbol *pSymbol = sym_FindSymbol(tzSym); sym = sym_FindSymbol(tzSym);
if (pSymbol->pSection && pSymbol->pSection->nBank != -1) { if (sym_GetSection(sym) && sym_GetSection(sym)->nBank != -1) {
/* Symbol's section is known and bank is fixed */ /* Symbol's section is known and bank is fixed */
expr->nVal = pSymbol->pSection->nBank; expr->nVal = sym_GetSection(sym)->nBank;
} else { } else {
makeUnknown(expr, "\"%s\"'s bank is not known", tzSym); makeUnknown(expr, "\"%s\"'s bank is not known", tzSym);
expr->nRPNPatchSize += 5; /* opcode + 4-byte sect ID */ expr->nRPNPatchSize += 5; /* opcode + 4-byte sect ID */
@@ -218,7 +223,8 @@ void rpn_CheckHRAM(struct Expression *expr, const struct Expression *src)
/* That range is valid, but only keep the lower byte */ /* That range is valid, but only keep the lower byte */
expr->nVal &= 0xFF; expr->nVal &= 0xFF;
} else if (expr->nVal < 0 || expr->nVal > 0xFF) { } else if (expr->nVal < 0 || expr->nVal > 0xFF) {
yyerror("Source address $%x not in $FF00 to $FFFF", expr->nVal); yyerror("Source address $%" PRIx32 " not between $FF00 to $FFFF",
expr->nVal);
} }
} }
@@ -229,7 +235,8 @@ void rpn_CheckRST(struct Expression *expr, const struct Expression *src)
if (rpn_isKnown(expr)) { if (rpn_isKnown(expr)) {
/* A valid RST address must be masked with 0x38 */ /* A valid RST address must be masked with 0x38 */
if (expr->nVal & ~0x38) if (expr->nVal & ~0x38)
yyerror("Invalid address $%x for RST", expr->nVal); yyerror("Invalid address $%" PRIx32 " for RST",
expr->nVal);
/* The target is in the "0x38" bits, all other bits are set */ /* The target is in the "0x38" bits, all other bits are set */
expr->nVal |= 0xC7; expr->nVal |= 0xC7;
} else { } else {
@@ -256,7 +263,7 @@ static int32_t shift(int32_t shiftee, int32_t amount)
if (amount >= 0) { if (amount >= 0) {
// Left shift // Left shift
if (amount >= 32) { if (amount >= 32) {
warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %d", warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %" PRId32,
amount); amount);
return 0; return 0;
@@ -272,7 +279,7 @@ static int32_t shift(int32_t shiftee, int32_t amount)
// Right shift // Right shift
amount = -amount; amount = -amount;
if (amount >= 32) { if (amount >= 32) {
warning(WARNING_SHIFT_AMOUNT, "Shifting right by large amount %d", warning(WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32,
amount); amount);
return shiftee < 0 ? -1 : 0; return shiftee < 0 ? -1 : 0;
@@ -285,12 +292,12 @@ static int32_t shift(int32_t shiftee, int32_t amount)
* undefined, so use a left shift manually sign-extended * undefined, so use a left shift manually sign-extended
*/ */
return (uint32_t)shiftee >> amount return (uint32_t)shiftee >> amount
| -((uint32_t)1 << (32 - amount)); | -(UINT32_C(1) << (32 - amount));
} }
} }
} }
static struct sSymbol const *symbolOf(struct Expression const *expr) static struct Symbol const *symbolOf(struct Expression const *expr)
{ {
if (!rpn_isSymbol(expr)) if (!rpn_isSymbol(expr))
return NULL; return NULL;
@@ -301,14 +308,14 @@ static bool isDiffConstant(struct Expression const *src1,
struct Expression const *src2) struct Expression const *src2)
{ {
/* Check if both expressions only refer to a single symbol */ /* Check if both expressions only refer to a single symbol */
struct sSymbol const *symbol1 = symbolOf(src1); struct Symbol const *symbol1 = symbolOf(src1);
struct sSymbol const *symbol2 = symbolOf(src2); struct Symbol const *symbol2 = symbolOf(src2);
if (!symbol1 || !symbol2 if (!symbol1 || !symbol2
|| symbol1->type != SYM_LABEL || symbol2->type != SYM_LABEL) || symbol1->type != SYM_LABEL || symbol2->type != SYM_LABEL)
return false; return false;
return symbol1->pSection == symbol2->pSection; return sym_GetSection(symbol1) == sym_GetSection(symbol2);
} }
void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr, void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
@@ -366,18 +373,18 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
break; break;
case RPN_SHL: case RPN_SHL:
if (src2->nVal < 0) if (src2->nVal < 0)
warning(WARNING_SHIFT_AMOUNT, "Shifting left by negative amount %d", warning(WARNING_SHIFT_AMOUNT, "Shifting left by negative amount %" PRId32,
src2->nVal); src2->nVal);
expr->nVal = shift(src1->nVal, src2->nVal); expr->nVal = shift(src1->nVal, src2->nVal);
break; break;
case RPN_SHR: case RPN_SHR:
if (src1->nVal < 0) if (src1->nVal < 0)
warning(WARNING_SHIFT, "Shifting negative value %d", warning(WARNING_SHIFT, "Shifting negative value %" PRId32,
src1->nVal); src1->nVal);
if (src2->nVal < 0) if (src2->nVal < 0)
warning(WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %d", warning(WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32,
src2->nVal); src2->nVal);
expr->nVal = shift(src1->nVal, -src2->nVal); expr->nVal = shift(src1->nVal, -src2->nVal);
@@ -417,12 +424,12 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
case RPN_RST: case RPN_RST:
case RPN_CONST: case RPN_CONST:
case RPN_SYM: case RPN_SYM:
fatalerror("%d is no binary operator", op); fatalerror("%d is not a binary operator", op);
} }
} else if (op == RPN_SUB && isDiffConstant(src1, src2)) { } else if (op == RPN_SUB && isDiffConstant(src1, src2)) {
struct sSymbol const *symbol1 = symbolOf(src1); struct Symbol const *symbol1 = symbolOf(src1);
struct sSymbol const *symbol2 = symbolOf(src2); struct Symbol const *symbol2 = symbolOf(src2);
expr->nVal = sym_GetValue(symbol1) - sym_GetValue(symbol2); expr->nVal = sym_GetValue(symbol1) - sym_GetValue(symbol2);
expr->isKnown = true; expr->isKnown = true;

View File

@@ -1,5 +1,7 @@
#include <errno.h> #include <errno.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -12,10 +14,11 @@
#include "asm/warning.h" #include "asm/warning.h"
#include "extern/err.h" #include "extern/err.h"
#include "platform.h" // strdup
struct SectionStackEntry { struct SectionStackEntry {
struct Section *pSection; struct Section *pSection;
struct sSymbol *pScope; /* Section's symbol scope */ struct Symbol *pScope; /* Section's symbol scope */
uint32_t offset; uint32_t offset;
struct SectionStackEntry *pNext; struct SectionStackEntry *pNext;
}; };
@@ -63,7 +66,7 @@ static void reserveSpace(uint32_t delta_size)
* A check at the linking stage is still necessary. * A check at the linking stage is still necessary.
*/ */
if (newSize > maxSize) if (newSize > maxSize)
fatalerror("Section '%s' is too big (max size = 0x%X bytes, reached 0x%X).", fatalerror("Section '%s' is too big (max size = 0x%" PRIX32 " bytes, reached 0x%" PRIX32 ").",
pCurrentSection->pzName, maxSize, newSize); pCurrentSection->pzName, maxSize, newSize);
} }
@@ -85,29 +88,40 @@ struct Section *out_FindSectionByName(const char *pzName)
* Find a section by name and type. If it doesn't exist, create it * Find a section by name and type. If it doesn't exist, create it
*/ */
static struct Section *getSection(char const *pzName, enum SectionType type, static struct Section *getSection(char const *pzName, enum SectionType type,
uint32_t org, uint32_t bank, uint32_t org, struct SectionSpec const *attrs,
uint32_t alignment, bool isUnion) enum SectionModifier mod)
{ {
#define mask(align) ((1 << (align)) - 1)
uint32_t bank = attrs->bank;
uint8_t alignment = attrs->alignment;
uint16_t alignOffset = attrs->alignOfs;
if (bank != -1) { if (bank != -1) {
if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM
&& type != SECTTYPE_SRAM && type != SECTTYPE_WRAMX) && type != SECTTYPE_SRAM && type != SECTTYPE_WRAMX)
yyerror("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections"); yyerror("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections");
else if (bank < bankranges[type][0] else if (bank < bankranges[type][0]
|| bank > bankranges[type][1]) || bank > bankranges[type][1])
yyerror("%s bank value $%x out of range ($%x to $%x)", yyerror("%s bank value $%" PRIx32 " out of range ($%" PRIx32 " to $%" PRIx32 ")",
typeNames[type], bank, typeNames[type], bank,
bankranges[type][0], bankranges[type][1]); bankranges[type][0], bankranges[type][1]);
} }
if (alignment != 1) { if (alignOffset >= 1 << alignment) {
/* It doesn't make sense to have both set */ yyerror("Alignment offset must not be greater than alignment (%" PRIu16 " < %u)",
uint32_t mask = alignment - 1; alignOffset, 1U << alignment);
alignOffset = 0;
}
if (alignment != 0) {
/* It doesn't make sense to have both alignment and org set */
uint32_t mask = mask(alignment);
if (org != -1) { if (org != -1) {
if (org & mask) if ((org - alignOffset) & mask)
yyerror("Section \"%s\"'s fixed address doesn't match its alignment", yyerror("Section \"%s\"'s fixed address doesn't match its alignment",
pzName); pzName);
alignment = 1; /* Ignore it if it's satisfied */ alignment = 0; /* Ignore it if it's satisfied */
} else if (startaddr[type] & mask) { } else if (startaddr[type] & mask) {
yyerror("Section \"%s\"'s alignment cannot be attained in %s", yyerror("Section \"%s\"'s alignment cannot be attained in %s",
pzName, typeNames[type]); pzName, typeNames[type]);
@@ -116,7 +130,7 @@ static struct Section *getSection(char const *pzName, enum SectionType type,
if (org != -1) { if (org != -1) {
if (org < startaddr[type] || org > endaddr(type)) if (org < startaddr[type] || org > endaddr(type))
yyerror("Section \"%s\"'s fixed address %#x is outside of range [%#x; %#x]", yyerror("Section \"%s\"'s fixed address %#" PRIx32 " is outside of range [%#" PRIx16 "; %#" PRIx16 "]",
pzName, org, startaddr[type], endaddr(type)); pzName, org, startaddr[type], endaddr(type));
} }
@@ -137,48 +151,57 @@ static struct Section *getSection(char const *pzName, enum SectionType type,
fail("Section \"%s\" already exists but with type %s", fail("Section \"%s\" already exists but with type %s",
pSect->pzName, typeNames[pSect->nType]); pSect->pzName, typeNames[pSect->nType]);
if (pSect->modifier != mod)
fail("Section \"%s\" already declared as %s section",
pSect->pzName, sectionModNames[pSect->modifier]);
/* /*
* Normal sections need to have exactly identical constraints; * Normal sections need to have exactly identical constraints;
* but unionized sections only need "compatible" constraints, * but unionized sections only need "compatible" constraints,
* and they end up with the strictest combination of both * and they end up with the strictest combination of both
*/ */
if (isUnion) { if (mod == SECTION_UNION) {
if (!pSect->isUnion)
fail("Section \"%s\" already declared as non-union",
pSect->pzName);
/* /*
* WARNING: see comment abount assumption in * WARNING: see comment about assumption in
* `EndLoadSection` if modifying the following check! * `EndLoadSection` if modifying the following check!
*/ */
if (sect_HasData(type)) if (sect_HasData(type))
fail("Cannot declare ROM sections as UNION"); fail("Cannot declare ROM sections as UNION");
if (org != -1) { if (org != -1) {
/* If neither is fixed, they must be the same */ /* If both are fixed, they must be the same */
if (pSect->nOrg != -1 && pSect->nOrg != org) if (pSect->nOrg != -1 && pSect->nOrg != org)
fail("Section \"%s\" already declared as fixed at different address $%x", fail("Section \"%s\" already declared as fixed at different address $%" PRIx32,
pSect->pzName, pSect->nOrg); pSect->pzName, pSect->nOrg);
else if (pSect->nAlign != 0 else if (pSect->nAlign != 0
&& ((pSect->nAlign - 1) & org)) && (mask(pSect->nAlign)
fail("Section \"%s\" already declared as aligned to %u bytes", & (org - pSect->alignOfs)))
pSect->pzName, pSect->nAlign); fail("Section \"%s\" already declared as aligned to %u bytes (offset %" PRIu16 ")",
pSect->pzName, 1U << pSect->nAlign,
pSect->alignOfs);
else else
/* Otherwise, just override */ /* Otherwise, just override */
pSect->nOrg = org; pSect->nOrg = org;
} else if (alignment != 0) { } else if (alignment != 0) {
/* Make sure any fixed address is compatible */ /* Make sure any fixed address is compatible */
if (pSect->nOrg != -1) { if (pSect->nOrg != -1) {
uint32_t mask = alignment - 1; if ((pSect->nOrg - alignOffset)
& mask(alignment))
if (pSect->nOrg & mask) fail("Section \"%s\" already declared as fixed at incompatible address $%" PRIx32,
fail("Section \"%s\" already declared as fixed at incompatible address $%x",
pSect->pzName, pSect->pzName,
pSect->nOrg); pSect->nOrg);
/* Check if alignment offsets are compatible */
} else if ((alignOffset & mask(pSect->nAlign))
!= (pSect->alignOfs
& mask(alignment))) {
fail("Section \"%s\" already declared with incompatible %" PRIu8 "-byte alignment (offset %" PRIu16 ")",
pSect->pzName, pSect->nAlign,
pSect->alignOfs);
} else if (alignment > pSect->nAlign) { } else if (alignment > pSect->nAlign) {
/* /*
* If the section is not fixed, * If the section is not fixed,
* its alignment is the largest of both * its alignment is the largest of both
*/ */
pSect->nAlign = alignment; pSect->nAlign = alignment;
pSect->alignOfs = alignOffset;
} }
} }
/* If the section's bank is unspecified, override it */ /* If the section's bank is unspecified, override it */
@@ -186,18 +209,19 @@ static struct Section *getSection(char const *pzName, enum SectionType type,
pSect->nBank = bank; pSect->nBank = bank;
/* If both specify a bank, it must be the same one */ /* If both specify a bank, it must be the same one */
else if (bank != -1 && pSect->nBank != bank) else if (bank != -1 && pSect->nBank != bank)
fail("Section \"%s\" already declared with different bank %u", fail("Section \"%s\" already declared with different bank %" PRIu32,
pSect->pzName, pSect->nBank); pSect->pzName, pSect->nBank);
} else { } else { /* Section fragments are handled identically in RGBASM */
if (pSect->isUnion) /* However, concaternating non-fragments will be made an error */
fail("Section \"%s\" already declared as union", if (pSect->modifier != SECTION_FRAGMENT || mod != SECTION_FRAGMENT)
pSect->pzName); warning(WARNING_OBSOLETE, "Concatenation of non-fragment sections is deprecated");
if (org != pSect->nOrg) { if (org != pSect->nOrg) {
if (pSect->nOrg == -1) if (pSect->nOrg == -1)
fail("Section \"%s\" already declared as floating", fail("Section \"%s\" already declared as floating",
pSect->pzName); pSect->pzName);
else else
fail("Section \"%s\" already declared as fixed at $%x", fail("Section \"%s\" already declared as fixed at $%" PRIx32,
pSect->pzName, pSect->nOrg); pSect->pzName, pSect->nOrg);
} }
if (bank != pSect->nBank) { if (bank != pSect->nBank) {
@@ -205,7 +229,7 @@ static struct Section *getSection(char const *pzName, enum SectionType type,
fail("Section \"%s\" already declared as floating bank", fail("Section \"%s\" already declared as floating bank",
pSect->pzName); pSect->pzName);
else else
fail("Section \"%s\" already declared as fixed at bank %u", fail("Section \"%s\" already declared as fixed at bank %" PRIu32,
pSect->pzName, pSect->nBank); pSect->pzName, pSect->nBank);
} }
if (alignment != pSect->nAlign) { if (alignment != pSect->nAlign) {
@@ -214,7 +238,8 @@ static struct Section *getSection(char const *pzName, enum SectionType type,
pSect->pzName); pSect->pzName);
else else
fail("Section \"%s\" already declared as aligned to %u bytes", fail("Section \"%s\" already declared as aligned to %u bytes",
pSect->pzName, pSect->nAlign); pSect->pzName,
1U << pSect->nAlign);
} }
} }
@@ -234,11 +259,12 @@ static struct Section *getSection(char const *pzName, enum SectionType type,
fatalerror("Not enough memory for sectionname"); fatalerror("Not enough memory for sectionname");
pSect->nType = type; pSect->nType = type;
pSect->isUnion = isUnion; pSect->modifier = mod;
pSect->size = 0; pSect->size = 0;
pSect->nOrg = org; pSect->nOrg = org;
pSect->nBank = bank; pSect->nBank = bank;
pSect->nAlign = alignment; pSect->nAlign = alignment;
pSect->alignOfs = alignOffset;
pSect->pNext = pSectionList; pSect->pNext = pSectionList;
pSect->pPatches = NULL; pSect->pPatches = NULL;
@@ -261,18 +287,17 @@ static struct Section *getSection(char const *pzName, enum SectionType type,
pSectionList = pSect; pSectionList = pSect;
return pSect; return pSect;
#undef mask
} }
/* /*
* Set the current section * Set the current section
*/ */
static void setSection(struct Section *pSect) static void changeSection(void)
{ {
if (nUnionDepth > 0) if (nUnionDepth > 0)
fatalerror("Cannot change the section within a UNION"); fatalerror("Cannot change the section within a UNION");
pPCSymbol->pSection = pSect;
sym_SetCurrentSymbolScope(NULL); sym_SetCurrentSymbolScope(NULL);
} }
@@ -280,16 +305,15 @@ static void setSection(struct Section *pSect)
* Set the current section by name and type * Set the current section by name and type
*/ */
void out_NewSection(char const *pzName, uint32_t type, uint32_t org, void out_NewSection(char const *pzName, uint32_t type, uint32_t org,
struct SectionSpec const *attributes, bool isUnion) struct SectionSpec const *attribs, enum SectionModifier mod)
{ {
if (currentLoadSection) if (currentLoadSection)
fatalerror("Cannot change the section within a `LOAD` block"); fatalerror("Cannot change the section within a `LOAD` block");
struct Section *pSect = getSection(pzName, type, org, attributes->bank, struct Section *pSect = getSection(pzName, type, org, attribs, mod);
1 << attributes->alignment, isUnion);
setSection(pSect); changeSection();
curOffset = isUnion ? 0 : pSect->size; curOffset = mod == SECTION_UNION ? 0 : pSect->size;
pCurrentSection = pSect; pCurrentSection = pSect;
} }
@@ -297,19 +321,18 @@ void out_NewSection(char const *pzName, uint32_t type, uint32_t org,
* Set the current section by name and type * Set the current section by name and type
*/ */
void out_SetLoadSection(char const *name, uint32_t type, uint32_t org, void out_SetLoadSection(char const *name, uint32_t type, uint32_t org,
struct SectionSpec const *attributes) struct SectionSpec const *attribs)
{ {
checkcodesection(); checkcodesection();
if (currentLoadSection) if (currentLoadSection)
fatalerror("`LOAD` blocks cannot be nested"); fatalerror("`LOAD` blocks cannot be nested");
struct Section *pSect = getSection(name, type, org, attributes->bank, struct Section *pSect = getSection(name, type, org, attribs, false);
1 << attributes->alignment, false);
loadOffset = curOffset; loadOffset = curOffset;
curOffset = 0; /* curOffset -= loadOffset; */ curOffset = 0; /* curOffset -= loadOffset; */
setSection(pSect); changeSection();
currentLoadSection = pSect; currentLoadSection = pSect;
} }
@@ -319,7 +342,7 @@ void out_EndLoadSection(void)
yyerror("Found `ENDL` outside of a `LOAD` block"); yyerror("Found `ENDL` outside of a `LOAD` block");
currentLoadSection = NULL; currentLoadSection = NULL;
setSection(pCurrentSection); changeSection();
curOffset += loadOffset; curOffset += loadOffset;
loadOffset = 0; loadOffset = 0;
} }
@@ -334,6 +357,30 @@ uint32_t sect_GetOutputOffset(void)
return curOffset + loadOffset; return curOffset + loadOffset;
} }
void sect_AlignPC(uint8_t alignment, uint16_t offset)
{
struct Section *sect = sect_GetSymbolSection();
if (sect->nOrg != -1) {
if ((sym_GetPCValue() - offset) % (1 << alignment))
yyerror("Section's fixed address fails required alignment (PC = $%04" PRIx32 ")",
sym_GetPCValue());
} else if (sect->nAlign != 0) {
if ((((sect->alignOfs + curOffset) % (1 << sect->nAlign))
- offset) % (1 << alignment)) {
yyerror("Section's alignment fails required alignment (offset from section start = $%04" PRIx32 ")",
curOffset);
} else if (alignment > sect->nAlign) {
sect->nAlign = alignment;
sect->alignOfs =
(offset - curOffset) % (1 << alignment);
}
} else {
sect->nAlign = alignment;
sect->alignOfs = offset;
}
}
static inline void growSection(uint32_t growth) static inline void growSection(uint32_t growth)
{ {
curOffset += growth; curOffset += growth;
@@ -392,11 +439,14 @@ void out_AbsByteGroup(uint8_t const *s, int32_t length)
/* /*
* Skip this many bytes * Skip this many bytes
*/ */
void out_Skip(int32_t skip) void out_Skip(int32_t skip, bool ds)
{ {
checksection(); checksection();
reserveSpace(skip); reserveSpace(skip);
if (!ds && sect_HasData(pCurrentSection->nType))
warning(WARNING_EMPTY_DATA_DIRECTIVE, "db/dw/dl directive without data in ROM");
if (!sect_HasData(pCurrentSection->nType)) { if (!sect_HasData(pCurrentSection->nType)) {
growSection(skip); growSection(skip);
} else if (nUnionDepth > 0) { } else if (nUnionDepth > 0) {
@@ -509,12 +559,12 @@ void out_PCRelByte(struct Expression *expr)
writebyte(0); writebyte(0);
} else { } else {
/* Target is relative to the byte *after* the operand */ /* Target is relative to the byte *after* the operand */
uint16_t address = sym_GetValue(pPCSymbol) + 1; uint16_t address = sym_GetPCValue() + 1;
/* The offset wraps (jump from ROM to HRAM, for loopexample) */ /* The offset wraps (jump from ROM to HRAM, for loopexample) */
int16_t offset = expr->nVal - address; int16_t offset = expr->nVal - address;
if (offset < -128 || offset > 127) { if (offset < -128 || offset > 127) {
yyerror("jr target out of reach (expected -129 < %d < 128)", yyerror("jr target out of reach (expected -129 < %" PRId16 " < 128)",
offset); offset);
writebyte(0); writebyte(0);
} else { } else {
@@ -570,12 +620,13 @@ void out_BinaryFile(char const *s)
void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length) void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
{ {
if (start_pos < 0) { if (start_pos < 0) {
yyerror("Start position cannot be negative (%d)", start_pos); yyerror("Start position cannot be negative (%" PRId32 ")",
start_pos);
start_pos = 0; start_pos = 0;
} }
if (length < 0) { if (length < 0) {
yyerror("Number of bytes to read cannot be negative (%d)", yyerror("Number of bytes to read cannot be negative (%" PRId32 ")",
length); length);
length = 0; length = 0;
} }
@@ -630,7 +681,7 @@ void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
yyerror("Error reading INCBIN file '%s': %s", s, yyerror("Error reading INCBIN file '%s': %s", s,
strerror(errno)); strerror(errno));
} else { } else {
yyerror("Premature end of file (%d bytes left to read)", yyerror("Premature end of file (%" PRId32 " bytes left to read)",
todo + 1); todo + 1);
} }
} }
@@ -654,6 +705,7 @@ void out_PushSection(void)
pSect->offset = curOffset; pSect->offset = curOffset;
pSect->pNext = pSectionStack; pSect->pNext = pSectionStack;
pSectionStack = pSect; pSectionStack = pSect;
/* TODO: maybe set current section to NULL? */
} }
void out_PopSection(void) void out_PopSection(void)
@@ -667,7 +719,7 @@ void out_PopSection(void)
struct SectionStackEntry *pSect; struct SectionStackEntry *pSect;
pSect = pSectionStack; pSect = pSectionStack;
setSection(pSect->pSection); changeSection();
pCurrentSection = pSect->pSection; pCurrentSection = pSect->pSection;
sym_SetCurrentSymbolScope(pSect->pScope); sym_SetCurrentSymbolScope(pSect->pScope);
curOffset = pSect->offset; curOffset = pSect->offset;

View File

@@ -12,6 +12,7 @@
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <inttypes.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@@ -35,112 +36,111 @@
HashMap symbols; HashMap symbols;
static struct sSymbol *pScope; /* Current section symbol scope */ static struct Symbol *symbolScope; /* Current section symbol scope */
struct sSymbol *pPCSymbol; static struct Symbol *PCSymbol;
static struct sSymbol *p_NARGSymbol; static char savedTIME[256];
static struct sSymbol *p__LINE__Symbol; static char savedDATE[256];
static char SavedTIME[256]; static char savedTIMESTAMP_ISO8601_LOCAL[256];
static char SavedDATE[256]; static char savedTIMESTAMP_ISO8601_UTC[256];
static char SavedTIMESTAMP_ISO8601_LOCAL[256]; static char savedDAY[3];
static char SavedTIMESTAMP_ISO8601_UTC[256]; static char savedMONTH[3];
static char SavedDAY[3]; static char savedYEAR[20];
static char SavedMONTH[3]; static char savedHOUR[3];
static char SavedYEAR[20]; static char savedMINUTE[3];
static char SavedHOUR[3]; static char savedSECOND[3];
static char SavedMINUTE[3];
static char SavedSECOND[3];
static bool exportall; static bool exportall;
bool sym_IsPC(struct Symbol const *sym)
{
return sym == PCSymbol;
}
struct ForEachArgs { struct ForEachArgs {
void (*func)(struct sSymbol *, void *); void (*func)(struct Symbol *symbol, void *arg);
void *arg; void *arg;
}; };
static void forEachWrapper(void *_symbol, void *_argWrapper) static void forEachWrapper(void *_symbol, void *_argWrapper)
{ {
struct ForEachArgs *argWrapper = _argWrapper; struct ForEachArgs *argWrapper = _argWrapper;
struct sSymbol *symbol = _symbol; struct Symbol *symbol = _symbol;
argWrapper->func(symbol, argWrapper->arg); argWrapper->func(symbol, argWrapper->arg);
} }
void sym_ForEach(void (*func)(struct sSymbol *, void *), void *arg) void sym_ForEach(void (*func)(struct Symbol *, void *), void *arg)
{ {
struct ForEachArgs argWrapper = { .func = func, .arg = arg }; struct ForEachArgs argWrapper = { .func = func, .arg = arg };
hash_ForEach(symbols, forEachWrapper, &argWrapper); hash_ForEach(symbols, forEachWrapper, &argWrapper);
} }
static int32_t Callback_NARG(struct sSymbol const *self) static int32_t Callback_NARG(void)
{ {
(void)self;
return macro_NbArgs(); return macro_NbArgs();
} }
static int32_t Callback__LINE__(struct sSymbol const *self) static int32_t Callback__LINE__(void)
{ {
(void)self;
return nLineNo; return nLineNo;
} }
static int32_t CallbackPC(struct sSymbol const *self) static int32_t CallbackPC(void)
{ {
return self->pSection ? self->pSection->nOrg + curOffset : 0; struct Section const *section = sect_GetSymbolSection();
return section ? section->nOrg + curOffset : 0;
} }
/* /*
* Get the nValue field of a symbol * Get the value field of a symbol
*/ */
int32_t sym_GetValue(struct sSymbol const *sym) int32_t sym_GetValue(struct Symbol const *sym)
{ {
if (sym->Callback) if (sym_IsNumeric(sym) && sym->callback)
return sym->Callback(sym); return sym->callback();
if (sym->type == SYM_LABEL) if (sym->type == SYM_LABEL)
return sym->nValue + sym->pSection->nOrg; /* TODO: do not use section's org directly */
return sym->value + sym_GetSection(sym)->nOrg;
return sym->nValue; return sym->value;
} }
/* /*
* Update a symbol's definition filename and line * Update a symbol's definition filename and line
*/ */
static void updateSymbolFilename(struct sSymbol *nsym) static void updateSymbolFilename(struct Symbol *sym)
{ {
if (snprintf(nsym->tzFileName, _MAX_PATH + 1, "%s", if (snprintf(sym->fileName, _MAX_PATH + 1, "%s",
tzCurrentFileName) > _MAX_PATH) { tzCurrentFileName) > _MAX_PATH)
fatalerror("%s: File name is too long: '%s'", __func__, fatalerror("%s: File name is too long: '%s'", __func__,
tzCurrentFileName); tzCurrentFileName);
} sym->fileLine = fstk_GetLine();
nsym->nFileLine = fstk_GetLine();
} }
/* /*
* Create a new symbol by name * Create a new symbol by name
*/ */
static struct sSymbol *createsymbol(char const *s) static struct Symbol *createsymbol(char const *s)
{ {
struct sSymbol *symbol = malloc(sizeof(*symbol)); struct Symbol *symbol = malloc(sizeof(*symbol));
if (!symbol) if (!symbol)
fatalerror("Failed to create symbol: %s", strerror(errno)); fatalerror("Failed to create symbol: %s", strerror(errno));
if (snprintf(symbol->tzName, MAXSYMLEN + 1, "%s", s) > MAXSYMLEN) if (snprintf(symbol->name, MAXSYMLEN + 1, "%s", s) > MAXSYMLEN)
warning(WARNING_LONG_STR, "Symbol name is too long: '%s'", s); warning(WARNING_LONG_STR, "Symbol name is too long: '%s'", s);
hash_AddElement(symbols, symbol->tzName, symbol);
symbol->isExported = false; symbol->isExported = false;
symbol->isBuiltin = false; symbol->isBuiltin = false;
symbol->pScope = NULL; symbol->scope = NULL;
symbol->pSection = NULL; symbol->section = NULL;
symbol->nValue = 0; /* TODO: is this necessary? */ updateSymbolFilename(symbol);
symbol->pMacro = NULL;
symbol->Callback = NULL;
symbol->ID = -1; symbol->ID = -1;
symbol->next = NULL; symbol->next = NULL;
updateSymbolFilename(symbol);
hash_AddElement(symbols, symbol->name, symbol);
return symbol; return symbol;
} }
@@ -149,20 +149,20 @@ static struct sSymbol *createsymbol(char const *s)
* the name with the parent symbol's name. * the name with the parent symbol's name.
*/ */
static void fullSymbolName(char *output, size_t outputSize, static void fullSymbolName(char *output, size_t outputSize,
char const *localName, const struct sSymbol *scope) char const *localName, const struct Symbol *scope)
{ {
const struct sSymbol *parent = scope->pScope ? scope->pScope : scope; const struct Symbol *parent = scope->scope ? scope->scope : scope;
int n = snprintf(output, outputSize, "%s%s", parent->tzName, localName); int n = snprintf(output, outputSize, "%s%s", parent->name, localName);
if (n >= (int)outputSize) if (n >= (int)outputSize)
fatalerror("Symbol name is too long: '%s%s'", fatalerror("Symbol name is too long: '%s%s'", parent->name,
parent->tzName, localName); localName);
} }
/* /*
* Find a symbol by name and scope * Find a symbol by name and scope
*/ */
static struct sSymbol *findsymbol(char const *s, struct sSymbol const *scope) static struct Symbol *findsymbol(char const *s, struct Symbol const *scope)
{ {
char fullname[MAXSYMLEN + 1]; char fullname[MAXSYMLEN + 1];
@@ -183,19 +183,12 @@ static struct sSymbol *findsymbol(char const *s, struct sSymbol const *scope)
/* /*
* Find a symbol by name, with automatically determined scope * Find a symbol by name, with automatically determined scope
*/ */
struct sSymbol *sym_FindSymbol(char const *tzName) struct Symbol *sym_FindSymbol(char const *symName)
{ {
struct sSymbol *pscope; return findsymbol(symName, symName[0] == '.' ? symbolScope : NULL);
if (*tzName == '.')
pscope = pScope;
else
pscope = NULL;
return findsymbol(tzName, pscope);
} }
static inline bool isReferenced(struct sSymbol const *sym) static inline bool isReferenced(struct Symbol const *sym)
{ {
return sym->ID != -1; return sym->ID != -1;
} }
@@ -203,48 +196,54 @@ static inline bool isReferenced(struct sSymbol const *sym)
/* /*
* Purge a symbol * Purge a symbol
*/ */
void sym_Purge(char const *tzName) void sym_Purge(char const *symName)
{ {
struct sSymbol *scope = tzName[0] == '.' ? pScope : NULL; struct Symbol *scope = symName[0] == '.' ? symbolScope : NULL;
struct sSymbol *symbol = findsymbol(tzName, scope); struct Symbol *symbol = findsymbol(symName, scope);
if (!symbol) { if (!symbol) {
yyerror("'%s' not defined", tzName); yyerror("'%s' not defined", symName);
} else if (symbol->isBuiltin) { } else if (symbol->isBuiltin) {
yyerror("Built-in symbol '%s' cannot be purged", tzName); yyerror("Built-in symbol '%s' cannot be purged", symName);
} else if (isReferenced(symbol)) { } else if (isReferenced(symbol)) {
yyerror("Symbol \"%s\" is referenced and thus cannot be purged", yyerror("Symbol \"%s\" is referenced and thus cannot be purged",
tzName); symName);
} else { } else {
hash_RemoveElement(symbols, tzName); hash_RemoveElement(symbols, symbol->name);
free(symbol->pMacro); if (symbol->type == SYM_MACRO)
free(symbol->macro);
free(symbol); free(symbol);
} }
} }
uint32_t sym_GetPCValue(void)
{
struct Section const *sect = sect_GetSymbolSection();
if (!sect)
yyerror("PC has no value outside a section");
else if (sect->nOrg == -1)
yyerror("Expected constant PC but section is not fixed");
else
return CallbackPC();
return 0;
}
/* /*
* Return a constant symbols value * Return a constant symbols value
*/ */
uint32_t sym_GetConstantValue(char const *s) uint32_t sym_GetConstantValue(char const *s)
{ {
struct sSymbol const *psym = sym_FindSymbol(s); struct Symbol const *sym = sym_FindSymbol(s);
if (psym == pPCSymbol) { if (sym == NULL)
if (!pCurrentSection)
yyerror("PC has no value outside a section");
else if (pCurrentSection->nOrg == -1)
yyerror("Expected constant PC but section is not fixed");
else
return sym_GetValue(psym);
} else if (psym != NULL) {
if (sym_IsConstant(psym))
return sym_GetValue(psym);
yyerror("\"%s\" does not have a constant value", s);
} else {
yyerror("'%s' not defined", s); yyerror("'%s' not defined", s);
} else if (sym == PCSymbol)
return sym_GetPCValue();
else if (!sym_IsConstant(sym))
yyerror("\"%s\" does not have a constant value", s);
else
return sym_GetValue(sym);
return 0; return 0;
} }
@@ -254,38 +253,26 @@ uint32_t sym_GetConstantValue(char const *s)
*/ */
uint32_t sym_GetDefinedValue(char const *s) uint32_t sym_GetDefinedValue(char const *s)
{ {
struct sSymbol const *psym = sym_FindSymbol(s); struct Symbol const *sym = sym_FindSymbol(s);
if (psym != NULL) {
if (sym_IsDefined(psym)) {
if (!sym_IsNumeric(psym))
yyerror("'%s' is a macro or string symbol", s);
return sym_GetValue(psym);
}
}
if (sym == NULL || !sym_IsDefined(sym))
yyerror("'%s' not defined", s); yyerror("'%s' not defined", s);
else if (!sym_IsNumeric(sym))
yyerror("'%s' is a macro or string symbol", s);
else
return sym_GetValue(sym);
return 0; return 0;
} }
struct sSymbol *sym_GetCurrentSymbolScope(void) struct Symbol *sym_GetCurrentSymbolScope(void)
{ {
return pScope; return symbolScope;
} }
void sym_SetCurrentSymbolScope(struct sSymbol *pNewScope) void sym_SetCurrentSymbolScope(struct Symbol *newScope)
{ {
pScope = pNewScope; symbolScope = newScope;
}
/*
* Find a macro by name
*/
struct sSymbol *sym_FindMacro(char const *s)
{
return findsymbol(s, NULL);
} }
/* /*
@@ -293,38 +280,31 @@ struct sSymbol *sym_FindMacro(char const *s)
* hasn't already been defined or referenced in a context that would * hasn't already been defined or referenced in a context that would
* require that it be relocatable * require that it be relocatable
*/ */
static struct sSymbol *createNonrelocSymbol(char const *tzSym) static struct Symbol *createNonrelocSymbol(char const *symbolName)
{ {
struct sSymbol *nsym = findsymbol(tzSym, NULL); struct Symbol *symbol = findsymbol(symbolName, NULL);
if (nsym != NULL) { if (!symbol)
if (sym_IsDefined(nsym)) { symbol = createsymbol(symbolName);
yyerror("'%s' already defined at %s(%u)", else if (sym_IsDefined(symbol))
tzSym, nsym->tzFileName, nsym->nFileLine); yyerror("'%s' already defined at %s(%" PRIu32 ")", symbolName,
} else { symbol->fileName, symbol->fileLine);
yyerror("'%s' referenced as label at %s(%u)",
tzSym, nsym->tzFileName, nsym->nFileLine);
}
} else {
nsym = createsymbol(tzSym);
}
return nsym; return symbol;
} }
/* /*
* Add an equated symbol * Add an equated symbol
*/ */
struct sSymbol *sym_AddEqu(char const *tzSym, int32_t value) struct Symbol *sym_AddEqu(char const *symName, int32_t value)
{ {
struct sSymbol *nsym = createNonrelocSymbol(tzSym); struct Symbol *sym = createNonrelocSymbol(symName);
nsym->nValue = value; sym->type = SYM_EQU;
nsym->type = SYM_EQU; sym->callback = NULL;
nsym->pScope = NULL; sym->value = value;
updateSymbolFilename(nsym);
return nsym; return sym;
} }
/* /*
@@ -339,191 +319,180 @@ struct sSymbol *sym_AddEqu(char const *tzSym, int32_t value)
* of the string are enough: sym_AddString("M_PI", "3.1415"). This is the same * of the string are enough: sym_AddString("M_PI", "3.1415"). This is the same
* as ``` M_PI EQUS "3.1415" ``` * as ``` M_PI EQUS "3.1415" ```
*/ */
struct sSymbol *sym_AddString(char const *tzSym, char const *tzValue) struct Symbol *sym_AddString(char const *symName, char const *value)
{ {
struct sSymbol *nsym = createNonrelocSymbol(tzSym); struct Symbol *sym = createNonrelocSymbol(symName);
size_t len = strlen(value);
char *string = malloc(len + 1);
nsym->pMacro = malloc(strlen(tzValue) + 1); if (string == NULL)
if (nsym->pMacro != NULL)
strcpy(nsym->pMacro, tzValue);
else
fatalerror("No memory for string equate"); fatalerror("No memory for string equate");
strcpy(string, value);
nsym->type = SYM_EQUS; sym->type = SYM_EQUS;
nsym->ulMacroSize = strlen(tzValue); /* TODO: use other fields */
nsym->pScope = NULL; sym->macroSize = len;
sym->macro = string;
return nsym; return sym;
} }
/* /*
* Alter a SET symbols value * Alter a SET symbols value
*/ */
struct sSymbol *sym_AddSet(char const *tzSym, int32_t value) struct Symbol *sym_AddSet(char const *symName, int32_t value)
{ {
struct sSymbol *nsym = findsymbol(tzSym, NULL); struct Symbol *sym = findsymbol(symName, NULL);
if (nsym != NULL) { if (sym == NULL)
if (sym_IsDefined(nsym)) { sym = createsymbol(symName);
if (nsym->type == SYM_LABEL) else if (sym_IsDefined(sym) && sym->type != SYM_SET)
yyerror("'%s' already defined as non-constant at %s(%u)", yyerror("'%s' already defined as %s at %s(%" PRIu32 ")",
tzSym, symName, sym->type == SYM_LABEL ? "label" : "constant",
nsym->tzFileName, sym->fileName, sym->fileLine);
nsym->nFileLine); else
else if (nsym->type != SYM_SET) /* TODO: can the scope be incorrect when talking over refs? */
yyerror("'%s' already defined as constant at %s(%u)", updateSymbolFilename(sym);
tzSym,
nsym->tzFileName,
nsym->nFileLine);
} else if (nsym->type == SYM_REF) {
yyerror("'%s' already referenced at %s(%u)",
tzSym,
nsym->tzFileName,
nsym->nFileLine);
}
} else {
nsym = createsymbol(tzSym);
}
nsym->nValue = value; sym->type = SYM_SET;
nsym->type = SYM_SET; sym->callback = NULL;
nsym->pScope = NULL; sym->value = value;
updateSymbolFilename(nsym);
return nsym; return sym;
} }
/* /*
* Add a local (.name) relocatable symbol * Add a local (.name) relocatable symbol
*/ */
struct sSymbol *sym_AddLocalReloc(char const *tzSym) struct Symbol *sym_AddLocalReloc(char const *symName)
{ {
if (pScope) { if (!symbolScope) {
char fullname[MAXSYMLEN + 1]; yyerror("Local label '%s' in main scope", symName);
fullSymbolName(fullname, sizeof(fullname), tzSym, pScope);
return sym_AddReloc(fullname);
} else {
yyerror("Local label '%s' in main scope", tzSym);
return NULL; return NULL;
} }
char fullname[MAXSYMLEN + 1];
fullSymbolName(fullname, sizeof(fullname), symName, symbolScope);
return sym_AddReloc(fullname);
} }
/* /*
* Add a relocatable symbol * Add a relocatable symbol
*/ */
struct sSymbol *sym_AddReloc(char const *tzSym) struct Symbol *sym_AddReloc(char const *symName)
{ {
struct sSymbol *scope = NULL; struct Symbol const *scope = NULL;
struct sSymbol *nsym; char *localPtr = strchr(symName, '.');
char *localPtr = strchr(tzSym, '.');
if (localPtr != NULL) { if (localPtr != NULL) {
if (!pScope) { if (!symbolScope) {
yyerror("Local label in main scope"); yyerror("Local label in main scope");
return NULL; return NULL;
} }
struct sSymbol *parent = pScope->pScope ? scope = symbolScope->scope ? symbolScope->scope : symbolScope;
pScope->pScope : pScope; uint32_t parentLen = localPtr - symName;
uint32_t parentLen = localPtr - tzSym;
if (strchr(localPtr + 1, '.') != NULL) if (strchr(localPtr + 1, '.') != NULL)
fatalerror("'%s' is a nonsensical reference to a nested local symbol", fatalerror("'%s' is a nonsensical reference to a nested local symbol",
tzSym); symName);
else if (strlen(parent->tzName) != parentLen else if (strlen(scope->name) != parentLen
|| strncmp(tzSym, parent->tzName, parentLen) != 0) || strncmp(symName, scope->name, parentLen) != 0)
yyerror("Not currently in the scope of '%.*s'", yyerror("Not currently in the scope of '%.*s'",
parentLen, tzSym); parentLen, symName);
scope = parent;
} }
nsym = findsymbol(tzSym, scope); struct Symbol *sym = findsymbol(symName, scope);
if (!nsym) if (!sym)
nsym = createsymbol(tzSym); sym = createsymbol(symName);
else if (sym_IsDefined(nsym)) else if (sym_IsDefined(sym))
yyerror("'%s' already defined in %s(%d)", tzSym, yyerror("'%s' already defined in %s(%" PRIu32 ")", symName,
nsym->tzFileName, nsym->nFileLine); sym->fileName, sym->fileLine);
/* If the symbol already exists as a ref, just "take over" it */ /* If the symbol already exists as a ref, just "take over" it */
nsym->nValue = curOffset; sym->type = SYM_LABEL;
nsym->type = SYM_LABEL; sym->callback = NULL;
sym->value = curOffset;
if (exportall) if (exportall)
nsym->isExported = true; sym->isExported = true;
nsym->pScope = scope; sym->scope = scope;
nsym->pSection = sect_GetSymbolSection(); sym->section = sect_GetSymbolSection();
/* Labels need to be assigned a section, except PC */ /* Labels need to be assigned a section, except PC */
if (!pCurrentSection && strcmp(tzSym, "@")) if (!sym->section && strcmp(symName, "@"))
yyerror("Label \"%s\" created outside of a SECTION", yyerror("Label \"%s\" created outside of a SECTION",
tzSym); symName);
updateSymbolFilename(nsym); updateSymbolFilename(sym);
pScope = findsymbol(tzSym, scope); /* Set the symbol as the new scope */
return pScope; /* TODO: don't do this for local labels */
symbolScope = findsymbol(symName, scope);
return symbolScope;
} }
/* /*
* Export a symbol * Export a symbol
*/ */
void sym_Export(char const *tzSym) void sym_Export(char const *symName)
{ {
struct sSymbol *nsym = sym_FindSymbol(tzSym); struct Symbol *sym = sym_FindSymbol(symName);
/* If the symbol doesn't exist, create a ref that can be purged */ /* If the symbol doesn't exist, create a ref that can be purged */
if (!nsym) if (!sym)
nsym = sym_Ref(tzSym); sym = sym_Ref(symName);
nsym->isExported = true; sym->isExported = true;
} }
/* /*
* Add a macro definition * Add a macro definition
*/ */
struct sSymbol *sym_AddMacro(char const *tzSym, int32_t nDefLineNo) struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo)
{ {
struct sSymbol *nsym = createNonrelocSymbol(tzSym); struct Symbol *sym = createNonrelocSymbol(symName);
nsym->type = SYM_MACRO; sym->type = SYM_MACRO;
nsym->pScope = NULL; sym->macroSize = ulNewMacroSize;
nsym->ulMacroSize = ulNewMacroSize; sym->macro = tzNewMacro;
nsym->pMacro = tzNewMacro; updateSymbolFilename(sym);
updateSymbolFilename(nsym);
/* /*
* The symbol is created at the line after the `endm`, * The symbol is created at the line after the `endm`,
* override this with the actual definition line * override this with the actual definition line
*/ */
nsym->nFileLine = nDefLineNo; sym->fileLine = defLineNo;
return nsym; return sym;
} }
/* /*
* Flag that a symbol is referenced in an RPN expression * Flag that a symbol is referenced in an RPN expression
* and create it if it doesn't exist yet * and create it if it doesn't exist yet
*/ */
struct sSymbol *sym_Ref(char const *tzSym) struct Symbol *sym_Ref(char const *symName)
{ {
struct sSymbol *nsym = sym_FindSymbol(tzSym); struct Symbol *nsym = sym_FindSymbol(symName);
if (nsym == NULL) { if (nsym == NULL) {
char fullname[MAXSYMLEN + 1]; char fullname[MAXSYMLEN + 1];
struct Symbol const *scope = NULL;
if (*tzSym == '.') { if (symName[0] == '.') {
if (!pScope) if (!symbolScope)
fatalerror("Local label reference '%s' in main scope", fatalerror("Local label reference '%s' in main scope",
tzSym); symName);
fullSymbolName(fullname, sizeof(fullname), tzSym, scope = symbolScope->scope ? symbolScope->scope
pScope); : symbolScope;
tzSym = fullname; fullSymbolName(fullname, sizeof(fullname), symName,
symbolScope);
symName = fullname;
} }
nsym = createsymbol(tzSym); nsym = createsymbol(symName);
nsym->type = SYM_REF; nsym->type = SYM_REF;
nsym->scope = scope;
} }
return nsym; return nsym;
@@ -553,22 +522,21 @@ static inline char const *removeLeadingZeros(char const *ptr)
*/ */
void sym_Init(void) void sym_Init(void)
{ {
pPCSymbol = sym_AddReloc("@"); struct Symbol *_NARGSymbol = sym_AddEqu("_NARG", 0);
pPCSymbol->Callback = CallbackPC; struct Symbol *__LINE__Symbol = sym_AddEqu("__LINE__", 0);
pPCSymbol->isBuiltin = true;
p_NARGSymbol = sym_AddEqu("_NARG", 0);
p_NARGSymbol->Callback = Callback_NARG;
p_NARGSymbol->isBuiltin = true;
p__LINE__Symbol = sym_AddEqu("__LINE__", 0);
p__LINE__Symbol->Callback = Callback__LINE__;
p__LINE__Symbol->isBuiltin = true;
struct sSymbol *_RSSymbol = sym_AddSet("_RS", 0);
_RSSymbol->isBuiltin = true; PCSymbol = sym_AddReloc("@"),
PCSymbol->isBuiltin = true;
PCSymbol->callback = CallbackPC;
_NARGSymbol->isBuiltin = true;
_NARGSymbol->callback = Callback_NARG;
__LINE__Symbol->isBuiltin = true;
__LINE__Symbol->callback = Callback__LINE__;
sym_AddSet("_RS", 0)->isBuiltin = true;
sym_AddEqu("__RGBDS_MAJOR__", PACKAGE_VERSION_MAJOR); sym_AddEqu("__RGBDS_MAJOR__", PACKAGE_VERSION_MAJOR)->isBuiltin = true;
sym_AddEqu("__RGBDS_MINOR__", PACKAGE_VERSION_MINOR); sym_AddEqu("__RGBDS_MINOR__", PACKAGE_VERSION_MINOR)->isBuiltin = true;
sym_AddEqu("__RGBDS_PATCH__", PACKAGE_VERSION_PATCH); sym_AddEqu("__RGBDS_PATCH__", PACKAGE_VERSION_PATCH)->isBuiltin = true;
time_t now = time(NULL); time_t now = time(NULL);
@@ -581,43 +549,38 @@ void sym_Init(void)
const struct tm *time_utc = gmtime(&now); const struct tm *time_utc = gmtime(&now);
const struct tm *time_local = localtime(&now); const struct tm *time_local = localtime(&now);
strftime(SavedTIME, sizeof(SavedTIME), "\"%H:%M:%S\"", strftime(savedTIME, sizeof(savedTIME), "\"%H:%M:%S\"", time_local);
time_local); strftime(savedDATE, sizeof(savedDATE), "\"%d %B %Y\"", time_local);
strftime(SavedDATE, sizeof(SavedDATE), "\"%d %B %Y\"", strftime(savedTIMESTAMP_ISO8601_LOCAL,
time_local); sizeof(savedTIMESTAMP_ISO8601_LOCAL), "\"%Y-%m-%dT%H:%M:%S%z\"",
strftime(SavedTIMESTAMP_ISO8601_LOCAL,
sizeof(SavedTIMESTAMP_ISO8601_LOCAL), "\"%Y-%m-%dT%H-%M-%S%z\"",
time_local); time_local);
strftime(SavedTIMESTAMP_ISO8601_UTC, strftime(savedTIMESTAMP_ISO8601_UTC,
sizeof(SavedTIMESTAMP_ISO8601_UTC), "\"%Y-%m-%dT%H-%M-%SZ\"", sizeof(savedTIMESTAMP_ISO8601_UTC), "\"%Y-%m-%dT%H:%M:%SZ\"",
time_utc); time_utc);
strftime(SavedYEAR, sizeof(SavedYEAR), "%Y", time_utc); strftime(savedYEAR, sizeof(savedYEAR), "%Y", time_utc);
strftime(SavedMONTH, sizeof(SavedMONTH), "%m", time_utc); strftime(savedMONTH, sizeof(savedMONTH), "%m", time_utc);
strftime(SavedDAY, sizeof(SavedDAY), "%d", time_utc); strftime(savedDAY, sizeof(savedDAY), "%d", time_utc);
strftime(SavedHOUR, sizeof(SavedHOUR), "%H", time_utc); strftime(savedHOUR, sizeof(savedHOUR), "%H", time_utc);
strftime(SavedMINUTE, sizeof(SavedMINUTE), "%M", time_utc); strftime(savedMINUTE, sizeof(savedMINUTE), "%M", time_utc);
strftime(SavedSECOND, sizeof(SavedSECOND), "%S", time_utc); strftime(savedSECOND, sizeof(savedSECOND), "%S", time_utc);
#define addString(name, val) do { \ #define addString(name, val) sym_AddString(name, val)->isBuiltin = true
struct sSymbol *symbol = sym_AddString(name, val); \ addString("__TIME__", savedTIME);
symbol->isBuiltin = true; \ addString("__DATE__", savedDATE);
} while (0) addString("__ISO_8601_LOCAL__", savedTIMESTAMP_ISO8601_LOCAL);
addString("__TIME__", SavedTIME); addString("__ISO_8601_UTC__", savedTIMESTAMP_ISO8601_UTC);
addString("__DATE__", SavedDATE);
addString("__ISO_8601_LOCAL__", SavedTIMESTAMP_ISO8601_LOCAL);
addString("__ISO_8601_UTC__", SavedTIMESTAMP_ISO8601_UTC);
/* This cannot start with zeros */ /* This cannot start with zeros */
addString("__UTC_YEAR__", SavedYEAR); addString("__UTC_YEAR__", savedYEAR);
addString("__UTC_MONTH__", removeLeadingZeros(SavedMONTH)); addString("__UTC_MONTH__", removeLeadingZeros(savedMONTH));
addString("__UTC_DAY__", removeLeadingZeros(SavedDAY)); addString("__UTC_DAY__", removeLeadingZeros(savedDAY));
addString("__UTC_HOUR__", removeLeadingZeros(SavedHOUR)); addString("__UTC_HOUR__", removeLeadingZeros(savedHOUR));
addString("__UTC_MINUTE__", removeLeadingZeros(SavedMINUTE)); addString("__UTC_MINUTE__", removeLeadingZeros(savedMINUTE));
addString("__UTC_SECOND__", removeLeadingZeros(SavedSECOND)); addString("__UTC_SECOND__", removeLeadingZeros(savedSECOND));
#undef addString #undef addString
pScope = NULL; symbolScope = NULL;
math_DefinePI(); math_DefinePI();
} }

View File

@@ -31,10 +31,11 @@ static enum WarningState const defaultWarnings[NB_WARNINGS] = {
WARNING_ENABLED, /* Assertions */ WARNING_ENABLED, /* Assertions */
WARNING_DISABLED, /* Invalid args to builtins */ WARNING_DISABLED, /* Invalid args to builtins */
WARNING_DISABLED, /* Division undefined behavior */ WARNING_DISABLED, /* Division undefined behavior */
WARNING_DISABLED, /* `db`, `dw`, or `dl` with no directive in ROM */
WARNING_DISABLED, /* Empty entry in `db`, `dw` or `dl` */ WARNING_DISABLED, /* Empty entry in `db`, `dw` or `dl` */
WARNING_DISABLED, /* Constants too large */ WARNING_DISABLED, /* Constants too large */
WARNING_DISABLED, /* String too long for internal buffers */ WARNING_DISABLED, /* String too long for internal buffers */
WARNING_DISABLED, /* Obsolete things */ WARNING_ENABLED, /* Obsolete things */
WARNING_DISABLED, /* Shifting undefined behavior */ WARNING_DISABLED, /* Shifting undefined behavior */
WARNING_DISABLED, /* Strange shift amount */ WARNING_DISABLED, /* Strange shift amount */
WARNING_ENABLED, /* Implicit truncation loses some bits */ WARNING_ENABLED, /* Implicit truncation loses some bits */
@@ -48,7 +49,7 @@ static bool warningsAreErrors; /* Set if `-Werror` was specified */
static enum WarningState warningState(enum WarningID id) static enum WarningState warningState(enum WarningID id)
{ {
/* Check if warnings are globally disabled */ /* Check if warnings are globally disabled */
if (!CurrentOptions.warnings) if (!warnings)
return WARNING_DISABLED; return WARNING_DISABLED;
/* Get the actual state */ /* Get the actual state */
@@ -68,6 +69,7 @@ static char const *warningFlags[NB_WARNINGS_ALL] = {
"assert", "assert",
"builtin-args", "builtin-args",
"div", "div",
"empty-data-directive",
"empty-entry", "empty-entry",
"large-constant", "large-constant",
"long-string", "long-string",
@@ -90,6 +92,7 @@ enum MetaWarningCommand {
/* Warnings that probably indicate an error */ /* Warnings that probably indicate an error */
static uint8_t const _wallCommands[] = { static uint8_t const _wallCommands[] = {
WARNING_BUILTIN_ARG, WARNING_BUILTIN_ARG,
WARNING_EMPTY_DATA_DIRECTIVE,
WARNING_LARGE_CONSTANT, WARNING_LARGE_CONSTANT,
WARNING_LONG_STR, WARNING_LONG_STR,
META_WARNING_DONE META_WARNING_DONE
@@ -98,7 +101,6 @@ static uint8_t const _wallCommands[] = {
/* Warnings that are less likely to indicate an error */ /* Warnings that are less likely to indicate an error */
static uint8_t const _wextraCommands[] = { static uint8_t const _wextraCommands[] = {
WARNING_EMPTY_ENTRY, WARNING_EMPTY_ENTRY,
WARNING_OBSOLETE,
META_WARNING_DONE META_WARNING_DONE
}; };
@@ -106,6 +108,7 @@ static uint8_t const _wextraCommands[] = {
static uint8_t const _weverythingCommands[] = { static uint8_t const _weverythingCommands[] = {
WARNING_BUILTIN_ARG, WARNING_BUILTIN_ARG,
WARNING_DIV, WARNING_DIV,
WARNING_EMPTY_DATA_DIRECTIVE,
WARNING_EMPTY_ENTRY, WARNING_EMPTY_ENTRY,
WARNING_LARGE_CONSTANT, WARNING_LARGE_CONSTANT,
WARNING_LONG_STR, WARNING_LONG_STR,

138
src/extern/getopt.c vendored
View File

@@ -1,5 +1,5 @@
/* /*
* Copyright © 2005-2019 Rich Felker, et al. * Copyright © 2005-2020 Rich Felker, et al.
* *
* Permission is hereby granted, free of charge, to any person obtaining * Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the * a copy of this software and associated documentation files (the
@@ -26,40 +26,128 @@
#include <stddef.h> #include <stddef.h>
#include <stdlib.h> #include <stdlib.h>
#include <limits.h> #include <limits.h>
#include <unistd.h> #ifndef _MSC_VER
# include <unistd.h>
#endif
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <wchar.h>
#include "extern/getopt.h" #include "extern/getopt.h"
int __optpos, __optreset; #ifdef _MSC_VER
char *optarg;
int optind=1, opterr=1, optopt;
#endif
int optreset=0;
static int optpos;
void musl__getopt_msg(const char *a, const char *b, const char *c, size_t l) static void musl_getopt_msg(const char *a, const char *b, const char *c, size_t l)
{ {
FILE *f = stderr; FILE *f = stderr;
(void)(fputs(a, f)>=0
&& fwrite(b, strlen(b), 1, f) if (fputs(a, f) >= 0 &&
&& fwrite(c, 1, l, f)==l fwrite(b, strlen(b), 1, f) &&
&& putc('\n', f)); fwrite(c, 1, l, f) == l)
putc('\n', f);
} }
#ifdef _MSC_VER
static int getopt(int argc, char *argv[], const char *optstring)
{
int i;
wchar_t c, d;
int k, l;
char *optchar;
if (!optind || optreset) {
optreset = 0;
optpos = 0;
optind = 1;
}
if (optind >= argc || !argv[optind])
return -1;
if (argv[optind][0] != '-') {
if (optstring[0] == '-') {
optarg = argv[optind++];
return 1;
}
return -1;
}
if (!argv[optind][1])
return -1;
if (argv[optind][1] == '-' && !argv[optind][2])
return optind++, -1;
if (!optpos) optpos++;
if ((k = mbtowc(&c, argv[optind]+optpos, MB_LEN_MAX)) < 0) {
k = 1;
c = 0xfffd; /* replacement char */
}
optchar = argv[optind]+optpos;
optpos += k;
if (!argv[optind][optpos]) {
optind++;
optpos = 0;
}
if (optstring[0] == '-' || optstring[0] == '+')
optstring++;
i = 0;
d = 0;
do {
l = mbtowc(&d, optstring+i, MB_LEN_MAX);
if (l>0) i+=l; else i++;
} while (l && d != c);
if (d != c || c == ':') {
optopt = c;
if (optstring[0] != ':' && opterr)
musl_getopt_msg(argv[0], ": unrecognized option: ", optchar, k);
return '?';
}
if (optstring[i] == ':') {
optarg = 0;
if (optstring[i+1] != ':' || optpos) {
optarg = argv[optind++] + optpos;
optpos = 0;
}
if (optind > argc) {
optopt = c;
if (optstring[0] == ':') return ':';
if (opterr) musl_getopt_msg(argv[0],
": option requires an argument: ",
optchar, k);
return '?';
}
}
return c;
}
#endif /* _MSC_VER */
static void permute(char **argv, int dest, int src) static void permute(char **argv, int dest, int src)
{ {
char **av = (char **)argv; char *tmp = argv[src];
char *tmp = av[src];
int i; int i;
for (i=src; i>dest; i--) for (i=src; i>dest; i--)
av[i] = av[i-1]; argv[i] = argv[i-1];
av[dest] = tmp; argv[dest] = tmp;
} }
static int musl__getopt_long_core(int argc, char **argv, const char *optstring, const struct option *longopts, int *idx, int longonly); static int musl_getopt_long_core(int argc, char **argv, const char *optstring, const struct option *longopts, int *idx, int longonly);
static int musl__getopt_long(int argc, char **argv, const char *optstring, const struct option *longopts, int *idx, int longonly) static int musl_getopt_long(int argc, char **argv, const char *optstring, const struct option *longopts, int *idx, int longonly)
{ {
int ret, skipped, resumed; int ret, skipped, resumed;
if (!optind || __optreset) { if (!optind || optreset) {
__optreset = 0; optreset = 0;
__optpos = 0; optpos = 0;
optind = 1; optind = 1;
} }
if (optind >= argc || !argv[optind]) return -1; if (optind >= argc || !argv[optind]) return -1;
@@ -73,7 +161,7 @@ static int musl__getopt_long(int argc, char **argv, const char *optstring, const
optind = i; optind = i;
} }
resumed = optind; resumed = optind;
ret = musl__getopt_long_core(argc, argv, optstring, longopts, idx, longonly); ret = musl_getopt_long_core(argc, argv, optstring, longopts, idx, longonly);
if (resumed > skipped) { if (resumed > skipped) {
int i, cnt = optind-resumed; int i, cnt = optind-resumed;
for (i=0; i<cnt; i++) for (i=0; i<cnt; i++)
@@ -83,7 +171,7 @@ static int musl__getopt_long(int argc, char **argv, const char *optstring, const
return ret; return ret;
} }
static int musl__getopt_long_core(int argc, char **argv, const char *optstring, const struct option *longopts, int *idx, int longonly) static int musl_getopt_long_core(int argc, char **argv, const char *optstring, const struct option *longopts, int *idx, int longonly)
{ {
optarg = 0; optarg = 0;
if (longopts && argv[optind][0] == '-' && if (longopts && argv[optind][0] == '-' &&
@@ -91,8 +179,8 @@ static int musl__getopt_long_core(int argc, char **argv, const char *optstring,
(argv[optind][1] == '-' && argv[optind][2]))) (argv[optind][1] == '-' && argv[optind][2])))
{ {
int colon = optstring[optstring[0]=='+'||optstring[0]=='-']==':'; int colon = optstring[optstring[0]=='+'||optstring[0]=='-']==':';
int i, cnt, match; int i, cnt, match = 0;
char *arg, *opt, *start = argv[optind]+1; char *arg = 0, *opt, *start = argv[optind]+1;
for (cnt=i=0; longopts[i].name; i++) { for (cnt=i=0; longopts[i].name; i++) {
const char *name = longopts[i].name; const char *name = longopts[i].name;
opt = start; opt = start;
@@ -128,7 +216,7 @@ static int musl__getopt_long_core(int argc, char **argv, const char *optstring,
optopt = longopts[i].val; optopt = longopts[i].val;
if (colon || !opterr) if (colon || !opterr)
return '?'; return '?';
musl__getopt_msg(argv[0], musl_getopt_msg(argv[0],
": option does not take an argument: ", ": option does not take an argument: ",
longopts[i].name, longopts[i].name,
strlen(longopts[i].name)); strlen(longopts[i].name));
@@ -140,7 +228,7 @@ static int musl__getopt_long_core(int argc, char **argv, const char *optstring,
optopt = longopts[i].val; optopt = longopts[i].val;
if (colon) return ':'; if (colon) return ':';
if (!opterr) return '?'; if (!opterr) return '?';
musl__getopt_msg(argv[0], musl_getopt_msg(argv[0],
": option requires an argument: ", ": option requires an argument: ",
longopts[i].name, longopts[i].name,
strlen(longopts[i].name)); strlen(longopts[i].name));
@@ -158,7 +246,7 @@ static int musl__getopt_long_core(int argc, char **argv, const char *optstring,
if (argv[optind][1] == '-') { if (argv[optind][1] == '-') {
optopt = 0; optopt = 0;
if (!colon && opterr) if (!colon && opterr)
musl__getopt_msg(argv[0], cnt ? musl_getopt_msg(argv[0], cnt ?
": option is ambiguous: " : ": option is ambiguous: " :
": unrecognized option: ", ": unrecognized option: ",
argv[optind]+2, argv[optind]+2,
@@ -172,5 +260,5 @@ static int musl__getopt_long_core(int argc, char **argv, const char *optstring,
int musl_getopt_long_only(int argc, char **argv, const char *optstring, const struct option *longopts, int *idx) int musl_getopt_long_only(int argc, char **argv, const char *optstring, const struct option *longopts, int *idx)
{ {
return musl__getopt_long(argc, argv, optstring, longopts, idx, 1); return musl_getopt_long(argc, argv, optstring, longopts, idx, 1);
} }

View File

@@ -98,9 +98,9 @@ int main(int argc, char *argv[])
bool resize = false; bool resize = false;
bool setversion = false; bool setversion = false;
char *title; /* game title in ASCII */ char *title = NULL; /* game title in ASCII */
char *id; /* game ID in ASCII */ char *id = NULL; /* game ID in ASCII */
char *newlicensee; /* new licensee ID, two ASCII characters */ char *newlicensee = NULL; /* new licensee ID, two ASCII characters */
int licensee = 0; /* old licensee ID */ int licensee = 0; /* old licensee ID */
int cartridge = 0; /* cartridge hardware ID */ int cartridge = 0; /* cartridge hardware ID */

View File

@@ -1142,10 +1142,10 @@ This is roughly equivalent to the following
.Em imaginary .Em imaginary
instructions: instructions:
.Bd -literal -offset indent .Bd -literal -offset indent
ld f, [sp] ; See below for individual flags
inc sp inc sp
ld a, [sp] ld a, [sp]
inc sp inc sp
ld f, [sp] ; See below for individual flags
.Ed .Ed
.Pp .Pp
Cycles: 3 Cycles: 3

View File

@@ -10,7 +10,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "gfx/main.h" #include "gfx/gb.h"
void transpose_tiles(struct GBImage *gb, int width) void transpose_tiles(struct GBImage *gb, int width)
{ {
@@ -19,6 +19,9 @@ void transpose_tiles(struct GBImage *gb, int width)
int newbyte; int newbyte;
newdata = calloc(gb->size, 1); newdata = calloc(gb->size, 1);
if (!newdata)
err(1, "%s: Failed to allocate memory for new data", __func__);
for (i = 0; i < gb->size; i++) { for (i = 0; i < gb->size; i++) {
newbyte = i / (8 * depth) * width * 8 * depth; newbyte = i / (8 * depth) * width * 8 * depth;
newbyte = newbyte % gb->size newbyte = newbyte % gb->size
@@ -62,7 +65,8 @@ void output_file(const struct Options *opts, const struct GBImage *gb)
f = fopen(opts->outfile, "wb"); f = fopen(opts->outfile, "wb");
if (!f) if (!f)
err(1, "Opening output file '%s' failed", opts->outfile); err(1, "%s: Opening output file '%s' failed", __func__,
opts->outfile);
fwrite(gb->data, 1, gb->size - gb->trim * 8 * depth, f); fwrite(gb->data, 1, gb->size - gb->trim * 8 * depth, f);
@@ -136,6 +140,9 @@ int get_mirrored_tile_index(uint8_t *tile, uint8_t **tiles, int num_tiles,
} }
tile_yflip = malloc(tile_size); tile_yflip = malloc(tile_size);
if (!tile_yflip)
err(1, "%s: Failed to allocate memory for Y flip of tile",
__func__);
yflip(tile, tile_yflip, tile_size); yflip(tile, tile_yflip, tile_size);
index = get_tile_index(tile_yflip, tiles, num_tiles, tile_size); index = get_tile_index(tile_yflip, tiles, num_tiles, tile_size);
if (index >= 0) { if (index >= 0) {
@@ -145,6 +152,9 @@ int get_mirrored_tile_index(uint8_t *tile, uint8_t **tiles, int num_tiles,
} }
tile_xflip = malloc(tile_size); tile_xflip = malloc(tile_size);
if (!tile_xflip)
err(1, "%s: Failed to allocate memory for X flip of tile",
__func__);
xflip(tile, tile_xflip, tile_size); xflip(tile, tile_xflip, tile_size);
index = get_tile_index(tile_xflip, tiles, num_tiles, tile_size); index = get_tile_index(tile_xflip, tiles, num_tiles, tile_size);
if (index >= 0) { if (index >= 0) {
@@ -178,7 +188,7 @@ void create_mapfiles(const struct Options *opts, struct GBImage *gb,
uint8_t *tile; uint8_t *tile;
uint8_t **tiles; uint8_t **tiles;
tile_size = sizeof(uint8_t) * depth * 8; tile_size = sizeof(*tile) * depth * 8;
gb_size = gb->size - (gb->trim * tile_size); gb_size = gb->size - (gb->trim * tile_size);
max_tiles = gb_size / tile_size; max_tiles = gb_size / tile_size;
@@ -186,16 +196,24 @@ void create_mapfiles(const struct Options *opts, struct GBImage *gb,
if (gb_size > max_tiles * tile_size) if (gb_size > max_tiles * tile_size)
max_tiles++; max_tiles++;
tiles = calloc(max_tiles, sizeof(uint8_t *)); tiles = calloc(max_tiles, sizeof(*tiles));
if (!tiles)
err(1, "%s: Failed to allocate memory for tiles", __func__);
num_tiles = 0; num_tiles = 0;
if (*opts->tilemapfile) { if (*opts->tilemapfile) {
tilemap->data = calloc(max_tiles, sizeof(uint8_t)); tilemap->data = calloc(max_tiles, sizeof(*tilemap->data));
if (!tilemap->data)
err(1, "%s: Failed to allocate memory for tilemap data",
__func__);
tilemap->size = 0; tilemap->size = 0;
} }
if (*opts->attrmapfile) { if (*opts->attrmapfile) {
attrmap->data = calloc(max_tiles, sizeof(uint8_t)); attrmap->data = calloc(max_tiles, sizeof(*attrmap->data));
if (!attrmap->data)
err(1, "%s: Failed to allocate memory for attrmap data",
__func__);
attrmap->size = 0; attrmap->size = 0;
} }
@@ -203,6 +221,9 @@ void create_mapfiles(const struct Options *opts, struct GBImage *gb,
while (gb_i < gb_size) { while (gb_i < gb_size) {
flags = 0; flags = 0;
tile = malloc(tile_size); tile = malloc(tile_size);
if (!tile)
err(1, "%s: Failed to allocate memory for tile",
__func__);
for (i = 0; i < tile_size; i++) { for (i = 0; i < tile_size; i++) {
tile[i] = gb->data[gb_i]; tile[i] = gb->data[gb_i];
gb_i++; gb_i++;
@@ -217,10 +238,13 @@ void create_mapfiles(const struct Options *opts, struct GBImage *gb,
index = get_tile_index(tile, tiles, num_tiles, index = get_tile_index(tile, tiles, num_tiles,
tile_size); tile_size);
} }
if (index < 0) { if (index < 0) {
index = num_tiles; index = num_tiles;
tiles[num_tiles] = tile; tiles[num_tiles] = tile;
num_tiles++; num_tiles++;
} else {
free(tile);
} }
} else { } else {
index = num_tiles; index = num_tiles;
@@ -240,6 +264,9 @@ void create_mapfiles(const struct Options *opts, struct GBImage *gb,
if (opts->unique) { if (opts->unique) {
free(gb->data); free(gb->data);
gb->data = malloc(tile_size * num_tiles); gb->data = malloc(tile_size * num_tiles);
if (!gb->data)
err(1, "%s: Failed to allocate memory for tile data",
__func__);
for (i = 0; i < num_tiles; i++) { for (i = 0; i < num_tiles; i++) {
tile = tiles[i]; tile = tiles[i];
for (j = 0; j < tile_size; j++) for (j = 0; j < tile_size; j++)
@@ -261,7 +288,8 @@ void output_tilemap_file(const struct Options *opts,
f = fopen(opts->tilemapfile, "wb"); f = fopen(opts->tilemapfile, "wb");
if (!f) if (!f)
err(1, "Opening tilemap file '%s' failed", opts->tilemapfile); err(1, "%s: Opening tilemap file '%s' failed", __func__,
opts->tilemapfile);
fwrite(tilemap->data, 1, tilemap->size, f); fwrite(tilemap->data, 1, tilemap->size, f);
fclose(f); fclose(f);
@@ -277,7 +305,8 @@ void output_attrmap_file(const struct Options *opts,
f = fopen(opts->attrmapfile, "wb"); f = fopen(opts->attrmapfile, "wb");
if (!f) if (!f)
err(1, "Opening attrmap file '%s' failed", opts->attrmapfile); err(1, "%s: Opening attrmap file '%s' failed", __func__,
opts->attrmapfile);
fwrite(attrmap->data, 1, attrmap->size, f); fwrite(attrmap->data, 1, attrmap->size, f);
fclose(f); fclose(f);
@@ -319,7 +348,8 @@ void output_palette_file(const struct Options *opts,
f = fopen(opts->palfile, "wb"); f = fopen(opts->palfile, "wb");
if (!f) if (!f)
err(1, "Opening palette file '%s' failed", opts->palfile); err(1, "%s: Opening palette file '%s' failed", __func__,
opts->palfile);
for (i = 0; i < raw_image->num_colors; i++) { for (i = 0; i < raw_image->num_colors; i++) {
int r = raw_image->palette[i].red; int r = raw_image->palette[i].red;

View File

@@ -15,6 +15,8 @@
#include "extern/getopt.h" #include "extern/getopt.h"
#include "version.h" #include "version.h"
int depth, colors;
/* Short options */ /* Short options */
static char const *optstring = "Aa:CDd:Ffhmo:Pp:Tt:uVvx:"; static char const *optstring = "Aa:CDd:Ffhmo:Pp:Tt:uVvx:";
@@ -208,7 +210,7 @@ int main(int argc, char *argv[])
if (opts.trim && if (opts.trim &&
opts.trim > (raw_image->width / 8) * (raw_image->height / 8) - 1) { opts.trim > (raw_image->width / 8) * (raw_image->height / 8) - 1) {
errx(1, "Trim (%i) for input raw_image file '%s' too large (max: %i)", errx(1, "Trim (%d) for input raw_image file '%s' too large (max: %u)",
opts.trim, opts.infile, opts.trim, opts.infile,
(raw_image->width / 8) * (raw_image->height / 8) - 1); (raw_image->width / 8) * (raw_image->height / 8) - 1);
} }

View File

@@ -11,7 +11,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "gfx/main.h" #include "gfx/makepng.h"
static void initialize_png(struct PNGImage *img, FILE * f); static void initialize_png(struct PNGImage *img, FILE * f);
static struct RawIndexedImage *indexed_png_to_raw(struct PNGImage *img); static struct RawIndexedImage *indexed_png_to_raw(struct PNGImage *img);
@@ -38,7 +38,7 @@ struct RawIndexedImage *input_png_file(const struct Options *opts,
if (img.depth != depth) { if (img.depth != depth) {
if (opts->verbose) { if (opts->verbose) {
warnx("Image bit depth is not %i (is %i).", warnx("Image bit depth is not %d (is %d).",
depth, img.depth); depth, img.depth);
} }
} }
@@ -82,6 +82,9 @@ void output_png_file(const struct Options *opts,
*/ */
if (opts->debug) { if (opts->debug) {
outfile = malloc(strlen(opts->infile) + 5); outfile = malloc(strlen(opts->infile) + 5);
if (!outfile)
err(1, "%s: Failed to allocate memory for outfile",
__func__);
strcpy(outfile, opts->infile); strcpy(outfile, opts->infile);
strcat(outfile, ".out"); strcat(outfile, ".out");
} else { } else {
@@ -110,7 +113,10 @@ void output_png_file(const struct Options *opts,
8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_palette = malloc(sizeof(png_color *) * raw_image->num_colors); png_palette = malloc(sizeof(*png_palette) * raw_image->num_colors);
if (!png_palette)
err(1, "%s: Failed to allocate memory for PNG palette",
__func__);
for (i = 0; i < raw_image->num_colors; i++) { for (i = 0; i < raw_image->num_colors; i++) {
png_palette[i].red = raw_image->palette[i].red; png_palette[i].red = raw_image->palette[i].red;
png_palette[i].green = raw_image->palette[i].green; png_palette[i].green = raw_image->palette[i].green;
@@ -207,9 +213,16 @@ static struct RawIndexedImage *indexed_png_to_raw(struct PNGImage *img)
if (png_get_tRNS(img->png, img->info, &trans_alpha, &num_trans, if (png_get_tRNS(img->png, img->info, &trans_alpha, &num_trans,
&trans_color)) { &trans_color)) {
original_palette = palette; original_palette = palette;
palette = malloc(sizeof(png_color) * colors_in_PLTE); palette = malloc(sizeof(*palette) * colors_in_PLTE);
if (!palette)
err(1, "%s: Failed to allocate memory for palette",
__func__);
colors_in_new_palette = 0; colors_in_new_palette = 0;
old_to_new_palette = malloc(sizeof(uint8_t) * colors_in_PLTE); old_to_new_palette = malloc(sizeof(*old_to_new_palette)
* colors_in_PLTE);
if (!old_to_new_palette)
err(1, "%s: Failed to allocate memory for new palette",
__func__);
for (i = 0; i < num_trans; i++) { for (i = 0; i < num_trans; i++) {
if (trans_alpha[i] == 0) { if (trans_alpha[i] == 0) {
@@ -227,8 +240,11 @@ static struct RawIndexedImage *indexed_png_to_raw(struct PNGImage *img)
if (colors_in_new_palette != colors_in_PLTE) { if (colors_in_new_palette != colors_in_PLTE) {
palette = realloc(palette, palette = realloc(palette,
sizeof(png_color) * sizeof(*palette) *
colors_in_new_palette); colors_in_new_palette);
if (!palette)
err(1, "%s: Failed to allocate memory for palette",
__func__);
} }
/* /*
@@ -247,6 +263,7 @@ static struct RawIndexedImage *indexed_png_to_raw(struct PNGImage *img)
} }
} }
free(palette);
free(old_to_new_palette); free(old_to_new_palette);
} else { } else {
set_raw_image_palette(raw_image, palette, colors_in_PLTE); set_raw_image_palette(raw_image, palette, colors_in_PLTE);
@@ -353,7 +370,9 @@ static void rgba_build_palette(struct PNGImage *img,
* By filling the palette up with black by default, if the image * By filling the palette up with black by default, if the image
* doesn't have enough colors, the palette gets padded with black. * doesn't have enough colors, the palette gets padded with black.
*/ */
*palette_ptr_ptr = calloc(colors, sizeof(png_color)); *palette_ptr_ptr = calloc(colors, sizeof(**palette_ptr_ptr));
if (!*palette_ptr_ptr)
err(1, "%s: Failed to allocate memory for palette", __func__);
palette = *palette_ptr_ptr; palette = *palette_ptr_ptr;
*num_colors = 0; *num_colors = 0;
@@ -421,10 +440,15 @@ static void update_built_palette(png_color *palette,
static int fit_grayscale_palette(png_color *palette, int *num_colors) static int fit_grayscale_palette(png_color *palette, int *num_colors)
{ {
int interval = 256 / colors; int interval = 256 / colors;
png_color *fitted_palette = malloc(sizeof(png_color) * colors); png_color *fitted_palette = malloc(sizeof(*fitted_palette) * colors);
bool *set_indices = calloc(colors, sizeof(bool)); bool *set_indices = calloc(colors, sizeof(*set_indices));
int i, shade_index; int i, shade_index;
if (!fitted_palette)
err(1, "%s: Failed to allocate memory for palette", __func__);
if (!set_indices)
err(1, "%s: Failed to allocate memory for indices", __func__);
fitted_palette[0].red = 0xFF; fitted_palette[0].red = 0xFF;
fitted_palette[0].green = 0xFF; fitted_palette[0].green = 0xFF;
fitted_palette[0].blue = 0xFF; fitted_palette[0].blue = 0xFF;
@@ -481,7 +505,10 @@ static void order_color_palette(png_color *palette, int num_colors)
{ {
int i; int i;
struct ColorWithLuminance *palette_with_luminance = struct ColorWithLuminance *palette_with_luminance =
malloc(sizeof(struct ColorWithLuminance) * num_colors); malloc(sizeof(*palette_with_luminance) * num_colors);
if (!palette_with_luminance)
err(1, "%s: Failed to allocate memory for palette", __func__);
for (i = 0; i < num_colors; i++) { for (i = 0; i < num_colors; i++) {
/* /*
@@ -494,7 +521,7 @@ static void order_color_palette(png_color *palette, int num_colors)
722 * palette[i].blue; 722 * palette[i].blue;
} }
qsort(palette_with_luminance, num_colors, qsort(palette_with_luminance, num_colors,
sizeof(struct ColorWithLuminance), compare_luminance); sizeof(*palette_with_luminance), compare_luminance);
for (i = 0; i < num_colors; i++) for (i = 0; i < num_colors; i++)
palette[i] = palette_with_luminance[i].color; palette[i] = palette_with_luminance[i].color;
@@ -582,9 +609,16 @@ static void read_png(struct PNGImage *img)
png_read_update_info(img->png, img->info); png_read_update_info(img->png, img->info);
img->data = malloc(sizeof(png_byte *) * img->height); img->data = malloc(sizeof(*img->data) * img->height);
for (y = 0; y < img->height; y++) if (!img->data)
err(1, "%s: Failed to allocate memory for image data",
__func__);
for (y = 0; y < img->height; y++) {
img->data[y] = malloc(png_get_rowbytes(img->png, img->info)); img->data[y] = malloc(png_get_rowbytes(img->png, img->info));
if (!img->data[y])
err(1, "%s: Failed to allocate memory for image data",
__func__);
}
png_read_image(img->png, img->data); png_read_image(img->png, img->data);
png_read_end(img->png, img->info); png_read_end(img->png, img->info);
@@ -596,17 +630,31 @@ static struct RawIndexedImage *create_raw_image(int width, int height,
struct RawIndexedImage *raw_image; struct RawIndexedImage *raw_image;
int y; int y;
raw_image = malloc(sizeof(struct RawIndexedImage)); raw_image = malloc(sizeof(*raw_image));
if (!raw_image)
err(1, "%s: Failed to allocate memory for raw image",
__func__);
raw_image->width = width; raw_image->width = width;
raw_image->height = height; raw_image->height = height;
raw_image->num_colors = num_colors; raw_image->num_colors = num_colors;
raw_image->palette = malloc(sizeof(struct RGBColor) * num_colors); raw_image->palette = malloc(sizeof(*raw_image->palette) * num_colors);
if (!raw_image->palette)
err(1, "%s: Failed to allocate memory for raw image palette",
__func__);
raw_image->data = malloc(sizeof(uint8_t *) * height); raw_image->data = malloc(sizeof(*raw_image->data) * height);
for (y = 0; y < height; y++) if (!raw_image->data)
raw_image->data[y] = malloc(sizeof(uint8_t) * width); err(1, "%s: Failed to allocate memory for raw image data",
__func__);
for (y = 0; y < height; y++) {
raw_image->data[y] = malloc(sizeof(*raw_image->data[y])
* width);
if (!raw_image->data[y])
err(1, "%s: Failed to allocate memory for raw image data",
__func__);
}
return raw_image; return raw_image;
} }
@@ -690,7 +738,10 @@ static void set_text(const struct PNGImage *img,
png_text *text; png_text *text;
char buffer[3]; char buffer[3];
text = malloc(sizeof(png_text)); text = malloc(sizeof(*text));
if (!text)
err(1, "%s: Failed to allocate memory for PNG text",
__func__);
if (png_options->horizontal) { if (png_options->horizontal) {
text[0].key = "h"; text[0].key = "h";

View File

@@ -78,6 +78,7 @@ bool hash_RemoveElement(HashMap map, char const *key)
*ptr = next; *ptr = next;
return true; return true;
} }
ptr = &(*ptr)->next;
} }
return false; return false;
} }

View File

@@ -6,6 +6,7 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <inttypes.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -51,7 +52,7 @@ static void initFreeSpace(void)
memory[type][bank].next = memory[type][bank].next =
malloc(sizeof(*memory[type][0].next)); malloc(sizeof(*memory[type][0].next));
if (!memory[type][bank].next) if (!memory[type][bank].next)
err(1, "Failed to init free space for region %d bank %u", err(1, "Failed to init free space for region %d bank %" PRIu32,
type, bank); type, bank);
memory[type][bank].next->address = startaddr[type]; memory[type][bank].next->address = startaddr[type];
memory[type][bank].next->size = maxsize[type]; memory[type][bank].next->size = maxsize[type];
@@ -80,14 +81,14 @@ static void processLinkerScript(void)
/* Check if this doesn't conflict with what the code says */ /* Check if this doesn't conflict with what the code says */
if (section->isBankFixed && placement->bank != section->bank) if (section->isBankFixed && placement->bank != section->bank)
errx(1, "Linker script contradicts \"%s\"'s bank placement", error("Linker script contradicts \"%s\"'s bank placement",
section->name); section->name);
if (section->isAddressFixed && placement->org != section->org) if (section->isAddressFixed && placement->org != section->org)
errx(1, "Linker script contradicts \"%s\"'s address placement", error("Linker script contradicts \"%s\"'s address placement",
section->name); section->name);
if (section->isAlignFixed if (section->isAlignFixed
&& (placement->org & section->alignMask) != 0) && (placement->org & section->alignMask) != 0)
errx(1, "Linker script contradicts \"%s\"'s alignment", error("Linker script contradicts \"%s\"'s alignment",
section->name); section->name);
section->isAddressFixed = true; section->isAddressFixed = true;
@@ -132,7 +133,8 @@ static bool isLocationSuitable(struct Section const *section,
if (section->isAddressFixed && section->org != location->address) if (section->isAddressFixed && section->org != location->address)
return false; return false;
if (section->isAlignFixed && location->address & section->alignMask) if (section->isAlignFixed
&& ((location->address - section->alignOfs) & section->alignMask))
return false; return false;
if (location->address < freeSpace->address) if (location->address < freeSpace->address)
@@ -183,8 +185,13 @@ static struct FreeSpace *getPlacement(struct Section const *section,
space = NULL; space = NULL;
} else if (section->isAlignFixed) { } else if (section->isAlignFixed) {
/* Move to next aligned location */ /* Move to next aligned location */
/* Move back to alignment boundary */
location->address -= section->alignOfs;
/* Ensure we're there (e.g. on first check) */
location->address &= ~section->alignMask; location->address &= ~section->alignMask;
location->address += section->alignMask + 1; /* Go to next align boundary and add offset */
location->address += section->alignMask + 1
+ section->alignOfs;
} else { } else {
/* Any location is fine, so, next free block */ /* Any location is fine, so, next free block */
space = space->next; space = space->next;
@@ -298,19 +305,22 @@ static void placeSection(struct Section *section)
if (section->isBankFixed && nbbanks(section->type) != 1) { if (section->isBankFixed && nbbanks(section->type) != 1) {
if (section->isAddressFixed) if (section->isAddressFixed)
snprintf(where, 64, "at $%02x:%04x", snprintf(where, 64, "at $%02" PRIx32 ":%04" PRIx16,
section->bank, section->org); section->bank, section->org);
else if (section->isAlignFixed) else if (section->isAlignFixed)
snprintf(where, 64, "in bank $%02x with align mask %x", snprintf(where, 64, "in bank $%02" PRIx32 " with align mask %" PRIx16,
section->bank, ~section->alignMask); section->bank, (uint16_t)~section->alignMask);
else else
snprintf(where, 64, "in bank $%02x", section->bank); snprintf(where, 64, "in bank $%02" PRIx32,
section->bank);
} else { } else {
if (section->isAddressFixed) if (section->isAddressFixed)
snprintf(where, 64, "at address $%04x", section->org); snprintf(where, 64, "at address $%04" PRIx16,
section->org);
else if (section->isAlignFixed) else if (section->isAlignFixed)
snprintf(where, 64, "with align mask %x", snprintf(where, 64, "with align mask %" PRIx16 " and offset %" PRIx16,
~section->alignMask); (uint16_t)~section->alignMask,
section->alignOfs);
else else
strcpy(where, "anywhere"); strcpy(where, "anywhere");
} }
@@ -321,7 +331,7 @@ static void placeSection(struct Section *section)
section->name, typeNames[section->type], where); section->name, typeNames[section->type], where);
/* If the section just can't fit the bank, report that */ /* If the section just can't fit the bank, report that */
else if (section->org + section->size > endaddr(section->type) + 1) else if (section->org + section->size > endaddr(section->type) + 1)
errx(1, "Unable to place \"%s\" (%s section) %s: section runs past end of region ($%04x > $%04x)", errx(1, "Unable to place \"%s\" (%s section) %s: section runs past end of region ($%04" PRIx16 " > $%04" PRIx16 ")",
section->name, typeNames[section->type], where, section->name, typeNames[section->type], where,
section->org + section->size, endaddr(section->type) + 1); section->org + section->size, endaddr(section->type) + 1);
/* Otherwise there is overlap with another section */ /* Otherwise there is overlap with another section */
@@ -412,7 +422,7 @@ void assign_AssignSections(void)
/* Overlaying requires only fully-constrained sections */ /* Overlaying requires only fully-constrained sections */
verbosePrint("Assigning other sections...\n"); verbosePrint("Assigning other sections...\n");
if (overlayFileName) if (overlayFileName)
errx(1, "All sections must be fixed when using an overlay file; %lu %sn't", errx(1, "All sections must be fixed when using an overlay file; %" PRIu64 " %sn't",
nbSectionsToAssign, nbSectionsToAssign == 1 ? "is" : "are"); nbSectionsToAssign, nbSectionsToAssign == 1 ? "is" : "are");
/* Assign all remaining sections by decreasing constraint order */ /* Assign all remaining sections by decreasing constraint order */

View File

@@ -6,12 +6,13 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <sys/types.h> #include <inttypes.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "link/object.h" #include "link/object.h"
#include "link/symbol.h" #include "link/symbol.h"
@@ -25,7 +26,7 @@
#include "version.h" #include "version.h"
bool isDmgMode; /* -d */ bool isDmgMode; /* -d */
char const *linkerScriptName; /* -l */ char *linkerScriptName; /* -l */
char const *mapFileName; /* -m */ char const *mapFileName; /* -m */
char const *symFileName; /* -n */ char const *symFileName; /* -n */
char const *overlayFileName; /* -O */ char const *overlayFileName; /* -O */
@@ -34,6 +35,41 @@ uint8_t padValue; /* -p */
bool is32kMode; /* -t */ bool is32kMode; /* -t */
bool beVerbose; /* -v */ bool beVerbose; /* -v */
bool isWRA0Mode; /* -w */ bool isWRA0Mode; /* -w */
bool disablePadding; /* -x */
static uint32_t nbErrors = 0;
void error(char const *fmt, ...)
{
va_list ap;
fprintf(stderr, "error: ");
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
putc('\n', stderr);
if (nbErrors != UINT32_MAX)
nbErrors++;
}
noreturn_ void fatal(char const *fmt, ...)
{
va_list ap;
fprintf(stderr, "fatal: ");
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
putc('\n', stderr);
if (nbErrors != UINT32_MAX)
nbErrors++;
fprintf(stderr, "Linking aborted after %" PRIu32 " error%s\n", nbErrors,
nbErrors != 1 ? "s" : "");
exit(1);
}
FILE *openFile(char const *fileName, char const *mode) FILE *openFile(char const *fileName, char const *mode)
{ {
@@ -49,7 +85,7 @@ FILE *openFile(char const *fileName, char const *mode)
} }
/* Short options */ /* Short options */
static char const *optstring = "dl:m:n:O:o:p:s:tVvw"; static char const *optstring = "dl:m:n:O:o:p:s:tVvwx";
/* /*
* Equivalent long options * Equivalent long options
@@ -74,6 +110,7 @@ static struct option const longopts[] = {
{ "version", no_argument, NULL, 'V' }, { "version", no_argument, NULL, 'V' },
{ "verbose", no_argument, NULL, 'v' }, { "verbose", no_argument, NULL, 'v' },
{ "wramx", no_argument, NULL, 'w' }, { "wramx", no_argument, NULL, 'w' },
{ "nopad", no_argument, NULL, 'x' },
{ NULL, no_argument, NULL, 0 } { NULL, no_argument, NULL, 0 }
}; };
@@ -83,7 +120,7 @@ static struct option const longopts[] = {
static void printUsage(void) static void printUsage(void)
{ {
fputs( fputs(
"Usage: rgblink [-dtVvw] [-l script] [-m map_file] [-n sym_file]\n" "Usage: rgblink [-dtVvwx] [-l script] [-m map_file] [-n sym_file]\n"
" [-O overlay_file] [-o out_file] [-p pad_value] [-s symbol]\n" " [-O overlay_file] [-o out_file] [-p pad_value] [-s symbol]\n"
" <file> ...\n" " <file> ...\n"
"Useful options:\n" "Useful options:\n"
@@ -92,6 +129,7 @@ static void printUsage(void)
" -n, --sym <path> set the output symbol list file\n" " -n, --sym <path> set the output symbol list file\n"
" -o, --output <path> set the output file\n" " -o, --output <path> set the output file\n"
" -p, --pad <value> set the value to pad between sections with\n" " -p, --pad <value> set the value to pad between sections with\n"
" -x, --nopad disable padding of output binary\n"
" -V, --version print RGBLINK version and exits\n" " -V, --version print RGBLINK version and exits\n"
"\n" "\n"
"For help, use `man rgblink' or go to https://rednex.github.io/rgbds/\n", "For help, use `man rgblink' or go to https://rednex.github.io/rgbds/\n",
@@ -138,10 +176,14 @@ int main(int argc, char *argv[])
break; break;
case 'p': case 'p':
value = strtoul(optarg, &endptr, 0); value = strtoul(optarg, &endptr, 0);
if (optarg[0] == '\0' || *endptr != '\0') if (optarg[0] == '\0' || *endptr != '\0') {
errx(1, "Invalid argument for option 'p'"); error("Invalid argument for option 'p'");
if (value > 0xFF) value = 0xFF;
errx(1, "Argument for 'p' must be a byte (between 0 and 0xFF)"); }
if (value > 0xFF) {
error("Argument for 'p' must be a byte (between 0 and 0xFF)");
value = 0xFF;
}
padValue = value; padValue = value;
break; break;
case 's': case 's':
@@ -161,6 +203,11 @@ int main(int argc, char *argv[])
case 'w': case 'w':
isWRA0Mode = true; isWRA0Mode = true;
break; break;
case 'x':
disablePadding = true;
/* implies tiny mode */
is32kMode = true;
break;
default: default:
printUsage(); printUsage();
exit(1); exit(1);
@@ -171,7 +218,7 @@ int main(int argc, char *argv[])
/* If no input files were specified, the user must have screwed up */ /* If no input files were specified, the user must have screwed up */
if (curArgIndex == argc) { if (curArgIndex == argc) {
fputs("FATAL: no input files\n", stderr); fputs("fatal: no input files\n", stderr);
printUsage(); printUsage();
exit(1); exit(1);
} }
@@ -198,6 +245,11 @@ int main(int argc, char *argv[])
/* and finally output the result. */ /* and finally output the result. */
patch_ApplyPatches(); patch_ApplyPatches();
if (nbErrors) {
fprintf(stderr, "Linking failed with %" PRIu32 " error%s\n",
nbErrors, nbErrors != 1 ? "s" : "");
exit(1);
}
out_WriteFiles(); out_WriteFiles();
/* Do cleanup before quitting, though. */ /* Do cleanup before quitting, though. */

View File

@@ -6,12 +6,13 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include "link/assign.h" #include "link/assign.h"
#include "link/main.h" #include "link/main.h"
@@ -207,20 +208,30 @@ static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName)
* @param fileName The filename to report in errors * @param fileName The filename to report in errors
* @param i The number of the patch to report in errors * @param i The number of the patch to report in errors
*/ */
static void readPatch(FILE *file, struct Patch *patch, static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
char const *fileName, char const *sectName, uint32_t i) char const *sectName, uint32_t i,
struct Section *fileSections[])
{ {
tryReadstr(patch->fileName, file, tryReadstr(patch->fileName, file,
"%s: Unable to read \"%s\"'s patch #%u's name: %s", "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s name: %s",
fileName, sectName, i); fileName, sectName, i);
tryReadlong(patch->offset, file, tryReadlong(patch->offset, file,
"%s: Unable to read \"%s\"'s patch #%u's offset: %s", "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s offset: %s",
fileName, sectName, i);
tryReadlong(patch->pcSectionID, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
fileName, sectName, i);
patch->pcSection = patch->pcSectionID == -1
? NULL
: fileSections[patch->pcSectionID];
tryReadlong(patch->pcOffset, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
fileName, sectName, i); fileName, sectName, i);
tryGetc(patch->type, file, tryGetc(patch->type, file,
"%s: Unable to read \"%s\"'s patch #%u's type: %s", "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s type: %s",
fileName, sectName, i); fileName, sectName, i);
tryReadlong(patch->rpnSize, file, tryReadlong(patch->rpnSize, file,
"%s: Unable to read \"%s\"'s patch #%u's RPN size: %s", "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s RPN size: %s",
fileName, sectName, i); fileName, sectName, i);
uint8_t *rpnExpression = uint8_t *rpnExpression =
@@ -229,7 +240,7 @@ static void readPatch(FILE *file, struct Patch *patch,
patch->rpnSize, file); patch->rpnSize, file);
if (nbElementsRead != patch->rpnSize) if (nbElementsRead != patch->rpnSize)
errx(1, "%s: Cannot read \"%s\"'s patch #%u's RPN expression: %s", errx(1, "%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN expression: %s",
fileName, sectName, i, fileName, sectName, i,
feof(file) ? "Unexpected end of file" : strerror(errno)); feof(file) ? "Unexpected end of file" : strerror(errno));
patch->rpnExpression = rpnExpression; patch->rpnExpression = rpnExpression;
@@ -242,37 +253,54 @@ static void readPatch(FILE *file, struct Patch *patch,
* @param fileName The filename to report in errors * @param fileName The filename to report in errors
*/ */
static void readSection(FILE *file, struct Section *section, static void readSection(FILE *file, struct Section *section,
char const *fileName) char const *fileName, struct Section *fileSections[])
{ {
int32_t tmp; int32_t tmp;
uint8_t type; uint8_t byte;
tryReadstr(section->name, file, "%s: Cannot read section name: %s", tryReadstr(section->name, file, "%s: Cannot read section name: %s",
fileName); fileName);
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s' size: %s", tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s' size: %s",
fileName, section->name); fileName, section->name);
if (tmp < 0 || tmp > UINT16_MAX) if (tmp < 0 || tmp > UINT16_MAX)
errx(1, "\"%s\"'s section size (%d) is invalid", section->name, errx(1, "\"%s\"'s section size (%" PRId32 ") is invalid",
tmp); section->name, tmp);
section->size = tmp; section->size = tmp;
tryGetc(type, file, "%s: Cannot read \"%s\"'s type: %s", section->offset = 0;
tryGetc(byte, file, "%s: Cannot read \"%s\"'s type: %s",
fileName, section->name); fileName, section->name);
section->type = type & 0x7F; section->type = byte & 0x3F;
section->isUnion = type >> 7; if (byte >> 7)
section->modifier = SECTION_UNION;
else if (byte >> 6)
section->modifier = SECTION_FRAGMENT;
else
section->modifier = SECTION_NORMAL;
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s org: %s", tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s org: %s",
fileName, section->name); fileName, section->name);
section->isAddressFixed = tmp >= 0; section->isAddressFixed = tmp >= 0;
if (tmp > UINT16_MAX) if (tmp > UINT16_MAX) {
errx(1, "\"%s\"'s org' is too large (%d)", section->name, tmp); error("\"%s\"'s org is too large (%" PRId32 ")",
section->name, tmp);
tmp = UINT16_MAX;
}
section->org = tmp; section->org = tmp;
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s bank: %s", tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s bank: %s",
fileName, section->name); fileName, section->name);
section->isBankFixed = tmp >= 0; section->isBankFixed = tmp >= 0;
section->bank = tmp; section->bank = tmp;
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s alignment: %s", tryGetc(byte, file, "%s: Cannot read \"%s\"'s alignment: %s",
fileName, section->name); fileName, section->name);
section->isAlignFixed = tmp != 1; section->isAlignFixed = byte != 0;
section->alignMask = tmp - 1; section->alignMask = (1 << byte) - 1;
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s alignment offset: %s",
fileName, section->name);
if (tmp > UINT16_MAX) {
error("\"%s\"'s alignment offset is too large (%" PRId32 ")",
section->name, tmp);
tmp = UINT16_MAX;
}
section->alignOfs = tmp;
if (sect_HasData(section->type)) { if (sect_HasData(section->type)) {
/* Ensure we never allocate 0 bytes */ /* Ensure we never allocate 0 bytes */
@@ -302,9 +330,10 @@ static void readSection(FILE *file, struct Section *section,
if (!patches) if (!patches)
err(1, "%s: Unable to read \"%s\"'s patches", fileName, err(1, "%s: Unable to read \"%s\"'s patches", fileName,
section->name); section->name);
for (uint32_t i = 0; i < section->nbPatches; i++) for (uint32_t i = 0; i < section->nbPatches; i++) {
readPatch(file, &patches[i], fileName, section->name, readPatch(file, &patches[i], fileName, section->name,
i); i, fileSections);
}
section->patches = patches; section->patches = patches;
} }
} }
@@ -345,25 +374,21 @@ static void linkSymToSect(struct Symbol const *symbol, struct Section *section)
* @param fileName The filename to report in errors * @param fileName The filename to report in errors
*/ */
static void readAssertion(FILE *file, struct Assertion *assert, static void readAssertion(FILE *file, struct Assertion *assert,
char const *fileName, struct Section *fileSections[], char const *fileName, uint32_t i,
uint32_t i) struct Section *fileSections[])
{ {
char assertName[sizeof("Assertion #" EXPAND_AND_STR(UINT32_MAX))]; char assertName[sizeof("Assertion #" EXPAND_AND_STR(UINT32_MAX))];
uint32_t sectionID;
snprintf(assertName, sizeof(assertName), "Assertion #%u", i); snprintf(assertName, sizeof(assertName), "Assertion #%" PRIu32, i);
readPatch(file, &assert->patch, fileName, assertName, 0); readPatch(file, &assert->patch, fileName, assertName, 0, fileSections);
tryReadlong(sectionID, file, "%s: Cannot read assertion's section ID: %s",
fileName);
assert->section = sectionID == -1 ? NULL : fileSections[sectionID];
tryReadstr(assert->message, file, "%s: Cannot read assertion's message: %s", tryReadstr(assert->message, file, "%s: Cannot read assertion's message: %s",
fileName); fileName);
} }
static inline struct Section *getMainSection(struct Section *section) static inline struct Section *getMainSection(struct Section *section)
{ {
if (section->isUnion) if (section->modifier != SECTION_NORMAL)
section = sect_GetSection(section->name); section = sect_GetSection(section->name);
return section; return section;
@@ -381,18 +406,18 @@ void obj_ReadFile(char const *fileName)
err(1, "Could not open file %s", fileName); err(1, "Could not open file %s", fileName);
/* Begin by reading the magic bytes and version number */ /* Begin by reading the magic bytes and version number */
uint8_t versionNumber; unsigned versionNumber;
int matchedElems = fscanf(file, RGBDS_OBJECT_VERSION_STRING, int matchedElems = fscanf(file, RGBDS_OBJECT_VERSION_STRING,
&versionNumber); &versionNumber);
if (matchedElems != 1) if (matchedElems != 1)
errx(1, "\"%s\" is not a RGBDS object file", fileName); errx(1, "\"%s\" is not a RGBDS object file", fileName);
verbosePrint("Reading object file %s, version %hhu\n", verbosePrint("Reading object file %s, version %u\n",
fileName, versionNumber); fileName, versionNumber);
if (versionNumber != RGBDS_OBJECT_VERSION_NUMBER) if (versionNumber != RGBDS_OBJECT_VERSION_NUMBER)
errx(1, "\"%s\" is an incompatible version %hhu object file", errx(1, "\"%s\" is an incompatible version %u object file",
fileName, versionNumber); fileName, versionNumber);
uint32_t revNum; uint32_t revNum;
@@ -400,7 +425,7 @@ void obj_ReadFile(char const *fileName)
tryReadlong(revNum, file, "%s: Cannot read revision number: %s", tryReadlong(revNum, file, "%s: Cannot read revision number: %s",
fileName); fileName);
if (revNum != RGBDS_OBJECT_REV) if (revNum != RGBDS_OBJECT_REV)
errx(1, "%s is a revision 0x%04x object file, only 0x%04x is supported", errx(1, "%s is a revision 0x%04" PRIx32 " object file; only 0x%04x is supported",
fileName, revNum, RGBDS_OBJECT_REV); fileName, revNum, RGBDS_OBJECT_REV);
uint32_t nbSymbols; uint32_t nbSymbols;
@@ -429,11 +454,10 @@ void obj_ReadFile(char const *fileName)
symbolList->next = symbolLists; symbolList->next = symbolLists;
symbolLists = symbolList; symbolLists = symbolList;
uint32_t nbSymPerSect[nbSections ? nbSections : 1]; uint32_t *nbSymPerSect = calloc(nbSections ? nbSections : 1,
sizeof(*nbSymPerSect));
memset(nbSymPerSect, 0, sizeof(nbSymPerSect)); verbosePrint("Reading %" PRIu32 " symbols...\n", nbSymbols);
verbosePrint("Reading %u symbols...\n", nbSymbols);
for (uint32_t i = 0; i < nbSymbols; i++) { for (uint32_t i = 0; i < nbSymbols; i++) {
/* Read symbol */ /* Read symbol */
struct Symbol *symbol = malloc(sizeof(*symbol)); struct Symbol *symbol = malloc(sizeof(*symbol));
@@ -450,33 +474,35 @@ void obj_ReadFile(char const *fileName)
} }
/* This file's sections, stored in a table to link symbols to them */ /* This file's sections, stored in a table to link symbols to them */
struct Section *fileSections[nbSections ? nbSections : 1]; struct Section **fileSections = malloc(sizeof(*fileSections)
* (nbSections ? nbSections : 1));
verbosePrint("Reading %u sections...\n", nbSections); verbosePrint("Reading %" PRIu32 " sections...\n", nbSections);
for (uint32_t i = 0; i < nbSections; i++) { for (uint32_t i = 0; i < nbSections; i++) {
/* Read section */ /* Read section */
struct Section *section = malloc(sizeof(*section)); fileSections[i] = malloc(sizeof(*fileSections[i]));
if (!fileSections[i])
if (!section)
err(1, "%s: Couldn't create new section", fileName); err(1, "%s: Couldn't create new section", fileName);
section->nextu = NULL;
readSection(file, section, fileName);
section->fileSymbols = fileSymbols;
sect_AddSection(section); fileSections[i]->nextu = NULL;
fileSections[i] = section; readSection(file, fileSections[i], fileName, fileSections);
fileSections[i]->fileSymbols = fileSymbols;
if (nbSymPerSect[i]) { if (nbSymPerSect[i]) {
section->symbols = malloc(sizeof(*section->symbols) fileSections[i]->symbols = malloc(nbSymPerSect[i]
* nbSymPerSect[i]); * sizeof(*fileSections[i]->symbols));
if (!section->symbols) if (!fileSections[i]->symbols)
err(1, "%s: Couldn't link to symbols", err(1, "%s: Couldn't link to symbols",
fileName); fileName);
} else { } else {
section->symbols = NULL; fileSections[i]->symbols = NULL;
} }
section->nbSymbols = 0; fileSections[i]->nbSymbols = 0;
sect_AddSection(fileSections[i]);
} }
free(nbSymPerSect);
/* Give symbols pointers to their sections */ /* Give symbols pointers to their sections */
for (uint32_t i = 0; i < nbSymbols; i++) { for (uint32_t i = 0; i < nbSymbols; i++) {
int32_t sectionID = fileSymbols[i]->sectionID; int32_t sectionID = fileSymbols[i]->sectionID;
@@ -484,10 +510,18 @@ void obj_ReadFile(char const *fileName)
if (sectionID == -1) { if (sectionID == -1) {
fileSymbols[i]->section = NULL; fileSymbols[i]->section = NULL;
} else { } else {
struct Section *section = fileSections[sectionID];
/* Give the section a pointer to the symbol as well */ /* Give the section a pointer to the symbol as well */
linkSymToSect(fileSymbols[i], fileSections[sectionID]); linkSymToSect(fileSymbols[i], section);
fileSymbols[i]->section =
getMainSection(fileSections[sectionID]); if (section->modifier != SECTION_NORMAL) {
if (section->modifier == SECTION_FRAGMENT)
/* Add the fragment's offset to the symbol's */
fileSymbols[i]->offset += section->offset;
section = getMainSection(section);
}
fileSymbols[i]->section = section;
} }
} }
@@ -495,18 +529,19 @@ void obj_ReadFile(char const *fileName)
tryReadlong(nbAsserts, file, "%s: Cannot read number of assertions: %s", tryReadlong(nbAsserts, file, "%s: Cannot read number of assertions: %s",
fileName); fileName);
verbosePrint("Reading %u assertions...\n", nbAsserts); verbosePrint("Reading %" PRIu32 " assertions...\n", nbAsserts);
for (uint32_t i = 0; i < nbAsserts; i++) { for (uint32_t i = 0; i < nbAsserts; i++) {
struct Assertion *assertion = malloc(sizeof(*assertion)); struct Assertion *assertion = malloc(sizeof(*assertion));
if (!assertion) if (!assertion)
err(1, "%s: Couldn't create new assertion", fileName); err(1, "%s: Couldn't create new assertion", fileName);
readAssertion(file, assertion, fileName, fileSections, i); readAssertion(file, assertion, fileName, i, fileSections);
assertion->fileSymbols = fileSymbols; assertion->fileSymbols = fileSymbols;
assertion->next = assertions; assertion->next = assertions;
assertions = assertion; assertions = assertion;
} }
free(fileSections);
fclose(file); fclose(file);
} }

View File

@@ -6,8 +6,9 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <stdlib.h> #include <inttypes.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h>
#include "link/output.h" #include "link/output.h"
#include "link/main.h" #include "link/main.h"
@@ -51,7 +52,7 @@ void out_AddSection(struct Section const *section)
uint32_t minNbBanks = targetBank + 1; uint32_t minNbBanks = targetBank + 1;
if (minNbBanks > maxNbBanks[section->type]) if (minNbBanks > maxNbBanks[section->type])
errx(1, "Section \"%s\" has invalid bank range (%u > %u)", errx(1, "Section \"%s\" has an invalid bank range (%" PRIu32 " > %" PRIu32 ")",
section->name, section->bank, section->name, section->bank,
maxNbBanks[section->type] - 1); maxNbBanks[section->type] - 1);
@@ -87,8 +88,9 @@ void out_AddSection(struct Section const *section)
struct Section const *out_OverlappingSection(struct Section const *section) struct Section const *out_OverlappingSection(struct Section const *section)
{ {
struct SortedSections *banks = sections[section->type].banks;
struct SortedSection *ptr = struct SortedSection *ptr =
sections[section->type].banks[section->bank].sections; banks[section->bank - bankranges[section->type][0]].sections;
while (ptr) { while (ptr) {
if (ptr->section->org < section->org + section->size if (ptr->section->org < section->org + section->size
@@ -175,10 +177,14 @@ static void writeBank(struct SortedSection *bankSections, uint16_t baseOffset,
bankSections = bankSections->next; bankSections = bankSections->next;
} }
if (!disablePadding) {
while (offset < size) { while (offset < size) {
putc(overlayFile ? getc(overlayFile) : padValue, outputFile); putc(overlayFile ? getc(overlayFile)
: padValue,
outputFile);
offset++; offset++;
} }
}
} }
/** /**
@@ -279,7 +285,7 @@ static void writeSymBank(struct SortedSections const *bankSections)
minSectList = &zlSectList; minSectList = &zlSectList;
} }
fprintf(symFile, "%02x:%04x %s\n", fprintf(symFile, "%02" PRIx32 ":%04" PRIx16 " %s\n",
minSectList->sect->bank, minSectList->addr, minSectList->sect->bank, minSectList->addr,
minSectList->sym->name); minSectList->sym->name);
minSectList->i++; minSectList->i++;
@@ -300,7 +306,7 @@ static void writeMapBank(struct SortedSections const *sectList,
struct SortedSection const *section = sectList->sections; struct SortedSection const *section = sectList->sections;
struct SortedSection const *zeroLenSection = sectList->zeroLenSections; struct SortedSection const *zeroLenSection = sectList->zeroLenSections;
fprintf(mapFile, "%s bank #%u:\n", typeNames[type], fprintf(mapFile, "%s bank #%" PRIu32 ":\n", typeNames[type],
bank + bankranges[type][0]); bank + bankranges[type][0]);
uint16_t slack = maxsize[type]; uint16_t slack = maxsize[type];
@@ -312,12 +318,17 @@ static void writeMapBank(struct SortedSections const *sectList,
slack -= sect->size; slack -= sect->size;
fprintf(mapFile, " SECTION: $%04x-$%04x ($%04x byte%s) [\"%s\"]\n", if (sect->size != 0)
sect->org, sect->org + sect->size - 1, sect->size, fprintf(mapFile, " SECTION: $%04" PRIx16 "-$%04" PRIx16 " ($%04" PRIx16 " byte%s) [\"%s\"]\n",
sect->size == 1 ? "" : "s", sect->name); sect->org, sect->org + sect->size - 1,
sect->size, sect->size == 1 ? "" : "s",
sect->name);
else
fprintf(mapFile, " SECTION: $%04" PRIx16 " (0 bytes) [\"%s\"]\n",
sect->org, sect->name);
for (size_t i = 0; i < sect->nbSymbols; i++) for (size_t i = 0; i < sect->nbSymbols; i++)
fprintf(mapFile, " $%04x = %s\n", fprintf(mapFile, " $%04" PRIx32 " = %s\n",
sect->symbols[i]->offset + sect->org, sect->symbols[i]->offset + sect->org,
sect->symbols[i]->name); sect->symbols[i]->name);
@@ -327,7 +338,7 @@ static void writeMapBank(struct SortedSections const *sectList,
if (slack == maxsize[type]) if (slack == maxsize[type])
fputs(" EMPTY\n\n", mapFile); fputs(" EMPTY\n\n", mapFile);
else else
fprintf(mapFile, " SLACK: $%04x byte%s\n\n", slack, fprintf(mapFile, " SLACK: $%04" PRIx16 " byte%s\n\n", slack,
slack == 1 ? "" : "s"); slack == 1 ? "" : "s");
} }

View File

@@ -6,8 +6,9 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <stdlib.h> #include <inttypes.h>
#include <limits.h> #include <limits.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include "link/patch.h" #include "link/patch.h"
@@ -82,10 +83,20 @@ static inline void clearRPNStack(void)
static void pushRPN(int32_t value) static void pushRPN(int32_t value)
{ {
if (stack.size >= stack.capacity) { if (stack.size >= stack.capacity) {
stack.capacity *= 2; static const size_t increase_factor = 2;
if (stack.capacity > SIZE_MAX / increase_factor)
errx(1, "Overflow in RPN stack resize");
stack.capacity *= increase_factor;
stack.buf = stack.buf =
realloc(stack.buf, sizeof(*stack.buf) * stack.capacity); realloc(stack.buf, sizeof(*stack.buf) * stack.capacity);
if (!stack.buf) /*
* Static analysis tools complain that the capacity might become
* zero due to overflow, but fail to realize that it's caught by
* the overflow check above. Hence the stringent check below.
*/
if (!stack.buf || !stack.capacity)
err(1, "Failed to resize RPN stack"); err(1, "Failed to resize RPN stack");
} }
@@ -118,19 +129,13 @@ static uint32_t getRPNByte(uint8_t const **expression, int32_t *size,
} }
static struct Symbol const *getSymbol(struct Symbol const * const *symbolList, static struct Symbol const *getSymbol(struct Symbol const * const *symbolList,
uint32_t index, char const *fileName) uint32_t index)
{ {
struct Symbol const *symbol = symbolList[index]; struct Symbol const *symbol = symbolList[index];
/* If the symbol is defined elsewhere... */ /* If the symbol is defined elsewhere... */
if (symbol->type == SYMTYPE_IMPORT) { if (symbol->type == SYMTYPE_IMPORT)
struct Symbol const *symbolDefinition = return sym_GetSymbol(symbol->name);
sym_GetSymbol(symbol->name);
if (!symbolDefinition)
errx(1, "%s: Unknown symbol \"%s\"", fileName,
symbol->name);
symbol = symbolDefinition;
}
return symbol; return symbol;
} }
@@ -142,7 +147,6 @@ static struct Symbol const *getSymbol(struct Symbol const * const *symbolList,
* @return The patch's value * @return The patch's value
*/ */
static int32_t computeRPNExpr(struct Patch const *patch, static int32_t computeRPNExpr(struct Patch const *patch,
struct Section const *section,
struct Symbol const * const *fileSymbols) struct Symbol const * const *fileSymbols)
{ {
/* Small shortcut to avoid a lot of repetition */ /* Small shortcut to avoid a lot of repetition */
@@ -182,13 +186,23 @@ static int32_t computeRPNExpr(struct Patch const *patch,
break; break;
case RPN_DIV: case RPN_DIV:
value = popRPN(); value = popRPN();
if (value == 0) if (value == 0) {
errx(1, "%s: Division by 0", patch->fileName); error("%s: Division by 0", patch->fileName);
popRPN();
value = INT32_MAX;
} else {
value = popRPN() / value; value = popRPN() / value;
}
break; break;
case RPN_MOD: case RPN_MOD:
value = popRPN(); value = popRPN();
if (value == 0) {
error("%s: Modulo by 0", patch->fileName);
popRPN();
value = 0;
} else {
value = popRPN() % value; value = popRPN() % value;
}
break; break;
case RPN_UNSUB: case RPN_UNSUB:
value = -popRPN(); value = -popRPN();
@@ -256,9 +270,21 @@ static int32_t computeRPNExpr(struct Patch const *patch,
for (uint8_t shift = 0; shift < 32; shift += 8) for (uint8_t shift = 0; shift < 32; shift += 8)
value |= getRPNByte(&expression, &size, value |= getRPNByte(&expression, &size,
patch->fileName) << shift; patch->fileName) << shift;
symbol = getSymbol(fileSymbols, value);
value = getSymbol(fileSymbols, value, if (!symbol) {
patch->fileName)->section->bank; error("%s: Requested BANK() of symbol \"%s\", which was not found",
patch->fileName,
fileSymbols[value]->name);
value = 1;
} else if (!symbol->section) {
error("%s: Requested BANK() of non-label symbol \"%s\"",
patch->fileName,
fileSymbols[value]->name);
value = 1;
} else {
value = symbol->section->bank;
}
break; break;
case RPN_BANK_SECT: case RPN_BANK_SECT:
@@ -268,15 +294,22 @@ static int32_t computeRPNExpr(struct Patch const *patch,
sect = sect_GetSection(name); sect = sect_GetSection(name);
if (!sect) if (!sect) {
errx(1, "%s: Requested BANK() of section \"%s\", which was not found", error("%s: Requested BANK() of section \"%s\", which was not found",
patch->fileName, name); patch->fileName, name);
value = 1;
} else {
value = sect->bank; value = sect->bank;
}
break; break;
case RPN_BANK_SELF: case RPN_BANK_SELF:
value = section->bank; if (!patch->pcSection) {
error("%s: PC has no bank outside a section");
value = 1;
} else {
value = patch->pcSection->bank;
}
break; break;
case RPN_HRAM: case RPN_HRAM:
@@ -284,7 +317,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
if (value < 0 if (value < 0
|| (value > 0xFF && value < 0xFF00) || (value > 0xFF && value < 0xFF00)
|| value > 0xFFFF) || value > 0xFFFF)
errx(1, "%s: Value %d is not in HRAM range", error("%s: Value %" PRId32 " is not in HRAM range",
patch->fileName, value); patch->fileName, value);
value &= 0xFF; value &= 0xFF;
break; break;
@@ -295,7 +328,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
* They can be easily checked with a bitmask * They can be easily checked with a bitmask
*/ */
if (value & ~0x38) if (value & ~0x38)
errx(1, "%s: Value %d is not a RST vector", error("%s: Value %" PRId32 " is not a RST vector",
patch->fileName, value); patch->fileName, value);
value |= 0xC7; value |= 0xC7;
break; break;
@@ -313,15 +346,23 @@ static int32_t computeRPNExpr(struct Patch const *patch,
value |= getRPNByte(&expression, &size, value |= getRPNByte(&expression, &size,
patch->fileName) << shift; patch->fileName) << shift;
symbol = getSymbol(fileSymbols, value, patch->fileName); symbol = getSymbol(fileSymbols, value);
if (!strcmp(symbol->name, "@")) { if (!symbol) {
value = section->org + patch->offset; error("%s: Unknown symbol \"%s\"",
} else { patch->fileName,
fileSymbols[value]->name);
} else if (strcmp(symbol->name, "@")) {
value = symbol->value; value = symbol->value;
/* Symbols attached to sections have offsets */ /* Symbols attached to sections have offsets */
if (symbol->section) if (symbol->section)
value += symbol->section->org; value += symbol->section->org;
} else if (!patch->pcSection) {
error("%s: PC has no value outside a section",
patch->fileName);
value = 0;
} else {
value = patch->pcOffset + patch->pcSection->org;
} }
break; break;
} }
@@ -330,7 +371,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
} }
if (stack.size > 1) if (stack.size > 1)
warnx("%s: RPN stack has %lu entries on exit, not 1", error("%s: RPN stack has %zu entries on exit, not 1",
patch->fileName, stack.size); patch->fileName, stack.size);
return popRPN(); return popRPN();
@@ -343,25 +384,21 @@ void patch_CheckAssertions(struct Assertion *assert)
verbosePrint("Checking assertions..."); verbosePrint("Checking assertions...");
initRPNStack(); initRPNStack();
uint8_t failures = 0;
while (assert) { while (assert) {
if (!computeRPNExpr(&assert->patch, assert->section, if (!computeRPNExpr(&assert->patch,
(struct Symbol const * const *) (struct Symbol const * const *)
assert->fileSymbols)) { assert->fileSymbols)) {
switch ((enum AssertionType)assert->patch.type) { switch ((enum AssertionType)assert->patch.type) {
case ASSERT_FATAL: case ASSERT_FATAL:
errx(1, "%s: %s", assert->patch.fileName, fatal("%s: %s", assert->patch.fileName,
assert->message[0] ? assert->message assert->message[0] ? assert->message
: "assert failure"); : "assert failure");
/* Not reached */ /* Not reached */
break; /* Here so checkpatch doesn't complain */ break; /* Here so checkpatch doesn't complain */
case ASSERT_ERROR: case ASSERT_ERROR:
fprintf(stderr, "%s: %s\n", error("%s: %s", assert->patch.fileName,
assert->patch.fileName,
assert->message[0] ? assert->message assert->message[0] ? assert->message
: "assert failure"); : "assert failure");
failures++;
break; break;
case ASSERT_WARN: case ASSERT_WARN:
warnx("%s: %s", assert->patch.fileName, warnx("%s: %s", assert->patch.fileName,
@@ -377,9 +414,6 @@ void patch_CheckAssertions(struct Assertion *assert)
} }
freeRPNStack(); freeRPNStack();
if (failures)
errx(1, "%u assertions failed!", failures);
} }
/** /**
@@ -387,7 +421,7 @@ void patch_CheckAssertions(struct Assertion *assert)
* @param section The section to patch * @param section The section to patch
* @param arg Ignored callback arg * @param arg Ignored callback arg
*/ */
static void applyFilePatches(struct Section *section) static void applyFilePatches(struct Section *section, struct Section *dataSection)
{ {
if (!sect_HasData(section->type)) if (!sect_HasData(section->type))
return; return;
@@ -395,20 +429,22 @@ static void applyFilePatches(struct Section *section)
verbosePrint("Patching section \"%s\"...\n", section->name); verbosePrint("Patching section \"%s\"...\n", section->name);
for (uint32_t patchID = 0; patchID < section->nbPatches; patchID++) { for (uint32_t patchID = 0; patchID < section->nbPatches; patchID++) {
struct Patch *patch = &section->patches[patchID]; struct Patch *patch = &section->patches[patchID];
int32_t value = computeRPNExpr(patch, section, int32_t value = computeRPNExpr(patch,
(struct Symbol const * const *) (struct Symbol const * const *)
section->fileSymbols); section->fileSymbols);
uint16_t offset = patch->offset + section->offset;
/* `jr` is quite unlike the others... */ /* `jr` is quite unlike the others... */
if (patch->type == PATCHTYPE_JR) { if (patch->type == PATCHTYPE_JR) {
/* Target is relative to the byte *after* the operand */ /* Target is relative to the byte *after* the operand */
uint16_t address = section->org + patch->offset + 1; uint16_t address = patch->pcSection->org
int16_t offset = value - address; + patch->pcOffset + 1;
int16_t jumpOffset = value - address;
if (offset < -128 || offset > 127) if (jumpOffset < -128 || jumpOffset > 127)
errx(1, "%s: jr target out of reach (expected -129 < %d < 128)", error("%s: jr target out of reach (expected -129 < %" PRId16 " < 128)",
patch->fileName, offset); patch->fileName, jumpOffset);
section->data[patch->offset] = offset & 0xFF; dataSection->data[offset] = jumpOffset & 0xFF;
} else { } else {
/* Patch a certain number of bytes */ /* Patch a certain number of bytes */
struct { struct {
@@ -423,12 +459,12 @@ static void applyFilePatches(struct Section *section)
if (value < types[patch->type].min if (value < types[patch->type].min
|| value > types[patch->type].max) || value > types[patch->type].max)
errx(1, "%s: Value %#x%s is not %u-bit", error("%s: Value %#" PRIx32 "%s is not %u-bit",
patch->fileName, value, patch->fileName, value,
value < 0 ? " (maybe negative?)" : "", value < 0 ? " (maybe negative?)" : "",
types[patch->type].size * 8); types[patch->type].size * 8U);
for (uint8_t i = 0; i < types[patch->type].size; i++) { for (uint8_t i = 0; i < types[patch->type].size; i++) {
section->data[patch->offset + i] = value & 0xFF; dataSection->data[offset + i] = value & 0xFF;
value >>= 8; value >>= 8;
} }
} }
@@ -444,9 +480,10 @@ static void applyFilePatches(struct Section *section)
static void applyPatches(struct Section *section, void *arg) static void applyPatches(struct Section *section, void *arg)
{ {
(void)arg; (void)arg;
struct Section *dataSection = section;
do { do {
applyFilePatches(section); applyFilePatches(section, dataSection);
section = section->nextu; section = section->nextu;
} while (section); } while (section);
} }

View File

@@ -13,7 +13,7 @@
.Nd Game Boy linker .Nd Game Boy linker
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl dtVvw .Op Fl dtVvwx
.Op Fl l Ar linker_script .Op Fl l Ar linker_script
.Op Fl m Ar map_file .Op Fl m Ar map_file
.Op Fl n Ar sym_file .Op Fl n Ar sym_file
@@ -63,7 +63,7 @@ The arguments are as follows:
.Bl -tag -width Ds .Bl -tag -width Ds
.It Fl d , Fl Fl dmg .It Fl d , Fl Fl dmg
Enable DMG mode. Enable DMG mode.
Prohibit the use of sections that doesn't exist on a DMG, such as WRAMX and VRAM bank 1. Prohibit the use of sections that doesn't exist on a DMG, such as VRAM bank 1.
This option automatically enables This option automatically enables
.Fl w . .Fl w .
.It Fl l Ar linker_script, Fl Fl linkerscript Ar linker_script .It Fl l Ar linker_script, Fl Fl linkerscript Ar linker_script
@@ -94,14 +94,24 @@ This option is ignored.
It was supposed to perform smart linking but fell into disrepair, and so has been removed. It was supposed to perform smart linking but fell into disrepair, and so has been removed.
It will be reimplemented at some point. It will be reimplemented at some point.
.It Fl t , Fl Fl tiny .It Fl t , Fl Fl tiny
Expand the ROM0 section size from 16 KiB to the full 32 KiB assigned to ROM and prohibit the use of ROMX sections. Expand the ROM0 section size from 16 KiB to the full 32 KiB assigned to ROM.
ROMX sections that are fixed to a bank other than 1 become errors, other ROMX sections are treated as ROM0.
Useful for ROMs that fit in 32 KiB. Useful for ROMs that fit in 32 KiB.
.It Fl V , Fl Fl version .It Fl V , Fl Fl version
Print the version of the program and exit. Print the version of the program and exit.
.It Fl v , Fl Fl verbose .It Fl v , Fl Fl verbose
Verbose: enable printing more information to standard error. Verbose: enable printing more information to standard error.
.It Fl w , Fl Fl wramx .It Fl w , Fl Fl wramx
Expand the WRAM0 section size from 4 KiB to the full 8 KiB assigned to WRAM and prohibit the use of WRAMX sections. Expand the WRAM0 section size from 4 KiB to the full 8 KiB assigned to WRAM.
WRAMX sections that are fixed to a bank other than 1 become errors, other WRAMX sections are treated as WRAM0.
.It Fl x , Fl Fl nopad
Disables padding the end of the final file.
This option automatically enables
.Fl t .
You can use this when not not making a ROM.
When making a ROM, be careful that not using this is not a replacement for
.Xr rgbfix 1 Ap s Fl p
option!
.El .El
.Sh EXAMPLES .Sh EXAMPLES
All you need for a basic ROM is an object file, which can be made into a ROM image like so: All you need for a basic ROM is an object file, which can be made into a ROM image like so:

View File

@@ -6,11 +6,12 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <stdlib.h> #include <ctype.h>
#include <inttypes.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <ctype.h>
#include "link/main.h" #include "link/main.h"
#include "link/script.h" #include "link/script.h"
@@ -19,22 +20,23 @@
#include "extern/err.h" #include "extern/err.h"
FILE * linkerScript; FILE * linkerScript;
char *includeFileName;
static uint32_t lineNo; static uint32_t lineNo;
static struct { static struct {
FILE *file; FILE *file;
uint32_t lineNo; uint32_t lineNo;
char const *name; char *name;
} *fileStack; } *fileStack;
static uint32_t fileStackSize; static uint32_t fileStackSize;
static uint32_t fileStackIndex; static uint32_t fileStackIndex;
static void pushFile(char const *newFileName) static void pushFile(char *newFileName)
{ {
if (fileStackIndex == UINT32_MAX) if (fileStackIndex == UINT32_MAX)
errx(1, "%s(%u): INCLUDE recursion limit reached", errx(1, "%s(%" PRIu32 "): INCLUDE recursion limit reached",
linkerScriptName, lineNo); linkerScriptName, lineNo);
if (fileStackIndex == fileStackSize) { if (fileStackIndex == fileStackSize) {
@@ -44,7 +46,7 @@ static void pushFile(char const *newFileName)
fileStack = realloc(fileStack, fileStack = realloc(fileStack,
sizeof(*fileStack) * fileStackSize); sizeof(*fileStack) * fileStackSize);
if (!fileStack) if (!fileStack)
err(1, "%s(%u): Internal INCLUDE error", err(1, "%s(%" PRIu32 "): Internal INCLUDE error",
linkerScriptName, lineNo); linkerScriptName, lineNo);
} }
@@ -55,7 +57,7 @@ static void pushFile(char const *newFileName)
linkerScript = fopen(newFileName, "r"); linkerScript = fopen(newFileName, "r");
if (!linkerScript) if (!linkerScript)
err(1, "%s(%u): Could not open \"%s\"", err(1, "%s(%" PRIu32 "): Could not open \"%s\"",
linkerScriptName, lineNo, newFileName); linkerScriptName, lineNo, newFileName);
lineNo = 1; lineNo = 1;
linkerScriptName = newFileName; linkerScriptName = newFileName;
@@ -66,6 +68,8 @@ static bool popFile(void)
if (!fileStackIndex) if (!fileStackIndex)
return false; return false;
free(linkerScriptName);
fileStackIndex--; fileStackIndex--;
linkerScript = fileStack[fileStackIndex].file; linkerScript = fileStack[fileStackIndex].file;
lineNo = fileStack[fileStackIndex].lineNo; lineNo = fileStack[fileStackIndex].lineNo;
@@ -174,12 +178,12 @@ static int readChar(FILE *file)
int curchar = getc(file); int curchar = getc(file);
if (curchar == EOF && ferror(file)) if (curchar == EOF && ferror(file))
err(1, "%s(%u): Unexpected error in %s", linkerScriptName, err(1, "%s(%" PRIu32 "): Unexpected error in %s",
lineNo, __func__); linkerScriptName, lineNo, __func__);
return curchar; return curchar;
} }
static struct LinkerScriptToken const *nextToken(void) static struct LinkerScriptToken *nextToken(void)
{ {
static struct LinkerScriptToken token; static struct LinkerScriptToken token;
int curchar; int curchar;
@@ -220,7 +224,7 @@ static struct LinkerScriptToken const *nextToken(void)
do { do {
curchar = readChar(linkerScript); curchar = readChar(linkerScript);
if (curchar == EOF || isNewline(curchar)) if (curchar == EOF || isNewline(curchar))
errx(1, "%s(%u): Unterminated string", errx(1, "%s(%" PRIu32 "): Unterminated string",
linkerScriptName, lineNo); linkerScriptName, lineNo);
else if (curchar == '"') else if (curchar == '"')
/* Quotes force a string termination */ /* Quotes force a string termination */
@@ -299,7 +303,7 @@ static struct LinkerScriptToken const *nextToken(void)
if (tryParseNumber(str, &token.attr.number)) if (tryParseNumber(str, &token.attr.number))
token.type = TOKEN_NUMBER; token.type = TOKEN_NUMBER;
else else
errx(1, "%s(%u): Unknown token \"%s\"", errx(1, "%s(%" PRIu32 "): Unknown token \"%s\"",
linkerScriptName, lineNo, str); linkerScriptName, lineNo, str);
} }
@@ -327,7 +331,7 @@ static void processCommand(enum LinkerScriptCommand command, uint16_t arg,
} }
if (arg < *pc) if (arg < *pc)
errx(1, "%s(%u): `%s` cannot be used to go backwards", errx(1, "%s(%" PRIu32 "): `%s` cannot be used to go backwards",
linkerScriptName, lineNo, commands[command]); linkerScriptName, lineNo, commands[command]);
*pc = arg; *pc = arg;
} }
@@ -368,7 +372,7 @@ struct SectionPlacement *script_NextSection(void)
} }
for (;;) { for (;;) {
struct LinkerScriptToken const *token = nextToken(); struct LinkerScriptToken *token = nextToken();
enum LinkerScriptTokenType tokType; enum LinkerScriptTokenType tokType;
union LinkerScriptTokenAttr attr; union LinkerScriptTokenAttr attr;
bool hasArg; bool hasArg;
@@ -376,11 +380,11 @@ struct SectionPlacement *script_NextSection(void)
if (type != SECTTYPE_INVALID) { if (type != SECTTYPE_INVALID) {
if (curaddr[type][bankID] > endaddr(type) + 1) if (curaddr[type][bankID] > endaddr(type) + 1)
errx(1, "%s(%u): Sections would extend past the end of %s ($%04hx > $%04hx)", errx(1, "%s(%" PRIu32 "): Sections would extend past the end of %s ($%04" PRIx16 " > $%04" PRIx16 ")",
linkerScriptName, lineNo, typeNames[type], linkerScriptName, lineNo, typeNames[type],
curaddr[type][bankID], endaddr(type)); curaddr[type][bankID], endaddr(type));
if (curaddr[type][bankID] < startaddr[type]) if (curaddr[type][bankID] < startaddr[type])
errx(1, "%s(%u): PC underflowed ($%04hx < $%04hx)", errx(1, "%s(%" PRIu32 "): PC underflowed ($%04" PRIx16 " < $%04" PRIx16 ")",
linkerScriptName, lineNo, linkerScriptName, lineNo,
curaddr[type][bankID], startaddr[type]); curaddr[type][bankID], startaddr[type]);
} }
@@ -401,7 +405,7 @@ struct SectionPlacement *script_NextSection(void)
break; break;
case TOKEN_NUMBER: case TOKEN_NUMBER:
errx(1, "%s(%u): stray number \"%u\"", errx(1, "%s(%" PRIu32 "): stray number \"%" PRIu32 "\"",
linkerScriptName, lineNo, linkerScriptName, lineNo,
token->attr.number); token->attr.number);
@@ -414,13 +418,13 @@ struct SectionPlacement *script_NextSection(void)
parserState = PARSER_LINEEND; parserState = PARSER_LINEEND;
if (type == SECTTYPE_INVALID) if (type == SECTTYPE_INVALID)
errx(1, "%s(%u): Didn't specify a location before the section", errx(1, "%s(%" PRIu32 "): Didn't specify a location before the section",
linkerScriptName, lineNo); linkerScriptName, lineNo);
section.section = section.section =
sect_GetSection(token->attr.string); sect_GetSection(token->attr.string);
if (!section.section) if (!section.section)
errx(1, "%s(%u): Unknown section \"%s\"", errx(1, "%s(%" PRIu32 "): Unknown section \"%s\"",
linkerScriptName, lineNo, linkerScriptName, lineNo,
token->attr.string); token->attr.string);
section.org = curaddr[type][bankID]; section.org = curaddr[type][bankID];
@@ -449,10 +453,10 @@ struct SectionPlacement *script_NextSection(void)
if (tokType == TOKEN_COMMAND) { if (tokType == TOKEN_COMMAND) {
if (type == SECTTYPE_INVALID) if (type == SECTTYPE_INVALID)
errx(1, "%s(%u): Didn't specify a location before the command", errx(1, "%s(%" PRIu32 "): Didn't specify a location before the command",
linkerScriptName, lineNo); linkerScriptName, lineNo);
if (!hasArg) if (!hasArg)
errx(1, "%s(%u): Command specified without an argument", errx(1, "%s(%" PRIu32 "): Command specified without an argument",
linkerScriptName, lineNo); linkerScriptName, lineNo);
processCommand(attr.command, arg, processCommand(attr.command, arg,
@@ -464,16 +468,16 @@ struct SectionPlacement *script_NextSection(void)
* specifying the number is optional. * specifying the number is optional.
*/ */
if (!hasArg && nbbanks(type) != 1) if (!hasArg && nbbanks(type) != 1)
errx(1, "%s(%u): Didn't specify a bank number", errx(1, "%s(%" PRIu32 "): Didn't specify a bank number",
linkerScriptName, lineNo); linkerScriptName, lineNo);
else if (!hasArg) else if (!hasArg)
arg = bankranges[type][0]; arg = bankranges[type][0];
else if (arg < bankranges[type][0]) else if (arg < bankranges[type][0])
errx(1, "%s(%u): specified bank number is too low (%u < %u)", errx(1, "%s(%" PRIu32 "): specified bank number is too low (%" PRIu32 " < %" PRIu32 ")",
linkerScriptName, lineNo, linkerScriptName, lineNo,
arg, bankranges[type][0]); arg, bankranges[type][0]);
else if (arg > bankranges[type][1]) else if (arg > bankranges[type][1])
errx(1, "%s(%u): specified bank number is too high (%u > %u)", errx(1, "%s(%" PRIu32 "): specified bank number is too high (%" PRIu32 " > %" PRIu32 ")",
linkerScriptName, lineNo, linkerScriptName, lineNo,
arg, bankranges[type][1]); arg, bankranges[type][1]);
bank = arg; bank = arg;
@@ -493,11 +497,13 @@ struct SectionPlacement *script_NextSection(void)
case PARSER_INCLUDE: case PARSER_INCLUDE:
if (token->type != TOKEN_STRING) if (token->type != TOKEN_STRING)
errx(1, "%s(%u): Expected a file name after INCLUDE", errx(1, "%s(%" PRIu32 "): Expected a file name after INCLUDE",
linkerScriptName, lineNo); linkerScriptName, lineNo);
/* Switch to that file */ /* Switch to that file */
pushFile(token->attr.string); pushFile(token->attr.string);
/* The file stack took ownership of the string */
token->attr.string = NULL;
parserState = PARSER_LINESTART; parserState = PARSER_LINESTART;
break; break;
@@ -511,7 +517,7 @@ lineend:
return NULL; return NULL;
parserState = PARSER_LINEEND; parserState = PARSER_LINEEND;
} else if (token->type != TOKEN_NEWLINE) } else if (token->type != TOKEN_NEWLINE)
errx(1, "%s(%u): Unexpected %s at the end of the line", errx(1, "%s(%" PRIu32 "): Unexpected %s at the end of the line",
linkerScriptName, lineNo, linkerScriptName, lineNo,
tokenTypes[token->type]); tokenTypes[token->type]);
break; break;

View File

@@ -6,7 +6,10 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <inttypes.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "link/main.h" #include "link/main.h"
#include "link/section.h" #include "link/section.h"
@@ -36,7 +39,7 @@ void sect_ForEach(void (*callback)(struct Section *, void *), void *arg)
hash_ForEach(sections, forEach, &callbackArg); hash_ForEach(sections, forEach, &callbackArg);
} }
static void mergeSections(struct Section *target, struct Section *other) static void mergeSections(struct Section *target, struct Section *other, enum SectionModifier mod)
{ {
if (target->type != other->type) if (target->type != other->type)
errx(1, "Section \"%s\" is defined with conflicting types %s and %s", errx(1, "Section \"%s\" is defined with conflicting types %s and %s",
@@ -45,24 +48,31 @@ static void mergeSections(struct Section *target, struct Section *other)
if (other->isAddressFixed) { if (other->isAddressFixed) {
if (target->isAddressFixed) { if (target->isAddressFixed) {
if (target->org != other->org) if (target->org != other->org)
errx(1, "Section \"%s\" is defined with conflicting addresses $%x and $%x", errx(1, "Section \"%s\" is defined with conflicting addresses $%" PRIx16 " and $%" PRIx16,
other->name, target->org, other->org); other->name, target->org, other->org);
} else if (target->isAlignFixed) { } else if (target->isAlignFixed) {
if (other->org & target->alignMask) if ((other->org - target->alignOfs) & target->alignMask)
errx(1, "Section \"%s\" is defined with conflicting %u-byte alignment and address $%x", errx(1, "Section \"%s\" is defined with conflicting %" PRIu16 "-byte alignment (offset %" PRIu16 ") and address $%" PRIx16,
other->name, target->alignMask + 1, other->name, target->alignMask + 1,
other->org); target->alignOfs, other->org);
} }
target->isAddressFixed = true; target->isAddressFixed = true;
target->org = other->org; target->org = other->org;
} else if (other->isAlignFixed) { } else if (other->isAlignFixed) {
if (target->isAddressFixed) { if (target->isAddressFixed) {
if (target->org & other->alignMask) if ((target->org - other->alignOfs) & other->alignMask)
errx(1, "Section \"%s\" is defined with conflicting address $%x and %u-byte alignment", errx(1, "Section \"%s\" is defined with conflicting address $%" PRIx16 " and %" PRIu16 "-byte alignment (offset %" PRIu16 ")",
other->name, target->org, other->name, target->org,
other->alignMask + 1); other->alignMask + 1, other->alignOfs);
} else if (target->isAlignFixed
&& (other->alignMask & target->alignOfs)
!= (target->alignMask & other->alignOfs)) {
errx(1, "Section \"%s\" is defined with conflicting %" PRIu16 "-byte alignment (offset %" PRIu16 ") and %" PRIu16 "-byte alignment (offset %" PRIu16 ")",
other->name, target->alignMask + 1,
target->alignOfs, other->alignMask + 1,
other->alignOfs);
} else if (!target->isAlignFixed } else if (!target->isAlignFixed
|| other->alignMask > target->alignMask) { || (other->alignMask > target->alignMask)) {
target->isAlignFixed = true; target->isAlignFixed = true;
target->alignMask = other->alignMask; target->alignMask = other->alignMask;
} }
@@ -73,14 +83,35 @@ static void mergeSections(struct Section *target, struct Section *other)
target->isBankFixed = true; target->isBankFixed = true;
target->bank = other->bank; target->bank = other->bank;
} else if (target->bank != other->bank) { } else if (target->bank != other->bank) {
errx(1, "Section \"%s\" is defined with conflicting banks %u and %u", errx(1, "Section \"%s\" is defined with conflicting banks %" PRIu32 " and %" PRIu32,
other->name, target->bank, other->bank); other->name, target->bank, other->bank);
} }
} }
switch (mod) {
case SECTION_UNION:
if (other->size > target->size) if (other->size > target->size)
target->size = other->size; target->size = other->size;
break;
case SECTION_FRAGMENT:
target->size += other->size;
other->offset = target->size - other->size;
if (sect_HasData(target->type)) {
/* Ensure we're not allocating 0 bytes */
target->data = realloc(target->data,
sizeof(*target->data) * target->size + 1);
if (!target->data)
errx(1, "Failed to concatenate \"%s\"'s fragments", target->name);
memcpy(target->data + target->size - other->size, other->data, other->size);
}
break;
case SECTION_NORMAL:
trap_;
}
other->nextu = target->nextu;
target->nextu = other; target->nextu = other;
} }
@@ -90,16 +121,14 @@ void sect_AddSection(struct Section *section)
struct Section *other = hash_GetElement(sections, section->name); struct Section *other = hash_GetElement(sections, section->name);
if (other) { if (other) {
if (other->isUnion && section->isUnion) { if (section->modifier != other->modifier)
mergeSections(other, section); errx(1, "Section \"%s\" defined as %s and %s", section->name,
} else if (section->isUnion || other->isUnion) { sectionModNames[section->modifier], sectionModNames[other->modifier]);
errx(1, "Section \"%s\" defined as both unionized and not", else if (section->modifier == SECTION_NORMAL)
section->name); errx(1, "Section name \"%s\" is already in use", section->name);
} else { else
errx(1, "Section name \"%s\" is already in use", mergeSections(other, section, section->modifier);
section->name); } else if (section->modifier == SECTION_UNION && sect_HasData(section->type)) {
}
} else if (section->isUnion && sect_HasData(section->type)) {
errx(1, "Section \"%s\" is of type %s, which cannot be unionized", errx(1, "Section \"%s\" is of type %s, which cannot be unionized",
section->name, typeNames[section->type]); section->name, typeNames[section->type]);
} else { } else {
@@ -164,7 +193,7 @@ static void doSanityChecks(struct Section *section, void *ptr)
/* Too large an alignment may not be satisfiable */ /* Too large an alignment may not be satisfiable */
if (section->isAlignFixed if (section->isAlignFixed
&& (section->alignMask & startaddr[section->type])) && (section->alignMask & startaddr[section->type]))
fail("%s: %s sections cannot be aligned to $%x bytes", fail("%s: %s sections cannot be aligned to $%" PRIx16 " bytes",
section->name, typeNames[section->type], section->name, typeNames[section->type],
section->alignMask + 1); section->alignMask + 1);
@@ -174,13 +203,13 @@ static void doSanityChecks(struct Section *section, void *ptr)
if (section->isBankFixed && section->bank < minbank if (section->isBankFixed && section->bank < minbank
&& section->bank > maxbank) && section->bank > maxbank)
fail(minbank == maxbank fail(minbank == maxbank
? "Cannot place section \"%s\" in bank %d, it must be %d" ? "Cannot place section \"%s\" in bank %" PRIu32 ", it must be %" PRIu32
: "Cannot place section \"%s\" in bank %d, it must be between %d and %d", : "Cannot place section \"%s\" in bank %" PRIu32 ", it must be between %" PRIu32 " and %" PRIu32,
section->name, section->bank, minbank, maxbank); section->name, section->bank, minbank, maxbank);
/* Check if section has a chance to be placed */ /* Check if section has a chance to be placed */
if (section->size > maxsize[section->type]) if (section->size > maxsize[section->type])
fail("Section \"%s\" is bigger than the max size for that type: %#x > %#x", fail("Section \"%s\" is bigger than the max size for that type: %#" PRIx16 " > %#" PRIx16,
section->name, section->size, maxsize[section->type]); section->name, section->size, maxsize[section->type]);
/* Translate loose constraints to strong ones when they're equivalent */ /* Translate loose constraints to strong ones when they're equivalent */
@@ -211,12 +240,12 @@ static void doSanityChecks(struct Section *section, void *ptr)
/* Ensure the target address is valid */ /* Ensure the target address is valid */
if (section->org < startaddr[section->type] if (section->org < startaddr[section->type]
|| section->org > endaddr(section->type)) || section->org > endaddr(section->type))
fail("Section \"%s\"'s fixed address %#x is outside of range [%#x; %#x]", fail("Section \"%s\"'s fixed address %#" PRIx16 " is outside of range [%#" PRIx16 "; %#" PRIx16 "]",
section->name, section->org, section->name, section->org,
startaddr[section->type], endaddr(section->type)); startaddr[section->type], endaddr(section->type));
if (section->org + section->size > endaddr(section->type) + 1) if (section->org + section->size > endaddr(section->type) + 1)
fail("Section \"%s\"'s end address %#x is greater than last address %#x", fail("Section \"%s\"'s end address %#" PRIx16 " is greater than last address %#" PRIx16,
section->name, section->org + section->size, section->name, section->org + section->size,
endaddr(section->type) + 1); endaddr(section->type) + 1);
} }

View File

@@ -6,6 +6,7 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <inttypes.h>
#include <stdbool.h> #include <stdbool.h>
#include "link/symbol.h" #include "link/symbol.h"
@@ -40,7 +41,7 @@ void sym_AddSymbol(struct Symbol *symbol)
struct Symbol *other = hash_GetElement(symbols, symbol->name); struct Symbol *other = hash_GetElement(symbols, symbol->name);
if (other) if (other)
errx(1, "\"%s\" both in %s from %s(%d) and in %s from %s(%d)", errx(1, "\"%s\" both in %s from %s(%" PRId32 ") and in %s from %s(%" PRId32 ")",
symbol->name, symbol->name,
symbol->objFileName, symbol->fileName, symbol->lineNo, symbol->objFileName, symbol->fileName, symbol->lineNo,
other->objFileName, other->fileName, other->lineNo); other->objFileName, other->fileName, other->lineNo);

View File

@@ -44,3 +44,9 @@ char const * const typeNames[] = {
[SECTTYPE_OAM] = "OAM", [SECTTYPE_OAM] = "OAM",
[SECTTYPE_HRAM] = "HRAM" [SECTTYPE_HRAM] = "HRAM"
}; };
char const * const sectionModNames[] = {
[SECTION_NORMAL] = "regular",
[SECTION_UNION] = "union",
[SECTION_FRAGMENT] = "fragment",
};

View File

@@ -92,8 +92,10 @@ REPT NumberOfSections
; decide (floating bank). This field is only valid for ROMX, ; decide (floating bank). This field is only valid for ROMX,
; VRAM, WRAMX and SRAM sections. ; VRAM, WRAMX and SRAM sections.
LONG Align ; Alignment of this section, expressed as 1 << align. 1 if BYTE Align ; Alignment of this section, as N bits. 0 when not specified.
; not specified.
LONG Ofs ; Offset relative to the alignment specified above.
; Must be below 1 << Align.
IF (Type == ROMX) || (Type == ROM0) ; Sections that can contain data. IF (Type == ROMX) || (Type == ROM0) ; Sections that can contain data.
@@ -101,8 +103,6 @@ REPT NumberOfSections
LONG NumberOfPatches ; Number of patches to apply. LONG NumberOfPatches ; Number of patches to apply.
; These types of sections may have patches
REPT NumberOfPatches REPT NumberOfPatches
STRING SourceFile ; Name of the source file (for printing error STRING SourceFile ; Name of the source file (for printing error
@@ -111,6 +111,16 @@ REPT NumberOfSections
LONG Offset ; Offset into the section where patch should LONG Offset ; Offset into the section where patch should
; be applied (in bytes). ; be applied (in bytes).
LONG PCSectionID ; Index within the file of the section in which
; PC is located.
; This is usually the same section that the
; patch should be applied into, except e.g.
; with LOAD blocks.
LONG PCOffset ; PC's offset into the above section.
; Used because the section may be floating, so
; PC's value is not known to RGBASM.
BYTE Type ; 0 = BYTE patch. BYTE Type ; 0 = BYTE patch.
; 1 = little endian WORD patch. ; 1 = little endian WORD patch.
; 2 = little endian LONG patch. ; 2 = little endian LONG patch.
@@ -137,6 +147,13 @@ REPT NumberOfAssertions
LONG Offset ; Offset into the section where the assertion is located. LONG Offset ; Offset into the section where the assertion is located.
LONG SectionID ; Index within the file of the section in which PC is
; located, or -1 if defined outside a section.
LONG PCOffset ; PC's offset into the above section.
; Used because the section may be floating, so PC's value
; is not known to RGBASM.
BYTE Type ; 0 = Prints the message but allows linking to continue BYTE Type ; 0 = Prints the message but allows linking to continue
; 1 = Prints the message and evaluates other assertions, ; 1 = Prints the message and evaluates other assertions,
; but linking fails afterwards ; but linking fails afterwards
@@ -146,10 +163,6 @@ REPT NumberOfAssertions
BYTE RPN[RPNSize] ; RPN expression, same as patches. Assert fails if == 0. BYTE RPN[RPNSize] ; RPN expression, same as patches. Assert fails if == 0.
LONG SectionID ; The section number (of this object file) in which this
; assert is defined. If it doesn't belong to any specific
; section (like a constant), this field has the value -1.
STRING Message ; A message displayed when the assert fails. If set to STRING Message ; A message displayed when the assert fails. If set to
; the empty string, a generic message is printed instead. ; the empty string, a generic message is printed instead.
@@ -182,7 +195,7 @@ with some bytes being special prefixes for integers and symbols.
.It Li $13 Ta Li unary ~ .It Li $13 Ta Li unary ~
.It Li $21 Ta Li && comparison .It Li $21 Ta Li && comparison
.It Li $22 Ta Li || comparison .It Li $22 Ta Li || comparison
.It Li $23 Ta Li unary ! .It Li $23 Ta Li unary\ !
.It Li $30 Ta Li == comparison .It Li $30 Ta Li == comparison
.It Li $31 Ta Li != comparison .It Li $31 Ta Li != comparison
.It Li $32 Ta Li > comparison .It Li $32 Ta Li > comparison

22
test/asm/align-pc.asm Normal file
View File

@@ -0,0 +1,22 @@
SECTION "fixed org", SRAM[$BEAD]
ds 42
static_assert @ == $BED7
align 5,$17
; Should land at $0001
SECTION "align", ROM0,ALIGN[1,1]
db 69
align 1 ; This wants to go at $0000, $0002, $0004...
; Should land at $0003
SECTION "under-aligned", ROM0,ALIGN[1,1]
dw $BEEF
align 2,1 ; This wants to go at $0001, $0005, $0009...
; Should land at $0005
SECTION "forced align", ROM0
dw $DEAD
align 2,3 ; This wants to go at $0003, $0007, $000B...

0
test/asm/align-pc.err Normal file
View File

0
test/asm/align-pc.out Normal file
View File

View File

View File

@@ -0,0 +1 @@
assert BANK(@) == 1

View File

@@ -0,0 +1,3 @@
ERROR: assert-nosect-bank.asm(1):
PC has no bank outside a section
error: Assembly aborted (1 errors)!

View File

View File

@@ -1 +1 @@
SECTION "sec", ROMX[$/0] SECTION "sec", ROMX[1/0]

View File

@@ -1,4 +1,2 @@
ERROR: divzero-section-bank.asm(1):
Invalid integer constant
ERROR: divzero-section-bank.asm(1): ERROR: divzero-section-bank.asm(1):
Division by zero Division by zero

View File

@@ -0,0 +1,16 @@
SECTION "Empty Data Directive in ROM", ROM0
ds 1
ds 2
ds 3
ds 4
db
dw
dl
SECTION "Empty Data Directive in HRAM", HRAM
ds 1
ds 2
ds 3
ds 4
db
dw
dl

View File

@@ -0,0 +1,6 @@
warning: empty-data-directive.asm(6): [-Wempty-data-directive]
db/dw/dl directive without data in ROM
warning: empty-data-directive.asm(7): [-Wempty-data-directive]
db/dw/dl directive without data in ROM
warning: empty-data-directive.asm(8): [-Wempty-data-directive]
db/dw/dl directive without data in ROM

View File

8
test/asm/local-purge.asm Normal file
View File

@@ -0,0 +1,8 @@
; At some point, the name of the local label was passed *unexpanded* to the
; function doing the removal. Ensure that this is fixed.
SECTION "Test", ROM0[0]
Glob:
.loc
PURGE .loc
PRINTT "{.loc}\n" ; This should fail because the label doesn't exist anymore

3
test/asm/local-purge.err Normal file
View File

@@ -0,0 +1,3 @@
ERROR: local-purge.asm(8):
'.loc' not defined
error: Assembly aborted (1 errors)!

1
test/asm/local-purge.out Normal file
View File

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

View File

@@ -1,4 +1,5 @@
ERROR: macro-@.asm(1): ERROR: macro-@.asm(1):
Label "foo" created outside of a SECTION Label "foo" created outside of a SECTION
ERROR: macro-@.asm(1): ERROR: macro-@.asm(1):
Macro '@' not defined "@" is not a macro
error: Assembly aborted (2 errors)!

View File

@@ -0,0 +1,789 @@
addargs: MACRO
sum = 0
rept _NARG
sum = sum + \1
shift
endr
dw sum & $FFFF
dw (sum >> 16) & $FFFF
ENDM
SECTION "test", ROM0[0]
addargs 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, \
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, \
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, \
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, \
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, \
51, 52, 53, 54, 55, 56, 57, 58, 59, 60, \
61, 62, 63, 64, 65, 66, 67, 68, 69, 70, \
71, 72, 73, 74, 75, 76, 77, 78, 79, 80, \
81, 82, 83, 84, 85, 86, 87, 88, 89, 90, \
91, 92, 93, 94, 95, 96, 97, 98, 99, 100, \
101, 102, 103, 104, 105, 106, 107, 108, 109, 110, \
111, 112, 113, 114, 115, 116, 117, 118, 119, 120, \
121, 122, 123, 124, 125, 126, 127, 128, 129, 130, \
131, 132, 133, 134, 135, 136, 137, 138, 139, 140, \
141, 142, 143, 144, 145, 146, 147, 148, 149, 150, \
151, 152, 153, 154, 155, 156, 157, 158, 159, 160, \
161, 162, 163, 164, 165, 166, 167, 168, 169, 170, \
171, 172, 173, 174, 175, 176, 177, 178, 179, 180, \
181, 182, 183, 184, 185, 186, 187, 188, 189, 190, \
191, 192, 193, 194, 195, 196, 197, 198, 199, 200, \
201, 202, 203, 204, 205, 206, 207, 208, 209, 210, \
211, 212, 213, 214, 215, 216, 217, 218, 219, 220, \
221, 222, 223, 224, 225, 226, 227, 228, 229, 230, \
231, 232, 233, 234, 235, 236, 237, 238, 239, 240, \
241, 242, 243, 244, 245, 246, 247, 248, 249, 250, \
251, 252, 253, 254, 255, 256, 257, 258, 259, 260, \
261, 262, 263, 264, 265, 266, 267, 268, 269, 270, \
271, 272, 273, 274, 275, 276, 277, 278, 279, 280, \
281, 282, 283, 284, 285, 286, 287, 288, 289, 290, \
291, 292, 293, 294, 295, 296, 297, 298, 299, 300, \
301, 302, 303, 304, 305, 306, 307, 308, 309, 310, \
311, 312, 313, 314, 315, 316, 317, 318, 319, 320, \
321, 322, 323, 324, 325, 326, 327, 328, 329, 330, \
331, 332, 333, 334, 335, 336, 337, 338, 339, 340, \
341, 342, 343, 344, 345, 346, 347, 348, 349, 350, \
351, 352, 353, 354, 355, 356, 357, 358, 359, 360, \
361, 362, 363, 364, 365, 366, 367, 368, 369, 370, \
371, 372, 373, 374, 375, 376, 377, 378, 379, 380, \
381, 382, 383, 384, 385, 386, 387, 388, 389, 390, \
391, 392, 393, 394, 395, 396, 397, 398, 399, 400, \
401, 402, 403, 404, 405, 406, 407, 408, 409, 410, \
411, 412, 413, 414, 415, 416, 417, 418, 419, 420, \
421, 422, 423, 424, 425, 426, 427, 428, 429, 430, \
431, 432, 433, 434, 435, 436, 437, 438, 439, 440, \
441, 442, 443, 444, 445, 446, 447, 448, 449, 450, \
451, 452, 453, 454, 455, 456, 457, 458, 459, 460, \
461, 462, 463, 464, 465, 466, 467, 468, 469, 470, \
471, 472, 473, 474, 475, 476, 477, 478, 479, 480, \
481, 482, 483, 484, 485, 486, 487, 488, 489, 490, \
491, 492, 493, 494, 495, 496, 497, 498, 499, 500, \
501, 502, 503, 504, 505, 506, 507, 508, 509, 510, \
511, 512, 513, 514, 515, 516, 517, 518, 519, 520, \
521, 522, 523, 524, 525, 526, 527, 528, 529, 530, \
531, 532, 533, 534, 535, 536, 537, 538, 539, 540, \
541, 542, 543, 544, 545, 546, 547, 548, 549, 550, \
551, 552, 553, 554, 555, 556, 557, 558, 559, 560, \
561, 562, 563, 564, 565, 566, 567, 568, 569, 570, \
571, 572, 573, 574, 575, 576, 577, 578, 579, 580, \
581, 582, 583, 584, 585, 586, 587, 588, 589, 590, \
591, 592, 593, 594, 595, 596, 597, 598, 599, 600, \
601, 602, 603, 604, 605, 606, 607, 608, 609, 610, \
611, 612, 613, 614, 615, 616, 617, 618, 619, 620, \
621, 622, 623, 624, 625, 626, 627, 628, 629, 630, \
631, 632, 633, 634, 635, 636, 637, 638, 639, 640, \
641, 642, 643, 644, 645, 646, 647, 648, 649, 650, \
651, 652, 653, 654, 655, 656, 657, 658, 659, 660, \
661, 662, 663, 664, 665, 666, 667, 668, 669, 670, \
671, 672, 673, 674, 675, 676, 677, 678, 679, 680, \
681, 682, 683, 684, 685, 686, 687, 688, 689, 690, \
691, 692, 693, 694, 695, 696, 697, 698, 699, 700, \
701, 702, 703, 704, 705, 706, 707, 708, 709, 710, \
711, 712, 713, 714, 715, 716, 717, 718, 719, 720, \
721, 722, 723, 724, 725, 726, 727, 728, 729, 730, \
731, 732, 733, 734, 735, 736, 737, 738, 739, 740, \
741, 742, 743, 744, 745, 746, 747, 748, 749, 750, \
751, 752, 753, 754, 755, 756, 757, 758, 759, 760, \
761, 762, 763, 764, 765, 766, 767, 768, 769, 770, \
771, 772, 773, 774, 775, 776, 777, 778, 779, 780, \
781, 782, 783, 784, 785, 786, 787, 788, 789, 790, \
791, 792, 793, 794, 795, 796, 797, 798, 799, 800, \
801, 802, 803, 804, 805, 806, 807, 808, 809, 810, \
811, 812, 813, 814, 815, 816, 817, 818, 819, 820, \
821, 822, 823, 824, 825, 826, 827, 828, 829, 830, \
831, 832, 833, 834, 835, 836, 837, 838, 839, 840, \
841, 842, 843, 844, 845, 846, 847, 848, 849, 850, \
851, 852, 853, 854, 855, 856, 857, 858, 859, 860, \
861, 862, 863, 864, 865, 866, 867, 868, 869, 870, \
871, 872, 873, 874, 875, 876, 877, 878, 879, 880, \
881, 882, 883, 884, 885, 886, 887, 888, 889, 890, \
891, 892, 893, 894, 895, 896, 897, 898, 899, 900, \
901, 902, 903, 904, 905, 906, 907, 908, 909, 910, \
911, 912, 913, 914, 915, 916, 917, 918, 919, 920, \
921, 922, 923, 924, 925, 926, 927, 928, 929, 930, \
931, 932, 933, 934, 935, 936, 937, 938, 939, 940, \
941, 942, 943, 944, 945, 946, 947, 948, 949, 950, \
951, 952, 953, 954, 955, 956, 957, 958, 959, 960, \
961, 962, 963, 964, 965, 966, 967, 968, 969, 970, \
971, 972, 973, 974, 975, 976, 977, 978, 979, 980, \
981, 982, 983, 984, 985, 986, 987, 988, 989, 990, \
991, 992, 993, 994, 995, 996, 997, 998, 999, 1000, \
1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, \
1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, \
1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, \
1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, \
1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, \
1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, \
1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, \
1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, \
1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, \
1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, \
1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, \
1111, 1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119, 1120, \
1121, 1122, 1123, 1124, 1125, 1126, 1127, 1128, 1129, 1130, \
1131, 1132, 1133, 1134, 1135, 1136, 1137, 1138, 1139, 1140, \
1141, 1142, 1143, 1144, 1145, 1146, 1147, 1148, 1149, 1150, \
1151, 1152, 1153, 1154, 1155, 1156, 1157, 1158, 1159, 1160, \
1161, 1162, 1163, 1164, 1165, 1166, 1167, 1168, 1169, 1170, \
1171, 1172, 1173, 1174, 1175, 1176, 1177, 1178, 1179, 1180, \
1181, 1182, 1183, 1184, 1185, 1186, 1187, 1188, 1189, 1190, \
1191, 1192, 1193, 1194, 1195, 1196, 1197, 1198, 1199, 1200, \
1201, 1202, 1203, 1204, 1205, 1206, 1207, 1208, 1209, 1210, \
1211, 1212, 1213, 1214, 1215, 1216, 1217, 1218, 1219, 1220, \
1221, 1222, 1223, 1224, 1225, 1226, 1227, 1228, 1229, 1230, \
1231, 1232, 1233, 1234, 1235, 1236, 1237, 1238, 1239, 1240, \
1241, 1242, 1243, 1244, 1245, 1246, 1247, 1248, 1249, 1250, \
1251, 1252, 1253, 1254, 1255, 1256, 1257, 1258, 1259, 1260, \
1261, 1262, 1263, 1264, 1265, 1266, 1267, 1268, 1269, 1270, \
1271, 1272, 1273, 1274, 1275, 1276, 1277, 1278, 1279, 1280, \
1281, 1282, 1283, 1284, 1285, 1286, 1287, 1288, 1289, 1290, \
1291, 1292, 1293, 1294, 1295, 1296, 1297, 1298, 1299, 1300, \
1301, 1302, 1303, 1304, 1305, 1306, 1307, 1308, 1309, 1310, \
1311, 1312, 1313, 1314, 1315, 1316, 1317, 1318, 1319, 1320, \
1321, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329, 1330, \
1331, 1332, 1333, 1334, 1335, 1336, 1337, 1338, 1339, 1340, \
1341, 1342, 1343, 1344, 1345, 1346, 1347, 1348, 1349, 1350, \
1351, 1352, 1353, 1354, 1355, 1356, 1357, 1358, 1359, 1360, \
1361, 1362, 1363, 1364, 1365, 1366, 1367, 1368, 1369, 1370, \
1371, 1372, 1373, 1374, 1375, 1376, 1377, 1378, 1379, 1380, \
1381, 1382, 1383, 1384, 1385, 1386, 1387, 1388, 1389, 1390, \
1391, 1392, 1393, 1394, 1395, 1396, 1397, 1398, 1399, 1400, \
1401, 1402, 1403, 1404, 1405, 1406, 1407, 1408, 1409, 1410, \
1411, 1412, 1413, 1414, 1415, 1416, 1417, 1418, 1419, 1420, \
1421, 1422, 1423, 1424, 1425, 1426, 1427, 1428, 1429, 1430, \
1431, 1432, 1433, 1434, 1435, 1436, 1437, 1438, 1439, 1440, \
1441, 1442, 1443, 1444, 1445, 1446, 1447, 1448, 1449, 1450, \
1451, 1452, 1453, 1454, 1455, 1456, 1457, 1458, 1459, 1460, \
1461, 1462, 1463, 1464, 1465, 1466, 1467, 1468, 1469, 1470, \
1471, 1472, 1473, 1474, 1475, 1476, 1477, 1478, 1479, 1480, \
1481, 1482, 1483, 1484, 1485, 1486, 1487, 1488, 1489, 1490, \
1491, 1492, 1493, 1494, 1495, 1496, 1497, 1498, 1499, 1500, \
1501, 1502, 1503, 1504, 1505, 1506, 1507, 1508, 1509, 1510, \
1511, 1512, 1513, 1514, 1515, 1516, 1517, 1518, 1519, 1520, \
1521, 1522, 1523, 1524, 1525, 1526, 1527, 1528, 1529, 1530, \
1531, 1532, 1533, 1534, 1535, 1536, 1537, 1538, 1539, 1540, \
1541, 1542, 1543, 1544, 1545, 1546, 1547, 1548, 1549, 1550, \
1551, 1552, 1553, 1554, 1555, 1556, 1557, 1558, 1559, 1560, \
1561, 1562, 1563, 1564, 1565, 1566, 1567, 1568, 1569, 1570, \
1571, 1572, 1573, 1574, 1575, 1576, 1577, 1578, 1579, 1580, \
1581, 1582, 1583, 1584, 1585, 1586, 1587, 1588, 1589, 1590, \
1591, 1592, 1593, 1594, 1595, 1596, 1597, 1598, 1599, 1600, \
1601, 1602, 1603, 1604, 1605, 1606, 1607, 1608, 1609, 1610, \
1611, 1612, 1613, 1614, 1615, 1616, 1617, 1618, 1619, 1620, \
1621, 1622, 1623, 1624, 1625, 1626, 1627, 1628, 1629, 1630, \
1631, 1632, 1633, 1634, 1635, 1636, 1637, 1638, 1639, 1640, \
1641, 1642, 1643, 1644, 1645, 1646, 1647, 1648, 1649, 1650, \
1651, 1652, 1653, 1654, 1655, 1656, 1657, 1658, 1659, 1660, \
1661, 1662, 1663, 1664, 1665, 1666, 1667, 1668, 1669, 1670, \
1671, 1672, 1673, 1674, 1675, 1676, 1677, 1678, 1679, 1680, \
1681, 1682, 1683, 1684, 1685, 1686, 1687, 1688, 1689, 1690, \
1691, 1692, 1693, 1694, 1695, 1696, 1697, 1698, 1699, 1700, \
1701, 1702, 1703, 1704, 1705, 1706, 1707, 1708, 1709, 1710, \
1711, 1712, 1713, 1714, 1715, 1716, 1717, 1718, 1719, 1720, \
1721, 1722, 1723, 1724, 1725, 1726, 1727, 1728, 1729, 1730, \
1731, 1732, 1733, 1734, 1735, 1736, 1737, 1738, 1739, 1740, \
1741, 1742, 1743, 1744, 1745, 1746, 1747, 1748, 1749, 1750, \
1751, 1752, 1753, 1754, 1755, 1756, 1757, 1758, 1759, 1760, \
1761, 1762, 1763, 1764, 1765, 1766, 1767, 1768, 1769, 1770, \
1771, 1772, 1773, 1774, 1775, 1776, 1777, 1778, 1779, 1780, \
1781, 1782, 1783, 1784, 1785, 1786, 1787, 1788, 1789, 1790, \
1791, 1792, 1793, 1794, 1795, 1796, 1797, 1798, 1799, 1800, \
1801, 1802, 1803, 1804, 1805, 1806, 1807, 1808, 1809, 1810, \
1811, 1812, 1813, 1814, 1815, 1816, 1817, 1818, 1819, 1820, \
1821, 1822, 1823, 1824, 1825, 1826, 1827, 1828, 1829, 1830, \
1831, 1832, 1833, 1834, 1835, 1836, 1837, 1838, 1839, 1840, \
1841, 1842, 1843, 1844, 1845, 1846, 1847, 1848, 1849, 1850, \
1851, 1852, 1853, 1854, 1855, 1856, 1857, 1858, 1859, 1860, \
1861, 1862, 1863, 1864, 1865, 1866, 1867, 1868, 1869, 1870, \
1871, 1872, 1873, 1874, 1875, 1876, 1877, 1878, 1879, 1880, \
1881, 1882, 1883, 1884, 1885, 1886, 1887, 1888, 1889, 1890, \
1891, 1892, 1893, 1894, 1895, 1896, 1897, 1898, 1899, 1900, \
1901, 1902, 1903, 1904, 1905, 1906, 1907, 1908, 1909, 1910, \
1911, 1912, 1913, 1914, 1915, 1916, 1917, 1918, 1919, 1920, \
1921, 1922, 1923, 1924, 1925, 1926, 1927, 1928, 1929, 1930, \
1931, 1932, 1933, 1934, 1935, 1936, 1937, 1938, 1939, 1940, \
1941, 1942, 1943, 1944, 1945, 1946, 1947, 1948, 1949, 1950, \
1951, 1952, 1953, 1954, 1955, 1956, 1957, 1958, 1959, 1960, \
1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, 1969, 1970, \
1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, \
1981, 1982, 1983, 1984, 1985, 1986, 1987, 1988, 1989, 1990, \
1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, \
2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, \
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, \
2021, 2022, 2023, 2024, 2025, 2026, 2027, 2028, 2029, 2030, \
2031, 2032, 2033, 2034, 2035, 2036, 2037, 2038, 2039, 2040, \
2041, 2042, 2043, 2044, 2045, 2046, 2047, 2048, 2049, 2050, \
2051, 2052, 2053, 2054, 2055, 2056, 2057, 2058, 2059, 2060, \
2061, 2062, 2063, 2064, 2065, 2066, 2067, 2068, 2069, 2070, \
2071, 2072, 2073, 2074, 2075, 2076, 2077, 2078, 2079, 2080, \
2081, 2082, 2083, 2084, 2085, 2086, 2087, 2088, 2089, 2090, \
2091, 2092, 2093, 2094, 2095, 2096, 2097, 2098, 2099, 2100, \
2101, 2102, 2103, 2104, 2105, 2106, 2107, 2108, 2109, 2110, \
2111, 2112, 2113, 2114, 2115, 2116, 2117, 2118, 2119, 2120, \
2121, 2122, 2123, 2124, 2125, 2126, 2127, 2128, 2129, 2130, \
2131, 2132, 2133, 2134, 2135, 2136, 2137, 2138, 2139, 2140, \
2141, 2142, 2143, 2144, 2145, 2146, 2147, 2148, 2149, 2150, \
2151, 2152, 2153, 2154, 2155, 2156, 2157, 2158, 2159, 2160, \
2161, 2162, 2163, 2164, 2165, 2166, 2167, 2168, 2169, 2170, \
2171, 2172, 2173, 2174, 2175, 2176, 2177, 2178, 2179, 2180, \
2181, 2182, 2183, 2184, 2185, 2186, 2187, 2188, 2189, 2190, \
2191, 2192, 2193, 2194, 2195, 2196, 2197, 2198, 2199, 2200, \
2201, 2202, 2203, 2204, 2205, 2206, 2207, 2208, 2209, 2210, \
2211, 2212, 2213, 2214, 2215, 2216, 2217, 2218, 2219, 2220, \
2221, 2222, 2223, 2224, 2225, 2226, 2227, 2228, 2229, 2230, \
2231, 2232, 2233, 2234, 2235, 2236, 2237, 2238, 2239, 2240, \
2241, 2242, 2243, 2244, 2245, 2246, 2247, 2248, 2249, 2250, \
2251, 2252, 2253, 2254, 2255, 2256, 2257, 2258, 2259, 2260, \
2261, 2262, 2263, 2264, 2265, 2266, 2267, 2268, 2269, 2270, \
2271, 2272, 2273, 2274, 2275, 2276, 2277, 2278, 2279, 2280, \
2281, 2282, 2283, 2284, 2285, 2286, 2287, 2288, 2289, 2290, \
2291, 2292, 2293, 2294, 2295, 2296, 2297, 2298, 2299, 2300, \
2301, 2302, 2303, 2304, 2305, 2306, 2307, 2308, 2309, 2310, \
2311, 2312, 2313, 2314, 2315, 2316, 2317, 2318, 2319, 2320, \
2321, 2322, 2323, 2324, 2325, 2326, 2327, 2328, 2329, 2330, \
2331, 2332, 2333, 2334, 2335, 2336, 2337, 2338, 2339, 2340, \
2341, 2342, 2343, 2344, 2345, 2346, 2347, 2348, 2349, 2350, \
2351, 2352, 2353, 2354, 2355, 2356, 2357, 2358, 2359, 2360, \
2361, 2362, 2363, 2364, 2365, 2366, 2367, 2368, 2369, 2370, \
2371, 2372, 2373, 2374, 2375, 2376, 2377, 2378, 2379, 2380, \
2381, 2382, 2383, 2384, 2385, 2386, 2387, 2388, 2389, 2390, \
2391, 2392, 2393, 2394, 2395, 2396, 2397, 2398, 2399, 2400, \
2401, 2402, 2403, 2404, 2405, 2406, 2407, 2408, 2409, 2410, \
2411, 2412, 2413, 2414, 2415, 2416, 2417, 2418, 2419, 2420, \
2421, 2422, 2423, 2424, 2425, 2426, 2427, 2428, 2429, 2430, \
2431, 2432, 2433, 2434, 2435, 2436, 2437, 2438, 2439, 2440, \
2441, 2442, 2443, 2444, 2445, 2446, 2447, 2448, 2449, 2450, \
2451, 2452, 2453, 2454, 2455, 2456, 2457, 2458, 2459, 2460, \
2461, 2462, 2463, 2464, 2465, 2466, 2467, 2468, 2469, 2470, \
2471, 2472, 2473, 2474, 2475, 2476, 2477, 2478, 2479, 2480, \
2481, 2482, 2483, 2484, 2485, 2486, 2487, 2488, 2489, 2490, \
2491, 2492, 2493, 2494, 2495, 2496, 2497, 2498, 2499, 2500, \
2501, 2502, 2503, 2504, 2505, 2506, 2507, 2508, 2509, 2510, \
2511, 2512, 2513, 2514, 2515, 2516, 2517, 2518, 2519, 2520, \
2521, 2522, 2523, 2524, 2525, 2526, 2527, 2528, 2529, 2530, \
2531, 2532, 2533, 2534, 2535, 2536, 2537, 2538, 2539, 2540, \
2541, 2542, 2543, 2544, 2545, 2546, 2547, 2548, 2549, 2550, \
2551, 2552, 2553, 2554, 2555, 2556, 2557, 2558, 2559, 2560, \
2561, 2562, 2563, 2564, 2565, 2566, 2567, 2568, 2569, 2570, \
2571, 2572, 2573, 2574, 2575, 2576, 2577, 2578, 2579, 2580, \
2581, 2582, 2583, 2584, 2585, 2586, 2587, 2588, 2589, 2590, \
2591, 2592, 2593, 2594, 2595, 2596, 2597, 2598, 2599, 2600, \
2601, 2602, 2603, 2604, 2605, 2606, 2607, 2608, 2609, 2610, \
2611, 2612, 2613, 2614, 2615, 2616, 2617, 2618, 2619, 2620, \
2621, 2622, 2623, 2624, 2625, 2626, 2627, 2628, 2629, 2630, \
2631, 2632, 2633, 2634, 2635, 2636, 2637, 2638, 2639, 2640, \
2641, 2642, 2643, 2644, 2645, 2646, 2647, 2648, 2649, 2650, \
2651, 2652, 2653, 2654, 2655, 2656, 2657, 2658, 2659, 2660, \
2661, 2662, 2663, 2664, 2665, 2666, 2667, 2668, 2669, 2670, \
2671, 2672, 2673, 2674, 2675, 2676, 2677, 2678, 2679, 2680, \
2681, 2682, 2683, 2684, 2685, 2686, 2687, 2688, 2689, 2690, \
2691, 2692, 2693, 2694, 2695, 2696, 2697, 2698, 2699, 2700, \
2701, 2702, 2703, 2704, 2705, 2706, 2707, 2708, 2709, 2710, \
2711, 2712, 2713, 2714, 2715, 2716, 2717, 2718, 2719, 2720, \
2721, 2722, 2723, 2724, 2725, 2726, 2727, 2728, 2729, 2730, \
2731, 2732, 2733, 2734, 2735, 2736, 2737, 2738, 2739, 2740, \
2741, 2742, 2743, 2744, 2745, 2746, 2747, 2748, 2749, 2750, \
2751, 2752, 2753, 2754, 2755, 2756, 2757, 2758, 2759, 2760, \
2761, 2762, 2763, 2764, 2765, 2766, 2767, 2768, 2769, 2770, \
2771, 2772, 2773, 2774, 2775, 2776, 2777, 2778, 2779, 2780, \
2781, 2782, 2783, 2784, 2785, 2786, 2787, 2788, 2789, 2790, \
2791, 2792, 2793, 2794, 2795, 2796, 2797, 2798, 2799, 2800, \
2801, 2802, 2803, 2804, 2805, 2806, 2807, 2808, 2809, 2810, \
2811, 2812, 2813, 2814, 2815, 2816, 2817, 2818, 2819, 2820, \
2821, 2822, 2823, 2824, 2825, 2826, 2827, 2828, 2829, 2830, \
2831, 2832, 2833, 2834, 2835, 2836, 2837, 2838, 2839, 2840, \
2841, 2842, 2843, 2844, 2845, 2846, 2847, 2848, 2849, 2850, \
2851, 2852, 2853, 2854, 2855, 2856, 2857, 2858, 2859, 2860, \
2861, 2862, 2863, 2864, 2865, 2866, 2867, 2868, 2869, 2870, \
2871, 2872, 2873, 2874, 2875, 2876, 2877, 2878, 2879, 2880, \
2881, 2882, 2883, 2884, 2885, 2886, 2887, 2888, 2889, 2890, \
2891, 2892, 2893, 2894, 2895, 2896, 2897, 2898, 2899, 2900, \
2901, 2902, 2903, 2904, 2905, 2906, 2907, 2908, 2909, 2910, \
2911, 2912, 2913, 2914, 2915, 2916, 2917, 2918, 2919, 2920, \
2921, 2922, 2923, 2924, 2925, 2926, 2927, 2928, 2929, 2930, \
2931, 2932, 2933, 2934, 2935, 2936, 2937, 2938, 2939, 2940, \
2941, 2942, 2943, 2944, 2945, 2946, 2947, 2948, 2949, 2950, \
2951, 2952, 2953, 2954, 2955, 2956, 2957, 2958, 2959, 2960, \
2961, 2962, 2963, 2964, 2965, 2966, 2967, 2968, 2969, 2970, \
2971, 2972, 2973, 2974, 2975, 2976, 2977, 2978, 2979, 2980, \
2981, 2982, 2983, 2984, 2985, 2986, 2987, 2988, 2989, 2990, \
2991, 2992, 2993, 2994, 2995, 2996, 2997, 2998, 2999, 3000, \
3001, 3002, 3003, 3004, 3005, 3006, 3007, 3008, 3009, 3010, \
3011, 3012, 3013, 3014, 3015, 3016, 3017, 3018, 3019, 3020, \
3021, 3022, 3023, 3024, 3025, 3026, 3027, 3028, 3029, 3030, \
3031, 3032, 3033, 3034, 3035, 3036, 3037, 3038, 3039, 3040, \
3041, 3042, 3043, 3044, 3045, 3046, 3047, 3048, 3049, 3050, \
3051, 3052, 3053, 3054, 3055, 3056, 3057, 3058, 3059, 3060, \
3061, 3062, 3063, 3064, 3065, 3066, 3067, 3068, 3069, 3070, \
3071, 3072, 3073, 3074, 3075, 3076, 3077, 3078, 3079, 3080, \
3081, 3082, 3083, 3084, 3085, 3086, 3087, 3088, 3089, 3090, \
3091, 3092, 3093, 3094, 3095, 3096, 3097, 3098, 3099, 3100, \
3101, 3102, 3103, 3104, 3105, 3106, 3107, 3108, 3109, 3110, \
3111, 3112, 3113, 3114, 3115, 3116, 3117, 3118, 3119, 3120, \
3121, 3122, 3123, 3124, 3125, 3126, 3127, 3128, 3129, 3130, \
3131, 3132, 3133, 3134, 3135, 3136, 3137, 3138, 3139, 3140, \
3141, 3142, 3143, 3144, 3145, 3146, 3147, 3148, 3149, 3150, \
3151, 3152, 3153, 3154, 3155, 3156, 3157, 3158, 3159, 3160, \
3161, 3162, 3163, 3164, 3165, 3166, 3167, 3168, 3169, 3170, \
3171, 3172, 3173, 3174, 3175, 3176, 3177, 3178, 3179, 3180, \
3181, 3182, 3183, 3184, 3185, 3186, 3187, 3188, 3189, 3190, \
3191, 3192, 3193, 3194, 3195, 3196, 3197, 3198, 3199, 3200, \
3201, 3202, 3203, 3204, 3205, 3206, 3207, 3208, 3209, 3210, \
3211, 3212, 3213, 3214, 3215, 3216, 3217, 3218, 3219, 3220, \
3221, 3222, 3223, 3224, 3225, 3226, 3227, 3228, 3229, 3230, \
3231, 3232, 3233, 3234, 3235, 3236, 3237, 3238, 3239, 3240, \
3241, 3242, 3243, 3244, 3245, 3246, 3247, 3248, 3249, 3250, \
3251, 3252, 3253, 3254, 3255, 3256, 3257, 3258, 3259, 3260, \
3261, 3262, 3263, 3264, 3265, 3266, 3267, 3268, 3269, 3270, \
3271, 3272, 3273, 3274, 3275, 3276, 3277, 3278, 3279, 3280, \
3281, 3282, 3283, 3284, 3285, 3286, 3287, 3288, 3289, 3290, \
3291, 3292, 3293, 3294, 3295, 3296, 3297, 3298, 3299, 3300, \
3301, 3302, 3303, 3304, 3305, 3306, 3307, 3308, 3309, 3310, \
3311, 3312, 3313, 3314, 3315, 3316, 3317, 3318, 3319, 3320, \
3321, 3322, 3323, 3324, 3325, 3326, 3327, 3328, 3329, 3330, \
3331, 3332, 3333, 3334, 3335, 3336, 3337, 3338, 3339, 3340, \
3341, 3342, 3343, 3344, 3345, 3346, 3347, 3348, 3349, 3350, \
3351, 3352, 3353, 3354, 3355, 3356, 3357, 3358, 3359, 3360, \
3361, 3362, 3363, 3364, 3365, 3366, 3367, 3368, 3369, 3370, \
3371, 3372, 3373, 3374, 3375, 3376, 3377, 3378, 3379, 3380, \
3381, 3382, 3383, 3384, 3385, 3386, 3387, 3388, 3389, 3390, \
3391, 3392, 3393, 3394, 3395, 3396, 3397, 3398, 3399, 3400, \
3401, 3402, 3403, 3404, 3405, 3406, 3407, 3408, 3409, 3410, \
3411, 3412, 3413, 3414, 3415, 3416, 3417, 3418, 3419, 3420, \
3421, 3422, 3423, 3424, 3425, 3426, 3427, 3428, 3429, 3430, \
3431, 3432, 3433, 3434, 3435, 3436, 3437, 3438, 3439, 3440, \
3441, 3442, 3443, 3444, 3445, 3446, 3447, 3448, 3449, 3450, \
3451, 3452, 3453, 3454, 3455, 3456, 3457, 3458, 3459, 3460, \
3461, 3462, 3463, 3464, 3465, 3466, 3467, 3468, 3469, 3470, \
3471, 3472, 3473, 3474, 3475, 3476, 3477, 3478, 3479, 3480, \
3481, 3482, 3483, 3484, 3485, 3486, 3487, 3488, 3489, 3490, \
3491, 3492, 3493, 3494, 3495, 3496, 3497, 3498, 3499, 3500, \
3501, 3502, 3503, 3504, 3505, 3506, 3507, 3508, 3509, 3510, \
3511, 3512, 3513, 3514, 3515, 3516, 3517, 3518, 3519, 3520, \
3521, 3522, 3523, 3524, 3525, 3526, 3527, 3528, 3529, 3530, \
3531, 3532, 3533, 3534, 3535, 3536, 3537, 3538, 3539, 3540, \
3541, 3542, 3543, 3544, 3545, 3546, 3547, 3548, 3549, 3550, \
3551, 3552, 3553, 3554, 3555, 3556, 3557, 3558, 3559, 3560, \
3561, 3562, 3563, 3564, 3565, 3566, 3567, 3568, 3569, 3570, \
3571, 3572, 3573, 3574, 3575, 3576, 3577, 3578, 3579, 3580, \
3581, 3582, 3583, 3584, 3585, 3586, 3587, 3588, 3589, 3590, \
3591, 3592, 3593, 3594, 3595, 3596, 3597, 3598, 3599, 3600, \
3601, 3602, 3603, 3604, 3605, 3606, 3607, 3608, 3609, 3610, \
3611, 3612, 3613, 3614, 3615, 3616, 3617, 3618, 3619, 3620, \
3621, 3622, 3623, 3624, 3625, 3626, 3627, 3628, 3629, 3630, \
3631, 3632, 3633, 3634, 3635, 3636, 3637, 3638, 3639, 3640, \
3641, 3642, 3643, 3644, 3645, 3646, 3647, 3648, 3649, 3650, \
3651, 3652, 3653, 3654, 3655, 3656, 3657, 3658, 3659, 3660, \
3661, 3662, 3663, 3664, 3665, 3666, 3667, 3668, 3669, 3670, \
3671, 3672, 3673, 3674, 3675, 3676, 3677, 3678, 3679, 3680, \
3681, 3682, 3683, 3684, 3685, 3686, 3687, 3688, 3689, 3690, \
3691, 3692, 3693, 3694, 3695, 3696, 3697, 3698, 3699, 3700, \
3701, 3702, 3703, 3704, 3705, 3706, 3707, 3708, 3709, 3710, \
3711, 3712, 3713, 3714, 3715, 3716, 3717, 3718, 3719, 3720, \
3721, 3722, 3723, 3724, 3725, 3726, 3727, 3728, 3729, 3730, \
3731, 3732, 3733, 3734, 3735, 3736, 3737, 3738, 3739, 3740, \
3741, 3742, 3743, 3744, 3745, 3746, 3747, 3748, 3749, 3750, \
3751, 3752, 3753, 3754, 3755, 3756, 3757, 3758, 3759, 3760, \
3761, 3762, 3763, 3764, 3765, 3766, 3767, 3768, 3769, 3770, \
3771, 3772, 3773, 3774, 3775, 3776, 3777, 3778, 3779, 3780, \
3781, 3782, 3783, 3784, 3785, 3786, 3787, 3788, 3789, 3790, \
3791, 3792, 3793, 3794, 3795, 3796, 3797, 3798, 3799, 3800, \
3801, 3802, 3803, 3804, 3805, 3806, 3807, 3808, 3809, 3810, \
3811, 3812, 3813, 3814, 3815, 3816, 3817, 3818, 3819, 3820, \
3821, 3822, 3823, 3824, 3825, 3826, 3827, 3828, 3829, 3830, \
3831, 3832, 3833, 3834, 3835, 3836, 3837, 3838, 3839, 3840, \
3841, 3842, 3843, 3844, 3845, 3846, 3847, 3848, 3849, 3850, \
3851, 3852, 3853, 3854, 3855, 3856, 3857, 3858, 3859, 3860, \
3861, 3862, 3863, 3864, 3865, 3866, 3867, 3868, 3869, 3870, \
3871, 3872, 3873, 3874, 3875, 3876, 3877, 3878, 3879, 3880, \
3881, 3882, 3883, 3884, 3885, 3886, 3887, 3888, 3889, 3890, \
3891, 3892, 3893, 3894, 3895, 3896, 3897, 3898, 3899, 3900, \
3901, 3902, 3903, 3904, 3905, 3906, 3907, 3908, 3909, 3910, \
3911, 3912, 3913, 3914, 3915, 3916, 3917, 3918, 3919, 3920, \
3921, 3922, 3923, 3924, 3925, 3926, 3927, 3928, 3929, 3930, \
3931, 3932, 3933, 3934, 3935, 3936, 3937, 3938, 3939, 3940, \
3941, 3942, 3943, 3944, 3945, 3946, 3947, 3948, 3949, 3950, \
3951, 3952, 3953, 3954, 3955, 3956, 3957, 3958, 3959, 3960, \
3961, 3962, 3963, 3964, 3965, 3966, 3967, 3968, 3969, 3970, \
3971, 3972, 3973, 3974, 3975, 3976, 3977, 3978, 3979, 3980, \
3981, 3982, 3983, 3984, 3985, 3986, 3987, 3988, 3989, 3990, \
3991, 3992, 3993, 3994, 3995, 3996, 3997, 3998, 3999, 4000, \
4001, 4002, 4003, 4004, 4005, 4006, 4007, 4008, 4009, 4010, \
4011, 4012, 4013, 4014, 4015, 4016, 4017, 4018, 4019, 4020, \
4021, 4022, 4023, 4024, 4025, 4026, 4027, 4028, 4029, 4030, \
4031, 4032, 4033, 4034, 4035, 4036, 4037, 4038, 4039, 4040, \
4041, 4042, 4043, 4044, 4045, 4046, 4047, 4048, 4049, 4050, \
4051, 4052, 4053, 4054, 4055, 4056, 4057, 4058, 4059, 4060, \
4061, 4062, 4063, 4064, 4065, 4066, 4067, 4068, 4069, 4070, \
4071, 4072, 4073, 4074, 4075, 4076, 4077, 4078, 4079, 4080, \
4081, 4082, 4083, 4084, 4085, 4086, 4087, 4088, 4089, 4090, \
4091, 4092, 4093, 4094, 4095, 4096, 4097, 4098, 4099, 4100, \
4101, 4102, 4103, 4104, 4105, 4106, 4107, 4108, 4109, 4110, \
4111, 4112, 4113, 4114, 4115, 4116, 4117, 4118, 4119, 4120, \
4121, 4122, 4123, 4124, 4125, 4126, 4127, 4128, 4129, 4130, \
4131, 4132, 4133, 4134, 4135, 4136, 4137, 4138, 4139, 4140, \
4141, 4142, 4143, 4144, 4145, 4146, 4147, 4148, 4149, 4150, \
4151, 4152, 4153, 4154, 4155, 4156, 4157, 4158, 4159, 4160, \
4161, 4162, 4163, 4164, 4165, 4166, 4167, 4168, 4169, 4170, \
4171, 4172, 4173, 4174, 4175, 4176, 4177, 4178, 4179, 4180, \
4181, 4182, 4183, 4184, 4185, 4186, 4187, 4188, 4189, 4190, \
4191, 4192, 4193, 4194, 4195, 4196, 4197, 4198, 4199, 4200, \
4201, 4202, 4203, 4204, 4205, 4206, 4207, 4208, 4209, 4210, \
4211, 4212, 4213, 4214, 4215, 4216, 4217, 4218, 4219, 4220, \
4221, 4222, 4223, 4224, 4225, 4226, 4227, 4228, 4229, 4230, \
4231, 4232, 4233, 4234, 4235, 4236, 4237, 4238, 4239, 4240, \
4241, 4242, 4243, 4244, 4245, 4246, 4247, 4248, 4249, 4250, \
4251, 4252, 4253, 4254, 4255, 4256, 4257, 4258, 4259, 4260, \
4261, 4262, 4263, 4264, 4265, 4266, 4267, 4268, 4269, 4270, \
4271, 4272, 4273, 4274, 4275, 4276, 4277, 4278, 4279, 4280, \
4281, 4282, 4283, 4284, 4285, 4286, 4287, 4288, 4289, 4290, \
4291, 4292, 4293, 4294, 4295, 4296, 4297, 4298, 4299, 4300, \
4301, 4302, 4303, 4304, 4305, 4306, 4307, 4308, 4309, 4310, \
4311, 4312, 4313, 4314, 4315, 4316, 4317, 4318, 4319, 4320, \
4321, 4322, 4323, 4324, 4325, 4326, 4327, 4328, 4329, 4330, \
4331, 4332, 4333, 4334, 4335, 4336, 4337, 4338, 4339, 4340, \
4341, 4342, 4343, 4344, 4345, 4346, 4347, 4348, 4349, 4350, \
4351, 4352, 4353, 4354, 4355, 4356, 4357, 4358, 4359, 4360, \
4361, 4362, 4363, 4364, 4365, 4366, 4367, 4368, 4369, 4370, \
4371, 4372, 4373, 4374, 4375, 4376, 4377, 4378, 4379, 4380, \
4381, 4382, 4383, 4384, 4385, 4386, 4387, 4388, 4389, 4390, \
4391, 4392, 4393, 4394, 4395, 4396, 4397, 4398, 4399, 4400, \
4401, 4402, 4403, 4404, 4405, 4406, 4407, 4408, 4409, 4410, \
4411, 4412, 4413, 4414, 4415, 4416, 4417, 4418, 4419, 4420, \
4421, 4422, 4423, 4424, 4425, 4426, 4427, 4428, 4429, 4430, \
4431, 4432, 4433, 4434, 4435, 4436, 4437, 4438, 4439, 4440, \
4441, 4442, 4443, 4444, 4445, 4446, 4447, 4448, 4449, 4450, \
4451, 4452, 4453, 4454, 4455, 4456, 4457, 4458, 4459, 4460, \
4461, 4462, 4463, 4464, 4465, 4466, 4467, 4468, 4469, 4470, \
4471, 4472, 4473, 4474, 4475, 4476, 4477, 4478, 4479, 4480, \
4481, 4482, 4483, 4484, 4485, 4486, 4487, 4488, 4489, 4490, \
4491, 4492, 4493, 4494, 4495, 4496, 4497, 4498, 4499, 4500, \
4501, 4502, 4503, 4504, 4505, 4506, 4507, 4508, 4509, 4510, \
4511, 4512, 4513, 4514, 4515, 4516, 4517, 4518, 4519, 4520, \
4521, 4522, 4523, 4524, 4525, 4526, 4527, 4528, 4529, 4530, \
4531, 4532, 4533, 4534, 4535, 4536, 4537, 4538, 4539, 4540, \
4541, 4542, 4543, 4544, 4545, 4546, 4547, 4548, 4549, 4550, \
4551, 4552, 4553, 4554, 4555, 4556, 4557, 4558, 4559, 4560, \
4561, 4562, 4563, 4564, 4565, 4566, 4567, 4568, 4569, 4570, \
4571, 4572, 4573, 4574, 4575, 4576, 4577, 4578, 4579, 4580, \
4581, 4582, 4583, 4584, 4585, 4586, 4587, 4588, 4589, 4590, \
4591, 4592, 4593, 4594, 4595, 4596, 4597, 4598, 4599, 4600, \
4601, 4602, 4603, 4604, 4605, 4606, 4607, 4608, 4609, 4610, \
4611, 4612, 4613, 4614, 4615, 4616, 4617, 4618, 4619, 4620, \
4621, 4622, 4623, 4624, 4625, 4626, 4627, 4628, 4629, 4630, \
4631, 4632, 4633, 4634, 4635, 4636, 4637, 4638, 4639, 4640, \
4641, 4642, 4643, 4644, 4645, 4646, 4647, 4648, 4649, 4650, \
4651, 4652, 4653, 4654, 4655, 4656, 4657, 4658, 4659, 4660, \
4661, 4662, 4663, 4664, 4665, 4666, 4667, 4668, 4669, 4670, \
4671, 4672, 4673, 4674, 4675, 4676, 4677, 4678, 4679, 4680, \
4681, 4682, 4683, 4684, 4685, 4686, 4687, 4688, 4689, 4690, \
4691, 4692, 4693, 4694, 4695, 4696, 4697, 4698, 4699, 4700, \
4701, 4702, 4703, 4704, 4705, 4706, 4707, 4708, 4709, 4710, \
4711, 4712, 4713, 4714, 4715, 4716, 4717, 4718, 4719, 4720, \
4721, 4722, 4723, 4724, 4725, 4726, 4727, 4728, 4729, 4730, \
4731, 4732, 4733, 4734, 4735, 4736, 4737, 4738, 4739, 4740, \
4741, 4742, 4743, 4744, 4745, 4746, 4747, 4748, 4749, 4750, \
4751, 4752, 4753, 4754, 4755, 4756, 4757, 4758, 4759, 4760, \
4761, 4762, 4763, 4764, 4765, 4766, 4767, 4768, 4769, 4770, \
4771, 4772, 4773, 4774, 4775, 4776, 4777, 4778, 4779, 4780, \
4781, 4782, 4783, 4784, 4785, 4786, 4787, 4788, 4789, 4790, \
4791, 4792, 4793, 4794, 4795, 4796, 4797, 4798, 4799, 4800, \
4801, 4802, 4803, 4804, 4805, 4806, 4807, 4808, 4809, 4810, \
4811, 4812, 4813, 4814, 4815, 4816, 4817, 4818, 4819, 4820, \
4821, 4822, 4823, 4824, 4825, 4826, 4827, 4828, 4829, 4830, \
4831, 4832, 4833, 4834, 4835, 4836, 4837, 4838, 4839, 4840, \
4841, 4842, 4843, 4844, 4845, 4846, 4847, 4848, 4849, 4850, \
4851, 4852, 4853, 4854, 4855, 4856, 4857, 4858, 4859, 4860, \
4861, 4862, 4863, 4864, 4865, 4866, 4867, 4868, 4869, 4870, \
4871, 4872, 4873, 4874, 4875, 4876, 4877, 4878, 4879, 4880, \
4881, 4882, 4883, 4884, 4885, 4886, 4887, 4888, 4889, 4890, \
4891, 4892, 4893, 4894, 4895, 4896, 4897, 4898, 4899, 4900, \
4901, 4902, 4903, 4904, 4905, 4906, 4907, 4908, 4909, 4910, \
4911, 4912, 4913, 4914, 4915, 4916, 4917, 4918, 4919, 4920, \
4921, 4922, 4923, 4924, 4925, 4926, 4927, 4928, 4929, 4930, \
4931, 4932, 4933, 4934, 4935, 4936, 4937, 4938, 4939, 4940, \
4941, 4942, 4943, 4944, 4945, 4946, 4947, 4948, 4949, 4950, \
4951, 4952, 4953, 4954, 4955, 4956, 4957, 4958, 4959, 4960, \
4961, 4962, 4963, 4964, 4965, 4966, 4967, 4968, 4969, 4970, \
4971, 4972, 4973, 4974, 4975, 4976, 4977, 4978, 4979, 4980, \
4981, 4982, 4983, 4984, 4985, 4986, 4987, 4988, 4989, 4990, \
4991, 4992, 4993, 4994, 4995, 4996, 4997, 4998, 4999, 5000, \
5001, 5002, 5003, 5004, 5005, 5006, 5007, 5008, 5009, 5010, \
5011, 5012, 5013, 5014, 5015, 5016, 5017, 5018, 5019, 5020, \
5021, 5022, 5023, 5024, 5025, 5026, 5027, 5028, 5029, 5030, \
5031, 5032, 5033, 5034, 5035, 5036, 5037, 5038, 5039, 5040, \
5041, 5042, 5043, 5044, 5045, 5046, 5047, 5048, 5049, 5050, \
5051, 5052, 5053, 5054, 5055, 5056, 5057, 5058, 5059, 5060, \
5061, 5062, 5063, 5064, 5065, 5066, 5067, 5068, 5069, 5070, \
5071, 5072, 5073, 5074, 5075, 5076, 5077, 5078, 5079, 5080, \
5081, 5082, 5083, 5084, 5085, 5086, 5087, 5088, 5089, 5090, \
5091, 5092, 5093, 5094, 5095, 5096, 5097, 5098, 5099, 5100, \
5101, 5102, 5103, 5104, 5105, 5106, 5107, 5108, 5109, 5110, \
5111, 5112, 5113, 5114, 5115, 5116, 5117, 5118, 5119, 5120, \
5121, 5122, 5123, 5124, 5125, 5126, 5127, 5128, 5129, 5130, \
5131, 5132, 5133, 5134, 5135, 5136, 5137, 5138, 5139, 5140, \
5141, 5142, 5143, 5144, 5145, 5146, 5147, 5148, 5149, 5150, \
5151, 5152, 5153, 5154, 5155, 5156, 5157, 5158, 5159, 5160, \
5161, 5162, 5163, 5164, 5165, 5166, 5167, 5168, 5169, 5170, \
5171, 5172, 5173, 5174, 5175, 5176, 5177, 5178, 5179, 5180, \
5181, 5182, 5183, 5184, 5185, 5186, 5187, 5188, 5189, 5190, \
5191, 5192, 5193, 5194, 5195, 5196, 5197, 5198, 5199, 5200, \
5201, 5202, 5203, 5204, 5205, 5206, 5207, 5208, 5209, 5210, \
5211, 5212, 5213, 5214, 5215, 5216, 5217, 5218, 5219, 5220, \
5221, 5222, 5223, 5224, 5225, 5226, 5227, 5228, 5229, 5230, \
5231, 5232, 5233, 5234, 5235, 5236, 5237, 5238, 5239, 5240, \
5241, 5242, 5243, 5244, 5245, 5246, 5247, 5248, 5249, 5250, \
5251, 5252, 5253, 5254, 5255, 5256, 5257, 5258, 5259, 5260, \
5261, 5262, 5263, 5264, 5265, 5266, 5267, 5268, 5269, 5270, \
5271, 5272, 5273, 5274, 5275, 5276, 5277, 5278, 5279, 5280, \
5281, 5282, 5283, 5284, 5285, 5286, 5287, 5288, 5289, 5290, \
5291, 5292, 5293, 5294, 5295, 5296, 5297, 5298, 5299, 5300, \
5301, 5302, 5303, 5304, 5305, 5306, 5307, 5308, 5309, 5310, \
5311, 5312, 5313, 5314, 5315, 5316, 5317, 5318, 5319, 5320, \
5321, 5322, 5323, 5324, 5325, 5326, 5327, 5328, 5329, 5330, \
5331, 5332, 5333, 5334, 5335, 5336, 5337, 5338, 5339, 5340, \
5341, 5342, 5343, 5344, 5345, 5346, 5347, 5348, 5349, 5350, \
5351, 5352, 5353, 5354, 5355, 5356, 5357, 5358, 5359, 5360, \
5361, 5362, 5363, 5364, 5365, 5366, 5367, 5368, 5369, 5370, \
5371, 5372, 5373, 5374, 5375, 5376, 5377, 5378, 5379, 5380, \
5381, 5382, 5383, 5384, 5385, 5386, 5387, 5388, 5389, 5390, \
5391, 5392, 5393, 5394, 5395, 5396, 5397, 5398, 5399, 5400, \
5401, 5402, 5403, 5404, 5405, 5406, 5407, 5408, 5409, 5410, \
5411, 5412, 5413, 5414, 5415, 5416, 5417, 5418, 5419, 5420, \
5421, 5422, 5423, 5424, 5425, 5426, 5427, 5428, 5429, 5430, \
5431, 5432, 5433, 5434, 5435, 5436, 5437, 5438, 5439, 5440, \
5441, 5442, 5443, 5444, 5445, 5446, 5447, 5448, 5449, 5450, \
5451, 5452, 5453, 5454, 5455, 5456, 5457, 5458, 5459, 5460, \
5461, 5462, 5463, 5464, 5465, 5466, 5467, 5468, 5469, 5470, \
5471, 5472, 5473, 5474, 5475, 5476, 5477, 5478, 5479, 5480, \
5481, 5482, 5483, 5484, 5485, 5486, 5487, 5488, 5489, 5490, \
5491, 5492, 5493, 5494, 5495, 5496, 5497, 5498, 5499, 5500, \
5501, 5502, 5503, 5504, 5505, 5506, 5507, 5508, 5509, 5510, \
5511, 5512, 5513, 5514, 5515, 5516, 5517, 5518, 5519, 5520, \
5521, 5522, 5523, 5524, 5525, 5526, 5527, 5528, 5529, 5530, \
5531, 5532, 5533, 5534, 5535, 5536, 5537, 5538, 5539, 5540, \
5541, 5542, 5543, 5544, 5545, 5546, 5547, 5548, 5549, 5550, \
5551, 5552, 5553, 5554, 5555, 5556, 5557, 5558, 5559, 5560, \
5561, 5562, 5563, 5564, 5565, 5566, 5567, 5568, 5569, 5570, \
5571, 5572, 5573, 5574, 5575, 5576, 5577, 5578, 5579, 5580, \
5581, 5582, 5583, 5584, 5585, 5586, 5587, 5588, 5589, 5590, \
5591, 5592, 5593, 5594, 5595, 5596, 5597, 5598, 5599, 5600, \
5601, 5602, 5603, 5604, 5605, 5606, 5607, 5608, 5609, 5610, \
5611, 5612, 5613, 5614, 5615, 5616, 5617, 5618, 5619, 5620, \
5621, 5622, 5623, 5624, 5625, 5626, 5627, 5628, 5629, 5630, \
5631, 5632, 5633, 5634, 5635, 5636, 5637, 5638, 5639, 5640, \
5641, 5642, 5643, 5644, 5645, 5646, 5647, 5648, 5649, 5650, \
5651, 5652, 5653, 5654, 5655, 5656, 5657, 5658, 5659, 5660, \
5661, 5662, 5663, 5664, 5665, 5666, 5667, 5668, 5669, 5670, \
5671, 5672, 5673, 5674, 5675, 5676, 5677, 5678, 5679, 5680, \
5681, 5682, 5683, 5684, 5685, 5686, 5687, 5688, 5689, 5690, \
5691, 5692, 5693, 5694, 5695, 5696, 5697, 5698, 5699, 5700, \
5701, 5702, 5703, 5704, 5705, 5706, 5707, 5708, 5709, 5710, \
5711, 5712, 5713, 5714, 5715, 5716, 5717, 5718, 5719, 5720, \
5721, 5722, 5723, 5724, 5725, 5726, 5727, 5728, 5729, 5730, \
5731, 5732, 5733, 5734, 5735, 5736, 5737, 5738, 5739, 5740, \
5741, 5742, 5743, 5744, 5745, 5746, 5747, 5748, 5749, 5750, \
5751, 5752, 5753, 5754, 5755, 5756, 5757, 5758, 5759, 5760, \
5761, 5762, 5763, 5764, 5765, 5766, 5767, 5768, 5769, 5770, \
5771, 5772, 5773, 5774, 5775, 5776, 5777, 5778, 5779, 5780, \
5781, 5782, 5783, 5784, 5785, 5786, 5787, 5788, 5789, 5790, \
5791, 5792, 5793, 5794, 5795, 5796, 5797, 5798, 5799, 5800, \
5801, 5802, 5803, 5804, 5805, 5806, 5807, 5808, 5809, 5810, \
5811, 5812, 5813, 5814, 5815, 5816, 5817, 5818, 5819, 5820, \
5821, 5822, 5823, 5824, 5825, 5826, 5827, 5828, 5829, 5830, \
5831, 5832, 5833, 5834, 5835, 5836, 5837, 5838, 5839, 5840, \
5841, 5842, 5843, 5844, 5845, 5846, 5847, 5848, 5849, 5850, \
5851, 5852, 5853, 5854, 5855, 5856, 5857, 5858, 5859, 5860, \
5861, 5862, 5863, 5864, 5865, 5866, 5867, 5868, 5869, 5870, \
5871, 5872, 5873, 5874, 5875, 5876, 5877, 5878, 5879, 5880, \
5881, 5882, 5883, 5884, 5885, 5886, 5887, 5888, 5889, 5890, \
5891, 5892, 5893, 5894, 5895, 5896, 5897, 5898, 5899, 5900, \
5901, 5902, 5903, 5904, 5905, 5906, 5907, 5908, 5909, 5910, \
5911, 5912, 5913, 5914, 5915, 5916, 5917, 5918, 5919, 5920, \
5921, 5922, 5923, 5924, 5925, 5926, 5927, 5928, 5929, 5930, \
5931, 5932, 5933, 5934, 5935, 5936, 5937, 5938, 5939, 5940, \
5941, 5942, 5943, 5944, 5945, 5946, 5947, 5948, 5949, 5950, \
5951, 5952, 5953, 5954, 5955, 5956, 5957, 5958, 5959, 5960, \
5961, 5962, 5963, 5964, 5965, 5966, 5967, 5968, 5969, 5970, \
5971, 5972, 5973, 5974, 5975, 5976, 5977, 5978, 5979, 5980, \
5981, 5982, 5983, 5984, 5985, 5986, 5987, 5988, 5989, 5990, \
5991, 5992, 5993, 5994, 5995, 5996, 5997, 5998, 5999, 6000, \
6001, 6002, 6003, 6004, 6005, 6006, 6007, 6008, 6009, 6010, \
6011, 6012, 6013, 6014, 6015, 6016, 6017, 6018, 6019, 6020, \
6021, 6022, 6023, 6024, 6025, 6026, 6027, 6028, 6029, 6030, \
6031, 6032, 6033, 6034, 6035, 6036, 6037, 6038, 6039, 6040, \
6041, 6042, 6043, 6044, 6045, 6046, 6047, 6048, 6049, 6050, \
6051, 6052, 6053, 6054, 6055, 6056, 6057, 6058, 6059, 6060, \
6061, 6062, 6063, 6064, 6065, 6066, 6067, 6068, 6069, 6070, \
6071, 6072, 6073, 6074, 6075, 6076, 6077, 6078, 6079, 6080, \
6081, 6082, 6083, 6084, 6085, 6086, 6087, 6088, 6089, 6090, \
6091, 6092, 6093, 6094, 6095, 6096, 6097, 6098, 6099, 6100, \
6101, 6102, 6103, 6104, 6105, 6106, 6107, 6108, 6109, 6110, \
6111, 6112, 6113, 6114, 6115, 6116, 6117, 6118, 6119, 6120, \
6121, 6122, 6123, 6124, 6125, 6126, 6127, 6128, 6129, 6130, \
6131, 6132, 6133, 6134, 6135, 6136, 6137, 6138, 6139, 6140, \
6141, 6142, 6143, 6144, 6145, 6146, 6147, 6148, 6149, 6150, \
6151, 6152, 6153, 6154, 6155, 6156, 6157, 6158, 6159, 6160, \
6161, 6162, 6163, 6164, 6165, 6166, 6167, 6168, 6169, 6170, \
6171, 6172, 6173, 6174, 6175, 6176, 6177, 6178, 6179, 6180, \
6181, 6182, 6183, 6184, 6185, 6186, 6187, 6188, 6189, 6190, \
6191, 6192, 6193, 6194, 6195, 6196, 6197, 6198, 6199, 6200, \
6201, 6202, 6203, 6204, 6205, 6206, 6207, 6208, 6209, 6210, \
6211, 6212, 6213, 6214, 6215, 6216, 6217, 6218, 6219, 6220, \
6221, 6222, 6223, 6224, 6225, 6226, 6227, 6228, 6229, 6230, \
6231, 6232, 6233, 6234, 6235, 6236, 6237, 6238, 6239, 6240, \
6241, 6242, 6243, 6244, 6245, 6246, 6247, 6248, 6249, 6250, \
6251, 6252, 6253, 6254, 6255, 6256, 6257, 6258, 6259, 6260, \
6261, 6262, 6263, 6264, 6265, 6266, 6267, 6268, 6269, 6270, \
6271, 6272, 6273, 6274, 6275, 6276, 6277, 6278, 6279, 6280, \
6281, 6282, 6283, 6284, 6285, 6286, 6287, 6288, 6289, 6290, \
6291, 6292, 6293, 6294, 6295, 6296, 6297, 6298, 6299, 6300, \
6301, 6302, 6303, 6304, 6305, 6306, 6307, 6308, 6309, 6310, \
6311, 6312, 6313, 6314, 6315, 6316, 6317, 6318, 6319, 6320, \
6321, 6322, 6323, 6324, 6325, 6326, 6327, 6328, 6329, 6330, \
6331, 6332, 6333, 6334, 6335, 6336, 6337, 6338, 6339, 6340, \
6341, 6342, 6343, 6344, 6345, 6346, 6347, 6348, 6349, 6350, \
6351, 6352, 6353, 6354, 6355, 6356, 6357, 6358, 6359, 6360, \
6361, 6362, 6363, 6364, 6365, 6366, 6367, 6368, 6369, 6370, \
6371, 6372, 6373, 6374, 6375, 6376, 6377, 6378, 6379, 6380, \
6381, 6382, 6383, 6384, 6385, 6386, 6387, 6388, 6389, 6390, \
6391, 6392, 6393, 6394, 6395, 6396, 6397, 6398, 6399, 6400, \
6401, 6402, 6403, 6404, 6405, 6406, 6407, 6408, 6409, 6410, \
6411, 6412, 6413, 6414, 6415, 6416, 6417, 6418, 6419, 6420, \
6421, 6422, 6423, 6424, 6425, 6426, 6427, 6428, 6429, 6430, \
6431, 6432, 6433, 6434, 6435, 6436, 6437, 6438, 6439, 6440, \
6441, 6442, 6443, 6444, 6445, 6446, 6447, 6448, 6449, 6450, \
6451, 6452, 6453, 6454, 6455, 6456, 6457, 6458, 6459, 6460, \
6461, 6462, 6463, 6464, 6465, 6466, 6467, 6468, 6469, 6470, \
6471, 6472, 6473, 6474, 6475, 6476, 6477, 6478, 6479, 6480, \
6481, 6482, 6483, 6484, 6485, 6486, 6487, 6488, 6489, 6490, \
6491, 6492, 6493, 6494, 6495, 6496, 6497, 6498, 6499, 6500, \
6501, 6502, 6503, 6504, 6505, 6506, 6507, 6508, 6509, 6510, \
6511, 6512, 6513, 6514, 6515, 6516, 6517, 6518, 6519, 6520, \
6521, 6522, 6523, 6524, 6525, 6526, 6527, 6528, 6529, 6530, \
6531, 6532, 6533, 6534, 6535, 6536, 6537, 6538, 6539, 6540, \
6541, 6542, 6543, 6544, 6545, 6546, 6547, 6548, 6549, 6550, \
6551, 6552, 6553, 6554, 6555, 6556, 6557, 6558, 6559, 6560, \
6561, 6562, 6563, 6564, 6565, 6566, 6567, 6568, 6569, 6570, \
6571, 6572, 6573, 6574, 6575, 6576, 6577, 6578, 6579, 6580, \
6581, 6582, 6583, 6584, 6585, 6586, 6587, 6588, 6589, 6590, \
6591, 6592, 6593, 6594, 6595, 6596, 6597, 6598, 6599, 6600, \
6601, 6602, 6603, 6604, 6605, 6606, 6607, 6608, 6609, 6610, \
6611, 6612, 6613, 6614, 6615, 6616, 6617, 6618, 6619, 6620, \
6621, 6622, 6623, 6624, 6625, 6626, 6627, 6628, 6629, 6630, \
6631, 6632, 6633, 6634, 6635, 6636, 6637, 6638, 6639, 6640, \
6641, 6642, 6643, 6644, 6645, 6646, 6647, 6648, 6649, 6650, \
6651, 6652, 6653, 6654, 6655, 6656, 6657, 6658, 6659, 6660, \
6661, 6662, 6663, 6664, 6665, 6666, 6667, 6668, 6669, 6670, \
6671, 6672, 6673, 6674, 6675, 6676, 6677, 6678, 6679, 6680, \
6681, 6682, 6683, 6684, 6685, 6686, 6687, 6688, 6689, 6690, \
6691, 6692, 6693, 6694, 6695, 6696, 6697, 6698, 6699, 6700, \
6701, 6702, 6703, 6704, 6705, 6706, 6707, 6708, 6709, 6710, \
6711, 6712, 6713, 6714, 6715, 6716, 6717, 6718, 6719, 6720, \
6721, 6722, 6723, 6724, 6725, 6726, 6727, 6728, 6729, 6730, \
6731, 6732, 6733, 6734, 6735, 6736, 6737, 6738, 6739, 6740, \
6741, 6742, 6743, 6744, 6745, 6746, 6747, 6748, 6749, 6750, \
6751, 6752, 6753, 6754, 6755, 6756, 6757, 6758, 6759, 6760, \
6761, 6762, 6763, 6764, 6765, 6766, 6767, 6768, 6769, 6770, \
6771, 6772, 6773, 6774, 6775, 6776, 6777, 6778, 6779, 6780, \
6781, 6782, 6783, 6784, 6785, 6786, 6787, 6788, 6789, 6790, \
6791, 6792, 6793, 6794, 6795, 6796, 6797, 6798, 6799, 6800, \
6801, 6802, 6803, 6804, 6805, 6806, 6807, 6808, 6809, 6810, \
6811, 6812, 6813, 6814, 6815, 6816, 6817, 6818, 6819, 6820, \
6821, 6822, 6823, 6824, 6825, 6826, 6827, 6828, 6829, 6830, \
6831, 6832, 6833, 6834, 6835, 6836, 6837, 6838, 6839, 6840, \
6841, 6842, 6843, 6844, 6845, 6846, 6847, 6848, 6849, 6850, \
6851, 6852, 6853, 6854, 6855, 6856, 6857, 6858, 6859, 6860, \
6861, 6862, 6863, 6864, 6865, 6866, 6867, 6868, 6869, 6870, \
6871, 6872, 6873, 6874, 6875, 6876, 6877, 6878, 6879, 6880, \
6881, 6882, 6883, 6884, 6885, 6886, 6887, 6888, 6889, 6890, \
6891, 6892, 6893, 6894, 6895, 6896, 6897, 6898, 6899, 6900, \
6901, 6902, 6903, 6904, 6905, 6906, 6907, 6908, 6909, 6910, \
6911, 6912, 6913, 6914, 6915, 6916, 6917, 6918, 6919, 6920, \
6921, 6922, 6923, 6924, 6925, 6926, 6927, 6928, 6929, 6930, \
6931, 6932, 6933, 6934, 6935, 6936, 6937, 6938, 6939, 6940, \
6941, 6942, 6943, 6944, 6945, 6946, 6947, 6948, 6949, 6950, \
6951, 6952, 6953, 6954, 6955, 6956, 6957, 6958, 6959, 6960, \
6961, 6962, 6963, 6964, 6965, 6966, 6967, 6968, 6969, 6970, \
6971, 6972, 6973, 6974, 6975, 6976, 6977, 6978, 6979, 6980, \
6981, 6982, 6983, 6984, 6985, 6986, 6987, 6988, 6989, 6990, \
6991, 6992, 6993, 6994, 6995, 6996, 6997, 6998, 6999, 7000, \
7001, 7002, 7003, 7004, 7005, 7006, 7007, 7008, 7009, 7010, \
7011, 7012, 7013, 7014, 7015, 7016, 7017, 7018, 7019, 7020, \
7021, 7022, 7023, 7024, 7025, 7026, 7027, 7028, 7029, 7030, \
7031, 7032, 7033, 7034, 7035, 7036, 7037, 7038, 7039, 7040, \
7041, 7042, 7043, 7044, 7045, 7046, 7047, 7048, 7049, 7050, \
7051, 7052, 7053, 7054, 7055, 7056, 7057, 7058, 7059, 7060, \
7061, 7062, 7063, 7064, 7065, 7066, 7067, 7068, 7069, 7070, \
7071, 7072, 7073, 7074, 7075, 7076, 7077, 7078, 7079, 7080, \
7081, 7082, 7083, 7084, 7085, 7086, 7087, 7088, 7089, 7090, \
7091, 7092, 7093, 7094, 7095, 7096, 7097, 7098, 7099, 7100, \
7101, 7102, 7103, 7104, 7105, 7106, 7107, 7108, 7109, 7110, \
7111, 7112, 7113, 7114, 7115, 7116, 7117, 7118, 7119, 7120, \
7121, 7122, 7123, 7124, 7125, 7126, 7127, 7128, 7129, 7130, \
7131, 7132, 7133, 7134, 7135, 7136, 7137, 7138, 7139, 7140, \
7141, 7142, 7143, 7144, 7145, 7146, 7147, 7148, 7149, 7150, \
7151, 7152, 7153, 7154, 7155, 7156, 7157, 7158, 7159, 7160, \
7161, 7162, 7163, 7164, 7165, 7166, 7167, 7168, 7169, 7170, \
7171, 7172, 7173, 7174, 7175, 7176, 7177, 7178, 7179, 7180, \
7181, 7182, 7183, 7184, 7185, 7186, 7187, 7188, 7189, 7190, \
7191, 7192, 7193, 7194, 7195, 7196, 7197, 7198, 7199, 7200, \
7201, 7202, 7203, 7204, 7205, 7206, 7207, 7208, 7209, 7210, \
7211, 7212, 7213, 7214, 7215, 7216, 7217, 7218, 7219, 7220, \
7221, 7222, 7223, 7224, 7225, 7226, 7227, 7228, 7229, 7230, \
7231, 7232, 7233, 7234, 7235, 7236, 7237, 7238, 7239, 7240, \
7241, 7242, 7243, 7244, 7245, 7246, 7247, 7248, 7249, 7250, \
7251, 7252, 7253, 7254, 7255, 7256, 7257, 7258, 7259, 7260, \
7261, 7262, 7263, 7264, 7265, 7266, 7267, 7268, 7269, 7270, \
7271, 7272, 7273, 7274, 7275, 7276, 7277, 7278, 7279, 7280, \
7281, 7282, 7283, 7284, 7285, 7286, 7287, 7288, 7289, 7290, \
7291, 7292, 7293, 7294, 7295, 7296, 7297, 7298, 7299, 7300, \
7301, 7302, 7303, 7304, 7305, 7306, 7307, 7308, 7309, 7310, \
7311, 7312, 7313, 7314, 7315, 7316, 7317, 7318, 7319, 7320, \
7321, 7322, 7323, 7324, 7325, 7326, 7327, 7328, 7329, 7330, \
7331, 7332, 7333, 7334, 7335, 7336, 7337, 7338, 7339, 7340, \
7341, 7342, 7343, 7344, 7345, 7346, 7347, 7348, 7349, 7350, \
7351, 7352, 7353, 7354, 7355, 7356, 7357, 7358, 7359, 7360, \
7361, 7362, 7363, 7364, 7365, 7366, 7367, 7368, 7369, 7370, \
7371, 7372, 7373, 7374, 7375, 7376, 7377, 7378, 7379, 7380, \
7381, 7382, 7383, 7384, 7385, 7386, 7387, 7388, 7389, 7390, \
7391, 7392, 7393, 7394, 7395, 7396, 7397, 7398, 7399, 7400, \
7401, 7402, 7403, 7404, 7405, 7406, 7407, 7408, 7409, 7410, \
7411, 7412, 7413, 7414, 7415, 7416, 7417, 7418, 7419, 7420, \
7421, 7422, 7423, 7424, 7425, 7426, 7427, 7428, 7429, 7430, \
7431, 7432, 7433, 7434, 7435, 7436, 7437, 7438, 7439, 7440, \
7441, 7442, 7443, 7444, 7445, 7446, 7447, 7448, 7449, 7450, \
7451, 7452, 7453, 7454, 7455, 7456, 7457, 7458, 7459, 7460, \
7461, 7462, 7463, 7464, 7465, 7466, 7467, 7468, 7469, 7470, \
7471, 7472, 7473, 7474, 7475, 7476, 7477, 7478, 7479, 7480, \
7481, 7482, 7483, 7484, 7485, 7486, 7487, 7488, 7489, 7490, \
7491, 7492, 7493, 7494, 7495, 7496, 7497, 7498, 7499, 7500, \
7501, 7502, 7503, 7504, 7505, 7506, 7507, 7508, 7509, 7510, \
7511, 7512, 7513, 7514, 7515, 7516, 7517, 7518, 7519, 7520, \
7521, 7522, 7523, 7524, 7525, 7526, 7527, 7528, 7529, 7530, \
7531, 7532, 7533, 7534, 7535, 7536, 7537, 7538, 7539, 7540, \
7541, 7542, 7543, 7544, 7545, 7546, 7547, 7548, 7549, 7550, \
7551, 7552, 7553, 7554, 7555, 7556, 7557, 7558, 7559, 7560, \
7561, 7562, 7563, 7564, 7565, 7566, 7567, 7568, 7569, 7570, \
7571, 7572, 7573, 7574, 7575, 7576, 7577, 7578, 7579, 7580, \
7581, 7582, 7583, 7584, 7585, 7586, 7587, 7588, 7589, 7590, \
7591, 7592, 7593, 7594, 7595, 7596, 7597, 7598, 7599, 7600, \
7601, 7602, 7603, 7604, 7605, 7606, 7607, 7608, 7609, 7610, \
7611, 7612, 7613, 7614, 7615, 7616, 7617, 7618, 7619, 7620, \
7621, 7622, 7623, 7624, 7625, 7626, 7627, 7628, 7629, 7630, \
7631, 7632, 7633, 7634, 7635, 7636, 7637, 7638, 7639, 7640, \
7641, 7642, 7643, 7644, 7645, 7646, 7647, 7648, 7649, 7650, \
7651, 7652, 7653, 7654, 7655, 7656, 7657, 7658, 7659, 7660, \
7661, 7662, 7663, 7664, 7665, 7666, 7667, 7668, 7669, 7670, \
7671, 7672, 7673, 7674, 7675, 7676, 7677, 7678, 7679, 7680, \
7681, 7682, 7683, 7684, 7685, 7686, 7687, 7688, 7689, 7690, \
7691, 7692, 7693, 7694, 7695, 7696, 7697, 7698, 7699, 7700, \
7701, 7702, 7703, 7704, 7705, 7706, 7707, 7708, 7709, 7710, \
7711, 7712, 7713, 7714, 7715, 7716, 7717, 7718, 7719, 7720, \
7721, 7722, 7723, 7724, 7725, 7726, 7727, 7728, 7729, 7730, \
7731, 7732, 7733, 7734, 7735, 7736, 7737, 7738, 7739, 7740, \
7741, 7742, 7743, 7744, 7745, 7746, 7747, 7748, 7749, 7750, \
7751, 7752, 7753, 7754, 7755, 7756, 7757, 7758, 7759, 7760, \
7761, 7762, 7763, 7764, 7765, 7766, 7767, 7768, 7769, 7770, \
7771, 7772, 7773, 7774, 7775, 7776, 7777

View File

View File

View File

@@ -0,0 +1 @@
<EFBFBD><EFBFBD>

View File

@@ -1,5 +1,5 @@
ERROR: pc-bank.asm(2): ERROR: pc-bank.asm(2):
Source address $2a00 not in $FF00 to $FFFF Source address $2a00 not between $FF00 to $FFFF
ERROR: pc-bank.asm(11): ERROR: pc-bank.asm(11):
Expected constant expression: Current section's bank is not known Expected constant expression: Current section's bank is not known
error: Assembly aborted (2 errors)! error: Assembly aborted (2 errors)!

View File

@@ -1,8 +1,14 @@
SECTION "test", ROM0[1] SECTION "test", ROM0[1]
call Target call Target
LOAD "new", WRAM0[$C001] PRINTT "PC in ROM: {@}\n"
LOAD "new", WRAMX[$D001],BANK[1]
PRINTT "PC in WRAM: {@}\n"
assert @ == $D001
Target: dl DEAD << 16 | BEEF Target: dl DEAD << 16 | BEEF
db BANK(@)
jr .end
.end .end
jr .end
ds 2, $2A ds 2, $2A
ENDL ENDL
After: After:
@@ -10,16 +16,16 @@ After:
ld hl, Word ld hl, Word
dw Byte, Target.end, After dw Byte, Target.end, After
SECTION "dead", WRAMX[$DEAD] SECTION "dead", WRAMX[$DEAD],BANK[2]
DEAD: DEAD:
SECTION "beef", SRAM[$BEEF] SECTION "beef", SRAM[$BEEF]
BEEF: BEEF:
SECTION "ram test", WRAM0 ; Should end up at $C005 SECTION "ram test", WRAMX,BANK[1] ; Should end up at $D005
Word: Word:
dw dw
SECTION "small ram test", WRAM0 ; Should end up at $C000 SECTION "small ram test", WRAMX,BANK[1] ; Should end up at $D000
Byte: Byte:
db db

View File

@@ -1,3 +1,5 @@
$C001 PC in ROM: $4
$C005 PC in WRAM: $D001
$A $D001
$D008
$F

Binary file not shown.

View File

@@ -0,0 +1,4 @@
SECTION "Test", ROM0[0]
db CONSTANT
CONSTANT equ 42

View File

View File

View File

@@ -0,0 +1 @@
*

View File

@@ -1,4 +1,4 @@
SECTION "sec", ROM0 SECTION "sec", ROM0[0]
db X db X
X = 2 X = 2

View File

@@ -1,3 +0,0 @@
ERROR: reference-undefined-sym.asm(4):
'X' already referenced at reference-undefined-sym.asm(2)
error: Assembly aborted (1 errors)!

View File

View File

@@ -1,5 +1,7 @@
ERROR: section-union.asm(37): ERROR: section-union.asm(37):
Section "test" already declared as union Section "test" already declared as union section
warning: section-union.asm(37): [-Wobsolete]
Concatenation of non-fragment sections is deprecated
ERROR: section-union.asm(37): ERROR: section-union.asm(37):
Section "test" already declared as fixed at $c000 Section "test" already declared as fixed at $c000
ERROR: section-union.asm(37): ERROR: section-union.asm(37):

View File

@@ -0,0 +1,34 @@
; Hashmap collisions are pretty poorly-tested code path...
; At some point, `PURGE` would malfunction with them
SECTION "Collision course", OAM[$FE00]
; All the following symbols collide!
aqfj: ds 1 ; Give them different addresses
cxje: ds 1
dgsd: ds 1
dork: ds 1
lxok: ds 1
psgp: ds 1
sfly: ds 1
syyq: ds 1
ussg: ds 1
xlmm: ds 1
xtzp: ds 1
zxfr: ds 1
; Completely by accident, but cool
PURGE dork
PRINTT "aqfj: {aqfj}\n"
PRINTT "cxje: {cxje}\n"
PRINTT "dgsd: {dgsd}\n"
PRINTT "dork: {dork}\n"
PRINTT "lxok: {lxok}\n"
PRINTT "psgp: {psgp}\n"
PRINTT "sfly: {sfly}\n"
PRINTT "syyq: {syyq}\n"
PRINTT "ussg: {ussg}\n"
PRINTT "xlmm: {xlmm}\n"
PRINTT "xtzp: {xtzp}\n"
PRINTT "zxfr: {zxfr}\n"

View File

@@ -0,0 +1,3 @@
ERROR: sym-collision.asm(26):
'dork' not defined
error: Assembly aborted (1 errors)!

View File

@@ -0,0 +1,12 @@
aqfj: $FE00
cxje: $FE01
dgsd: $FE02
dork: $0
lxok: $FE04
psgp: $FE05
sfly: $FE06
syyq: $FE07
ussg: $FE08
xlmm: $FE09
xtzp: $FE0A
zxfr: $FE0B

View File

@@ -3,5 +3,5 @@ ERROR: symbol-override.asm(6):
ERROR: symbol-override.asm(10): ERROR: symbol-override.asm(10):
'X' already defined as constant at symbol-override.asm(9) 'X' already defined as constant at symbol-override.asm(9)
ERROR: symbol-override.asm(14): ERROR: symbol-override.asm(14):
'Y' already defined as non-constant at symbol-override.asm(13) 'Y' already defined as label at symbol-override.asm(13)
error: Assembly aborted (3 errors)! error: Assembly aborted (3 errors)!

View File

@@ -1 +1 @@
RGBDS version 0.4.0 RGBDS version 0.4.1

View File

@@ -1,3 +1,4 @@
warning: assert.asm(7): Worry about me, but not too much. warning: assert.asm(7): Worry about me, but not too much.
assert.asm(8): Okay, this is getting serious! error: assert.asm(8): Okay, this is getting serious!
error: assert.asm(9): It all ends now. fatal: assert.asm(9): It all ends now.
Linking aborted after 2 errors

View File

@@ -0,0 +1,2 @@
CONSTANT equ 0
EXPORT CONSTANT

View File

@@ -0,0 +1,2 @@
SECTION "Test", ROM0[0]
db BANK(CONSTANT)

Some files were not shown because too many files have changed in this diff Show More