Compare commits

..

58 Commits

Author SHA1 Message Date
Antonio Niño Díaz
193cc06561 Improve error messages
Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-08-21 23:48:24 +01:00
Antonio Niño Díaz
f3b475453f Replace fprintf by errx for consistency
Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-08-21 23:35:56 +01:00
Antonio Niño Díaz
0c71f5a4e9 Check return values of fread in rgblink
Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-08-21 23:35:52 +01:00
Antonio Niño Díaz
4b0dfd4f4a Initialize variables in rgbfix
Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-08-21 23:35:45 +01:00
Antonio Niño Díaz
2d117f68c9 Fix compiler warnings about unused return values
In some implementations of libc the function fread has the attribute
`warn_unused_result`, that is treated as an error by the compiler as
specified in the flags passed to it.

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-08-17 23:03:42 +01:00
Antonio Niño Díaz
8954858bf7 Fix warning about using uninitialized variable
Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-08-06 22:20:39 +01:00
Antonio Niño Díaz
4877e6dbba Merge pull request #196 from error-msgs
Print more useful error messages when redefining symbols

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-08-01 19:46:58 +01:00
Antonio Niño Díaz
ec171c5f00 Make object file magic string a common define
Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-07-29 13:11:26 +01:00
Antonio Niño Díaz
840ddcecd2 Update dates in manpages
Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-07-22 15:34:24 +01:00
Antonio Niño Díaz
c00f7409ee Improve linker symbol redefinition error messages
Now, the object file in which each definition is (as well as the source
file and line) are printed with the error message.

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-07-22 15:27:30 +01:00
Antonio Niño Díaz
92449a4fe4 Save object file name of each symbol in linker
This is useful to generate error messages when there is a symbol that
appears in more than one object file.

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-07-22 15:27:25 +01:00
Antonio Niño Díaz
4e2a035838 Print location of definition of redefined symbols
When trying to define a symbol with a name that is used by another one,
print the location of the first definition in the error message.

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-07-22 15:27:22 +01:00
Antonio Niño Díaz
df25fa73af Update documentation of object files
Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-07-22 15:27:17 +01:00
Antonio Niño Díaz
03bb2d04c3 Increment version number of object files
The previous change has broken compatibility of object files, so it is
needed to increment the version number to make the linker reject files
generated with the old code.

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-07-22 15:27:14 +01:00
Antonio Niño Díaz
4dc376b0ee Save location information of symbol definitions
Now, object files save the file name and line number where each global
symbol is defined.

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-07-22 15:27:06 +01:00
Antonio Niño Díaz
3dec5698db Merge pull request #188 from version-string
Add command to print version string

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-07-22 14:10:10 +01:00
Ben10do
f8bbe9be48 Add support for unions
Unions allow multiple memory allocations (using ds, etc.) to share the
same space in memory.

This allows games to use the same memory for different purposes,
depending on their state.

This also adds documentation on how to use the new UNION, NEXTU, and
ENDU keywords.
2017-07-22 14:03:17 +01:00
Antonio Niño Díaz
4d01b2d5ac Document ELIF
Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-07-22 10:45:17 +01:00
Antonio Niño Díaz
086f02c1d9 Add EQUs with the version numbers of RGBDS
Document new EQUs.

Add missing EQUs to list of keywords in documentation.

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-07-22 10:27:49 +01:00
Antonio Niño Díaz
8ed6c32ae7 Reorder getopt switch options alphabetically
Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-07-22 10:14:02 +01:00
Antonio Niño Díaz
d0e0525302 Add -V to all programs to show the version
This option has been added to all programs of the toolchain, and it
prints the version string of the toolchain.

Manpages and help command line output updated.

Add missing 'w' flag to the command line output of rgbasm. It was
correctly documented in the manpages.

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-07-22 10:14:02 +01:00
Antonio Niño Díaz
318c981c00 Add code to determine a version string
If the folder where the code is compiled is a valid git repository, the
version string is generated with `git describe`. If it isn't a valid
repository, a string included in the source code is used instead. This
one must be updated regularly as the toolchain is developed.

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-07-22 10:14:02 +01:00
YamaArashi
5c7db42fc4 Added ELIF
In addition, make some formatting changes, and add some extra error handling (for when ELIF, ELSE, or ENDC are encountered without a corresponding IF).
2017-07-21 22:32:15 +01:00
Ben10do
4be92e14e6 Add shebang to test shell scripts
This ensures that the test scripts are correctly run with the Bourne shell, regardless of the (potentially more exotic) shell that is used to invoke the script.
2017-07-20 19:21:06 +01:00
Antonio Niño Díaz
1b155d9d4c Fix typo in documentation of RSRESET
Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-07-18 21:48:19 +01:00
Antonio Niño Díaz
f9a1aba0d8 Allow CFLAGS to be modified from make command line
Previously, if the user wanted to modify CFLAGS, it was necessary to add
the options used in the Makefile as well, which doesn't make sense.

This patch splits CFLAGS into CFLAGS and the previously removed
REALCFLAGS so that the user can modify the arguments passed to the
compiler without having to worry about things like passing the list of
include directories.

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-07-15 20:10:28 +01:00
Antonio Niño Díaz
fe65e07cb6 Fail when using negative constants if not allowed
Some commands, such as `DS`, `BANK[n]`, etc, don't allow the use of
negative constants, but there wasn't any check to prohibit the code from
trying to do so.

This patch adds the `uconst` type to the parser to use when a constant
is expected, but it mustn't be negative.

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-07-01 14:31:58 +01:00
Antonio Niño Díaz
bb12806da1 Fix indentation
Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-07-01 14:31:58 +01:00
Antonio Niño Díaz
fa36042131 Add missing documentation of RL command
Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-07-01 14:31:39 +01:00
Antonio Niño Díaz
efaad99f25 Update manpage documentation about labels
- Local labels can now be exported.
- Local labels can be declared as Scope.Label in addition of .Label.

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-06-13 20:16:42 +01:00
Antonio Niño Díaz
62d820c261 Merge pull request #181 from Ben10do/reference-local-symbols
Allow local labels to be referenced (and exported)

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-06-12 21:46:32 +01:00
Ben10do
a53d795361 Add tests for explicit definitions of local labels
These check that “Parent.child” may be used in place of “.child”, and that “WrongParent.child” may not be used in the scope of “Parent”.
2017-06-12 19:50:02 +01:00
Sanqui
2e60c4dd2e Add tests for remote local symbols 2017-06-12 19:36:52 +01:00
Ben10do
ce8a13a562 Allow local symbols to be referenced
Local symbols can now be referenced outside the scope of their parent, by using the syntax “Parent.Chlid”.

- Local symbol names are now stored internally as “Parent.Child”.
- The symbol’s scope field no longer forms a linked list of the prior local symbols; it will now always contain the parent.
- Add the ability use EXPORT and GLOBAL with local symbols.
- Reduce duplication between findsymbol() and findpsymbol(), as well as between sym_AddLocalReloc() and sym_AddReloc().
2017-06-12 19:36:46 +01:00
Antonio Niño Díaz
ff2321a8ce Make fatalerror and yyerror consistent
There are two ways in which the assembly process can fail:

1. If there is a really big problem that compromises the whole process,
   the assembler has to stop right there and generate an error message.
   This happens with unterminated REPT loops, macros, etc.

2. If the problem isn't that big and the process can still continue,
   even though the final result is invalid, the assembler can try to
   continue and warn the user about all errors it finds in the code.

This patch clarifies the use of each function and replaces the function
used in two places by the correct one.

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-04-29 15:02:57 +01:00
Antonio Niño Díaz
4228e3e890 Document dependency problem for EQUS and MACRO
This is hard to detect in MACROs, as there are legitimate uses for
MACROs that call themselves recursively.

For an EQUS, the problem is that its value may be modified at different
points in the source code, so the only way to detect a possible problem
is by doing an analysis at each usage of the EQUS.

Also, since an EQUS may expand to the name of a MACRO and a MACRO can
use an EQUS, it becomes even harder to check all possible problems that
come out of it. It's better to let this task to the programmer.

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-04-26 22:02:47 +01:00
Antonio Niño Díaz
023b574fc5 New warning for rgbasm and fixes in error messages
Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-04-26 00:57:29 +02:00
Antonio Niño Díaz
a77df57f1c Merge pull request #177 from AntonioND/an/section-checks
Improve section size checks and buffer handling

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-04-25 13:31:06 +02:00
Antonio Niño Díaz
8f553e89ce Merge pull request #176 from makefile-cleanup
Cleanup Makefile

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-04-25 13:29:52 +02:00
Antonio Niño Díaz
b7810ffdb3 Check for section overflows in rgbasm
When allocating a section, allocate only the max possible size for that
type (only applies to ROM0 and ROMX).

When finding an overflow, in any kind of section, output an error with
the location of the line of code that caused the overflow.

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-04-24 14:29:27 +02:00
Antonio Niño Díaz
0d3401058d Check max section sizes in rgblink
The max size of some section types depends on the flags passed to
rgblink. Instead of doing in rgbasm some checks (for the sections with
fixed size) and others in rgblink, all checks are now done in rgblink.

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-04-24 02:05:30 +02:00
Antonio Niño Díaz
6e123ccc36 Optimize allocation of buffers for sections
Instead of allocating 0x4000 bytes for all sections and resize them as
needed, allocate 0x8000 bytes and don't let them to be resized. This is
the max possible size (ROM0 when ROMX sections aren't present).

Buffers are not needed for RAM sections, this patch changes the code so
that it only allocates buffers for ROM sections.

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-04-24 02:05:30 +02:00
Antonio Niño Díaz
f2724df566 Fix rgblink error messages about prohibited sections
Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-04-23 20:45:39 +02:00
Antonio Niño Díaz
646d71d927 Remove unused code
Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-04-23 20:25:19 +02:00
Antonio Niño Díaz
9b9b41e605 Fix install instructions
It isn't needed to create the folders manually, the Makefile does it
automatically.

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-04-23 14:34:33 +02:00
Antonio Niño Díaz
907ccfb280 Cleanup Makefile 2017-04-23 13:32:32 +02:00
Antonio Niño Díaz
fcd7c117e7 Fix man page documentation for dependcy files
Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-04-22 11:43:28 +02:00
Antonio Niño Díaz
323922d854 Merge pull request #174 from makedepend
Generate make-style dependency files

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-04-22 11:40:16 +02:00
Antonio Niño Díaz
f97e3bad33 Merge pull request #171 from NieDzejkob/master
Implement separate time constants

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-04-22 11:36:59 +02:00
Antonio Niño Díaz
64415555f1 Merge pull request #175 from NieDzejkob/man
Fix some man installation locations

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-04-19 19:46:58 +01:00
NieDzejkob
ab2aef3f2b Fix some man installation locations 2017-04-19 13:18:18 +02:00
NieDzejkob
947db1e21b Implement numeric time and date constants 2017-04-19 13:12:08 +02:00
Antonio Niño Díaz
cde607c09c Deps file can only be created if object file specified
Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-04-17 22:02:18 +01:00
Antonio Niño Díaz
739b113f57 Print dependencies of all included files
Files that weren't found with the absolute path weren't added as
dependencies even if they were found after considering the list of
include directories.

This patch makes rgbasm print the complete path (including the include
directory path) in these cases.

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-04-17 20:27:58 +01:00
Antonio Niño Díaz
64585eebf6 Merge branch 'makedepend'
Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-04-17 19:29:25 +01:00
Antonio Niño Díaz
1050acc290 Remove useless link in documentation
Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-04-17 18:21:08 +01:00
Antonio Niño Díaz
466bb9ed0b Fix links in man pages
Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2017-04-17 18:14:49 +01:00
Anthony J. Bentley
581133ecce Output make-style dependencies with -M. 2015-12-11 01:06:19 -07:00
50 changed files with 1124 additions and 486 deletions

View File

@@ -1,6 +0,0 @@
# GNU Make 3.x doesn't support the "!=" shell syntax, so here's an alternative
PKG_CONFIG = pkg-config
PNGFLAGS = $(shell ${PKG_CONFIG} --cflags libpng)
include Makefile

187
Makefile
View File

@@ -1,25 +1,42 @@
PKG_CONFIG = pkg-config # User-defined variables
WARNFLAGS = -Wall -Werror
PNGFLAGS != ${PKG_CONFIG} --cflags libpng
REALCFLAGS = ${CFLAGS} ${WARNFLAGS} ${PNGFLAGS} -Iinclude -g \
-std=c99 -D_POSIX_C_SOURCE=200809L
Q := @
PREFIX := /usr/local
bindir := ${PREFIX}/bin
mandir := ${PREFIX}/man
STRIP := -s
BINMODE := 555
MANMODE := 444
# Other variables
PKG_CONFIG := pkg-config
PNGCFLAGS := `${PKG_CONFIG} --static --cflags libpng`
PNGLDFLAGS := `${PKG_CONFIG} --static --libs-only-L libpng`
PNGLDLIBS := `${PKG_CONFIG} --static --libs-only-l libpng`
VERSION_STRING := `git describe --tags --dirty --always 2>/dev/null`
WARNFLAGS := -Wall -Werror
# Overridable CFLAGS
CFLAGS := -g
# Non-overridable CFLAGS
REALCFLAGS := ${CFLAGS} ${WARNFLAGS} -std=c99 -D_POSIX_C_SOURCE=200809L \
-Iinclude -DBUILD_VERSION_STRING=\"${VERSION_STRING}\"
YFLAGS :=
LFLAGS := --nounistd LFLAGS := --nounistd
YACC := yacc YACC := yacc
FLEX := flex LEX := flex
RM := rm -rf RM := rm -rf
# User-defined variables # Rules to build the RGBDS binaries
PREFIX = /usr/local
bindir = ${PREFIX}/bin
mandir = ${PREFIX}/man
Q = @
STRIP = -s
BINMODE = 555
MANMODE = 444
rgbasm_obj = \ all: rgbasm rgblink rgbfix rgbgfx
rgbasm_obj := \
src/asm/asmy.o \ src/asm/asmy.o \
src/asm/charmap.o \ src/asm/charmap.o \
src/asm/fstack.o \ src/asm/fstack.o \
@@ -34,9 +51,14 @@ rgbasm_obj = \
src/extern/err.o \ src/extern/err.o \
src/extern/reallocarray.o \ src/extern/reallocarray.o \
src/extern/strlcpy.o \ src/extern/strlcpy.o \
src/extern/strlcat.o src/extern/strlcat.o \
src/extern/version.o
rgblink_obj = \
src/asm/asmy.h: src/asm/asmy.c
src/asm/locallex.o src/asm/globlex.o src/asm/lexer.o: src/asm/asmy.h
rgblink_obj := \
src/link/assign.o \ src/link/assign.o \
src/link/lexer.o \ src/link/lexer.o \
src/link/library.o \ src/link/library.o \
@@ -48,45 +70,23 @@ rgblink_obj = \
src/link/parser.o \ src/link/parser.o \
src/link/script.o \ src/link/script.o \
src/link/symbol.o \ src/link/symbol.o \
src/extern/err.o src/extern/err.o \
src/extern/version.o
rgbfix_obj = \ src/link/parser.h: src/link/parser.c
src/link/lexer.o: src/link/parser.h
rgbfix_obj := \
src/fix/main.o \ src/fix/main.o \
src/extern/err.o src/extern/err.o \
src/extern/version.o
rgbgfx_obj = \ rgbgfx_obj := \
src/gfx/gb.o \ src/gfx/gb.o \
src/gfx/main.o \ src/gfx/main.o \
src/gfx/makepng.o \ src/gfx/makepng.o \
src/extern/err.o src/extern/err.o \
src/extern/version.o
all: rgbasm rgblink rgbfix rgbgfx
clean:
$Q${RM} rgbds.7.html gbz80.7.html rgbds.5.html
$Q${RM} rgbasm rgbasm.exe ${rgbasm_obj} rgbasm.1.html rgbasm.5.html
$Q${RM} rgblink rgblink.exe ${rgblink_obj} rgblink.1.html rgblink.5.html
$Q${RM} rgbfix rgbfix.exe ${rgbfix_obj} rgbfix.1.html
$Q${RM} rgbgfx rgbgfx.exe ${rgbgfx_obj} rgbgfx.1.html
$Q${RM} src/asm/asmy.c src/asm/asmy.h
$Q${RM} src/link/lexer.c src/link/parser.c src/link/parser.h
install: all
$Qmkdir -p ${DESTDIR}${bindir}
$Qinstall ${STRIP} -m ${BINMODE} rgbasm ${DESTDIR}${bindir}/rgbasm
$Qinstall ${STRIP} -m ${BINMODE} rgbfix ${DESTDIR}${bindir}/rgbfix
$Qinstall ${STRIP} -m ${BINMODE} rgblink ${DESTDIR}${bindir}/rgblink
$Qinstall ${STRIP} -m ${BINMODE} rgbgfx ${DESTDIR}${bindir}/rgbgfx
$Qmkdir -p ${DESTDIR}${mandir}/man1 ${DESTDIR}${mandir}/man5 ${DESTDIR}${mandir}/man7
$Qinstall -m ${MANMODE} src/rgbds.7 ${DESTDIR}${mandir}/man7/rgbds.7
$Qinstall -m ${MANMODE} src/gbz80.7 ${DESTDIR}${mandir}/man7/gbz80.7
$Qinstall -m ${MANMODE} src/rgbds.5 ${DESTDIR}${mandir}/man7/rgbds.5
$Qinstall -m ${MANMODE} src/asm/rgbasm.1 ${DESTDIR}${mandir}/man1/rgbasm.1
$Qinstall -m ${MANMODE} src/asm/rgbasm.5 ${DESTDIR}${mandir}/man1/rgbasm.5
$Qinstall -m ${MANMODE} src/fix/rgbfix.1 ${DESTDIR}${mandir}/man1/rgbfix.1
$Qinstall -m ${MANMODE} src/link/rgblink.1 ${DESTDIR}${mandir}/man1/rgblink.1
$Qinstall -m ${MANMODE} src/link/rgblink.5 ${DESTDIR}${mandir}/man5/rgblink.5
$Qinstall -m ${MANMODE} src/gfx/rgbgfx.1 ${DESTDIR}${mandir}/man1/rgbgfx.1
rgbasm: ${rgbasm_obj} rgbasm: ${rgbasm_obj}
$Q${CC} ${REALCFLAGS} -o $@ ${rgbasm_obj} -lm $Q${CC} ${REALCFLAGS} -o $@ ${rgbasm_obj} -lm
@@ -98,52 +98,56 @@ rgbfix: ${rgbfix_obj}
$Q${CC} ${REALCFLAGS} -o $@ ${rgbfix_obj} $Q${CC} ${REALCFLAGS} -o $@ ${rgbfix_obj}
rgbgfx: ${rgbgfx_obj} rgbgfx: ${rgbgfx_obj}
$Q${CC} ${REALCFLAGS} -o $@ ${rgbgfx_obj} `${PKG_CONFIG} --libs libpng` $Q${CC} ${REALCFLAGS} ${PNGLDFLAGS} -o $@ ${rgbgfx_obj} ${PNGLDLIBS}
# Rules to process files
.y.c: .y.c:
$Q${YACC} -d ${YFLAGS} -o $@ $< $Q${YACC} -d ${YFLAGS} -o $@ $<
.l.o: .l.o:
$Q${RM} $*.c $Q${RM} $*.c
$Q${FLEX} ${LFLAGS} -o $*.c $< $Q${LEX} ${LFLAGS} -o $*.c $<
$Q${CC} ${REALCFLAGS} -c -o $@ $*.c $Q${CC} ${REALCFLAGS} -c -o $@ $*.c
$Q${RM} $*.c $Q${RM} $*.c
.c.o: .c.o:
$Q${CC} ${REALCFLAGS} -c -o $@ $< $Q${CC} ${REALCFLAGS} ${PNGCFLAGS} -c -o $@ $<
src/asm/locallex.o src/asm/globlex.o src/asm/lexer.o: src/asm/asmy.h # Target used to remove all files generated by other Makefile targets.
src/asm/asmy.h: src/asm/asmy.c
src/link/lexer.o : src/link/parser.h clean:
src/link/parser.h : src/link/parser.c $Q${RM} rgbds.7.html gbz80.7.html rgbds.5.html
$Q${RM} rgbasm rgbasm.exe ${rgbasm_obj} rgbasm.1.html rgbasm.5.html
$Q${RM} rgblink rgblink.exe ${rgblink_obj} rgblink.1.html rgblink.5.html
$Q${RM} rgbfix rgbfix.exe ${rgbfix_obj} rgbfix.1.html
$Q${RM} rgbgfx rgbgfx.exe ${rgbgfx_obj} rgbgfx.1.html
$Q${RM} src/asm/asmy.c src/asm/asmy.h
$Q${RM} src/link/lexer.c src/link/parser.c src/link/parser.h
# Below is a target for the project maintainer to easily create win32 exes. # Target used to install the binaries and man pages.
# This is not for Windows users!
# If you're building on Windows with Cygwin or Mingw, just follow the Unix
# install instructions instead.
mingw:
$Q${RM} win32 win64
$Qmkdir win32 win64
$Qenv make clean
$Qenv PKG_CONFIG_PATH=/usr/i686-w64-mingw32/sys-root/mingw/lib/pkgconfig/ \
make CC=i686-w64-mingw32-gcc YACC=bison WARNFLAGS= -j
$Qmv rgbasm win32/rgbasm.exe
$Qmv rgblink win32/rgblink.exe
$Qmv rgbfix win32/rgbfix.exe
$Qmv rgbgfx win32/rgbgfx.exe
$Qenv make clean
$Qenv PKG_CONFIG_PATH=/usr/x86_64-w64-mingw32/sys-root/mingw/lib/pkgconfig/ \
make CC=x86_64-w64-mingw32-gcc YACC=bison WARNFLAGS= -j
$Qmv rgbasm win64/rgbasm.exe
$Qmv rgblink win64/rgblink.exe
$Qmv rgbfix win64/rgbfix.exe
$Qmv rgbgfx win64/rgbgfx.exe
$Qenv make clean
# Below is a target for the project maintainer to easily create web manuals. install: all
$Qmkdir -p ${DESTDIR}${bindir}
$Qinstall ${STRIP} -m ${BINMODE} rgbasm ${DESTDIR}${bindir}/rgbasm
$Qinstall ${STRIP} -m ${BINMODE} rgbfix ${DESTDIR}${bindir}/rgbfix
$Qinstall ${STRIP} -m ${BINMODE} rgblink ${DESTDIR}${bindir}/rgblink
$Qinstall ${STRIP} -m ${BINMODE} rgbgfx ${DESTDIR}${bindir}/rgbgfx
$Qmkdir -p ${DESTDIR}${mandir}/man1 ${DESTDIR}${mandir}/man5 ${DESTDIR}${mandir}/man7
$Qinstall -m ${MANMODE} src/rgbds.7 ${DESTDIR}${mandir}/man7/rgbds.7
$Qinstall -m ${MANMODE} src/gbz80.7 ${DESTDIR}${mandir}/man7/gbz80.7
$Qinstall -m ${MANMODE} src/rgbds.5 ${DESTDIR}${mandir}/man5/rgbds.5
$Qinstall -m ${MANMODE} src/asm/rgbasm.1 ${DESTDIR}${mandir}/man1/rgbasm.1
$Qinstall -m ${MANMODE} src/asm/rgbasm.5 ${DESTDIR}${mandir}/man5/rgbasm.5
$Qinstall -m ${MANMODE} src/fix/rgbfix.1 ${DESTDIR}${mandir}/man1/rgbfix.1
$Qinstall -m ${MANMODE} src/link/rgblink.1 ${DESTDIR}${mandir}/man1/rgblink.1
$Qinstall -m ${MANMODE} src/link/rgblink.5 ${DESTDIR}${mandir}/man5/rgblink.5
$Qinstall -m ${MANMODE} src/gfx/rgbgfx.1 ${DESTDIR}${mandir}/man1/rgbgfx.1
# Target for the project maintainer to easily create web manuals.
# It relies on mandoc: http://mdocml.bsd.lv # It relies on mandoc: http://mdocml.bsd.lv
MANDOC = -Thtml -Ios=General -Oman=%N.%S.html -Ostyle=manual.css
MANDOC := -Thtml -Ios=General -Oman=%N.%S.html -Ostyle=manual.css
wwwman: wwwman:
$Qmandoc ${MANDOC} src/rgbds.7 > rgbds.7.html $Qmandoc ${MANDOC} src/rgbds.7 > rgbds.7.html
@@ -155,3 +159,24 @@ wwwman:
$Qmandoc ${MANDOC} src/link/rgblink.1 > rgblink.1.html $Qmandoc ${MANDOC} src/link/rgblink.1 > rgblink.1.html
$Qmandoc ${MANDOC} src/link/rgblink.5 > rgblink.5.html $Qmandoc ${MANDOC} src/link/rgblink.5 > rgblink.5.html
$Qmandoc ${MANDOC} src/gfx/rgbgfx.1 > rgbgfx.1.html $Qmandoc ${MANDOC} src/gfx/rgbgfx.1 > rgbgfx.1.html
# Targets for the project maintainer to easily create Windows exes.
# This is not for Windows users!
# If you're building on Windows with Cygwin or Mingw, just follow the Unix
# install instructions instead.
mingw32:
$Qenv PKG_CONFIG_PATH=/usr/i686-w64-mingw32/sys-root/mingw/lib/pkgconfig/ \
make CC=i686-w64-mingw32-gcc YACC=bison WARNFLAGS= -j
$Qmv rgbasm rgbasm.exe
$Qmv rgblink rgblink.exe
$Qmv rgbfix rgbfix.exe
$Qmv rgbgfx rgbgfx.exe
mingw64:
$Qenv PKG_CONFIG_PATH=/usr/x86_64-w64-mingw32/sys-root/mingw/lib/pkgconfig/ \
make CC=x86_64-w64-mingw32-gcc YACC=bison WARNFLAGS= -j
$Qmv rgbasm rgbasm.exe
$Qmv rgblink rgblink.exe
$Qmv rgbfix rgbfix.exe
$Qmv rgbgfx rgbgfx.exe

View File

@@ -110,7 +110,6 @@ make command line. For example, to install RGBDS in your home directory instead
of systemwide, run the following: of systemwide, run the following:
```sh ```sh
mkdir -p $HOME/{bin,man/man1,man/man7}
make install PREFIX=$HOME make install PREFIX=$HOME
``` ```

View File

@@ -18,18 +18,23 @@
#include "asm/localasm.h" #include "asm/localasm.h"
#define MAXUNIONS 128
#define MAXMACROARGS 256
#define MAXINCPATHS 128
extern SLONG nLineNo; extern SLONG nLineNo;
extern ULONG nTotalLines; extern ULONG nTotalLines;
extern ULONG nPC; extern ULONG nPC;
extern ULONG nPass; extern ULONG nPass;
extern ULONG nIFDepth; extern ULONG nIFDepth;
extern bool skipElif;
extern ULONG nUnionDepth;
extern ULONG unionStart[MAXUNIONS];
extern ULONG 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 *tHashedSymbols[HASHSIZE]; extern struct sSymbol *tHashedSymbols[HASHSIZE];
extern struct sSymbol *pPCSymbol; extern struct sSymbol *pPCSymbol;
extern bool oDontExpandStrings; extern bool oDontExpandStrings;
#define MAXMACROARGS 256
#define MAXINCPATHS 128
#endif /* // ASM_H */ #endif /* // ASM_H */

View File

@@ -41,6 +41,8 @@ extern void fstk_RunRept(ULONG count);
FILE * FILE *
fstk_FindFile(char *); fstk_FindFile(char *);
int fstk_GetLine(void);
extern int yywrap(void); extern int yywrap(void);
#endif #endif

View File

@@ -80,8 +80,6 @@
*/ */
#define MAXSECTIONSIZE 0x4000
#define NAME_DB "db" #define NAME_DB "db"
#define NAME_DW "dw" #define NAME_DW "dw"
#define NAME_RB "rb" #define NAME_RB "rb"

View File

@@ -26,8 +26,25 @@ extern void opt_Push(void);
extern void opt_Pop(void); extern void opt_Pop(void);
extern void opt_Parse(char *s); extern void opt_Parse(char *s);
/*
* Used for errors that compromise the whole assembly process by affecting the
* folliwing code, potencially making the assembler generate errors caused by
* the first one and unrelated to the code that the assembler complains about.
* It is also used when the assembler goes into an invalid state (for example,
* when it fails to allocate memory).
*/
noreturn void fatalerror(const char *fmt, ...); noreturn void fatalerror(const char *fmt, ...);
/*
* Used for errors that make it impossible to assemble correctly, but don't
* affect the following code. The code will fail to assemble but the user will
* get a list of all errors at the end, making it easier to fix all of them at
* once.
*/
void yyerror(const char *fmt, ...); void yyerror(const char *fmt, ...);
/*
* Used to warn the user about problems that don't prevent the generation of
* valid code.
*/
void warning(const char *fmt, ...); void warning(const char *fmt, ...);
#define YY_FATAL_ERROR fatalerror #define YY_FATAL_ERROR fatalerror

View File

@@ -16,6 +16,8 @@ struct sSymbol {
ULONG ulMacroSize; ULONG ulMacroSize;
char *pMacro; char *pMacro;
SLONG(*Callback) (struct sSymbol *); SLONG(*Callback) (struct sSymbol *);
char tzFileName[_MAX_PATH + 1]; /* File where the symbol was defined. */
ULONG nFileLine; /* Line where the symbol was defined. */
}; };
#define SYMF_RELOC 0x001 /* symbol will be reloc'ed during #define SYMF_RELOC 0x001 /* symbol will be reloc'ed during
* linking, it's absolute value is * linking, it's absolute value is

6
include/common.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef RGBDS_COMMON_H
#define RGBDS_COMMON_H
#define RGBDS_OBJECT_VERSION_STRING "RGB5"
#endif /* RGBDS_COMMON_H */

21
include/extern/version.h vendored Normal file
View File

@@ -0,0 +1,21 @@
/*
* Copyright (C) 2017 Antonio Nino Diaz <antonio_nd@outlook.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define PACKAGE_VERSION_MAJOR (0)
#define PACKAGE_VERSION_MINOR (3)
#define PACKAGE_VERSION_PATCH (2)
const char * get_package_version_string(void);

View File

@@ -89,6 +89,9 @@ struct sSymbol {
SLONG nSectionID; /* internal to object.c */ SLONG nSectionID; /* internal to object.c */
struct sSection *pSection; struct sSection *pSection;
SLONG nOffset; SLONG nOffset;
char *pzObjFileName; /* Object file where the symbol is located. */
char *pzFileName; /* Source file where the symbol was defined. */
ULONG nFileLine; /* Line where the symbol was defined. */
}; };
enum ePatchType { enum ePatchType {

View File

@@ -4,7 +4,8 @@
#include "types.h" #include "types.h"
void sym_Init(void); void sym_Init(void);
void sym_CreateSymbol(char *tzName, SLONG nValue, SLONG nBank); void sym_CreateSymbol(char *tzName, SLONG nValue, SLONG nBank,
char *tzObjFileName, char *tzFileName, ULONG nFileLine);
SLONG sym_GetValue(char *tzName); SLONG sym_GetValue(char *tzName);
SLONG sym_GetBank(char *tzName); SLONG sym_GetBank(char *tzName);

View File

@@ -23,7 +23,7 @@ ULONG ulNewMacroSize;
void void
bankrangecheck(char *name, ULONG secttype, SLONG org, SLONG bank) bankrangecheck(char *name, ULONG secttype, SLONG org, SLONG bank)
{ {
SLONG minbank, maxbank; SLONG minbank = 0, maxbank = 0;
char *stype = NULL; char *stype = NULL;
switch (secttype) { switch (secttype) {
case SECT_ROMX: case SECT_ROMX:
@@ -291,68 +291,73 @@ void copymacro( void )
ULONG isIf(char *s) ULONG isIf(char *s)
{ {
return( (strncasecmp(s,"If",2)==0) && isWhiteSpace(*(s-1)) && isWhiteSpace(s[2]) ); return((strncasecmp(s,"If",2) == 0) && isWhiteSpace(s[-1]) && isWhiteSpace(s[2]));
}
ULONG isElif(char *s)
{
return((strncasecmp(s,"Elif",4) == 0) && isWhiteSpace(s[-1]) && isWhiteSpace(s[4]));
} }
ULONG isElse(char *s) ULONG isElse(char *s)
{ {
return( (strncasecmp(s,"Else",4)==0) && isWhiteSpace(*(s-1)) && isWhiteSpace(s[4]) ); return((strncasecmp(s,"Else",4) == 0) && isWhiteSpace(s[-1]) && isWhiteSpace(s[4]));
} }
ULONG isEndc(char *s) ULONG isEndc(char *s)
{ {
return( (strncasecmp(s,"Endc",4)==0) && isWhiteSpace(*(s-1)) && isWhiteSpace(s[4]) ); return((strncasecmp(s,"Endc",4) == 0) && isWhiteSpace(s[-1]) && isWhiteSpace(s[4]));
} }
void if_skip_to_else( void ) void if_skip_to_else()
{ {
SLONG level=1, len, instring=0; SLONG level = 1;
bool inString = false;
char *src=pCurrentBuffer->pBuffer; char *src=pCurrentBuffer->pBuffer;
while( *src && level ) while (*src && level) {
{ if (*src == '\n') {
if( *src=='\n' ) nLineNo++;
nLineNo+=1; }
if( instring==0 ) if (!inString) {
{ if (isIf(src)) {
if( isIf(src) ) level++;
{
level+=1;
src += 2; src += 2;
}
else if( level==1 && isElse(src) ) } else if (level == 1 && isElif(src)) {
{ level--;
level-=1; skipElif = false;
} else if (level == 1 && isElse(src)) {
level--;
src += 4;
} else if (isEndc(src)) {
level--;
if (level != 0) {
src += 4; src += 4;
} }
else if( isEndc(src) )
{ } else {
level-=1; if (*src=='\"') {
if( level!=0 ) inString = true;
src+=4;
} }
else src++;
{
if( *src=='\"' )
instring=1;
src+=1;
} }
} } else {
else switch (*src) {
{ case '\\':
if( *src=='\\' )
{
src += 2; src += 2;
} break;
else if( *src=='\"' )
{ case '\"':
src+=1; src++;
instring=0; inString = false;
}
else default:
{ src++;
src+=1; break;
} }
} }
} }
@@ -361,57 +366,56 @@ void if_skip_to_else( void )
fatalerror("Unterminated IF construct"); fatalerror("Unterminated IF construct");
} }
len=src-pCurrentBuffer->pBuffer; SLONG len = src - pCurrentBuffer->pBuffer;
yyskipbytes(len); yyskipbytes(len);
yyunput('\n'); yyunput('\n');
nLineNo-=1; nLineNo--;
} }
void if_skip_to_endc( void ) void if_skip_to_endc()
{ {
SLONG level=1, len, instring=0; SLONG level = 1;
bool inString = false;
char *src=pCurrentBuffer->pBuffer; char *src=pCurrentBuffer->pBuffer;
while( *src && level ) while (*src && level) {
{ if (*src == '\n') {
if( *src=='\n' ) nLineNo++;
nLineNo+=1;
if( instring==0 )
{
if( isIf(src) )
{
level+=1;
src+=2;
} }
else if( isEndc(src) )
{ if (!inString) {
level-=1; if (isIf(src)) {
if( level!=0 ) level++;
src += 2;
} else if (isEndc(src)) {
level--;
if (level != 0) {
src += 4; src += 4;
} }
else
{ } else {
if( *src=='\"' ) if (*src=='\"') {
instring=1; inString = true;
src+=1; }
src++;
} }
} }
else else {
{ switch (*src) {
if( *src=='\\' ) case '\\':
{
src += 2; src += 2;
} break;
else if( *src=='\"' )
{ case '\"':
src+=1; src++;
instring=0; inString = false;
} break;
else
{ default:
src+=1; src++;
break;
} }
} }
} }
@@ -420,11 +424,39 @@ void if_skip_to_endc( void )
fatalerror("Unterminated IF construct"); fatalerror("Unterminated IF construct");
} }
len=src-pCurrentBuffer->pBuffer; SLONG len = src - pCurrentBuffer->pBuffer;
yyskipbytes(len); yyskipbytes(len);
yyunput('\n'); yyunput('\n');
nLineNo-=1; nLineNo--;
}
void startUnion() {
if (!pCurrentSection) {
fatalerror("UNIONs must be inside a SECTION");
}
ULONG unionIndex = nUnionDepth;
nUnionDepth++;
if (nUnionDepth > MAXUNIONS) {
fatalerror("Too many nested UNIONs");
}
unionStart[unionIndex] = nPC;
unionSize[unionIndex] = 0;
}
void updateUnion() {
ULONG unionIndex = nUnionDepth - 1;
ULONG size = nPC - unionStart[unionIndex];
if (size > unionSize[unionIndex]) {
unionSize[unionIndex] = size;
}
nPC = unionStart[unionIndex];
pCurrentSection->nPC = unionStart[unionIndex];
pPCSymbol->nValue = unionStart[unionIndex];
} }
%} %}
@@ -439,6 +471,7 @@ void if_skip_to_endc( void )
%type <sVal> relocconst %type <sVal> relocconst
%type <nConstValue> const %type <nConstValue> const
%type <nConstValue> uconst
%type <nConstValue> const_3bit %type <nConstValue> const_3bit
%type <sVal> const_8bit %type <sVal> const_8bit
%type <sVal> const_16bit %type <sVal> const_16bit
@@ -491,7 +524,7 @@ void if_skip_to_endc( void )
%token <tzSym> T_POP_SET %token <tzSym> T_POP_SET
%token <tzSym> T_POP_EQUS %token <tzSym> T_POP_EQUS
%token T_POP_INCLUDE T_POP_PRINTF T_POP_PRINTT T_POP_PRINTV T_POP_IF T_POP_ELSE T_POP_ENDC %token T_POP_INCLUDE T_POP_PRINTF T_POP_PRINTT T_POP_PRINTV T_POP_IF T_POP_ELIF T_POP_ELSE T_POP_ENDC
%token T_POP_IMPORT T_POP_EXPORT T_POP_GLOBAL %token T_POP_IMPORT T_POP_EXPORT T_POP_GLOBAL
%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
@@ -501,6 +534,7 @@ void if_skip_to_endc( void )
%token T_POP_MACRO %token T_POP_MACRO
%token T_POP_ENDM %token T_POP_ENDM
%token T_POP_RSRESET T_POP_RSSET %token T_POP_RSRESET T_POP_RSSET
%token T_POP_UNION T_POP_NEXTU T_POP_ENDU
%token T_POP_INCBIN T_POP_REPT %token T_POP_INCBIN T_POP_REPT
%token T_POP_CHARMAP %token T_POP_CHARMAP
%token T_POP_SHIFT %token T_POP_SHIFT
@@ -587,7 +621,11 @@ label : /* empty */
else else
sym_AddReloc($1); sym_AddReloc($1);
} | T_LABEL ':' ':' { } | T_LABEL ':' ':' {
if ($1[0] == '.') {
sym_AddLocalReloc($1);
} else {
sym_AddReloc($1); sym_AddReloc($1);
}
sym_Export($1); sym_Export($1);
}; };
@@ -622,6 +660,7 @@ simple_pseudoop : include
| printt | printt
| printv | printv
| if | if
| elif
| else | else
| endc | endc
| import | import
@@ -634,6 +673,9 @@ simple_pseudoop : include
| section | section
| rsreset | rsreset
| rsset | rsset
| union
| nextu
| endu
| incbin | incbin
| charmap | charmap
| rept | rept
@@ -688,7 +730,7 @@ shift : T_POP_SHIFT
{ sym_ShiftCurrentMacroArgs(); } { sym_ShiftCurrentMacroArgs(); }
; ;
rept : T_POP_REPT const rept : T_POP_REPT uconst
{ {
copyrept(); copyrept();
fstk_RunRept( $2 ); fstk_RunRept( $2 );
@@ -706,7 +748,7 @@ equs : T_LABEL T_POP_EQUS string
{ sym_AddString( $1, $3 ); } { sym_AddString( $1, $3 ); }
; ;
rsset : T_POP_RSSET const rsset : T_POP_RSSET uconst
{ sym_AddSet( "_RS", $2 ); } { sym_AddSet( "_RS", $2 ); }
; ;
@@ -714,28 +756,53 @@ rsreset : T_POP_RSRESET
{ sym_AddSet( "_RS", 0 ); } { sym_AddSet( "_RS", 0 ); }
; ;
rl : T_LABEL T_POP_RL const rl : T_LABEL T_POP_RL uconst
{ {
sym_AddEqu( $1, sym_GetConstantValue("_RS") ); sym_AddEqu( $1, sym_GetConstantValue("_RS") );
sym_AddSet( "_RS", sym_GetConstantValue("_RS")+4*$3 ); sym_AddSet( "_RS", sym_GetConstantValue("_RS")+4*$3 );
} }
; ;
rw : T_LABEL T_POP_RW const rw : T_LABEL T_POP_RW uconst
{ {
sym_AddEqu( $1, sym_GetConstantValue("_RS") ); sym_AddEqu( $1, sym_GetConstantValue("_RS") );
sym_AddSet( "_RS", sym_GetConstantValue("_RS")+2*$3 ); sym_AddSet( "_RS", sym_GetConstantValue("_RS")+2*$3 );
} }
; ;
rb : T_LABEL T_POP_RB const rb : T_LABEL T_POP_RB uconst
{ {
sym_AddEqu( $1, sym_GetConstantValue("_RS") ); sym_AddEqu( $1, sym_GetConstantValue("_RS") );
sym_AddSet( "_RS", sym_GetConstantValue("_RS")+$3 ); sym_AddSet( "_RS", sym_GetConstantValue("_RS")+$3 );
} }
; ;
ds : T_POP_DS const union : T_POP_UNION {
startUnion();
};
nextu : T_POP_NEXTU {
if (nUnionDepth <= 0) {
fatalerror("Found NEXTU outside of a UNION construct");
}
updateUnion();
};
endu : T_POP_ENDU {
if (nUnionDepth <= 0) {
fatalerror("Found ENDU outside of a UNION construct");
}
updateUnion();
nUnionDepth--;
nPC = unionStart[nUnionDepth] + unionSize[nUnionDepth];
pCurrentSection->nPC = nPC;
pPCSymbol->nValue = nPC;
};
ds : T_POP_DS uconst
{ out_Skip( $2 ); } { out_Skip( $2 ); }
; ;
@@ -817,7 +884,7 @@ include : T_POP_INCLUDE string
incbin : T_POP_INCBIN string incbin : T_POP_INCBIN string
{ out_BinaryFile( $2 ); } { out_BinaryFile( $2 ); }
| T_POP_INCBIN string ',' const ',' const | T_POP_INCBIN string ',' uconst ',' uconst
{ {
out_BinaryFileSlice( $2, $4, $6 ); out_BinaryFileSlice( $2, $4, $6 );
} }
@@ -862,26 +929,47 @@ printf : T_POP_PRINTF const
} }
; ;
if : T_POP_IF const if : T_POP_IF const {
{ nIFDepth++;
nIFDepth+=1; if (!$2) {
if( !$2 ) if_skip_to_else(); // Continue parsing after ELSE, or at ELIF or ENDC keyword
{
if_skip_to_else(); /* will continue parsing just after ELSE or just at ENDC keyword */
} }
};
elif : T_POP_ELIF const {
if (nIFDepth <= 0) {
fatalerror("Found ELIF outside an IF construct");
} }
else : T_POP_ELSE if (skipElif) {
{ // This is for when ELIF is reached at the end of an IF or ELIF block for which the condition was true.
if_skip_to_endc(); /* will continue parsing just at ENDC keyword */ if_skip_to_endc(); // Continue parsing at ENDC keyword
}
;
endc : T_POP_ENDC } else {
{ // This is for when ELIF is skipped to because the condition of the previous IF or ELIF block was false.
nIFDepth-=1; skipElif = true;
if (!$2) {
if_skip_to_else(); // Continue parsing after ELSE, or at ELIF or ENDC keyword
} }
; }
};
else : T_POP_ELSE {
if (nIFDepth <= 0) {
fatalerror("Found ELSE outside an IF construct");
}
if_skip_to_endc(); // Continue parsing at ENDC keyword
};
endc : T_POP_ENDC {
if (nIFDepth <= 0) {
fatalerror("Found ENDC outside an IF construct");
}
nIFDepth--;
};
const_3bit : const const_3bit : const
{ {
@@ -1038,6 +1126,14 @@ relocconst : T_ID
{ $$ = $2; } { $$ = $2; }
; ;
uconst : const
{
if($1 < 0)
fatalerror("Constant mustn't be negative: %d", $1);
$$=$1;
}
;
const : T_ID { $$ = sym_GetConstantValue($1); } const : T_ID { $$ = sym_GetConstantValue($1); }
| T_NUMBER { $$ = $1; } | T_NUMBER { $$ = $1; }
| string { $$ = str2int($1); } | string { $$ = str2int($1); }
@@ -1064,12 +1160,14 @@ const : T_ID { $$ = sym_GetConstantValue($1); }
| const T_OP_SHL const { $$ = $1 << $3; } | const T_OP_SHL const { $$ = $1 << $3; }
| const T_OP_SHR const { $$ = $1 >> $3; } | const T_OP_SHR const { $$ = $1 >> $3; }
| const T_OP_MUL const { $$ = $1 * $3; } | const T_OP_MUL const { $$ = $1 * $3; }
| const T_OP_DIV const { | const T_OP_DIV const
{
if ($3 == 0) if ($3 == 0)
fatalerror("division by zero"); fatalerror("division by zero");
$$ = $1 / $3; $$ = $1 / $3;
} }
| const T_OP_MOD const { | const T_OP_MOD const
{
if ($3 == 0) if ($3 == 0)
fatalerror("division by zero"); fatalerror("division by zero");
$$ = $1 % $3; $$ = $1 % $3;
@@ -1109,7 +1207,7 @@ const : T_ID { $$ = sym_GetConstantValue($1); }
string : T_STRING string : T_STRING
{ strcpy($$,$1); } { strcpy($$,$1); }
| T_OP_STRSUB '(' string ',' const ',' const ')' | T_OP_STRSUB '(' string ',' uconst ',' uconst ')'
{ strncpy($$,$3+$5-1,$7); $$[$7]=0; } { strncpy($$,$3+$5-1,$7); $$[$7]=0; }
| T_OP_STRCAT '(' string ',' string ')' | T_OP_STRCAT '(' string ',' string ')'
{ strcpy($$,$3); strcat($$,$5); } { strcpy($$,$3); strcat($$,$5); }
@@ -1123,33 +1221,33 @@ section:
{ {
out_NewSection($2,$4); out_NewSection($2,$4);
} }
| T_POP_SECTION string ',' sectiontype '[' const ']' | T_POP_SECTION string ',' sectiontype '[' uconst ']'
{ {
if( $6>=0 && $6<0x10000 ) if( $6>=0 && $6<0x10000 )
out_NewAbsSection($2,$4,$6,-1); out_NewAbsSection($2,$4,$6,-1);
else else
yyerror("Address $%x not 16-bit", $6); yyerror("Address $%x not 16-bit", $6);
} }
| T_POP_SECTION string ',' sectiontype ',' T_OP_ALIGN '[' const ']' | T_POP_SECTION string ',' sectiontype ',' T_OP_ALIGN '[' uconst ']'
{ {
out_NewAlignedSection($2, $4, $8, -1); out_NewAlignedSection($2, $4, $8, -1);
} }
| T_POP_SECTION string ',' sectiontype ',' T_OP_BANK '[' const ']' | T_POP_SECTION string ',' sectiontype ',' T_OP_BANK '[' uconst ']'
{ {
bankrangecheck($2, $4, -1, $8); bankrangecheck($2, $4, -1, $8);
} }
| T_POP_SECTION string ',' sectiontype '[' const ']' ',' T_OP_BANK '[' const ']' | T_POP_SECTION string ',' sectiontype '[' uconst ']' ',' T_OP_BANK '[' uconst ']'
{ {
if ($6 < 0 || $6 > 0x10000) { if ($6 < 0 || $6 > 0x10000) {
yyerror("Address $%x not 16-bit", $6); yyerror("Address $%x not 16-bit", $6);
} }
bankrangecheck($2, $4, $6, $11); bankrangecheck($2, $4, $6, $11);
} }
| T_POP_SECTION string ',' sectiontype ',' T_OP_ALIGN '[' const ']' ',' T_OP_BANK '[' const ']' | T_POP_SECTION string ',' sectiontype ',' T_OP_ALIGN '[' uconst ']' ',' T_OP_BANK '[' uconst ']'
{ {
out_NewAlignedSection($2, $4, $8, $13); out_NewAlignedSection($2, $4, $8, $13);
} }
| T_POP_SECTION string ',' sectiontype ',' T_OP_BANK '[' const ']' ',' T_OP_ALIGN '[' const ']' | T_POP_SECTION string ',' sectiontype ',' T_OP_BANK '[' uconst ']' ',' T_OP_ALIGN '[' uconst ']'
{ {
out_NewAlignedSection($2, $4, $13, $8); out_NewAlignedSection($2, $4, $13, $8);
} }
@@ -1351,7 +1449,7 @@ z80_ldio : T_Z80_LDIO T_MODE_A comma op_mem_ind
if( (!rpn_isReloc(&$4)) if( (!rpn_isReloc(&$4))
&& ($4.nVal<0 || ($4.nVal>0xFF && $4.nVal<0xFF00) || $4.nVal>0xFFFF) ) && ($4.nVal<0 || ($4.nVal>0xFF && $4.nVal<0xFF00) || $4.nVal>0xFFFF) )
{ {
yyerror("Source address $%x not in HRAM ($FF00 to $FFFE)", $4.nVal); yyerror("Source address $%x not in $FF00 to $FFFF", $4.nVal);
} }
out_AbsByte(0xF0); out_AbsByte(0xF0);
@@ -1365,7 +1463,7 @@ z80_ldio : T_Z80_LDIO T_MODE_A comma op_mem_ind
if( (!rpn_isReloc(&$2)) if( (!rpn_isReloc(&$2))
&& ($2.nVal<0 || ($2.nVal>0xFF && $2.nVal<0xFF00) || $2.nVal>0xFFFF) ) && ($2.nVal<0 || ($2.nVal>0xFF && $2.nVal<0xFF00) || $2.nVal>0xFFFF) )
{ {
yyerror("Destination address $%x not in HRAM ($FF00 to $FFFE)", $2.nVal); yyerror("Destination address $%x not in $FF00 to $FFFF", $2.nVal);
} }
out_AbsByte(0xE0); out_AbsByte(0xE0);
@@ -1385,7 +1483,10 @@ z80_ld : z80_ld_mem
; ;
z80_ld_hl : T_Z80_LD T_MODE_HL comma '[' T_MODE_SP const_8bit ']' z80_ld_hl : T_Z80_LD T_MODE_HL comma '[' T_MODE_SP const_8bit ']'
{ out_AbsByte(0xF8); out_RelByte(&$6); } {
out_AbsByte(0xF8); out_RelByte(&$6);
warning("'LD HL,[SP+e8]' is obsolete, use 'LD HL,SP+e8' instead.");
}
| T_Z80_LD T_MODE_HL comma T_MODE_SP const_8bit | T_Z80_LD T_MODE_HL comma T_MODE_SP const_8bit
{ out_AbsByte(0xF8); out_RelByte(&$5); } { out_AbsByte(0xF8); out_RelByte(&$5); }
| T_Z80_LD T_MODE_HL comma const_16bit | T_Z80_LD T_MODE_HL comma const_16bit

View File

@@ -36,10 +36,13 @@ ULONG nCurrentREPTBlockCount;
ULONG ulMacroReturnValue; ULONG ulMacroReturnValue;
extern char *tzObjectname;
extern FILE *dependfile;
/* /*
* defines for nCurrentStatus * defines for nCurrentStatus
*/ */
#define STAT_isInclude 0 #define STAT_isInclude 0 /* 'Normal' state as well */
#define STAT_isMacro 1 #define STAT_isMacro 1
#define STAT_isMacroArg 2 #define STAT_isMacroArg 2
#define STAT_isREPTBlock 3 #define STAT_isREPTBlock 3
@@ -148,6 +151,37 @@ popcontext(void)
return (1); return (1);
} }
int
fstk_GetLine(void)
{
struct sContext *pLastFile, **ppLastFile;
switch (nCurrentStatus) {
case STAT_isInclude:
/* This is the normal mode, also used when including a file. */
return nLineNo;
case STAT_isMacro:
break; /* Peek top file of the stack */
case STAT_isMacroArg:
return nLineNo; /* ??? */
case STAT_isREPTBlock:
break; /* Peek top file of the stack */
}
if ((pLastFile = pFileStack) != NULL) {
ppLastFile = &pFileStack;
while (pLastFile->pNext) {
ppLastFile = &(pLastFile->pNext);
pLastFile = *ppLastFile;
}
return pLastFile->nLine;
}
/* This is only reached if the lexer is in REPT or MACRO mode but there
* are no saved contexts with the origin of said REPT or MACRO. */
fatalerror("fstk_GetLine: Internal error.");
}
int int
yywrap(void) yywrap(void)
{ {
@@ -198,6 +232,9 @@ fstk_FindFile(char *fname)
FILE *f; FILE *f;
if ((f = fopen(fname, "rb")) != NULL || errno != ENOENT) { if ((f = fopen(fname, "rb")) != NULL || errno != ENOENT) {
if (dependfile) {
fprintf(dependfile, "%s: %s\n", tzObjectname, fname);
}
return f; return f;
} }
@@ -211,6 +248,9 @@ fstk_FindFile(char *fname)
} }
if ((f = fopen(path, "rb")) != NULL || errno != ENOENT) { if ((f = fopen(path, "rb")) != NULL || errno != ENOENT) {
if (dependfile) {
fprintf(dependfile, "%s: %s\n", tzObjectname, path);
}
return f; return f;
} }
} }

View File

@@ -329,8 +329,13 @@ struct sLexInitString staticstrings[] = {
{"if", T_POP_IF}, {"if", T_POP_IF},
{"else", T_POP_ELSE}, {"else", T_POP_ELSE},
{"elif", T_POP_ELIF},
{"endc", T_POP_ENDC}, {"endc", T_POP_ENDC},
{"union", T_POP_UNION},
{"nextu", T_POP_NEXTU},
{"endu", T_POP_ENDU},
{"wram0", T_SECT_WRAM0}, {"wram0", T_SECT_WRAM0},
{"vram", T_SECT_VRAM}, {"vram", T_SECT_VRAM},
{"romx", T_SECT_ROMX}, {"romx", T_SECT_ROMX},
@@ -475,6 +480,7 @@ setuplex(void)
lex_FloatAddSecondRange(id, '\\', '\\'); lex_FloatAddSecondRange(id, '\\', '\\');
lex_FloatAddSecondRange(id, '@', '@'); lex_FloatAddSecondRange(id, '@', '@');
lex_FloatAddSecondRange(id, '#', '#'); lex_FloatAddSecondRange(id, '#', '#');
lex_FloatAddRange(id, '.', '.');
lex_FloatAddRange(id, 'a', 'z'); lex_FloatAddRange(id, 'a', 'z');
lex_FloatAddRange(id, 'A', 'Z'); lex_FloatAddRange(id, 'A', 'Z');
lex_FloatAddRange(id, '0', '9'); lex_FloatAddRange(id, '0', '9');

View File

@@ -535,7 +535,7 @@ yylex_ReadBracketedSymbol(char *dest, size_t index)
if (*pLexBuffer == '}') if (*pLexBuffer == '}')
pLexBuffer++; pLexBuffer++;
else else
yyerror("Missing }"); fatalerror("Missing }");
return length; return length;
} }
@@ -601,7 +601,7 @@ yylex_ReadQuotedString()
if (*pLexBuffer == '"') if (*pLexBuffer == '"')
pLexBuffer++; pLexBuffer++;
else else
yyerror("Unterminated string"); fatalerror("Unterminated string");
} }
ULONG ULONG

View File

@@ -12,6 +12,7 @@
#include "asm/main.h" #include "asm/main.h"
#include "extern/err.h" #include "extern/err.h"
#include "extern/reallocarray.h" #include "extern/reallocarray.h"
#include "extern/version.h"
int yyparse(void); int yyparse(void);
void setuplex(void); void setuplex(void);
@@ -22,10 +23,15 @@ char **cldefines;
clock_t nStartClock, nEndClock; clock_t nStartClock, nEndClock;
SLONG nLineNo; SLONG nLineNo;
ULONG nTotalLines, nPass, nPC, nIFDepth, nErrors; ULONG nTotalLines, nPass, nPC, nIFDepth, nUnionDepth, nErrors;
bool skipElif;
ULONG unionStart[128], unionSize[128];
extern int yydebug; extern int yydebug;
FILE *dependfile;
extern char *tzObjectname;
/* /*
* Option stack * Option stack
*/ */
@@ -273,8 +279,8 @@ static void
usage(void) usage(void)
{ {
printf( printf(
"Usage: rgbasm [-hvE] [-b chars] [-Dname[=value]] [-g chars] [-i path]\n" "Usage: rgbasm [-EhVvw] [-b chars] [-Dname[=value]] [-g chars] [-i path]\n"
" [-o outfile] [-p pad_value] file.asm\n"); " [-M dependfile] [-o outfile] [-p pad_value] file.asm\n");
exit(1); exit(1);
} }
@@ -288,6 +294,8 @@ main(int argc, char *argv[])
char *tzMainfile; char *tzMainfile;
dependfile = NULL;
cldefines_size = 32; cldefines_size = 32;
cldefines = reallocarray(cldefines, cldefines_size, cldefines = reallocarray(cldefines, cldefines_size,
2 * sizeof(void *)); 2 * sizeof(void *));
@@ -317,7 +325,7 @@ main(int argc, char *argv[])
newopt = CurrentOptions; newopt = CurrentOptions;
while ((ch = getopt(argc, argv, "b:D:g:hi:o:p:vEw")) != -1) { while ((ch = getopt(argc, argv, "b:D:g:hi:M:o:p:EVvw")) != -1) {
switch (ch) { switch (ch) {
case 'b': case 'b':
if (strlen(optarg) == 2) { if (strlen(optarg) == 2) {
@@ -331,6 +339,9 @@ main(int argc, char *argv[])
case 'D': case 'D':
opt_AddDefine(optarg); opt_AddDefine(optarg);
break; break;
case 'E':
newopt.exportall = true;
break;
case 'g': case 'g':
if (strlen(optarg) == 4) { if (strlen(optarg) == 4) {
newopt.gbgfx[0] = optarg[1]; newopt.gbgfx[0] = optarg[1];
@@ -348,6 +359,11 @@ main(int argc, char *argv[])
case 'i': case 'i':
fstk_AddIncludePath(optarg); fstk_AddIncludePath(optarg);
break; break;
case 'M':
if ((dependfile = fopen(optarg, "w")) == NULL) {
err(1, "Could not open dependfile %s", optarg);
}
break;
case 'o': case 'o':
out_SetFileName(optarg); out_SetFileName(optarg);
break; break;
@@ -361,17 +377,18 @@ main(int argc, char *argv[])
"between 0 and 0xFF"); "between 0 and 0xFF");
} }
break; break;
case 'V':
printf("rgbasm %s\n", get_package_version_string());
exit(0);
case 'v': case 'v':
newopt.verbose = true; newopt.verbose = true;
break; break;
case 'E':
newopt.exportall = true;
break;
case 'w': case 'w':
newopt.warnings = false; newopt.warnings = false;
break; break;
default: default:
usage(); usage();
/* NOTREACHED */
} }
} }
argc -= optind; argc -= optind;
@@ -392,11 +409,20 @@ main(int argc, char *argv[])
printf("Assembling %s\n", tzMainfile); printf("Assembling %s\n", tzMainfile);
} }
if (dependfile) {
if (!tzObjectname)
errx(1, "Dependency files can only be created if an output object file is specified.\n");
fprintf(dependfile, "%s: %s\n", tzObjectname, tzMainfile);
}
nStartClock = clock(); nStartClock = clock();
nLineNo = 1; nLineNo = 1;
nTotalLines = 0; nTotalLines = 0;
nIFDepth = 0; nIFDepth = 0;
skipElif = true;
nUnionDepth = 0;
nPC = 0; nPC = 0;
nPass = 1; nPass = 1;
nErrors = 0; nErrors = 0;
@@ -420,9 +446,15 @@ main(int argc, char *argv[])
errx(1, "Unterminated IF construct (%ld levels)!", nIFDepth); errx(1, "Unterminated IF construct (%ld levels)!", nIFDepth);
} }
if (nUnionDepth != 0) {
errx(1, "Unterminated UNION construct (%ld levels)!", nUnionDepth);
}
nTotalLines = 0; nTotalLines = 0;
nLineNo = 1; nLineNo = 1;
nIFDepth = 0; nIFDepth = 0;
skipElif = true;
nUnionDepth = 0;
nPC = 0; nPC = 0;
nPass = 2; nPass = 2;
nErrors = 0; nErrors = 0;

View File

@@ -15,10 +15,9 @@
#include "asm/main.h" #include "asm/main.h"
#include "asm/rpn.h" #include "asm/rpn.h"
#include "asm/fstack.h" #include "asm/fstack.h"
#include "common.h"
#include "extern/err.h" #include "extern/err.h"
#define SECTIONCHUNK 0x4000
void out_SetCurrentSection(struct Section * pSect); void out_SetCurrentSection(struct Section * pSect);
struct Patch { struct Patch {
@@ -80,6 +79,24 @@ out_PopSection(void)
fatalerror("No entries in the section stack"); fatalerror("No entries in the section stack");
} }
ULONG
getmaxsectionsize(ULONG secttype, char * sectname)
{
switch (secttype)
{
case SECT_ROM0: return 0x8000; /* If ROMX sections not used. */
case SECT_ROMX: return 0x4000;
case SECT_VRAM: return 0x2000;
case SECT_SRAM: return 0x2000;
case SECT_WRAM0: return 0x2000; /* If WRAMX sections not used. */
case SECT_WRAMX: return 0x1000;
case SECT_OAM: return 0xA0;
case SECT_HRAM: return 0x7F;
default: break;
}
errx(1, "Section \"%s\" has an invalid section type.", sectname);
}
/* /*
* Count the number of symbols used in this object * Count the number of symbols used in this object
*/ */
@@ -244,10 +261,6 @@ writesymbol(struct sSymbol * pSym, FILE * f)
sectid = -1; sectid = -1;
type = SYM_IMPORT; type = SYM_IMPORT;
} else { } else {
if (pSym->nType & SYMF_LOCAL) {
strcpy(symname, pSym->pScope->tzName);
strcat(symname, pSym->tzName);
} else
strcpy(symname, pSym->tzName); strcpy(symname, pSym->tzName);
if (pSym->nType & SYMF_EXPORT) { if (pSym->nType & SYMF_EXPORT) {
@@ -270,6 +283,9 @@ writesymbol(struct sSymbol * pSym, FILE * f)
fputc(type, f); fputc(type, f);
if (type != SYM_IMPORT) { if (type != SYM_IMPORT) {
fputstring(pSym->tzFileName, f);
fputlong(pSym->nFileLine, f);
fputlong(sectid, f); fputlong(sectid, f);
fputlong(offset, f); fputlong(offset, f);
} }
@@ -441,39 +457,39 @@ checksection(void)
* this much initialized data * this much initialized data
*/ */
void void
checkcodesection(SLONG size) checkcodesection(void)
{ {
checksection(); checksection();
if (pCurrentSection->nType != SECT_ROM0 && if (pCurrentSection->nType != SECT_ROM0 &&
pCurrentSection->nType != SECT_ROMX) { pCurrentSection->nType != SECT_ROMX) {
errx(1, "Section '%s' cannot contain code or data (not a " fatalerror("Section '%s' cannot contain code or data (not ROM0 or ROMX)",
"ROM0 or ROMX)", pCurrentSection->pzName); pCurrentSection->pzName);
} else if (nUnionDepth > 0) {
fatalerror("UNIONs cannot contain code or data");
} }
if (pCurrentSection->nPC + size > MAXSECTIONSIZE) {
/*
* N.B.: This check is not sufficient to ensure the section
* will fit, because there can be multiple sections of this
* type. The definitive check must be done at the linking
* stage.
*/
errx(1, "Section '%s' is too big (old size %d + %d > %d)",
pCurrentSection->pzName, pCurrentSection->nPC, size,
MAXSECTIONSIZE);
} }
if (((pCurrentSection->nPC % SECTIONCHUNK) >
((pCurrentSection->nPC + size) % SECTIONCHUNK)) &&
(pCurrentSection->nType == SECT_ROM0 ||
pCurrentSection->nType == SECT_ROMX)) {
pCurrentSection->tData = realloc(pCurrentSection->tData,
((pCurrentSection->nPC + size) / SECTIONCHUNK + 1) *
SECTIONCHUNK);
if (pCurrentSection->tData == NULL) { /*
err(1, "Could not expand section"); * Check if the section has grown too much.
*/
void
checksectionoverflow(ULONG delta_size)
{
ULONG maxsize = getmaxsectionsize(pCurrentSection->nType,
pCurrentSection->pzName);
if (pCurrentSection->nPC + delta_size > maxsize) {
/*
* This check is here to trap broken code that generates
* sections that are too big and to prevent the assembler from
* generating huge object files or trying to allocate too much
* memory.
* The real check must be done at the linking stage.
*/
fatalerror("Section '%s' is too big (max size = 0x%X bytes).",
pCurrentSection->pzName, maxsize);
} }
} }
return;
}
/* /*
* Write an objectfile * Write an objectfile
@@ -489,7 +505,9 @@ out_WriteObject(void)
struct PatchSymbol *pSym; struct PatchSymbol *pSym;
struct Section *pSect; struct Section *pSect;
fwrite("RGB4", 1, 4, f); fwrite(RGBDS_OBJECT_VERSION_STRING, 1,
strlen(RGBDS_OBJECT_VERSION_STRING), f);
fputlong(countsymbols(), f); fputlong(countsymbols(), f);
fputlong(countsections(), f); fputlong(countsections(), f);
@@ -580,10 +598,15 @@ out_FindSection(char *pzName, ULONG secttype, SLONG org, SLONG bank, SLONG align
pSect->charmap = NULL; pSect->charmap = NULL;
pPatchSymbols = NULL; pPatchSymbols = NULL;
if ((pSect->tData = malloc(SECTIONCHUNK)) != NULL) { pSect->tData = NULL;
return (pSect); if (secttype == SECT_ROM0 || secttype == SECT_ROMX) {
} else /* It is only needed to allocate memory for ROM
* sections. */
ULONG sectsize = getmaxsectionsize(secttype, pzName);
if ((pSect->tData = malloc(sectsize)) == NULL)
fatalerror("Not enough memory for section"); fatalerror("Not enough memory for section");
}
return (pSect);
} else } else
fatalerror("Not enough memory for sectionname"); fatalerror("Not enough memory for sectionname");
} else } else
@@ -598,6 +621,10 @@ out_FindSection(char *pzName, ULONG secttype, SLONG org, SLONG bank, SLONG align
void void
out_SetCurrentSection(struct Section * pSect) out_SetCurrentSection(struct Section * pSect)
{ {
if (nUnionDepth > 0) {
fatalerror("Cannot change the section within a UNION");
}
pCurrentSection = pSect; pCurrentSection = pSect;
nPC = pSect->nPC; nPC = pSect->nPC;
@@ -636,12 +663,12 @@ out_NewAlignedSection(char *pzName, ULONG secttype, SLONG alignment, SLONG bank)
} }
/* /*
* Output an absolute byte * Output an absolute byte (bypassing ROM/union checks)
*/ */
void void
out_AbsByte(int b) out_AbsByteBypassCheck(int b)
{ {
checkcodesection(1); checksectionoverflow(1);
b &= 0xFF; b &= 0xFF;
if (nPass == 2) if (nPass == 2)
pCurrentSection->tData[nPC] = b; pCurrentSection->tData[nPC] = b;
@@ -651,10 +678,21 @@ out_AbsByte(int b)
pPCSymbol->nValue += 1; pPCSymbol->nValue += 1;
} }
/*
* Output an absolute byte
*/
void
out_AbsByte(int b)
{
checkcodesection();
out_AbsByteBypassCheck(b);
}
void void
out_AbsByteGroup(char *s, int length) out_AbsByteGroup(char *s, int length)
{ {
checkcodesection(length); checkcodesection();
checksectionoverflow(length);
while (length--) while (length--)
out_AbsByte(*s++); out_AbsByte(*s++);
} }
@@ -666,13 +704,17 @@ void
out_Skip(int skip) out_Skip(int skip)
{ {
checksection(); checksection();
checksectionoverflow(skip);
if (!((pCurrentSection->nType == SECT_ROM0) if (!((pCurrentSection->nType == SECT_ROM0)
|| (pCurrentSection->nType == SECT_ROMX))) { || (pCurrentSection->nType == SECT_ROMX))) {
pCurrentSection->nPC += skip; pCurrentSection->nPC += skip;
nPC += skip; nPC += skip;
pPCSymbol->nValue += skip; pPCSymbol->nValue += skip;
} else if (nUnionDepth > 0) {
while (skip--)
out_AbsByteBypassCheck(CurrentOptions.fillchar);
} else { } else {
checkcodesection(skip); checkcodesection();
while (skip--) while (skip--)
out_AbsByte(CurrentOptions.fillchar); out_AbsByte(CurrentOptions.fillchar);
} }
@@ -684,7 +726,8 @@ out_Skip(int skip)
void void
out_String(char *s) out_String(char *s)
{ {
checkcodesection(strlen(s)); checkcodesection();
checksectionoverflow(strlen(s));
while (*s) while (*s)
out_AbsByte(*s++); out_AbsByte(*s++);
} }
@@ -697,7 +740,8 @@ out_String(char *s)
void void
out_RelByte(struct Expression * expr) out_RelByte(struct Expression * expr)
{ {
checkcodesection(1); checkcodesection();
checksectionoverflow(1);
if (rpn_isReloc(expr)) { if (rpn_isReloc(expr)) {
if (nPass == 2) { if (nPass == 2) {
pCurrentSection->tData[nPC] = 0; pCurrentSection->tData[nPC] = 0;
@@ -718,7 +762,8 @@ out_RelByte(struct Expression * expr)
void void
out_AbsWord(int b) out_AbsWord(int b)
{ {
checkcodesection(2); checkcodesection();
checksectionoverflow(2);
b &= 0xFFFF; b &= 0xFFFF;
if (nPass == 2) { if (nPass == 2) {
pCurrentSection->tData[nPC] = b & 0xFF; pCurrentSection->tData[nPC] = b & 0xFF;
@@ -738,7 +783,8 @@ out_RelWord(struct Expression * expr)
{ {
ULONG b; ULONG b;
checkcodesection(2); checkcodesection();
checksectionoverflow(2);
b = expr->nVal & 0xFFFF; b = expr->nVal & 0xFFFF;
if (rpn_isReloc(expr)) { if (rpn_isReloc(expr)) {
if (nPass == 2) { if (nPass == 2) {
@@ -760,7 +806,8 @@ out_RelWord(struct Expression * expr)
void void
out_AbsLong(SLONG b) out_AbsLong(SLONG b)
{ {
checkcodesection(sizeof(SLONG)); checkcodesection();
checksectionoverflow(sizeof(SLONG));
if (nPass == 2) { if (nPass == 2) {
pCurrentSection->tData[nPC] = b & 0xFF; pCurrentSection->tData[nPC] = b & 0xFF;
pCurrentSection->tData[nPC + 1] = b >> 8; pCurrentSection->tData[nPC + 1] = b >> 8;
@@ -781,7 +828,8 @@ out_RelLong(struct Expression * expr)
{ {
SLONG b; SLONG b;
checkcodesection(4); checkcodesection();
checksectionoverflow(4);
b = expr->nVal; b = expr->nVal;
if (rpn_isReloc(expr)) { if (rpn_isReloc(expr)) {
if (nPass == 2) { if (nPass == 2) {
@@ -807,7 +855,8 @@ out_PCRelByte(struct Expression * expr)
{ {
SLONG b = expr->nVal; SLONG b = expr->nVal;
checkcodesection(1); checkcodesection();
checksectionoverflow(1);
b = (b & 0xFFFF) - (nPC + 1); b = (b & 0xFFFF) - (nPC + 1);
if (nPass == 2 && (b < -128 || b > 127)) if (nPass == 2 && (b < -128 || b > 127))
yyerror("PC-relative value must be 8-bit"); yyerror("PC-relative value must be 8-bit");
@@ -835,7 +884,8 @@ out_BinaryFile(char *s)
fsize = ftell(f); fsize = ftell(f);
fseek(f, 0, SEEK_SET); fseek(f, 0, SEEK_SET);
checkcodesection(fsize); checkcodesection();
checksectionoverflow(fsize);
if (nPass == 2) { if (nPass == 2) {
SLONG dest = nPC; SLONG dest = nPC;
@@ -879,7 +929,8 @@ out_BinaryFileSlice(char *s, SLONG start_pos, SLONG length)
fseek(f, start_pos, SEEK_SET); fseek(f, start_pos, SEEK_SET);
checkcodesection(length); checkcodesection();
checksectionoverflow(length);
if (nPass == 2) { if (nPass == 2) {
SLONG dest = nPC; SLONG dest = nPC;

View File

@@ -20,11 +20,12 @@
.Nd Game Boy assembler .Nd Game Boy assembler
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm rgbasm .Nm rgbasm
.Op Fl Ehvw .Op Fl EhVvw
.Op Fl b Ar chars .Op Fl b Ar chars
.Op Fl D Ar name Ns Op = Ns Ar value .Op Fl D Ar name Ns Op = Ns Ar value
.Op Fl g Ar chars .Op Fl g Ar chars
.Op Fl i Ar path .Op Fl i Ar path
.Op Fl M Ar dependfile
.Op Fl o Ar outfile .Op Fl o Ar outfile
.Op Fl p Ar pad_value .Op Fl p Ar pad_value
.Ar file .Ar file
@@ -61,11 +62,18 @@ The
option disables this behavior. option disables this behavior.
.It Fl i Ar path .It Fl i Ar path
Add an include path. Add an include path.
.It Fl M Ar dependfile
Print
.Xr make 1
dependencies to
.Ar dependfile .
.It Fl o Ar outfile .It Fl o Ar outfile
Write an object file to the given filename. Write an object file to the given filename.
.It Fl p Ar pad_value .It Fl p Ar pad_value
When padding an image, pad with this value. When padding an image, pad with this value.
The default is 0x00. The default is 0x00.
.It Fl V
Print the version of the program and exit.
.It Fl v .It Fl v
Be verbose. Be verbose.
.It Fl w .It Fl w
@@ -88,10 +96,9 @@ and
.Xr rgbds 5 , .Xr rgbds 5 ,
.Xr rgbds 7 , .Xr rgbds 7 ,
.Xr gbz80 7 .Xr gbz80 7
.Pp
.Lk https://rednex.github.io/rgbds/asm.htm rgbasm assembly commands
.Sh HISTORY .Sh HISTORY
.Nm .Nm
was originally written by Carsten S\(/orensen as part of the ASMotor package, was originally written by Carsten S\(/orensen as part of the ASMotor package,
and was later packaged in RGBDS by Justin Lloyd. It is now maintained by a and was later packaged in RGBDS by Justin Lloyd. It is now maintained by a
number of contributors at https://github.com/rednex/rgbds. number of contributors at
.Lk https://github.com/rednex/rgbds .

View File

@@ -12,7 +12,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\" .\"
.Dd April 17, 2017 .Dd July 22, 2017
.Dt RGBASM 5 .Dt RGBASM 5
.Os RGBDS Manual .Os RGBDS Manual
.Sh NAME .Sh NAME
@@ -223,13 +223,17 @@ GlobalLabel
AnotherGlobal: AnotherGlobal:
\&.locallabel \&.locallabel
\&.yet_a_local: \&.yet_a_local:
AnotherGlobal.with_another_local:
ThisWillBeExported:: ;note the two colons ThisWillBeExported:: ;note the two colons
ThisWillBeExported.too::
.Ed .Ed
.Pp .Pp
In the line where a label is defined there musn't be any whitespace before it. In the line where a label is defined there musn't be any whitespace before it.
Local labels are only accessible within the scope they are defined. Local labels are only accessible within the scope they are defined.
A scope starts after a global label and ends at the next global label. A scope starts after a global label and ends at the next global label.
Declaring a normal label with :: does an EXPORT at the same time. Declaring a label (global or local) with :: does an EXPORT at the same time.
Local labels can be declared as scope.local or simply as as .local.
If the former notation is used, the scope must be the actual current scope.
.Pp .Pp
Labels will normally change their value during the link process and are thus not Labels will normally change their value during the link process and are thus not
constant. constant.
@@ -271,7 +275,7 @@ Alternatively you can use = as a synonym for SET.
.Pp .Pp
.Dl COUNT = 2 .Dl COUNT = 2
.Pp .Pp
.It Sy RSSET , RERESET , RB , RW .It Sy RSSET , RSRESET , RB , RW
.Pp .Pp
The RS group of commands is a handy way of defining structures: The RS group of commands is a handy way of defining structures:
.Pp .Pp
@@ -303,6 +307,8 @@ There are four commands in the RS group of commands:
.Ic _RS No and adds Ar constexpr No to Ic _RS . .Ic _RS No and adds Ar constexpr No to Ic _RS .
.It Ic RW Ar constexpr Ta Sets the preceding symbol to .It Ic RW Ar constexpr Ta Sets the preceding symbol to
.Ic _RS No and adds Ar constexpr No * 2 to Ic _RS. .Ic _RS No and adds Ar constexpr No * 2 to Ic _RS.
.It Ic RL Ar constexpr Ta Sets the preceding symbol to
.Ic _RS No and adds Ar constexpr No * 4 to Ic _RS.
.El .El
.Pp .Pp
Note that a colon (:) following the symbol-name is not allowed. Note that a colon (:) following the symbol-name is not allowed.
@@ -340,6 +346,14 @@ String-symbols can also be used to define small one-line macros:
Note that a colon (:) following the label-name is not allowed. Note that a colon (:) following the label-name is not allowed.
String equates can't be exported or imported. String equates can't be exported or imported.
.Pp .Pp
.Sy Important note :
An EQUS can be expanded to a string that contains another EQUS
and it will be expanded as well.
This means that, if you aren't careful, you may trap the assembler into an
infinite loop if there's a circular dependency in the expansions.
Also, a MACRO can have inside an EQUS which references the same MACRO, which has
the same problem.
.Pp
.It Sy MACRO .It Sy MACRO
.Pp .Pp
One of the best features of an assembler is the ability to write macros for it. One of the best features of an assembler is the ability to write macros for it.
@@ -401,6 +415,14 @@ LoopyMacro: MACRO
ENDM ENDM
.Ed .Ed
.Pp .Pp
.Sy Important note :
Since a MACRO can call itself (or a different MACRO that calls the first one)
there can be problems of circular dependency.
They trap the assembler in an infinite loop, so you have to be careful when
using recursion with MACROs.
Also, a MACRO can have inside an EQUS which references the same MACRO, which has
the same problem.
.Pp
.Sy Macro Arguments .Sy Macro Arguments
.Pp .Pp
I'd like LoopyMacro a lot better if I didn't have to pre-load the registers I'd like LoopyMacro a lot better if I didn't have to pre-load the registers
@@ -521,6 +543,15 @@ The following symbols are defined by the assembler:
.It Ic EQUS Ta Ic __TIME__ Ta Ta The current time .It Ic EQUS Ta Ic __TIME__ Ta Ta The current time
.It Ic EQUS Ta Ic __ISO_8601_LOCAL__ Ta ISO 8601 timestamp (local) .It Ic EQUS Ta Ic __ISO_8601_LOCAL__ Ta ISO 8601 timestamp (local)
.It Ic EQUS Ta Ic __ISO_8601_UTC__ Ta ISO 8601 timestamp (UTC) .It Ic EQUS Ta Ic __ISO_8601_UTC__ Ta ISO 8601 timestamp (UTC)
.It Ic EQU Ta Ic __UTC_YEAR__ Ta Ta Today's year
.It Ic EQU Ta Ic __UTC_MONTH__ Ta Ta Today's month number, 1-12
.It Ic EQU Ta Ic __UTC_DAY__ Ta Ta Today's day of the month, 1-31
.It Ic EQU Ta Ic __UTC_HOUR__ Ta Ta Current hour, 0-23
.It Ic EQU Ta Ic __UTC_MINUTE__ Ta Ta Current minute, 0-59
.It Ic EQU Ta Ic __UTC_SECOND__ Ta Ta Current second, 0-59
.It Ic EQU Ta Ic __RGBDS_MAJOR__ Ta Ta Major version number of RGBDS.
.It Ic EQU Ta Ic __RGBDS_MINOR__ Ta Ta Minor version number of RGBDS.
.It Ic EQU Ta Ic __RGBDS_PATCH__ Ta Ta Patch version number of RGBDS.
.El .El
.Pp .Pp
.Sh DEFINING DATA .Sh DEFINING DATA
@@ -584,6 +615,41 @@ You can also include only part of a file with
The example below includes 256 bytes from data.bin starting from byte 78. The example below includes 256 bytes from data.bin starting from byte 78.
.Pp .Pp
.Dl INCBIN \[dq]data.bin\[dq],78,256 .Dl INCBIN \[dq]data.bin\[dq],78,256
.Ss Unions
Unions allow multiple memory allocations to share the same space in memory,
like unions in C.
This allows you to easily reuse memory for different purposes, depending on
the game's state.
.Pp
You create unions using the
.Ic UNION ,
.Ic NEXTU
and
.Ic ENDU
keywords.
.Ic NEXTU
lets you create a new block of allocations, and you may use it as many times
within a union as necessary.
.Pp
.Bd -literal -offset indent
UNION
Name: ds 8
Nickname: ds 8
NEXTU
Health: dw
Something: ds 3
Lives: db
NEXTU
Temporary: ds 19
ENDU
.Ed
.Pp
This union will use up 19 bytes, as this is the size of the largest block
(the last one, containing 'Temporary').
Of course, as 'Name', 'Health', and 'Temporary' all point to the same memory
locations, writes to any one of these will affect values read from the others.
.Pp
Unions may be used in any section, but code and data may not be included.
.Sh THE MACRO LANGUAGE .Sh THE MACRO LANGUAGE
.Pp .Pp
.Ss Printing things during assembly .Ss Printing things during assembly
@@ -676,27 +742,48 @@ calls infinitely (or until you run out of memory, whichever comes first).
.Dl INCLUDE \[dq]irq.inc\[dq] .Dl INCLUDE \[dq]irq.inc\[dq]
.Pp .Pp
.Ss Conditional assembling .Ss Conditional assembling
The three commands The four commands
.Ic IF , .Ic IF ,
.Ic ELSE .Ic ELIF ,
.Ic ELSE ,
and and
.Ic ENDC .Ic ENDC
are used to conditionally assemble parts of your file. are used to conditionally assemble parts of your file.
This is a powerful feature commonly used in macros. This is a powerful feature commonly used in macros.
.Pp .Pp
.Bd -literal -offset indent .Bd -literal -offset indent
IF 2+2==4 IF NUM < 0
PRINTT \[dq]2+2==4\[rs]n\[dq] PRINTT \[dq]NUM < 0\[rs]n\[dq]
ELIF NUM == 0
PRINTT \[dq]NUM == 0\[rs]n\[dq]
ELSE ELSE
PRINTT \[dq]2+2!=4\[rs]n\[dq] PRINTT \[dq]NUM > 0\[rs]n\[dq]
ENDC ENDC
.Ed .Ed
.Pp .Pp
The The
.Ic ELIF
and
.Ic ELSE .Ic ELSE
block is optional. blocks are optional.
.Ic IF No / Ic ELSE No / Ic ENDC .Ic IF No / Ic ELIF No / Ic ELSE No / Ic ENDC
blocks can be nested. blocks can be nested.
.Pp
Note that if an
.Ic ELSE
block is found before an
.Ic ELIF
block, the
.Ic ELIF
block will be ignored.
All
.Ic ELIF
blocks must go before the
.Ic ELSE
block.
Also, if there is more than one
.Ic ELSE
block, all of them but the first one are ignored.
.Ss Integer and Boolean expressions .Ss Integer and Boolean expressions
An expression can be composed of many things. An expression can be composed of many things.
Expressions are always evaluated using signed 32-bit math. Expressions are always evaluated using signed 32-bit math.
@@ -928,6 +1015,15 @@ machine.
.It Sx __ISO_8601_UTC__ .It Sx __ISO_8601_UTC__
.It Sx __LINE__ .It Sx __LINE__
.It Sx __TIME__ .It Sx __TIME__
.It Sx __RGBDS_MAJOR__
.It Sx __RGBDS_MINOR__
.It Sx __RGBDS_PATCH__
.It Sx __UTC_YEAR__
.It Sx __UTC_MONTH__
.It Sx __UTC_DAY__
.It Sx __UTC_HOUR__
.It Sx __UTC_MINUTE__
.It Sx __UTC_SECOND__
.It Sx _NARG .It Sx _NARG
.It Sx _PI .It Sx _PI
.It Sx _RS .It Sx _RS
@@ -942,6 +1038,7 @@ machine.
.It Sx DIV .It Sx DIV
.It Sx DS .It Sx DS
.It Sx DW .It Sx DW
.It Sx ELIF
.It Sx ELSE .It Sx ELSE
.It Sx ENDC .It Sx ENDC
.It Sx ENDM .It Sx ENDM
@@ -970,6 +1067,7 @@ machine.
.It Sx PUSHS .It Sx PUSHS
.It Sx REPT .It Sx REPT
.It Sx RB .It Sx RB
.It Sx RL
.It Sx ROM0 .It Sx ROM0
.It Sx ROMX .It Sx ROMX
.It Sx RSRESET .It Sx RSRESET
@@ -1005,4 +1103,4 @@ machine.
was originally written by Carsten S\(/orensen as part of the ASMotor package, was originally written by Carsten S\(/orensen as part of the ASMotor package,
and was later packaged in RGBDS by Justin Lloyd. and was later packaged in RGBDS by Justin Lloyd.
It is now maintained by a number of contributors at It is now maintained by a number of contributors at
https://github.com/rednex/rgbds. .Lk https://github.com/rednex/rgbds .

View File

@@ -8,11 +8,13 @@
#include <time.h> #include <time.h>
#include "asm/asm.h" #include "asm/asm.h"
#include "asm/fstack.h"
#include "asm/symbol.h" #include "asm/symbol.h"
#include "asm/main.h" #include "asm/main.h"
#include "asm/mymath.h" #include "asm/mymath.h"
#include "asm/output.h" #include "asm/output.h"
#include "extern/err.h" #include "extern/err.h"
#include "extern/version.h"
struct sSymbol *tHashedSymbols[HASHSIZE]; struct sSymbol *tHashedSymbols[HASHSIZE];
struct sSymbol *pScope = NULL; struct sSymbol *pScope = NULL;
@@ -24,8 +26,29 @@ char SavedTIME[256];
char SavedDATE[256]; char SavedDATE[256];
char SavedTIMESTAMP_ISO8601_LOCAL[256]; char SavedTIMESTAMP_ISO8601_LOCAL[256];
char SavedTIMESTAMP_ISO8601_UTC[256]; char SavedTIMESTAMP_ISO8601_UTC[256];
char SavedDAY[3];
char SavedMONTH[3];
char SavedYEAR[5];
char SavedHOUR[3];
char SavedMINUTE[3];
char SavedSECOND[3];
bool exportall; bool exportall;
void helper_RemoveLeadingZeros(char * string){
char * new_beginning = string;
while(*new_beginning == '0')
new_beginning++;
if(new_beginning == string)
return;
if(*new_beginning == '\0')
new_beginning--;
memmove(string, new_beginning, strlen(new_beginning) + 1);
}
SLONG SLONG
Callback_NARG(struct sSymbol * sym) Callback_NARG(struct sSymbol * sym)
{ {
@@ -87,32 +110,24 @@ createsymbol(char *s)
(*ppsym)->pMacro = NULL; (*ppsym)->pMacro = NULL;
(*ppsym)->pSection = NULL; (*ppsym)->pSection = NULL;
(*ppsym)->Callback = NULL; (*ppsym)->Callback = NULL;
strcpy((*ppsym)->tzFileName, tzCurrentFileName);
(*ppsym)->nFileLine = fstk_GetLine();
return (*ppsym); return (*ppsym);
} else { } else {
fatalerror("No memory for symbol"); fatalerror("No memory for symbol");
return (NULL); return (NULL);
} }
} }
/* /*
* Find a symbol by name and scope * Creates the full name of a local symbol in a given scope, by prepending
* the name with the parent symbol's name.
*/ */
struct sSymbol * size_t
findsymbol(char *s, struct sSymbol * scope) fullSymbolName(char *output, size_t outputSize, char *localName, struct sSymbol *scope)
{ {
struct sSymbol **ppsym; struct sSymbol *parent = scope->pScope ? scope->pScope : scope;
SLONG hash; return snprintf(output, outputSize, "%s%s", parent->tzName, localName);
hash = calchash(s);
ppsym = &(tHashedSymbols[hash]);
while ((*ppsym) != NULL) {
if ((strcmp(s, (*ppsym)->tzName) == 0)
&& ((*ppsym)->pScope == scope)) {
return (*ppsym);
} else
ppsym = &((*ppsym)->pNext);
}
return (NULL);
} }
/* /*
@@ -123,13 +138,25 @@ findpsymbol(char *s, struct sSymbol * scope)
{ {
struct sSymbol **ppsym; struct sSymbol **ppsym;
SLONG hash; SLONG hash;
char fullname[MAXSYMLEN + 1];
if (s[0] == '.' && scope) {
fullSymbolName(fullname, sizeof(fullname), s, scope);
s = fullname;
}
char *seperator;
if ((seperator = strchr(s, '.'))) {
if (strchr(seperator + 1, '.')) {
fatalerror("'%s' is a nonsensical reference to a nested local symbol", s);
}
}
hash = calchash(s); hash = calchash(s);
ppsym = &(tHashedSymbols[hash]); ppsym = &(tHashedSymbols[hash]);
while ((*ppsym) != NULL) { while ((*ppsym) != NULL) {
if ((strcmp(s, (*ppsym)->tzName) == 0) if ((strcmp(s, (*ppsym)->tzName) == 0)) {
&& ((*ppsym)->pScope == scope)) {
return (ppsym); return (ppsym);
} else } else
ppsym = &((*ppsym)->pNext); ppsym = &((*ppsym)->pNext);
@@ -137,6 +164,16 @@ findpsymbol(char *s, struct sSymbol * scope)
return (NULL); return (NULL);
} }
/*
* Find a symbol by name and scope
*/
struct sSymbol *
findsymbol(char *s, struct sSymbol * scope)
{
struct sSymbol **ppsym = findpsymbol(s, scope);
return ppsym ? *ppsym : NULL;
}
/* /*
* Find a symbol by name and scope * Find a symbol by name and scope
*/ */
@@ -484,7 +521,8 @@ sym_AddEqu(char *tzSym, SLONG value)
if ((nsym = findsymbol(tzSym, NULL)) != NULL) { if ((nsym = findsymbol(tzSym, NULL)) != NULL) {
if (nsym->nType & SYMF_DEFINED) { if (nsym->nType & SYMF_DEFINED) {
yyerror("'%s' already defined", tzSym); yyerror("'%s' already defined in %s(%d)",
tzSym, nsym->tzFileName, nsym->nFileLine);
} }
} else } else
nsym = createsymbol(tzSym); nsym = createsymbol(tzSym);
@@ -516,7 +554,8 @@ sym_AddString(char *tzSym, char *tzValue)
if ((nsym = findsymbol(tzSym, NULL)) != NULL) { if ((nsym = findsymbol(tzSym, NULL)) != NULL) {
if (nsym->nType & SYMF_DEFINED) { if (nsym->nType & SYMF_DEFINED) {
yyerror("'%s' already defined", tzSym); yyerror("'%s' already defined in %s(%d)",
tzSym, nsym->tzFileName, nsym->nFileLine);
} }
} else } else
nsym = createsymbol(tzSym); nsym = createsymbol(tzSym);
@@ -572,30 +611,16 @@ sym_AddSet(char *tzSym, SLONG value)
void void
sym_AddLocalReloc(char *tzSym) sym_AddLocalReloc(char *tzSym)
{ {
if ((nPass == 1)
|| ((nPass == 2) && (sym_isDefined(tzSym) == 0))) {
/* only add local reloc symbols in pass 1 */
struct sSymbol *nsym;
if (pScope) { if (pScope) {
if ((nsym = findsymbol(tzSym, pScope)) != NULL) { if (strlen(tzSym) + strlen(pScope->tzName) > MAXSYMLEN) {
if (nsym->nType & SYMF_DEFINED) { fatalerror("Symbol too long");
yyerror("'%s' already defined", tzSym);
} }
} else
nsym = createsymbol(tzSym);
if (nsym) { char fullname[MAXSYMLEN + 1];
nsym->nValue = nPC; fullSymbolName(fullname, sizeof(fullname), tzSym, pScope);
nsym->nType |= sym_AddReloc(fullname);
SYMF_RELOC | SYMF_LOCAL | SYMF_DEFINED;
if (exportall) { } else {
nsym->nType |= SYMF_EXPORT;
}
nsym->pScope = pScope;
nsym->pSection = pCurrentSection;
}
} else
fatalerror("Local label in main scope"); fatalerror("Local label in main scope");
} }
} }
@@ -606,14 +631,35 @@ sym_AddLocalReloc(char *tzSym)
void void
sym_AddReloc(char *tzSym) sym_AddReloc(char *tzSym)
{ {
struct sSymbol* scope = NULL;
if ((nPass == 1) if ((nPass == 1)
|| ((nPass == 2) && (sym_isDefined(tzSym) == 0))) { || ((nPass == 2) && (sym_isDefined(tzSym) == 0))) {
/* only add reloc symbols in pass 1 */ /* only add reloc symbols in pass 1 */
struct sSymbol *nsym; struct sSymbol *nsym;
char *localPtr = NULL;
if ((nsym = findsymbol(tzSym, NULL)) != NULL) { if ((localPtr = strchr(tzSym, '.')) != NULL) {
if (!pScope) {
fatalerror("Local label in main scope");
}
struct sSymbol *parent = pScope->pScope ? pScope->pScope : pScope;
int parentLen = localPtr - tzSym;
if (strchr(localPtr + 1, '.') != NULL) {
fatalerror("'%s' is a nonsensical reference to a nested local symbol", tzSym);
} else if (strlen(parent->tzName) != parentLen || strncmp(tzSym, parent->tzName, parentLen) != 0) {
yyerror("Not currently in the scope of '%.*s'", parentLen, tzSym);
}
scope = parent;
}
if ((nsym = findsymbol(tzSym, scope)) != NULL) {
if (nsym->nType & SYMF_DEFINED) { if (nsym->nType & SYMF_DEFINED) {
yyerror("'%s' already defined", tzSym); yyerror("'%s' already defined in %s(%d)",
tzSym, nsym->tzFileName, nsym->nFileLine);
} }
} else } else
nsym = createsymbol(tzSym); nsym = createsymbol(tzSym);
@@ -621,14 +667,17 @@ sym_AddReloc(char *tzSym)
if (nsym) { if (nsym) {
nsym->nValue = nPC; nsym->nValue = nPC;
nsym->nType |= SYMF_RELOC | SYMF_DEFINED; nsym->nType |= SYMF_RELOC | SYMF_DEFINED;
if (localPtr) {
nsym->nType |= SYMF_LOCAL;
}
if (exportall) { if (exportall) {
nsym->nType |= SYMF_EXPORT; nsym->nType |= SYMF_EXPORT;
} }
nsym->pScope = NULL; nsym->pScope = scope;
nsym->pSection = pCurrentSection; nsym->pSection = pCurrentSection;
} }
} }
pScope = findsymbol(tzSym, NULL); pScope = findsymbol(tzSym, scope);
} }
/* /*
@@ -684,7 +733,7 @@ sym_Export(char *tzSym)
/* only export symbols in pass 1 */ /* only export symbols in pass 1 */
struct sSymbol *nsym; struct sSymbol *nsym;
if ((nsym = findsymbol(tzSym, 0)) == NULL) if ((nsym = sym_FindSymbol(tzSym)) == NULL)
nsym = createsymbol(tzSym); nsym = createsymbol(tzSym);
if (nsym) if (nsym)
@@ -692,7 +741,7 @@ sym_Export(char *tzSym)
} else { } else {
struct sSymbol *nsym; struct sSymbol *nsym;
if ((nsym = findsymbol(tzSym, 0)) != NULL) { if ((nsym = sym_FindSymbol(tzSym)) != NULL) {
if (nsym->nType & SYMF_DEFINED) if (nsym->nType & SYMF_DEFINED)
return; return;
} }
@@ -711,7 +760,7 @@ sym_Global(char *tzSym)
/* only globalize symbols in pass 2 */ /* only globalize symbols in pass 2 */
struct sSymbol *nsym; struct sSymbol *nsym;
nsym = findsymbol(tzSym, 0); nsym = sym_FindSymbol(tzSym);
if ((nsym == NULL) || ((nsym->nType & SYMF_DEFINED) == 0)) { if ((nsym == NULL) || ((nsym->nType & SYMF_DEFINED) == 0)) {
if (nsym == NULL) if (nsym == NULL)
@@ -739,7 +788,8 @@ sym_AddMacro(char *tzSym)
if ((nsym = findsymbol(tzSym, NULL)) != NULL) { if ((nsym = findsymbol(tzSym, NULL)) != NULL) {
if (nsym->nType & SYMF_DEFINED) { if (nsym->nType & SYMF_DEFINED) {
yyerror("'%s' already defined", tzSym); yyerror("'%s' already defined in %s(%d)",
tzSym, nsym->tzFileName, nsym->nFileLine);
} }
} else } else
nsym = createsymbol(tzSym); nsym = createsymbol(tzSym);
@@ -800,6 +850,15 @@ sym_PrepPass2(void)
sym_AddString("__DATE__", SavedDATE); sym_AddString("__DATE__", SavedDATE);
sym_AddString("__ISO_8601_LOCAL__", SavedTIMESTAMP_ISO8601_LOCAL); sym_AddString("__ISO_8601_LOCAL__", SavedTIMESTAMP_ISO8601_LOCAL);
sym_AddString("__ISO_8601_UTC__", SavedTIMESTAMP_ISO8601_UTC); sym_AddString("__ISO_8601_UTC__", SavedTIMESTAMP_ISO8601_UTC);
sym_AddString("__UTC_DAY__", SavedDAY);
sym_AddString("__UTC_MONTH__", SavedMONTH);
sym_AddString("__UTC_YEAR__", SavedYEAR);
sym_AddString("__UTC_HOUR__", SavedHOUR);
sym_AddString("__UTC_MINUTE__", SavedMINUTE);
sym_AddString("__UTC_SECOND__", SavedSECOND);
sym_AddEqu("__RGBDS_MAJOR__", PACKAGE_VERSION_MAJOR);
sym_AddEqu("__RGBDS_MINOR__", PACKAGE_VERSION_MINOR);
sym_AddEqu("__RGBDS_PATCH__", PACKAGE_VERSION_PATCH);
sym_AddSet("_RS", 0); sym_AddSet("_RS", 0);
sym_AddEqu("_NARG", 0); sym_AddEqu("_NARG", 0);
@@ -845,6 +904,19 @@ sym_Init(void)
struct tm *time_utc = gmtime(&now); struct tm *time_utc = gmtime(&now);
strftime(SavedTIMESTAMP_ISO8601_UTC, strftime(SavedTIMESTAMP_ISO8601_UTC,
sizeof(SavedTIMESTAMP_ISO8601_UTC), "\"%FT%TZ\"", time_utc); sizeof(SavedTIMESTAMP_ISO8601_UTC), "\"%FT%TZ\"", time_utc);
strftime(SavedDAY, sizeof(SavedDAY), "%d", time_utc);
strftime(SavedMONTH, sizeof(SavedMONTH), "%m", time_utc);
strftime(SavedYEAR, sizeof(SavedYEAR), "%Y", time_utc);
strftime(SavedHOUR, sizeof(SavedHOUR), "%H", time_utc);
strftime(SavedMINUTE, sizeof(SavedMINUTE), "%M", time_utc);
strftime(SavedSECOND, sizeof(SavedSECOND), "%S", time_utc);
helper_RemoveLeadingZeros(SavedDAY);
helper_RemoveLeadingZeros(SavedMONTH);
helper_RemoveLeadingZeros(SavedHOUR);
helper_RemoveLeadingZeros(SavedMINUTE);
helper_RemoveLeadingZeros(SavedSECOND);
} else { } else {
warnx("Couldn't determine current time."); warnx("Couldn't determine current time.");
/* The '?' have to be escaped or they will be treated as /* The '?' have to be escaped or they will be treated as
@@ -853,12 +925,24 @@ sym_Init(void)
strcpy(SavedDATE, "\"\?\? \?\?\? \?\?\?\?\""); strcpy(SavedDATE, "\"\?\? \?\?\? \?\?\?\?\"");
strcpy(SavedTIMESTAMP_ISO8601_LOCAL, "\"\?\?\?\?-\?\?-\?\?T\?\?:\?\?:\?\?+\?\?\?\?\""); strcpy(SavedTIMESTAMP_ISO8601_LOCAL, "\"\?\?\?\?-\?\?-\?\?T\?\?:\?\?:\?\?+\?\?\?\?\"");
strcpy(SavedTIMESTAMP_ISO8601_UTC, "\"\?\?\?\?-\?\?-\?\?T\?\?:\?\?:\?\?Z\""); strcpy(SavedTIMESTAMP_ISO8601_UTC, "\"\?\?\?\?-\?\?-\?\?T\?\?:\?\?:\?\?Z\"");
strcpy(SavedDAY, "1");
strcpy(SavedMONTH, "1");
strcpy(SavedYEAR, "1900");
strcpy(SavedHOUR, "0");
strcpy(SavedMINUTE, "0");
strcpy(SavedSECOND, "0");
} }
sym_AddString("__TIME__", SavedTIME); sym_AddString("__TIME__", SavedTIME);
sym_AddString("__DATE__", SavedDATE); sym_AddString("__DATE__", SavedDATE);
sym_AddString("__ISO_8601_LOCAL__", SavedTIMESTAMP_ISO8601_LOCAL); sym_AddString("__ISO_8601_LOCAL__", SavedTIMESTAMP_ISO8601_LOCAL);
sym_AddString("__ISO_8601_UTC__", SavedTIMESTAMP_ISO8601_UTC); sym_AddString("__ISO_8601_UTC__", SavedTIMESTAMP_ISO8601_UTC);
sym_AddString("__UTC_DAY__", SavedDAY);
sym_AddString("__UTC_MONTH__", SavedMONTH);
sym_AddString("__UTC_YEAR__", SavedYEAR);
sym_AddString("__UTC_HOUR__", SavedHOUR);
sym_AddString("__UTC_MINUTE__", SavedMINUTE);
sym_AddString("__UTC_SECOND__", SavedSECOND);
pScope = NULL; pScope = NULL;

34
src/extern/version.c vendored Normal file
View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2017 Antonio Nino Diaz <antonio_nd@outlook.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdio.h>
#include <string.h>
#include "extern/version.h"
const char * get_package_version_string(void)
{
static char s[50];
/* The following conditional should be simplified by the compiler. */
if (strlen(BUILD_VERSION_STRING) == 0) {
snprintf(s, sizeof(s), "v%d.%d.%d", PACKAGE_VERSION_MAJOR,
PACKAGE_VERSION_MINOR, PACKAGE_VERSION_PATCH);
return s;
} else {
return BUILD_VERSION_STRING;
}
}

View File

@@ -22,12 +22,13 @@
#include <unistd.h> #include <unistd.h>
#include "extern/err.h" #include "extern/err.h"
#include "extern/version.h"
static void static void
usage(void) usage(void)
{ {
printf( printf(
"usage: rgbfix [-Ccjsv] [-i game_id] [-k licensee_str] [-l licensee_id]\n" "usage: rgbfix [-CcjsVv] [-i game_id] [-k licensee_str] [-l licensee_id]\n"
" [-m mbc_type] [-n rom_version] [-p pad_value] [-r ram_size]\n" " [-m mbc_type] [-n rom_version] [-p pad_value] [-r ram_size]\n"
" [-t title_str] file\n"); " [-t title_str] file\n");
exit(1); exit(1);
@@ -63,13 +64,13 @@ main(int argc, char *argv[])
char *id; /* game ID in ASCII */ char *id; /* game ID in ASCII */
char *newlicensee; /* new licensee ID, two ASCII characters */ char *newlicensee; /* new licensee ID, two ASCII characters */
int licensee; /* old licensee ID */ int licensee = 0; /* old licensee ID */
int cartridge; /* cartridge hardware ID */ int cartridge = 0; /* cartridge hardware ID */
int ramsize; /* RAM size ID */ int ramsize = 0; /* RAM size ID */
int version; /* mask ROM version number */ int version = 0; /* mask ROM version number */
int padvalue; /* to pad the rom with if it changes size */ int padvalue = 0; /* to pad the rom with if it changes size */
while ((ch = getopt(argc, argv, "Cci:jk:l:m:n:p:sr:t:v")) != -1) { while ((ch = getopt(argc, argv, "Cci:jk:l:m:n:p:sr:t:Vv")) != -1) {
switch (ch) { switch (ch) {
case 'C': case 'C':
coloronly = true; coloronly = true;
@@ -177,6 +178,9 @@ main(int argc, char *argv[])
title = optarg; title = optarg;
break; break;
case 'V':
printf("rgbfix %s\n", get_package_version_string());
exit(0);
case 'v': case 'v':
validate = true; validate = true;
break; break;

View File

@@ -12,7 +12,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\" .\"
.Dd April 16, 2017 .Dd April 17, 2017
.Dt RGBFIX 1 .Dt RGBFIX 1
.Os RGBDS Manual .Os RGBDS Manual
.Sh NAME .Sh NAME
@@ -20,7 +20,7 @@
.Nd Game Boy checksum fixer .Nd Game Boy checksum fixer
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm rgbfix .Nm rgbfix
.Op Fl Ccjsv .Op Fl CcjsVv
.Op Fl i Ar game_id .Op Fl i Ar game_id
.Op Fl k Ar licensee_str .Op Fl k Ar licensee_str
.Op Fl l Ar licensee_id .Op Fl l Ar licensee_id
@@ -108,6 +108,8 @@ or
.Pc . .Pc .
If both this and the game ID are set, the game ID will overwrite the If both this and the game ID are set, the game ID will overwrite the
overlapping portion of the title. overlapping portion of the title.
.It Fl V
Print the version of the program and exit.
.It Fl v .It Fl v
Validate the header and fix checksums: the Nintendo character area Validate the header and fix checksums: the Nintendo character area
.Pq Ad 0x104 Ns \(en Ns Ad 0x133 , .Pq Ad 0x104 Ns \(en Ns Ad 0x133 ,
@@ -153,4 +155,5 @@ SurvivalKids.gbc
.Nm .Nm
was originally released by Carsten S\(/orensen as a standalone program called was originally released by Carsten S\(/orensen as a standalone program called
gbfix, and was later packaged in RGBDS by Justin Lloyd. It is now maintained by gbfix, and was later packaged in RGBDS by Justin Lloyd. It is now maintained by
a number of contributors at https://github.com/rednex/rgbds. a number of contributors at
.Lk https://github.com/rednex/rgbds .

View File

@@ -12,7 +12,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\" .\"
.Dd April 16, 2017 .Dd April 17, 2017
.Dt GBZ80 7 .Dt GBZ80 7
.Os RGBDS Manual .Os RGBDS Manual
.Sh NAME .Sh NAME
@@ -1821,4 +1821,4 @@ Flags: See
was originally written by Carsten S\(/orensen as part of the ASMotor package, was originally written by Carsten S\(/orensen as part of the ASMotor package,
and was later packaged in RGBDS by Justin Lloyd. and was later packaged in RGBDS by Justin Lloyd.
It is now maintained by a number of contributors at It is now maintained by a number of contributors at
https://github.com/rednex/rgbds. .Lk https://github.com/rednex/rgbds .

View File

@@ -14,16 +14,18 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#include <getopt.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include "extern/version.h"
#include "gfx/main.h" #include "gfx/main.h"
static void static void
usage(void) usage(void)
{ {
printf( printf(
"usage: rgbgfx [-DFfhPTuv] [-d #] [-o outfile] [-p palfile] [-t mapfile]\n" "usage: rgbgfx [-DFfhPTuVv] [-d #] [-o outfile] [-p palfile] [-t mapfile]\n"
" [-x #] infile\n"); " [-x #] infile\n");
exit(1); exit(1);
} }
@@ -49,27 +51,30 @@ main(int argc, char *argv[])
depth = 2; depth = 2;
while((ch = getopt(argc, argv, "DvFfd:hx:Tt:uPp:o:")) != -1) { while((ch = getopt(argc, argv, "Dd:Ffho:Tt:uPp:Vvx:")) != -1) {
switch(ch) { switch(ch) {
case 'D': case 'D':
opts.debug = true; opts.debug = true;
break; break;
case 'v': case 'd':
opts.verbose = true; depth = strtoul(optarg, NULL, 0);
break; break;
case 'F': case 'F':
opts.hardfix = true; opts.hardfix = true;
case 'f': case 'f':
opts.fix = true; opts.fix = true;
break; break;
case 'd':
depth = strtoul(optarg, NULL, 0);
break;
case 'h': case 'h':
opts.horizontal = true; opts.horizontal = true;
break; break;
case 'x': case 'o':
opts.trim = strtoul(optarg, NULL, 0); opts.outfile = optarg;
break;
case 'P':
opts.palout = true;
break;
case 'p':
opts.palfile = optarg;
break; break;
case 'T': case 'T':
opts.mapout = true; opts.mapout = true;
@@ -80,17 +85,18 @@ main(int argc, char *argv[])
case 'u': case 'u':
opts.unique = true; opts.unique = true;
break; break;
case 'P': case 'V':
opts.palout = true; printf("rgbgfx %s\n", get_package_version_string());
exit(0);
case 'v':
opts.verbose = true;
break; break;
case 'p': case 'x':
opts.palfile = optarg; opts.trim = strtoul(optarg, NULL, 0);
break;
case 'o':
opts.outfile = optarg;
break; break;
default: default:
usage(); usage();
/* NOTREACHED */
} }
} }
argc -= optind; argc -= optind;

View File

@@ -12,7 +12,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\" .\"
.Dd April 8, 2017 .Dd April 17, 2017
.Dt RGBGFX 1 .Dt RGBGFX 1
.Os RGBDS Manual .Os RGBDS Manual
.Sh NAME .Sh NAME
@@ -20,7 +20,7 @@
.Nd Game Boy graphics converter .Nd Game Boy graphics converter
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm rgbgfx .Nm rgbgfx
.Op Fl DfFhPTv .Op Fl DfFhPTVv
.Op Fl o Ar outfile .Op Fl o Ar outfile
.Op Fl d Ar depth .Op Fl d Ar depth
.Op Fl p Ar palfile .Op Fl p Ar palfile
@@ -70,6 +70,8 @@ removing the file extension, and appending
.Pa .tilemap . .Pa .tilemap .
.It Fl u .It Fl u
Truncate repeated tiles. Useful with tilemaps. Truncate repeated tiles. Useful with tilemaps.
.It Fl V
Print the version of the program and exit.
.It Fl v .It Fl v
Verbose. Verbose.
Print errors when the command line parameters and the parameters in Print errors when the command line parameters and the parameters in
@@ -103,4 +105,4 @@ was created by
.An stag019 .An stag019
to be included in RGBDS. to be included in RGBDS.
It is now maintained by a number of contributors at It is now maintained by a number of contributors at
https://github.com/rednex/rgbds. .Lk https://github.com/rednex/rgbds .

View File

@@ -336,7 +336,7 @@ AssignFloatingBankSections(enum eSectionType type)
if ((org = area_AllocAnyBank(pSection->nByteSize, pSection->nAlign, type)) != -1) { if ((org = area_AllocAnyBank(pSection->nByteSize, pSection->nAlign, type)) != -1) {
if (options & OPT_OVERLAY) { if (options & OPT_OVERLAY) {
errx(1, "All sections must be fixed when using overlay"); errx(1, "All sections must be fixed when using an overlay file.");
} }
pSection->nOrg = org & 0xFFFF; pSection->nOrg = org & 0xFFFF;
pSection->nBank = org >> 16; pSection->nBank = org >> 16;
@@ -512,7 +512,7 @@ AssignSections(void)
if (pSection->oAssigned == 0 if (pSection->oAssigned == 0
&& pSection->nOrg != -1 && pSection->nBank == -1) { && pSection->nOrg != -1 && pSection->nBank == -1) {
if (options & OPT_OVERLAY) { if (options & OPT_OVERLAY) {
errx(1, "All sections must be fixed when using overlay"); errx(1, "All sections must be fixed when using an overlay file.");
} }
switch (pSection->Type) { switch (pSection->Type) {
case SECT_ROMX: case SECT_ROMX:
@@ -566,16 +566,21 @@ CreateSymbolTable(void)
((pSect->tSymbols[i]->pSection == pSect) || ((pSect->tSymbols[i]->pSection == pSect) ||
(pSect->tSymbols[i]->pSection == NULL))) { (pSect->tSymbols[i]->pSection == NULL))) {
if (pSect->tSymbols[i]->pSection == NULL) if (pSect->tSymbols[i]->pSection == NULL)
sym_CreateSymbol(pSect->tSymbols[i]-> sym_CreateSymbol(
pzName, pSect->tSymbols[i]->pzName,
pSect->tSymbols[i]-> pSect->tSymbols[i]->nOffset,
nOffset, -1); -1,
pSect->tSymbols[i]->pzObjFileName,
pSect->tSymbols[i]->pzFileName,
pSect->tSymbols[i]->nFileLine);
else else
sym_CreateSymbol(pSect->tSymbols[i]-> sym_CreateSymbol(
pzName, pSect->tSymbols[i]->pzName,
pSect->nOrg + pSect->nOrg + pSect->tSymbols[i]->nOffset,
pSect->tSymbols[i]-> pSect->nBank,
nOffset, pSect->nBank); pSect->tSymbols[i]->pzObjFileName,
pSect->tSymbols[i]->pzFileName,
pSect->tSymbols[i]->nFileLine);
} }
} }
pSect = pSect->pNext; pSect = pSect->pNext;

View File

@@ -4,6 +4,7 @@
#include <unistd.h> #include <unistd.h>
#include "extern/err.h" #include "extern/err.h"
#include "extern/version.h"
#include "link/object.h" #include "link/object.h"
#include "link/output.h" #include "link/output.h"
#include "link/assign.h" #include "link/assign.h"
@@ -33,7 +34,7 @@ static void
usage(void) usage(void)
{ {
printf( printf(
"usage: rgblink [-twd] [-l linkerscript] [-m mapfile] [-n symfile] [-O overlay]\n" "usage: rgblink [-dtVw] [-l linkerscript] [-m mapfile] [-n symfile] [-O overlay]\n"
" [-o outfile] [-p pad_value] [-s symbol] file [...]\n"); " [-o outfile] [-p pad_value] [-s symbol] file [...]\n");
exit(1); exit(1);
} }
@@ -52,7 +53,7 @@ main(int argc, char *argv[])
if (argc == 1) if (argc == 1)
usage(); usage();
while ((ch = getopt(argc, argv, "l:m:n:o:O:p:s:twd")) != -1) { while ((ch = getopt(argc, argv, "dl:m:n:O:o:p:s:tVw")) != -1) {
switch (ch) { switch (ch) {
case 'l': case 'l':
SetLinkerscriptName(optarg); SetLinkerscriptName(optarg);
@@ -76,8 +77,7 @@ main(int argc, char *argv[])
errx(1, "Invalid argument for option 'p'"); errx(1, "Invalid argument for option 'p'");
} }
if (fillchar < 0 || fillchar > 0xFF) { if (fillchar < 0 || fillchar > 0xFF) {
fprintf(stderr, "Argument for option 'p' must be between 0 and 0xFF"); errx(1, "Argument for option 'p' must be between 0 and 0xFF");
exit(1);
} }
break; break;
case 's': case 's':
@@ -98,7 +98,7 @@ main(int argc, char *argv[])
* This option implies OPT_CONTWRAM. * This option implies OPT_CONTWRAM.
*/ */
options |= OPT_DMG_MODE; options |= OPT_DMG_MODE;
/* fallthrough */ /* FALLTHROUGH */
case 'w': case 'w':
/* Set to set WRAM as a single continuous block as on /* Set to set WRAM as a single continuous block as on
* DMG. All WRAM sections must be WRAM0 as bankable WRAM * DMG. All WRAM sections must be WRAM0 as bankable WRAM
@@ -106,6 +106,9 @@ main(int argc, char *argv[])
* will raise an error. */ * will raise an error. */
options |= OPT_CONTWRAM; options |= OPT_CONTWRAM;
break; break;
case 'V':
printf("rgblink %s\n", get_package_version_string());
exit(0);
default: default:
usage(); usage();
/* NOTREACHED */ /* NOTREACHED */

View File

@@ -3,11 +3,13 @@
* *
*/ */
#include <ctype.h>
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "common.h"
#include "extern/err.h" #include "extern/err.h"
#include "link/assign.h" #include "link/assign.h"
#include "link/mylink.h" #include "link/mylink.h"
@@ -116,7 +118,7 @@ AllocSection(void)
*/ */
struct sSymbol * struct sSymbol *
obj_ReadSymbol(FILE * f) obj_ReadSymbol(FILE * f, char *tzObjectfile)
{ {
struct sSymbol *pSym; struct sSymbol *pSym;
@@ -126,7 +128,14 @@ obj_ReadSymbol(FILE * f)
} }
readasciiz(&pSym->pzName, f); readasciiz(&pSym->pzName, f);
if ((pSym->Type = (enum eSymbolType) fgetc(f)) != SYM_IMPORT) { pSym->Type = (enum eSymbolType)fgetc(f);
pSym->pzObjFileName = tzObjectfile;
if (pSym->Type != SYM_IMPORT) {
readasciiz(&pSym->pzFileName, f);
pSym->nFileLine = readlong(f);
pSym->nSectionID = readlong(f); pSym->nSectionID = readlong(f);
pSym->nOffset = readlong(f); pSym->nOffset = readlong(f);
} }
@@ -160,15 +169,51 @@ obj_ReadRGBSection(FILE * f)
errx(1, "ROMX sections can't be used with option -t."); errx(1, "ROMX sections can't be used with option -t.");
} }
if ((options & OPT_CONTWRAM) && (pSection->Type == SECT_WRAMX)) { if ((options & OPT_CONTWRAM) && (pSection->Type == SECT_WRAMX)) {
errx(1, "WRAMX sections can't be used with option -w."); errx(1, "WRAMX sections can't be used with options -w or -d.");
} }
if (options & OPT_DMG_MODE) { if (options & OPT_DMG_MODE) {
/* WRAMX sections are checked for OPT_CONTWRAM */ /* WRAMX sections are checked for OPT_CONTWRAM */
if (pSection->Type == SECT_VRAM && pSection->nBank == 1) { if (pSection->Type == SECT_VRAM && pSection->nBank == 1) {
errx(1, "VRAM bank 1 can't be used with option -w."); errx(1, "VRAM bank 1 can't be used with option -d.");
} }
} }
unsigned int maxsize = 0;
/* Verify that the section isn't too big */
switch (pSection->Type)
{
case SECT_ROM0:
maxsize = (options & OPT_TINY) ? 0x8000 : 0x4000;
break;
case SECT_ROMX:
maxsize = 0x4000;
break;
case SECT_VRAM:
case SECT_SRAM:
maxsize = 0x2000;
break;
case SECT_WRAM0:
maxsize = (options & OPT_CONTWRAM) ? 0x2000 : 0x1000;
break;
case SECT_WRAMX:
maxsize = 0x1000;
break;
case SECT_OAM:
maxsize = 0xA0;
break;
case SECT_HRAM:
maxsize = 0x7F;
break;
default:
errx(1, "Section \"%s\" has an invalid section type.", pzName);
break;
}
if (pSection->nByteSize > maxsize) {
errx(1, "Section \"%s\" is bigger than the max size for that type: 0x%X > 0x%X",
pzName, pSection->nByteSize, maxsize);
}
if ((pSection->Type == SECT_ROMX) || (pSection->Type == SECT_ROM0)) { if ((pSection->Type == SECT_ROMX) || (pSection->Type == SECT_ROM0)) {
/* /*
* These sectiontypes contain data... * These sectiontypes contain data...
@@ -183,8 +228,11 @@ obj_ReadRGBSection(FILE * f)
SLONG nNumberOfPatches; SLONG nNumberOfPatches;
struct sPatch **ppPatch, *pPatch; struct sPatch **ppPatch, *pPatch;
fread(pSection->pData, sizeof(UBYTE), if (fread(pSection->pData, sizeof(UBYTE),
pSection->nByteSize, f); pSection->nByteSize, f) != pSection->nByteSize) {
err(1, "Read error.");
}
nNumberOfPatches = readlong(f); nNumberOfPatches = readlong(f);
ppPatch = &pSection->pPatches; ppPatch = &pSection->pPatches;
@@ -209,8 +257,10 @@ obj_ReadRGBSection(FILE * f)
err(1, NULL); err(1, NULL);
} }
fread(pPatch->pRPN, sizeof(UBYTE), if (fread(pPatch->pRPN, sizeof(UBYTE),
pPatch->nRPNSize, f); pPatch->nRPNSize, f) != pPatch->nRPNSize) {
errx(1, "Read error.");
}
} else } else
pPatch->pRPN = NULL; pPatch->pRPN = NULL;
@@ -227,7 +277,7 @@ obj_ReadRGBSection(FILE * f)
} }
void void
obj_ReadRGB(FILE * pObjfile) obj_ReadRGB(FILE * pObjfile, char *tzObjectfile)
{ {
struct sSection *pFirstSection; struct sSection *pFirstSection;
SLONG nNumberOfSymbols, nNumberOfSections, i; SLONG nNumberOfSymbols, nNumberOfSections, i;
@@ -244,7 +294,7 @@ obj_ReadRGB(FILE * pObjfile)
} }
for (i = 0; i < nNumberOfSymbols; i += 1) for (i = 0; i < nNumberOfSymbols; i += 1)
tSymbols[i] = obj_ReadSymbol(pObjfile); tSymbols[i] = obj_ReadSymbol(pObjfile, tzObjectfile);
} else } else
tSymbols = (struct sSymbol **) & dummymem; tSymbols = (struct sSymbol **) & dummymem;
@@ -289,21 +339,24 @@ obj_ReadRGB(FILE * pObjfile)
void void
obj_ReadOpenFile(FILE * pObjfile, char *tzObjectfile) obj_ReadOpenFile(FILE * pObjfile, char *tzObjectfile)
{ {
char tzHeader[8]; char tzHeader[strlen(RGBDS_OBJECT_VERSION_STRING) + 1];
fread(tzHeader, sizeof(char), 4, pObjfile); if (fread(tzHeader, sizeof(char), strlen(RGBDS_OBJECT_VERSION_STRING),
tzHeader[4] = 0; pObjfile) != strlen(RGBDS_OBJECT_VERSION_STRING)) {
if (strncmp(tzHeader, "RGB", 3) == 0) { errx(1, "%s: Read error.", tzObjectfile);
switch (tzHeader[3]) {
case '3':
case '4': // V4 supports OAM sections, but is otherwise identical
obj_ReadRGB(pObjfile);
break;
default:
errx(1, "'%s' uses an unsupported object file version (%s). Please reassemble it.", tzObjectfile, tzHeader);
} }
tzHeader[strlen(RGBDS_OBJECT_VERSION_STRING)] = 0;
if (strncmp(tzHeader, RGBDS_OBJECT_VERSION_STRING,
strlen(RGBDS_OBJECT_VERSION_STRING)) == 0) {
obj_ReadRGB(pObjfile, tzObjectfile);
} else { } else {
errx(1, "'%s' is not a valid object", tzObjectfile); for (int i = 0; i < strlen(RGBDS_OBJECT_VERSION_STRING); i++)
if (!isprint(tzHeader[i]))
tzHeader[i] = '?';
errx(1, "%s: Invalid file or object file version [%s]",
tzObjectfile, tzHeader);
} }
} }
@@ -339,24 +392,3 @@ file_Length(FILE * f)
return (r); return (r);
} }
void
lib_ReadXLB0(FILE * f)
{
SLONG size;
size = file_Length(f) - 4;
while (size) {
char *name;
size -= readasciiz(&name, f);
readword(f);
size -= 2;
readword(f);
size -= 2;
size -= readlong(f);
size -= 4;
obj_ReadOpenFile(f, name);
free(name);
}
}

View File

@@ -2,6 +2,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "extern/err.h"
#include "link/mylink.h" #include "link/mylink.h"
#include "link/mapfile.h" #include "link/mapfile.h"
#include "link/main.h" #include "link/main.h"
@@ -24,7 +25,10 @@ writehome(FILE * f, FILE * f_overlay)
if (f_overlay != NULL) { if (f_overlay != NULL) {
fseek(f_overlay, 0L, SEEK_SET); fseek(f_overlay, 0L, SEEK_SET);
fread(mem, 1, MaxAvail[BANK_ROM0], f_overlay); if (fread(mem, 1, MaxAvail[BANK_ROM0], f_overlay) !=
MaxAvail[BANK_ROM0]) {
warnx("Failed to read data from overlay file.");
}
} else { } else {
memset(mem, fillchar, MaxAvail[BANK_ROM0]); memset(mem, fillchar, MaxAvail[BANK_ROM0]);
} }
@@ -58,7 +62,10 @@ writebank(FILE * f, FILE * f_overlay, SLONG bank)
if (f_overlay != NULL && bank <= MaxOverlayBank) { if (f_overlay != NULL && bank <= MaxOverlayBank) {
fseek(f_overlay, bank*0x4000, SEEK_SET); fseek(f_overlay, bank*0x4000, SEEK_SET);
fread(mem, 1, MaxAvail[bank], f_overlay); if (fread(mem, 1, MaxAvail[bank], f_overlay) !=
MaxAvail[bank]) {
warnx("Failed to read data from overlay file.");
}
} else { } else {
memset(mem, fillchar, MaxAvail[bank]); memset(mem, fillchar, MaxAvail[bank]);
} }
@@ -104,18 +111,15 @@ Output(void)
if (tzOverlayname) { if (tzOverlayname) {
f_overlay = fopen(tzOverlayname, "rb"); f_overlay = fopen(tzOverlayname, "rb");
if (!f_overlay) { if (!f_overlay) {
fprintf(stderr, "Failed to open overlay file %s\n", tzOverlayname); errx(1, "Failed to open overlay file %s\n", tzOverlayname);
exit(1);
} }
fseek(f_overlay, 0, SEEK_END); fseek(f_overlay, 0, SEEK_END);
if (ftell(f_overlay) % 0x4000 != 0) { if (ftell(f_overlay) % 0x4000 != 0) {
fprintf(stderr, "Overlay file must be aligned to 0x4000 bytes\n"); errx(1, "Overlay file must be aligned to 0x4000 bytes.");
exit(1);
} }
MaxOverlayBank = (ftell(f_overlay) / 0x4000) - 1; MaxOverlayBank = (ftell(f_overlay) / 0x4000) - 1;
if (MaxOverlayBank < 1) { if (MaxOverlayBank < 1) {
fprintf(stderr, "Overlay file be at least 0x8000 bytes\n"); errx(1, "Overlay file must be at least 0x8000 bytes.");
exit(1);
} }
if (MaxOverlayBank > MaxBankUsed) { if (MaxOverlayBank > MaxBankUsed) {
MaxBankUsed = MaxOverlayBank; MaxBankUsed = MaxOverlayBank;

View File

@@ -20,9 +20,7 @@
.Nd Game Boy linker .Nd Game Boy linker
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm rgblink .Nm rgblink
.Op Fl t .Op Fl dtVw
.Op Fl w
.Op Fl d
.Op Fl m Ar mapfile .Op Fl m Ar mapfile
.Op Fl n Ar symfile .Op Fl n Ar symfile
.Op Fl O Ar overlayfile .Op Fl O Ar overlayfile
@@ -95,6 +93,8 @@ have to be consistent.
See See
.Xr rgblink 5 .Xr rgblink 5
for more information about its format. for more information about its format.
.It Fl V
Print the version of the program and exit.
.El .El
.Sh EXAMPLES .Sh EXAMPLES
All you need for a basic ROM is an object file, which can be made into a ROM All you need for a basic ROM is an object file, which can be made into a ROM
@@ -119,4 +119,5 @@ to fix these so that the program will actually run in a Game Boy:
.Nm .Nm
was originally written by Carsten S\(/orensen as part of the ASMotor package, was originally written by Carsten S\(/orensen as part of the ASMotor package,
and was later packaged in RGBDS by Justin Lloyd. It is now maintained by a and was later packaged in RGBDS by Justin Lloyd. It is now maintained by a
number of contributors at https://github.com/rednex/rgbds. number of contributors at
.Lk https://github.com/rednex/rgbds .

View File

@@ -98,4 +98,5 @@ linkerscript. The address and alignment musn't be set.
.Nm .Nm
was originally written by Carsten S\(/orensen as part of the ASMotor package, was originally written by Carsten S\(/orensen as part of the ASMotor package,
and was later packaged in RGBDS by Justin Lloyd. It is now maintained by a and was later packaged in RGBDS by Justin Lloyd. It is now maintained by a
number of contributors at https://github.com/rednex/rgbds. number of contributors at
.Lk https://github.com/rednex/rgbds .

View File

@@ -12,8 +12,10 @@
struct ISymbol { struct ISymbol {
char *pzName; char *pzName;
SLONG nValue; SLONG nValue;
SLONG nBank; SLONG nBank; /* -1 = constant */
//-1 = const char tzObjFileName[_MAX_PATH + 1]; /* Object file where the symbol was defined. */
char tzFileName[_MAX_PATH + 1]; /* Source file where the symbol was defined. */
ULONG nFileLine; /* Line where the symbol was defined. */
struct ISymbol *pNext; struct ISymbol *pNext;
}; };
@@ -76,7 +78,8 @@ sym_GetBank(char *tzName)
} }
void void
sym_CreateSymbol(char *tzName, SLONG nValue, SLONG nBank) sym_CreateSymbol(char *tzName, SLONG nValue, SLONG nBank, char *tzObjFileName,
char *tzFileName, ULONG nFileLine)
{ {
if (strcmp(tzName, "@") == 0) if (strcmp(tzName, "@") == 0)
return; return;
@@ -92,7 +95,10 @@ sym_CreateSymbol(char *tzName, SLONG nValue, SLONG nBank)
if (nBank == -1) if (nBank == -1)
return; return;
errx(1, "Symbol '%s' defined more than once", tzName); errx(1, "'%s' in both %s : %s(%d) and %s : %s(%d)",
tzName, tzObjFileName, tzFileName, nFileLine,
(*ppSym)->tzObjFileName,
(*ppSym)->tzFileName, (*ppSym)->nFileLine);
} }
} }
@@ -102,6 +108,11 @@ sym_CreateSymbol(char *tzName, SLONG nValue, SLONG nBank)
(*ppSym)->nValue = nValue; (*ppSym)->nValue = nValue;
(*ppSym)->nBank = nBank; (*ppSym)->nBank = nBank;
(*ppSym)->pNext = NULL; (*ppSym)->pNext = NULL;
strncpy((*ppSym)->tzObjFileName, tzObjFileName,
sizeof((*ppSym)->tzObjFileName));
strncpy((*ppSym)->tzFileName, tzFileName,
sizeof((*ppSym)->tzFileName));
(*ppSym)->nFileLine = nFileLine;
} }
} }
} }

View File

@@ -12,7 +12,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\" .\"
.Dd April 17, 2017 .Dd July 22, 2017
.Dt RGBDS 5 .Dt RGBDS 5
.Os RGBDS Manual .Os RGBDS Manual
.Sh NAME .Sh NAME
@@ -42,7 +42,7 @@ is a 0terminated string of
.Bd -literal .Bd -literal
; Header ; Header
BYTE ID[4] ; "RGB4" BYTE ID[4] ; "RGB5"
LONG NumberOfSymbols ; The number of symbols used in this file LONG NumberOfSymbols ; The number of symbols used in this file
LONG NumberOfSections ; The number of sections used in this file LONG NumberOfSections ; The number of sections used in this file
@@ -59,6 +59,10 @@ REPT NumberOfSymbols ; Number of symbols defined in this object file.
IF Type != 1 ; If symbol is defined in this object file. IF Type != 1 ; If symbol is defined in this object file.
STRING FileName ; File where the symbol is defined.
LONG LineNum ; Line number in the file where the symbol is defined.
LONG SectionID ; The section number (of this object file) in which LONG SectionID ; The section number (of this object file) in which
; this symbol is defined. ; this symbol is defined.
@@ -187,4 +191,4 @@ Symbol ID follows.
was originally written by Carsten S\(/orensen as part of the ASMotor package, was originally written by Carsten S\(/orensen as part of the ASMotor package,
and was later packaged in RGBDS by Justin Lloyd. and was later packaged in RGBDS by Justin Lloyd.
It is now maintained by a number of contributors at It is now maintained by a number of contributors at
https://github.com/rednex/rgbds. .Lk https://github.com/rednex/rgbds .

View File

@@ -47,5 +47,5 @@ implementation of rgbds.
.It .It
2017, Bentley's repository is moved to a neutral name. 2017, Bentley's repository is moved to a neutral name.
It is now maintained by a number of contributors at It is now maintained by a number of contributors at
https://github.com/rednex/rgbds .Lk https://github.com/rednex/rgbds .
.El .El

View File

@@ -0,0 +1,6 @@
SECTION "sec", ROM0
Parent:
db 0
WrongParent.child
db 0

View File

@@ -0,0 +1,3 @@
ERROR: local-wrong-parent.asm(5):
Not currently in the scope of 'WrongParent'
error: Assembly aborted in pass 1 (1 errors)!

View File

@@ -0,0 +1,7 @@
SECTION "sec", ROM0
Parent:
Parent.child::
db 0
NotParent:
dw Parent.child

View File

View File

@@ -0,0 +1,7 @@
SECTION "sec", ROM0
Parent:
.child:
db 0
NotParent:
dw Parent.child.fail

View File

@@ -0,0 +1,2 @@
ERROR: remote-local-noexist.asm(7):
'Parent.child.fail' is a nonsensical reference to a nested local symbol

View File

@@ -0,0 +1,7 @@
SECTION "sec", ROM0
Parent:
.child:
db 0
NotParent:
dw Parent.child

View File

View File

@@ -1,3 +1,4 @@
#!/bin/sh
fname=$(mktemp) fname=$(mktemp)
rc=0 rc=0

View File

@@ -1,3 +1,4 @@
#!/bin/sh
fname=$(mktemp) fname=$(mktemp)
for i in *.asm; do for i in *.asm; do

View File

@@ -1,3 +1,4 @@
#!/bin/sh
otemp=$(mktemp) otemp=$(mktemp)
gbtemp=$(mktemp) gbtemp=$(mktemp)
gbtemp2=$(mktemp) gbtemp2=$(mktemp)

View File

@@ -1,3 +1,4 @@
#!/bin/sh
otemp=$(mktemp) otemp=$(mktemp)
gbtemp=$(mktemp) gbtemp=$(mktemp)

View File

@@ -1 +1 @@
error: VRAM bank 1 can't be used with option -w. error: VRAM bank 1 can't be used with option -d.

View File

@@ -1 +1 @@
error: WRAMX sections can't be used with option -w. error: WRAMX sections can't be used with options -w or -d.