Compare commits

..

22 Commits

Author SHA1 Message Date
ISSOtm
76c1995559 Fix CI 2021-04-01 11:38:07 +02:00
Rangi
ae84570f04 Revise RGBASM manual 2021-03-31 18:35:09 -04:00
Rangi
094a31ef8c Update RGBASM command-line manual 2021-03-31 18:17:18 -04:00
ISSOtm
291dcf3b6c Update RGBASM manual 2021-04-01 00:11:41 +02:00
Rangi
a890bd072b Fix "INCBIN"
Examples: (...s are optional)

ld [b @:...] = "Dockerfile"
ld [b @:...] = "Dockerfile"[451:...]
ld [b @:...] = "Dockerfile"[23:5]
ld [b @:5] = "Dockerfile"[23:...]
2021-03-31 18:06:14 -04:00
Rangi
2f6c808ccb Revise instruction reference 2021-03-31 17:59:12 -04:00
ISSOtm
e80907abd0 Update instruction reference 2021-03-31 23:26:57 +02:00
Rangi
25d39155d3 Support ld a, a±c±LOW(bc) as well as ld a, a±c±c 2021-03-30 12:36:11 -04:00
Rangi
77021d229b Support ld [c], a and ld a, [c] 2021-03-30 12:02:29 -04:00
Rangi
1b250b90b2 Implement ds <len> ==> ld [b @:<len>], ? 2021-03-30 11:54:39 -04:00
Rangi
e2b4723489 Fix lexing @ 2021-03-30 11:52:25 -04:00
Rangi
2507413162 Fix lexing a., b., etc 2021-03-30 11:41:44 -04:00
Rangi
e023a84d04 Allow 'ld a, a±c±<r8>' or 'ld a, a±<r8>±c' for adc/sbc 2021-03-30 11:27:19 -04:00
Rangi
34c127d9c3 Allow ld [b @:<len>] = "file.bin"[<ofs>:...] 2021-03-30 10:51:48 -04:00
Rangi
9a930988c2 Implement db, dw, dl, ds, and INCBIN with ld
To do: let the `b` in `ld [b @]` be optional, and allow
`ld [b @:<len>] = "file.bin"[<ofs>:...]`
2021-03-30 10:47:05 -04:00
Rangi
8c4204c542 Make 'w' and '...' tokens, and make '@' a separate token
Now '@' is valid as a relocexpr_no_str, in 'BANK(@)', and
in 'DEF(@)', but not in general T_ID or T_LABEL contexts

This will make it easier to implement INCBIN with 'ld'
2021-03-30 10:17:09 -04:00
Rangi
663c1930ec Factor out 'ld a, a+c+' and 'ld a, a-c-' prefixes
This fixes all the shift/reduce and reduce/reduce conflicts
2021-03-30 09:57:08 -04:00
Rangi
30ccf43f44 Factor out individual 'ld <r16>,' prefixes 2021-03-30 09:43:34 -04:00
Rangi
fdc17adbcb Factor out common ld a, prefix 2021-03-30 09:19:58 -04:00
Rangi
cc196954f3 Consolidate some parser rules with reg_ss and reg_r
There are now 5 shift/reduce conflicts and 3 reduce/reduce conflicts
2021-03-29 20:52:24 -04:00
Rangi
55b6cfff84 Prevent GitHub Actions from running any workflows 2021-03-29 19:50:13 -04:00
Rangi
1fc73b04eb Parse ld instructions as discussed
There are 13 shift/reduce conflicts, so some instructions
may need different formats.

This also does not yet implement `db`, `dw`, `dl`, `ds`,
or `INCBIN` using `ld`.

The `lexerState->nextToken` solution to lexing something
like "a.2" as three tokens instead of one identifier
is taken from the first commit in rgbds PR #799.
2021-03-29 19:42:18 -04:00
280 changed files with 2935 additions and 4582 deletions

View File

@@ -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
View File

@@ -1,2 +0,0 @@
# Shell scripts need Unix line endings (see https://github.com/gbdev/rgbds/issues/841)
*.sh text eol=lf

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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"

View File

@@ -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()

View File

@@ -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
-------------- --------------

View File

@@ -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!

View File

@@ -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
------------------- -------------------

View File

@@ -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!

View File

@@ -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 */

View File

@@ -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;
}; };

View File

@@ -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 */

View File

@@ -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 {

View File

@@ -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 */

View File

@@ -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

View File

@@ -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);

View File

@@ -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 */

View File

@@ -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

View File

@@ -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);

View File

@@ -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
*/ */

View File

@@ -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);
/* /*

View File

@@ -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

View File

@@ -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)
{ {

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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>

View File

@@ -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);

View File

@@ -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 = &currentCharmap->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 = &currentCharmap->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 = &currentCharmap->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 = &currentCharmap->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 = &currentCharmap->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 = &currentCharmap->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;
} }

View File

@@ -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';

View File

@@ -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);

File diff suppressed because it is too large Load Diff

View File

@@ -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;
} }

View File

@@ -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);
} }

View File

@@ -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);
} }

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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 "&iacute", 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

View File

@@ -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;
} }
} }

View File

@@ -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);

View File

@@ -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 */

View File

@@ -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)

View File

@@ -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++) {

View File

@@ -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",

View File

@@ -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 .

File diff suppressed because it is too large Load Diff

View File

@@ -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++;
} }

View File

@@ -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++) {

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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;
} }
} }

View File

@@ -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
? &sectList
: &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 = &sectList;
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 = &sections[type].banks[bank]; struct SortedSections const *sect = &sections[type].banks[bank];
writeSymBank(sect, type, bank); writeSymBank(sect);
usedMap[type] += writeMapBank(sect, type, bank); usedMap[type] += writeMapBank(sect, type, bank);
} }
} }

View File

@@ -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;
} }

View File

@@ -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;
/* /*

View File

@@ -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

View File

@@ -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)

View File

@@ -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 .

View File

@@ -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 ,

View File

@@ -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)!

View File

@@ -1,3 +0,0 @@
SECTION UNION "X", WRAM0
SECTION UNION "X", WRAM0, ALIGN[16]

View File

@@ -1,3 +0,0 @@
ERROR: align-unattainable.asm(3):
Section "X"'s alignment cannot be attained in WRAM0
error: Assembly aborted (1 error)!

View File

@@ -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)!

View File

@@ -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

View File

@@ -1,7 +0,0 @@
first = A
next = B
next = C
last = D
$F0
$F0
$F0

View File

@@ -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

View File

@@ -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)!

View File

@@ -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

View File

@@ -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}"

View File

@@ -1,2 +0,0 @@
Bold
B

Binary file not shown.

View File

@@ -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)!

View File

@@ -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

View File

@@ -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)!

View File

@@ -7,4 +7,3 @@ here
$0 $1 $5 $9 $0 $1 $5 $9
$2A $2A
there there
$36

View File

@@ -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::

View File

@@ -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

View File

@@ -1,2 +1,6 @@
recurse EQUS "recurse" recurse EQUS "recurse "
recurse recurse
; FIXME: also handle the following:
; recurse EQUS "recurse"
; recurse

View File

@@ -1,6 +0,0 @@
test: MACRO
v equs "X"
X equs "" ; should not be expanded
\1
ENDM
test v 0

View File

@@ -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)!

View File

@@ -1,3 +0,0 @@
ERROR: expand-empty-string.asm(6) -> expand-empty-string.asm::test(4):
syntax error
error: Assembly aborted (1 error)!

View File

@@ -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

View File

@@ -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)!

View File

@@ -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:

View File

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

View File

@@ -1 +0,0 @@
<EFBFBD>*<2A><> <0C><>*<2A><> <0C>

View File

@@ -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

View File

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