mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76c1995559 | ||
|
|
ae84570f04 | ||
|
|
094a31ef8c | ||
|
|
291dcf3b6c | ||
|
|
a890bd072b | ||
|
|
2f6c808ccb | ||
|
|
e80907abd0 | ||
|
|
25d39155d3 | ||
|
|
77021d229b | ||
|
|
1b250b90b2 | ||
|
|
e2b4723489 | ||
|
|
2507413162 | ||
|
|
e023a84d04 | ||
|
|
34c127d9c3 | ||
|
|
9a930988c2 | ||
|
|
8c4204c542 | ||
|
|
663c1930ec | ||
|
|
30ccf43f44 | ||
|
|
fdc17adbcb | ||
|
|
cc196954f3 | ||
|
|
55b6cfff84 | ||
|
|
1fc73b04eb |
@@ -1,9 +0,0 @@
|
|||||||
[*]
|
|
||||||
root = true
|
|
||||||
indent_style = tab
|
|
||||||
indent_size = tab
|
|
||||||
tab_width = 8
|
|
||||||
charset = utf-8
|
|
||||||
insert_final_newline = true
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
end_of_line = lf
|
|
||||||
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -1,2 +0,0 @@
|
|||||||
# Shell scripts need Unix line endings (see https://github.com/gbdev/rgbds/issues/841)
|
|
||||||
*.sh text eol=lf
|
|
||||||
@@ -6,7 +6,6 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: github.repository_owner == 'gbdev'
|
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-18.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout rgbds@release
|
- name: Checkout rgbds@release
|
||||||
@@ -88,9 +88,9 @@ jobs:
|
|||||||
Write-Host "libpng SHA256 mismatch! ($hash)"
|
Write-Host "libpng SHA256 mismatch! ($hash)"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
$wc.DownloadFile('https://github.com/lexxmark/winflexbison/releases/download/v2.5.24/win_flex_bison-2.5.24.zip', 'winflexbison.zip')
|
$wc.DownloadFile('https://github.com/lexxmark/winflexbison/releases/download/v2.5.23/win_flex_bison-2.5.23.zip', 'winflexbison.zip')
|
||||||
$hash = (Get-FileHash "winflexbison.zip" -Algorithm SHA256).Hash
|
$hash = (Get-FileHash "winflexbison.zip" -Algorithm SHA256).Hash
|
||||||
if ($hash -ne '39c6086ce211d5415500acc5ed2d8939861ca1696aee48909c7f6daf5122b505') {
|
if ($hash -ne '6AA5C8EA662DA1550020A5804C28BE63FFAA53486DA9F6842E24C379EC422DFC') {
|
||||||
Write-Host "bison SHA256 mismatch! ($hash)"
|
Write-Host "bison SHA256 mismatch! ($hash)"
|
||||||
}
|
}
|
||||||
Expand-Archive -DestinationPath . "zlib.zip"
|
Expand-Archive -DestinationPath . "zlib.zip"
|
||||||
@@ -17,7 +17,6 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: github.repository_owner == 'gbdev'
|
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-18.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout rgbds@master
|
- name: Checkout rgbds@master
|
||||||
@@ -29,9 +29,9 @@ jobs:
|
|||||||
Write-Host "libpng SHA256 mismatch! ($hash)"
|
Write-Host "libpng SHA256 mismatch! ($hash)"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
$wc.DownloadFile('https://github.com/lexxmark/winflexbison/releases/download/v2.5.24/win_flex_bison-2.5.24.zip', 'winflexbison.zip')
|
$wc.DownloadFile('https://github.com/lexxmark/winflexbison/releases/download/v2.5.23/win_flex_bison-2.5.23.zip', 'winflexbison.zip')
|
||||||
$hash = (Get-FileHash "winflexbison.zip" -Algorithm SHA256).Hash
|
$hash = (Get-FileHash "winflexbison.zip" -Algorithm SHA256).Hash
|
||||||
if ($hash -ne '39c6086ce211d5415500acc5ed2d8939861ca1696aee48909c7f6daf5122b505') {
|
if ($hash -ne '6AA5C8EA662DA1550020A5804C28BE63FFAA53486DA9F6842E24C379EC422DFC') {
|
||||||
Write-Host "bison SHA256 mismatch! ($hash)"
|
Write-Host "bison SHA256 mismatch! ($hash)"
|
||||||
}
|
}
|
||||||
Expand-Archive -DestinationPath . "zlib.zip"
|
Expand-Archive -DestinationPath . "zlib.zip"
|
||||||
|
|||||||
@@ -35,13 +35,12 @@ if(MSVC)
|
|||||||
add_definitions(/D_CRT_SECURE_NO_WARNINGS)
|
add_definitions(/D_CRT_SECURE_NO_WARNINGS)
|
||||||
else()
|
else()
|
||||||
add_compile_options(-Wall -pedantic)
|
add_compile_options(-Wall -pedantic)
|
||||||
add_definitions(-D_POSIX_C_SOURCE=200809L -D_ISOC11_SOURCE)
|
|
||||||
if(SANITIZERS)
|
if(SANITIZERS)
|
||||||
set(SAN_FLAGS -fsanitize=shift -fsanitize=integer-divide-by-zero
|
set(SAN_FLAGS -fsanitize=shift -fsanitize=integer-divide-by-zero
|
||||||
-fsanitize=unreachable -fsanitize=vla-bound
|
-fsanitize=unreachable -fsanitize=vla-bound
|
||||||
-fsanitize=signed-integer-overflow -fsanitize=bounds
|
-fsanitize=signed-integer-overflow -fsanitize=bounds
|
||||||
-fsanitize=object-size -fsanitize=bool -fsanitize=enum
|
-fsanitize=object-size -fsanitize=bool -fsanitize=enum
|
||||||
-fsanitize=alignment -fsanitize=null -fsanitize=address)
|
-fsanitize=alignment -fsanitize=null)
|
||||||
add_compile_options(${SAN_FLAGS})
|
add_compile_options(${SAN_FLAGS})
|
||||||
link_libraries(${SAN_FLAGS})
|
link_libraries(${SAN_FLAGS})
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
Contributing
|
Contributing
|
||||||
============
|
============
|
||||||
|
|
||||||
RGBDS was created in the late '90s and has received contributions from several
|
RGBDS was created in the late 90's and has received contributions from several
|
||||||
developers since then. It wouldn't have been possible to get to this point
|
developers since then. It wouldn't have been possible to get to this point
|
||||||
without their work, and it is always open to the contributions of other people.
|
without their work and, for that reason, it is always open to the contributions
|
||||||
|
of other people.
|
||||||
|
|
||||||
Reporting Bugs
|
Reporting Bugs
|
||||||
--------------
|
--------------
|
||||||
|
|||||||
12
Makefile
12
Makefile
@@ -35,9 +35,8 @@ WARNFLAGS := -Wall
|
|||||||
# Overridable CFLAGS
|
# Overridable CFLAGS
|
||||||
CFLAGS ?= -O3 -flto -DNDEBUG
|
CFLAGS ?= -O3 -flto -DNDEBUG
|
||||||
# Non-overridable CFLAGS
|
# Non-overridable CFLAGS
|
||||||
# _ISOC11_SOURCE is required on certain platforms to get C11 on top of the C99-based POSIX 2008
|
REALCFLAGS := ${CFLAGS} ${WARNFLAGS} -std=gnu11 -D_POSIX_C_SOURCE=200809L \
|
||||||
REALCFLAGS := ${CFLAGS} ${WARNFLAGS} -std=gnu11 -I include \
|
-Iinclude
|
||||||
-D_POSIX_C_SOURCE=200809L -D_ISOC11_SOURCE
|
|
||||||
# Overridable LDFLAGS
|
# Overridable LDFLAGS
|
||||||
LDFLAGS ?=
|
LDFLAGS ?=
|
||||||
# Non-overridable LDFLAGS
|
# Non-overridable LDFLAGS
|
||||||
@@ -61,9 +60,9 @@ rgbasm_obj := \
|
|||||||
src/asm/lexer.o \
|
src/asm/lexer.o \
|
||||||
src/asm/macro.o \
|
src/asm/macro.o \
|
||||||
src/asm/main.o \
|
src/asm/main.o \
|
||||||
|
src/asm/parser.o \
|
||||||
src/asm/opt.o \
|
src/asm/opt.o \
|
||||||
src/asm/output.o \
|
src/asm/output.o \
|
||||||
src/asm/parser.o \
|
|
||||||
src/asm/rpn.o \
|
src/asm/rpn.o \
|
||||||
src/asm/section.o \
|
src/asm/section.o \
|
||||||
src/asm/symbol.o \
|
src/asm/symbol.o \
|
||||||
@@ -204,7 +203,7 @@ checkpatch:
|
|||||||
# compilation and make the continous integration infrastructure return failure.
|
# compilation and make the continous integration infrastructure return failure.
|
||||||
|
|
||||||
develop:
|
develop:
|
||||||
$Qenv $(MAKE) WARNFLAGS="-Werror -Wall -Wextra -Wpedantic -Wno-type-limits \
|
$Qenv $(MAKE) -j WARNFLAGS="-Werror -Wall -Wextra -Wpedantic -Wno-type-limits \
|
||||||
-Wno-sign-compare -Wvla -Wformat -Wformat-security -Wformat-overflow=2 \
|
-Wno-sign-compare -Wvla -Wformat -Wformat-security -Wformat-overflow=2 \
|
||||||
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused \
|
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused \
|
||||||
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=4 \
|
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=4 \
|
||||||
@@ -216,8 +215,7 @@ develop:
|
|||||||
-fsanitize=unreachable -fsanitize=vla-bound \
|
-fsanitize=unreachable -fsanitize=vla-bound \
|
||||||
-fsanitize=signed-integer-overflow -fsanitize=bounds \
|
-fsanitize=signed-integer-overflow -fsanitize=bounds \
|
||||||
-fsanitize=object-size -fsanitize=bool -fsanitize=enum \
|
-fsanitize=object-size -fsanitize=bool -fsanitize=enum \
|
||||||
-fsanitize=alignment -fsanitize=null -fsanitize=address" \
|
-fsanitize=alignment -fsanitize=null" CFLAGS="-ggdb3 -O0"
|
||||||
CFLAGS="-ggdb3 -O0"
|
|
||||||
|
|
||||||
# Targets for the project maintainer to easily create Windows exes.
|
# Targets for the project maintainer to easily create Windows exes.
|
||||||
# This is not for Windows users!
|
# This is not for Windows users!
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ The documentation of this toolchain can be viewed online
|
|||||||
`here <https://rgbds.gbdev.io/docs/>`__, it is generated from the man pages
|
`here <https://rgbds.gbdev.io/docs/>`__, it is generated from the man pages
|
||||||
found in this repository.
|
found in this repository.
|
||||||
|
|
||||||
If you want to contribute or maintain RGBDS, and have questions regarding the code, its organisation, etc. you can find me `on GBDev <https://gbdev.io/chat>`__ or via mail at ``rgbds at eldred dot fr``.
|
|
||||||
|
|
||||||
1. Installing RGBDS
|
1. Installing RGBDS
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
|||||||
65
RELEASE.rst
65
RELEASE.rst
@@ -1,65 +0,0 @@
|
|||||||
Releasing
|
|
||||||
=========
|
|
||||||
|
|
||||||
This describes for the maintainers of RGBDS how to publish a new release on
|
|
||||||
GitHub.
|
|
||||||
|
|
||||||
1. Update, commit, and push `include/version.h <include/version.h>`__ with
|
|
||||||
values for ``PACKAGE_VERSION_MAJOR``, ``PACKAGE_VERSION_MINOR``,
|
|
||||||
``PACKAGE_VERSION_PATCH``, and ``PACKAGE_VERSION_RC``. Only define
|
|
||||||
``PACKAGE_VERSION_RC`` if you are publishing a release candidate! You can
|
|
||||||
use ``git commit -m "Release <version>"`` and ``git push origin master``.
|
|
||||||
|
|
||||||
2. Create a Git tag formatted as ``v<MAJOR>.<MINOR>.<PATCH>``, or
|
|
||||||
``v<MAJOR>.<MINOR>.<PATCH>-rc<RC>`` for a release candidate. ``MAJOR``,
|
|
||||||
``MINOR``, ``PATCH``, and ``RC`` should match their values from
|
|
||||||
`include/version.h <include/version.h>`__. You can use ``git tag <tag>``.
|
|
||||||
|
|
||||||
3. Push the tag to GitHub. You can use ``git push origin <tag>``.
|
|
||||||
|
|
||||||
GitHub Actions will run the `create-release-artifacts.yaml
|
|
||||||
<.github/workflows/create-release-artifacts.yaml>`__ workflow to detect the
|
|
||||||
tag starting with "``v[0-9]``" and automatically do the following:
|
|
||||||
|
|
||||||
1. Build 32-bit and 64-bit RGBDS binaries for Windows with ``cmake``.
|
|
||||||
|
|
||||||
2. Package the binaries into zip files.
|
|
||||||
|
|
||||||
3. Package the source code into a tar.gz file with ``make dist``.
|
|
||||||
|
|
||||||
4. Create a draft GitHub release for the tag, attaching the three
|
|
||||||
packaged files. It will be a prerelease if the tag contains "``-rc``".
|
|
||||||
|
|
||||||
If an error occurred in the above steps, delete the tag and restart the
|
|
||||||
procedure. You can use ``git push --delete origin <tag>`` and
|
|
||||||
``git tag --delete <tag>``.
|
|
||||||
|
|
||||||
4. GitHub Actions will run the `create-release-docs.yml
|
|
||||||
<.github/workflows/create-release-docs.yml>`__ workflow to add the release
|
|
||||||
documentation to `rgbds-www <https://github.com/gbdev/rgbds-www>`__.
|
|
||||||
|
|
||||||
For a release candidate, which creates a prerelease, you will have to
|
|
||||||
take these steps yourself.
|
|
||||||
|
|
||||||
1. Clone `rgbds-www <https://github.com/gbdev/rgbds-www>`__. You can use
|
|
||||||
``git clone https://github.com/gbdev/rgbds-www.git``.
|
|
||||||
|
|
||||||
2. Make sure that you have installed ``groff`` and ``mandoc``. You will
|
|
||||||
need ``mandoc`` 1.14.5 or later to support ``-O toc``.
|
|
||||||
|
|
||||||
3. Run ``.github/actions/get-pages.sh -r <path/to/rgbds-www> <tag>``. This
|
|
||||||
will render the RGBDS documentation as HTML and PDF and copy it to
|
|
||||||
``rgbds-www``.
|
|
||||||
|
|
||||||
If you do not have ``groff`` installed, you can change
|
|
||||||
``groff -Tpdf -mdoc -wall`` to ``mandoc -Tpdf -I os=Linux`` in
|
|
||||||
`.github/actions/get-pages.sh <.github/actions/get-pages.sh>`__ and it
|
|
||||||
will suffice.
|
|
||||||
|
|
||||||
4. Commit and push the documentation. You can use ``git commit -m
|
|
||||||
"Create RGBDS <tag> documentation"`` and ``git push origin master``
|
|
||||||
(within the ``rgbds-www`` directory, not RGBDS).
|
|
||||||
|
|
||||||
5. Write a changelog in the GitHub draft release.
|
|
||||||
|
|
||||||
6. Click the "Publish release" button to publish it!
|
|
||||||
@@ -18,6 +18,5 @@ void charmap_Push(void);
|
|||||||
void charmap_Pop(void);
|
void charmap_Pop(void);
|
||||||
void charmap_Add(char *mapping, uint8_t value);
|
void charmap_Add(char *mapping, uint8_t value);
|
||||||
size_t charmap_Convert(char const *input, uint8_t *output);
|
size_t charmap_Convert(char const *input, uint8_t *output);
|
||||||
size_t charmap_ConvertNext(char const **input, uint8_t **output);
|
|
||||||
|
|
||||||
#endif /* RGBDS_ASM_CHARMAP_H */
|
#endif /* RGBDS_ASM_CHARMAP_H */
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
#ifndef RGBDS_FORMAT_SPEC_H
|
#ifndef RGBDS_FORMAT_SPEC_H
|
||||||
#define RGBDS_FORMAT_SPEC_H
|
#define RGBDS_FORMAT_SPEC_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
enum FormatState {
|
enum FormatState {
|
||||||
FORMAT_SIGN, // expects '+' or ' ' (optional)
|
FORMAT_SIGN, // expects '+' or ' ' (optional)
|
||||||
@@ -28,9 +28,9 @@ struct FormatSpec {
|
|||||||
bool prefix;
|
bool prefix;
|
||||||
bool alignLeft;
|
bool alignLeft;
|
||||||
bool padZero;
|
bool padZero;
|
||||||
size_t width;
|
uint8_t width;
|
||||||
bool hasFrac;
|
bool hasFrac;
|
||||||
size_t fracWidth;
|
uint8_t fracWidth;
|
||||||
int type;
|
int type;
|
||||||
bool valid;
|
bool valid;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ struct FileStackNamedNode { /* NODE_FILE, NODE_MACRO */
|
|||||||
char name[]; /* File name for files, file::macro name for macros */
|
char name[]; /* File name for files, file::macro name for macros */
|
||||||
};
|
};
|
||||||
|
|
||||||
extern size_t maxRecursionDepth;
|
extern size_t nMaxRecursionDepth;
|
||||||
|
|
||||||
struct MacroArgs;
|
struct MacroArgs;
|
||||||
|
|
||||||
@@ -72,12 +72,12 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size);
|
|||||||
bool yywrap(void);
|
bool yywrap(void);
|
||||||
void fstk_RunInclude(char const *path);
|
void fstk_RunInclude(char const *path);
|
||||||
void fstk_RunMacro(char const *macroName, struct MacroArgs *args);
|
void fstk_RunMacro(char const *macroName, struct MacroArgs *args);
|
||||||
void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size);
|
void fstk_RunRept(uint32_t count, int32_t nReptLineNo, char *body, size_t size);
|
||||||
void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step,
|
void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step,
|
||||||
int32_t reptLineNo, char *body, size_t size);
|
int32_t reptLineNo, char *body, size_t size);
|
||||||
void fstk_StopRept(void);
|
void fstk_StopRept(void);
|
||||||
bool fstk_Break(void);
|
bool fstk_Break(void);
|
||||||
|
|
||||||
void fstk_Init(char const *mainPath, size_t maxDepth);
|
void fstk_Init(char const *mainPath, size_t maxRecursionDepth);
|
||||||
|
|
||||||
#endif /* RGBDS_ASM_FSTACK_H */
|
#endif /* RGBDS_ASM_FSTACK_H */
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ static inline void lexer_SetGfxDigits(char const digits[4])
|
|||||||
* `path` is referenced, but not held onto..!
|
* `path` is referenced, but not held onto..!
|
||||||
*/
|
*/
|
||||||
struct LexerState *lexer_OpenFile(char const *path);
|
struct LexerState *lexer_OpenFile(char const *path);
|
||||||
struct LexerState *lexer_OpenFileView(char const *path, char *buf, size_t size, uint32_t lineNo);
|
struct LexerState *lexer_OpenFileView(char *buf, size_t size, uint32_t lineNo);
|
||||||
void lexer_RestartRept(uint32_t lineNo);
|
void lexer_RestartRept(uint32_t lineNo);
|
||||||
void lexer_DeleteState(struct LexerState *state);
|
void lexer_DeleteState(struct LexerState *state);
|
||||||
void lexer_Init(void);
|
void lexer_Init(void);
|
||||||
@@ -88,8 +88,8 @@ uint32_t lexer_GetLineNo(void);
|
|||||||
uint32_t lexer_GetColNo(void);
|
uint32_t lexer_GetColNo(void);
|
||||||
void lexer_DumpStringExpansions(void);
|
void lexer_DumpStringExpansions(void);
|
||||||
int yylex(void);
|
int yylex(void);
|
||||||
bool lexer_CaptureRept(struct CaptureBody *capture);
|
void lexer_CaptureRept(struct CaptureBody *capture);
|
||||||
bool lexer_CaptureMacroBody(struct CaptureBody *capture);
|
void lexer_CaptureMacroBody(struct CaptureBody *capture);
|
||||||
|
|
||||||
#define INITIAL_DS_ARG_SIZE 2
|
#define INITIAL_DS_ARG_SIZE 2
|
||||||
struct DsArgList {
|
struct DsArgList {
|
||||||
|
|||||||
@@ -16,14 +16,22 @@
|
|||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
|
||||||
extern bool haltnop;
|
extern bool haltnop;
|
||||||
extern bool optimizeLoads;
|
extern bool optimizeloads;
|
||||||
extern bool verbose;
|
extern bool verbose;
|
||||||
extern bool warnings; /* True to enable warnings, false to disable them. */
|
extern bool warnings; /* True to enable warnings, false to disable them. */
|
||||||
|
|
||||||
extern FILE *dependfile;
|
extern FILE *dependfile;
|
||||||
extern char *targetFileName;
|
extern char *tzTargetFileName;
|
||||||
extern bool generatedMissingIncludes;
|
extern bool oGeneratedMissingIncludes;
|
||||||
extern bool failedOnMissingInclude;
|
extern bool oFailedOnMissingInclude;
|
||||||
extern bool generatePhonyDeps;
|
extern bool oGeneratePhonyDeps;
|
||||||
|
|
||||||
|
/* TODO: are these really needed? */
|
||||||
|
#define YY_FATAL_ERROR fatalerror
|
||||||
|
|
||||||
|
#ifdef YYLMAX
|
||||||
|
#undef YYLMAX
|
||||||
|
#endif
|
||||||
|
#define YYLMAX 65536
|
||||||
|
|
||||||
#endif /* RGBDS_MAIN_H */
|
#endif /* RGBDS_MAIN_H */
|
||||||
|
|||||||
@@ -9,16 +9,14 @@
|
|||||||
#ifndef RGBDS_OPT_H
|
#ifndef RGBDS_OPT_H
|
||||||
#define RGBDS_OPT_H
|
#define RGBDS_OPT_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
void opt_B(char chars[2]);
|
void opt_B(char chars[2]);
|
||||||
void opt_G(char chars[4]);
|
void opt_G(char chars[4]);
|
||||||
void opt_P(uint8_t fill);
|
void opt_P(uint8_t fill);
|
||||||
void opt_L(bool optimize);
|
|
||||||
void opt_W(char const *flag);
|
|
||||||
void opt_Parse(char const *option);
|
void opt_Parse(char const *option);
|
||||||
|
|
||||||
void opt_Push(void);
|
void opt_Push(void);
|
||||||
void opt_Pop(void);
|
void opt_Pop(void);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
struct Expression;
|
struct Expression;
|
||||||
struct FileStackNode;
|
struct FileStackNode;
|
||||||
|
|
||||||
extern char *objectName;
|
extern char *tzObjectname;
|
||||||
extern struct Section *sectionList;
|
extern struct Section *pSectionList, *pCurrentSection;
|
||||||
|
|
||||||
void out_RegisterNode(struct FileStackNode *node);
|
void out_RegisterNode(struct FileStackNode *node);
|
||||||
void out_ReplaceNode(struct FileStackNode *node);
|
void out_ReplaceNode(struct FileStackNode *node);
|
||||||
|
|||||||
@@ -17,20 +17,20 @@
|
|||||||
#define MAXRPNLEN 1048576
|
#define MAXRPNLEN 1048576
|
||||||
|
|
||||||
struct Expression {
|
struct Expression {
|
||||||
int32_t val; // If the expression's value is known, it's here
|
int32_t nVal; // If the expression's value is known, it's here
|
||||||
char *reason; // Why the expression is not known, if it isn't
|
char *reason; // Why the expression is not known, if it isn't
|
||||||
bool isKnown; // Whether the expression's value is known
|
bool isKnown; // Whether the expression's value is known
|
||||||
bool isSymbol; // Whether the expression represents a symbol
|
bool isSymbol; // Whether the expression represents a symbol
|
||||||
uint8_t *rpn; // Array of bytes serializing the RPN expression
|
uint8_t *tRPN; // Array of bytes serializing the RPN expression
|
||||||
uint32_t rpnCapacity; // Size of the `rpn` buffer
|
uint32_t nRPNCapacity; // Size of the `tRPN` buffer
|
||||||
uint32_t rpnLength; // Used size of the `rpn` buffer
|
uint32_t nRPNLength; // Used size of the `tRPN` buffer
|
||||||
uint32_t rpnPatchSize; // Size the expression will take in the obj file
|
uint32_t nRPNPatchSize; // Size the expression will take in the obj file
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determines if an expression is known at assembly time
|
* Determines if an expression is known at assembly time
|
||||||
*/
|
*/
|
||||||
static inline bool rpn_isKnown(struct Expression const *expr)
|
static inline bool rpn_isKnown(const struct Expression *expr)
|
||||||
{
|
{
|
||||||
return expr->isKnown;
|
return expr->isKnown;
|
||||||
}
|
}
|
||||||
@@ -43,11 +43,11 @@ static inline bool rpn_isSymbol(const struct Expression *expr)
|
|||||||
return expr->isSymbol;
|
return expr->isSymbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rpn_Symbol(struct Expression *expr, char const *symName);
|
void rpn_Symbol(struct Expression *expr, char const *tzSym);
|
||||||
void rpn_Number(struct Expression *expr, uint32_t i);
|
void rpn_Number(struct Expression *expr, uint32_t i);
|
||||||
void rpn_LOGNOT(struct Expression *expr, const struct Expression *src);
|
void rpn_LOGNOT(struct Expression *expr, const struct Expression *src);
|
||||||
struct Symbol const *rpn_SymbolOf(struct Expression const *expr);
|
struct Symbol const *rpn_SymbolOf(struct Expression const *expr);
|
||||||
bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *symName);
|
bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *sym);
|
||||||
void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
||||||
const struct Expression *src1,
|
const struct Expression *src1,
|
||||||
const struct Expression *src2);
|
const struct Expression *src2);
|
||||||
@@ -56,15 +56,11 @@ void rpn_LOW(struct Expression *expr, const struct Expression *src);
|
|||||||
void rpn_ISCONST(struct Expression *expr, const struct Expression *src);
|
void rpn_ISCONST(struct Expression *expr, const struct Expression *src);
|
||||||
void rpn_UNNEG(struct Expression *expr, const struct Expression *src);
|
void rpn_UNNEG(struct Expression *expr, const struct Expression *src);
|
||||||
void rpn_UNNOT(struct Expression *expr, const struct Expression *src);
|
void rpn_UNNOT(struct Expression *expr, const struct Expression *src);
|
||||||
void rpn_BankSymbol(struct Expression *expr, char const *symName);
|
void rpn_BankSymbol(struct Expression *expr, char const *tzSym);
|
||||||
void rpn_BankSection(struct Expression *expr, char const *sectionName);
|
void rpn_BankSection(struct Expression *expr, char const *tzSectionName);
|
||||||
void rpn_BankSelf(struct Expression *expr);
|
void rpn_BankSelf(struct Expression *expr);
|
||||||
void rpn_SizeOfSection(struct Expression *expr, char const *sectionName);
|
|
||||||
void rpn_StartOfSection(struct Expression *expr, char const *sectionName);
|
|
||||||
void rpn_Free(struct Expression *expr);
|
void rpn_Free(struct Expression *expr);
|
||||||
void rpn_CheckHRAM(struct Expression *expr, const struct Expression *src);
|
void rpn_CheckHRAM(struct Expression *expr, const struct Expression *src);
|
||||||
void rpn_CheckRST(struct Expression *expr, const struct Expression *src);
|
void rpn_CheckRST(struct Expression *expr, const struct Expression *src);
|
||||||
void rpn_CheckNBit(struct Expression const *expr, uint8_t n);
|
|
||||||
int32_t rpn_GetConstVal(struct Expression const *expr);
|
|
||||||
|
|
||||||
#endif /* RGBDS_ASM_RPN_H */
|
#endif /* RGBDS_ASM_RPN_H */
|
||||||
|
|||||||
@@ -40,14 +40,14 @@ struct SectionSpec {
|
|||||||
uint16_t alignOfs;
|
uint16_t alignOfs;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct Section *currentSection;
|
struct Section *out_FindSectionByName(const char *name);
|
||||||
|
void out_NewSection(char const *name, uint32_t secttype, uint32_t org,
|
||||||
struct Section *sect_FindSectionByName(const char *name);
|
struct SectionSpec const *attributes,
|
||||||
void sect_NewSection(char const *name, uint32_t secttype, uint32_t org,
|
enum SectionModifier mod);
|
||||||
struct SectionSpec const *attributes, enum SectionModifier mod);
|
void out_SetLoadSection(char const *name, uint32_t secttype, uint32_t org,
|
||||||
void sect_SetLoadSection(char const *name, uint32_t secttype, uint32_t org,
|
struct SectionSpec const *attributes,
|
||||||
struct SectionSpec const *attributes, enum SectionModifier mod);
|
enum SectionModifier mod);
|
||||||
void sect_EndLoadSection(void);
|
void out_EndLoadSection(void);
|
||||||
|
|
||||||
struct Section *sect_GetSymbolSection(void);
|
struct Section *sect_GetSymbolSection(void);
|
||||||
uint32_t sect_GetSymbolOffset(void);
|
uint32_t sect_GetSymbolOffset(void);
|
||||||
@@ -59,21 +59,21 @@ void sect_NextUnionMember(void);
|
|||||||
void sect_EndUnion(void);
|
void sect_EndUnion(void);
|
||||||
void sect_CheckUnionClosed(void);
|
void sect_CheckUnionClosed(void);
|
||||||
|
|
||||||
void sect_AbsByte(uint8_t b);
|
void out_AbsByte(uint8_t b);
|
||||||
void sect_AbsByteGroup(uint8_t const *s, int32_t length);
|
void out_AbsByteGroup(uint8_t const *s, int32_t length);
|
||||||
void sect_AbsWordGroup(uint8_t const *s, int32_t length);
|
void out_AbsWordGroup(uint8_t const *s, int32_t length);
|
||||||
void sect_AbsLongGroup(uint8_t const *s, int32_t length);
|
void out_AbsLongGroup(uint8_t const *s, int32_t length);
|
||||||
void sect_Skip(int32_t skip, bool ds);
|
void out_Skip(int32_t skip, bool ds);
|
||||||
void sect_String(char const *s);
|
void out_String(char const *s);
|
||||||
void sect_RelByte(struct Expression *expr, uint32_t pcShift);
|
void out_RelByte(struct Expression *expr, uint32_t pcShift);
|
||||||
void sect_RelBytes(uint32_t n, struct Expression *exprs, size_t size);
|
void out_RelBytes(uint32_t n, struct Expression *exprs, size_t size);
|
||||||
void sect_RelWord(struct Expression *expr, uint32_t pcShift);
|
void out_RelWord(struct Expression *expr, uint32_t pcShift);
|
||||||
void sect_RelLong(struct Expression *expr, uint32_t pcShift);
|
void out_RelLong(struct Expression *expr, uint32_t pcShift);
|
||||||
void sect_PCRelByte(struct Expression *expr, uint32_t pcShift);
|
void out_PCRelByte(struct Expression *expr, uint32_t pcShift);
|
||||||
void sect_BinaryFile(char const *s, int32_t startPos);
|
void out_BinaryFile(char const *s, int32_t startPos);
|
||||||
void sect_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length);
|
void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length);
|
||||||
|
|
||||||
void sect_PushSection(void);
|
void out_PushSection(void);
|
||||||
void sect_PopSection(void);
|
void out_PopSection(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -45,13 +45,13 @@ struct Symbol {
|
|||||||
/* If sym_IsNumeric */
|
/* If sym_IsNumeric */
|
||||||
int32_t value;
|
int32_t value;
|
||||||
int32_t (*numCallback)(void);
|
int32_t (*numCallback)(void);
|
||||||
/* For SYM_MACRO and SYM_EQUS; TODO: have separate fields */
|
/* For SYM_MACRO */
|
||||||
struct {
|
struct {
|
||||||
size_t macroSize;
|
size_t macroSize;
|
||||||
char *macro;
|
char *macro;
|
||||||
};
|
};
|
||||||
/* For SYM_EQUS */
|
/* For SYM_EQUS, TODO: separate "base" fields from SYM_MACRO */
|
||||||
char const *(*strCallback)(void);
|
char const *(*strCallback)(void); /* For SYM_EQUS */
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32_t ID; /* ID of the symbol in the object file (-1 if none) */
|
uint32_t ID; /* ID of the symbol in the object file (-1 if none) */
|
||||||
@@ -82,7 +82,8 @@ static inline bool sym_IsConstant(struct Symbol const *sym)
|
|||||||
|
|
||||||
static inline bool sym_IsNumeric(struct Symbol const *sym)
|
static inline bool sym_IsNumeric(struct Symbol const *sym)
|
||||||
{
|
{
|
||||||
return sym->type == SYM_LABEL || sym->type == SYM_EQU || sym->type == SYM_SET;
|
return sym->type == SYM_LABEL || sym->type == SYM_EQU
|
||||||
|
|| sym->type == SYM_SET;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool sym_IsLabel(struct Symbol const *sym)
|
static inline bool sym_IsLabel(struct Symbol const *sym)
|
||||||
@@ -120,23 +121,22 @@ struct Symbol *sym_AddAnonLabel(void);
|
|||||||
void sym_WriteAnonLabelName(char buf[MIN_NB_ELMS(MAXSYMLEN + 1)], uint32_t ofs, bool neg);
|
void sym_WriteAnonLabelName(char buf[MIN_NB_ELMS(MAXSYMLEN + 1)], uint32_t ofs, bool neg);
|
||||||
void sym_Export(char const *symName);
|
void sym_Export(char const *symName);
|
||||||
struct Symbol *sym_AddEqu(char const *symName, int32_t value);
|
struct Symbol *sym_AddEqu(char const *symName, int32_t value);
|
||||||
struct Symbol *sym_RedefEqu(char const *symName, int32_t value);
|
|
||||||
struct Symbol *sym_AddSet(char const *symName, int32_t value);
|
struct Symbol *sym_AddSet(char const *symName, int32_t value);
|
||||||
uint32_t sym_GetPCValue(void);
|
uint32_t sym_GetPCValue(void);
|
||||||
uint32_t sym_GetConstantSymValue(struct Symbol const *sym);
|
uint32_t sym_GetConstantSymValue(struct Symbol const *sym);
|
||||||
uint32_t sym_GetConstantValue(char const *symName);
|
uint32_t sym_GetConstantValue(char const *s);
|
||||||
/*
|
/*
|
||||||
* Find a symbol by exact name, bypassing expansion checks
|
* Find a symbol by exact name, bypassing expansion checks
|
||||||
*/
|
*/
|
||||||
struct Symbol *sym_FindExactSymbol(char const *symName);
|
struct Symbol *sym_FindExactSymbol(char const *name);
|
||||||
/*
|
/*
|
||||||
* Find a symbol by exact name; may not be scoped, produces an error if it is
|
* Find a symbol by exact name; may not be scoped, produces an error if it is
|
||||||
*/
|
*/
|
||||||
struct Symbol *sym_FindUnscopedSymbol(char const *symName);
|
struct Symbol *sym_FindUnscopedSymbol(char const *name);
|
||||||
/*
|
/*
|
||||||
* Find a symbol, possibly scoped, by name
|
* Find a symbol, possibly scoped, by name
|
||||||
*/
|
*/
|
||||||
struct Symbol *sym_FindScopedSymbol(char const *symName);
|
struct Symbol *sym_FindScopedSymbol(char const *name);
|
||||||
struct Symbol const *sym_GetPC(void);
|
struct Symbol const *sym_GetPC(void);
|
||||||
struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size);
|
struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size);
|
||||||
struct Symbol *sym_Ref(char const *symName);
|
struct Symbol *sym_Ref(char const *symName);
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
char const *printChar(int c);
|
uint32_t calchash(const char *s);
|
||||||
|
char const *print(int c);
|
||||||
/*
|
/*
|
||||||
* @return The number of bytes read, or 0 if invalid data was found
|
* @return The number of bytes read, or 0 if invalid data was found
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -13,16 +13,8 @@
|
|||||||
|
|
||||||
extern unsigned int nbErrors;
|
extern unsigned int nbErrors;
|
||||||
|
|
||||||
enum WarningState {
|
|
||||||
WARNING_DEFAULT,
|
|
||||||
WARNING_DISABLED,
|
|
||||||
WARNING_ENABLED,
|
|
||||||
WARNING_ERROR
|
|
||||||
};
|
|
||||||
|
|
||||||
enum WarningID {
|
enum WarningID {
|
||||||
WARNING_ASSERT, /* Assertions */
|
WARNING_ASSERT, /* Assertions */
|
||||||
WARNING_BACKWARDS_FOR, /* `for` loop with backwards range */
|
|
||||||
WARNING_BUILTIN_ARG, /* Invalid args to builtins */
|
WARNING_BUILTIN_ARG, /* Invalid args to builtins */
|
||||||
WARNING_CHARMAP_REDEF, /* Charmap entry re-definition */
|
WARNING_CHARMAP_REDEF, /* Charmap entry re-definition */
|
||||||
WARNING_DIV, /* Division undefined behavior */
|
WARNING_DIV, /* Division undefined behavior */
|
||||||
@@ -50,9 +42,6 @@ enum WarningID {
|
|||||||
#define NB_META_WARNINGS (NB_WARNINGS_ALL - NB_WARNINGS)
|
#define NB_META_WARNINGS (NB_WARNINGS_ALL - NB_WARNINGS)
|
||||||
};
|
};
|
||||||
|
|
||||||
extern enum WarningState warningStates[NB_WARNINGS];
|
|
||||||
extern bool warningsAreErrors;
|
|
||||||
|
|
||||||
void processWarningFlag(char const *flag);
|
void processWarningFlag(char const *flag);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -29,9 +29,19 @@ typedef struct HashMapEntry *HashMap[HASHMAP_NB_BUCKETS];
|
|||||||
* @param map The HashMap to add the element to
|
* @param map The HashMap to add the element to
|
||||||
* @param key The key with which the element will be stored and retrieved
|
* @param key The key with which the element will be stored and retrieved
|
||||||
* @param element The element to add
|
* @param element The element to add
|
||||||
* @return A pointer to the pointer to the element.
|
* @return True if a collision occurred (for statistics)
|
||||||
*/
|
*/
|
||||||
void **hash_AddElement(HashMap map, char const *key, void *element);
|
bool hash_AddElement(HashMap map, char const *key, void *element);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces an element with an already-present key in a hashmap.
|
||||||
|
* @warning Inserting a NULL will make `hash_GetElement`'s return ambiguous!
|
||||||
|
* @param map The HashMap to replace the element in
|
||||||
|
* @param key The key with which the element will be stored and retrieved
|
||||||
|
* @param element The element to replace
|
||||||
|
* @return True if the element was found and replaced
|
||||||
|
*/
|
||||||
|
bool hash_ReplaceElement(HashMap const map, char const *key, void *element);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes an element from a hashmap.
|
* Removes an element from a hashmap.
|
||||||
@@ -41,14 +51,6 @@ void **hash_AddElement(HashMap map, char const *key, void *element);
|
|||||||
*/
|
*/
|
||||||
bool hash_RemoveElement(HashMap map, char const *key);
|
bool hash_RemoveElement(HashMap map, char const *key);
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds an element in a hashmap, and returns a pointer to its value field.
|
|
||||||
* @param map The map to consider the elements of
|
|
||||||
* @param key The key to search an element for
|
|
||||||
* @return A pointer to the pointer to the element, or NULL if not found.
|
|
||||||
*/
|
|
||||||
void **hash_GetNode(HashMap const map, char const *key);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds an element in a hashmap.
|
* Finds an element in a hashmap.
|
||||||
* @param map The map to consider the elements of
|
* @param map The map to consider the elements of
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
#ifdef __GNUC__ // GCC or compatible
|
#ifdef __GNUC__ // GCC or compatible
|
||||||
#define format_(archetype, str_index, first_arg) \
|
#define format_(archetype, str_index, first_arg) \
|
||||||
__attribute__ ((format (archetype, str_index, first_arg)))
|
__attribute__ ((format (archetype, str_index, first_arg)))
|
||||||
#define attr_(...) __attribute__ ((__VA_ARGS__))
|
|
||||||
// In release builds, define "unreachable" as such, but trap in debug builds
|
// In release builds, define "unreachable" as such, but trap in debug builds
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
#define unreachable_ __builtin_unreachable
|
#define unreachable_ __builtin_unreachable
|
||||||
@@ -28,7 +27,6 @@
|
|||||||
#else
|
#else
|
||||||
// Unsupported, but no need to throw a fit
|
// Unsupported, but no need to throw a fit
|
||||||
#define format_(archetype, str_index, first_arg)
|
#define format_(archetype, str_index, first_arg)
|
||||||
#define attr_(...)
|
|
||||||
// This seems to generate similar code to __builtin_unreachable, despite different semantics
|
// This seems to generate similar code to __builtin_unreachable, despite different semantics
|
||||||
// Note that executing this is undefined behavior (declared _Noreturn, but does return)
|
// Note that executing this is undefined behavior (declared _Noreturn, but does return)
|
||||||
static inline _Noreturn unreachable_(void) {}
|
static inline _Noreturn unreachable_(void) {}
|
||||||
@@ -61,6 +59,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
// FIXME: these are rarely used, and need testing...
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
static inline int ctz(unsigned int x)
|
static inline int ctz(unsigned int x)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,7 +21,10 @@ struct Assertion {
|
|||||||
struct Patch patch;
|
struct Patch patch;
|
||||||
// enum AssertionType type; The `patch`'s field is instead re-used
|
// enum AssertionType type; The `patch`'s field is instead re-used
|
||||||
char *message;
|
char *message;
|
||||||
// This would be redundant with `.section->fileSymbols`... but `section` is sometimes NULL!
|
/*
|
||||||
|
* This would be redundant with `.section->fileSymbols`... but
|
||||||
|
* `section` is sometimes NULL!
|
||||||
|
*/
|
||||||
struct Symbol **fileSymbols;
|
struct Symbol **fileSymbols;
|
||||||
|
|
||||||
struct Assertion *next;
|
struct Assertion *next;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
#define RGBDS_OBJECT_VERSION_STRING "RGB%1u"
|
#define RGBDS_OBJECT_VERSION_STRING "RGB%1u"
|
||||||
#define RGBDS_OBJECT_VERSION_NUMBER 9U
|
#define RGBDS_OBJECT_VERSION_NUMBER 9U
|
||||||
#define RGBDS_OBJECT_REV 8U
|
#define RGBDS_OBJECT_REV 7U
|
||||||
|
|
||||||
enum AssertionType {
|
enum AssertionType {
|
||||||
ASSERT_WARN,
|
ASSERT_WARN,
|
||||||
@@ -23,44 +23,42 @@ enum AssertionType {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum RPNCommand {
|
enum RPNCommand {
|
||||||
RPN_ADD = 0x00,
|
RPN_ADD = 0x00,
|
||||||
RPN_SUB = 0x01,
|
RPN_SUB = 0x01,
|
||||||
RPN_MUL = 0x02,
|
RPN_MUL = 0x02,
|
||||||
RPN_DIV = 0x03,
|
RPN_DIV = 0x03,
|
||||||
RPN_MOD = 0x04,
|
RPN_MOD = 0x04,
|
||||||
RPN_UNSUB = 0x05, // FIXME: should be renamed to "NEG" for consistency
|
RPN_UNSUB = 0x05,
|
||||||
RPN_EXP = 0x06,
|
RPN_EXP = 0x06,
|
||||||
|
|
||||||
RPN_OR = 0x10,
|
RPN_OR = 0x10,
|
||||||
RPN_AND = 0x11,
|
RPN_AND = 0x11,
|
||||||
RPN_XOR = 0x12,
|
RPN_XOR = 0x12,
|
||||||
RPN_UNNOT = 0x13, // FIXME: should be renamed to "NOT" for consistency
|
RPN_UNNOT = 0x13,
|
||||||
|
|
||||||
RPN_LOGAND = 0x21,
|
RPN_LOGAND = 0x21,
|
||||||
RPN_LOGOR = 0x22,
|
RPN_LOGOR = 0x22,
|
||||||
RPN_LOGUNNOT = 0x23, // FIXME: should be renamed to "LOGNOT" for consistency
|
RPN_LOGUNNOT = 0x23,
|
||||||
|
|
||||||
RPN_LOGEQ = 0x30,
|
RPN_LOGEQ = 0x30,
|
||||||
RPN_LOGNE = 0x31,
|
RPN_LOGNE = 0x31,
|
||||||
RPN_LOGGT = 0x32,
|
RPN_LOGGT = 0x32,
|
||||||
RPN_LOGLT = 0x33,
|
RPN_LOGLT = 0x33,
|
||||||
RPN_LOGGE = 0x34,
|
RPN_LOGGE = 0x34,
|
||||||
RPN_LOGLE = 0x35,
|
RPN_LOGLE = 0x35,
|
||||||
|
|
||||||
RPN_SHL = 0x40,
|
RPN_SHL = 0x40,
|
||||||
RPN_SHR = 0x41,
|
RPN_SHR = 0x41,
|
||||||
|
|
||||||
RPN_BANK_SYM = 0x50,
|
RPN_BANK_SYM = 0x50,
|
||||||
RPN_BANK_SECT = 0x51,
|
RPN_BANK_SECT = 0x51,
|
||||||
RPN_BANK_SELF = 0x52,
|
RPN_BANK_SELF = 0x52,
|
||||||
RPN_SIZEOF_SECT = 0x53,
|
|
||||||
RPN_STARTOF_SECT = 0x54,
|
|
||||||
|
|
||||||
RPN_HRAM = 0x60,
|
RPN_HRAM = 0x60,
|
||||||
RPN_RST = 0x61,
|
RPN_RST = 0x61,
|
||||||
|
|
||||||
RPN_CONST = 0x80,
|
RPN_CONST = 0x80,
|
||||||
RPN_SYM = 0x81
|
RPN_SYM = 0x81
|
||||||
};
|
};
|
||||||
|
|
||||||
enum SectionType {
|
enum SectionType {
|
||||||
|
|||||||
@@ -11,10 +11,9 @@
|
|||||||
#ifndef RGBDS_PLATFORM_H
|
#ifndef RGBDS_PLATFORM_H
|
||||||
#define RGBDS_PLATFORM_H
|
#define RGBDS_PLATFORM_H
|
||||||
|
|
||||||
// MSVC doesn't have str(n)casecmp, use a suitable replacement
|
/* MSVC doesn't have strncasecmp, use a suitable replacement */
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
# include <string.h>
|
# include <string.h>
|
||||||
# define strcasecmp _stricmp
|
|
||||||
# define strncasecmp _strnicmp
|
# define strncasecmp _strnicmp
|
||||||
#else
|
#else
|
||||||
# include <strings.h>
|
# include <strings.h>
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
|
|
||||||
#define PACKAGE_VERSION_MAJOR 0
|
#define PACKAGE_VERSION_MAJOR 0
|
||||||
#define PACKAGE_VERSION_MINOR 5
|
#define PACKAGE_VERSION_MINOR 5
|
||||||
#define PACKAGE_VERSION_PATCH 1
|
#define PACKAGE_VERSION_PATCH 0
|
||||||
|
#define PACKAGE_VERSION_RC 2
|
||||||
|
|
||||||
const char *get_package_version_string(void);
|
const char *get_package_version_string(void);
|
||||||
|
|
||||||
|
|||||||
@@ -44,35 +44,32 @@ struct Charmap {
|
|||||||
|
|
||||||
static HashMap charmaps;
|
static HashMap charmaps;
|
||||||
|
|
||||||
/*
|
static struct Charmap *currentCharmap;
|
||||||
* Store pointers to hashmap nodes, so that there is only one pointer to the memory block
|
|
||||||
* that gets reallocated.
|
|
||||||
*/
|
|
||||||
static struct Charmap **currentCharmap;
|
|
||||||
|
|
||||||
struct CharmapStackEntry {
|
struct CharmapStackEntry {
|
||||||
struct Charmap **charmap;
|
struct Charmap *charmap;
|
||||||
struct CharmapStackEntry *next;
|
struct CharmapStackEntry *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CharmapStackEntry *charmapStack;
|
struct CharmapStackEntry *charmapStack;
|
||||||
|
|
||||||
static struct Charmap *charmap_Get(char const *name)
|
static inline struct Charmap *charmap_Get(const char *name)
|
||||||
{
|
{
|
||||||
return hash_GetElement(charmaps, name);
|
return hash_GetElement(charmaps, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void resizeCharmap(struct Charmap **map, size_t capacity)
|
static inline struct Charmap *resizeCharmap(struct Charmap *map, size_t capacity)
|
||||||
{
|
{
|
||||||
*map = realloc(*map, sizeof(**map) + sizeof(*(*map)->nodes) * capacity);
|
struct Charmap *new = realloc(map, sizeof(*map) + sizeof(*map->nodes) * capacity);
|
||||||
|
|
||||||
if (!*map)
|
if (!new)
|
||||||
fatalerror("Failed to %s charmap: %s\n",
|
fatalerror("Failed to %s charmap: %s\n",
|
||||||
*map ? "create" : "resize", strerror(errno));
|
map ? "create" : "resize", strerror(errno));
|
||||||
(**map).capacity = capacity;
|
new->capacity = capacity;
|
||||||
|
return new;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void initNode(struct Charnode *node)
|
static inline void initNode(struct Charnode *node)
|
||||||
{
|
{
|
||||||
node->isTerminal = false;
|
node->isTerminal = false;
|
||||||
memset(node->next, 0, sizeof(node->next));
|
memset(node->next, 0, sizeof(node->next));
|
||||||
@@ -98,18 +95,19 @@ struct Charmap *charmap_New(const char *name, const char *baseName)
|
|||||||
|
|
||||||
/* Init the new charmap's fields */
|
/* Init the new charmap's fields */
|
||||||
if (base) {
|
if (base) {
|
||||||
resizeCharmap(&charmap, base->capacity);
|
charmap = resizeCharmap(NULL, base->capacity);
|
||||||
charmap->usedNodes = base->usedNodes;
|
charmap->usedNodes = base->usedNodes;
|
||||||
|
|
||||||
memcpy(charmap->nodes, base->nodes, sizeof(base->nodes[0]) * charmap->usedNodes);
|
memcpy(charmap->nodes, base->nodes, sizeof(base->nodes[0]) * charmap->usedNodes);
|
||||||
} else {
|
} else {
|
||||||
resizeCharmap(&charmap, INITIAL_CAPACITY);
|
charmap = resizeCharmap(NULL, INITIAL_CAPACITY);
|
||||||
charmap->usedNodes = 1;
|
charmap->usedNodes = 1;
|
||||||
initNode(&charmap->nodes[0]); /* Init the root node */
|
initNode(&charmap->nodes[0]); /* Init the root node */
|
||||||
}
|
}
|
||||||
charmap->name = strdup(name);
|
charmap->name = strdup(name);
|
||||||
|
|
||||||
currentCharmap = (struct Charmap **)hash_AddElement(charmaps, charmap->name, charmap);
|
hash_AddElement(charmaps, charmap->name, charmap);
|
||||||
|
currentCharmap = charmap;
|
||||||
|
|
||||||
return charmap;
|
return charmap;
|
||||||
}
|
}
|
||||||
@@ -122,7 +120,7 @@ void charmap_Delete(struct Charmap *charmap)
|
|||||||
|
|
||||||
void charmap_Set(const char *name)
|
void charmap_Set(const char *name)
|
||||||
{
|
{
|
||||||
struct Charmap **charmap = (struct Charmap **)hash_GetNode(charmaps, name);
|
struct Charmap *charmap = charmap_Get(name);
|
||||||
|
|
||||||
if (charmap == NULL)
|
if (charmap == NULL)
|
||||||
error("Charmap '%s' doesn't exist\n", name);
|
error("Charmap '%s' doesn't exist\n", name);
|
||||||
@@ -160,26 +158,25 @@ void charmap_Pop(void)
|
|||||||
|
|
||||||
void charmap_Add(char *mapping, uint8_t value)
|
void charmap_Add(char *mapping, uint8_t value)
|
||||||
{
|
{
|
||||||
struct Charmap *charmap = *currentCharmap;
|
struct Charnode *node = ¤tCharmap->nodes[0];
|
||||||
struct Charnode *node = &charmap->nodes[0];
|
|
||||||
|
|
||||||
for (uint8_t c; *mapping; mapping++) {
|
for (uint8_t c; *mapping; mapping++) {
|
||||||
c = *mapping - 1;
|
c = *mapping - 1;
|
||||||
|
|
||||||
if (node->next[c]) {
|
if (node->next[c]) {
|
||||||
node = &charmap->nodes[node->next[c]];
|
node = ¤tCharmap->nodes[node->next[c]];
|
||||||
} else {
|
} else {
|
||||||
/* Register next available node */
|
/* Register next available node */
|
||||||
node->next[c] = charmap->usedNodes;
|
node->next[c] = currentCharmap->usedNodes;
|
||||||
/* If no more nodes are available, get new ones */
|
/* If no more nodes are available, get new ones */
|
||||||
if (charmap->usedNodes == charmap->capacity) {
|
if (currentCharmap->usedNodes == currentCharmap->capacity) {
|
||||||
charmap->capacity *= 2;
|
currentCharmap->capacity *= 2;
|
||||||
resizeCharmap(currentCharmap, charmap->capacity);
|
currentCharmap = resizeCharmap(currentCharmap, currentCharmap->capacity);
|
||||||
charmap = *currentCharmap;
|
hash_ReplaceElement(charmaps, currentCharmap->name, currentCharmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Switch to and init new node */
|
/* Switch to and init new node */
|
||||||
node = &charmap->nodes[charmap->usedNodes++];
|
node = ¤tCharmap->nodes[currentCharmap->usedNodes++];
|
||||||
initNode(node);
|
initNode(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,16 +189,6 @@ void charmap_Add(char *mapping, uint8_t value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t charmap_Convert(char const *input, uint8_t *output)
|
size_t charmap_Convert(char const *input, uint8_t *output)
|
||||||
{
|
|
||||||
uint8_t *start = output;
|
|
||||||
|
|
||||||
while (charmap_ConvertNext(&input, &output))
|
|
||||||
;
|
|
||||||
|
|
||||||
return output - start;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t charmap_ConvertNext(char const **input, uint8_t **output)
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* The goal is to match the longest mapping possible.
|
* The goal is to match the longest mapping possible.
|
||||||
@@ -209,55 +196,51 @@ size_t charmap_ConvertNext(char const **input, uint8_t **output)
|
|||||||
* If that would lead to a dead end, rewind characters until the last match, and output.
|
* If that would lead to a dead end, rewind characters until the last match, and output.
|
||||||
* If no match, read a UTF-8 codepoint and output that.
|
* If no match, read a UTF-8 codepoint and output that.
|
||||||
*/
|
*/
|
||||||
struct Charmap const *charmap = *currentCharmap;
|
size_t outputLen = 0;
|
||||||
struct Charnode const *node = &charmap->nodes[0];
|
struct Charnode const *node = ¤tCharmap->nodes[0];
|
||||||
struct Charnode const *match = NULL;
|
struct Charnode const *match = NULL;
|
||||||
size_t rewindDistance = 0;
|
size_t rewindDistance = 0;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
/* We still want NULs to reach the `else` path, to give a chance to rewind */
|
/* We still want NULs to reach the `else` path, to give a chance to rewind */
|
||||||
uint8_t c = **input - 1;
|
uint8_t c = *input - 1;
|
||||||
|
|
||||||
if (**input && node->next[c]) {
|
if (*input && node->next[c]) {
|
||||||
(*input)++; /* Consume that char */
|
input++; /* Consume that char */
|
||||||
rewindDistance++;
|
rewindDistance++;
|
||||||
|
|
||||||
node = &charmap->nodes[node->next[c]];
|
node = ¤tCharmap->nodes[node->next[c]];
|
||||||
if (node->isTerminal) {
|
if (node->isTerminal) {
|
||||||
match = node;
|
match = node;
|
||||||
rewindDistance = 0; /* Rewind from after the match */
|
rewindDistance = 0; /* Rewind from after the match */
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
*input -= rewindDistance; /* Rewind */
|
input -= rewindDistance; /* Rewind */
|
||||||
rewindDistance = 0;
|
rewindDistance = 0;
|
||||||
node = &charmap->nodes[0];
|
node = ¤tCharmap->nodes[0];
|
||||||
|
|
||||||
if (match) { /* Arrived at a dead end with a match found */
|
if (match) { /* Arrived at a dead end with a match found */
|
||||||
if (output)
|
*output++ = match->value;
|
||||||
*(*output)++ = match->value;
|
outputLen++;
|
||||||
|
match = NULL; /* Reset match for next round */
|
||||||
|
|
||||||
return 1;
|
} else if (*input) { /* No match found */
|
||||||
|
size_t codepointLen = readUTF8Char(output, input);
|
||||||
|
|
||||||
} else if (**input) { /* No match found */
|
if (codepointLen == 0) {
|
||||||
size_t codepointLen = readUTF8Char(output ? *output : NULL,
|
|
||||||
*input);
|
|
||||||
|
|
||||||
if (codepointLen == 0)
|
|
||||||
error("Input string is not valid UTF-8!\n");
|
error("Input string is not valid UTF-8!\n");
|
||||||
|
break;
|
||||||
/* OK because UTF-8 has no NUL in multi-byte chars */
|
}
|
||||||
*input += codepointLen;
|
input += codepointLen; /* OK because UTF-8 has no NUL in multi-byte chars */
|
||||||
if (output)
|
output += codepointLen;
|
||||||
*output += codepointLen;
|
outputLen += codepointLen;
|
||||||
|
|
||||||
return codepointLen;
|
|
||||||
|
|
||||||
} else { /* End of input */
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!*input)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unreachable_();
|
return outputLen;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
@@ -149,24 +148,20 @@ void fmt_PrintString(char *buf, size_t bufLen, struct FormatSpec const *fmt, cha
|
|||||||
size_t len = strlen(value);
|
size_t len = strlen(value);
|
||||||
size_t totalLen = fmt->width > len ? fmt->width : len;
|
size_t totalLen = fmt->width > len ? fmt->width : len;
|
||||||
|
|
||||||
if (totalLen > bufLen - 1) { /* bufLen includes terminator */
|
if (totalLen + 1 > bufLen) /* bufLen includes terminator */
|
||||||
error("Formatted string value too long\n");
|
error("Formatted string value too long\n");
|
||||||
totalLen = bufLen - 1;
|
|
||||||
if (len > totalLen)
|
|
||||||
len = totalLen;
|
|
||||||
}
|
|
||||||
assert(len < bufLen && totalLen < bufLen && len <= totalLen);
|
|
||||||
|
|
||||||
size_t padLen = totalLen - len;
|
size_t padLen = fmt->width > len ? fmt->width - len : 0;
|
||||||
|
|
||||||
if (fmt->alignLeft) {
|
if (fmt->alignLeft) {
|
||||||
memcpy(buf, value, len);
|
strncpy(buf, value, len < bufLen ? len : bufLen);
|
||||||
for (size_t i = len; i < totalLen; i++)
|
for (size_t i = 0; i < totalLen && len + i < bufLen; i++)
|
||||||
buf[i] = ' ';
|
buf[len + i] = ' ';
|
||||||
} else {
|
} else {
|
||||||
for (size_t i = 0; i < padLen; i++)
|
for (size_t i = 0; i < padLen && i < bufLen; i++)
|
||||||
buf[i] = ' ';
|
buf[i] = ' ';
|
||||||
memcpy(buf + padLen, value, len);
|
if (bufLen > padLen)
|
||||||
|
strncpy(buf + padLen, value, bufLen - padLen - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
buf[totalLen] = '\0';
|
buf[totalLen] = '\0';
|
||||||
@@ -226,18 +221,12 @@ void fmt_PrintNumber(char *buf, size_t bufLen, struct FormatSpec const *fmt, uin
|
|||||||
/* Special case for fixed-point */
|
/* Special case for fixed-point */
|
||||||
|
|
||||||
/* Default fractional width (C's is 6 for "%f"; here 5 is enough) */
|
/* Default fractional width (C's is 6 for "%f"; here 5 is enough) */
|
||||||
size_t fracWidth = fmt->hasFrac ? fmt->fracWidth : 5;
|
uint8_t fracWidth = fmt->hasFrac ? fmt->fracWidth : 5;
|
||||||
|
|
||||||
if (fracWidth) {
|
if (fracWidth) {
|
||||||
if (fracWidth > 255) {
|
|
||||||
error("Fractional width %zu too long, limiting to 255\n",
|
|
||||||
fracWidth);
|
|
||||||
fracWidth = 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
char spec[16]; /* Max "%" + 5-char PRIu32 + ".%0255.f" + terminator */
|
char spec[16]; /* Max "%" + 5-char PRIu32 + ".%0255.f" + terminator */
|
||||||
|
|
||||||
snprintf(spec, sizeof(spec), "%%" PRIu32 ".%%0%zu.f", fracWidth);
|
snprintf(spec, sizeof(spec), "%%" PRIu32 ".%%0%d.f", fracWidth);
|
||||||
snprintf(valueBuf, sizeof(valueBuf), spec, value >> 16,
|
snprintf(valueBuf, sizeof(valueBuf), spec, value >> 16,
|
||||||
(value % 65536) / 65536.0 * pow(10, fracWidth) + 0.5);
|
(value % 65536) / 65536.0 * pow(10, fracWidth) + 0.5);
|
||||||
} else {
|
} else {
|
||||||
@@ -255,49 +244,55 @@ void fmt_PrintNumber(char *buf, size_t bufLen, struct FormatSpec const *fmt, uin
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t len = strlen(valueBuf);
|
size_t len = strlen(valueBuf);
|
||||||
size_t numLen = !!sign + !!prefix + len;
|
size_t numLen = len;
|
||||||
|
|
||||||
|
if (sign)
|
||||||
|
numLen++;
|
||||||
|
if (prefix)
|
||||||
|
numLen++;
|
||||||
|
|
||||||
size_t totalLen = fmt->width > numLen ? fmt->width : numLen;
|
size_t totalLen = fmt->width > numLen ? fmt->width : numLen;
|
||||||
|
|
||||||
if (totalLen > bufLen - 1) { /* bufLen includes terminator */
|
if (totalLen + 1 > bufLen) /* bufLen includes terminator */
|
||||||
error("Formatted numeric value too long\n");
|
error("Formatted numeric value too long\n");
|
||||||
totalLen = bufLen - 1;
|
|
||||||
if (numLen > totalLen) {
|
|
||||||
len -= numLen - totalLen;
|
|
||||||
numLen = totalLen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(numLen < bufLen && totalLen < bufLen && numLen <= totalLen && len <= numLen);
|
|
||||||
|
|
||||||
size_t padLen = totalLen - numLen;
|
size_t padLen = fmt->width > numLen ? fmt->width - numLen : 0;
|
||||||
size_t pos = 0;
|
|
||||||
|
|
||||||
if (fmt->alignLeft) {
|
if (fmt->alignLeft) {
|
||||||
if (sign)
|
size_t pos = 0;
|
||||||
|
|
||||||
|
if (sign && pos < bufLen)
|
||||||
buf[pos++] = sign;
|
buf[pos++] = sign;
|
||||||
if (prefix)
|
if (prefix && pos < bufLen)
|
||||||
buf[pos++] = prefix;
|
buf[pos++] = prefix;
|
||||||
memcpy(buf + pos, valueBuf, len);
|
|
||||||
for (size_t i = pos + len; i < totalLen; i++)
|
strcpy(buf + pos, valueBuf);
|
||||||
buf[i] = ' ';
|
pos += len;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < totalLen && pos + i < bufLen; i++)
|
||||||
|
buf[pos + i] = ' ';
|
||||||
} else {
|
} else {
|
||||||
|
size_t pos = 0;
|
||||||
|
|
||||||
if (fmt->padZero) {
|
if (fmt->padZero) {
|
||||||
/* sign, then prefix, then zero padding */
|
/* sign, then prefix, then zero padding */
|
||||||
if (sign)
|
if (sign && pos < bufLen)
|
||||||
buf[pos++] = sign;
|
buf[pos++] = sign;
|
||||||
if (prefix)
|
if (prefix && pos < bufLen)
|
||||||
buf[pos++] = prefix;
|
buf[pos++] = prefix;
|
||||||
for (size_t i = 0; i < padLen; i++)
|
for (size_t i = 0; i < padLen && pos < bufLen; i++)
|
||||||
buf[pos++] = '0';
|
buf[pos++] = '0';
|
||||||
} else {
|
} else {
|
||||||
/* space padding, then sign, then prefix */
|
/* space padding, then sign, then prefix */
|
||||||
for (size_t i = 0; i < padLen; i++)
|
for (size_t i = 0; i < padLen && pos < bufLen; i++)
|
||||||
buf[pos++] = ' ';
|
buf[pos++] = ' ';
|
||||||
if (sign)
|
if (sign && pos < bufLen)
|
||||||
buf[pos++] = sign;
|
buf[pos++] = sign;
|
||||||
if (prefix)
|
if (prefix && pos < bufLen)
|
||||||
buf[pos++] = prefix;
|
buf[pos++] = prefix;
|
||||||
}
|
}
|
||||||
memcpy(buf + pos, valueBuf, len);
|
if (bufLen > pos)
|
||||||
|
strcpy(buf + pos, valueBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
buf[totalLen] = '\0';
|
buf[totalLen] = '\0';
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ struct Context {
|
|||||||
static struct Context *contextStack;
|
static struct Context *contextStack;
|
||||||
static size_t contextDepth = 0;
|
static size_t contextDepth = 0;
|
||||||
#define DEFAULT_MAX_DEPTH 64
|
#define DEFAULT_MAX_DEPTH 64
|
||||||
size_t maxRecursionDepth;
|
size_t nMaxRecursionDepth;
|
||||||
|
|
||||||
static unsigned int nbIncPaths = 0;
|
static unsigned int nbIncPaths = 0;
|
||||||
static char const *includePaths[MAXINCPATHS];
|
static char const *includePaths[MAXINCPATHS];
|
||||||
@@ -143,8 +143,8 @@ void fstk_AddIncludePath(char const *path)
|
|||||||
static void printDep(char const *path)
|
static void printDep(char const *path)
|
||||||
{
|
{
|
||||||
if (dependfile) {
|
if (dependfile) {
|
||||||
fprintf(dependfile, "%s: %s\n", targetFileName, path);
|
fprintf(dependfile, "%s: %s\n", tzTargetFileName, path);
|
||||||
if (generatePhonyDeps)
|
if (oGeneratePhonyDeps)
|
||||||
fprintf(dependfile, "%s:\n", path);
|
fprintf(dependfile, "%s:\n", path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -191,11 +191,6 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
len = sprintf(*fullPath, "%s%s", incPath, path);
|
len = sprintf(*fullPath, "%s%s", incPath, path);
|
||||||
if (len < 0) {
|
|
||||||
error("sprintf error during include path search: %s\n",
|
|
||||||
strerror(errno));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPathValid(*fullPath)) {
|
if (isPathValid(*fullPath)) {
|
||||||
@@ -206,18 +201,18 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
errno = ENOENT;
|
errno = ENOENT;
|
||||||
if (generatedMissingIncludes)
|
if (oGeneratedMissingIncludes)
|
||||||
printDep(path);
|
printDep(path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool yywrap(void)
|
bool yywrap(void)
|
||||||
{
|
{
|
||||||
uint32_t ifDepth = lexer_GetIFDepth();
|
uint32_t nIFDepth = lexer_GetIFDepth();
|
||||||
|
|
||||||
if (ifDepth != 0)
|
if (nIFDepth != 0)
|
||||||
fatalerror("Ended block with %" PRIu32 " unterminated IF construct%s\n",
|
fatalerror("Ended block with %" PRIu32 " unterminated IF construct%s\n",
|
||||||
ifDepth, ifDepth == 1 ? "" : "s");
|
nIFDepth, nIFDepth == 1 ? "" : "s");
|
||||||
|
|
||||||
if (contextStack->fileInfo->type == NODE_REPT) { /* The context is a REPT block, which may loop */
|
if (contextStack->fileInfo->type == NODE_REPT) { /* The context is a REPT block, which may loop */
|
||||||
struct FileStackReptNode *fileInfo = (struct FileStackReptNode *)contextStack->fileInfo;
|
struct FileStackReptNode *fileInfo = (struct FileStackReptNode *)contextStack->fileInfo;
|
||||||
@@ -293,8 +288,8 @@ bool yywrap(void)
|
|||||||
*/
|
*/
|
||||||
static void newContext(struct FileStackNode *fileInfo)
|
static void newContext(struct FileStackNode *fileInfo)
|
||||||
{
|
{
|
||||||
if (++contextDepth >= maxRecursionDepth)
|
if (++contextDepth >= nMaxRecursionDepth)
|
||||||
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
|
fatalerror("Recursion limit (%zu) exceeded\n", nMaxRecursionDepth);
|
||||||
struct Context *context = malloc(sizeof(*context));
|
struct Context *context = malloc(sizeof(*context));
|
||||||
|
|
||||||
if (!context)
|
if (!context)
|
||||||
@@ -322,11 +317,11 @@ void fstk_RunInclude(char const *path)
|
|||||||
|
|
||||||
if (!fstk_FindFile(path, &fullPath, &size)) {
|
if (!fstk_FindFile(path, &fullPath, &size)) {
|
||||||
free(fullPath);
|
free(fullPath);
|
||||||
if (generatedMissingIncludes) {
|
if (oGeneratedMissingIncludes) {
|
||||||
if (verbose)
|
if (verbose)
|
||||||
printf("Aborting (-MG) on INCLUDE file '%s' (%s)\n",
|
printf("Aborting (-MG) on INCLUDE file '%s' (%s)\n",
|
||||||
path, strerror(errno));
|
path, strerror(errno));
|
||||||
failedOnMissingInclude = true;
|
oFailedOnMissingInclude = true;
|
||||||
} else {
|
} else {
|
||||||
error("Unable to open included file '%s': %s\n", path, strerror(errno));
|
error("Unable to open included file '%s': %s\n", path, strerror(errno));
|
||||||
}
|
}
|
||||||
@@ -417,7 +412,7 @@ void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
|
|||||||
memcpy(dest, macro->name, macroNameLen + 1);
|
memcpy(dest, macro->name, macroNameLen + 1);
|
||||||
|
|
||||||
newContext((struct FileStackNode *)fileInfo);
|
newContext((struct FileStackNode *)fileInfo);
|
||||||
contextStack->lexerState = lexer_OpenFileView("MACRO", macro->macro, macro->macroSize,
|
contextStack->lexerState = lexer_OpenFileView(macro->macro, macro->macroSize,
|
||||||
macro->fileLine);
|
macro->fileLine);
|
||||||
if (!contextStack->lexerState)
|
if (!contextStack->lexerState)
|
||||||
fatalerror("Failed to set up lexer for macro invocation\n");
|
fatalerror("Failed to set up lexer for macro invocation\n");
|
||||||
@@ -451,7 +446,7 @@ static bool newReptContext(int32_t reptLineNo, char *body, size_t size)
|
|||||||
/* Correct our line number, which currently points to the `ENDR` line */
|
/* Correct our line number, which currently points to the `ENDR` line */
|
||||||
contextStack->fileInfo->lineNo = reptLineNo;
|
contextStack->fileInfo->lineNo = reptLineNo;
|
||||||
|
|
||||||
contextStack->lexerState = lexer_OpenFileView("REPT", body, size, reptLineNo);
|
contextStack->lexerState = lexer_OpenFileView(body, size, reptLineNo);
|
||||||
if (!contextStack->lexerState)
|
if (!contextStack->lexerState)
|
||||||
fatalerror("Failed to set up lexer for REPT block\n");
|
fatalerror("Failed to set up lexer for REPT block\n");
|
||||||
lexer_SetStateAtEOL(contextStack->lexerState);
|
lexer_SetStateAtEOL(contextStack->lexerState);
|
||||||
@@ -492,10 +487,6 @@ void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step,
|
|||||||
else if (step == 0)
|
else if (step == 0)
|
||||||
error("FOR cannot have a step value of 0\n");
|
error("FOR cannot have a step value of 0\n");
|
||||||
|
|
||||||
if ((step > 0 && start > stop) || (step < 0 && start < stop))
|
|
||||||
warning(WARNING_BACKWARDS_FOR, "FOR goes backwards from %d to %d by %d\n",
|
|
||||||
start, stop, step);
|
|
||||||
|
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
return;
|
return;
|
||||||
if (!newReptContext(reptLineNo, body, size))
|
if (!newReptContext(reptLineNo, body, size))
|
||||||
@@ -528,7 +519,7 @@ bool fstk_Break(void)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_Init(char const *mainPath, size_t maxDepth)
|
void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
|
||||||
{
|
{
|
||||||
struct LexerState *state = lexer_OpenFile(mainPath);
|
struct LexerState *state = lexer_OpenFile(mainPath);
|
||||||
|
|
||||||
@@ -570,12 +561,12 @@ void fstk_Init(char const *mainPath, size_t maxDepth)
|
|||||||
* This assumes that the rept node is larger
|
* This assumes that the rept node is larger
|
||||||
*/
|
*/
|
||||||
#define DEPTH_LIMIT ((SIZE_MAX - sizeof(struct FileStackReptNode)) / sizeof(uint32_t))
|
#define DEPTH_LIMIT ((SIZE_MAX - sizeof(struct FileStackReptNode)) / sizeof(uint32_t))
|
||||||
if (maxDepth > DEPTH_LIMIT) {
|
if (maxRecursionDepth > DEPTH_LIMIT) {
|
||||||
error("Recursion depth may not be higher than %zu, defaulting to "
|
error("Recursion depth may not be higher than %zu, defaulting to "
|
||||||
EXPAND_AND_STR(DEFAULT_MAX_DEPTH) "\n", DEPTH_LIMIT);
|
EXPAND_AND_STR(DEFAULT_MAX_DEPTH) "\n", DEPTH_LIMIT);
|
||||||
maxRecursionDepth = DEFAULT_MAX_DEPTH;
|
nMaxRecursionDepth = DEFAULT_MAX_DEPTH;
|
||||||
} else {
|
} else {
|
||||||
maxRecursionDepth = maxDepth;
|
nMaxRecursionDepth = maxRecursionDepth;
|
||||||
}
|
}
|
||||||
/* Make sure that the default of 64 is OK, though */
|
/* Make sure that the default of 64 is OK, though */
|
||||||
assert(DEPTH_LIMIT >= DEFAULT_MAX_DEPTH);
|
assert(DEPTH_LIMIT >= DEFAULT_MAX_DEPTH);
|
||||||
|
|||||||
1205
src/asm/lexer.c
1205
src/asm/lexer.c
File diff suppressed because it is too large
Load Diff
@@ -36,18 +36,6 @@
|
|||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
#ifdef __clang__
|
|
||||||
#if __has_feature(address_sanitizer) && !defined(__SANITIZE_ADDRESS__)
|
|
||||||
#define __SANITIZE_ADDRESS__
|
|
||||||
#endif /* __has_feature(address_sanitizer) && !defined(__SANITIZE_ADDRESS__) */
|
|
||||||
#endif /* __clang__ */
|
|
||||||
|
|
||||||
#ifdef __SANITIZE_ADDRESS__
|
|
||||||
// There are known, non-trivial to fix leaks. We would still like to have `make develop'
|
|
||||||
// detect memory corruption, though.
|
|
||||||
const char *__asan_default_options(void) { return "detect_leaks=0"; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Old Bison versions (confirmed for 2.3) do not forward-declare `yyparse` in the generated header
|
// Old Bison versions (confirmed for 2.3) do not forward-declare `yyparse` in the generated header
|
||||||
// Unfortunately, macOS still ships 2.3, which is from 2008...
|
// Unfortunately, macOS still ships 2.3, which is from 2008...
|
||||||
int yyparse(void);
|
int yyparse(void);
|
||||||
@@ -57,13 +45,13 @@ extern int yydebug;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
FILE * dependfile;
|
FILE * dependfile;
|
||||||
bool generatedMissingIncludes;
|
bool oGeneratedMissingIncludes;
|
||||||
bool failedOnMissingInclude;
|
bool oFailedOnMissingInclude;
|
||||||
bool generatePhonyDeps;
|
bool oGeneratePhonyDeps;
|
||||||
char *targetFileName;
|
char *tzTargetFileName;
|
||||||
|
|
||||||
bool haltnop;
|
bool haltnop;
|
||||||
bool optimizeLoads;
|
bool optimizeloads;
|
||||||
bool verbose;
|
bool verbose;
|
||||||
bool warnings; /* True to enable warnings, false to disable them. */
|
bool warnings; /* True to enable warnings, false to disable them. */
|
||||||
|
|
||||||
@@ -170,21 +158,21 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
// Set defaults
|
// Set defaults
|
||||||
|
|
||||||
generatePhonyDeps = false;
|
oGeneratePhonyDeps = false;
|
||||||
generatedMissingIncludes = false;
|
oGeneratedMissingIncludes = false;
|
||||||
failedOnMissingInclude = false;
|
oFailedOnMissingInclude = false;
|
||||||
targetFileName = NULL;
|
tzTargetFileName = NULL;
|
||||||
|
|
||||||
opt_B("01");
|
opt_B("01");
|
||||||
opt_G("0123");
|
opt_G("0123");
|
||||||
opt_P(0);
|
opt_P(0);
|
||||||
optimizeLoads = true;
|
optimizeloads = true;
|
||||||
haltnop = true;
|
haltnop = true;
|
||||||
verbose = false;
|
verbose = false;
|
||||||
warnings = true;
|
warnings = true;
|
||||||
sym_SetExportAll(false);
|
sym_SetExportAll(false);
|
||||||
uint32_t maxDepth = 64;
|
uint32_t maxRecursionDepth = 64;
|
||||||
size_t targetFileNameLen = 0;
|
size_t nTargetFileNameLen = 0;
|
||||||
|
|
||||||
while ((ch = musl_getopt_long_only(argc, argv, optstring, longopts, NULL)) != -1) {
|
while ((ch = musl_getopt_long_only(argc, argv, optstring, longopts, NULL)) != -1) {
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
@@ -226,7 +214,7 @@ int main(int argc, char *argv[])
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'L':
|
case 'L':
|
||||||
optimizeLoads = false;
|
optimizeloads = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'M':
|
case 'M':
|
||||||
@@ -256,7 +244,7 @@ int main(int argc, char *argv[])
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'r':
|
case 'r':
|
||||||
maxDepth = strtoul(musl_optarg, &ep, 0);
|
maxRecursionDepth = strtoul(musl_optarg, &ep, 0);
|
||||||
|
|
||||||
if (musl_optarg[0] == '\0' || *ep != '\0')
|
if (musl_optarg[0] == '\0' || *ep != '\0')
|
||||||
errx(1, "Invalid argument for option 'r'");
|
errx(1, "Invalid argument for option 'r'");
|
||||||
@@ -281,11 +269,11 @@ int main(int argc, char *argv[])
|
|||||||
case 0:
|
case 0:
|
||||||
switch (depType) {
|
switch (depType) {
|
||||||
case 'G':
|
case 'G':
|
||||||
generatedMissingIncludes = true;
|
oGeneratedMissingIncludes = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'P':
|
case 'P':
|
||||||
generatePhonyDeps = true;
|
oGeneratePhonyDeps = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'Q':
|
case 'Q':
|
||||||
@@ -296,22 +284,22 @@ int main(int argc, char *argv[])
|
|||||||
if (depType == 'Q')
|
if (depType == 'Q')
|
||||||
ep = make_escape(ep);
|
ep = make_escape(ep);
|
||||||
|
|
||||||
targetFileNameLen += strlen(ep) + 1;
|
nTargetFileNameLen += strlen(ep) + 1;
|
||||||
if (!targetFileName) {
|
if (!tzTargetFileName) {
|
||||||
/* On first alloc, make an empty str */
|
/* On first alloc, make an empty str */
|
||||||
targetFileName = malloc(targetFileNameLen + 1);
|
tzTargetFileName = malloc(nTargetFileNameLen + 1);
|
||||||
if (targetFileName)
|
if (tzTargetFileName)
|
||||||
*targetFileName = '\0';
|
*tzTargetFileName = '\0';
|
||||||
} else {
|
} else {
|
||||||
targetFileName = realloc(targetFileName,
|
tzTargetFileName = realloc(tzTargetFileName,
|
||||||
targetFileNameLen + 1);
|
nTargetFileNameLen + 1);
|
||||||
}
|
}
|
||||||
if (targetFileName == NULL)
|
if (tzTargetFileName == NULL)
|
||||||
err(1, "Cannot append new file to target file list");
|
err(1, "Cannot append new file to target file list");
|
||||||
strcat(targetFileName, ep);
|
strcat(tzTargetFileName, ep);
|
||||||
if (depType == 'Q')
|
if (depType == 'Q')
|
||||||
free(ep);
|
free(ep);
|
||||||
char *ptr = targetFileName + strlen(targetFileName);
|
char *ptr = tzTargetFileName + strlen(tzTargetFileName);
|
||||||
|
|
||||||
*ptr++ = ' ';
|
*ptr++ = ' ';
|
||||||
*ptr = '\0';
|
*ptr = '\0';
|
||||||
@@ -326,8 +314,8 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetFileName == NULL)
|
if (tzTargetFileName == NULL)
|
||||||
targetFileName = objectName;
|
tzTargetFileName = tzObjectname;
|
||||||
|
|
||||||
if (argc == musl_optind) {
|
if (argc == musl_optind) {
|
||||||
fputs("FATAL: No input files\n", stderr);
|
fputs("FATAL: No input files\n", stderr);
|
||||||
@@ -343,17 +331,17 @@ int main(int argc, char *argv[])
|
|||||||
printf("Assembling %s\n", mainFileName);
|
printf("Assembling %s\n", mainFileName);
|
||||||
|
|
||||||
if (dependfile) {
|
if (dependfile) {
|
||||||
if (!targetFileName)
|
if (!tzTargetFileName)
|
||||||
errx(1, "Dependency files can only be created if a target file is specified with either -o, -MQ or -MT\n");
|
errx(1, "Dependency files can only be created if a target file is specified with either -o, -MQ or -MT\n");
|
||||||
|
|
||||||
fprintf(dependfile, "%s: %s\n", targetFileName, mainFileName);
|
fprintf(dependfile, "%s: %s\n", tzTargetFileName, mainFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
charmap_New("main", NULL);
|
charmap_New("main", NULL);
|
||||||
|
|
||||||
// Init lexer and file stack, prodiving file info
|
// Init lexer and file stack, prodiving file info
|
||||||
lexer_Init();
|
lexer_Init();
|
||||||
fstk_Init(mainFileName, maxDepth);
|
fstk_Init(mainFileName, maxRecursionDepth);
|
||||||
|
|
||||||
// Perform parse (yyparse is auto-generated from `parser.y`)
|
// Perform parse (yyparse is auto-generated from `parser.y`)
|
||||||
if (yyparse() != 0 && nbErrors == 0)
|
if (yyparse() != 0 && nbErrors == 0)
|
||||||
@@ -369,11 +357,11 @@ int main(int argc, char *argv[])
|
|||||||
nbErrors == 1 ? "" : "s");
|
nbErrors == 1 ? "" : "s");
|
||||||
|
|
||||||
// If parse aborted due to missing an include, and `-MG` was given, exit normally
|
// If parse aborted due to missing an include, and `-MG` was given, exit normally
|
||||||
if (failedOnMissingInclude)
|
if (oFailedOnMissingInclude)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* If no path specified, don't write file */
|
/* If no path specified, don't write file */
|
||||||
if (objectName != NULL)
|
if (tzObjectname != NULL)
|
||||||
out_WriteObject();
|
out_WriteObject();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -6,7 +7,6 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "asm/lexer.h"
|
#include "asm/lexer.h"
|
||||||
#include "asm/main.h"
|
|
||||||
#include "asm/section.h"
|
#include "asm/section.h"
|
||||||
#include "asm/warning.h"
|
#include "asm/warning.h"
|
||||||
|
|
||||||
@@ -14,10 +14,6 @@ struct OptStackEntry {
|
|||||||
char binary[2];
|
char binary[2];
|
||||||
char gbgfx[4];
|
char gbgfx[4];
|
||||||
int32_t fillByte;
|
int32_t fillByte;
|
||||||
bool haltnop;
|
|
||||||
bool optimizeLoads;
|
|
||||||
bool warningsAreErrors;
|
|
||||||
enum WarningState warningStates[NB_WARNINGS];
|
|
||||||
struct OptStackEntry *next;
|
struct OptStackEntry *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -38,21 +34,6 @@ void opt_P(uint8_t fill)
|
|||||||
fillByte = fill;
|
fillByte = fill;
|
||||||
}
|
}
|
||||||
|
|
||||||
void opt_h(bool halt)
|
|
||||||
{
|
|
||||||
haltnop = halt;
|
|
||||||
}
|
|
||||||
|
|
||||||
void opt_L(bool optimize)
|
|
||||||
{
|
|
||||||
optimizeLoads = optimize;
|
|
||||||
}
|
|
||||||
|
|
||||||
void opt_W(char const *flag)
|
|
||||||
{
|
|
||||||
processWarningFlag(flag);
|
|
||||||
}
|
|
||||||
|
|
||||||
void opt_Parse(char *s)
|
void opt_Parse(char *s)
|
||||||
{
|
{
|
||||||
switch (s[0]) {
|
switch (s[0]) {
|
||||||
@@ -85,49 +66,6 @@ void opt_Parse(char *s)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'h':
|
|
||||||
if (s[1] == '\0')
|
|
||||||
opt_h(false);
|
|
||||||
else
|
|
||||||
error("Option 'h' does not take an argument\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'L':
|
|
||||||
if (s[1] == '\0')
|
|
||||||
opt_L(false);
|
|
||||||
else
|
|
||||||
error("Option 'L' does not take an argument\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'W':
|
|
||||||
if (strlen(&s[1]) > 0)
|
|
||||||
opt_W(&s[1]);
|
|
||||||
else
|
|
||||||
error("Must specify an argument for option 'W'\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '!': // negates flag options that do not take an argument
|
|
||||||
switch (s[1]) {
|
|
||||||
case 'h':
|
|
||||||
if (s[2] == '\0')
|
|
||||||
opt_h(true);
|
|
||||||
else
|
|
||||||
error("Option '!h' does not take an argument\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'L':
|
|
||||||
if (s[2] == '\0')
|
|
||||||
opt_L(true);
|
|
||||||
else
|
|
||||||
error("Option '!L' does not take an argument\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
error("Unknown option '!%c'\n", s[1]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
error("Unknown option '%c'\n", s[0]);
|
error("Unknown option '%c'\n", s[0]);
|
||||||
break;
|
break;
|
||||||
@@ -152,14 +90,6 @@ void opt_Push(void)
|
|||||||
|
|
||||||
entry->fillByte = fillByte; // Pulled from section.h
|
entry->fillByte = fillByte; // Pulled from section.h
|
||||||
|
|
||||||
entry->haltnop = haltnop; // Pulled from main.h
|
|
||||||
|
|
||||||
entry->optimizeLoads = optimizeLoads; // Pulled from main.h
|
|
||||||
|
|
||||||
// Both of these pulled from warning.h
|
|
||||||
entry->warningsAreErrors = warningsAreErrors;
|
|
||||||
memcpy(entry->warningStates, warningStates, sizeof(warningStates));
|
|
||||||
|
|
||||||
entry->next = stack;
|
entry->next = stack;
|
||||||
stack = entry;
|
stack = entry;
|
||||||
}
|
}
|
||||||
@@ -176,13 +106,6 @@ void opt_Pop(void)
|
|||||||
opt_B(entry->binary);
|
opt_B(entry->binary);
|
||||||
opt_G(entry->gbgfx);
|
opt_G(entry->gbgfx);
|
||||||
opt_P(entry->fillByte);
|
opt_P(entry->fillByte);
|
||||||
opt_h(entry->haltnop);
|
|
||||||
opt_L(entry->optimizeLoads);
|
|
||||||
|
|
||||||
// opt_W does not apply a whole warning state; it processes one flag string
|
|
||||||
warningsAreErrors = entry->warningsAreErrors;
|
|
||||||
memcpy(warningStates, entry->warningStates, sizeof(warningStates));
|
|
||||||
|
|
||||||
stack = entry->next;
|
stack = entry->next;
|
||||||
free(entry);
|
free(entry);
|
||||||
}
|
}
|
||||||
|
|||||||
131
src/asm/output.c
131
src/asm/output.c
@@ -35,12 +35,12 @@
|
|||||||
struct Patch {
|
struct Patch {
|
||||||
struct FileStackNode const *src;
|
struct FileStackNode const *src;
|
||||||
uint32_t lineNo;
|
uint32_t lineNo;
|
||||||
uint32_t offset;
|
uint32_t nOffset;
|
||||||
struct Section *pcSection;
|
struct Section *pcSection;
|
||||||
uint32_t pcOffset;
|
uint32_t pcOffset;
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
uint32_t rpnSize;
|
uint32_t nRPNSize;
|
||||||
uint8_t *rpn;
|
uint8_t *pRPN;
|
||||||
struct Patch *next;
|
struct Patch *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -51,9 +51,10 @@ struct Assertion {
|
|||||||
struct Assertion *next;
|
struct Assertion *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
char *objectName;
|
char *tzObjectname;
|
||||||
|
|
||||||
struct Section *sectionList;
|
/* TODO: shouldn't `pCurrentSection` be somewhere else? */
|
||||||
|
struct Section *pSectionList, *pCurrentSection;
|
||||||
|
|
||||||
/* Linked list of symbols to put in the object file */
|
/* Linked list of symbols to put in the object file */
|
||||||
static struct Symbol *objectSymbols = NULL;
|
static struct Symbol *objectSymbols = NULL;
|
||||||
@@ -71,7 +72,7 @@ static uint32_t countSections(void)
|
|||||||
{
|
{
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
|
|
||||||
for (struct Section const *sect = sectionList; sect; sect = sect->next)
|
for (struct Section const *sect = pSectionList; sect; sect = sect->next)
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
@@ -178,7 +179,7 @@ This is code intended to replace a node, which is pretty useless until ref count
|
|||||||
*/
|
*/
|
||||||
static uint32_t getsectid(struct Section const *sect)
|
static uint32_t getsectid(struct Section const *sect)
|
||||||
{
|
{
|
||||||
struct Section const *sec = sectionList;
|
struct Section const *sec = pSectionList;
|
||||||
uint32_t ID = 0;
|
uint32_t ID = 0;
|
||||||
|
|
||||||
while (sec) {
|
while (sec) {
|
||||||
@@ -204,12 +205,12 @@ static void writepatch(struct Patch const *patch, FILE *f)
|
|||||||
assert(patch->src->ID != -1);
|
assert(patch->src->ID != -1);
|
||||||
putlong(patch->src->ID, f);
|
putlong(patch->src->ID, f);
|
||||||
putlong(patch->lineNo, f);
|
putlong(patch->lineNo, f);
|
||||||
putlong(patch->offset, f);
|
putlong(patch->nOffset, f);
|
||||||
putlong(getSectIDIfAny(patch->pcSection), f);
|
putlong(getSectIDIfAny(patch->pcSection), f);
|
||||||
putlong(patch->pcOffset, f);
|
putlong(patch->pcOffset, f);
|
||||||
putc(patch->type, f);
|
putc(patch->type, f);
|
||||||
putlong(patch->rpnSize, f);
|
putlong(patch->nRPNSize, f);
|
||||||
fwrite(patch->rpn, 1, patch->rpnSize, f);
|
fwrite(patch->pRPN, 1, patch->nRPNSize, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -241,21 +242,6 @@ static void writesection(struct Section const *sect, FILE *f)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void freesection(struct Section const *sect)
|
|
||||||
{
|
|
||||||
if (sect_HasData(sect->type)) {
|
|
||||||
struct Patch *patch = sect->patches;
|
|
||||||
|
|
||||||
while (patch != NULL) {
|
|
||||||
struct Patch *next = patch->next;
|
|
||||||
|
|
||||||
free(patch->rpn);
|
|
||||||
free(patch);
|
|
||||||
patch = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write a symbol to a file
|
* Write a symbol to a file
|
||||||
*/
|
*/
|
||||||
@@ -300,7 +286,7 @@ static uint32_t getSymbolID(struct Symbol *sym)
|
|||||||
static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
||||||
uint32_t rpnlen)
|
uint32_t rpnlen)
|
||||||
{
|
{
|
||||||
char symName[512];
|
char tzSym[512];
|
||||||
|
|
||||||
for (size_t offset = 0; offset < rpnlen; ) {
|
for (size_t offset = 0; offset < rpnlen; ) {
|
||||||
#define popbyte() rpn[offset++]
|
#define popbyte() rpn[offset++]
|
||||||
@@ -324,14 +310,14 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
|||||||
case RPN_SYM:
|
case RPN_SYM:
|
||||||
i = 0;
|
i = 0;
|
||||||
do {
|
do {
|
||||||
symName[i] = popbyte();
|
tzSym[i] = popbyte();
|
||||||
} while (symName[i++]);
|
} while (tzSym[i++]);
|
||||||
|
|
||||||
// The symbol name is always written expanded
|
// The symbol name is always written expanded
|
||||||
sym = sym_FindExactSymbol(symName);
|
sym = sym_FindExactSymbol(tzSym);
|
||||||
if (sym_IsConstant(sym)) {
|
if (sym_IsConstant(sym)) {
|
||||||
writebyte(RPN_CONST);
|
writebyte(RPN_CONST);
|
||||||
value = sym_GetConstantValue(symName);
|
value = sym_GetConstantValue(tzSym);
|
||||||
} else {
|
} else {
|
||||||
writebyte(RPN_SYM);
|
writebyte(RPN_SYM);
|
||||||
value = getSymbolID(sym);
|
value = getSymbolID(sym);
|
||||||
@@ -346,11 +332,11 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
|||||||
case RPN_BANK_SYM:
|
case RPN_BANK_SYM:
|
||||||
i = 0;
|
i = 0;
|
||||||
do {
|
do {
|
||||||
symName[i] = popbyte();
|
tzSym[i] = popbyte();
|
||||||
} while (symName[i++]);
|
} while (tzSym[i++]);
|
||||||
|
|
||||||
// The symbol name is always written expanded
|
// The symbol name is always written expanded
|
||||||
sym = sym_FindExactSymbol(symName);
|
sym = sym_FindExactSymbol(tzSym);
|
||||||
value = getSymbolID(sym);
|
value = getSymbolID(sym);
|
||||||
|
|
||||||
writebyte(RPN_BANK_SYM);
|
writebyte(RPN_BANK_SYM);
|
||||||
@@ -368,22 +354,6 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
|||||||
} while (b != 0);
|
} while (b != 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_SIZEOF_SECT:
|
|
||||||
writebyte(RPN_SIZEOF_SECT);
|
|
||||||
do {
|
|
||||||
b = popbyte();
|
|
||||||
writebyte(b);
|
|
||||||
} while (b != 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RPN_STARTOF_SECT:
|
|
||||||
writebyte(RPN_STARTOF_SECT);
|
|
||||||
do {
|
|
||||||
b = popbyte();
|
|
||||||
writebyte(b);
|
|
||||||
} while (b != 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
writebyte(rpndata);
|
writebyte(rpndata);
|
||||||
break;
|
break;
|
||||||
@@ -400,38 +370,38 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
|||||||
static struct Patch *allocpatch(uint32_t type, struct Expression const *expr, uint32_t ofs)
|
static struct Patch *allocpatch(uint32_t type, struct Expression const *expr, uint32_t ofs)
|
||||||
{
|
{
|
||||||
struct Patch *patch = malloc(sizeof(struct Patch));
|
struct Patch *patch = malloc(sizeof(struct Patch));
|
||||||
uint32_t rpnSize = expr->isKnown ? 5 : expr->rpnPatchSize;
|
uint32_t rpnSize = expr->isKnown ? 5 : expr->nRPNPatchSize;
|
||||||
struct FileStackNode *node = fstk_GetFileStack();
|
struct FileStackNode *node = fstk_GetFileStack();
|
||||||
|
|
||||||
if (!patch)
|
if (!patch)
|
||||||
fatalerror("No memory for patch: %s\n", strerror(errno));
|
fatalerror("No memory for patch: %s\n", strerror(errno));
|
||||||
|
|
||||||
patch->rpn = malloc(sizeof(*patch->rpn) * rpnSize);
|
patch->pRPN = malloc(sizeof(*patch->pRPN) * rpnSize);
|
||||||
if (!patch->rpn)
|
if (!patch->pRPN)
|
||||||
fatalerror("No memory for patch's RPN rpnSize: %s\n", strerror(errno));
|
fatalerror("No memory for patch's RPN expression: %s\n", strerror(errno));
|
||||||
|
|
||||||
patch->type = type;
|
patch->type = type;
|
||||||
patch->src = node;
|
patch->src = node;
|
||||||
out_RegisterNode(node);
|
out_RegisterNode(node);
|
||||||
patch->lineNo = lexer_GetLineNo();
|
patch->lineNo = lexer_GetLineNo();
|
||||||
patch->offset = ofs;
|
patch->nOffset = ofs;
|
||||||
patch->pcSection = sect_GetSymbolSection();
|
patch->pcSection = sect_GetSymbolSection();
|
||||||
patch->pcOffset = sect_GetSymbolOffset();
|
patch->pcOffset = sect_GetSymbolOffset();
|
||||||
|
|
||||||
/* If the rpnSize's value is known, output a constant RPN rpnSize directly */
|
/* If the expression's value is known, output a constant RPN expression directly */
|
||||||
if (expr->isKnown) {
|
if (expr->isKnown) {
|
||||||
patch->rpnSize = rpnSize;
|
patch->nRPNSize = rpnSize;
|
||||||
/* Make sure to update `rpnSize` above if modifying this! */
|
/* Make sure to update `rpnSize` above if modifying this! */
|
||||||
patch->rpn[0] = RPN_CONST;
|
patch->pRPN[0] = RPN_CONST;
|
||||||
patch->rpn[1] = (uint32_t)(expr->val) & 0xFF;
|
patch->pRPN[1] = (uint32_t)(expr->nVal) & 0xFF;
|
||||||
patch->rpn[2] = (uint32_t)(expr->val) >> 8;
|
patch->pRPN[2] = (uint32_t)(expr->nVal) >> 8;
|
||||||
patch->rpn[3] = (uint32_t)(expr->val) >> 16;
|
patch->pRPN[3] = (uint32_t)(expr->nVal) >> 16;
|
||||||
patch->rpn[4] = (uint32_t)(expr->val) >> 24;
|
patch->pRPN[4] = (uint32_t)(expr->nVal) >> 24;
|
||||||
} else {
|
} else {
|
||||||
patch->rpnSize = 0;
|
patch->nRPNSize = 0;
|
||||||
writerpn(patch->rpn, &patch->rpnSize, expr->rpn, expr->rpnLength);
|
writerpn(patch->pRPN, &patch->nRPNSize, expr->tRPN, expr->nRPNLength);
|
||||||
}
|
}
|
||||||
assert(patch->rpnSize == rpnSize);
|
assert(patch->nRPNSize == rpnSize);
|
||||||
|
|
||||||
return patch;
|
return patch;
|
||||||
}
|
}
|
||||||
@@ -448,8 +418,8 @@ void out_CreatePatch(uint32_t type, struct Expression const *expr, uint32_t ofs,
|
|||||||
// before those bytes.
|
// before those bytes.
|
||||||
patch->pcOffset -= pcShift;
|
patch->pcOffset -= pcShift;
|
||||||
|
|
||||||
patch->next = currentSection->patches;
|
patch->next = pCurrentSection->patches;
|
||||||
currentSection->patches = patch;
|
pCurrentSection->patches = patch;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -482,13 +452,6 @@ static void writeassert(struct Assertion *assert, FILE *f)
|
|||||||
putstring(assert->message, f);
|
putstring(assert->message, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void freeassert(struct Assertion *assert)
|
|
||||||
{
|
|
||||||
free(assert->patch->rpn);
|
|
||||||
free(assert->patch);
|
|
||||||
free(assert);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void writeFileStackNode(struct FileStackNode const *node, FILE *f)
|
static void writeFileStackNode(struct FileStackNode const *node, FILE *f)
|
||||||
{
|
{
|
||||||
putlong(node->parent ? node->parent->ID : -1, f);
|
putlong(node->parent ? node->parent->ID : -1, f);
|
||||||
@@ -522,13 +485,13 @@ static void registerUnregisteredSymbol(struct Symbol *symbol, void *arg)
|
|||||||
void out_WriteObject(void)
|
void out_WriteObject(void)
|
||||||
{
|
{
|
||||||
FILE *f;
|
FILE *f;
|
||||||
if (strcmp(objectName, "-") != 0)
|
if (strcmp(tzObjectname, "-") != 0)
|
||||||
f = fopen(objectName, "wb");
|
f = fopen(tzObjectname, "wb");
|
||||||
else
|
else
|
||||||
f = fdopen(1, "wb");
|
f = fdopen(1, "wb");
|
||||||
|
|
||||||
if (!f)
|
if (!f)
|
||||||
err(1, "Couldn't write file '%s'", objectName);
|
err(1, "Couldn't write file '%s'", tzObjectname);
|
||||||
|
|
||||||
/* Also write symbols that weren't written above */
|
/* Also write symbols that weren't written above */
|
||||||
sym_ForEach(registerUnregisteredSymbol, NULL);
|
sym_ForEach(registerUnregisteredSymbol, NULL);
|
||||||
@@ -551,21 +514,13 @@ void out_WriteObject(void)
|
|||||||
for (struct Symbol const *sym = objectSymbols; sym; sym = sym->next)
|
for (struct Symbol const *sym = objectSymbols; sym; sym = sym->next)
|
||||||
writesymbol(sym, f);
|
writesymbol(sym, f);
|
||||||
|
|
||||||
for (struct Section *sect = sectionList; sect; sect = sect->next) {
|
for (struct Section *sect = pSectionList; sect; sect = sect->next)
|
||||||
writesection(sect, f);
|
writesection(sect, f);
|
||||||
freesection(sect);
|
|
||||||
}
|
|
||||||
|
|
||||||
putlong(countAsserts(), f);
|
putlong(countAsserts(), f);
|
||||||
struct Assertion *assert = assertions;
|
for (struct Assertion *assert = assertions; assert;
|
||||||
|
assert = assert->next)
|
||||||
while (assert != NULL) {
|
|
||||||
struct Assertion *next = assert->next;
|
|
||||||
|
|
||||||
writeassert(assert, f);
|
writeassert(assert, f);
|
||||||
freeassert(assert);
|
|
||||||
assert = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
@@ -575,7 +530,7 @@ void out_WriteObject(void)
|
|||||||
*/
|
*/
|
||||||
void out_SetFileName(char *s)
|
void out_SetFileName(char *s)
|
||||||
{
|
{
|
||||||
objectName = s;
|
tzObjectname = s;
|
||||||
if (verbose)
|
if (verbose)
|
||||||
printf("Output filename %s\n", s);
|
printf("Output filename %s\n", s);
|
||||||
}
|
}
|
||||||
|
|||||||
1474
src/asm/parser.y
1474
src/asm/parser.y
File diff suppressed because it is too large
Load Diff
@@ -83,7 +83,7 @@ Add an include path.
|
|||||||
Disable the optimization that turns loads of the form
|
Disable the optimization that turns loads of the form
|
||||||
.Ic LD [$FF00+n8],A
|
.Ic LD [$FF00+n8],A
|
||||||
into the opcode
|
into the opcode
|
||||||
.Ic LDH [$FF00+n8],A
|
.Ic LD [H $FF00+n8],A
|
||||||
in order to have full control of the result in the final ROM.
|
in order to have full control of the result in the final ROM.
|
||||||
.It Fl M Ar depend_file , Fl Fl dependfile Ar depend_file
|
.It Fl M Ar depend_file , Fl Fl dependfile Ar depend_file
|
||||||
Print
|
Print
|
||||||
@@ -189,7 +189,7 @@ Ignoring the
|
|||||||
prefix, entries are listed alphabetically.
|
prefix, entries are listed alphabetically.
|
||||||
.Bl -tag -width Ds
|
.Bl -tag -width Ds
|
||||||
.It Fl Wno-assert
|
.It Fl Wno-assert
|
||||||
Warn when
|
Warns when
|
||||||
.Ic WARN Ns No -type
|
.Ic WARN Ns No -type
|
||||||
assertions fail. (See
|
assertions fail. (See
|
||||||
.Dq Aborting the assembly process
|
.Dq Aborting the assembly process
|
||||||
@@ -197,12 +197,6 @@ in
|
|||||||
.Xr rgbasm 5
|
.Xr rgbasm 5
|
||||||
for
|
for
|
||||||
.Ic ASSERT ) .
|
.Ic ASSERT ) .
|
||||||
.It Fl Wbackwards-for
|
|
||||||
Warn when
|
|
||||||
.Ic FOR
|
|
||||||
loops have their start and stop values switched according to the step value.
|
|
||||||
This warning is enabled by
|
|
||||||
.Fl Wall .
|
|
||||||
.It Fl Wbuiltin-args
|
.It Fl Wbuiltin-args
|
||||||
Warn about incorrect arguments to built-in functions, such as
|
Warn about incorrect arguments to built-in functions, such as
|
||||||
.Fn STRSUB
|
.Fn STRSUB
|
||||||
@@ -245,12 +239,12 @@ constant or
|
|||||||
directive are encountered.
|
directive are encountered.
|
||||||
.It Fl Wshift
|
.It Fl Wshift
|
||||||
Warn when shifting right a negative value.
|
Warn when shifting right a negative value.
|
||||||
Use a division by 2**N instead.
|
Use a division by 2^N instead.
|
||||||
.It Fl Wshift-amount
|
.It Fl Wshift-amount
|
||||||
Warn when a shift's operand is negative or greater than 32.
|
Warn when a shift's operand is negative or greater than 32.
|
||||||
.It Fl Wno-truncation
|
.It Fl Wno-truncation
|
||||||
Warn when an implicit truncation (for example,
|
Warn when an implicit truncation (for example,
|
||||||
.Ic db )
|
.Ic LD [B @] )
|
||||||
loses some bits.
|
loses some bits.
|
||||||
.It Fl Wno-user
|
.It Fl Wno-user
|
||||||
Warn when the
|
Warn when the
|
||||||
|
|||||||
330
src/asm/rgbasm.5
330
src/asm/rgbasm.5
@@ -63,9 +63,9 @@ X = /* the value of x
|
|||||||
Sometimes lines can be too long and it may be necessary to split them.
|
Sometimes lines can be too long and it may be necessary to split them.
|
||||||
To do so, put a backslash at the end of the line:
|
To do so, put a backslash at the end of the line:
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
DB 1, 2, 3,\ \[rs]
|
LD [B @:...], 1, 2, 3,\ \[rs]
|
||||||
4, 5, 6,\ \[rs]\ ;\ Put it before any comments
|
4, 5, 6,\ \[rs]\ ;\ Put it before any comments
|
||||||
7, 8, 9
|
7, 8, 9
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
This works anywhere in the code except inside of strings.
|
This works anywhere in the code except inside of strings.
|
||||||
@@ -73,8 +73,8 @@ To split strings it is needed to use
|
|||||||
.Fn STRCAT
|
.Fn STRCAT
|
||||||
like this:
|
like this:
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
db STRCAT("Hello ",\ \[rs]
|
LD [B @:], STRCAT("Hello ",\ \[rs]
|
||||||
"world!")
|
"world!")
|
||||||
.Ed
|
.Ed
|
||||||
.Sh EXPRESSIONS
|
.Sh EXPRESSIONS
|
||||||
An expression can be composed of many things.
|
An expression can be composed of many things.
|
||||||
@@ -91,13 +91,13 @@ section.
|
|||||||
The instructions in the macro-language generally require constant expressions.
|
The instructions in the macro-language generally require constant expressions.
|
||||||
.Ss Numeric Formats
|
.Ss Numeric Formats
|
||||||
There are a number of numeric formats.
|
There are a number of numeric formats.
|
||||||
.Bl -column -offset indent "Fixed point (Q16.16)" "Prefix"
|
.Bl -column -offset indent "Fixed point (16.16)" "Prefix"
|
||||||
.It Sy Format type Ta Sy Prefix Ta Sy Accepted characters
|
.It Sy Format type Ta Sy Prefix Ta Sy Accepted characters
|
||||||
.It Hexadecimal Ta $ Ta 0123456789ABCDEF
|
.It Hexadecimal Ta $ Ta 0123456789ABCDEF
|
||||||
.It Decimal Ta none Ta 0123456789
|
.It Decimal Ta none Ta 0123456789
|
||||||
.It Octal Ta & Ta 01234567
|
.It Octal Ta & Ta 01234567
|
||||||
.It Binary Ta % Ta 01
|
.It Binary Ta % Ta 01
|
||||||
.It Fixed point (Q16.16) Ta none Ta 01234.56789
|
.It Fixed point (16.16) Ta none Ta 01234.56789
|
||||||
.It Character constant Ta none Ta \(dqABYZ\(dq
|
.It Character constant Ta none Ta \(dqABYZ\(dq
|
||||||
.It Gameboy graphics Ta \` Ta 0123
|
.It Gameboy graphics Ta \` Ta 0123
|
||||||
.El
|
.El
|
||||||
@@ -226,7 +226,7 @@ For example:
|
|||||||
; (shifted and scaled from the range [-1.0, 1.0])
|
; (shifted and scaled from the range [-1.0, 1.0])
|
||||||
ANGLE = 0.0
|
ANGLE = 0.0
|
||||||
REPT 256
|
REPT 256
|
||||||
db (MUL(64.0, SIN(ANGLE)) + 64.0) >> 16
|
ld [b @], (MUL(64.0, SIN(ANGLE)) + 64.0) >> 16
|
||||||
ANGLE = ANGLE + 256.0 ; 256.0 = 65536 degrees / 256 entries
|
ANGLE = ANGLE + 256.0 ; 256.0 = 65536 degrees / 256 entries
|
||||||
ENDR
|
ENDR
|
||||||
.Ed
|
.Ed
|
||||||
@@ -337,7 +337,7 @@ followed by one or more
|
|||||||
\[en]
|
\[en]
|
||||||
.Ql 9 .
|
.Ql 9 .
|
||||||
If specified, prints this many digits of a fixed-point fraction.
|
If specified, prints this many digits of a fixed-point fraction.
|
||||||
Defaults to 5 digits, maximum 255 digits.
|
Defaults to 5 digits.
|
||||||
.It Ql <type> Ta Specifies the type of value.
|
.It Ql <type> Ta Specifies the type of value.
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
@@ -394,13 +394,11 @@ Most of them return a string, however some of these functions actually return an
|
|||||||
.It Fn STRCMP str1 str2 Ta Returns -1 if Ar str1 No is alphabetically lower than Ar str2 No , zero if they match, 1 if Ar str1 No is greater than Ar str2 .
|
.It Fn STRCMP str1 str2 Ta Returns -1 if Ar str1 No is alphabetically lower than Ar str2 No , zero if they match, 1 if Ar str1 No is greater than Ar str2 .
|
||||||
.It Fn STRIN str1 str2 Ta Returns the first position of Ar str2 No in Ar str1 No or zero if it's not present Pq first character is position 1 .
|
.It Fn STRIN str1 str2 Ta Returns the first position of Ar str2 No in Ar str1 No or zero if it's not present Pq first character is position 1 .
|
||||||
.It Fn STRRIN str1 str2 Ta Returns the last position of Ar str2 No in Ar str1 No or zero if it's not present Pq first character is position 1 .
|
.It Fn STRRIN str1 str2 Ta Returns the last position of Ar str2 No in Ar str1 No or zero if it's not present Pq first character is position 1 .
|
||||||
.It Fn STRSUB str pos len Ta Returns a substring from Ar str No starting at Ar pos No (first character is position 1, last is position -1) and Ar len No characters long. If Ar len No is not specified the substring continues to the end of Ar str .
|
.It Fn STRSUB str pos len Ta Returns a substring from Ar str No starting at Ar pos Po first character is position 1 Pc and Ar len No characters long.
|
||||||
.It Fn STRUPR str Ta Returns Ar str No with all letters in uppercase.
|
.It Fn STRUPR str Ta Returns Ar str No with all letters in uppercase.
|
||||||
.It Fn STRLWR str Ta Returns Ar str No with all letters in lowercase.
|
.It Fn STRLWR str Ta Returns Ar str No with all letters in lowercase.
|
||||||
.It Fn STRRPL str old new Ta Returns Ar str No with each non-overlapping occurrence of the substring Ar old No replaced with Ar new .
|
.It Fn STRRPL str old new Ta Returns Ar str No with each non-overlapping occurrence of the substring Ar old No replaced with Ar new .
|
||||||
.It Fn STRFMT fmt args... Ta Returns the string Ar fmt No with each
|
.It Fn STRFMT fmt args... Ta Returns the string Ar fmt No with each
|
||||||
.It Fn CHARLEN str Ta Returns the number of charmap entries in Ar str No with the current charmap.
|
|
||||||
.It Fn CHARSUB str pos Ta Returns the substring for the charmap entry at Ar pos No in Ar str No (first character is position 1, last is position -1) with the current charmap.
|
|
||||||
.Ql %spec
|
.Ql %spec
|
||||||
pattern replaced by interpolating the format
|
pattern replaced by interpolating the format
|
||||||
.Ar spec
|
.Ar spec
|
||||||
@@ -419,9 +417,9 @@ CHARMAP "í", 20
|
|||||||
CHARMAP "A", 128
|
CHARMAP "A", 128
|
||||||
.Ed
|
.Ed
|
||||||
This would result in
|
This would result in
|
||||||
.Ql db \(dqAmen<LF>\(dq
|
.Ql ld [b @:...], \(dqAmen<LF>\(dq
|
||||||
being equivalent to
|
being equivalent to
|
||||||
.Ql db 128, 109, 101, 110, 10 .
|
.Ql ld [b @:...], 128, 109, 101, 110, 10 .
|
||||||
.Pp
|
.Pp
|
||||||
Any characters in a string without defined mappings will be copied directly, using the source file's encoding of characters to bytes.
|
Any characters in a string without defined mappings will be copied directly, using the source file's encoding of characters to bytes.
|
||||||
.Pp
|
.Pp
|
||||||
@@ -461,12 +459,6 @@ is a label, it returns the bank number the label is in.
|
|||||||
The result may be constant if
|
The result may be constant if
|
||||||
.Nm
|
.Nm
|
||||||
is able to compute it.
|
is able to compute it.
|
||||||
.It Fn SIZEOF arg Ta Returns the size of the section named
|
|
||||||
.Ar arg .
|
|
||||||
The result is not constant, since only RGBLINK can compute its value.
|
|
||||||
.It Fn STARTOF arg Ta Returns the starting address of the section named
|
|
||||||
.Ar arg .
|
|
||||||
The result is not constant, since only RGBLINK can compute its value.
|
|
||||||
.It Fn DEF symbol Ta Returns TRUE (1) if
|
.It Fn DEF symbol Ta Returns TRUE (1) if
|
||||||
.Ar symbol
|
.Ar symbol
|
||||||
has been defined, FALSE (0) otherwise.
|
has been defined, FALSE (0) otherwise.
|
||||||
@@ -582,15 +574,15 @@ While
|
|||||||
will automatically optimize
|
will automatically optimize
|
||||||
.Ic ld
|
.Ic ld
|
||||||
instructions to the smaller and faster
|
instructions to the smaller and faster
|
||||||
.Ic ldh
|
.Ic ld\ h
|
||||||
(see
|
(see
|
||||||
.Xr gbz80 7 )
|
.Xr gbz80 7 )
|
||||||
whenever possible, it is generally unable to do so when a label is involved.
|
whenever possible, it is generally unable to do so when a label is involved.
|
||||||
Using the
|
Using the
|
||||||
.Ic ldh
|
.Ic ld\ h
|
||||||
instruction directly is recommended.
|
instruction directly is recommended.
|
||||||
This forces the assembler to emit a
|
This forces the assembler to emit a
|
||||||
.Ic ldh
|
.Ic ld\ h
|
||||||
instruction and the linker to check if the value is in the correct range.
|
instruction and the linker to check if the value is in the correct range.
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
@@ -707,11 +699,11 @@ CopyCode:
|
|||||||
ld c, RAMLocation.end - RAMLocation
|
ld c, RAMLocation.end - RAMLocation
|
||||||
\&.loop
|
\&.loop
|
||||||
ld a, [de]
|
ld a, [de]
|
||||||
inc de
|
ld de+
|
||||||
ld [hli], a
|
ld [hli], a
|
||||||
dec c
|
ld c-
|
||||||
jr nz, .loop
|
ld nz pc, b .loop
|
||||||
ret
|
ld pc,[sp++]
|
||||||
|
|
||||||
RAMCode:
|
RAMCode:
|
||||||
LOAD "RAM code", WRAM0
|
LOAD "RAM code", WRAM0
|
||||||
@@ -721,13 +713,13 @@ RAMLocation:
|
|||||||
\&.copy
|
\&.copy
|
||||||
ld a, [hli]
|
ld a, [hli]
|
||||||
ld [de], a
|
ld [de], a
|
||||||
inc de
|
ld de+
|
||||||
and a
|
ld a,a&a
|
||||||
jr nz, .copy
|
ld nz pc, b .copy
|
||||||
ret
|
ld pc,[sp++]
|
||||||
|
|
||||||
\&.string
|
\&.string
|
||||||
db "Hello World!", 0
|
ld [b @:...], "Hello World!", 0
|
||||||
\&.end
|
\&.end
|
||||||
ENDL
|
ENDL
|
||||||
.Ed
|
.Ed
|
||||||
@@ -937,14 +929,14 @@ references the one before the expression;
|
|||||||
and so on.
|
and so on.
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
ld hl, :++
|
ld hl, :++
|
||||||
: ld a, [hli] ; referenced by "jr nz"
|
: ld a, [hli] ; referenced by "ld nz pc"
|
||||||
ldh [c], a
|
ld [h c], a
|
||||||
dec c
|
ld c-
|
||||||
jr nz, :-
|
ld nz pc,b :-
|
||||||
ret
|
ld pc,[sp++]
|
||||||
|
|
||||||
: ; referenced by "ld hl"
|
: ; referenced by "ld hl"
|
||||||
dw $7FFF, $1061, $03E0, $58A5
|
ld [w @:], $7FFF, $1061, $03E0, $58A5
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
A label's location (and thus value) is usually not determined until the linking stage, so labels usually cannot be used as constants.
|
A label's location (and thus value) is usually not determined until the linking stage, so labels usually cannot be used as constants.
|
||||||
@@ -958,7 +950,7 @@ is used to define numerical constant symbols.
|
|||||||
Unlike
|
Unlike
|
||||||
.Ic SET
|
.Ic SET
|
||||||
below, constants defined this way cannot be redefined.
|
below, constants defined this way cannot be redefined.
|
||||||
These constants can be used for unchanging values such as properties of the hardware.
|
They can, for example, be used for things such as bit definitions of hardware registers.
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
def SCREEN_WIDTH equ 160 ;\ In pixels
|
def SCREEN_WIDTH equ 160 ;\ In pixels
|
||||||
def SCREEN_HEIGHT equ 144
|
def SCREEN_HEIGHT equ 144
|
||||||
@@ -967,26 +959,6 @@ def SCREEN_HEIGHT equ 144
|
|||||||
Note that colons
|
Note that colons
|
||||||
.Ql \&:
|
.Ql \&:
|
||||||
following the name are not allowed.
|
following the name are not allowed.
|
||||||
.Pp
|
|
||||||
If you
|
|
||||||
.Em really
|
|
||||||
need to, the
|
|
||||||
.Ic REDEF
|
|
||||||
keyword will define or redefine a constant symbol.
|
|
||||||
This can be used, for example, to update a constant using a macro, without making it mutable in general.
|
|
||||||
.Bd -literal -offset indent
|
|
||||||
def NUM_ITEMS equ 0
|
|
||||||
MACRO add_item
|
|
||||||
redef NUM_ITEMS equ NUM_ITEMS + 1
|
|
||||||
def ITEM_{02x:NUM_ITEMS} equ \1
|
|
||||||
ENDM
|
|
||||||
add_item 1
|
|
||||||
add_item 4
|
|
||||||
add_item 9
|
|
||||||
add_item 16
|
|
||||||
assert NUM_ITEMS == 4
|
|
||||||
assert ITEM_04 == 16
|
|
||||||
.Ed
|
|
||||||
.Ss Mutable constants
|
.Ss Mutable constants
|
||||||
.Ic SET ,
|
.Ic SET ,
|
||||||
or its synonym
|
or its synonym
|
||||||
@@ -1064,18 +1036,18 @@ DEF COUNTREG EQUS "[hl+]"
|
|||||||
ld a,COUNTREG
|
ld a,COUNTREG
|
||||||
|
|
||||||
DEF PLAYER_NAME EQUS "\[rs]"John\[rs]""
|
DEF PLAYER_NAME EQUS "\[rs]"John\[rs]""
|
||||||
db PLAYER_NAME
|
ld [b @:], PLAYER_NAME
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
This will be interpreted as:
|
This will be interpreted as:
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
ld a,[hl+]
|
ld a,[hl+]
|
||||||
db "John"
|
ld [b @:], "John"
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
String equates can also be used to define small one-line macros:
|
String equates can also be used to define small one-line macros:
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
DEF pusha EQUS "push af\[rs]npush bc\[rs]npush de\[rs]npush hl\[rs]n"
|
DEF ld_de_hl EQUS "ld d,h\[rs]n ld e,l\[rs]n"
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
Note that colons
|
Note that colons
|
||||||
@@ -1095,7 +1067,7 @@ For example:
|
|||||||
DEF s EQUS "Hello, "
|
DEF s EQUS "Hello, "
|
||||||
REDEF s EQUS "{s}world!"
|
REDEF s EQUS "{s}world!"
|
||||||
; prints "Hello, world!"
|
; prints "Hello, world!"
|
||||||
PRINTLN "{s}\n"
|
PRINTT "{s}\n"
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
.Sy Important note :
|
.Sy Important note :
|
||||||
@@ -1163,7 +1135,7 @@ constructs.
|
|||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
MACRO MyMacro
|
MACRO MyMacro
|
||||||
ld a, 80
|
ld a, 80
|
||||||
call MyFunc
|
ld[--sp],pc, MyFunc
|
||||||
ENDM
|
ENDM
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
@@ -1204,11 +1176,7 @@ ENDM
|
|||||||
.Pp
|
.Pp
|
||||||
Macro arguments support all the escape sequences of strings, as well as
|
Macro arguments support all the escape sequences of strings, as well as
|
||||||
.Ql \[rs],
|
.Ql \[rs],
|
||||||
to escape commas, as well as
|
to escape commas, since those otherwise separate arguments.
|
||||||
.Ql \[rs](
|
|
||||||
and
|
|
||||||
.Ql \[rs])
|
|
||||||
to escape parentheses, since those otherwise separate and enclose arguments, respectively.
|
|
||||||
.Ss Exporting and importing symbols
|
.Ss Exporting and importing symbols
|
||||||
Importing and exporting of symbols is a feature that is very useful when your project spans many source files and, for example, you need to jump to a routine defined in another file.
|
Importing and exporting of symbols is a feature that is very useful when your project spans many source files and, for example, you need to jump to a routine defined in another file.
|
||||||
.Pp
|
.Pp
|
||||||
@@ -1240,9 +1208,9 @@ ExportedLabelB2:
|
|||||||
.Ql c.asm :
|
.Ql c.asm :
|
||||||
.Bd -literal -compact
|
.Bd -literal -compact
|
||||||
SECTION "C", ROM0[0]
|
SECTION "C", ROM0[0]
|
||||||
dw LabelA
|
ld [w @], LabelA
|
||||||
dw ExportedLabelB1
|
ld [w @], ExportedLabelB1
|
||||||
dw ExportedLabelB2
|
ld [w @], ExportedLabelB2
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
Then
|
Then
|
||||||
@@ -1304,7 +1272,6 @@ The following symbols are defined by the assembler:
|
|||||||
.It Dv __RGBDS_MINOR__ Ta Ic EQU Ta Minor version number of RGBDS
|
.It Dv __RGBDS_MINOR__ Ta Ic EQU Ta Minor version number of RGBDS
|
||||||
.It Dv __RGBDS_PATCH__ Ta Ic EQU Ta Patch version number of RGBDS
|
.It Dv __RGBDS_PATCH__ Ta Ic EQU Ta Patch version number of RGBDS
|
||||||
.It Dv __RGBDS_RC__ Ta Ic EQU Ta Release candidate ID of RGBDS, not defined for final releases
|
.It Dv __RGBDS_RC__ Ta Ic EQU Ta Release candidate ID of RGBDS, not defined for final releases
|
||||||
.It Dv __RGBDS_VERSION__ Ta Ic EQUS Ta Version of RGBDS, as printed by Ql rgbasm --version
|
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
The current time values will be taken from the
|
The current time values will be taken from the
|
||||||
@@ -1314,18 +1281,20 @@ Refer to the spec at
|
|||||||
.Lk https://reproducible-builds.org/docs/source-date-epoch/ .
|
.Lk https://reproducible-builds.org/docs/source-date-epoch/ .
|
||||||
.Sh DEFINING DATA
|
.Sh DEFINING DATA
|
||||||
.Ss Declaring variables in a RAM section
|
.Ss Declaring variables in a RAM section
|
||||||
.Ic DS
|
.Ic LD [B @:<len>]
|
||||||
allocates a number of empty bytes.
|
allocates a number of empty bytes.
|
||||||
This is the preferred method of allocating space in a RAM section.
|
This is the preferred method of allocating space in a RAM section.
|
||||||
You can also use
|
You can also use
|
||||||
.Ic DB , DW
|
.Ic LD [B @] , LD [W @]
|
||||||
and
|
and
|
||||||
.Ic DL
|
.Ic LD [L @]
|
||||||
without any arguments instead (see
|
with
|
||||||
|
.Ql \&?
|
||||||
|
instead (see
|
||||||
.Sx Defining constant data
|
.Sx Defining constant data
|
||||||
below).
|
below).
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
DS 42 ;\ Allocates 42 bytes
|
LD [B @:42], ? ;\ Allocates 42 bytes
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
Empty space in RAM sections will not be initialized.
|
Empty space in RAM sections will not be initialized.
|
||||||
@@ -1334,17 +1303,24 @@ In ROM sections, it will be filled with the value passed to the
|
|||||||
command-line option, except when using overlays with
|
command-line option, except when using overlays with
|
||||||
.Fl O .
|
.Fl O .
|
||||||
.Ss Defining constant data
|
.Ss Defining constant data
|
||||||
.Ic DB
|
.Ic LD [B @]
|
||||||
defines a list of bytes that will be stored in the final image.
|
defines a list of bytes that will be stored in the final image.
|
||||||
Ideal for tables and text.
|
Ideal for tables and text.
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
DB 1,2,3,4,"This is a string"
|
LD [B @:...], 1,2,3,4,"This is a string"
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
|
Python slice syntax is used: for a single byte, close the brackets immediately after
|
||||||
|
.Ql @ ;
|
||||||
|
if more than one entry follows, a colon must be added after the
|
||||||
|
.Ql @ ,
|
||||||
|
optionally followed by an ellipsis
|
||||||
|
.Ql ... .
|
||||||
|
.Pp
|
||||||
Alternatively, you can use
|
Alternatively, you can use
|
||||||
.Ic DW
|
.Ic LD [W @]
|
||||||
to store a list of words (16-bit) or
|
to store a list of words (16-bit) or
|
||||||
.Ic DL
|
.Ic LD [L @]
|
||||||
to store a list of double-words/longs (32-bit).
|
to store a list of double-words/longs (32-bit).
|
||||||
.Pp
|
.Pp
|
||||||
Strings are handled a little specially: they first undergo charmap conversion (see
|
Strings are handled a little specially: they first undergo charmap conversion (see
|
||||||
@@ -1352,36 +1328,42 @@ Strings are handled a little specially: they first undergo charmap conversion (s
|
|||||||
then each resulting character is output individually.
|
then each resulting character is output individually.
|
||||||
For example, under the default charmap, the following two lines are identical:
|
For example, under the default charmap, the following two lines are identical:
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
DW "Hello!"
|
LD [W @:], "Hello!"
|
||||||
DW "H", "e", "l", "l", "o", "!"
|
LD [W @:], "H", "e", "l", "l", "o", "!"
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
|
Note that strings require a colon after the
|
||||||
|
.Ql @
|
||||||
|
in the directive.
|
||||||
|
.Pp
|
||||||
If you do not want this special handling, enclose the string in parentheses.
|
If you do not want this special handling, enclose the string in parentheses.
|
||||||
.Pp
|
.Pp
|
||||||
.Ic DS
|
.Ic LD [B @:<len>]
|
||||||
can also be used to fill a region of memory with some repeated values.
|
can also be used to fill a region of memory with some repeated values.
|
||||||
For example:
|
For example:
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
; outputs 3 bytes: $AA, $AA, $AA
|
; outputs 3 bytes: $AA, $AA, $AA
|
||||||
DS 3, $AA
|
LD [B @:3], $AA
|
||||||
; outputs 7 bytes: $BB, $CC, $BB, $CC, $BB, $CC, $BB
|
; outputs 7 bytes: $BB, $CC, $BB, $CC, $BB, $CC, $BB
|
||||||
DS 7, $BB, $CC
|
LD [B @:7], $BB, $CC
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
You can also use
|
You can also use
|
||||||
.Ic DB , DW
|
.Ic LD [B @] , LD [W @]
|
||||||
and
|
and
|
||||||
.Ic DL
|
.Ic LD [L @]
|
||||||
without arguments.
|
with
|
||||||
|
.Ql \&?
|
||||||
|
as its sole argument (in which case neither the colon nor an ellipsis may be present).
|
||||||
This works exactly like
|
This works exactly like
|
||||||
.Ic DS 1 , DS 2
|
.Ic LD [B @:1] , LD [B @:2]
|
||||||
and
|
and
|
||||||
.Ic DS 4
|
.Ic LD [B @:4]
|
||||||
respectively.
|
respectively.
|
||||||
Consequently, no-argument
|
Consequently, these forms of
|
||||||
.Ic DB , DW
|
.Ic LD [B @] , LD [W @]
|
||||||
and
|
and
|
||||||
.Ic DL
|
.Ic LD [L @]
|
||||||
can be used in a
|
can be used in a
|
||||||
.Ic WRAM0
|
.Ic WRAM0
|
||||||
/
|
/
|
||||||
@@ -1396,23 +1378,25 @@ section.
|
|||||||
.Ss Including binary files
|
.Ss Including binary files
|
||||||
You probably have some graphics, level data, etc. you'd like to include.
|
You probably have some graphics, level data, etc. you'd like to include.
|
||||||
Use
|
Use
|
||||||
.Ic INCBIN
|
.Ic LD [B @:...] =
|
||||||
to include a raw binary file as it is.
|
to include a raw binary file as it is.
|
||||||
|
.Pq The ellipsis are optional.
|
||||||
If the file isn't found in the current directory, the include-path list passed to
|
If the file isn't found in the current directory, the include-path list passed to
|
||||||
.Xr rgbasm 1
|
.Xr rgbasm 1
|
||||||
(see the
|
(see the
|
||||||
.Fl i
|
.Fl i
|
||||||
option) on the command line will be searched.
|
option) on the command line will be searched.
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
INCBIN "titlepic.bin"
|
LD [B @:] = "titlepic.bin"
|
||||||
INCBIN "sprites/hero.bin"
|
LD [B @:...] = "sprites/hero.bin"
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
You can also include only part of a file with
|
You can also include only part of a file with
|
||||||
.Ic INCBIN .
|
.Ic LD [B @:] .
|
||||||
The example below includes 256 bytes from data.bin, starting from byte 78.
|
The examples below include 256 bytes from data.bin, starting from byte 78.
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
INCBIN "data.bin",78,256
|
LD [B @:] = "data.bin"[78:256]
|
||||||
|
LD [B @:256] = "data.bin"[78:]
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
The length argument is optional.
|
The length argument is optional.
|
||||||
@@ -1432,19 +1416,19 @@ separates each block of allocations, and you may use it as many times within a u
|
|||||||
; Let's say PC = $C0DE here
|
; Let's say PC = $C0DE here
|
||||||
UNION
|
UNION
|
||||||
; Here, PC = $C0DE
|
; Here, PC = $C0DE
|
||||||
Name: ds 8
|
Name: ld [b @:8], ?
|
||||||
; PC = $C0E6
|
; PC = $C0E6
|
||||||
Nickname: ds 8
|
Nickname: ld [b @:8], ?
|
||||||
; PC = $C0EE
|
; PC = $C0EE
|
||||||
NEXTU
|
NEXTU
|
||||||
; PC is back to $C0DE
|
; PC is back to $C0DE
|
||||||
Health: dw
|
Health: ld [w @], ?
|
||||||
; PC = $C0E0
|
; PC = $C0E0
|
||||||
Something: ds 6
|
Something: ld [b @:6], ?
|
||||||
; And so on
|
; And so on
|
||||||
Lives: db
|
Lives: ld [b @], ?
|
||||||
NEXTU
|
NEXTU
|
||||||
VideoBuffer: ds 19
|
VideoBuffer: ld [b @:19], ?
|
||||||
ENDU
|
ENDU
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
@@ -1464,17 +1448,17 @@ The size of this union is 19 bytes, as this is the size of the largest block (th
|
|||||||
Nesting unions is possible, with each inner union's size being considered as described above.
|
Nesting unions is possible, with each inner union's size being considered as described above.
|
||||||
.Pp
|
.Pp
|
||||||
Unions may be used in any section, but inside them may only be
|
Unions may be used in any section, but inside them may only be
|
||||||
.Ic DS -
|
.Ic LD\ [B\ @],\ ? Ns - Ns
|
||||||
like commands (see
|
like commands (see
|
||||||
.Sx Declaring variables in a RAM section ) .
|
.Sx Declaring variables in a RAM section ) .
|
||||||
.Sh THE MACRO LANGUAGE
|
.Sh THE MACRO LANGUAGE
|
||||||
.Ss Invoking macros
|
.Ss Invoking macros
|
||||||
You execute the macro by inserting its name.
|
You execute the macro by inserting its name.
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
add a,b
|
ld a,a+b
|
||||||
ld sp,hl
|
ld sp,hl
|
||||||
MyMacro ;\ This will be expanded
|
MyMacro ;\ This will be expanded
|
||||||
sub a,87
|
ld a,a-87
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
It's valid to call a macro from a macro (yes, even the same one).
|
It's valid to call a macro from a macro (yes, even the same one).
|
||||||
@@ -1491,10 +1475,10 @@ it will insert the macro definition (the code enclosed in
|
|||||||
Suppose your macro contains a loop.
|
Suppose your macro contains a loop.
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
MACRO LoopyMacro
|
MACRO LoopyMacro
|
||||||
xor a,a
|
ld a,a^a
|
||||||
\&.loop ld [hl+],a
|
\&.loop ld [hl+],a
|
||||||
dec c
|
ld c-
|
||||||
jr nz,.loop
|
ld nz pc,b .loop
|
||||||
ENDM
|
ENDM
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
@@ -1509,10 +1493,10 @@ also works in
|
|||||||
blocks.
|
blocks.
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
MACRO LoopyMacro
|
MACRO LoopyMacro
|
||||||
xor a,a
|
ld a,a^a
|
||||||
\&.loop\[rs]@ ld [hl+],a
|
\&.loop\[rs]@ ld [hl+],a
|
||||||
dec c
|
ld c-
|
||||||
jr nz,.loop\[rs]@
|
ld nz pc,b .loop\[rs]@
|
||||||
ENDM
|
ENDM
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
@@ -1540,10 +1524,10 @@ being the first argument specified on the macro invocation.
|
|||||||
MACRO LoopyMacro
|
MACRO LoopyMacro
|
||||||
ld hl,\[rs]1
|
ld hl,\[rs]1
|
||||||
ld c,\[rs]2
|
ld c,\[rs]2
|
||||||
xor a,a
|
ld a,a^a
|
||||||
\&.loop\[rs]@ ld [hl+],a
|
\&.loop\[rs]@ ld [hl+],a
|
||||||
dec c
|
ld c-
|
||||||
jr nz,.loop\[rs]@
|
ld nz pc,b .loop\[rs]@
|
||||||
ENDM
|
ENDM
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
@@ -1577,57 +1561,29 @@ which will print 5 and not 6 as you might have expected.
|
|||||||
Line continuations work as usual inside macros or lists of macro arguments.
|
Line continuations work as usual inside macros or lists of macro arguments.
|
||||||
However, some characters need to be escaped, as in the following example:
|
However, some characters need to be escaped, as in the following example:
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
MACRO PrintMacro1
|
MACRO PrintMacro
|
||||||
PRINTLN STRCAT(\[rs]1)
|
|
||||||
ENDM
|
|
||||||
PrintMacro1 "Hello "\[rs], \[rs]
|
|
||||||
"world"
|
|
||||||
MACRO PrintMacro2
|
|
||||||
PRINT \[rs]1
|
PRINT \[rs]1
|
||||||
ENDM
|
ENDM
|
||||||
PrintMacro2 STRCAT("Hello ", \[rs]
|
|
||||||
"world\[rs]n")
|
PrintMacro STRCAT("Hello "\[rs], \[rs]
|
||||||
|
"world\[rs]n")
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
The comma in
|
The comma needs to be escaped to avoid it being treated as separating the macro's arguments.
|
||||||
.Ql PrintMacro1
|
|
||||||
needs to be escaped to prevent it from starting another macro argument.
|
|
||||||
The comma in
|
|
||||||
.Ql PrintMacro2
|
|
||||||
does not need escaping because it is inside parentheses, similar to macro arguments in C.
|
|
||||||
The backslash in
|
The backslash in
|
||||||
.Ql \[rs]n
|
.Ql \[rs]n
|
||||||
also does not need escaping because string literals work as usual inside macro arguments.
|
does not need to be escaped because string literals also work as usual inside macro arguments.
|
||||||
.Pp
|
.Pp
|
||||||
Since there are only nine digits, you can only access the first nine macro arguments like this.
|
In reality, up to 256 arguments can be passed to a macro, but you can only use the first 9 like this.
|
||||||
To use the rest, you need to put the multi-digit argument number in angle brackets, like
|
If you want to use the rest, you need to use the
|
||||||
.Ql \[rs]<10> .
|
|
||||||
This bracketed syntax supports decimal numbers and numeric symbol names.
|
|
||||||
For example,
|
|
||||||
.Ql \[rs]<_NARG>
|
|
||||||
will get the last argument.
|
|
||||||
.Pp
|
|
||||||
Other macro arguments and symbol interpolations will be expanded inside the angle brackets.
|
|
||||||
For example, if
|
|
||||||
.Ql \[rs]1
|
|
||||||
is
|
|
||||||
.Ql 13 ,
|
|
||||||
then
|
|
||||||
.Ql \[rs]<\[rs]1>
|
|
||||||
will expand to
|
|
||||||
.Ql \[rs]<13> .
|
|
||||||
Or if
|
|
||||||
.Ql v10 = 42
|
|
||||||
and
|
|
||||||
.Ql x = 10 ,
|
|
||||||
then
|
|
||||||
.Ql \[rs]<v{d:x}>
|
|
||||||
will expand to
|
|
||||||
.Ql \[rs]<42> .
|
|
||||||
.Pp
|
|
||||||
Another way to access more than nine macro arguments is the
|
|
||||||
.Ic SHIFT
|
.Ic SHIFT
|
||||||
command, a special command only available in macros.
|
command.
|
||||||
|
.Pp
|
||||||
|
.Ic SHIFT
|
||||||
|
is a special command only available in macros.
|
||||||
|
Very useful in
|
||||||
|
.Ic REPT
|
||||||
|
blocks.
|
||||||
It will shift the arguments by one to the left, and decrease
|
It will shift the arguments by one to the left, and decrease
|
||||||
.Dv _NARG
|
.Dv _NARG
|
||||||
by 1.
|
by 1.
|
||||||
@@ -1638,14 +1594,11 @@ will get the value of
|
|||||||
.Ic \[rs]3 ,
|
.Ic \[rs]3 ,
|
||||||
and so forth.
|
and so forth.
|
||||||
.Pp
|
.Pp
|
||||||
|
This is the only way of accessing the value of arguments from 10 to 256.
|
||||||
|
.Pp
|
||||||
.Ic SHIFT
|
.Ic SHIFT
|
||||||
can optionally be given an integer parameter, and will apply the above shifting that number of times.
|
can optionally be given an integer parameter, and will apply the above shifting that number of times.
|
||||||
A negative parameter will shift the arguments in reverse.
|
A negative parameter will shift the arguments in reverse.
|
||||||
.Pp
|
|
||||||
.Ic SHIFT
|
|
||||||
is useful in
|
|
||||||
.Ic REPT
|
|
||||||
blocks to repeat the same commands with multiple arguments.
|
|
||||||
.Ss Printing things during assembly
|
.Ss Printing things during assembly
|
||||||
The
|
The
|
||||||
.Ic PRINT
|
.Ic PRINT
|
||||||
@@ -1686,7 +1639,7 @@ The following example will assemble
|
|||||||
four times:
|
four times:
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
REPT 4
|
REPT 4
|
||||||
add a,c
|
ld a,a+c
|
||||||
ENDR
|
ENDR
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
@@ -1698,7 +1651,7 @@ to generate tables on the fly:
|
|||||||
; (shifted and scaled from the range [-1.0, 1.0])
|
; (shifted and scaled from the range [-1.0, 1.0])
|
||||||
ANGLE = 0.0
|
ANGLE = 0.0
|
||||||
REPT 256
|
REPT 256
|
||||||
db (MUL(64.0, SIN(ANGLE)) + 64.0) >> 16
|
ld [b @], (MUL(64.0, SIN(ANGLE)) + 64.0) >> 16
|
||||||
ANGLE = ANGLE + 256.0 ; 256.0 = 65536 degrees / 256 entries
|
ANGLE = ANGLE + 256.0 ; 256.0 = 65536 degrees / 256 entries
|
||||||
ENDR
|
ENDR
|
||||||
.Ed
|
.Ed
|
||||||
@@ -1722,21 +1675,21 @@ String equates are not expanded within the symbol name.
|
|||||||
For example, this code will produce a table of squared values from 0 to 255:
|
For example, this code will produce a table of squared values from 0 to 255:
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
FOR N, 256
|
FOR N, 256
|
||||||
dw N * N
|
ld [w @], N * N
|
||||||
ENDR
|
ENDR
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
It acts just as if you had done:
|
It acts just as if you had done:
|
||||||
.Bd -literal -offset ident
|
.Bd -literal -offset ident
|
||||||
N = 0
|
N = 0
|
||||||
dw N * N
|
ld [w @], N * N
|
||||||
N = 1
|
N = 1
|
||||||
dw N * N
|
ld [w @], N * N
|
||||||
N = 2
|
N = 2
|
||||||
dw N * N
|
ld [w @], N * N
|
||||||
; ...
|
; ...
|
||||||
N = 255
|
N = 255
|
||||||
dw N * N
|
ld [w @], N * N
|
||||||
N = 256
|
N = 256
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
@@ -1827,18 +1780,18 @@ and
|
|||||||
Syntax examples are given below:
|
Syntax examples are given below:
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
Function:
|
Function:
|
||||||
xor a
|
ld a,a^a
|
||||||
ASSERT LOW(Variable) == 0
|
ASSERT LOW(Variable) == 0
|
||||||
ld h, HIGH(Variable)
|
ld h, HIGH(Variable)
|
||||||
ld l, a
|
ld l, a
|
||||||
ld a, [hli]
|
ld a, [hli]
|
||||||
; You can also indent this!
|
; You can also indent this!
|
||||||
ASSERT BANK(OtherFunction) == BANK(Function)
|
ASSERT BANK(OtherFunction) == BANK(Function)
|
||||||
call OtherFunction
|
ld [--sp], pc,OtherFunction
|
||||||
; Lowercase also works
|
; Lowercase also works
|
||||||
assert Variable + 1 == OtherVariable
|
assert Variable + 1 == OtherVariable
|
||||||
ld c, [hl]
|
ld c, [hl]
|
||||||
ret
|
ld pc,[sp++]
|
||||||
\&.end
|
\&.end
|
||||||
; If you specify one, a message will be printed
|
; If you specify one, a message will be printed
|
||||||
STATIC_ASSERT .end - Function < 256, "Function is too large!"
|
STATIC_ASSERT .end - Function < 256, "Function is too large!"
|
||||||
@@ -1946,29 +1899,16 @@ can be used to change some of the options during assembling from within the sour
|
|||||||
takes a comma-separated list of options as its argument:
|
takes a comma-separated list of options as its argument:
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
PUSHO
|
PUSHO
|
||||||
OPT g.oOX, Wdiv, L ; acts like command-line -g.oOX -Wdiv -L
|
OPT g.oOX ;Set the GB graphics constants to use these characters
|
||||||
DW `..ooOOXX ; uses the graphics constant characters from OPT g
|
LD [W @], `..ooOOXX
|
||||||
PRINTLN $80000000/-1 ; prints a warning about division
|
|
||||||
LD [$FF88], A ; encoded as LD, not LDH
|
|
||||||
POPO
|
POPO
|
||||||
DW `00112233 ; uses the default graphics constant characters
|
LD [W @], `00112233
|
||||||
PRINTLN $80000000/-1 ; no warning by default
|
|
||||||
LD [$FF88], A ; optimized to use LDH by default
|
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
The options that OPT can modify are currently:
|
The options that OPT can modify are currently:
|
||||||
.Cm b , g , p , h , L ,
|
.Cm b , g
|
||||||
and
|
and
|
||||||
.Cm W .
|
.Cm p .
|
||||||
The Boolean flag options
|
|
||||||
.Cm h
|
|
||||||
and
|
|
||||||
.Cm L
|
|
||||||
can be negated as
|
|
||||||
.Ql OPT !h
|
|
||||||
and
|
|
||||||
.Ql OPT !L
|
|
||||||
to act like omitting them from the command-line.
|
|
||||||
.Pp
|
.Pp
|
||||||
.Ic POPO
|
.Ic POPO
|
||||||
and
|
and
|
||||||
|
|||||||
306
src/asm/rpn.c
306
src/asm/rpn.c
@@ -13,7 +13,6 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <limits.h>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -46,47 +45,47 @@
|
|||||||
static uint8_t *reserveSpace(struct Expression *expr, uint32_t size)
|
static uint8_t *reserveSpace(struct Expression *expr, uint32_t size)
|
||||||
{
|
{
|
||||||
/* This assumes the RPN length is always less than the capacity */
|
/* This assumes the RPN length is always less than the capacity */
|
||||||
if (expr->rpnCapacity - expr->rpnLength < size) {
|
if (expr->nRPNCapacity - expr->nRPNLength < size) {
|
||||||
/* If there isn't enough room to reserve the space, realloc */
|
/* If there isn't enough room to reserve the space, realloc */
|
||||||
if (!expr->rpn)
|
if (!expr->tRPN)
|
||||||
expr->rpnCapacity = 256; /* Initial size */
|
expr->nRPNCapacity = 256; /* Initial size */
|
||||||
while (expr->rpnCapacity - expr->rpnLength < size) {
|
while (expr->nRPNCapacity - expr->nRPNLength < size) {
|
||||||
if (expr->rpnCapacity >= MAXRPNLEN)
|
if (expr->nRPNCapacity >= MAXRPNLEN)
|
||||||
/*
|
/*
|
||||||
* To avoid generating humongous object files, cap the
|
* To avoid generating humongous object files, cap the
|
||||||
* size of RPN expressions
|
* size of RPN expressions
|
||||||
*/
|
*/
|
||||||
fatalerror("RPN expression cannot grow larger than "
|
fatalerror("RPN expression cannot grow larger than "
|
||||||
EXPAND_AND_STR(MAXRPNLEN) " bytes\n");
|
EXPAND_AND_STR(MAXRPNLEN) " bytes\n");
|
||||||
else if (expr->rpnCapacity > MAXRPNLEN / 2)
|
else if (expr->nRPNCapacity > MAXRPNLEN / 2)
|
||||||
expr->rpnCapacity = MAXRPNLEN;
|
expr->nRPNCapacity = MAXRPNLEN;
|
||||||
else
|
else
|
||||||
expr->rpnCapacity *= 2;
|
expr->nRPNCapacity *= 2;
|
||||||
}
|
}
|
||||||
expr->rpn = realloc(expr->rpn, expr->rpnCapacity);
|
expr->tRPN = realloc(expr->tRPN, expr->nRPNCapacity);
|
||||||
|
|
||||||
if (!expr->rpn)
|
if (!expr->tRPN)
|
||||||
fatalerror("Failed to grow RPN expression: %s\n", strerror(errno));
|
fatalerror("Failed to grow RPN expression: %s\n", strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *ptr = expr->rpn + expr->rpnLength;
|
uint8_t *ptr = expr->tRPN + expr->nRPNLength;
|
||||||
|
|
||||||
expr->rpnLength += size;
|
expr->nRPNLength += size;
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Init a RPN expression
|
* Init the RPN expression
|
||||||
*/
|
*/
|
||||||
static void rpn_Init(struct Expression *expr)
|
static void rpn_Init(struct Expression *expr)
|
||||||
{
|
{
|
||||||
expr->reason = NULL;
|
expr->reason = NULL;
|
||||||
expr->isKnown = true;
|
expr->isKnown = true;
|
||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
expr->rpn = NULL;
|
expr->tRPN = NULL;
|
||||||
expr->rpnCapacity = 0;
|
expr->nRPNCapacity = 0;
|
||||||
expr->rpnLength = 0;
|
expr->nRPNLength = 0;
|
||||||
expr->rpnPatchSize = 0;
|
expr->nRPNPatchSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -94,7 +93,7 @@ static void rpn_Init(struct Expression *expr)
|
|||||||
*/
|
*/
|
||||||
void rpn_Free(struct Expression *expr)
|
void rpn_Free(struct Expression *expr)
|
||||||
{
|
{
|
||||||
free(expr->rpn);
|
free(expr->tRPN);
|
||||||
free(expr->reason);
|
free(expr->reason);
|
||||||
rpn_Init(expr);
|
rpn_Init(expr);
|
||||||
}
|
}
|
||||||
@@ -105,12 +104,12 @@ void rpn_Free(struct Expression *expr)
|
|||||||
void rpn_Number(struct Expression *expr, uint32_t i)
|
void rpn_Number(struct Expression *expr, uint32_t i)
|
||||||
{
|
{
|
||||||
rpn_Init(expr);
|
rpn_Init(expr);
|
||||||
expr->val = i;
|
expr->nVal = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rpn_Symbol(struct Expression *expr, char const *symName)
|
void rpn_Symbol(struct Expression *expr, char const *tzSym)
|
||||||
{
|
{
|
||||||
struct Symbol *sym = sym_FindScopedSymbol(symName);
|
struct Symbol *sym = sym_FindScopedSymbol(tzSym);
|
||||||
|
|
||||||
if (sym_IsPC(sym) && !sect_GetSymbolSection()) {
|
if (sym_IsPC(sym) && !sect_GetSymbolSection()) {
|
||||||
error("PC has no value outside a section\n");
|
error("PC has no value outside a section\n");
|
||||||
@@ -120,16 +119,16 @@ void rpn_Symbol(struct Expression *expr, char const *symName)
|
|||||||
expr->isSymbol = true;
|
expr->isSymbol = true;
|
||||||
|
|
||||||
makeUnknown(expr, sym_IsPC(sym) ? "PC is not constant at assembly time"
|
makeUnknown(expr, sym_IsPC(sym) ? "PC is not constant at assembly time"
|
||||||
: "'%s' is not constant at assembly time", symName);
|
: "'%s' is not constant at assembly time", tzSym);
|
||||||
sym = sym_Ref(symName);
|
sym = sym_Ref(tzSym);
|
||||||
expr->rpnPatchSize += 5; /* 1-byte opcode + 4-byte symbol ID */
|
expr->nRPNPatchSize += 5; /* 1-byte opcode + 4-byte symbol ID */
|
||||||
|
|
||||||
size_t nameLen = strlen(sym->name) + 1; /* Don't forget NUL! */
|
size_t nameLen = strlen(sym->name) + 1; /* Don't forget NUL! */
|
||||||
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
||||||
*ptr++ = RPN_SYM;
|
*ptr++ = RPN_SYM;
|
||||||
memcpy(ptr, sym->name, nameLen);
|
memcpy(ptr, sym->name, nameLen);
|
||||||
} else {
|
} else {
|
||||||
rpn_Number(expr, sym_GetConstantValue(symName));
|
rpn_Number(expr, sym_GetConstantValue(tzSym));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,21 +136,21 @@ void rpn_BankSelf(struct Expression *expr)
|
|||||||
{
|
{
|
||||||
rpn_Init(expr);
|
rpn_Init(expr);
|
||||||
|
|
||||||
if (!currentSection) {
|
if (!pCurrentSection) {
|
||||||
error("PC has no bank outside a section\n");
|
error("PC has no bank outside a section\n");
|
||||||
expr->val = 1;
|
expr->nVal = 1;
|
||||||
} else if (currentSection->bank == (uint32_t)-1) {
|
} else if (pCurrentSection->bank == (uint32_t)-1) {
|
||||||
makeUnknown(expr, "Current section's bank is not known");
|
makeUnknown(expr, "Current section's bank is not known");
|
||||||
expr->rpnPatchSize++;
|
expr->nRPNPatchSize++;
|
||||||
*reserveSpace(expr, 1) = RPN_BANK_SELF;
|
*reserveSpace(expr, 1) = RPN_BANK_SELF;
|
||||||
} else {
|
} else {
|
||||||
expr->val = currentSection->bank;
|
expr->nVal = pCurrentSection->bank;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void rpn_BankSymbol(struct Expression *expr, char const *symName)
|
void rpn_BankSymbol(struct Expression *expr, char const *tzSym)
|
||||||
{
|
{
|
||||||
struct Symbol const *sym = sym_FindScopedSymbol(symName);
|
struct Symbol const *sym = sym_FindScopedSymbol(tzSym);
|
||||||
|
|
||||||
/* The @ symbol is treated differently. */
|
/* The @ symbol is treated differently. */
|
||||||
if (sym_IsPC(sym)) {
|
if (sym_IsPC(sym)) {
|
||||||
@@ -163,15 +162,15 @@ void rpn_BankSymbol(struct Expression *expr, char const *symName)
|
|||||||
if (sym && !sym_IsLabel(sym)) {
|
if (sym && !sym_IsLabel(sym)) {
|
||||||
error("BANK argument must be a label\n");
|
error("BANK argument must be a label\n");
|
||||||
} else {
|
} else {
|
||||||
sym = sym_Ref(symName);
|
sym = sym_Ref(tzSym);
|
||||||
assert(sym); // If the symbol didn't exist, it should have been created
|
assert(sym); // If the symbol didn't exist, it should have been created
|
||||||
|
|
||||||
if (sym_GetSection(sym) && sym_GetSection(sym)->bank != (uint32_t)-1) {
|
if (sym_GetSection(sym) && sym_GetSection(sym)->bank != (uint32_t)-1) {
|
||||||
/* Symbol's section is known and bank is fixed */
|
/* Symbol's section is known and bank is fixed */
|
||||||
expr->val = sym_GetSection(sym)->bank;
|
expr->nVal = sym_GetSection(sym)->bank;
|
||||||
} else {
|
} else {
|
||||||
makeUnknown(expr, "\"%s\"'s bank is not known", symName);
|
makeUnknown(expr, "\"%s\"'s bank is not known", tzSym);
|
||||||
expr->rpnPatchSize += 5; /* opcode + 4-byte sect ID */
|
expr->nRPNPatchSize += 5; /* opcode + 4-byte sect ID */
|
||||||
|
|
||||||
size_t nameLen = strlen(sym->name) + 1; /* Room for NUL! */
|
size_t nameLen = strlen(sym->name) + 1; /* Room for NUL! */
|
||||||
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
||||||
@@ -181,67 +180,40 @@ void rpn_BankSymbol(struct Expression *expr, char const *symName)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void rpn_BankSection(struct Expression *expr, char const *sectionName)
|
void rpn_BankSection(struct Expression *expr, char const *tzSectionName)
|
||||||
{
|
{
|
||||||
rpn_Init(expr);
|
rpn_Init(expr);
|
||||||
|
|
||||||
struct Section *section = sect_FindSectionByName(sectionName);
|
struct Section *pSection = out_FindSectionByName(tzSectionName);
|
||||||
|
|
||||||
if (section && section->bank != (uint32_t)-1) {
|
if (pSection && pSection->bank != (uint32_t)-1) {
|
||||||
expr->val = section->bank;
|
expr->nVal = pSection->bank;
|
||||||
} else {
|
} else {
|
||||||
makeUnknown(expr, "Section \"%s\"'s bank is not known", sectionName);
|
makeUnknown(expr, "Section \"%s\"'s bank is not known",
|
||||||
|
tzSectionName);
|
||||||
|
|
||||||
size_t nameLen = strlen(sectionName) + 1; /* Room for NUL! */
|
size_t nameLen = strlen(tzSectionName) + 1; /* Room for NUL! */
|
||||||
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
||||||
|
|
||||||
expr->rpnPatchSize += nameLen + 1;
|
expr->nRPNPatchSize += nameLen + 1;
|
||||||
*ptr++ = RPN_BANK_SECT;
|
*ptr++ = RPN_BANK_SECT;
|
||||||
memcpy(ptr, sectionName, nameLen);
|
memcpy(ptr, tzSectionName, nameLen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void rpn_SizeOfSection(struct Expression *expr, char const *sectionName)
|
|
||||||
{
|
|
||||||
rpn_Init(expr);
|
|
||||||
|
|
||||||
makeUnknown(expr, "Section \"%s\"'s size is not known", sectionName);
|
|
||||||
|
|
||||||
size_t nameLen = strlen(sectionName) + 1; /* Room for NUL! */
|
|
||||||
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
|
||||||
|
|
||||||
expr->rpnPatchSize += nameLen + 1;
|
|
||||||
*ptr++ = RPN_SIZEOF_SECT;
|
|
||||||
memcpy(ptr, sectionName, nameLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void rpn_StartOfSection(struct Expression *expr, char const *sectionName)
|
|
||||||
{
|
|
||||||
rpn_Init(expr);
|
|
||||||
|
|
||||||
makeUnknown(expr, "Section \"%s\"'s start is not known", sectionName);
|
|
||||||
|
|
||||||
size_t nameLen = strlen(sectionName) + 1; /* Room for NUL! */
|
|
||||||
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
|
||||||
|
|
||||||
expr->rpnPatchSize += nameLen + 1;
|
|
||||||
*ptr++ = RPN_STARTOF_SECT;
|
|
||||||
memcpy(ptr, sectionName, nameLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void rpn_CheckHRAM(struct Expression *expr, const struct Expression *src)
|
void rpn_CheckHRAM(struct Expression *expr, const struct Expression *src)
|
||||||
{
|
{
|
||||||
*expr = *src;
|
*expr = *src;
|
||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
|
|
||||||
if (!rpn_isKnown(expr)) {
|
if (!rpn_isKnown(expr)) {
|
||||||
expr->rpnPatchSize++;
|
expr->nRPNPatchSize++;
|
||||||
*reserveSpace(expr, 1) = RPN_HRAM;
|
*reserveSpace(expr, 1) = RPN_HRAM;
|
||||||
} else if (expr->val >= 0xFF00 && expr->val <= 0xFFFF) {
|
} else if (expr->nVal >= 0xFF00 && expr->nVal <= 0xFFFF) {
|
||||||
/* That range is valid, but only keep the lower byte */
|
/* That range is valid, but only keep the lower byte */
|
||||||
expr->val &= 0xFF;
|
expr->nVal &= 0xFF;
|
||||||
} else if (expr->val < 0 || expr->val > 0xFF) {
|
} else if (expr->nVal < 0 || expr->nVal > 0xFF) {
|
||||||
error("Source address $%" PRIx32 " not between $FF00 to $FFFF\n", expr->val);
|
error("Source address $%" PRIx32 " not between $FF00 to $FFFF\n", expr->nVal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,50 +223,25 @@ void rpn_CheckRST(struct Expression *expr, const struct Expression *src)
|
|||||||
|
|
||||||
if (rpn_isKnown(expr)) {
|
if (rpn_isKnown(expr)) {
|
||||||
/* A valid RST address must be masked with 0x38 */
|
/* A valid RST address must be masked with 0x38 */
|
||||||
if (expr->val & ~0x38)
|
if (expr->nVal & ~0x38)
|
||||||
error("Invalid address $%" PRIx32 " for RST\n", expr->val);
|
error("Invalid address $%" PRIx32 " for RST\n", expr->nVal);
|
||||||
/* The target is in the "0x38" bits, all other bits are set */
|
/* The target is in the "0x38" bits, all other bits are set */
|
||||||
expr->val |= 0xC7;
|
expr->nVal |= 0xC7;
|
||||||
} else {
|
} else {
|
||||||
expr->rpnPatchSize++;
|
expr->nRPNPatchSize++;
|
||||||
*reserveSpace(expr, 1) = RPN_RST;
|
*reserveSpace(expr, 1) = RPN_RST;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Checks that an RPN expression's value fits within N bits (signed or unsigned)
|
|
||||||
*/
|
|
||||||
void rpn_CheckNBit(struct Expression const *expr, uint8_t n)
|
|
||||||
{
|
|
||||||
assert(n != 0); // That doesn't make sense
|
|
||||||
assert(n < CHAR_BIT * sizeof(int)); // Otherwise `1 << n` is UB
|
|
||||||
|
|
||||||
if (rpn_isKnown(expr)) {
|
|
||||||
int32_t val = expr->val;
|
|
||||||
|
|
||||||
if (val < -(1 << (n - 1)) || val >= 1 << n)
|
|
||||||
warning(WARNING_TRUNCATION, "Expression must be %u-bit\n", n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t rpn_GetConstVal(struct Expression const *expr)
|
|
||||||
{
|
|
||||||
if (!rpn_isKnown(expr)) {
|
|
||||||
error("Expected constant expression: %s\n", expr->reason);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return expr->val;
|
|
||||||
}
|
|
||||||
|
|
||||||
void rpn_LOGNOT(struct Expression *expr, const struct Expression *src)
|
void rpn_LOGNOT(struct Expression *expr, const struct Expression *src)
|
||||||
{
|
{
|
||||||
*expr = *src;
|
*expr = *src;
|
||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
|
|
||||||
if (rpn_isKnown(expr)) {
|
if (rpn_isKnown(expr)) {
|
||||||
expr->val = !expr->val;
|
expr->nVal = !expr->nVal;
|
||||||
} else {
|
} else {
|
||||||
expr->rpnPatchSize++;
|
expr->nRPNPatchSize++;
|
||||||
*reserveSpace(expr, 1) = RPN_LOGUNNOT;
|
*reserveSpace(expr, 1) = RPN_LOGUNNOT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -303,7 +250,7 @@ struct Symbol const *rpn_SymbolOf(struct Expression const *expr)
|
|||||||
{
|
{
|
||||||
if (!rpn_isSymbol(expr))
|
if (!rpn_isSymbol(expr))
|
||||||
return NULL;
|
return NULL;
|
||||||
return sym_FindScopedSymbol((char *)expr->rpn + 1);
|
return sym_FindScopedSymbol((char *)expr->tRPN + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *sym)
|
bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *sym)
|
||||||
@@ -336,110 +283,111 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
|||||||
rpn_Init(expr); /* Init the expression to something sane */
|
rpn_Init(expr); /* Init the expression to something sane */
|
||||||
|
|
||||||
/* If both expressions are known, just compute the value */
|
/* If both expressions are known, just compute the value */
|
||||||
uint32_t uleft = src1->val, uright = src2->val;
|
uint32_t uleft = src1->nVal, uright = src2->nVal;
|
||||||
|
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case RPN_LOGOR:
|
case RPN_LOGOR:
|
||||||
expr->val = src1->val || src2->val;
|
expr->nVal = src1->nVal || src2->nVal;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGAND:
|
case RPN_LOGAND:
|
||||||
expr->val = src1->val && src2->val;
|
expr->nVal = src1->nVal && src2->nVal;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGEQ:
|
case RPN_LOGEQ:
|
||||||
expr->val = src1->val == src2->val;
|
expr->nVal = src1->nVal == src2->nVal;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGGT:
|
case RPN_LOGGT:
|
||||||
expr->val = src1->val > src2->val;
|
expr->nVal = src1->nVal > src2->nVal;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGLT:
|
case RPN_LOGLT:
|
||||||
expr->val = src1->val < src2->val;
|
expr->nVal = src1->nVal < src2->nVal;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGGE:
|
case RPN_LOGGE:
|
||||||
expr->val = src1->val >= src2->val;
|
expr->nVal = src1->nVal >= src2->nVal;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGLE:
|
case RPN_LOGLE:
|
||||||
expr->val = src1->val <= src2->val;
|
expr->nVal = src1->nVal <= src2->nVal;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGNE:
|
case RPN_LOGNE:
|
||||||
expr->val = src1->val != src2->val;
|
expr->nVal = src1->nVal != src2->nVal;
|
||||||
break;
|
break;
|
||||||
case RPN_ADD:
|
case RPN_ADD:
|
||||||
expr->val = uleft + uright;
|
expr->nVal = uleft + uright;
|
||||||
break;
|
break;
|
||||||
case RPN_SUB:
|
case RPN_SUB:
|
||||||
expr->val = uleft - uright;
|
expr->nVal = uleft - uright;
|
||||||
break;
|
break;
|
||||||
case RPN_XOR:
|
case RPN_XOR:
|
||||||
expr->val = src1->val ^ src2->val;
|
expr->nVal = src1->nVal ^ src2->nVal;
|
||||||
break;
|
break;
|
||||||
case RPN_OR:
|
case RPN_OR:
|
||||||
expr->val = src1->val | src2->val;
|
expr->nVal = src1->nVal | src2->nVal;
|
||||||
break;
|
break;
|
||||||
case RPN_AND:
|
case RPN_AND:
|
||||||
expr->val = src1->val & src2->val;
|
expr->nVal = src1->nVal & src2->nVal;
|
||||||
break;
|
break;
|
||||||
case RPN_SHL:
|
case RPN_SHL:
|
||||||
if (src2->val < 0)
|
if (src2->nVal < 0)
|
||||||
warning(WARNING_SHIFT_AMOUNT,
|
warning(WARNING_SHIFT_AMOUNT,
|
||||||
"Shifting left by negative amount %" PRId32 "\n",
|
"Shifting left by negative amount %" PRId32 "\n",
|
||||||
src2->val);
|
src2->nVal);
|
||||||
|
|
||||||
if (src2->val >= 32)
|
if (src2->nVal >= 32)
|
||||||
warning(WARNING_SHIFT_AMOUNT,
|
warning(WARNING_SHIFT_AMOUNT,
|
||||||
"Shifting left by large amount %" PRId32 "\n", src2->val);
|
"Shifting left by large amount %" PRId32 "\n",
|
||||||
|
src2->nVal);
|
||||||
|
|
||||||
expr->val = op_shift_left(src1->val, src2->val);
|
expr->nVal = op_shift_left(src1->nVal, src2->nVal);
|
||||||
break;
|
break;
|
||||||
case RPN_SHR:
|
case RPN_SHR:
|
||||||
if (src1->val < 0)
|
if (src1->nVal < 0)
|
||||||
warning(WARNING_SHIFT,
|
warning(WARNING_SHIFT, "Shifting right negative value %"
|
||||||
"Shifting right negative value %" PRId32 "\n", src1->val);
|
PRId32 "\n",
|
||||||
|
src1->nVal);
|
||||||
|
|
||||||
if (src2->val < 0)
|
if (src2->nVal < 0)
|
||||||
warning(WARNING_SHIFT_AMOUNT,
|
warning(WARNING_SHIFT_AMOUNT,
|
||||||
"Shifting right by negative amount %" PRId32 "\n",
|
"Shifting right by negative amount %" PRId32 "\n",
|
||||||
src2->val);
|
src2->nVal);
|
||||||
|
|
||||||
if (src2->val >= 32)
|
if (src2->nVal >= 32)
|
||||||
warning(WARNING_SHIFT_AMOUNT,
|
warning(WARNING_SHIFT_AMOUNT,
|
||||||
"Shifting right by large amount %" PRId32 "\n",
|
"Shifting right by large amount %" PRId32 "\n",
|
||||||
src2->val);
|
src2->nVal);
|
||||||
|
|
||||||
expr->val = op_shift_right(src1->val, src2->val);
|
expr->nVal = op_shift_right(src1->nVal, src2->nVal);
|
||||||
break;
|
break;
|
||||||
case RPN_MUL:
|
case RPN_MUL:
|
||||||
expr->val = uleft * uright;
|
expr->nVal = uleft * uright;
|
||||||
break;
|
break;
|
||||||
case RPN_DIV:
|
case RPN_DIV:
|
||||||
if (src2->val == 0)
|
if (src2->nVal == 0)
|
||||||
fatalerror("Division by zero\n");
|
fatalerror("Division by zero\n");
|
||||||
|
|
||||||
if (src1->val == INT32_MIN && src2->val == -1) {
|
if (src1->nVal == INT32_MIN && src2->nVal == -1) {
|
||||||
warning(WARNING_DIV,
|
warning(WARNING_DIV, "Division of %" PRId32 " by -1 yields %"
|
||||||
"Division of %" PRId32 " by -1 yields %" PRId32 "\n",
|
PRId32 "\n", INT32_MIN, INT32_MIN);
|
||||||
INT32_MIN, INT32_MIN);
|
expr->nVal = INT32_MIN;
|
||||||
expr->val = INT32_MIN;
|
|
||||||
} else {
|
} else {
|
||||||
expr->val = op_divide(src1->val, src2->val);
|
expr->nVal = op_divide(src1->nVal, src2->nVal);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RPN_MOD:
|
case RPN_MOD:
|
||||||
if (src2->val == 0)
|
if (src2->nVal == 0)
|
||||||
fatalerror("Modulo by zero\n");
|
fatalerror("Modulo by zero\n");
|
||||||
|
|
||||||
if (src1->val == INT32_MIN && src2->val == -1)
|
if (src1->nVal == INT32_MIN && src2->nVal == -1)
|
||||||
expr->val = 0;
|
expr->nVal = 0;
|
||||||
else
|
else
|
||||||
expr->val = op_modulo(src1->val, src2->val);
|
expr->nVal = op_modulo(src1->nVal, src2->nVal);
|
||||||
break;
|
break;
|
||||||
case RPN_EXP:
|
case RPN_EXP:
|
||||||
if (src2->val < 0)
|
if (src2->nVal < 0)
|
||||||
fatalerror("Exponentiation by negative power\n");
|
fatalerror("Exponentiation by negative power\n");
|
||||||
|
|
||||||
if (src1->val == INT32_MIN && src2->val == -1)
|
if (src1->nVal == INT32_MIN && src2->nVal == -1)
|
||||||
expr->val = 0;
|
expr->nVal = 0;
|
||||||
else
|
else
|
||||||
expr->val = op_exponent(src1->val, src2->val);
|
expr->nVal = op_exponent(src1->nVal, src2->nVal);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_UNSUB:
|
case RPN_UNSUB:
|
||||||
@@ -448,8 +396,6 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
|||||||
case RPN_BANK_SYM:
|
case RPN_BANK_SYM:
|
||||||
case RPN_BANK_SECT:
|
case RPN_BANK_SECT:
|
||||||
case RPN_BANK_SELF:
|
case RPN_BANK_SELF:
|
||||||
case RPN_SIZEOF_SECT:
|
|
||||||
case RPN_STARTOF_SECT:
|
|
||||||
case RPN_HRAM:
|
case RPN_HRAM:
|
||||||
case RPN_RST:
|
case RPN_RST:
|
||||||
case RPN_CONST:
|
case RPN_CONST:
|
||||||
@@ -461,20 +407,20 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
|||||||
struct Symbol const *symbol1 = rpn_SymbolOf(src1);
|
struct Symbol const *symbol1 = rpn_SymbolOf(src1);
|
||||||
struct Symbol const *symbol2 = rpn_SymbolOf(src2);
|
struct Symbol const *symbol2 = rpn_SymbolOf(src2);
|
||||||
|
|
||||||
expr->val = sym_GetValue(symbol1) - sym_GetValue(symbol2);
|
expr->nVal = sym_GetValue(symbol1) - sym_GetValue(symbol2);
|
||||||
expr->isKnown = true;
|
expr->isKnown = true;
|
||||||
} else {
|
} else {
|
||||||
/* If it's not known, start computing the RPN expression */
|
/* If it's not known, start computing the RPN expression */
|
||||||
|
|
||||||
/* Convert the left-hand expression if it's constant */
|
/* Convert the left-hand expression if it's constant */
|
||||||
if (src1->isKnown) {
|
if (src1->isKnown) {
|
||||||
uint32_t lval = src1->val;
|
uint32_t lval = src1->nVal;
|
||||||
uint8_t bytes[] = {RPN_CONST, lval, lval >> 8,
|
uint8_t bytes[] = {RPN_CONST, lval, lval >> 8,
|
||||||
lval >> 16, lval >> 24};
|
lval >> 16, lval >> 24};
|
||||||
expr->rpnPatchSize = sizeof(bytes);
|
expr->nRPNPatchSize = sizeof(bytes);
|
||||||
expr->rpn = NULL;
|
expr->tRPN = NULL;
|
||||||
expr->rpnCapacity = 0;
|
expr->nRPNCapacity = 0;
|
||||||
expr->rpnLength = 0;
|
expr->nRPNLength = 0;
|
||||||
memcpy(reserveSpace(expr, sizeof(bytes)), bytes,
|
memcpy(reserveSpace(expr, sizeof(bytes)), bytes,
|
||||||
sizeof(bytes));
|
sizeof(bytes));
|
||||||
|
|
||||||
@@ -483,21 +429,21 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
|||||||
free(src1->reason);
|
free(src1->reason);
|
||||||
} else {
|
} else {
|
||||||
/* Otherwise just reuse its RPN buffer */
|
/* Otherwise just reuse its RPN buffer */
|
||||||
expr->rpnPatchSize = src1->rpnPatchSize;
|
expr->nRPNPatchSize = src1->nRPNPatchSize;
|
||||||
expr->rpn = src1->rpn;
|
expr->tRPN = src1->tRPN;
|
||||||
expr->rpnCapacity = src1->rpnCapacity;
|
expr->nRPNCapacity = src1->nRPNCapacity;
|
||||||
expr->rpnLength = src1->rpnLength;
|
expr->nRPNLength = src1->nRPNLength;
|
||||||
expr->reason = src1->reason;
|
expr->reason = src1->reason;
|
||||||
free(src2->reason);
|
free(src2->reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now, merge the right expression into the left one */
|
/* Now, merge the right expression into the left one */
|
||||||
uint8_t *ptr = src2->rpn; /* Pointer to the right RPN */
|
uint8_t *ptr = src2->tRPN; /* Pointer to the right RPN */
|
||||||
uint32_t len = src2->rpnLength; /* Size of the right RPN */
|
uint32_t len = src2->nRPNLength; /* Size of the right RPN */
|
||||||
uint32_t patchSize = src2->rpnPatchSize;
|
uint32_t patchSize = src2->nRPNPatchSize;
|
||||||
|
|
||||||
/* If the right expression is constant, merge a shim instead */
|
/* If the right expression is constant, merge a shim instead */
|
||||||
uint32_t rval = src2->val;
|
uint32_t rval = src2->nVal;
|
||||||
uint8_t bytes[] = {RPN_CONST, rval, rval >> 8, rval >> 16,
|
uint8_t bytes[] = {RPN_CONST, rval, rval >> 8, rval >> 16,
|
||||||
rval >> 24};
|
rval >> 24};
|
||||||
if (src2->isKnown) {
|
if (src2->isKnown) {
|
||||||
@@ -511,8 +457,8 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
|||||||
memcpy(buf, ptr, len);
|
memcpy(buf, ptr, len);
|
||||||
buf[len] = op;
|
buf[len] = op;
|
||||||
|
|
||||||
free(src2->rpn); /* If there was none, this is `free(NULL)` */
|
free(src2->tRPN); /* If there was none, this is `free(NULL)` */
|
||||||
expr->rpnPatchSize += patchSize + 1;
|
expr->nRPNPatchSize += patchSize + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,11 +468,11 @@ void rpn_HIGH(struct Expression *expr, const struct Expression *src)
|
|||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
|
|
||||||
if (rpn_isKnown(expr)) {
|
if (rpn_isKnown(expr)) {
|
||||||
expr->val = (uint32_t)expr->val >> 8 & 0xFF;
|
expr->nVal = (uint32_t)expr->nVal >> 8 & 0xFF;
|
||||||
} else {
|
} else {
|
||||||
uint8_t bytes[] = {RPN_CONST, 8, 0, 0, 0, RPN_SHR,
|
uint8_t bytes[] = {RPN_CONST, 8, 0, 0, 0, RPN_SHR,
|
||||||
RPN_CONST, 0xFF, 0, 0, 0, RPN_AND};
|
RPN_CONST, 0xFF, 0, 0, 0, RPN_AND};
|
||||||
expr->rpnPatchSize += sizeof(bytes);
|
expr->nRPNPatchSize += sizeof(bytes);
|
||||||
memcpy(reserveSpace(expr, sizeof(bytes)), bytes, sizeof(bytes));
|
memcpy(reserveSpace(expr, sizeof(bytes)), bytes, sizeof(bytes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -537,11 +483,11 @@ void rpn_LOW(struct Expression *expr, const struct Expression *src)
|
|||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
|
|
||||||
if (rpn_isKnown(expr)) {
|
if (rpn_isKnown(expr)) {
|
||||||
expr->val = expr->val & 0xFF;
|
expr->nVal = expr->nVal & 0xFF;
|
||||||
} else {
|
} else {
|
||||||
uint8_t bytes[] = {RPN_CONST, 0xFF, 0, 0, 0, RPN_AND};
|
uint8_t bytes[] = {RPN_CONST, 0xFF, 0, 0, 0, RPN_AND};
|
||||||
|
|
||||||
expr->rpnPatchSize += sizeof(bytes);
|
expr->nRPNPatchSize += sizeof(bytes);
|
||||||
memcpy(reserveSpace(expr, sizeof(bytes)), bytes, sizeof(bytes));
|
memcpy(reserveSpace(expr, sizeof(bytes)), bytes, sizeof(bytes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -549,7 +495,7 @@ void rpn_LOW(struct Expression *expr, const struct Expression *src)
|
|||||||
void rpn_ISCONST(struct Expression *expr, const struct Expression *src)
|
void rpn_ISCONST(struct Expression *expr, const struct Expression *src)
|
||||||
{
|
{
|
||||||
rpn_Init(expr);
|
rpn_Init(expr);
|
||||||
expr->val = rpn_isKnown(src);
|
expr->nVal = rpn_isKnown(src);
|
||||||
expr->isKnown = true;
|
expr->isKnown = true;
|
||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
}
|
}
|
||||||
@@ -560,9 +506,9 @@ void rpn_UNNEG(struct Expression *expr, const struct Expression *src)
|
|||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
|
|
||||||
if (rpn_isKnown(expr)) {
|
if (rpn_isKnown(expr)) {
|
||||||
expr->val = -(uint32_t)expr->val;
|
expr->nVal = -(uint32_t)expr->nVal;
|
||||||
} else {
|
} else {
|
||||||
expr->rpnPatchSize++;
|
expr->nRPNPatchSize++;
|
||||||
*reserveSpace(expr, 1) = RPN_UNSUB;
|
*reserveSpace(expr, 1) = RPN_UNSUB;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -573,9 +519,9 @@ void rpn_UNNOT(struct Expression *expr, const struct Expression *src)
|
|||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
|
|
||||||
if (rpn_isKnown(expr)) {
|
if (rpn_isKnown(expr)) {
|
||||||
expr->val = ~expr->val;
|
expr->nVal = ~expr->nVal;
|
||||||
} else {
|
} else {
|
||||||
expr->rpnPatchSize++;
|
expr->nRPNPatchSize++;
|
||||||
*reserveSpace(expr, 1) = RPN_UNNOT;
|
*reserveSpace(expr, 1) = RPN_UNNOT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,16 +22,13 @@ uint8_t fillByte;
|
|||||||
|
|
||||||
struct SectionStackEntry {
|
struct SectionStackEntry {
|
||||||
struct Section *section;
|
struct Section *section;
|
||||||
struct Section *loadSection;
|
|
||||||
char const *scope; /* Section's symbol scope */
|
char const *scope; /* Section's symbol scope */
|
||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
int32_t loadOffset;
|
|
||||||
struct SectionStackEntry *next;
|
struct SectionStackEntry *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SectionStackEntry *sectionStack;
|
struct SectionStackEntry *sectionStack;
|
||||||
uint32_t curOffset; /* Offset into the current section (see sect_GetSymbolOffset) */
|
uint32_t curOffset; /* Offset into the current section (see sect_GetSymbolOffset) */
|
||||||
struct Section *currentSection = NULL;
|
|
||||||
static struct Section *currentLoadSection = NULL;
|
static struct Section *currentLoadSection = NULL;
|
||||||
int32_t loadOffset; /* Offset into the LOAD section's parent (see sect_GetOutputOffset) */
|
int32_t loadOffset; /* Offset into the LOAD section's parent (see sect_GetOutputOffset) */
|
||||||
|
|
||||||
@@ -44,74 +41,53 @@ struct UnionStackEntry {
|
|||||||
/*
|
/*
|
||||||
* A quick check to see if we have an initialized section
|
* A quick check to see if we have an initialized section
|
||||||
*/
|
*/
|
||||||
attr_(warn_unused_result) static bool checksection(void)
|
static inline void checksection(void)
|
||||||
{
|
{
|
||||||
if (currentSection)
|
if (pCurrentSection == NULL)
|
||||||
return true;
|
fatalerror("Code generation before SECTION directive\n");
|
||||||
|
|
||||||
error("Cannot output data outside of a SECTION\n");
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A quick check to see if we have an initialized section that can contain
|
* A quick check to see if we have an initialized section that can contain
|
||||||
* this much initialized data
|
* this much initialized data
|
||||||
*/
|
*/
|
||||||
attr_(warn_unused_result) static bool checkcodesection(void)
|
static inline void checkcodesection(void)
|
||||||
{
|
{
|
||||||
if (!checksection())
|
checksection();
|
||||||
return false;
|
|
||||||
|
|
||||||
if (sect_HasData(currentSection->type))
|
if (!sect_HasData(pCurrentSection->type))
|
||||||
return true;
|
fatalerror("Section '%s' cannot contain code or data (not ROM0 or ROMX)\n",
|
||||||
|
pCurrentSection->name);
|
||||||
error("Section '%s' cannot contain code or data (not ROM0 or ROMX)\n",
|
|
||||||
currentSection->name);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attr_(warn_unused_result) static bool checkSectionSize(struct Section const *sect, uint32_t size)
|
static inline void checkSectionSize(struct Section const *sect, uint32_t size)
|
||||||
{
|
{
|
||||||
uint32_t maxSize = maxsize[sect->type];
|
uint32_t maxSize = maxsize[sect->type];
|
||||||
|
|
||||||
// If the new size is reasonable, keep going
|
if (size > maxSize)
|
||||||
if (size <= maxSize)
|
fatalerror("Section '%s' grew too big (max size = 0x%" PRIX32
|
||||||
return true;
|
" bytes, reached 0x%" PRIX32 ").\n", sect->name, maxSize, size);
|
||||||
|
|
||||||
error("Section '%s' grew too big (max size = 0x%" PRIX32
|
|
||||||
" bytes, reached 0x%" PRIX32 ").\n", sect->name, maxSize, size);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if the section has grown too much.
|
* Check if the section has grown too much.
|
||||||
*/
|
*/
|
||||||
attr_(warn_unused_result) static bool reserveSpace(uint32_t delta_size)
|
static inline void reserveSpace(uint32_t delta_size)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* This check is here to trap broken code that generates sections that are too big and to
|
* This check is here to trap broken code that generates sections that
|
||||||
* prevent the assembler from generating huge object files or trying to allocate too much
|
* are too big and to prevent the assembler from generating huge object
|
||||||
* memory.
|
* files or trying to allocate too much memory.
|
||||||
* A check at the linking stage is still necessary.
|
* A check at the linking stage is still necessary.
|
||||||
*/
|
*/
|
||||||
|
checkSectionSize(pCurrentSection, curOffset + loadOffset + delta_size);
|
||||||
// If the section has already overflowed, skip the check to avoid erroring out ad nauseam
|
if (currentLoadSection)
|
||||||
if (currentSection->size != UINT32_MAX
|
checkSectionSize(currentLoadSection, curOffset + delta_size);
|
||||||
&& !checkSectionSize(currentSection, curOffset + loadOffset + delta_size))
|
|
||||||
// Mark the section as overflowed, to avoid repeating the error
|
|
||||||
currentSection->size = UINT32_MAX;
|
|
||||||
|
|
||||||
if (currentLoadSection && currentLoadSection->size != UINT32_MAX
|
|
||||||
&& !checkSectionSize(currentLoadSection, curOffset + delta_size))
|
|
||||||
currentLoadSection->size = UINT32_MAX;
|
|
||||||
|
|
||||||
return currentSection->size != UINT32_MAX
|
|
||||||
&& (!currentLoadSection || currentLoadSection->size != UINT32_MAX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Section *sect_FindSectionByName(const char *name)
|
struct Section *out_FindSectionByName(const char *name)
|
||||||
{
|
{
|
||||||
for (struct Section *sect = sectionList; sect; sect = sect->next) {
|
for (struct Section *sect = pSectionList; sect; sect = sect->next) {
|
||||||
if (strcmp(name, sect->name) == 0)
|
if (strcmp(name, sect->name) == 0)
|
||||||
return sect;
|
return sect;
|
||||||
}
|
}
|
||||||
@@ -363,8 +339,6 @@ static struct Section *getSection(char const *name, enum SectionType type, uint3
|
|||||||
} else if (startaddr[type] & mask) {
|
} else if (startaddr[type] & mask) {
|
||||||
error("Section \"%s\"'s alignment cannot be attained in %s\n",
|
error("Section \"%s\"'s alignment cannot be attained in %s\n",
|
||||||
name, typeNames[type]);
|
name, typeNames[type]);
|
||||||
alignment = 0; /* Ignore it if it's unattainable */
|
|
||||||
org = 0;
|
|
||||||
} else if (alignment == 16) {
|
} else if (alignment == 16) {
|
||||||
// Treat an alignment of 16 as being fixed at address 0
|
// Treat an alignment of 16 as being fixed at address 0
|
||||||
alignment = 0;
|
alignment = 0;
|
||||||
@@ -375,15 +349,15 @@ static struct Section *getSection(char const *name, enum SectionType type, uint3
|
|||||||
|
|
||||||
// Check if another section exists with the same name; merge if yes, otherwise create one
|
// Check if another section exists with the same name; merge if yes, otherwise create one
|
||||||
|
|
||||||
struct Section *sect = sect_FindSectionByName(name);
|
struct Section *sect = out_FindSectionByName(name);
|
||||||
|
|
||||||
if (sect) {
|
if (sect) {
|
||||||
mergeSections(sect, type, org, bank, alignment, alignOffset, mod);
|
mergeSections(sect, type, org, bank, alignment, alignOffset, mod);
|
||||||
} else {
|
} else {
|
||||||
sect = createSection(name, type, org, bank, alignment, alignOffset, mod);
|
sect = createSection(name, type, org, bank, alignment, alignOffset, mod);
|
||||||
// Add the new section to the list (order doesn't matter)
|
// Add the new section to the list (order doesn't matter)
|
||||||
sect->next = sectionList;
|
sect->next = pSectionList;
|
||||||
sectionList = sect;
|
pSectionList = sect;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sect;
|
return sect;
|
||||||
@@ -403,8 +377,8 @@ static void changeSection(void)
|
|||||||
/*
|
/*
|
||||||
* Set the current section by name and type
|
* Set the current section by name and type
|
||||||
*/
|
*/
|
||||||
void sect_NewSection(char const *name, uint32_t type, uint32_t org,
|
void out_NewSection(char const *name, uint32_t type, uint32_t org,
|
||||||
struct SectionSpec const *attribs, enum SectionModifier mod)
|
struct SectionSpec const *attribs, enum SectionModifier mod)
|
||||||
{
|
{
|
||||||
if (currentLoadSection)
|
if (currentLoadSection)
|
||||||
fatalerror("Cannot change the section within a `LOAD` block\n");
|
fatalerror("Cannot change the section within a `LOAD` block\n");
|
||||||
@@ -418,32 +392,23 @@ void sect_NewSection(char const *name, uint32_t type, uint32_t org,
|
|||||||
|
|
||||||
changeSection();
|
changeSection();
|
||||||
curOffset = mod == SECTION_UNION ? 0 : sect->size;
|
curOffset = mod == SECTION_UNION ? 0 : sect->size;
|
||||||
currentSection = sect;
|
pCurrentSection = sect;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the current section by name and type
|
* Set the current section by name and type
|
||||||
*/
|
*/
|
||||||
void sect_SetLoadSection(char const *name, uint32_t type, uint32_t org,
|
void out_SetLoadSection(char const *name, uint32_t type, uint32_t org,
|
||||||
struct SectionSpec const *attribs, enum SectionModifier mod)
|
struct SectionSpec const *attribs,
|
||||||
|
enum SectionModifier mod)
|
||||||
{
|
{
|
||||||
if (!checkcodesection())
|
checkcodesection();
|
||||||
return;
|
|
||||||
|
|
||||||
if (currentLoadSection) {
|
if (currentLoadSection)
|
||||||
error("`LOAD` blocks cannot be nested\n");
|
fatalerror("`LOAD` blocks cannot be nested\n");
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sect_HasData(type)) {
|
if (sect_HasData(type))
|
||||||
error("`LOAD` blocks cannot create a ROM section\n");
|
error("`LOAD` blocks cannot create a ROM section\n");
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mod == SECTION_FRAGMENT) {
|
|
||||||
error("`LOAD FRAGMENT` is not allowed\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Section *sect = getSection(name, type, org, attribs, mod);
|
struct Section *sect = getSection(name, type, org, attribs, mod);
|
||||||
|
|
||||||
@@ -453,12 +418,10 @@ void sect_SetLoadSection(char const *name, uint32_t type, uint32_t org,
|
|||||||
currentLoadSection = sect;
|
currentLoadSection = sect;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sect_EndLoadSection(void)
|
void out_EndLoadSection(void)
|
||||||
{
|
{
|
||||||
if (!currentLoadSection) {
|
if (!currentLoadSection)
|
||||||
error("Found `ENDL` outside of a `LOAD` block\n");
|
error("Found `ENDL` outside of a `LOAD` block\n");
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
changeSection();
|
changeSection();
|
||||||
curOffset += loadOffset;
|
curOffset += loadOffset;
|
||||||
@@ -468,7 +431,7 @@ void sect_EndLoadSection(void)
|
|||||||
|
|
||||||
struct Section *sect_GetSymbolSection(void)
|
struct Section *sect_GetSymbolSection(void)
|
||||||
{
|
{
|
||||||
return currentLoadSection ? currentLoadSection : currentSection;
|
return currentLoadSection ? currentLoadSection : pCurrentSection;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -486,9 +449,7 @@ uint32_t sect_GetOutputOffset(void)
|
|||||||
|
|
||||||
void sect_AlignPC(uint8_t alignment, uint16_t offset)
|
void sect_AlignPC(uint8_t alignment, uint16_t offset)
|
||||||
{
|
{
|
||||||
if (!checksection())
|
checksection();
|
||||||
return;
|
|
||||||
|
|
||||||
struct Section *sect = sect_GetSymbolSection();
|
struct Section *sect = sect_GetSymbolSection();
|
||||||
uint16_t alignSize = 1 << alignment; // Size of an aligned "block"
|
uint16_t alignSize = 1 << alignment; // Size of an aligned "block"
|
||||||
|
|
||||||
@@ -511,28 +472,28 @@ void sect_AlignPC(uint8_t alignment, uint16_t offset)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void growSection(uint32_t growth)
|
static inline void growSection(uint32_t growth)
|
||||||
{
|
{
|
||||||
curOffset += growth;
|
curOffset += growth;
|
||||||
if (curOffset + loadOffset > currentSection->size)
|
if (curOffset + loadOffset > pCurrentSection->size)
|
||||||
currentSection->size = curOffset + loadOffset;
|
pCurrentSection->size = curOffset + loadOffset;
|
||||||
if (currentLoadSection && curOffset > currentLoadSection->size)
|
if (currentLoadSection && curOffset > currentLoadSection->size)
|
||||||
currentLoadSection->size = curOffset;
|
currentLoadSection->size = curOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writebyte(uint8_t byte)
|
static inline void writebyte(uint8_t byte)
|
||||||
{
|
{
|
||||||
currentSection->data[sect_GetOutputOffset()] = byte;
|
pCurrentSection->data[sect_GetOutputOffset()] = byte;
|
||||||
growSection(1);
|
growSection(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writeword(uint16_t b)
|
static inline void writeword(uint16_t b)
|
||||||
{
|
{
|
||||||
writebyte(b & 0xFF);
|
writebyte(b & 0xFF);
|
||||||
writebyte(b >> 8);
|
writebyte(b >> 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writelong(uint32_t b)
|
static inline void writelong(uint32_t b)
|
||||||
{
|
{
|
||||||
writebyte(b & 0xFF);
|
writebyte(b & 0xFF);
|
||||||
writebyte(b >> 8);
|
writebyte(b >> 8);
|
||||||
@@ -540,21 +501,18 @@ static void writelong(uint32_t b)
|
|||||||
writebyte(b >> 24);
|
writebyte(b >> 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void createPatch(enum PatchType type, struct Expression const *expr, uint32_t pcShift)
|
static inline void createPatch(enum PatchType type, struct Expression const *expr,
|
||||||
|
uint32_t pcShift)
|
||||||
{
|
{
|
||||||
out_CreatePatch(type, expr, sect_GetOutputOffset(), pcShift);
|
out_CreatePatch(type, expr, sect_GetOutputOffset(), pcShift);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sect_StartUnion(void)
|
void sect_StartUnion(void)
|
||||||
{
|
{
|
||||||
if (!currentSection) {
|
if (!pCurrentSection)
|
||||||
error("UNIONs must be inside a SECTION\n");
|
fatalerror("UNIONs must be inside a SECTION\n");
|
||||||
return;
|
if (sect_HasData(pCurrentSection->type))
|
||||||
}
|
fatalerror("Cannot use UNION inside of ROM0 or ROMX sections\n");
|
||||||
if (sect_HasData(currentSection->type)) {
|
|
||||||
error("Cannot use UNION inside of ROM0 or ROMX sections\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
struct UnionStackEntry *entry = malloc(sizeof(*entry));
|
struct UnionStackEntry *entry = malloc(sizeof(*entry));
|
||||||
|
|
||||||
if (!entry)
|
if (!entry)
|
||||||
@@ -576,19 +534,15 @@ static void endUnionMember(void)
|
|||||||
|
|
||||||
void sect_NextUnionMember(void)
|
void sect_NextUnionMember(void)
|
||||||
{
|
{
|
||||||
if (!unionStack) {
|
if (!unionStack)
|
||||||
error("Found NEXTU outside of a UNION construct\n");
|
fatalerror("Found NEXTU outside of a UNION construct\n");
|
||||||
return;
|
|
||||||
}
|
|
||||||
endUnionMember();
|
endUnionMember();
|
||||||
}
|
}
|
||||||
|
|
||||||
void sect_EndUnion(void)
|
void sect_EndUnion(void)
|
||||||
{
|
{
|
||||||
if (!unionStack) {
|
if (!unionStack)
|
||||||
error("Found ENDU outside of a UNION construct\n");
|
fatalerror("Found ENDU outside of a UNION construct\n");
|
||||||
return;
|
|
||||||
}
|
|
||||||
endUnionMember();
|
endUnionMember();
|
||||||
curOffset += unionStack->size;
|
curOffset += unionStack->size;
|
||||||
struct UnionStackEntry *next = unionStack->next;
|
struct UnionStackEntry *next = unionStack->next;
|
||||||
@@ -606,44 +560,36 @@ void sect_CheckUnionClosed(void)
|
|||||||
/*
|
/*
|
||||||
* Output an absolute byte
|
* Output an absolute byte
|
||||||
*/
|
*/
|
||||||
void sect_AbsByte(uint8_t b)
|
void out_AbsByte(uint8_t b)
|
||||||
{
|
{
|
||||||
if (!checkcodesection())
|
checkcodesection();
|
||||||
return;
|
reserveSpace(1);
|
||||||
if (!reserveSpace(1))
|
|
||||||
return;
|
|
||||||
|
|
||||||
writebyte(b);
|
writebyte(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sect_AbsByteGroup(uint8_t const *s, int32_t length)
|
void out_AbsByteGroup(uint8_t const *s, int32_t length)
|
||||||
{
|
{
|
||||||
if (!checkcodesection())
|
checkcodesection();
|
||||||
return;
|
reserveSpace(length);
|
||||||
if (!reserveSpace(length))
|
|
||||||
return;
|
|
||||||
|
|
||||||
while (length--)
|
while (length--)
|
||||||
writebyte(*s++);
|
writebyte(*s++);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sect_AbsWordGroup(uint8_t const *s, int32_t length)
|
void out_AbsWordGroup(uint8_t const *s, int32_t length)
|
||||||
{
|
{
|
||||||
if (!checkcodesection())
|
checkcodesection();
|
||||||
return;
|
reserveSpace(length * 2);
|
||||||
if (!reserveSpace(length * 2))
|
|
||||||
return;
|
|
||||||
|
|
||||||
while (length--)
|
while (length--)
|
||||||
writeword(*s++);
|
writeword(*s++);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sect_AbsLongGroup(uint8_t const *s, int32_t length)
|
void out_AbsLongGroup(uint8_t const *s, int32_t length)
|
||||||
{
|
{
|
||||||
if (!checkcodesection())
|
checkcodesection();
|
||||||
return;
|
reserveSpace(length * 4);
|
||||||
if (!reserveSpace(length * 4))
|
|
||||||
return;
|
|
||||||
|
|
||||||
while (length--)
|
while (length--)
|
||||||
writelong(*s++);
|
writelong(*s++);
|
||||||
@@ -652,20 +598,19 @@ void sect_AbsLongGroup(uint8_t const *s, int32_t length)
|
|||||||
/*
|
/*
|
||||||
* Skip this many bytes
|
* Skip this many bytes
|
||||||
*/
|
*/
|
||||||
void sect_Skip(int32_t skip, bool ds)
|
void out_Skip(int32_t skip, bool ds)
|
||||||
{
|
{
|
||||||
if (!checksection())
|
checksection();
|
||||||
return;
|
reserveSpace(skip);
|
||||||
if (!reserveSpace(skip))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!sect_HasData(currentSection->type)) {
|
if (!ds && sect_HasData(pCurrentSection->type))
|
||||||
|
warning(WARNING_EMPTY_DATA_DIRECTIVE, "%s directive without data in ROM\n",
|
||||||
|
(skip == 4) ? "DL" : (skip == 2) ? "DW" : "DB");
|
||||||
|
|
||||||
|
if (!sect_HasData(pCurrentSection->type)) {
|
||||||
growSection(skip);
|
growSection(skip);
|
||||||
} else {
|
} else {
|
||||||
if (!ds)
|
checkcodesection();
|
||||||
warning(WARNING_EMPTY_DATA_DIRECTIVE, "%s directive without data in ROM\n",
|
|
||||||
(skip == 4) ? "DL" : (skip == 2) ? "DW" : "DB");
|
|
||||||
// We know we're in a code SECTION
|
|
||||||
while (skip--)
|
while (skip--)
|
||||||
writebyte(fillByte);
|
writebyte(fillByte);
|
||||||
}
|
}
|
||||||
@@ -674,12 +619,10 @@ void sect_Skip(int32_t skip, bool ds)
|
|||||||
/*
|
/*
|
||||||
* Output a NULL terminated string (excluding the NULL-character)
|
* Output a NULL terminated string (excluding the NULL-character)
|
||||||
*/
|
*/
|
||||||
void sect_String(char const *s)
|
void out_String(char const *s)
|
||||||
{
|
{
|
||||||
if (!checkcodesection())
|
checkcodesection();
|
||||||
return;
|
reserveSpace(strlen(s));
|
||||||
if (!reserveSpace(strlen(s)))
|
|
||||||
return;
|
|
||||||
|
|
||||||
while (*s)
|
while (*s)
|
||||||
writebyte(*s++);
|
writebyte(*s++);
|
||||||
@@ -689,18 +632,16 @@ void sect_String(char const *s)
|
|||||||
* Output a relocatable byte. Checking will be done to see if it
|
* Output a relocatable byte. Checking will be done to see if it
|
||||||
* is an absolute value in disguise.
|
* is an absolute value in disguise.
|
||||||
*/
|
*/
|
||||||
void sect_RelByte(struct Expression *expr, uint32_t pcShift)
|
void out_RelByte(struct Expression *expr, uint32_t pcShift)
|
||||||
{
|
{
|
||||||
if (!checkcodesection())
|
checkcodesection();
|
||||||
return;
|
reserveSpace(1);
|
||||||
if (!reserveSpace(1))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!rpn_isKnown(expr)) {
|
if (!rpn_isKnown(expr)) {
|
||||||
createPatch(PATCHTYPE_BYTE, expr, pcShift);
|
createPatch(PATCHTYPE_BYTE, expr, pcShift);
|
||||||
writebyte(0);
|
writebyte(0);
|
||||||
} else {
|
} else {
|
||||||
writebyte(expr->val);
|
writebyte(expr->nVal);
|
||||||
}
|
}
|
||||||
rpn_Free(expr);
|
rpn_Free(expr);
|
||||||
}
|
}
|
||||||
@@ -709,12 +650,10 @@ void sect_RelByte(struct Expression *expr, uint32_t pcShift)
|
|||||||
* Output several copies of a relocatable byte. Checking will be done to see if
|
* Output several copies of a relocatable byte. Checking will be done to see if
|
||||||
* it is an absolute value in disguise.
|
* it is an absolute value in disguise.
|
||||||
*/
|
*/
|
||||||
void sect_RelBytes(uint32_t n, struct Expression *exprs, size_t size)
|
void out_RelBytes(uint32_t n, struct Expression *exprs, size_t size)
|
||||||
{
|
{
|
||||||
if (!checkcodesection())
|
checkcodesection();
|
||||||
return;
|
reserveSpace(n);
|
||||||
if (!reserveSpace(n))
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < n; i++) {
|
for (uint32_t i = 0; i < n; i++) {
|
||||||
struct Expression *expr = &exprs[i % size];
|
struct Expression *expr = &exprs[i % size];
|
||||||
@@ -723,7 +662,7 @@ void sect_RelBytes(uint32_t n, struct Expression *exprs, size_t size)
|
|||||||
createPatch(PATCHTYPE_BYTE, expr, i);
|
createPatch(PATCHTYPE_BYTE, expr, i);
|
||||||
writebyte(0);
|
writebyte(0);
|
||||||
} else {
|
} else {
|
||||||
writebyte(expr->val);
|
writebyte(expr->nVal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -735,18 +674,16 @@ void sect_RelBytes(uint32_t n, struct Expression *exprs, size_t size)
|
|||||||
* Output a relocatable word. Checking will be done to see if
|
* Output a relocatable word. Checking will be done to see if
|
||||||
* it's an absolute value in disguise.
|
* it's an absolute value in disguise.
|
||||||
*/
|
*/
|
||||||
void sect_RelWord(struct Expression *expr, uint32_t pcShift)
|
void out_RelWord(struct Expression *expr, uint32_t pcShift)
|
||||||
{
|
{
|
||||||
if (!checkcodesection())
|
checkcodesection();
|
||||||
return;
|
reserveSpace(2);
|
||||||
if (!reserveSpace(2))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!rpn_isKnown(expr)) {
|
if (!rpn_isKnown(expr)) {
|
||||||
createPatch(PATCHTYPE_WORD, expr, pcShift);
|
createPatch(PATCHTYPE_WORD, expr, pcShift);
|
||||||
writeword(0);
|
writeword(0);
|
||||||
} else {
|
} else {
|
||||||
writeword(expr->val);
|
writeword(expr->nVal);
|
||||||
}
|
}
|
||||||
rpn_Free(expr);
|
rpn_Free(expr);
|
||||||
}
|
}
|
||||||
@@ -755,18 +692,16 @@ void sect_RelWord(struct Expression *expr, uint32_t pcShift)
|
|||||||
* Output a relocatable longword. Checking will be done to see if
|
* Output a relocatable longword. Checking will be done to see if
|
||||||
* is an absolute value in disguise.
|
* is an absolute value in disguise.
|
||||||
*/
|
*/
|
||||||
void sect_RelLong(struct Expression *expr, uint32_t pcShift)
|
void out_RelLong(struct Expression *expr, uint32_t pcShift)
|
||||||
{
|
{
|
||||||
if (!checkcodesection())
|
checkcodesection();
|
||||||
return;
|
reserveSpace(2);
|
||||||
if (!reserveSpace(2))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!rpn_isKnown(expr)) {
|
if (!rpn_isKnown(expr)) {
|
||||||
createPatch(PATCHTYPE_LONG, expr, pcShift);
|
createPatch(PATCHTYPE_LONG, expr, pcShift);
|
||||||
writelong(0);
|
writelong(0);
|
||||||
} else {
|
} else {
|
||||||
writelong(expr->val);
|
writelong(expr->nVal);
|
||||||
}
|
}
|
||||||
rpn_Free(expr);
|
rpn_Free(expr);
|
||||||
}
|
}
|
||||||
@@ -775,12 +710,10 @@ void sect_RelLong(struct Expression *expr, uint32_t pcShift)
|
|||||||
* Output a PC-relative relocatable byte. Checking will be done to see if it
|
* Output a PC-relative relocatable byte. Checking will be done to see if it
|
||||||
* is an absolute value in disguise.
|
* is an absolute value in disguise.
|
||||||
*/
|
*/
|
||||||
void sect_PCRelByte(struct Expression *expr, uint32_t pcShift)
|
void out_PCRelByte(struct Expression *expr, uint32_t pcShift)
|
||||||
{
|
{
|
||||||
if (!checkcodesection())
|
checkcodesection();
|
||||||
return;
|
reserveSpace(1);
|
||||||
if (!reserveSpace(1))
|
|
||||||
return;
|
|
||||||
struct Symbol const *pc = sym_GetPC();
|
struct Symbol const *pc = sym_GetPC();
|
||||||
|
|
||||||
if (!rpn_IsDiffConstant(expr, pc)) {
|
if (!rpn_IsDiffConstant(expr, pc)) {
|
||||||
@@ -811,14 +744,12 @@ void sect_PCRelByte(struct Expression *expr, uint32_t pcShift)
|
|||||||
/*
|
/*
|
||||||
* Output a binary file
|
* Output a binary file
|
||||||
*/
|
*/
|
||||||
void sect_BinaryFile(char const *s, int32_t startPos)
|
void out_BinaryFile(char const *s, int32_t startPos)
|
||||||
{
|
{
|
||||||
if (startPos < 0) {
|
if (startPos < 0) {
|
||||||
error("Start position cannot be negative (%" PRId32 ")\n", startPos);
|
error("Start position cannot be negative (%" PRId32 ")\n", startPos);
|
||||||
startPos = 0;
|
startPos = 0;
|
||||||
}
|
}
|
||||||
if (!checkcodesection())
|
|
||||||
return;
|
|
||||||
|
|
||||||
char *fullPath = NULL;
|
char *fullPath = NULL;
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
@@ -829,10 +760,10 @@ void sect_BinaryFile(char const *s, int32_t startPos)
|
|||||||
free(fullPath);
|
free(fullPath);
|
||||||
|
|
||||||
if (!f) {
|
if (!f) {
|
||||||
if (generatedMissingIncludes) {
|
if (oGeneratedMissingIncludes) {
|
||||||
if (verbose)
|
if (verbose)
|
||||||
printf("Aborting (-MG) on INCBIN file '%s' (%s)\n", s, strerror(errno));
|
printf("Aborting (-MG) on INCBIN file '%s' (%s)\n", s, strerror(errno));
|
||||||
failedOnMissingInclude = true;
|
oFailedOnMissingInclude = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
error("Error opening INCBIN file '%s': %s\n", s, strerror(errno));
|
error("Error opening INCBIN file '%s': %s\n", s, strerror(errno));
|
||||||
@@ -842,21 +773,22 @@ void sect_BinaryFile(char const *s, int32_t startPos)
|
|||||||
int32_t fsize = -1;
|
int32_t fsize = -1;
|
||||||
int byte;
|
int byte;
|
||||||
|
|
||||||
|
checkcodesection();
|
||||||
if (fseek(f, 0, SEEK_END) != -1) {
|
if (fseek(f, 0, SEEK_END) != -1) {
|
||||||
fsize = ftell(f);
|
fsize = ftell(f);
|
||||||
|
|
||||||
if (startPos > fsize) {
|
if (startPos > fsize) {
|
||||||
error("Specified start position is greater than length of file\n");
|
error("Specified start position is greater than length of file\n");
|
||||||
goto cleanup;
|
fclose(f);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fseek(f, startPos, SEEK_SET);
|
fseek(f, startPos, SEEK_SET);
|
||||||
if (!reserveSpace(fsize - startPos))
|
reserveSpace(fsize - startPos);
|
||||||
goto cleanup;
|
|
||||||
} else {
|
} else {
|
||||||
if (errno != ESPIPE)
|
if (errno != ESPIPE)
|
||||||
error("Error determining size of INCBIN file '%s': %s\n",
|
error("Error determining size of INCBIN file '%s': %s\n",
|
||||||
s, strerror(errno));
|
s, strerror(errno));
|
||||||
/* The file isn't seekable, so we'll just skip bytes */
|
/* The file isn't seekable, so we'll just skip bytes */
|
||||||
while (startPos--)
|
while (startPos--)
|
||||||
(void)fgetc(f);
|
(void)fgetc(f);
|
||||||
@@ -871,11 +803,10 @@ void sect_BinaryFile(char const *s, int32_t startPos)
|
|||||||
if (ferror(f))
|
if (ferror(f))
|
||||||
error("Error reading INCBIN file '%s': %s\n", s, strerror(errno));
|
error("Error reading INCBIN file '%s': %s\n", s, strerror(errno));
|
||||||
|
|
||||||
cleanup:
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sect_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
|
void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
|
||||||
{
|
{
|
||||||
if (start_pos < 0) {
|
if (start_pos < 0) {
|
||||||
error("Start position cannot be negative (%" PRId32 ")\n", start_pos);
|
error("Start position cannot be negative (%" PRId32 ")\n", start_pos);
|
||||||
@@ -886,13 +817,8 @@ void sect_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
|
|||||||
error("Number of bytes to read cannot be negative (%" PRId32 ")\n", length);
|
error("Number of bytes to read cannot be negative (%" PRId32 ")\n", length);
|
||||||
length = 0;
|
length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!checkcodesection())
|
|
||||||
return;
|
|
||||||
if (length == 0) /* Don't even bother with 0-byte slices */
|
if (length == 0) /* Don't even bother with 0-byte slices */
|
||||||
return;
|
return;
|
||||||
if (!reserveSpace(length))
|
|
||||||
return;
|
|
||||||
|
|
||||||
char *fullPath = NULL;
|
char *fullPath = NULL;
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
@@ -900,19 +826,22 @@ void sect_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
|
|||||||
|
|
||||||
if (fstk_FindFile(s, &fullPath, &size))
|
if (fstk_FindFile(s, &fullPath, &size))
|
||||||
f = fopen(fullPath, "rb");
|
f = fopen(fullPath, "rb");
|
||||||
free(fullPath);
|
|
||||||
|
|
||||||
if (!f) {
|
if (!f) {
|
||||||
if (generatedMissingIncludes) {
|
free(fullPath);
|
||||||
|
if (oGeneratedMissingIncludes) {
|
||||||
if (verbose)
|
if (verbose)
|
||||||
printf("Aborting (-MG) on INCBIN file '%s' (%s)\n", s, strerror(errno));
|
printf("Aborting (-MG) on INCBIN file '%s' (%s)\n", s, strerror(errno));
|
||||||
failedOnMissingInclude = true;
|
oFailedOnMissingInclude = true;
|
||||||
} else {
|
return;
|
||||||
error("Error opening INCBIN file '%s': %s\n", s, strerror(errno));
|
|
||||||
}
|
}
|
||||||
|
error("Error opening INCBIN file '%s': %s\n", s, strerror(errno));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkcodesection();
|
||||||
|
reserveSpace(length);
|
||||||
|
|
||||||
int32_t fsize;
|
int32_t fsize;
|
||||||
|
|
||||||
if (fseek(f, 0, SEEK_END) != -1) {
|
if (fseek(f, 0, SEEK_END) != -1) {
|
||||||
@@ -920,13 +849,13 @@ void sect_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
|
|||||||
|
|
||||||
if (start_pos > fsize) {
|
if (start_pos > fsize) {
|
||||||
error("Specified start position is greater than length of file\n");
|
error("Specified start position is greater than length of file\n");
|
||||||
goto cleanup;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((start_pos + length) > fsize) {
|
if ((start_pos + length) > fsize) {
|
||||||
error("Specified range in INCBIN is out of bounds (%" PRIu32 " + %" PRIu32
|
error("Specified range in INCBIN is out of bounds (%" PRIu32 " + %" PRIu32
|
||||||
" > %" PRIu32 ")\n", start_pos, length, fsize);
|
" > %" PRIu32 ")\n", start_pos, length, fsize);
|
||||||
goto cleanup;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fseek(f, start_pos, SEEK_SET);
|
fseek(f, start_pos, SEEK_SET);
|
||||||
@@ -954,34 +883,28 @@ void sect_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup:
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
free(fullPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Section stack routines
|
* Section stack routines
|
||||||
*/
|
*/
|
||||||
void sect_PushSection(void)
|
void out_PushSection(void)
|
||||||
{
|
{
|
||||||
struct SectionStackEntry *sect = malloc(sizeof(*sect));
|
struct SectionStackEntry *sect = malloc(sizeof(*sect));
|
||||||
|
|
||||||
if (sect == NULL)
|
if (sect == NULL)
|
||||||
fatalerror("No memory for section stack: %s\n", strerror(errno));
|
fatalerror("No memory for section stack: %s\n", strerror(errno));
|
||||||
sect->section = currentSection;
|
sect->section = pCurrentSection;
|
||||||
sect->loadSection = currentLoadSection;
|
|
||||||
sect->scope = sym_GetCurrentSymbolScope();
|
sect->scope = sym_GetCurrentSymbolScope();
|
||||||
sect->offset = curOffset;
|
sect->offset = curOffset;
|
||||||
sect->loadOffset = loadOffset;
|
|
||||||
sect->next = sectionStack;
|
sect->next = sectionStack;
|
||||||
sectionStack = sect;
|
sectionStack = sect;
|
||||||
|
/* TODO: maybe set current section to NULL? */
|
||||||
// Reset the section scope
|
|
||||||
currentSection = NULL;
|
|
||||||
currentLoadSection = NULL;
|
|
||||||
sym_SetCurrentSymbolScope(NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sect_PopSection(void)
|
void out_PopSection(void)
|
||||||
{
|
{
|
||||||
if (!sectionStack)
|
if (!sectionStack)
|
||||||
fatalerror("No entries in the section stack\n");
|
fatalerror("No entries in the section stack\n");
|
||||||
@@ -993,11 +916,9 @@ void sect_PopSection(void)
|
|||||||
|
|
||||||
sect = sectionStack;
|
sect = sectionStack;
|
||||||
changeSection();
|
changeSection();
|
||||||
currentSection = sect->section;
|
pCurrentSection = sect->section;
|
||||||
currentLoadSection = sect->loadSection;
|
|
||||||
sym_SetCurrentSymbolScope(sect->scope);
|
sym_SetCurrentSymbolScope(sect->scope);
|
||||||
curOffset = sect->offset;
|
curOffset = sect->offset;
|
||||||
loadOffset = sect->loadOffset;
|
|
||||||
|
|
||||||
sectionStack = sect->next;
|
sectionStack = sect->next;
|
||||||
free(sect);
|
free(sect);
|
||||||
|
|||||||
289
src/asm/symbol.c
289
src/asm/symbol.c
@@ -43,6 +43,12 @@ static char savedTIME[256];
|
|||||||
static char savedDATE[256];
|
static char savedDATE[256];
|
||||||
static char savedTIMESTAMP_ISO8601_LOCAL[256];
|
static char savedTIMESTAMP_ISO8601_LOCAL[256];
|
||||||
static char savedTIMESTAMP_ISO8601_UTC[256];
|
static char savedTIMESTAMP_ISO8601_UTC[256];
|
||||||
|
static char savedDAY[3];
|
||||||
|
static char savedMONTH[3];
|
||||||
|
static char savedYEAR[20];
|
||||||
|
static char savedHOUR[3];
|
||||||
|
static char savedMINUTE[3];
|
||||||
|
static char savedSECOND[3];
|
||||||
static bool exportall;
|
static bool exportall;
|
||||||
|
|
||||||
bool sym_IsPC(struct Symbol const *sym)
|
bool sym_IsPC(struct Symbol const *sym)
|
||||||
@@ -51,16 +57,16 @@ bool sym_IsPC(struct Symbol const *sym)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct ForEachArgs {
|
struct ForEachArgs {
|
||||||
void (*func)(struct Symbol *sym, void *arg);
|
void (*func)(struct Symbol *symbol, void *arg);
|
||||||
void *arg;
|
void *arg;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void forEachWrapper(void *_sym, void *_argWrapper)
|
static void forEachWrapper(void *_symbol, void *_argWrapper)
|
||||||
{
|
{
|
||||||
struct ForEachArgs *argWrapper = _argWrapper;
|
struct ForEachArgs *argWrapper = _argWrapper;
|
||||||
struct Symbol *sym = _sym;
|
struct Symbol *symbol = _symbol;
|
||||||
|
|
||||||
argWrapper->func(sym, argWrapper->arg);
|
argWrapper->func(symbol, argWrapper->arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sym_ForEach(void (*func)(struct Symbol *, void *), void *arg)
|
void sym_ForEach(void (*func)(struct Symbol *, void *), void *arg)
|
||||||
@@ -96,6 +102,8 @@ static char const *Callback__FILE__(void)
|
|||||||
char const *fileName = fstk_GetFileName();
|
char const *fileName = fstk_GetFileName();
|
||||||
size_t j = 1;
|
size_t j = 1;
|
||||||
|
|
||||||
|
/* TODO: is there a way for a file name to be empty? */
|
||||||
|
assert(fileName[0]);
|
||||||
/* The assertion above ensures the loop runs at least once */
|
/* The assertion above ensures the loop runs at least once */
|
||||||
for (size_t i = 0; fileName[i]; i++, j++) {
|
for (size_t i = 0; fileName[i]; i++, j++) {
|
||||||
/* Account for the extra backslash inserted below */
|
/* Account for the extra backslash inserted below */
|
||||||
@@ -173,32 +181,32 @@ static void updateSymbolFilename(struct Symbol *sym)
|
|||||||
/* If the old node was referenced, ensure the new one is */
|
/* If the old node was referenced, ensure the new one is */
|
||||||
if (oldSrc && oldSrc->referenced && oldSrc->ID != (uint32_t)-1)
|
if (oldSrc && oldSrc->referenced && oldSrc->ID != (uint32_t)-1)
|
||||||
out_RegisterNode(sym->src);
|
out_RegisterNode(sym->src);
|
||||||
/* TODO: unref the old node, and use `out_ReplaceNode` instead of deleting it */
|
/* TODO: unref the old node, and use `out_ReplaceNode` instead if deleting it */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a new symbol by name
|
* Create a new symbol by name
|
||||||
*/
|
*/
|
||||||
static struct Symbol *createsymbol(char const *symName)
|
static struct Symbol *createsymbol(char const *s)
|
||||||
{
|
{
|
||||||
struct Symbol *sym = malloc(sizeof(*sym));
|
struct Symbol *symbol = malloc(sizeof(*symbol));
|
||||||
|
|
||||||
if (!sym)
|
if (!symbol)
|
||||||
fatalerror("Failed to create symbol '%s': %s\n", symName, strerror(errno));
|
fatalerror("Failed to create symbol '%s': %s\n", s, strerror(errno));
|
||||||
|
|
||||||
if (snprintf(sym->name, MAXSYMLEN + 1, "%s", symName) > MAXSYMLEN)
|
if (snprintf(symbol->name, MAXSYMLEN + 1, "%s", s) > MAXSYMLEN)
|
||||||
warning(WARNING_LONG_STR, "Symbol name is too long: '%s'\n", symName);
|
warning(WARNING_LONG_STR, "Symbol name is too long: '%s'\n", s);
|
||||||
|
|
||||||
sym->isExported = false;
|
symbol->isExported = false;
|
||||||
sym->isBuiltin = false;
|
symbol->isBuiltin = false;
|
||||||
sym->hasCallback = false;
|
symbol->hasCallback = false;
|
||||||
sym->section = NULL;
|
symbol->section = NULL;
|
||||||
setSymbolFilename(sym);
|
setSymbolFilename(symbol);
|
||||||
sym->ID = -1;
|
symbol->ID = -1;
|
||||||
sym->next = NULL;
|
symbol->next = NULL;
|
||||||
|
|
||||||
hash_AddElement(symbols, sym->name, sym);
|
hash_AddElement(symbols, symbol->name, symbol);
|
||||||
return sym;
|
return symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -220,45 +228,46 @@ static void assignStringSymbol(struct Symbol *sym, char const *value)
|
|||||||
{
|
{
|
||||||
char *string = strdup(value);
|
char *string = strdup(value);
|
||||||
|
|
||||||
if (!string)
|
if (string == NULL)
|
||||||
fatalerror("No memory for string equate: %s\n", strerror(errno));
|
fatalerror("No memory for string equate: %s\n", strerror(errno));
|
||||||
|
|
||||||
sym->type = SYM_EQUS;
|
sym->type = SYM_EQUS;
|
||||||
|
/* TODO: use other fields */
|
||||||
sym->macro = string;
|
sym->macro = string;
|
||||||
sym->macroSize = strlen(string);
|
sym->macroSize = strlen(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Symbol *sym_FindExactSymbol(char const *symName)
|
struct Symbol *sym_FindExactSymbol(char const *name)
|
||||||
{
|
{
|
||||||
return hash_GetElement(symbols, symName);
|
return hash_GetElement(symbols, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Symbol *sym_FindUnscopedSymbol(char const *symName)
|
struct Symbol *sym_FindUnscopedSymbol(char const *name)
|
||||||
{
|
{
|
||||||
if (strchr(symName, '.')) {
|
if (strchr(name, '.')) {
|
||||||
error("Expected non-scoped symbol name, not \"%s\"\n", symName);
|
error("Expected non-scoped symbol name, not \"%s\"\n", name);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return sym_FindExactSymbol(symName);
|
return sym_FindExactSymbol(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Symbol *sym_FindScopedSymbol(char const *symName)
|
struct Symbol *sym_FindScopedSymbol(char const *name)
|
||||||
{
|
{
|
||||||
char const *dotPtr = strchr(symName, '.');
|
char const *dotPtr = strchr(name, '.');
|
||||||
|
|
||||||
if (dotPtr) {
|
if (dotPtr) {
|
||||||
if (strchr(dotPtr + 1, '.'))
|
if (strchr(dotPtr + 1, '.'))
|
||||||
fatalerror("'%s' is a nonsensical reference to a nested local symbol\n",
|
fatalerror("'%s' is a nonsensical reference to a nested local symbol\n",
|
||||||
symName);
|
name);
|
||||||
/* If auto-scoped local label, expand the name */
|
/* If auto-scoped local label, expand the name */
|
||||||
if (dotPtr == symName) { /* Meaning, the name begins with the dot */
|
if (dotPtr == name) { /* Meaning, the name begins with the dot */
|
||||||
char fullname[MAXSYMLEN + 1];
|
char fullname[MAXSYMLEN + 1];
|
||||||
|
|
||||||
fullSymbolName(fullname, sizeof(fullname), symName, labelScope);
|
fullSymbolName(fullname, sizeof(fullname), name, labelScope);
|
||||||
return sym_FindExactSymbol(fullname);
|
return sym_FindExactSymbol(fullname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sym_FindExactSymbol(symName);
|
return sym_FindExactSymbol(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Symbol const *sym_GetPC(void)
|
struct Symbol const *sym_GetPC(void)
|
||||||
@@ -266,7 +275,7 @@ struct Symbol const *sym_GetPC(void)
|
|||||||
return PCSymbol;
|
return PCSymbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isReferenced(struct Symbol const *sym)
|
static inline bool isReferenced(struct Symbol const *sym)
|
||||||
{
|
{
|
||||||
return sym->ID != (uint32_t)-1;
|
return sym->ID != (uint32_t)-1;
|
||||||
}
|
}
|
||||||
@@ -276,26 +285,27 @@ static bool isReferenced(struct Symbol const *sym)
|
|||||||
*/
|
*/
|
||||||
void sym_Purge(char const *symName)
|
void sym_Purge(char const *symName)
|
||||||
{
|
{
|
||||||
struct Symbol *sym = sym_FindScopedSymbol(symName);
|
struct Symbol *symbol = sym_FindScopedSymbol(symName);
|
||||||
|
|
||||||
if (!sym) {
|
if (!symbol) {
|
||||||
error("'%s' not defined\n", symName);
|
error("'%s' not defined\n", symName);
|
||||||
} else if (sym->isBuiltin) {
|
} else if (symbol->isBuiltin) {
|
||||||
error("Built-in symbol '%s' cannot be purged\n", symName);
|
error("Built-in symbol '%s' cannot be purged\n", symName);
|
||||||
} else if (isReferenced(sym)) {
|
} else if (isReferenced(symbol)) {
|
||||||
error("Symbol \"%s\" is referenced and thus cannot be purged\n", symName);
|
error("Symbol \"%s\" is referenced and thus cannot be purged\n", symName);
|
||||||
} else {
|
} else {
|
||||||
/* Do not keep a reference to the label's name after purging it */
|
/* Do not keep a reference to the label's name after purging it */
|
||||||
if (sym->name == labelScope)
|
if (symbol->name == labelScope)
|
||||||
sym_SetCurrentSymbolScope(NULL);
|
labelScope = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* FIXME: this leaks sym->macro for SYM_EQUS and SYM_MACRO, but this can't
|
* FIXME: this leaks symbol->macro for SYM_EQUS and SYM_MACRO, but this can't
|
||||||
* free(sym->macro) because the expansion may be purging itself.
|
* free(symbol->macro) because the expansion may be purging itself.
|
||||||
*/
|
*/
|
||||||
hash_RemoveElement(symbols, sym->name);
|
|
||||||
|
hash_RemoveElement(symbols, symbol->name);
|
||||||
/* TODO: ideally, also unref the file stack nodes */
|
/* TODO: ideally, also unref the file stack nodes */
|
||||||
free(sym);
|
free(symbol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,12 +340,12 @@ uint32_t sym_GetConstantSymValue(struct Symbol const *sym)
|
|||||||
/*
|
/*
|
||||||
* Return a constant symbol's value
|
* Return a constant symbol's value
|
||||||
*/
|
*/
|
||||||
uint32_t sym_GetConstantValue(char const *symName)
|
uint32_t sym_GetConstantValue(char const *s)
|
||||||
{
|
{
|
||||||
struct Symbol const *sym = sym_FindScopedSymbol(symName);
|
struct Symbol const *sym = sym_FindScopedSymbol(s);
|
||||||
|
|
||||||
if (!sym)
|
if (sym == NULL)
|
||||||
error("'%s' not defined\n", symName);
|
error("'%s' not defined\n", s);
|
||||||
else
|
else
|
||||||
return sym_GetConstantSymValue(sym);
|
return sym_GetConstantSymValue(sym);
|
||||||
|
|
||||||
@@ -356,29 +366,28 @@ void sym_SetCurrentSymbolScope(char const *newScope)
|
|||||||
* Create a symbol that will be non-relocatable and ensure that it
|
* Create a symbol that will be non-relocatable and ensure that it
|
||||||
* hasn't already been defined or referenced in a context that would
|
* hasn't already been defined or referenced in a context that would
|
||||||
* require that it be relocatable
|
* require that it be relocatable
|
||||||
* @param symName The name of the symbol to create
|
* @param symbolName The name of the symbol to create
|
||||||
* @param numeric If false, the symbol may not have been referenced earlier
|
* @param numeric If false, the symbol may not have been referenced earlier
|
||||||
*/
|
*/
|
||||||
static struct Symbol *createNonrelocSymbol(char const *symName, bool numeric)
|
static struct Symbol *createNonrelocSymbol(char const *symbolName, bool numeric)
|
||||||
{
|
{
|
||||||
struct Symbol *sym = sym_FindExactSymbol(symName);
|
struct Symbol *symbol = sym_FindExactSymbol(symbolName);
|
||||||
|
|
||||||
if (!sym) {
|
if (!symbol) {
|
||||||
sym = createsymbol(symName);
|
symbol = createsymbol(symbolName);
|
||||||
} else if (sym_IsDefined(sym)) {
|
} else if (sym_IsDefined(symbol)) {
|
||||||
error("'%s' already defined at ", symName);
|
error("'%s' already defined at ", symbolName);
|
||||||
dumpFilename(sym);
|
dumpFilename(symbol);
|
||||||
putc('\n', stderr);
|
putc('\n', stderr);
|
||||||
return NULL; // Don't allow overriding the symbol, that'd be bad!
|
|
||||||
} else if (!numeric) {
|
} else if (!numeric) {
|
||||||
// The symbol has already been referenced, but it's not allowed
|
// The symbol has already been referenced, but it's not allowed
|
||||||
error("'%s' already referenced at ", symName);
|
error("'%s' already referenced at ", symbolName);
|
||||||
dumpFilename(sym);
|
dumpFilename(symbol);
|
||||||
putc('\n', stderr);
|
putc('\n', stderr);
|
||||||
return NULL; // Don't allow overriding the symbol, that'd be bad!
|
return NULL; // Don't allow overriding the symbol, that'd be bad!
|
||||||
}
|
}
|
||||||
|
|
||||||
return sym;
|
return symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -397,30 +406,6 @@ struct Symbol *sym_AddEqu(char const *symName, int32_t value)
|
|||||||
return sym;
|
return sym;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Symbol *sym_RedefEqu(char const *symName, int32_t value)
|
|
||||||
{
|
|
||||||
struct Symbol *sym = sym_FindExactSymbol(symName);
|
|
||||||
|
|
||||||
if (!sym)
|
|
||||||
return sym_AddEqu(symName, value);
|
|
||||||
|
|
||||||
if (sym_IsDefined(sym) && sym->type != SYM_EQU) {
|
|
||||||
error("'%s' already defined as non-EQU at ", symName);
|
|
||||||
dumpFilename(sym);
|
|
||||||
putc('\n', stderr);
|
|
||||||
return NULL;
|
|
||||||
} else if (sym->isBuiltin) {
|
|
||||||
error("Built-in symbol '%s' cannot be redefined\n", symName);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSymbolFilename(sym);
|
|
||||||
sym->type = SYM_EQU;
|
|
||||||
sym->value = value;
|
|
||||||
|
|
||||||
return sym;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a string equated symbol.
|
* Add a string equated symbol.
|
||||||
*
|
*
|
||||||
@@ -448,40 +433,32 @@ struct Symbol *sym_RedefString(char const *symName, char const *value)
|
|||||||
{
|
{
|
||||||
struct Symbol *sym = sym_FindExactSymbol(symName);
|
struct Symbol *sym = sym_FindExactSymbol(symName);
|
||||||
|
|
||||||
if (!sym)
|
if (!sym) {
|
||||||
return sym_AddString(symName, value);
|
sym = createsymbol(symName);
|
||||||
|
} else if (sym->type != SYM_EQUS) {
|
||||||
if (sym->type != SYM_EQUS) {
|
error("'%s' already defined as non-EQUS at ", symName);
|
||||||
if (sym_IsDefined(sym))
|
|
||||||
error("'%s' already defined as non-EQUS at ", symName);
|
|
||||||
else
|
|
||||||
error("'%s' already referenced at ", symName);
|
|
||||||
dumpFilename(sym);
|
dumpFilename(sym);
|
||||||
putc('\n', stderr);
|
putc('\n', stderr);
|
||||||
return NULL;
|
|
||||||
} else if (sym->isBuiltin) {
|
|
||||||
error("Built-in symbol '%s' cannot be redefined\n", symName);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSymbolFilename(sym);
|
|
||||||
/*
|
/*
|
||||||
* FIXME: this leaks the previous sym->macro value, but this can't
|
* FIXME: this leaks the previous sym->macro value, but this can't
|
||||||
* free(sym->macro) because the expansion may be redefining itself.
|
* free(sym->macro) because the expansion may be redefining itself.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
assignStringSymbol(sym, value);
|
assignStringSymbol(sym, value);
|
||||||
|
|
||||||
return sym;
|
return sym;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Alter a SET symbol's value
|
* Alter a SET symbols value
|
||||||
*/
|
*/
|
||||||
struct Symbol *sym_AddSet(char const *symName, int32_t value)
|
struct Symbol *sym_AddSet(char const *symName, int32_t value)
|
||||||
{
|
{
|
||||||
struct Symbol *sym = sym_FindExactSymbol(symName);
|
struct Symbol *sym = sym_FindExactSymbol(symName);
|
||||||
|
|
||||||
if (!sym) {
|
if (sym == NULL) {
|
||||||
sym = createsymbol(symName);
|
sym = createsymbol(symName);
|
||||||
} else if (sym_IsDefined(sym) && sym->type != SYM_SET) {
|
} else if (sym_IsDefined(sym) && sym->type != SYM_SET) {
|
||||||
error("'%s' already defined as %s at ",
|
error("'%s' already defined as %s at ",
|
||||||
@@ -501,18 +478,18 @@ struct Symbol *sym_AddSet(char const *symName, int32_t value)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a label (aka "relocatable symbol")
|
* Add a label (aka "relocatable symbol")
|
||||||
* @param symName The label's full name (so `.name` is invalid)
|
* @param name The label's full name (so `.name` is invalid)
|
||||||
* @return The created symbol
|
* @return The created symbol
|
||||||
*/
|
*/
|
||||||
static struct Symbol *addLabel(char const *symName)
|
static struct Symbol *addLabel(char const *name)
|
||||||
{
|
{
|
||||||
assert(symName[0] != '.'); /* The symbol name must have been expanded prior */
|
assert(name[0] != '.'); /* The symbol name must have been expanded prior */
|
||||||
struct Symbol *sym = sym_FindExactSymbol(symName);
|
struct Symbol *sym = sym_FindExactSymbol(name);
|
||||||
|
|
||||||
if (!sym) {
|
if (!sym) {
|
||||||
sym = createsymbol(symName);
|
sym = createsymbol(name);
|
||||||
} else if (sym_IsDefined(sym)) {
|
} else if (sym_IsDefined(sym)) {
|
||||||
error("'%s' already defined at ", symName);
|
error("'%s' already defined at ", name);
|
||||||
dumpFilename(sym);
|
dumpFilename(sym);
|
||||||
putc('\n', stderr);
|
putc('\n', stderr);
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -527,62 +504,62 @@ static struct Symbol *addLabel(char const *symName)
|
|||||||
sym->section = sect_GetSymbolSection();
|
sym->section = sect_GetSymbolSection();
|
||||||
|
|
||||||
if (sym && !sym->section)
|
if (sym && !sym->section)
|
||||||
error("Label \"%s\" created outside of a SECTION\n", symName);
|
error("Label \"%s\" created outside of a SECTION\n", name);
|
||||||
return sym;
|
return sym;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a local (.name or Parent.name) relocatable symbol
|
* Add a local (.name or Parent.name) relocatable symbol
|
||||||
*/
|
*/
|
||||||
struct Symbol *sym_AddLocalLabel(char const *symName)
|
struct Symbol *sym_AddLocalLabel(char const *name)
|
||||||
{
|
{
|
||||||
if (!labelScope) {
|
if (!labelScope) {
|
||||||
error("Local label '%s' in main scope\n", symName);
|
error("Local label '%s' in main scope\n", name);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
char fullname[MAXSYMLEN + 1];
|
char fullname[MAXSYMLEN + 1];
|
||||||
|
|
||||||
if (symName[0] == '.') {
|
if (name[0] == '.') {
|
||||||
/* If symbol is of the form `.name`, expand to the full `Parent.name` name */
|
/* If symbol is of the form `.name`, expand to the full `Parent.name` name */
|
||||||
fullSymbolName(fullname, sizeof(fullname), symName, labelScope);
|
fullSymbolName(fullname, sizeof(fullname), name, labelScope);
|
||||||
symName = fullname; /* Use the expanded name instead */
|
name = fullname; /* Use the expanded name instead */
|
||||||
} else {
|
} else {
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
|
||||||
/* Otherwise, check that `Parent` is in fact the current scope */
|
/* Otherwise, check that `Parent` is in fact the current scope */
|
||||||
while (labelScope[i] && symName[i] == labelScope[i])
|
while (labelScope[i] && name[i] == labelScope[i])
|
||||||
i++;
|
i++;
|
||||||
/* Assuming no dots in `labelScope` */
|
/* Assuming no dots in `labelScope` */
|
||||||
assert(strchr(&symName[i], '.')); /* There should be at least one dot, though */
|
assert(strchr(&name[i], '.')); /* There should be at least one dot, though */
|
||||||
size_t parentLen = i + (strchr(&symName[i], '.') - symName);
|
size_t parentLen = i + (strchr(&name[i], '.') - name);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check that `labelScope[i]` ended the check, guaranteeing that `symName` is at
|
* Check that `labelScope[i]` ended the check, guaranteeing that `name` is at least
|
||||||
* least as long, and then that this was the entire `Parent` part of `symName`.
|
* as long, and then that this was the entirety of the `Parent` part of `name`.
|
||||||
*/
|
*/
|
||||||
if (labelScope[i] != '\0' || symName[i] != '.') {
|
if (labelScope[i] != '\0' || name[i] != '.') {
|
||||||
assert(parentLen <= INT_MAX);
|
assert(parentLen <= INT_MAX);
|
||||||
error("Not currently in the scope of '%.*s'\n", (int)parentLen, symName);
|
error("Not currently in the scope of '%.*s'\n", (int)parentLen, name);
|
||||||
}
|
}
|
||||||
if (strchr(&symName[parentLen + 1], '.')) /* There will at least be a terminator */
|
if (strchr(&name[parentLen + 1], '.')) /* There will at least be a terminator */
|
||||||
fatalerror("'%s' is a nonsensical reference to a nested local label\n",
|
fatalerror("'%s' is a nonsensical reference to a nested local label\n",
|
||||||
symName);
|
name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return addLabel(symName);
|
return addLabel(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a relocatable symbol
|
* Add a relocatable symbol
|
||||||
*/
|
*/
|
||||||
struct Symbol *sym_AddLabel(char const *symName)
|
struct Symbol *sym_AddLabel(char const *name)
|
||||||
{
|
{
|
||||||
struct Symbol *sym = addLabel(symName);
|
struct Symbol *sym = addLabel(name);
|
||||||
|
|
||||||
/* Set the symbol as the new scope */
|
/* Set the symbol as the new scope */
|
||||||
if (sym)
|
if (sym)
|
||||||
sym_SetCurrentSymbolScope(sym->name);
|
labelScope = sym->name;
|
||||||
return sym;
|
return sym;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -677,9 +654,9 @@ struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body,
|
|||||||
*/
|
*/
|
||||||
struct Symbol *sym_Ref(char const *symName)
|
struct Symbol *sym_Ref(char const *symName)
|
||||||
{
|
{
|
||||||
struct Symbol *sym = sym_FindScopedSymbol(symName);
|
struct Symbol *nsym = sym_FindScopedSymbol(symName);
|
||||||
|
|
||||||
if (!sym) {
|
if (nsym == NULL) {
|
||||||
char fullname[MAXSYMLEN + 1];
|
char fullname[MAXSYMLEN + 1];
|
||||||
|
|
||||||
if (symName[0] == '.') {
|
if (symName[0] == '.') {
|
||||||
@@ -689,11 +666,11 @@ struct Symbol *sym_Ref(char const *symName)
|
|||||||
symName = fullname;
|
symName = fullname;
|
||||||
}
|
}
|
||||||
|
|
||||||
sym = createsymbol(symName);
|
nsym = createsymbol(symName);
|
||||||
sym->type = SYM_REF;
|
nsym->type = SYM_REF;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sym;
|
return nsym;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -704,9 +681,20 @@ void sym_SetExportAll(bool set)
|
|||||||
exportall = set;
|
exportall = set;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct Symbol *createBuiltinSymbol(char const *symName)
|
/**
|
||||||
|
* Returns a pointer to the first non-zero character in a string
|
||||||
|
* Non-'0', not non-'\0'.
|
||||||
|
*/
|
||||||
|
static inline char const *removeLeadingZeros(char const *ptr)
|
||||||
{
|
{
|
||||||
struct Symbol *sym = createsymbol(symName);
|
while (*ptr == '0')
|
||||||
|
ptr++;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct Symbol *createBuiltinSymbol(char const *name)
|
||||||
|
{
|
||||||
|
struct Symbol *sym = createsymbol(name);
|
||||||
|
|
||||||
sym->isBuiltin = true;
|
sym->isBuiltin = true;
|
||||||
sym->hasCallback = true;
|
sym->hasCallback = true;
|
||||||
@@ -734,18 +722,13 @@ void sym_Init(time_t now)
|
|||||||
__LINE__Symbol->numCallback = Callback__LINE__;
|
__LINE__Symbol->numCallback = Callback__LINE__;
|
||||||
__FILE__Symbol->type = SYM_EQUS;
|
__FILE__Symbol->type = SYM_EQUS;
|
||||||
__FILE__Symbol->strCallback = Callback__FILE__;
|
__FILE__Symbol->strCallback = Callback__FILE__;
|
||||||
|
|
||||||
sym_AddSet("_RS", 0)->isBuiltin = true;
|
sym_AddSet("_RS", 0)->isBuiltin = true;
|
||||||
|
|
||||||
#define addNumber(name, val) sym_AddEqu(name, val)->isBuiltin = true
|
sym_AddEqu("__RGBDS_MAJOR__", PACKAGE_VERSION_MAJOR)->isBuiltin = true;
|
||||||
#define addString(name, val) sym_AddString(name, val)->isBuiltin = true
|
sym_AddEqu("__RGBDS_MINOR__", PACKAGE_VERSION_MINOR)->isBuiltin = true;
|
||||||
|
sym_AddEqu("__RGBDS_PATCH__", PACKAGE_VERSION_PATCH)->isBuiltin = true;
|
||||||
addString("__RGBDS_VERSION__", get_package_version_string());
|
|
||||||
addNumber("__RGBDS_MAJOR__", PACKAGE_VERSION_MAJOR);
|
|
||||||
addNumber("__RGBDS_MINOR__", PACKAGE_VERSION_MINOR);
|
|
||||||
addNumber("__RGBDS_PATCH__", PACKAGE_VERSION_PATCH);
|
|
||||||
#ifdef PACKAGE_VERSION_RC
|
#ifdef PACKAGE_VERSION_RC
|
||||||
addNumber("__RGBDS_RC__", PACKAGE_VERSION_RC);
|
sym_AddEqu("__RGBDS_RC__", PACKAGE_VERSION_RC)->isBuiltin = true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (now == (time_t)-1) {
|
if (now == (time_t)-1) {
|
||||||
@@ -767,22 +750,28 @@ void sym_Init(time_t now)
|
|||||||
sizeof(savedTIMESTAMP_ISO8601_UTC), "\"%Y-%m-%dT%H:%M:%SZ\"",
|
sizeof(savedTIMESTAMP_ISO8601_UTC), "\"%Y-%m-%dT%H:%M:%SZ\"",
|
||||||
time_utc);
|
time_utc);
|
||||||
|
|
||||||
|
strftime(savedYEAR, sizeof(savedYEAR), "%Y", time_utc);
|
||||||
|
strftime(savedMONTH, sizeof(savedMONTH), "%m", time_utc);
|
||||||
|
strftime(savedDAY, sizeof(savedDAY), "%d", time_utc);
|
||||||
|
strftime(savedHOUR, sizeof(savedHOUR), "%H", time_utc);
|
||||||
|
strftime(savedMINUTE, sizeof(savedMINUTE), "%M", time_utc);
|
||||||
|
strftime(savedSECOND, sizeof(savedSECOND), "%S", time_utc);
|
||||||
|
|
||||||
|
#define addString(name, val) sym_AddString(name, val)->isBuiltin = true
|
||||||
addString("__TIME__", savedTIME);
|
addString("__TIME__", savedTIME);
|
||||||
addString("__DATE__", savedDATE);
|
addString("__DATE__", savedDATE);
|
||||||
addString("__ISO_8601_LOCAL__", savedTIMESTAMP_ISO8601_LOCAL);
|
addString("__ISO_8601_LOCAL__", savedTIMESTAMP_ISO8601_LOCAL);
|
||||||
addString("__ISO_8601_UTC__", savedTIMESTAMP_ISO8601_UTC);
|
addString("__ISO_8601_UTC__", savedTIMESTAMP_ISO8601_UTC);
|
||||||
|
/* This cannot start with zeros */
|
||||||
addNumber("__UTC_YEAR__", time_utc->tm_year + 1900);
|
addString("__UTC_YEAR__", savedYEAR);
|
||||||
addNumber("__UTC_MONTH__", time_utc->tm_mon + 1);
|
addString("__UTC_MONTH__", removeLeadingZeros(savedMONTH));
|
||||||
addNumber("__UTC_DAY__", time_utc->tm_mday);
|
addString("__UTC_DAY__", removeLeadingZeros(savedDAY));
|
||||||
addNumber("__UTC_HOUR__", time_utc->tm_hour);
|
addString("__UTC_HOUR__", removeLeadingZeros(savedHOUR));
|
||||||
addNumber("__UTC_MINUTE__", time_utc->tm_min);
|
addString("__UTC_MINUTE__", removeLeadingZeros(savedMINUTE));
|
||||||
addNumber("__UTC_SECOND__", time_utc->tm_sec);
|
addString("__UTC_SECOND__", removeLeadingZeros(savedSECOND));
|
||||||
|
|
||||||
#undef addNumber
|
|
||||||
#undef addString
|
#undef addString
|
||||||
|
|
||||||
sym_SetCurrentSymbolScope(NULL);
|
labelScope = NULL;
|
||||||
anonLabelID = 0;
|
anonLabelID = 0;
|
||||||
|
|
||||||
/* _PI is deprecated */
|
/* _PI is deprecated */
|
||||||
|
|||||||
@@ -15,45 +15,52 @@
|
|||||||
|
|
||||||
#include "extern/utf8decoder.h"
|
#include "extern/utf8decoder.h"
|
||||||
|
|
||||||
char const *printChar(int c)
|
/*
|
||||||
|
* Calculate the hash value for a string.
|
||||||
|
* Uses the djb2 algorithm (xor version).
|
||||||
|
* http://www.cse.yorku.ca/~oz/hash.html
|
||||||
|
*/
|
||||||
|
uint32_t calchash(const char *s)
|
||||||
{
|
{
|
||||||
// "'A'" + '\0': 4 bytes
|
uint32_t hash = 5381;
|
||||||
// "'\\n'" + '\0': 5 bytes
|
|
||||||
// "0xFF" + '\0': 5 bytes
|
while (*s != 0)
|
||||||
static char buf[5];
|
hash = (hash * 33) ^ (*s++);
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
char const *print(int c)
|
||||||
|
{
|
||||||
|
static char buf[5]; /* '\xNN' + '\0' */
|
||||||
|
|
||||||
if (c == EOF)
|
if (c == EOF)
|
||||||
return "EOF";
|
return "EOF";
|
||||||
|
|
||||||
if (isprint(c)) {
|
if (isprint(c)) {
|
||||||
buf[0] = '\'';
|
buf[0] = c;
|
||||||
buf[1] = c;
|
buf[1] = '\0';
|
||||||
buf[2] = '\'';
|
|
||||||
buf[3] = '\0';
|
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buf[0] = '\\';
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '\n':
|
case '\n':
|
||||||
buf[2] = 'n';
|
buf[1] = 'n';
|
||||||
break;
|
break;
|
||||||
case '\r':
|
case '\r':
|
||||||
buf[2] = 'r';
|
buf[1] = 'r';
|
||||||
break;
|
break;
|
||||||
case '\t':
|
case '\t':
|
||||||
buf[2] = 't';
|
buf[1] = 't';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: /* Print as hex */
|
default: /* Print as hex */
|
||||||
buf[0] = '0';
|
|
||||||
buf[1] = 'x';
|
buf[1] = 'x';
|
||||||
snprintf(&buf[2], 3, "%02hhX", (uint8_t)c); // includes the '\0'
|
sprintf(&buf[2], "%02hhx", (uint8_t)c);
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
buf[0] = '\'';
|
buf[2] = '\0';
|
||||||
buf[1] = '\\';
|
|
||||||
buf[3] = '\'';
|
|
||||||
buf[4] = '\0';
|
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,8 +74,7 @@ size_t readUTF8Char(uint8_t *dest, char const *src)
|
|||||||
if (decode(&state, &codep, src[i]) == 1)
|
if (decode(&state, &codep, src[i]) == 1)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (dest)
|
dest[i] = src[i];
|
||||||
dest[i] = src[i];
|
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
if (state == 0)
|
if (state == 0)
|
||||||
|
|||||||
@@ -21,9 +21,15 @@
|
|||||||
|
|
||||||
unsigned int nbErrors = 0;
|
unsigned int nbErrors = 0;
|
||||||
|
|
||||||
|
enum WarningState {
|
||||||
|
WARNING_DEFAULT,
|
||||||
|
WARNING_DISABLED,
|
||||||
|
WARNING_ENABLED,
|
||||||
|
WARNING_ERROR
|
||||||
|
};
|
||||||
|
|
||||||
static enum WarningState const defaultWarnings[NB_WARNINGS] = {
|
static enum WarningState const defaultWarnings[NB_WARNINGS] = {
|
||||||
[WARNING_ASSERT] = WARNING_ENABLED,
|
[WARNING_ASSERT] = WARNING_ENABLED,
|
||||||
[WARNING_BACKWARDS_FOR] = WARNING_DISABLED,
|
|
||||||
[WARNING_BUILTIN_ARG] = WARNING_DISABLED,
|
[WARNING_BUILTIN_ARG] = WARNING_DISABLED,
|
||||||
[WARNING_CHARMAP_REDEF] = WARNING_DISABLED,
|
[WARNING_CHARMAP_REDEF] = WARNING_DISABLED,
|
||||||
[WARNING_DIV] = WARNING_DISABLED,
|
[WARNING_DIV] = WARNING_DISABLED,
|
||||||
@@ -41,9 +47,9 @@ static enum WarningState const defaultWarnings[NB_WARNINGS] = {
|
|||||||
[WARNING_USER] = WARNING_ENABLED,
|
[WARNING_USER] = WARNING_ENABLED,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum WarningState warningStates[NB_WARNINGS];
|
static enum WarningState warningStates[NB_WARNINGS];
|
||||||
|
|
||||||
bool warningsAreErrors; /* Set if `-Werror` was specified */
|
static bool warningsAreErrors; /* Set if `-Werror` was specified */
|
||||||
|
|
||||||
static enum WarningState warningState(enum WarningID id)
|
static enum WarningState warningState(enum WarningID id)
|
||||||
{
|
{
|
||||||
@@ -66,7 +72,6 @@ static enum WarningState warningState(enum WarningID id)
|
|||||||
|
|
||||||
static char const *warningFlags[NB_WARNINGS_ALL] = {
|
static char const *warningFlags[NB_WARNINGS_ALL] = {
|
||||||
"assert",
|
"assert",
|
||||||
"backwards-for",
|
|
||||||
"builtin-args",
|
"builtin-args",
|
||||||
"charmap-redef",
|
"charmap-redef",
|
||||||
"div",
|
"div",
|
||||||
@@ -95,7 +100,6 @@ enum MetaWarningCommand {
|
|||||||
|
|
||||||
/* Warnings that probably indicate an error */
|
/* Warnings that probably indicate an error */
|
||||||
static uint8_t const _wallCommands[] = {
|
static uint8_t const _wallCommands[] = {
|
||||||
WARNING_BACKWARDS_FOR,
|
|
||||||
WARNING_BUILTIN_ARG,
|
WARNING_BUILTIN_ARG,
|
||||||
WARNING_CHARMAP_REDEF,
|
WARNING_CHARMAP_REDEF,
|
||||||
WARNING_EMPTY_DATA_DIRECTIVE,
|
WARNING_EMPTY_DATA_DIRECTIVE,
|
||||||
@@ -115,7 +119,6 @@ static uint8_t const _wextraCommands[] = {
|
|||||||
|
|
||||||
/* Literally everything. Notably useful for testing */
|
/* Literally everything. Notably useful for testing */
|
||||||
static uint8_t const _weverythingCommands[] = {
|
static uint8_t const _weverythingCommands[] = {
|
||||||
WARNING_BACKWARDS_FOR,
|
|
||||||
WARNING_BUILTIN_ARG,
|
WARNING_BUILTIN_ARG,
|
||||||
WARNING_DIV,
|
WARNING_DIV,
|
||||||
WARNING_EMPTY_DATA_DIRECTIVE,
|
WARNING_EMPTY_DATA_DIRECTIVE,
|
||||||
@@ -152,7 +155,8 @@ void processWarningFlag(char const *flag)
|
|||||||
errx(1, "Cannot make meta warning \"%s\" into an error",
|
errx(1, "Cannot make meta warning \"%s\" into an error",
|
||||||
flag);
|
flag);
|
||||||
|
|
||||||
uint8_t const *ptr = metaWarningCommands[id - NB_WARNINGS];
|
uint8_t const *ptr =
|
||||||
|
metaWarningCommands[id - NB_WARNINGS];
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (*ptr == META_WARNING_DONE)
|
if (*ptr == META_WARNING_DONE)
|
||||||
@@ -193,7 +197,7 @@ void processWarningFlag(char const *flag)
|
|||||||
bool isNegation = !strncmp(flag, "no-", strlen("no-")) && !setError;
|
bool isNegation = !strncmp(flag, "no-", strlen("no-")) && !setError;
|
||||||
char const *rootFlag = isNegation ? flag + strlen("no-") : flag;
|
char const *rootFlag = isNegation ? flag + strlen("no-") : flag;
|
||||||
enum WarningState state = setError ? WARNING_ERROR :
|
enum WarningState state = setError ? WARNING_ERROR :
|
||||||
isNegation ? WARNING_DISABLED : WARNING_ENABLED;
|
isNegation ? WARNING_DISABLED : WARNING_ENABLED;
|
||||||
|
|
||||||
/* Try to match the flag against a "normal" flag */
|
/* Try to match the flag against a "normal" flag */
|
||||||
for (enum WarningID id = 0; id < NB_WARNINGS; id++) {
|
for (enum WarningID id = 0; id < NB_WARNINGS; id++) {
|
||||||
|
|||||||
360
src/fix/main.c
360
src/fix/main.c
@@ -25,7 +25,7 @@
|
|||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
#define UNSPECIFIED 0x200 // Should not be in byte range
|
#define UNSPECIFIED 0x100 // May not be in byte range
|
||||||
|
|
||||||
#define BANK_SIZE 0x4000
|
#define BANK_SIZE 0x4000
|
||||||
|
|
||||||
@@ -79,20 +79,6 @@ static void printUsage(void)
|
|||||||
stderr);
|
stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t nbErrors;
|
|
||||||
|
|
||||||
static format_(printf, 1, 2) void report(char const *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
vfprintf(stderr, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
if (nbErrors != UINT8_MAX)
|
|
||||||
nbErrors++;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum MbcType {
|
enum MbcType {
|
||||||
ROM = 0x00,
|
ROM = 0x00,
|
||||||
ROM_RAM = 0x08,
|
ROM_RAM = 0x08,
|
||||||
@@ -134,28 +120,6 @@ enum MbcType {
|
|||||||
|
|
||||||
HUC1_RAM_BATTERY = 0xFF,
|
HUC1_RAM_BATTERY = 0xFF,
|
||||||
|
|
||||||
// "Extended" values (still valid, but not directly actionable)
|
|
||||||
|
|
||||||
// A high byte of 0x01 means TPP1, the low byte is the requested features
|
|
||||||
// This does not include SRAM, which is instead implied by a non-zero SRAM size
|
|
||||||
// Note: Multiple rumble speeds imply rumble
|
|
||||||
TPP1 = 0x100,
|
|
||||||
TPP1_RUMBLE = 0x101,
|
|
||||||
TPP1_MULTIRUMBLE = 0x102, // Should not be possible
|
|
||||||
TPP1_MULTIRUMBLE_RUMBLE = 0x103,
|
|
||||||
TPP1_RTC = 0x104,
|
|
||||||
TPP1_RTC_RUMBLE = 0x105,
|
|
||||||
TPP1_RTC_MULTIRUMBLE = 0x106, // Should not be possible
|
|
||||||
TPP1_RTC_MULTIRUMBLE_RUMBLE = 0x107,
|
|
||||||
TPP1_BATTERY = 0x108,
|
|
||||||
TPP1_BATTERY_RUMBLE = 0x109,
|
|
||||||
TPP1_BATTERY_MULTIRUMBLE = 0x10a, // Should not be possible
|
|
||||||
TPP1_BATTERY_MULTIRUMBLE_RUMBLE = 0x10b,
|
|
||||||
TPP1_BATTERY_RTC = 0x10c,
|
|
||||||
TPP1_BATTERY_RTC_RUMBLE = 0x10d,
|
|
||||||
TPP1_BATTERY_RTC_MULTIRUMBLE = 0x10e, // Should not be possible
|
|
||||||
TPP1_BATTERY_RTC_MULTIRUMBLE_RUMBLE = 0x10f,
|
|
||||||
|
|
||||||
// Error values
|
// Error values
|
||||||
MBC_NONE = UNSPECIFIED, // No MBC specified, do not act on it
|
MBC_NONE = UNSPECIFIED, // No MBC specified, do not act on it
|
||||||
MBC_BAD, // Specified MBC does not exist / syntax error
|
MBC_BAD, // Specified MBC does not exist / syntax error
|
||||||
@@ -163,33 +127,6 @@ enum MbcType {
|
|||||||
MBC_BAD_RANGE, // MBC number out of range
|
MBC_BAD_RANGE, // MBC number out of range
|
||||||
};
|
};
|
||||||
|
|
||||||
static void printAcceptedMBCNames(void)
|
|
||||||
{
|
|
||||||
fputs("\tROM ($00) [aka ROM_ONLY]\n", stderr);
|
|
||||||
fputs("\tMBC1 ($02), MBC1+RAM ($02), MBC1+RAM+BATTERY ($03)\n", stderr);
|
|
||||||
fputs("\tMBC2 ($05), MBC2+BATTERY ($06)\n", stderr);
|
|
||||||
fputs("\tROM+RAM ($08) [deprecated], ROM+RAM+BATTERY ($09) [deprecated]\n", stderr);
|
|
||||||
fputs("\tMMM01 ($0B), MMM01+RAM ($0C), MMM01+RAM+BATTERY ($0D)\n", stderr);
|
|
||||||
fputs("\tMBC3+TIMER+BATTERY ($0F), MBC3+TIMER+RAM+BATTERY ($10)\n", stderr);
|
|
||||||
fputs("\tMBC3 ($11), MBC3+RAM ($12), MBC3+RAM+BATTERY ($13)\n", stderr);
|
|
||||||
fputs("\tMBC5 ($19), MBC5+RAM ($1A), MBC5+RAM+BATTERY ($1B)\n", stderr);
|
|
||||||
fputs("\tMBC5+RUMBLE ($1C), MBC5+RUMBLE+RAM ($1D), MBC5+RUMBLE+RAM+BATTERY ($1E)\n", stderr);
|
|
||||||
fputs("\tMBC6 ($20)\n", stderr);
|
|
||||||
fputs("\tMBC7+SENSOR+RUMBLE+RAM+BATTERY ($22)\n", stderr);
|
|
||||||
fputs("\tPOCKET_CAMERA ($FC)\n", stderr);
|
|
||||||
fputs("\tBANDAI_TAMA5 ($FD)\n", stderr);
|
|
||||||
fputs("\tHUC3 ($FE)\n", stderr);
|
|
||||||
fputs("\tHUC1+RAM+BATTERY ($FF)\n", stderr);
|
|
||||||
|
|
||||||
fputs("\n\tTPP1_1.0, TPP1_1.0+RUMBLE, TPP1_1.0+MULTIRUMBLE, TPP1_1.0+RTC,\n", stderr);
|
|
||||||
fputs("\tTPP1_1.0+RTC+RUMBLE, TPP1_1.0+RTC+MULTIRUMBLE, TPP1_1.0+BATTERY,\n", stderr);
|
|
||||||
fputs("\tTPP1_1.0+BATTERY+RUMBLE, TPP1_1.0+BATTERY+MULTIRUMBLE,\n", stderr);
|
|
||||||
fputs("\tTPP1_1.0+BATTERY+RTC, TPP1_1.0+BATTERY+RTC+RUMBLE,\n", stderr);
|
|
||||||
fputs("\tTPP1_1.0+BATTERY+RTC+MULTIRUMBLE\n", stderr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t tpp1Rev[2];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return False on failure
|
* @return False on failure
|
||||||
*/
|
*/
|
||||||
@@ -214,22 +151,10 @@ static bool readMBCSlice(char const **name, char const *expected)
|
|||||||
|
|
||||||
static enum MbcType parseMBC(char const *name)
|
static enum MbcType parseMBC(char const *name)
|
||||||
{
|
{
|
||||||
if (!strcasecmp(name, "help")) {
|
if (name[0] >= '0' && name[0] <= '9') {
|
||||||
fputs("Accepted MBC names:\n", stderr);
|
|
||||||
printAcceptedMBCNames();
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((name[0] >= '0' && name[0] <= '9') || name[0] == '$') {
|
|
||||||
int base = 0;
|
|
||||||
|
|
||||||
if (name[0] == '$') {
|
|
||||||
name++;
|
|
||||||
base = 16;
|
|
||||||
}
|
|
||||||
// Parse number, and return it as-is (unless it's too large)
|
// Parse number, and return it as-is (unless it's too large)
|
||||||
char *endptr;
|
char *endptr;
|
||||||
unsigned long mbc = strtoul(name, &endptr, base);
|
unsigned long mbc = strtoul(name, &endptr, 0);
|
||||||
|
|
||||||
if (*endptr)
|
if (*endptr)
|
||||||
return MBC_BAD;
|
return MBC_BAD;
|
||||||
@@ -323,50 +248,10 @@ do { \
|
|||||||
mbc = BANDAI_TAMA5;
|
mbc = BANDAI_TAMA5;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'T': // TAMA5 / TPP1
|
case 'T': // TAMA5
|
||||||
case 't':
|
case 't':
|
||||||
switch (*ptr++) {
|
tryReadSlice("AMA5");
|
||||||
case 'A':
|
mbc = BANDAI_TAMA5;
|
||||||
tryReadSlice("MA5");
|
|
||||||
mbc = BANDAI_TAMA5;
|
|
||||||
break;
|
|
||||||
case 'P':
|
|
||||||
tryReadSlice("P1");
|
|
||||||
// Parse version
|
|
||||||
while (*ptr == ' ' || *ptr == '_')
|
|
||||||
ptr++;
|
|
||||||
// Major
|
|
||||||
char *endptr;
|
|
||||||
unsigned long val = strtoul(ptr, &endptr, 10);
|
|
||||||
|
|
||||||
if (endptr == ptr) {
|
|
||||||
report("error: Failed to parse TPP1 major revision number\n");
|
|
||||||
return MBC_BAD;
|
|
||||||
}
|
|
||||||
ptr = endptr;
|
|
||||||
if (val != 1) {
|
|
||||||
report("error: RGBFIX only supports TPP1 versions 1.0\n");
|
|
||||||
return MBC_BAD;
|
|
||||||
}
|
|
||||||
tpp1Rev[0] = val;
|
|
||||||
tryReadSlice(".");
|
|
||||||
// Minor
|
|
||||||
val = strtoul(ptr, &endptr, 10);
|
|
||||||
if (endptr == ptr) {
|
|
||||||
report("error: Failed to parse TPP1 minor revision number\n");
|
|
||||||
return MBC_BAD;
|
|
||||||
}
|
|
||||||
ptr = endptr;
|
|
||||||
if (val > 0xFF) {
|
|
||||||
report("error: TPP1 minor revision number must be 8-bit\n");
|
|
||||||
return MBC_BAD;
|
|
||||||
}
|
|
||||||
tpp1Rev[1] = val;
|
|
||||||
mbc = TPP1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return MBC_BAD;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'H': // HuC{1, 3}
|
case 'H': // HuC{1, 3}
|
||||||
@@ -395,7 +280,6 @@ do { \
|
|||||||
#define TIMER 0x20
|
#define TIMER 0x20
|
||||||
#define RUMBLE 0x10
|
#define RUMBLE 0x10
|
||||||
#define SENSOR 0x08
|
#define SENSOR 0x08
|
||||||
#define MULTIRUMBLE 0x04
|
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
// Trim off trailing whitespace
|
// Trim off trailing whitespace
|
||||||
@@ -419,12 +303,6 @@ do { \
|
|||||||
features |= BATTERY;
|
features |= BATTERY;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'M':
|
|
||||||
case 'm':
|
|
||||||
tryReadSlice("ULTIRUMBLE");
|
|
||||||
features |= MULTIRUMBLE;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'R': // RAM or RUMBLE
|
case 'R': // RAM or RUMBLE
|
||||||
case 'r':
|
case 'r':
|
||||||
switch (*ptr++) {
|
switch (*ptr++) {
|
||||||
@@ -546,22 +424,6 @@ do { \
|
|||||||
if (features != (RAM | BATTERY)) // HuC1 expects RAM+BATTERY
|
if (features != (RAM | BATTERY)) // HuC1 expects RAM+BATTERY
|
||||||
return MBC_WRONG_FEATURES;
|
return MBC_WRONG_FEATURES;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TPP1:
|
|
||||||
if (features & RAM)
|
|
||||||
fprintf(stderr,
|
|
||||||
"warning: TPP1 requests RAM implicitly if given a non-zero RAM size");
|
|
||||||
if (features & BATTERY)
|
|
||||||
mbc |= 0x08;
|
|
||||||
if (features & TIMER)
|
|
||||||
mbc |= 0x04;
|
|
||||||
if (features & MULTIRUMBLE)
|
|
||||||
mbc |= 0x03; // Also set the rumble flag
|
|
||||||
if (features & RUMBLE)
|
|
||||||
mbc |= 0x01;
|
|
||||||
if (features & SENSOR)
|
|
||||||
return MBC_WRONG_FEATURES;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trim off trailing whitespace
|
// Trim off trailing whitespace
|
||||||
@@ -635,34 +497,6 @@ static char const *mbcName(enum MbcType type)
|
|||||||
return "HUC3";
|
return "HUC3";
|
||||||
case HUC1_RAM_BATTERY:
|
case HUC1_RAM_BATTERY:
|
||||||
return "HUC1+RAM+BATTERY";
|
return "HUC1+RAM+BATTERY";
|
||||||
case TPP1:
|
|
||||||
return "TPP1";
|
|
||||||
case TPP1_RUMBLE:
|
|
||||||
return "TPP1+RUMBLE";
|
|
||||||
case TPP1_MULTIRUMBLE:
|
|
||||||
case TPP1_MULTIRUMBLE_RUMBLE:
|
|
||||||
return "TPP1+MULTIRUMBLE";
|
|
||||||
case TPP1_RTC:
|
|
||||||
return "TPP1+RTC";
|
|
||||||
case TPP1_RTC_RUMBLE:
|
|
||||||
return "TPP1+RTC+RUMBLE";
|
|
||||||
case TPP1_RTC_MULTIRUMBLE:
|
|
||||||
case TPP1_RTC_MULTIRUMBLE_RUMBLE:
|
|
||||||
return "TPP1+RTC+MULTIRUMBLE";
|
|
||||||
case TPP1_BATTERY:
|
|
||||||
return "TPP1+BATTERY";
|
|
||||||
case TPP1_BATTERY_RUMBLE:
|
|
||||||
return "TPP1+BATTERY+RUMBLE";
|
|
||||||
case TPP1_BATTERY_MULTIRUMBLE:
|
|
||||||
case TPP1_BATTERY_MULTIRUMBLE_RUMBLE:
|
|
||||||
return "TPP1+BATTERY+MULTIRUMBLE";
|
|
||||||
case TPP1_BATTERY_RTC:
|
|
||||||
return "TPP1+BATTERY+RTC";
|
|
||||||
case TPP1_BATTERY_RTC_RUMBLE:
|
|
||||||
return "TPP1+BATTERY+RTC+RUMBLE";
|
|
||||||
case TPP1_BATTERY_RTC_MULTIRUMBLE:
|
|
||||||
case TPP1_BATTERY_RTC_MULTIRUMBLE_RUMBLE:
|
|
||||||
return "TPP1+BATTERY+RTC+MULTIRUMBLE";
|
|
||||||
|
|
||||||
// Error values
|
// Error values
|
||||||
case MBC_NONE:
|
case MBC_NONE:
|
||||||
@@ -713,30 +547,25 @@ static bool hasRAM(enum MbcType type)
|
|||||||
case HUC3:
|
case HUC3:
|
||||||
case HUC1_RAM_BATTERY:
|
case HUC1_RAM_BATTERY:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// TPP1 may or may not have RAM, don't call this function for it
|
|
||||||
case TPP1:
|
|
||||||
case TPP1_RUMBLE:
|
|
||||||
case TPP1_MULTIRUMBLE:
|
|
||||||
case TPP1_MULTIRUMBLE_RUMBLE:
|
|
||||||
case TPP1_RTC:
|
|
||||||
case TPP1_RTC_RUMBLE:
|
|
||||||
case TPP1_RTC_MULTIRUMBLE:
|
|
||||||
case TPP1_RTC_MULTIRUMBLE_RUMBLE:
|
|
||||||
case TPP1_BATTERY:
|
|
||||||
case TPP1_BATTERY_RUMBLE:
|
|
||||||
case TPP1_BATTERY_MULTIRUMBLE:
|
|
||||||
case TPP1_BATTERY_MULTIRUMBLE_RUMBLE:
|
|
||||||
case TPP1_BATTERY_RTC:
|
|
||||||
case TPP1_BATTERY_RTC_RUMBLE:
|
|
||||||
case TPP1_BATTERY_RTC_MULTIRUMBLE:
|
|
||||||
case TPP1_BATTERY_RTC_MULTIRUMBLE_RUMBLE:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unreachable_();
|
unreachable_();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t nbErrors;
|
||||||
|
|
||||||
|
static format_(printf, 1, 2) void report(char const *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
if (nbErrors != UINT8_MAX)
|
||||||
|
nbErrors++;
|
||||||
|
}
|
||||||
|
|
||||||
static const uint8_t ninLogo[] = {
|
static const uint8_t ninLogo[] = {
|
||||||
0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B,
|
0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B,
|
||||||
0x03, 0x73, 0x00, 0x83, 0x00, 0x0C, 0x00, 0x0D,
|
0x03, 0x73, 0x00, 0x83, 0x00, 0x0C, 0x00, 0x0D,
|
||||||
@@ -824,22 +653,6 @@ static ssize_t writeBytes(int fd, void *buf, size_t len)
|
|||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param rom0 A pointer to rom0
|
|
||||||
* @param startAddr What address to begin checking from
|
|
||||||
* @param size How many bytes to check
|
|
||||||
* @param areaName Name to be displayed in the warning message
|
|
||||||
*/
|
|
||||||
static void warnNonZero(uint8_t *rom0, uint16_t startAddr, uint8_t size, char const *areaName)
|
|
||||||
{
|
|
||||||
for (uint8_t i = 0; i < size; i++) {
|
|
||||||
if (rom0[i + startAddr] != 0) {
|
|
||||||
fprintf(stderr, "warning: Overwrote a non-zero byte in the %s\n", areaName);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param input File descriptor to be used for reading
|
* @param input File descriptor to be used for reading
|
||||||
* @param output File descriptor to be used for writing, may be equal to `input`
|
* @param output File descriptor to be used for writing, may be equal to `input`
|
||||||
@@ -856,21 +669,18 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
|
|
||||||
uint8_t rom0[BANK_SIZE];
|
uint8_t rom0[BANK_SIZE];
|
||||||
ssize_t rom0Len = readBytes(input, rom0, sizeof(rom0));
|
ssize_t rom0Len = readBytes(input, rom0, sizeof(rom0));
|
||||||
// Also used as how many bytes to write back when fixing in-place
|
|
||||||
ssize_t headerSize = (cartridgeType & 0xff00) == TPP1 ? 0x154 : 0x150;
|
|
||||||
|
|
||||||
if (rom0Len == -1) {
|
if (rom0Len == -1) {
|
||||||
report("FATAL: Failed to read \"%s\"'s header: %s\n", name, strerror(errno));
|
report("FATAL: Failed to read \"%s\"'s header: %s\n", name, strerror(errno));
|
||||||
return;
|
return;
|
||||||
} else if (rom0Len < headerSize) {
|
} else if (rom0Len < 0x150) {
|
||||||
report("FATAL: \"%s\" too short, expected at least %jd ($%jx) bytes, got only %jd\n",
|
report("FATAL: \"%s\" too short, expected at least 336 ($150) bytes, got only %ld\n",
|
||||||
name, (intmax_t)headerSize, (intmax_t)headerSize, (intmax_t)rom0Len);
|
name, rom0Len);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Accept partial reads if the file contains at least the header
|
// Accept partial reads if the file contains at least the header
|
||||||
|
|
||||||
if (fixSpec & (FIX_LOGO | TRASH_LOGO)) {
|
if (fixSpec & (FIX_LOGO | TRASH_LOGO)) {
|
||||||
warnNonZero(rom0, 0x0104, sizeof(ninLogo), "Nintendo logo");
|
|
||||||
if (fixSpec & FIX_LOGO) {
|
if (fixSpec & FIX_LOGO) {
|
||||||
memcpy(&rom0[0x104], ninLogo, sizeof(ninLogo));
|
memcpy(&rom0[0x104], ninLogo, sizeof(ninLogo));
|
||||||
} else {
|
} else {
|
||||||
@@ -879,85 +689,36 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (title) {
|
if (title)
|
||||||
warnNonZero(rom0, 0x134, titleLen, "title");
|
|
||||||
memcpy(&rom0[0x134], title, titleLen);
|
memcpy(&rom0[0x134], title, titleLen);
|
||||||
}
|
|
||||||
|
|
||||||
if (gameID) {
|
if (gameID)
|
||||||
warnNonZero(rom0, 0x13f, gameIDLen, "manufacturer code");
|
|
||||||
memcpy(&rom0[0x13f], gameID, gameIDLen);
|
memcpy(&rom0[0x13f], gameID, gameIDLen);
|
||||||
}
|
|
||||||
|
|
||||||
if (model != DMG) {
|
if (model != DMG)
|
||||||
warnNonZero(rom0, 0x143, 1, "CGB flag");
|
|
||||||
rom0[0x143] = model == BOTH ? 0x80 : 0xc0;
|
rom0[0x143] = model == BOTH ? 0x80 : 0xc0;
|
||||||
}
|
|
||||||
|
|
||||||
if (newLicensee) {
|
if (newLicensee)
|
||||||
warnNonZero(rom0, 0x144, newLicenseeLen, "new licensee code");
|
|
||||||
memcpy(&rom0[0x144], newLicensee, newLicenseeLen);
|
memcpy(&rom0[0x144], newLicensee, newLicenseeLen);
|
||||||
}
|
|
||||||
|
|
||||||
if (sgb) {
|
if (sgb)
|
||||||
warnNonZero(rom0, 0x146, 1, "SGB flag");
|
|
||||||
rom0[0x146] = 0x03;
|
rom0[0x146] = 0x03;
|
||||||
}
|
|
||||||
|
|
||||||
// If a valid MBC was specified...
|
// If a valid MBC was specified...
|
||||||
if (cartridgeType < MBC_NONE) {
|
if (cartridgeType < MBC_NONE)
|
||||||
warnNonZero(rom0, 0x147, 1, "cartridge type");
|
rom0[0x147] = cartridgeType;
|
||||||
uint8_t byte = cartridgeType;
|
|
||||||
|
|
||||||
if ((cartridgeType & 0xff00) == TPP1) {
|
if (ramSize != UNSPECIFIED)
|
||||||
// Cartridge type isn't directly actionable, translate it
|
rom0[0x149] = ramSize;
|
||||||
byte = 0xBC;
|
|
||||||
// The other TPP1 identification bytes will be written below
|
|
||||||
}
|
|
||||||
rom0[0x147] = byte;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ROM size will be written last, after evaluating the file's size
|
if (!japanese)
|
||||||
|
rom0[0x14a] = 0x01;
|
||||||
|
|
||||||
if ((cartridgeType & 0xff00) == TPP1) {
|
if (oldLicensee != UNSPECIFIED)
|
||||||
warnNonZero(rom0, 0x149, 2, "TPP1 identification code");
|
|
||||||
rom0[0x149] = 0xC1;
|
|
||||||
rom0[0x14a] = 0x65;
|
|
||||||
|
|
||||||
warnNonZero(rom0, 0x150, 2, "TPP1 revision number");
|
|
||||||
rom0[0x150] = tpp1Rev[0];
|
|
||||||
rom0[0x151] = tpp1Rev[1];
|
|
||||||
|
|
||||||
if (ramSize != UNSPECIFIED) {
|
|
||||||
warnNonZero(rom0, 0x152, 1, "RAM size");
|
|
||||||
rom0[0x152] = ramSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
warnNonZero(rom0, 0x153, 1, "TPP1 feature flags");
|
|
||||||
rom0[0x153] = cartridgeType & 0xFF;
|
|
||||||
} else {
|
|
||||||
// Regular mappers
|
|
||||||
|
|
||||||
if (ramSize != UNSPECIFIED) {
|
|
||||||
warnNonZero(rom0, 0x149, 1, "RAM size");
|
|
||||||
rom0[0x149] = ramSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!japanese) {
|
|
||||||
warnNonZero(rom0, 0x14a, 1, "destination code");
|
|
||||||
rom0[0x14a] = 0x01;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldLicensee != UNSPECIFIED) {
|
|
||||||
warnNonZero(rom0, 0x14b, 1, "old licensee code");
|
|
||||||
rom0[0x14b] = oldLicensee;
|
rom0[0x14b] = oldLicensee;
|
||||||
}
|
|
||||||
|
|
||||||
if (romVersion != UNSPECIFIED) {
|
if (romVersion != UNSPECIFIED)
|
||||||
warnNonZero(rom0, 0x14c, 1, "mask ROM version number");
|
|
||||||
rom0[0x14c] = romVersion;
|
rom0[0x14c] = romVersion;
|
||||||
}
|
|
||||||
|
|
||||||
// Remain to be handled the ROM size, and header checksum.
|
// Remain to be handled the ROM size, and header checksum.
|
||||||
// The latter depends on the former, and so will be handled after it.
|
// The latter depends on the former, and so will be handled after it.
|
||||||
@@ -1057,7 +818,6 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
|
|
||||||
for (uint16_t i = 0x134; i < 0x14d; i++)
|
for (uint16_t i = 0x134; i < 0x14d; i++)
|
||||||
sum -= rom0[i] + 1;
|
sum -= rom0[i] + 1;
|
||||||
warnNonZero(rom0, 0x14d, 1, "header checksum");
|
|
||||||
rom0[0x14d] = fixSpec & TRASH_HEADER_SUM ? ~sum : sum;
|
rom0[0x14d] = fixSpec & TRASH_HEADER_SUM ? ~sum : sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1081,7 +841,6 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
|
|
||||||
if (fixSpec & TRASH_GLOBAL_SUM)
|
if (fixSpec & TRASH_GLOBAL_SUM)
|
||||||
globalSum = ~globalSum;
|
globalSum = ~globalSum;
|
||||||
warnNonZero(rom0, 0x14e, 2, "global checksum");
|
|
||||||
rom0[0x14e] = globalSum >> 8;
|
rom0[0x14e] = globalSum >> 8;
|
||||||
rom0[0x14f] = globalSum & 0xff;
|
rom0[0x14f] = globalSum & 0xff;
|
||||||
}
|
}
|
||||||
@@ -1096,7 +855,7 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
// If modifying the file in-place, we only need to edit the header
|
// If modifying the file in-place, we only need to edit the header
|
||||||
// However, padding may have modified ROM0 (added padding), so don't in that case
|
// However, padding may have modified ROM0 (added padding), so don't in that case
|
||||||
if (padValue == UNSPECIFIED)
|
if (padValue == UNSPECIFIED)
|
||||||
rom0Len = headerSize;
|
rom0Len = 0x150;
|
||||||
}
|
}
|
||||||
ssize_t writeLen = writeBytes(output, rom0, rom0Len);
|
ssize_t writeLen = writeBytes(output, rom0, rom0Len);
|
||||||
|
|
||||||
@@ -1104,8 +863,8 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
report("FATAL: Failed to write \"%s\"'s ROM0: %s\n", name, strerror(errno));
|
report("FATAL: Failed to write \"%s\"'s ROM0: %s\n", name, strerror(errno));
|
||||||
goto free_romx;
|
goto free_romx;
|
||||||
} else if (writeLen < rom0Len) {
|
} else if (writeLen < rom0Len) {
|
||||||
report("FATAL: Could only write %jd of \"%s\"'s %jd ROM0 bytes\n",
|
report("FATAL: Could only write %ld of \"%s\"'s %ld ROM0 bytes\n",
|
||||||
(intmax_t)writeLen, name, (intmax_t)rom0Len);
|
writeLen, name, rom0Len);
|
||||||
goto free_romx;
|
goto free_romx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1118,8 +877,8 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
report("FATAL: Failed to write \"%s\"'s ROMX: %s\n", name, strerror(errno));
|
report("FATAL: Failed to write \"%s\"'s ROMX: %s\n", name, strerror(errno));
|
||||||
goto free_romx;
|
goto free_romx;
|
||||||
} else if ((size_t)writeLen < totalRomxLen) {
|
} else if ((size_t)writeLen < totalRomxLen) {
|
||||||
report("FATAL: Could only write %jd of \"%s\"'s %zu ROMX bytes\n",
|
report("FATAL: Could only write %ld of \"%s\"'s %ld ROMX bytes\n",
|
||||||
(intmax_t)writeLen, name, totalRomxLen);
|
writeLen, name, totalRomxLen);
|
||||||
goto free_romx;
|
goto free_romx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1156,6 +915,8 @@ free_romx:
|
|||||||
free(romx);
|
free(romx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef trySeek
|
||||||
|
|
||||||
static bool processFilename(char const *name)
|
static bool processFilename(char const *name)
|
||||||
{
|
{
|
||||||
nbErrors = 0;
|
nbErrors = 0;
|
||||||
@@ -1188,8 +949,8 @@ static bool processFilename(char const *name)
|
|||||||
} else if (stat.st_size < 0x150) {
|
} else if (stat.st_size < 0x150) {
|
||||||
// This check is in theory redundant with the one in `processFile`, but it
|
// This check is in theory redundant with the one in `processFile`, but it
|
||||||
// prevents passing a file size of 0, which usually indicates pipes
|
// prevents passing a file size of 0, which usually indicates pipes
|
||||||
report("FATAL: \"%s\" too short, expected at least 336 ($150) bytes, got only %jd\n",
|
report("FATAL: \"%s\" too short, expected at least 336 ($150) bytes, got only %ld\n",
|
||||||
name, (intmax_t)stat.st_size);
|
name, stat.st_size);
|
||||||
} else {
|
} else {
|
||||||
processFile(input, input, name, stat.st_size);
|
processFile(input, input, name, stat.st_size);
|
||||||
}
|
}
|
||||||
@@ -1206,7 +967,7 @@ fail:
|
|||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
nbErrors = 0;
|
nbErrors = 0;
|
||||||
int ch;
|
char ch;
|
||||||
|
|
||||||
while ((ch = musl_getopt_long_only(argc, argv, optstring, longopts, NULL)) != -1) {
|
while ((ch = musl_getopt_long_only(argc, argv, optstring, longopts, NULL)) != -1) {
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
@@ -1254,7 +1015,7 @@ do { \
|
|||||||
#define SPEC_H TRASH_HEADER_SUM
|
#define SPEC_H TRASH_HEADER_SUM
|
||||||
#define SPEC_g FIX_GLOBAL_SUM
|
#define SPEC_g FIX_GLOBAL_SUM
|
||||||
#define SPEC_G TRASH_GLOBAL_SUM
|
#define SPEC_G TRASH_GLOBAL_SUM
|
||||||
#define overrideSpec(new, bad) \
|
#define or(new, bad) \
|
||||||
do { \
|
do { \
|
||||||
if (fixSpec & SPEC_##bad) \
|
if (fixSpec & SPEC_##bad) \
|
||||||
fprintf(stderr, \
|
fprintf(stderr, \
|
||||||
@@ -1262,30 +1023,30 @@ do { \
|
|||||||
fixSpec = (fixSpec & ~SPEC_##bad) | SPEC_##new; \
|
fixSpec = (fixSpec & ~SPEC_##bad) | SPEC_##new; \
|
||||||
} while (0)
|
} while (0)
|
||||||
case 'l':
|
case 'l':
|
||||||
overrideSpec(l, L);
|
or(l, L);
|
||||||
break;
|
break;
|
||||||
case 'L':
|
case 'L':
|
||||||
overrideSpec(L, l);
|
or(L, l);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'h':
|
case 'h':
|
||||||
overrideSpec(h, H);
|
or(h, H);
|
||||||
break;
|
break;
|
||||||
case 'H':
|
case 'H':
|
||||||
overrideSpec(H, h);
|
or(H, h);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'g':
|
case 'g':
|
||||||
overrideSpec(g, G);
|
or(g, G);
|
||||||
break;
|
break;
|
||||||
case 'G':
|
case 'G':
|
||||||
overrideSpec(G, g);
|
or(G, g);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "warning: Ignoring '%c' in fix spec\n",
|
fprintf(stderr, "warning: Ignoring '%c' in fix spec\n",
|
||||||
*musl_optarg);
|
*musl_optarg);
|
||||||
#undef overrideSpec
|
#undef or
|
||||||
}
|
}
|
||||||
musl_optarg++;
|
musl_optarg++;
|
||||||
}
|
}
|
||||||
@@ -1330,13 +1091,10 @@ do { \
|
|||||||
case 'm':
|
case 'm':
|
||||||
cartridgeType = parseMBC(musl_optarg);
|
cartridgeType = parseMBC(musl_optarg);
|
||||||
if (cartridgeType == MBC_BAD) {
|
if (cartridgeType == MBC_BAD) {
|
||||||
report("error: Unknown MBC \"%s\"\nAccepted MBC names:\n",
|
report("error: Unknown MBC \"%s\"\n", musl_optarg);
|
||||||
musl_optarg);
|
|
||||||
printAcceptedMBCNames();
|
|
||||||
} else if (cartridgeType == MBC_WRONG_FEATURES) {
|
} else if (cartridgeType == MBC_WRONG_FEATURES) {
|
||||||
report("error: Features incompatible with MBC (\"%s\")\nAccepted combinations:\n",
|
report("error: Features incompatible with MBC (\"%s\")\n",
|
||||||
musl_optarg);
|
musl_optarg);
|
||||||
printAcceptedMBCNames();
|
|
||||||
} else if (cartridgeType == MBC_BAD_RANGE) {
|
} else if (cartridgeType == MBC_BAD_RANGE) {
|
||||||
report("error: Specified MBC ID out of range 0-255: %s\n",
|
report("error: Specified MBC ID out of range 0-255: %s\n",
|
||||||
musl_optarg);
|
musl_optarg);
|
||||||
@@ -1390,11 +1148,7 @@ do { \
|
|||||||
#undef parseByte
|
#undef parseByte
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((cartridgeType & 0xff00) == TPP1 && !japanese)
|
if (ramSize != UNSPECIFIED && cartridgeType < UNSPECIFIED) {
|
||||||
fprintf(stderr, "warning: TPP1 overwrites region flag for its identification code, ignoring `-j`\n");
|
|
||||||
|
|
||||||
// Check that RAM size is correct for "standard" mappers
|
|
||||||
if (ramSize != UNSPECIFIED && (cartridgeType & 0xff00) == 0) {
|
|
||||||
if (cartridgeType == ROM_RAM || cartridgeType == ROM_RAM_BATTERY) {
|
if (cartridgeType == ROM_RAM || cartridgeType == ROM_RAM_BATTERY) {
|
||||||
if (ramSize != 1)
|
if (ramSize != 1)
|
||||||
fprintf(stderr, "warning: MBC \"%s\" should have 2kiB of RAM (-r 1)\n",
|
fprintf(stderr, "warning: MBC \"%s\" should have 2kiB of RAM (-r 1)\n",
|
||||||
|
|||||||
@@ -112,13 +112,7 @@ This value is deprecated and should be set to 0x33 in all new software.
|
|||||||
Set the MBC type
|
Set the MBC type
|
||||||
.Pq Ad 0x147
|
.Pq Ad 0x147
|
||||||
to a given value from 0 to 0xFF.
|
to a given value from 0 to 0xFF.
|
||||||
.Pp
|
This value may also be an MBC name from the Pan Docs.
|
||||||
This value may also be an MBC name.
|
|
||||||
The list of accepted names can be obtained by passing "help" as the argument.
|
|
||||||
Any amount of whitespace (space and tabs) is allowed around plus signs, and the order of "components" is free, as long as the MBC name is first.
|
|
||||||
There are special considerations to take for the TPP1 mapper; see the
|
|
||||||
.Sx TPP1
|
|
||||||
section below.
|
|
||||||
.It Fl n Ar rom_version , Fl Fl rom-version Ar rom_version
|
.It Fl n Ar rom_version , Fl Fl rom-version Ar rom_version
|
||||||
Set the ROM version
|
Set the ROM version
|
||||||
.Pq Ad 0x14C
|
.Pq Ad 0x14C
|
||||||
@@ -185,46 +179,6 @@ sans global checksum:
|
|||||||
.Pp
|
.Pp
|
||||||
.D1 $ rgbfix -cjsv -k A4 -l 0x33 -m 0x1B -p 0xFF -r 3 -t SURVIVALKIDAVKE \
|
.D1 $ rgbfix -cjsv -k A4 -l 0x33 -m 0x1B -p 0xFF -r 3 -t SURVIVALKIDAVKE \
|
||||||
SurvivalKids.gbc
|
SurvivalKids.gbc
|
||||||
.Sh TPP1
|
|
||||||
TPP1 is a homebrew mapper designed as a functional superset of the common traditional MBCs, allowing larger ROM and RAM sizes combined with other hardware features.
|
|
||||||
Its specification, as well as more resources, can be found online at
|
|
||||||
.Lk https://github.com/TwitchPlaysPokemon/tpp1 .
|
|
||||||
.Ss MBC name
|
|
||||||
The MBC name for TPP1 is more complex than standard mappers.
|
|
||||||
It must be followed with the revision number, of the form
|
|
||||||
.Ql major.minor ,
|
|
||||||
where both
|
|
||||||
.Ql major
|
|
||||||
and
|
|
||||||
.Ql minor
|
|
||||||
are decimal, 8-bit integers.
|
|
||||||
There may be any amount of spaces or underscores between
|
|
||||||
.Ql TPP1
|
|
||||||
and the revision number.
|
|
||||||
.Nm
|
|
||||||
only supports 1.x revisions, and will reject everything else.
|
|
||||||
.Pp
|
|
||||||
Like other mappers, the name may be followed with a list of optional,
|
|
||||||
.Ql + Ns
|
|
||||||
-separated features; however,
|
|
||||||
.Ql RAM
|
|
||||||
should not be specified, as the TPP1 mapper implicitly requests RAM if a non-zero RAM size is specified.
|
|
||||||
Therefore,
|
|
||||||
.Nm
|
|
||||||
will ignore the
|
|
||||||
.Ql RAM
|
|
||||||
feature on a TPP1 mapper with a warning.
|
|
||||||
.Ss Special considerations
|
|
||||||
TPP1 overwrites the byte at
|
|
||||||
.Ad 0x14A ,
|
|
||||||
usually indicating the region destination
|
|
||||||
.Pq see Fl j ,
|
|
||||||
with one of its three identification bytes.
|
|
||||||
Therefore,
|
|
||||||
.Nm
|
|
||||||
will warn about and ignore
|
|
||||||
.Fl j
|
|
||||||
if used in combination with TPP1.
|
|
||||||
.Sh BUGS
|
.Sh BUGS
|
||||||
Please report bugs on
|
Please report bugs on
|
||||||
.Lk https://github.com/gbdev/rgbds/issues GitHub .
|
.Lk https://github.com/gbdev/rgbds/issues GitHub .
|
||||||
|
|||||||
559
src/gbz80.7
559
src/gbz80.7
File diff suppressed because it is too large
Load Diff
@@ -224,11 +224,7 @@ void create_mapfiles(const struct Options *opts, struct GBImage *gb,
|
|||||||
if (!tile)
|
if (!tile)
|
||||||
err(1, "%s: Failed to allocate memory for tile",
|
err(1, "%s: Failed to allocate memory for tile",
|
||||||
__func__);
|
__func__);
|
||||||
/*
|
for (i = 0; i < tile_size; i++) {
|
||||||
* If the input image doesn't fill the last tile,
|
|
||||||
* `gb_i` will reach `gb_size`.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < tile_size && gb_i < gb_size; i++) {
|
|
||||||
tile[i] = gb->data[gb_i];
|
tile[i] = gb->data[gb_i];
|
||||||
gb_i++;
|
gb_i++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ static HashType hash(char const *str)
|
|||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
void **hash_AddElement(HashMap map, char const *key, void *element)
|
bool hash_AddElement(HashMap map, char const *key, void *element)
|
||||||
{
|
{
|
||||||
HashType hashedKey = hash(key);
|
HashType hashedKey = hash(key);
|
||||||
HalfHashType index = hashedKey;
|
HalfHashType index = hashedKey;
|
||||||
@@ -61,7 +61,23 @@ void **hash_AddElement(HashMap map, char const *key, void *element)
|
|||||||
newEntry->next = map[index];
|
newEntry->next = map[index];
|
||||||
map[index] = newEntry;
|
map[index] = newEntry;
|
||||||
|
|
||||||
return &newEntry->content;
|
return newEntry->next != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hash_ReplaceElement(HashMap const map, char const *key, void *element)
|
||||||
|
{
|
||||||
|
HashType hashedKey = hash(key);
|
||||||
|
struct HashMapEntry *ptr = map[(HalfHashType)hashedKey];
|
||||||
|
|
||||||
|
while (ptr) {
|
||||||
|
if (hashedKey >> HALF_HASH_NB_BITS == ptr->hash
|
||||||
|
&& !strcmp(ptr->key, key)) {
|
||||||
|
ptr->content = element;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
ptr = ptr->next;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hash_RemoveElement(HashMap map, char const *key)
|
bool hash_RemoveElement(HashMap map, char const *key)
|
||||||
@@ -83,7 +99,7 @@ bool hash_RemoveElement(HashMap map, char const *key)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void **hash_GetNode(HashMap const map, char const *key)
|
void *hash_GetElement(HashMap const map, char const *key)
|
||||||
{
|
{
|
||||||
HashType hashedKey = hash(key);
|
HashType hashedKey = hash(key);
|
||||||
struct HashMapEntry *ptr = map[(HalfHashType)hashedKey];
|
struct HashMapEntry *ptr = map[(HalfHashType)hashedKey];
|
||||||
@@ -91,20 +107,13 @@ void **hash_GetNode(HashMap const map, char const *key)
|
|||||||
while (ptr) {
|
while (ptr) {
|
||||||
if (hashedKey >> HALF_HASH_NB_BITS == ptr->hash
|
if (hashedKey >> HALF_HASH_NB_BITS == ptr->hash
|
||||||
&& !strcmp(ptr->key, key)) {
|
&& !strcmp(ptr->key, key)) {
|
||||||
return &ptr->content;
|
return ptr->content;
|
||||||
}
|
}
|
||||||
ptr = ptr->next;
|
ptr = ptr->next;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *hash_GetElement(HashMap const map, char const *key)
|
|
||||||
{
|
|
||||||
void **node = hash_GetNode(map, key);
|
|
||||||
|
|
||||||
return node ? *node : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void hash_ForEach(HashMap const map, void (*func)(void *, void *), void *arg)
|
void hash_ForEach(HashMap const map, void (*func)(void *, void *), void *arg)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < HASHMAP_NB_BUCKETS; i++) {
|
for (size_t i = 0; i < HASHMAP_NB_BUCKETS; i++) {
|
||||||
|
|||||||
@@ -106,7 +106,8 @@ static void processLinkerScript(void)
|
|||||||
* @param section The section to assign
|
* @param section The section to assign
|
||||||
* @param location The location to assign the section to
|
* @param location The location to assign the section to
|
||||||
*/
|
*/
|
||||||
static void assignSection(struct Section *section, struct MemoryLocation const *location)
|
static inline void assignSection(struct Section *section,
|
||||||
|
struct MemoryLocation const *location)
|
||||||
{
|
{
|
||||||
section->org = location->address;
|
section->org = location->address;
|
||||||
section->bank = location->bank;
|
section->bank = location->bank;
|
||||||
@@ -337,7 +338,7 @@ static void placeSection(struct Section *section)
|
|||||||
section->name, typeNames[section->type], where);
|
section->name, typeNames[section->type], where);
|
||||||
/* If the section just can't fit the bank, report that */
|
/* If the section just can't fit the bank, report that */
|
||||||
else if (section->org + section->size > endaddr(section->type) + 1)
|
else if (section->org + section->size > endaddr(section->type) + 1)
|
||||||
errx(1, "Unable to place \"%s\" (%s section) %s: section runs past end of region ($%04x > $%04x)",
|
errx(1, "Unable to place \"%s\" (%s section) %s: section runs past end of region ($%04" PRIx16 " > $%04" PRIx16 ")",
|
||||||
section->name, typeNames[section->type], where,
|
section->name, typeNames[section->type], where,
|
||||||
section->org + section->size, endaddr(section->type) + 1);
|
section->org + section->size, endaddr(section->type) + 1);
|
||||||
/* Otherwise there is overlap with another section */
|
/* Otherwise there is overlap with another section */
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ static void cleanup(void)
|
|||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int optionChar;
|
int optionChar;
|
||||||
char *endptr; /* For error checking with `strtoul` */
|
char *endptr; /* For error checking with `strtol` */
|
||||||
unsigned long value; /* For storing `strtoul`'s return value */
|
unsigned long value; /* For storing `strtoul`'s return value */
|
||||||
|
|
||||||
/* Parse options */
|
/* Parse options */
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ static struct Assertion *assertions;
|
|||||||
do { \
|
do { \
|
||||||
FILE *tmpFile = file; \
|
FILE *tmpFile = file; \
|
||||||
type tmpVal = func(tmpFile); \
|
type tmpVal = func(tmpFile); \
|
||||||
/* TODO: maybe mark the condition as `unlikely`; how to do that portably? */ \
|
|
||||||
if (tmpVal == (errval)) { \
|
if (tmpVal == (errval)) { \
|
||||||
errx(1, __VA_ARGS__, feof(tmpFile) \
|
errx(1, __VA_ARGS__, feof(tmpFile) \
|
||||||
? "Unexpected end of file" \
|
? "Unexpected end of file" \
|
||||||
@@ -87,6 +86,7 @@ static int64_t readlong(FILE *file)
|
|||||||
/**
|
/**
|
||||||
* Helper macro for reading longs from a file, and errors out if it fails to.
|
* Helper macro for reading longs from a file, and errors out if it fails to.
|
||||||
* Not as a function to avoid overhead in the general case.
|
* Not as a function to avoid overhead in the general case.
|
||||||
|
* TODO: maybe mark the condition as `unlikely`; how to do that portably?
|
||||||
* @param var The variable to stash the number into
|
* @param var The variable to stash the number into
|
||||||
* @param file The file to read from. Its position will be advanced
|
* @param file The file to read from. Its position will be advanced
|
||||||
* @param ... A format string and related arguments; note that an extra string
|
* @param ... A format string and related arguments; note that an extra string
|
||||||
@@ -101,6 +101,7 @@ static int64_t readlong(FILE *file)
|
|||||||
* Helper macro for reading bytes from a file, and errors out if it fails to.
|
* Helper macro for reading bytes from a file, and errors out if it fails to.
|
||||||
* Differs from `tryGetc` in that the backing function is fgetc(1).
|
* Differs from `tryGetc` in that the backing function is fgetc(1).
|
||||||
* Not as a function to avoid overhead in the general case.
|
* Not as a function to avoid overhead in the general case.
|
||||||
|
* TODO: maybe mark the condition as `unlikely`; how to do that portably?
|
||||||
* @param var The variable to stash the number into
|
* @param var The variable to stash the number into
|
||||||
* @param file The file to read from. Its position will be advanced
|
* @param file The file to read from. Its position will be advanced
|
||||||
* @param ... A format string and related arguments; note that an extra string
|
* @param ... A format string and related arguments; note that an extra string
|
||||||
@@ -113,6 +114,7 @@ static int64_t readlong(FILE *file)
|
|||||||
* Helper macro for reading bytes from a file, and errors out if it fails to.
|
* Helper macro for reading bytes from a file, and errors out if it fails to.
|
||||||
* Differs from `tryGetc` in that the backing function is fgetc(1).
|
* Differs from `tryGetc` in that the backing function is fgetc(1).
|
||||||
* Not as a function to avoid overhead in the general case.
|
* Not as a function to avoid overhead in the general case.
|
||||||
|
* TODO: maybe mark the condition as `unlikely`; how to do that portably?
|
||||||
* @param var The variable to stash the number into
|
* @param var The variable to stash the number into
|
||||||
* @param file The file to read from. Its position will be advanced
|
* @param file The file to read from. Its position will be advanced
|
||||||
* @param ... A format string and related arguments; note that an extra string
|
* @param ... A format string and related arguments; note that an extra string
|
||||||
@@ -161,6 +163,7 @@ static char *readstr(FILE *file)
|
|||||||
/**
|
/**
|
||||||
* Helper macro for reading bytes from a file, and errors out if it fails to.
|
* Helper macro for reading bytes from a file, and errors out if it fails to.
|
||||||
* Not as a function to avoid overhead in the general case.
|
* Not as a function to avoid overhead in the general case.
|
||||||
|
* TODO: maybe mark the condition as `unlikely`; how to do that portably?
|
||||||
* @param var The variable to stash the string into
|
* @param var The variable to stash the string into
|
||||||
* @param file The file to read from. Its position will be advanced
|
* @param file The file to read from. Its position will be advanced
|
||||||
* @param ... A format string and related arguments; note that an extra string
|
* @param ... A format string and related arguments; note that an extra string
|
||||||
@@ -449,7 +452,7 @@ static void readAssertion(FILE *file, struct Assertion *assert,
|
|||||||
fileName);
|
fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct Section *getMainSection(struct Section *section)
|
static inline struct Section *getMainSection(struct Section *section)
|
||||||
{
|
{
|
||||||
if (section->modifier != SECTION_NORMAL)
|
if (section->modifier != SECTION_NORMAL)
|
||||||
section = sect_GetSection(section->name);
|
section = sect_GetSection(section->name);
|
||||||
@@ -640,33 +643,19 @@ void obj_Setup(unsigned int nbFiles)
|
|||||||
nodes = malloc(sizeof(*nodes) * nbFiles);
|
nodes = malloc(sizeof(*nodes) * nbFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void freeNode(struct FileStackNode *node)
|
|
||||||
{
|
|
||||||
if (node->type == NODE_REPT)
|
|
||||||
free(node->iters);
|
|
||||||
else
|
|
||||||
free(node->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void freeSection(struct Section *section, void *arg)
|
static void freeSection(struct Section *section, void *arg)
|
||||||
{
|
{
|
||||||
(void)arg;
|
(void)arg;
|
||||||
|
|
||||||
do {
|
free(section->name);
|
||||||
struct Section *next = section->nextu;
|
if (sect_HasData(section->type)) {
|
||||||
|
free(section->data);
|
||||||
free(section->name);
|
for (uint32_t i = 0; i < section->nbPatches; i++)
|
||||||
if (sect_HasData(section->type)) {
|
free(section->patches[i].rpnExpression);
|
||||||
free(section->data);
|
free(section->patches);
|
||||||
for (uint32_t i = 0; i < section->nbPatches; i++)
|
}
|
||||||
free(section->patches[i].rpnExpression);
|
free(section->symbols);
|
||||||
free(section->patches);
|
free(section);
|
||||||
}
|
|
||||||
free(section->symbols);
|
|
||||||
free(section);
|
|
||||||
|
|
||||||
section = next;
|
|
||||||
} while (section);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void freeSymbol(struct Symbol *symbol)
|
static void freeSymbol(struct Symbol *symbol)
|
||||||
@@ -679,7 +668,8 @@ void obj_Cleanup(void)
|
|||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < nbObjFiles; i++) {
|
for (unsigned int i = 0; i < nbObjFiles; i++) {
|
||||||
for (uint32_t j = 0; j < nodes[i].nbNodes; j++) {
|
for (uint32_t j = 0; j < nodes[i].nbNodes; j++) {
|
||||||
freeNode(&nodes[i].nodes[j]);
|
if (nodes[i].nodes[j].type == NODE_REPT)
|
||||||
|
free(nodes[i].nodes[j].iters);
|
||||||
}
|
}
|
||||||
free(nodes[i].nodes);
|
free(nodes[i].nodes);
|
||||||
}
|
}
|
||||||
@@ -690,12 +680,16 @@ void obj_Cleanup(void)
|
|||||||
sect_ForEach(freeSection, NULL);
|
sect_ForEach(freeSection, NULL);
|
||||||
sect_CleanupSections();
|
sect_CleanupSections();
|
||||||
|
|
||||||
for (struct SymbolList *list = symbolLists, *next; list; list = next) {
|
struct SymbolList *list = symbolLists;
|
||||||
next = list->next;
|
|
||||||
|
|
||||||
|
while (list) {
|
||||||
for (size_t i = 0; i < list->nbSymbols; i++)
|
for (size_t i = 0; i < list->nbSymbols; i++)
|
||||||
freeSymbol(list->symbolList[i]);
|
freeSymbol(list->symbolList[i]);
|
||||||
free(list->symbolList);
|
free(list->symbolList);
|
||||||
|
|
||||||
|
struct SymbolList *next = list->next;
|
||||||
|
|
||||||
free(list);
|
free(list);
|
||||||
|
list = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -34,12 +33,6 @@ struct SortedSection {
|
|||||||
struct SortedSection *next;
|
struct SortedSection *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SortedSymbol {
|
|
||||||
struct Symbol const *sym;
|
|
||||||
uint32_t idx;
|
|
||||||
uint16_t addr;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
uint32_t nbBanks;
|
uint32_t nbBanks;
|
||||||
struct SortedSections {
|
struct SortedSections {
|
||||||
@@ -275,86 +268,69 @@ static struct SortedSection const **nextSection(struct SortedSection const **s1,
|
|||||||
return (*s1)->section->org < (*s2)->section->org ? s1 : s2;
|
return (*s1)->section->org < (*s2)->section->org ? s1 : s2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Comparator function for `qsort` to sort symbols
|
|
||||||
* Symbols are ordered by address, or else by original index for a stable sort
|
|
||||||
*/
|
|
||||||
static int compareSymbols(void const *a, void const *b)
|
|
||||||
{
|
|
||||||
struct SortedSymbol const *sym1 = (struct SortedSymbol const *)a;
|
|
||||||
struct SortedSymbol const *sym2 = (struct SortedSymbol const *)b;
|
|
||||||
|
|
||||||
if (sym1->addr != sym2->addr)
|
|
||||||
return sym1->addr < sym2->addr ? -1 : 1;
|
|
||||||
|
|
||||||
return sym1->idx < sym2->idx ? -1 : sym1->idx > sym2->idx ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a bank's contents to the sym file
|
* Write a bank's contents to the sym file
|
||||||
* @param bankSections The bank's sections
|
* @param bankSections The bank's sections
|
||||||
*/
|
*/
|
||||||
static void writeSymBank(struct SortedSections const *bankSections,
|
static void writeSymBank(struct SortedSections const *bankSections)
|
||||||
enum SectionType type, uint32_t bank)
|
|
||||||
{
|
{
|
||||||
if (!symFile)
|
if (!symFile)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uint32_t nbSymbols = 0;
|
struct {
|
||||||
|
struct SortedSection const *sections;
|
||||||
|
#define sect sections->section /* Fake member as a shortcut */
|
||||||
|
uint32_t i;
|
||||||
|
struct Symbol const *sym;
|
||||||
|
uint16_t addr;
|
||||||
|
} sectList = { .sections = bankSections->sections, .i = 0 },
|
||||||
|
zlSectList = { .sections = bankSections->zeroLenSections, .i = 0 },
|
||||||
|
*minSectList;
|
||||||
|
|
||||||
for (struct SortedSection const *ptr = bankSections->zeroLenSections; ptr; ptr = ptr->next) {
|
for (;;) {
|
||||||
for (struct Section const *sect = ptr->section; sect; sect = sect->nextu)
|
while (sectList.sections
|
||||||
nbSymbols += sect->nbSymbols;
|
&& sectList.i == sectList.sect->nbSymbols) {
|
||||||
}
|
sectList.sections = sectList.sections->next;
|
||||||
for (struct SortedSection const *ptr = bankSections->sections; ptr; ptr = ptr->next) {
|
sectList.i = 0;
|
||||||
for (struct Section const *sect = ptr->section; sect; sect = sect->nextu)
|
|
||||||
nbSymbols += sect->nbSymbols;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!nbSymbols)
|
|
||||||
return;
|
|
||||||
|
|
||||||
struct SortedSymbol *symList = malloc(sizeof(*symList) * nbSymbols);
|
|
||||||
|
|
||||||
if (!symList)
|
|
||||||
err(1, "Failed to allocate symbol list");
|
|
||||||
|
|
||||||
uint32_t idx = 0;
|
|
||||||
|
|
||||||
for (struct SortedSection const *ptr = bankSections->zeroLenSections; ptr; ptr = ptr->next) {
|
|
||||||
for (struct Section const *sect = ptr->section; sect; sect = sect->nextu) {
|
|
||||||
for (uint32_t i = 0; i < sect->nbSymbols; i++) {
|
|
||||||
symList[idx].idx = idx;
|
|
||||||
symList[idx].sym = sect->symbols[i];
|
|
||||||
symList[idx].addr = symList[idx].sym->offset + sect->org;
|
|
||||||
idx++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
while (zlSectList.sections
|
||||||
for (struct SortedSection const *ptr = bankSections->sections; ptr; ptr = ptr->next) {
|
&& zlSectList.i == zlSectList.sect->nbSymbols) {
|
||||||
for (struct Section const *sect = ptr->section; sect; sect = sect->nextu) {
|
zlSectList.sections = zlSectList.sections->next;
|
||||||
for (uint32_t i = 0; i < sect->nbSymbols; i++) {
|
zlSectList.i = 0;
|
||||||
symList[idx].idx = idx;
|
|
||||||
symList[idx].sym = sect->symbols[i];
|
|
||||||
symList[idx].addr = symList[idx].sym->offset + sect->org;
|
|
||||||
idx++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
assert(idx == nbSymbols);
|
|
||||||
|
|
||||||
qsort(symList, nbSymbols, sizeof(*symList), compareSymbols);
|
if (!sectList.sections && !zlSectList.sections) {
|
||||||
|
break;
|
||||||
|
} else if (sectList.sections && zlSectList.sections) {
|
||||||
|
sectList.sym = sectList.sect->symbols[sectList.i];
|
||||||
|
zlSectList.sym = zlSectList.sect->symbols[zlSectList.i];
|
||||||
|
sectList.addr =
|
||||||
|
sectList.sym->offset + sectList.sect->org;
|
||||||
|
zlSectList.addr =
|
||||||
|
zlSectList.sym->offset + zlSectList.sect->org;
|
||||||
|
|
||||||
uint32_t symBank = bank + bankranges[type][0];
|
minSectList = sectList.addr < zlSectList.addr
|
||||||
|
? §List
|
||||||
|
: &zlSectList;
|
||||||
|
} else if (sectList.sections) {
|
||||||
|
sectList.sym = sectList.sect->symbols[sectList.i];
|
||||||
|
sectList.addr =
|
||||||
|
sectList.sym->offset + sectList.sect->org;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < nbSymbols; i++) {
|
minSectList = §List;
|
||||||
struct SortedSymbol *sym = &symList[i];
|
} else {
|
||||||
|
zlSectList.sym = zlSectList.sect->symbols[zlSectList.i];
|
||||||
|
zlSectList.addr =
|
||||||
|
zlSectList.sym->offset + zlSectList.sect->org;
|
||||||
|
|
||||||
|
minSectList = &zlSectList;
|
||||||
|
}
|
||||||
fprintf(symFile, "%02" PRIx32 ":%04" PRIx16 " %s\n",
|
fprintf(symFile, "%02" PRIx32 ":%04" PRIx16 " %s\n",
|
||||||
symBank, sym->addr, sym->sym->name);
|
minSectList->sect->bank, minSectList->addr,
|
||||||
|
minSectList->sym->name);
|
||||||
|
minSectList->i++;
|
||||||
}
|
}
|
||||||
|
#undef sect
|
||||||
free(symList);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -384,8 +360,7 @@ static uint16_t writeMapBank(struct SortedSections const *sectList,
|
|||||||
used += sect->size;
|
used += sect->size;
|
||||||
|
|
||||||
if (sect->size != 0)
|
if (sect->size != 0)
|
||||||
fprintf(mapFile, " SECTION: $%04" PRIx16 "-$%04x ($%04" PRIx16
|
fprintf(mapFile, " SECTION: $%04" PRIx16 "-$%04" PRIx16 " ($%04" PRIx16 " byte%s) [\"%s\"]\n",
|
||||||
" byte%s) [\"%s\"]\n",
|
|
||||||
sect->org, sect->org + sect->size - 1,
|
sect->org, sect->org + sect->size - 1,
|
||||||
sect->size, sect->size == 1 ? "" : "s",
|
sect->size, sect->size == 1 ? "" : "s",
|
||||||
sect->name);
|
sect->name);
|
||||||
@@ -468,7 +443,7 @@ static void writeSymAndMap(void)
|
|||||||
for (uint32_t bank = 0; bank < sections[type].nbBanks; bank++) {
|
for (uint32_t bank = 0; bank < sections[type].nbBanks; bank++) {
|
||||||
struct SortedSections const *sect = §ions[type].banks[bank];
|
struct SortedSections const *sect = §ions[type].banks[bank];
|
||||||
|
|
||||||
writeSymBank(sect, type, bank);
|
writeSymBank(sect);
|
||||||
usedMap[type] += writeMapBank(sect, type, bank);
|
usedMap[type] += writeMapBank(sect, type, bank);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ struct RPNStack {
|
|||||||
size_t capacity;
|
size_t capacity;
|
||||||
} stack;
|
} stack;
|
||||||
|
|
||||||
static void initRPNStack(void)
|
static inline void initRPNStack(void)
|
||||||
{
|
{
|
||||||
stack.capacity = 64;
|
stack.capacity = 64;
|
||||||
stack.values = malloc(sizeof(*stack.values) * stack.capacity);
|
stack.values = malloc(sizeof(*stack.values) * stack.capacity);
|
||||||
@@ -46,7 +46,7 @@ static void initRPNStack(void)
|
|||||||
err(1, "Failed to init RPN stack");
|
err(1, "Failed to init RPN stack");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clearRPNStack(void)
|
static inline void clearRPNStack(void)
|
||||||
{
|
{
|
||||||
stack.size = 0;
|
stack.size = 0;
|
||||||
}
|
}
|
||||||
@@ -92,7 +92,7 @@ static int32_t popRPN(struct FileStackNode const *node, uint32_t lineNo)
|
|||||||
return stack.values[stack.size];
|
return stack.values[stack.size];
|
||||||
}
|
}
|
||||||
|
|
||||||
static void freeRPNStack(void)
|
static inline void freeRPNStack(void)
|
||||||
{
|
{
|
||||||
free(stack.values);
|
free(stack.values);
|
||||||
free(stack.errorFlags);
|
free(stack.errorFlags);
|
||||||
@@ -292,11 +292,6 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_BANK_SECT:
|
case RPN_BANK_SECT:
|
||||||
/*
|
|
||||||
* `expression` is not guaranteed to be '\0'-terminated. If it is not,
|
|
||||||
* `getRPNByte` will have a fatal internal error.
|
|
||||||
* In either case, `getRPNByte` will not free `expression`.
|
|
||||||
*/
|
|
||||||
name = (char const *)expression;
|
name = (char const *)expression;
|
||||||
while (getRPNByte(&expression, &size, patch->src, patch->lineNo))
|
while (getRPNByte(&expression, &size, patch->src, patch->lineNo))
|
||||||
;
|
;
|
||||||
@@ -325,44 +320,6 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_SIZEOF_SECT:
|
|
||||||
/* This has assumptions commented in the `RPN_BANK_SECT` case above. */
|
|
||||||
name = (char const *)expression;
|
|
||||||
while (getRPNByte(&expression, &size, patch->src, patch->lineNo))
|
|
||||||
;
|
|
||||||
|
|
||||||
sect = sect_GetSection(name);
|
|
||||||
|
|
||||||
if (!sect) {
|
|
||||||
error(patch->src, patch->lineNo,
|
|
||||||
"Requested SIZEOF() of section \"%s\", which was not found",
|
|
||||||
name);
|
|
||||||
isError = true;
|
|
||||||
value = 1;
|
|
||||||
} else {
|
|
||||||
value = sect->size;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RPN_STARTOF_SECT:
|
|
||||||
/* This has assumptions commented in the `RPN_BANK_SECT` case above. */
|
|
||||||
name = (char const *)expression;
|
|
||||||
while (getRPNByte(&expression, &size, patch->src, patch->lineNo))
|
|
||||||
;
|
|
||||||
|
|
||||||
sect = sect_GetSection(name);
|
|
||||||
|
|
||||||
if (!sect) {
|
|
||||||
error(patch->src, patch->lineNo,
|
|
||||||
"Requested STARTOF() of section \"%s\", which was not found",
|
|
||||||
name);
|
|
||||||
isError = true;
|
|
||||||
value = 1;
|
|
||||||
} else {
|
|
||||||
value = sect->org;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RPN_HRAM:
|
case RPN_HRAM:
|
||||||
value = popRPN();
|
value = popRPN();
|
||||||
if (!isError && (value < 0
|
if (!isError && (value < 0
|
||||||
@@ -478,8 +435,6 @@ void patch_CheckAssertions(struct Assertion *assert)
|
|||||||
}
|
}
|
||||||
struct Assertion *next = assert->next;
|
struct Assertion *next = assert->next;
|
||||||
|
|
||||||
free(assert->patch.rpnExpression);
|
|
||||||
free(assert->message);
|
|
||||||
free(assert);
|
free(assert);
|
||||||
assert = next;
|
assert = next;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
#include "extern/err.h"
|
#include "extern/err.h"
|
||||||
|
|
||||||
FILE *linkerScript;
|
FILE * linkerScript;
|
||||||
char *includeFileName;
|
char *includeFileName;
|
||||||
|
|
||||||
static uint32_t lineNo;
|
static uint32_t lineNo;
|
||||||
@@ -43,7 +43,8 @@ static void pushFile(char *newFileName)
|
|||||||
if (!fileStackSize) /* Init file stack */
|
if (!fileStackSize) /* Init file stack */
|
||||||
fileStackSize = 4;
|
fileStackSize = 4;
|
||||||
fileStackSize *= 2;
|
fileStackSize *= 2;
|
||||||
fileStack = realloc(fileStack, sizeof(*fileStack) * fileStackSize);
|
fileStack = realloc(fileStack,
|
||||||
|
sizeof(*fileStack) * fileStackSize);
|
||||||
if (!fileStack)
|
if (!fileStack)
|
||||||
err(1, "%s(%" PRIu32 "): Internal INCLUDE error",
|
err(1, "%s(%" PRIu32 "): Internal INCLUDE error",
|
||||||
linkerScriptName, lineNo);
|
linkerScriptName, lineNo);
|
||||||
@@ -77,12 +78,12 @@ static bool popFile(void)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isWhiteSpace(int c)
|
static inline bool isWhiteSpace(int c)
|
||||||
{
|
{
|
||||||
return c == ' ' || c == '\t';
|
return c == ' ' || c == '\t';
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isNewline(int c)
|
static inline bool isNewline(int c)
|
||||||
{
|
{
|
||||||
return c == '\r' || c == '\n';
|
return c == '\r' || c == '\n';
|
||||||
}
|
}
|
||||||
@@ -172,11 +173,11 @@ static char const * const commands[] = {
|
|||||||
[COMMAND_ALIGN] = "ALIGN"
|
[COMMAND_ALIGN] = "ALIGN"
|
||||||
};
|
};
|
||||||
|
|
||||||
static int nextChar(void)
|
static int readChar(FILE *file)
|
||||||
{
|
{
|
||||||
int curchar = getc(linkerScript);
|
int curchar = getc(file);
|
||||||
|
|
||||||
if (curchar == EOF && ferror(linkerScript))
|
if (curchar == EOF && ferror(file))
|
||||||
err(1, "%s(%" PRIu32 "): Unexpected error in %s",
|
err(1, "%s(%" PRIu32 "): Unexpected error in %s",
|
||||||
linkerScriptName, lineNo, __func__);
|
linkerScriptName, lineNo, __func__);
|
||||||
return curchar;
|
return curchar;
|
||||||
@@ -193,14 +194,14 @@ static struct LinkerScriptToken *nextToken(void)
|
|||||||
|
|
||||||
/* Skip initial whitespace... */
|
/* Skip initial whitespace... */
|
||||||
do
|
do
|
||||||
curchar = nextChar();
|
curchar = readChar(linkerScript);
|
||||||
while (isWhiteSpace(curchar));
|
while (isWhiteSpace(curchar));
|
||||||
|
|
||||||
/* If this is a comment, skip to the end of the line */
|
/* If this is a comment, skip to the end of the line */
|
||||||
if (curchar == ';') {
|
if (curchar == ';') {
|
||||||
do {
|
do
|
||||||
curchar = nextChar();
|
curchar = readChar(linkerScript);
|
||||||
} while (!isNewline(curchar) && curchar != EOF);
|
while (!isNewline(curchar) && curchar != EOF);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (curchar == EOF) {
|
if (curchar == EOF) {
|
||||||
@@ -209,14 +210,9 @@ static struct LinkerScriptToken *nextToken(void)
|
|||||||
/* If we have a newline char, this is a newline token */
|
/* If we have a newline char, this is a newline token */
|
||||||
token.type = TOKEN_NEWLINE;
|
token.type = TOKEN_NEWLINE;
|
||||||
|
|
||||||
if (curchar == '\r') {
|
/* FIXME: This works with CRLF newlines, but not CR-only */
|
||||||
/* Handle CRLF */
|
if (curchar == '\r')
|
||||||
curchar = nextChar();
|
readChar(linkerScript); /* Read and discard LF */
|
||||||
if (curchar != '\n') {
|
|
||||||
ungetc(curchar, linkerScript);
|
|
||||||
curchar = '\r';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (curchar == '"') {
|
} else if (curchar == '"') {
|
||||||
/* If we have a string start, this is a string */
|
/* If we have a string start, this is a string */
|
||||||
token.type = TOKEN_STRING;
|
token.type = TOKEN_STRING;
|
||||||
@@ -226,33 +222,18 @@ static struct LinkerScriptToken *nextToken(void)
|
|||||||
size_t capacity = 16; /* Half of the default capacity */
|
size_t capacity = 16; /* Half of the default capacity */
|
||||||
|
|
||||||
do {
|
do {
|
||||||
curchar = nextChar();
|
curchar = readChar(linkerScript);
|
||||||
if (curchar == EOF || isNewline(curchar)) {
|
if (curchar == EOF || isNewline(curchar))
|
||||||
errx(1, "%s(%" PRIu32 "): Unterminated string",
|
errx(1, "%s(%" PRIu32 "): Unterminated string",
|
||||||
linkerScriptName, lineNo);
|
linkerScriptName, lineNo);
|
||||||
} else if (curchar == '"') {
|
else if (curchar == '"')
|
||||||
/* Quotes force a string termination */
|
/* Quotes force a string termination */
|
||||||
curchar = '\0';
|
curchar = '\0';
|
||||||
} else if (curchar == '\\') {
|
|
||||||
/* Backslashes are escape sequences */
|
|
||||||
curchar = nextChar();
|
|
||||||
if (curchar == EOF || isNewline(curchar))
|
|
||||||
errx(1, "%s(%" PRIu32 "): Unterminated string",
|
|
||||||
linkerScriptName, lineNo);
|
|
||||||
else if (curchar == 'n')
|
|
||||||
curchar = '\n';
|
|
||||||
else if (curchar == 'r')
|
|
||||||
curchar = '\r';
|
|
||||||
else if (curchar == 't')
|
|
||||||
curchar = '\t';
|
|
||||||
else if (curchar != '\\' && curchar != '"')
|
|
||||||
errx(1, "%s(%" PRIu32 "): Illegal character escape",
|
|
||||||
linkerScriptName, lineNo);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size >= capacity || token.attr.string == NULL) {
|
if (size >= capacity || token.attr.string == NULL) {
|
||||||
capacity *= 2;
|
capacity *= 2;
|
||||||
token.attr.string = realloc(token.attr.string, capacity);
|
token.attr.string = realloc(token.attr.string,
|
||||||
|
capacity);
|
||||||
if (!token.attr.string)
|
if (!token.attr.string)
|
||||||
err(1, "%s: Failed to allocate memory for string",
|
err(1, "%s: Failed to allocate memory for string",
|
||||||
__func__);
|
__func__);
|
||||||
@@ -279,9 +260,10 @@ static struct LinkerScriptToken *nextToken(void)
|
|||||||
if (!curchar)
|
if (!curchar)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
curchar = nextChar();
|
curchar = readChar(linkerScript);
|
||||||
/* Whitespace, a newline or a comment end the token */
|
/* Whitespace, a newline or a comment end the token */
|
||||||
if (isWhiteSpace(curchar) || isNewline(curchar) || curchar == ';') {
|
if (isWhiteSpace(curchar) || isNewline(curchar)
|
||||||
|
|| curchar == ';') {
|
||||||
ungetc(curchar, linkerScript);
|
ungetc(curchar, linkerScript);
|
||||||
curchar = '\0';
|
curchar = '\0';
|
||||||
}
|
}
|
||||||
@@ -300,7 +282,8 @@ static struct LinkerScriptToken *nextToken(void)
|
|||||||
|
|
||||||
if (token.type == TOKEN_INVALID) {
|
if (token.type == TOKEN_INVALID) {
|
||||||
/* Try to match a bank specifier */
|
/* Try to match a bank specifier */
|
||||||
for (enum SectionType type = 0; type < SECTTYPE_INVALID; type++) {
|
for (enum SectionType type = 0; type < SECTTYPE_INVALID;
|
||||||
|
type++) {
|
||||||
if (!strcmp(typeNames[type], str)) {
|
if (!strcmp(typeNames[type], str)) {
|
||||||
token.type = TOKEN_BANK;
|
token.type = TOKEN_BANK;
|
||||||
token.attr.secttype = type;
|
token.attr.secttype = type;
|
||||||
@@ -330,7 +313,8 @@ static struct LinkerScriptToken *nextToken(void)
|
|||||||
return &token;
|
return &token;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void processCommand(enum LinkerScriptCommand command, uint16_t arg, uint16_t *pc)
|
static void processCommand(enum LinkerScriptCommand command, uint16_t arg,
|
||||||
|
uint16_t *pc)
|
||||||
{
|
{
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case COMMAND_INVALID:
|
case COMMAND_INVALID:
|
||||||
@@ -475,7 +459,8 @@ struct SectionPlacement *script_NextSection(void)
|
|||||||
errx(1, "%s(%" PRIu32 "): Command specified without an argument",
|
errx(1, "%s(%" PRIu32 "): Command specified without an argument",
|
||||||
linkerScriptName, lineNo);
|
linkerScriptName, lineNo);
|
||||||
|
|
||||||
processCommand(attr.command, arg, &curaddr[type][bankID]);
|
processCommand(attr.command, arg,
|
||||||
|
&curaddr[type][bankID]);
|
||||||
} else { /* TOKEN_BANK */
|
} else { /* TOKEN_BANK */
|
||||||
type = attr.secttype;
|
type = attr.secttype;
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ static void checkSectUnionCompat(struct Section *target, struct Section *other)
|
|||||||
other->name, target->org, other->org);
|
other->name, target->org, other->org);
|
||||||
} else if (target->isAlignFixed) {
|
} else if (target->isAlignFixed) {
|
||||||
if ((other->org - target->alignOfs) & target->alignMask)
|
if ((other->org - target->alignOfs) & target->alignMask)
|
||||||
errx(1, "Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
|
errx(1, "Section \"%s\" is defined with conflicting %" PRIu16
|
||||||
PRIu16 ") and address $%04" PRIx16,
|
"-byte alignment (offset %" PRIu16 ") and address $%04" PRIx16,
|
||||||
other->name, target->alignMask + 1,
|
other->name, target->alignMask + 1,
|
||||||
target->alignOfs, other->org);
|
target->alignOfs, other->org);
|
||||||
}
|
}
|
||||||
@@ -61,14 +61,15 @@ static void checkSectUnionCompat(struct Section *target, struct Section *other)
|
|||||||
if (target->isAddressFixed) {
|
if (target->isAddressFixed) {
|
||||||
if ((target->org - other->alignOfs) & other->alignMask)
|
if ((target->org - other->alignOfs) & other->alignMask)
|
||||||
errx(1, "Section \"%s\" is defined with conflicting address $%04"
|
errx(1, "Section \"%s\" is defined with conflicting address $%04"
|
||||||
PRIx16 " and %d-byte alignment (offset %" PRIu16 ")",
|
PRIx16 " and %" PRIu16 "-byte alignment (offset %" PRIu16 ")",
|
||||||
other->name, target->org,
|
other->name, target->org,
|
||||||
other->alignMask + 1, other->alignOfs);
|
other->alignMask + 1, other->alignOfs);
|
||||||
} else if (target->isAlignFixed
|
} else if (target->isAlignFixed
|
||||||
&& (other->alignMask & target->alignOfs)
|
&& (other->alignMask & target->alignOfs)
|
||||||
!= (target->alignMask & other->alignOfs)) {
|
!= (target->alignMask & other->alignOfs)) {
|
||||||
errx(1, "Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
|
errx(1, "Section \"%s\" is defined with conflicting %" PRIu16
|
||||||
PRIu16 ") and %d-byte alignment (offset %" PRIu16 ")",
|
"-byte alignment (offset %" PRIu16 ") and %" PRIu16
|
||||||
|
"-byte alignment (offset %" PRIu16 ")",
|
||||||
other->name, target->alignMask + 1, target->alignOfs,
|
other->name, target->alignMask + 1, target->alignOfs,
|
||||||
other->alignMask + 1, other->alignOfs);
|
other->alignMask + 1, other->alignOfs);
|
||||||
} else if (!target->isAlignFixed || (other->alignMask > target->alignMask)) {
|
} else if (!target->isAlignFixed || (other->alignMask > target->alignMask)) {
|
||||||
@@ -91,8 +92,8 @@ static void checkFragmentCompat(struct Section *target, struct Section *other)
|
|||||||
|
|
||||||
} else if (target->isAlignFixed) {
|
} else if (target->isAlignFixed) {
|
||||||
if ((org - target->alignOfs) & target->alignMask)
|
if ((org - target->alignOfs) & target->alignMask)
|
||||||
errx(1, "Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
|
errx(1, "Section \"%s\" is defined with conflicting %" PRIu16
|
||||||
PRIu16 ") and address $%04" PRIx16,
|
"-byte alignment (offset %" PRIu16 ") and address $%04" PRIx16,
|
||||||
other->name, target->alignMask + 1,
|
other->name, target->alignMask + 1,
|
||||||
target->alignOfs, other->org);
|
target->alignOfs, other->org);
|
||||||
}
|
}
|
||||||
@@ -108,14 +109,15 @@ static void checkFragmentCompat(struct Section *target, struct Section *other)
|
|||||||
if (target->isAddressFixed) {
|
if (target->isAddressFixed) {
|
||||||
if ((target->org - ofs) & other->alignMask)
|
if ((target->org - ofs) & other->alignMask)
|
||||||
errx(1, "Section \"%s\" is defined with conflicting address $%04"
|
errx(1, "Section \"%s\" is defined with conflicting address $%04"
|
||||||
PRIx16 " and %d-byte alignment (offset %" PRIu16 ")",
|
PRIx16 " and %" PRIu16 "-byte alignment (offset %" PRIu16 ")",
|
||||||
other->name, target->org,
|
other->name, target->org,
|
||||||
other->alignMask + 1, other->alignOfs);
|
other->alignMask + 1, other->alignOfs);
|
||||||
|
|
||||||
} else if (target->isAlignFixed
|
} else if (target->isAlignFixed
|
||||||
&& (other->alignMask & target->alignOfs) != (target->alignMask & ofs)) {
|
&& (other->alignMask & target->alignOfs) != (target->alignMask & ofs)) {
|
||||||
errx(1, "Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
|
errx(1, "Section \"%s\" is defined with conflicting %" PRIu16
|
||||||
PRIu16 ") and %d-byte alignment (offset %" PRIu16 ")",
|
"-byte alignment (offset %" PRIu16 ") and %" PRIu16
|
||||||
|
"-byte alignment (offset %" PRIu16 ")",
|
||||||
other->name, target->alignMask + 1, target->alignOfs,
|
other->name, target->alignMask + 1, target->alignOfs,
|
||||||
other->alignMask + 1, other->alignOfs);
|
other->alignMask + 1, other->alignOfs);
|
||||||
|
|
||||||
@@ -163,9 +165,6 @@ static void mergeSections(struct Section *target, struct Section *other, enum Se
|
|||||||
if (!target->data)
|
if (!target->data)
|
||||||
errx(1, "Failed to concatenate \"%s\"'s fragments", target->name);
|
errx(1, "Failed to concatenate \"%s\"'s fragments", target->name);
|
||||||
memcpy(target->data + target->size - other->size, other->data, other->size);
|
memcpy(target->data + target->size - other->size, other->data, other->size);
|
||||||
/* Adjust patches' PC offsets */
|
|
||||||
for (uint32_t patchID = 0; patchID < other->nbPatches; patchID++)
|
|
||||||
other->patches[patchID].pcOffset += other->offset;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -250,7 +249,7 @@ static void doSanityChecks(struct Section *section, void *ptr)
|
|||||||
|
|
||||||
/* Too large an alignment may not be satisfiable */
|
/* Too large an alignment may not be satisfiable */
|
||||||
if (section->isAlignFixed && (section->alignMask & startaddr[section->type]))
|
if (section->isAlignFixed && (section->alignMask & startaddr[section->type]))
|
||||||
fail("%s: %s sections cannot be aligned to $%04x bytes",
|
fail("%s: %s sections cannot be aligned to $%04" PRIx16 " bytes",
|
||||||
section->name, typeNames[section->type], section->alignMask + 1);
|
section->name, typeNames[section->type], section->alignMask + 1);
|
||||||
|
|
||||||
uint32_t minbank = bankranges[section->type][0], maxbank = bankranges[section->type][1];
|
uint32_t minbank = bankranges[section->type][0], maxbank = bankranges[section->type][1];
|
||||||
@@ -290,9 +289,9 @@ static void doSanityChecks(struct Section *section, void *ptr)
|
|||||||
startaddr[section->type], endaddr(section->type));
|
startaddr[section->type], endaddr(section->type));
|
||||||
|
|
||||||
if (section->org + section->size > endaddr(section->type) + 1)
|
if (section->org + section->size > endaddr(section->type) + 1)
|
||||||
fail("Section \"%s\"'s end address %#x is greater than last address %#x",
|
fail("Section \"%s\"'s end address %#" PRIx16
|
||||||
section->name, section->org + section->size,
|
" is greater than last address %#" PRIx16, section->name,
|
||||||
endaddr(section->type) + 1);
|
section->org + section->size, endaddr(section->type) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef fail
|
#undef fail
|
||||||
|
|||||||
@@ -54,7 +54,10 @@ void sym_AddSymbol(struct Symbol *symbol)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* If not, add it */
|
/* If not, add it */
|
||||||
hash_AddElement(symbols, symbol->name, symbol);
|
bool collided = hash_AddElement(symbols, symbol->name, symbol);
|
||||||
|
|
||||||
|
if (beVerbose && collided)
|
||||||
|
warnx("Symbol hashmap collision occurred!");
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Symbol *sym_GetSymbol(char const *name)
|
struct Symbol *sym_GetSymbol(char const *name)
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ with some bytes being special prefixes for integers and symbols.
|
|||||||
.It Li $04 Ta Li % operator
|
.It Li $04 Ta Li % operator
|
||||||
.It Li $05 Ta Li unary -
|
.It Li $05 Ta Li unary -
|
||||||
.It Li $06 Ta Li ** operator
|
.It Li $06 Ta Li ** operator
|
||||||
.It Li $10 Ta Li \&| operator
|
.It Li $10 Ta Li | operator
|
||||||
.It Li $11 Ta Li & operator
|
.It Li $11 Ta Li & operator
|
||||||
.It Li $12 Ta Li ^ operator
|
.It Li $12 Ta Li ^ operator
|
||||||
.It Li $13 Ta Li unary ~
|
.It Li $13 Ta Li unary ~
|
||||||
@@ -248,10 +248,6 @@ Symbol ID follows, where -1 means PC
|
|||||||
.It Li $51 Ta Li BANK(section_name) ,
|
.It Li $51 Ta Li BANK(section_name) ,
|
||||||
a null-terminated string follows.
|
a null-terminated string follows.
|
||||||
.It Li $52 Ta Li Current BANK()
|
.It Li $52 Ta Li Current BANK()
|
||||||
.It Li $53 Ta Li SIZEOF(section_name) ,
|
|
||||||
a null-terminated string follows.
|
|
||||||
.It Li $54 Ta Li STARTOF(section_name) ,
|
|
||||||
a null-terminated string follows.
|
|
||||||
.It Li $60 Ta Li HRAMCheck .
|
.It Li $60 Ta Li HRAMCheck .
|
||||||
Checks if the value is in HRAM, ANDs it with 0xFF.
|
Checks if the value is in HRAM, ANDs it with 0xFF.
|
||||||
.It Li $61 Ta Li RSTCheck .
|
.It Li $61 Ta Li RSTCheck .
|
||||||
|
|||||||
@@ -18,10 +18,6 @@ $ rgbasm \-o bar.o foo.asm
|
|||||||
$ rgblink \-o baz.gb bar.o
|
$ rgblink \-o baz.gb bar.o
|
||||||
$ rgbfix \-v \-p 0 baz.gb
|
$ rgbfix \-v \-p 0 baz.gb
|
||||||
.Ed
|
.Ed
|
||||||
Or in a single command line:
|
|
||||||
.Bd -literal -offset indent
|
|
||||||
$ rgbasm \-o - foo.asm | rgblink \-o - - | rgbfix \-v \-p 0 - > baz.gb
|
|
||||||
.Ed
|
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr rgbasm 1 ,
|
.Xr rgbasm 1 ,
|
||||||
.Xr rgbfix 1 ,
|
.Xr rgbfix 1 ,
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
ERROR: align-pc-outside-section.asm(1):
|
FATAL: align-pc-outside-section.asm(1):
|
||||||
Cannot output data outside of a SECTION
|
Code generation before SECTION directive
|
||||||
error: Assembly aborted (1 error)!
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
SECTION UNION "X", WRAM0
|
|
||||||
|
|
||||||
SECTION UNION "X", WRAM0, ALIGN[16]
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
ERROR: align-unattainable.asm(3):
|
|
||||||
Section "X"'s alignment cannot be attained in WRAM0
|
|
||||||
error: Assembly aborted (1 error)!
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
ERROR: block-comment-termination-error.asm(1):
|
ERROR: block-comment-termination-error.asm(1):
|
||||||
Unterminated block comment
|
Unterminated block comment
|
||||||
ERROR: block-comment-termination-error.asm(1):
|
ERROR: block-comment-termination-error.asm(1):
|
||||||
syntax error, unexpected end of buffer
|
syntax error, unexpected newline
|
||||||
error: Assembly aborted (2 errors)!
|
error: Assembly aborted (2 errors)!
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
MACRO printargs
|
|
||||||
PRINTLN "first = \<1>"
|
|
||||||
FOR I, 2, _NARG
|
|
||||||
PRINTLN "next = \<{d:I}>"
|
|
||||||
ENDR
|
|
||||||
PRINTLN "last = \<{d:_NARG}>"
|
|
||||||
ENDM
|
|
||||||
|
|
||||||
printargs A, B, C, D
|
|
||||||
|
|
||||||
MACRO mac
|
|
||||||
println \<2__> + \<1_2> + \<\1>
|
|
||||||
x = 2
|
|
||||||
println \<{d:x}> + \<1_{d:x}> + \<\<\<13>>>
|
|
||||||
y equs "NARG"
|
|
||||||
println \<x> + \<1_{d:x}_> + \<\<\<_{y}>>>
|
|
||||||
ENDM
|
|
||||||
|
|
||||||
mac 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 1
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
first = A
|
|
||||||
next = B
|
|
||||||
next = C
|
|
||||||
last = D
|
|
||||||
$F0
|
|
||||||
$F0
|
|
||||||
$F0
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
macro tickle
|
|
||||||
; There once was a bug where overwriting worked only on the second try, so
|
|
||||||
; try everything twice for good measure
|
|
||||||
|
|
||||||
; Skip this syntax for EQUS, as it is invalid
|
|
||||||
IF \2
|
|
||||||
\1 = 0
|
|
||||||
\1 = 0
|
|
||||||
PRINTLN \1
|
|
||||||
|
|
||||||
\1 EQU 0
|
|
||||||
\1 EQU 0
|
|
||||||
PRINTLN \1
|
|
||||||
ENDC
|
|
||||||
|
|
||||||
PURGE \1
|
|
||||||
PURGE \1
|
|
||||||
PRINTLN \1
|
|
||||||
|
|
||||||
DEF \1 EQU 0
|
|
||||||
DEF \1 EQU 0
|
|
||||||
PRINTLN \1
|
|
||||||
|
|
||||||
DEF \1 = 0
|
|
||||||
DEF \1 = 0
|
|
||||||
PRINTLN \1
|
|
||||||
|
|
||||||
DEF \1 EQUS "hello"
|
|
||||||
DEF \1 EQUS "hello"
|
|
||||||
PRINTLN \1
|
|
||||||
|
|
||||||
REDEF \1 = 0
|
|
||||||
REDEF \1 = 0
|
|
||||||
PRINTLN \1
|
|
||||||
|
|
||||||
REDEF \1 EQUS "hello"
|
|
||||||
REDEF \1 EQUS "hello"
|
|
||||||
PRINTLN \1
|
|
||||||
endm
|
|
||||||
|
|
||||||
; Representative numeric and string builtins
|
|
||||||
tickle __LINE__, 1
|
|
||||||
tickle __FILE__, 0
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(7):
|
|
||||||
'__LINE__' already defined as constant at <builtin>
|
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(8):
|
|
||||||
'__LINE__' already defined as constant at <builtin>
|
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(11):
|
|
||||||
'__LINE__' already defined at <builtin>
|
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(12):
|
|
||||||
'__LINE__' already defined at <builtin>
|
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(16):
|
|
||||||
Built-in symbol '__LINE__' cannot be purged
|
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(17):
|
|
||||||
Built-in symbol '__LINE__' cannot be purged
|
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(20):
|
|
||||||
'__LINE__' already defined at <builtin>
|
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(21):
|
|
||||||
'__LINE__' already defined at <builtin>
|
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(24):
|
|
||||||
'__LINE__' already defined as constant at <builtin>
|
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(25):
|
|
||||||
'__LINE__' already defined as constant at <builtin>
|
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(28):
|
|
||||||
'__LINE__' already defined at <builtin>
|
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(29):
|
|
||||||
'__LINE__' already defined at <builtin>
|
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(32):
|
|
||||||
'__LINE__' already defined as constant at <builtin>
|
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(33):
|
|
||||||
'__LINE__' already defined as constant at <builtin>
|
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(36):
|
|
||||||
'__LINE__' already defined as non-EQUS at <builtin>
|
|
||||||
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(37):
|
|
||||||
'__LINE__' already defined as non-EQUS at <builtin>
|
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(16):
|
|
||||||
Built-in symbol '__FILE__' cannot be purged
|
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(17):
|
|
||||||
Built-in symbol '__FILE__' cannot be purged
|
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(20):
|
|
||||||
'__FILE__' already defined at <builtin>
|
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(21):
|
|
||||||
'__FILE__' already defined at <builtin>
|
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(24):
|
|
||||||
'__FILE__' already defined as constant at <builtin>
|
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(25):
|
|
||||||
'__FILE__' already defined as constant at <builtin>
|
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(28):
|
|
||||||
'__FILE__' already defined at <builtin>
|
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(29):
|
|
||||||
'__FILE__' already defined at <builtin>
|
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(32):
|
|
||||||
'__FILE__' already defined as constant at <builtin>
|
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(33):
|
|
||||||
'__FILE__' already defined as constant at <builtin>
|
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(36):
|
|
||||||
Built-in symbol '__FILE__' cannot be redefined
|
|
||||||
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(37):
|
|
||||||
Built-in symbol '__FILE__' cannot be redefined
|
|
||||||
error: Assembly aborted (28 errors)!
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
$9
|
|
||||||
$D
|
|
||||||
$12
|
|
||||||
$16
|
|
||||||
$1A
|
|
||||||
$1E
|
|
||||||
$22
|
|
||||||
$26
|
|
||||||
builtin-overwrite.asm
|
|
||||||
builtin-overwrite.asm
|
|
||||||
builtin-overwrite.asm
|
|
||||||
builtin-overwrite.asm
|
|
||||||
builtin-overwrite.asm
|
|
||||||
builtin-overwrite.asm
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
charmap "<NULL>", $00
|
|
||||||
charmap "A", $10
|
|
||||||
charmap "B", $20
|
|
||||||
charmap "C", $30
|
|
||||||
charmap "Bold", $88
|
|
||||||
|
|
||||||
SECTION "test", ROM0
|
|
||||||
|
|
||||||
S EQUS "XBold<NULL>ABC"
|
|
||||||
|
|
||||||
assert CHARLEN("{S}") == 6
|
|
||||||
println CHARSUB("{S}", 2)
|
|
||||||
assert !STRCMP(CHARSUB("{S}", 2), "Bold")
|
|
||||||
assert CHARSUB("{S}", -5) == CHARSUB("{S}", CHARLEN("{S}") + 1 - 5)
|
|
||||||
assert CHARSUB("{S}", 2) == "Bold" && "Bold" == $88
|
|
||||||
assert CHARSUB("{S}", 1) == $58 ; ASCII "X"
|
|
||||||
db "{S}"
|
|
||||||
|
|
||||||
newcharmap ascii
|
|
||||||
|
|
||||||
assert CHARLEN("{S}") == 14
|
|
||||||
println CHARSUB("{S}", 2)
|
|
||||||
assert !STRCMP(CHARSUB("{S}", 2), "B")
|
|
||||||
assert CHARSUB("{S}", -5) == CHARSUB("{S}", CHARLEN("{S}") + 1 - 5)
|
|
||||||
assert CHARSUB("{S}", 2) == "B" && "B" == $42 ; ASCII "B"
|
|
||||||
assert CHARSUB("{S}", 1) == $58 ; ASCII "X"
|
|
||||||
db "{S}"
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
Bold
|
|
||||||
B
|
|
||||||
Binary file not shown.
@@ -1,15 +1,15 @@
|
|||||||
ERROR: code-after-endm-endr-endc.asm(6):
|
ERROR: code-after-endm-endr-endc.asm(6):
|
||||||
syntax error, unexpected PRINTLN, expecting newline or end of buffer
|
syntax error, unexpected PRINTLN, expecting newline
|
||||||
ERROR: code-after-endm-endr-endc.asm(7):
|
ERROR: code-after-endm-endr-endc.asm(7):
|
||||||
Macro "mac" not defined
|
Macro "mac" not defined
|
||||||
ERROR: code-after-endm-endr-endc.asm(12):
|
ERROR: code-after-endm-endr-endc.asm(12):
|
||||||
syntax error, unexpected PRINTLN, expecting newline or end of buffer
|
syntax error, unexpected PRINTLN, expecting newline
|
||||||
ERROR: code-after-endm-endr-endc.asm(17):
|
ERROR: code-after-endm-endr-endc.asm(17):
|
||||||
syntax error, unexpected PRINTLN, expecting newline
|
syntax error, unexpected PRINTLN, expecting newline
|
||||||
ERROR: code-after-endm-endr-endc.asm(19):
|
ERROR: code-after-endm-endr-endc.asm(19):
|
||||||
syntax error, unexpected PRINTLN, expecting newline or end of buffer
|
syntax error, unexpected PRINTLN, expecting newline
|
||||||
ERROR: code-after-endm-endr-endc.asm(23):
|
ERROR: code-after-endm-endr-endc.asm(23):
|
||||||
syntax error, unexpected PRINTLN, expecting newline
|
syntax error, unexpected PRINTLN, expecting newline
|
||||||
ERROR: code-after-endm-endr-endc.asm(25):
|
ERROR: code-after-endm-endr-endc.asm(25):
|
||||||
syntax error, unexpected PRINTLN, expecting newline or end of buffer
|
syntax error, unexpected PRINTLN, expecting newline
|
||||||
error: Assembly aborted (7 errors)!
|
error: Assembly aborted (7 errors)!
|
||||||
|
|||||||
@@ -26,5 +26,4 @@ def constant equ 6*7 ; fails
|
|||||||
redef string equs "there"
|
redef string equs "there"
|
||||||
println "{string}"
|
println "{string}"
|
||||||
|
|
||||||
redef constant equ 6*9
|
redef constant equ 6*9 ; syntax error
|
||||||
println constant
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
ERROR: def.asm(23):
|
ERROR: def.asm(23):
|
||||||
'constant' already defined at def.asm(10)
|
'constant' already defined at def.asm(10)
|
||||||
error: Assembly aborted (1 error)!
|
ERROR: def.asm(29):
|
||||||
|
syntax error, unexpected EQU, expecting SET or = or EQUS
|
||||||
|
error: Assembly aborted (2 errors)!
|
||||||
|
|||||||
@@ -7,4 +7,3 @@ here
|
|||||||
$0 $1 $5 $9
|
$0 $1 $5 $9
|
||||||
$2A
|
$2A
|
||||||
there
|
there
|
||||||
$36
|
|
||||||
|
|||||||
@@ -1,62 +1,36 @@
|
|||||||
def _ASM equ 0
|
_ASM equ 0
|
||||||
|
|
||||||
test: MACRO
|
test: MACRO
|
||||||
; Test RGBASM
|
; Test RGBASM
|
||||||
redef V equs "_ASM +"
|
V equs "_ASM +"
|
||||||
static_assert \#
|
static_assert \#
|
||||||
; Test RGBLINK
|
PURGE V
|
||||||
redef V equs "_LINK +"
|
; Test RGBLINK
|
||||||
|
V equs "_LINK +"
|
||||||
assert \#
|
assert \#
|
||||||
|
PURGE V
|
||||||
ENDM
|
ENDM
|
||||||
|
|
||||||
test_mod: MACRO
|
for x, -300, 301
|
||||||
def x = \1 ; dividend
|
for y, -x - 1, x + 2
|
||||||
def y = \2 ; divisor
|
if y != 0
|
||||||
shift 2
|
q = x / y
|
||||||
def q = x / y ; quotient
|
r = x % y
|
||||||
def r = x % y ; remainder
|
test (V (q * y + r)) == (V x)
|
||||||
; identity laws
|
test (V (x + y) % y) == (V r)
|
||||||
test (V (q * y + r)) == (V x)
|
test (V (x - y) % y) == (V r)
|
||||||
test (V (x + y) % y) == (V r)
|
endc
|
||||||
test (V (x - y) % y) == (V r)
|
endr
|
||||||
ENDM
|
endr
|
||||||
|
|
||||||
test_each_mod: MACRO
|
for x, -300, 301
|
||||||
test_mod (\1), (\2)
|
for p, 31
|
||||||
test_mod (\1), -(\2)
|
y = 2 ** p
|
||||||
test_mod -(\1), (\2)
|
r = x % y
|
||||||
test_mod -(\1), -(\2)
|
m = x & (y - 1)
|
||||||
ENDM
|
test (V r) == (V m)
|
||||||
|
endr
|
||||||
test_pow: MACRO
|
endr
|
||||||
def x = \1 ; dividend
|
|
||||||
def y = 2 ** \2 ; divisor
|
|
||||||
def r = x % y ; remainder
|
|
||||||
def m = x & (y - 1) ; mask
|
|
||||||
; identity law
|
|
||||||
test (V r) == (V m)
|
|
||||||
ENDM
|
|
||||||
|
|
||||||
test_each_pow: MACRO
|
|
||||||
test_pow (\1), (\2)
|
|
||||||
test_pow -(\1), (\2)
|
|
||||||
ENDM
|
|
||||||
|
|
||||||
test_each_mod 0, 1
|
|
||||||
test_each_mod 7, 5
|
|
||||||
test_each_mod 42, 256
|
|
||||||
test_each_mod 567, 256
|
|
||||||
test_each_mod 256, 512
|
|
||||||
test_each_mod 1, 65535
|
|
||||||
test_each_mod 100, 65535
|
|
||||||
test_each_mod 10000, 65535
|
|
||||||
test_each_mod 1000000, 65535
|
|
||||||
|
|
||||||
test_each_pow 5, 1
|
|
||||||
test_each_pow 42, 8
|
|
||||||
test_each_pow 567, 8
|
|
||||||
test_each_pow 12345, 16
|
|
||||||
test_each_pow 99999, 16
|
|
||||||
|
|
||||||
SECTION "LINK", ROM0
|
SECTION "LINK", ROM0
|
||||||
_LINK::
|
_LINK::
|
||||||
|
|||||||
@@ -3,6 +3,5 @@ warning: equs-newline.asm(3): [-Wuser]
|
|||||||
while expanding symbol "ACT"
|
while expanding symbol "ACT"
|
||||||
warning: equs-newline.asm(3): [-Wuser]
|
warning: equs-newline.asm(3): [-Wuser]
|
||||||
Second
|
Second
|
||||||
while expanding symbol "ACT"
|
|
||||||
warning: equs-newline.asm(4): [-Wuser]
|
warning: equs-newline.asm(4): [-Wuser]
|
||||||
Third
|
Third
|
||||||
|
|||||||
@@ -1,2 +1,6 @@
|
|||||||
recurse EQUS "recurse"
|
recurse EQUS "recurse "
|
||||||
recurse
|
recurse
|
||||||
|
|
||||||
|
; FIXME: also handle the following:
|
||||||
|
; recurse EQUS "recurse"
|
||||||
|
; recurse
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
test: MACRO
|
|
||||||
v equs "X"
|
|
||||||
X equs "" ; should not be expanded
|
|
||||||
\1
|
|
||||||
ENDM
|
|
||||||
test v 0
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
ERROR: expand-empty-string.asm(6) -> expand-empty-string.asm::test(4):
|
|
||||||
syntax error, unexpected number
|
|
||||||
error: Assembly aborted (1 error)!
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
ERROR: expand-empty-string.asm(6) -> expand-empty-string.asm::test(4):
|
|
||||||
syntax error
|
|
||||||
error: Assembly aborted (1 error)!
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
|
|
||||||
SECTION "ff00+c or not to ff00+c", ROMX
|
|
||||||
|
|
||||||
ld a, [$ff00 + c]
|
|
||||||
ld [65280 + c], a
|
|
||||||
|
|
||||||
; Not ok
|
|
||||||
ld a, [$ff01 + c]
|
|
||||||
ld [xyz + c], a
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
ERROR: ff00+c-bad.asm(8):
|
|
||||||
Expected constant expression equal to $FF00 for "$ff00+c"
|
|
||||||
ERROR: ff00+c-bad.asm(9):
|
|
||||||
Expected constant expression equal to $FF00 for "$ff00+c"
|
|
||||||
error: Assembly aborted (2 errors)!
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
CONSTANT equ 42
|
|
||||||
PRINTLN $ff00 + CONSTANT
|
|
||||||
|
|
||||||
SECTION "Overreading much?", ROM0[0]
|
|
||||||
|
|
||||||
jp $ff00 + CONSTANT
|
|
||||||
jp $ff00 + carryOnPlease
|
|
||||||
jp $ff00+CONSTANT
|
|
||||||
jp $ff00+carryOnPlease
|
|
||||||
|
|
||||||
carryOnPlease:
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
$FF2A
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<EFBFBD>*<2A><><0C><>*<2A><><0C>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
SECTION "test", ROM0[0]
|
|
||||||
ld [ $ff00 + c ], a
|
|
||||||
; 257 spaces exceeds both LEXER_BUF_SIZE (42) and uint8_t limit (255)
|
|
||||||
ld [ $ff00 + c ], a
|
|
||||||
ld [ $ff00 + c ], a
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user