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:
build:
if: github.repository_owner == 'gbdev'
runs-on: ubuntu-18.04
steps:
- name: Checkout rgbds@release

View File

@@ -88,9 +88,9 @@ jobs:
Write-Host "libpng SHA256 mismatch! ($hash)"
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
if ($hash -ne '39c6086ce211d5415500acc5ed2d8939861ca1696aee48909c7f6daf5122b505') {
if ($hash -ne '6AA5C8EA662DA1550020A5804C28BE63FFAA53486DA9F6842E24C379EC422DFC') {
Write-Host "bison SHA256 mismatch! ($hash)"
}
Expand-Archive -DestinationPath . "zlib.zip"

View File

@@ -17,7 +17,6 @@ on:
jobs:
build:
if: github.repository_owner == 'gbdev'
runs-on: ubuntu-18.04
steps:
- name: Checkout rgbds@master

View File

@@ -29,9 +29,9 @@ jobs:
Write-Host "libpng SHA256 mismatch! ($hash)"
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
if ($hash -ne '39c6086ce211d5415500acc5ed2d8939861ca1696aee48909c7f6daf5122b505') {
if ($hash -ne '6AA5C8EA662DA1550020A5804C28BE63FFAA53486DA9F6842E24C379EC422DFC') {
Write-Host "bison SHA256 mismatch! ($hash)"
}
Expand-Archive -DestinationPath . "zlib.zip"

View File

@@ -35,13 +35,12 @@ if(MSVC)
add_definitions(/D_CRT_SECURE_NO_WARNINGS)
else()
add_compile_options(-Wall -pedantic)
add_definitions(-D_POSIX_C_SOURCE=200809L -D_ISOC11_SOURCE)
if(SANITIZERS)
set(SAN_FLAGS -fsanitize=shift -fsanitize=integer-divide-by-zero
-fsanitize=unreachable -fsanitize=vla-bound
-fsanitize=signed-integer-overflow -fsanitize=bounds
-fsanitize=object-size -fsanitize=bool -fsanitize=enum
-fsanitize=alignment -fsanitize=null -fsanitize=address)
-fsanitize=alignment -fsanitize=null)
add_compile_options(${SAN_FLAGS})
link_libraries(${SAN_FLAGS})
endif()

View File

@@ -1,9 +1,10 @@
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
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
--------------

View File

@@ -35,9 +35,8 @@ WARNFLAGS := -Wall
# Overridable CFLAGS
CFLAGS ?= -O3 -flto -DNDEBUG
# 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 -I include \
-D_POSIX_C_SOURCE=200809L -D_ISOC11_SOURCE
REALCFLAGS := ${CFLAGS} ${WARNFLAGS} -std=gnu11 -D_POSIX_C_SOURCE=200809L \
-Iinclude
# Overridable LDFLAGS
LDFLAGS ?=
# Non-overridable LDFLAGS
@@ -61,9 +60,9 @@ rgbasm_obj := \
src/asm/lexer.o \
src/asm/macro.o \
src/asm/main.o \
src/asm/parser.o \
src/asm/opt.o \
src/asm/output.o \
src/asm/parser.o \
src/asm/rpn.o \
src/asm/section.o \
src/asm/symbol.o \
@@ -204,7 +203,7 @@ checkpatch:
# compilation and make the continous integration infrastructure return failure.
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 \
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused \
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=4 \
@@ -216,8 +215,7 @@ develop:
-fsanitize=unreachable -fsanitize=vla-bound \
-fsanitize=signed-integer-overflow -fsanitize=bounds \
-fsanitize=object-size -fsanitize=bool -fsanitize=enum \
-fsanitize=alignment -fsanitize=null -fsanitize=address" \
CFLAGS="-ggdb3 -O0"
-fsanitize=alignment -fsanitize=null" CFLAGS="-ggdb3 -O0"
# Targets for the project maintainer to easily create Windows exes.
# 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
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
-------------------

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_Add(char *mapping, uint8_t value);
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 */

View File

@@ -9,8 +9,8 @@
#ifndef RGBDS_FORMAT_SPEC_H
#define RGBDS_FORMAT_SPEC_H
#include <stdbool.h>
#include <stdint.h>
#include <stdbool.h>
enum FormatState {
FORMAT_SIGN, // expects '+' or ' ' (optional)
@@ -28,9 +28,9 @@ struct FormatSpec {
bool prefix;
bool alignLeft;
bool padZero;
size_t width;
uint8_t width;
bool hasFrac;
size_t fracWidth;
uint8_t fracWidth;
int type;
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 */
};
extern size_t maxRecursionDepth;
extern size_t nMaxRecursionDepth;
struct MacroArgs;
@@ -72,12 +72,12 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size);
bool yywrap(void);
void fstk_RunInclude(char const *path);
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,
int32_t reptLineNo, char *body, size_t size);
void fstk_StopRept(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 */

View File

@@ -53,7 +53,7 @@ static inline void lexer_SetGfxDigits(char const digits[4])
* `path` is referenced, but not held onto..!
*/
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_DeleteState(struct LexerState *state);
void lexer_Init(void);
@@ -88,8 +88,8 @@ uint32_t lexer_GetLineNo(void);
uint32_t lexer_GetColNo(void);
void lexer_DumpStringExpansions(void);
int yylex(void);
bool lexer_CaptureRept(struct CaptureBody *capture);
bool lexer_CaptureMacroBody(struct CaptureBody *capture);
void lexer_CaptureRept(struct CaptureBody *capture);
void lexer_CaptureMacroBody(struct CaptureBody *capture);
#define INITIAL_DS_ARG_SIZE 2
struct DsArgList {

View File

@@ -16,14 +16,22 @@
#include "helpers.h"
extern bool haltnop;
extern bool optimizeLoads;
extern bool optimizeloads;
extern bool verbose;
extern bool warnings; /* True to enable warnings, false to disable them. */
extern FILE *dependfile;
extern char *targetFileName;
extern bool generatedMissingIncludes;
extern bool failedOnMissingInclude;
extern bool generatePhonyDeps;
extern char *tzTargetFileName;
extern bool oGeneratedMissingIncludes;
extern bool oFailedOnMissingInclude;
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 */

View File

@@ -9,16 +9,14 @@
#ifndef RGBDS_OPT_H
#define RGBDS_OPT_H
#include <stdbool.h>
void opt_B(char chars[2]);
void opt_G(char chars[4]);
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_Push(void);
void opt_Pop(void);
#endif

View File

@@ -16,8 +16,8 @@
struct Expression;
struct FileStackNode;
extern char *objectName;
extern struct Section *sectionList;
extern char *tzObjectname;
extern struct Section *pSectionList, *pCurrentSection;
void out_RegisterNode(struct FileStackNode *node);
void out_ReplaceNode(struct FileStackNode *node);

View File

@@ -17,20 +17,20 @@
#define MAXRPNLEN 1048576
struct Expression {
int32_t val; // If the expression's value is known, it's here
char *reason; // Why the expression is not known, if it isn't
bool isKnown; // Whether the expression's value is known
bool isSymbol; // Whether the expression represents a symbol
uint8_t *rpn; // Array of bytes serializing the RPN expression
uint32_t rpnCapacity; // Size of the `rpn` buffer
uint32_t rpnLength; // Used size of the `rpn` buffer
uint32_t rpnPatchSize; // Size the expression will take in the obj file
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
bool isKnown; // Whether the expression's value is known
bool isSymbol; // Whether the expression represents a symbol
uint8_t *tRPN; // Array of bytes serializing the RPN expression
uint32_t nRPNCapacity; // Size of the `tRPN` buffer
uint32_t nRPNLength; // Used size of the `tRPN` buffer
uint32_t nRPNPatchSize; // Size the expression will take in the obj file
};
/*
* 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;
}
@@ -43,11 +43,11 @@ static inline bool rpn_isSymbol(const struct Expression *expr)
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_LOGNOT(struct Expression *expr, const struct Expression *src);
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,
const struct Expression *src1,
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_UNNEG(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_BankSection(struct Expression *expr, char const *sectionName);
void rpn_BankSymbol(struct Expression *expr, char const *tzSym);
void rpn_BankSection(struct Expression *expr, char const *tzSectionName);
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_CheckHRAM(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 */

View File

@@ -40,14 +40,14 @@ struct SectionSpec {
uint16_t alignOfs;
};
extern struct Section *currentSection;
struct Section *sect_FindSectionByName(const char *name);
void sect_NewSection(char const *name, uint32_t secttype, uint32_t org,
struct SectionSpec const *attributes, enum SectionModifier mod);
void sect_SetLoadSection(char const *name, uint32_t secttype, uint32_t org,
struct SectionSpec const *attributes, enum SectionModifier mod);
void sect_EndLoadSection(void);
struct Section *out_FindSectionByName(const char *name);
void out_NewSection(char const *name, uint32_t secttype, uint32_t org,
struct SectionSpec const *attributes,
enum SectionModifier mod);
void out_SetLoadSection(char const *name, uint32_t secttype, uint32_t org,
struct SectionSpec const *attributes,
enum SectionModifier mod);
void out_EndLoadSection(void);
struct Section *sect_GetSymbolSection(void);
uint32_t sect_GetSymbolOffset(void);
@@ -59,21 +59,21 @@ void sect_NextUnionMember(void);
void sect_EndUnion(void);
void sect_CheckUnionClosed(void);
void sect_AbsByte(uint8_t b);
void sect_AbsByteGroup(uint8_t const *s, int32_t length);
void sect_AbsWordGroup(uint8_t const *s, int32_t length);
void sect_AbsLongGroup(uint8_t const *s, int32_t length);
void sect_Skip(int32_t skip, bool ds);
void sect_String(char const *s);
void sect_RelByte(struct Expression *expr, uint32_t pcShift);
void sect_RelBytes(uint32_t n, struct Expression *exprs, size_t size);
void sect_RelWord(struct Expression *expr, uint32_t pcShift);
void sect_RelLong(struct Expression *expr, uint32_t pcShift);
void sect_PCRelByte(struct Expression *expr, uint32_t pcShift);
void sect_BinaryFile(char const *s, int32_t startPos);
void sect_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length);
void out_AbsByte(uint8_t b);
void out_AbsByteGroup(uint8_t const *s, int32_t length);
void out_AbsWordGroup(uint8_t const *s, int32_t length);
void out_AbsLongGroup(uint8_t const *s, int32_t length);
void out_Skip(int32_t skip, bool ds);
void out_String(char const *s);
void out_RelByte(struct Expression *expr, uint32_t pcShift);
void out_RelBytes(uint32_t n, struct Expression *exprs, size_t size);
void out_RelWord(struct Expression *expr, uint32_t pcShift);
void out_RelLong(struct Expression *expr, uint32_t pcShift);
void out_PCRelByte(struct Expression *expr, uint32_t pcShift);
void out_BinaryFile(char const *s, int32_t startPos);
void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length);
void sect_PushSection(void);
void sect_PopSection(void);
void out_PushSection(void);
void out_PopSection(void);
#endif

View File

@@ -45,13 +45,13 @@ struct Symbol {
/* If sym_IsNumeric */
int32_t value;
int32_t (*numCallback)(void);
/* For SYM_MACRO and SYM_EQUS; TODO: have separate fields */
/* For SYM_MACRO */
struct {
size_t macroSize;
char *macro;
};
/* For SYM_EQUS */
char const *(*strCallback)(void);
/* For SYM_EQUS, TODO: separate "base" fields from SYM_MACRO */
char const *(*strCallback)(void); /* For SYM_EQUS */
};
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)
{
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)
@@ -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_Export(char const *symName);
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);
uint32_t sym_GetPCValue(void);
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
*/
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
*/
struct Symbol *sym_FindUnscopedSymbol(char const *symName);
struct Symbol *sym_FindUnscopedSymbol(char const *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 *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size);
struct Symbol *sym_Ref(char const *symName);

View File

@@ -11,8 +11,8 @@
#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
*/

View File

@@ -13,16 +13,8 @@
extern unsigned int nbErrors;
enum WarningState {
WARNING_DEFAULT,
WARNING_DISABLED,
WARNING_ENABLED,
WARNING_ERROR
};
enum WarningID {
WARNING_ASSERT, /* Assertions */
WARNING_BACKWARDS_FOR, /* `for` loop with backwards range */
WARNING_BUILTIN_ARG, /* Invalid args to builtins */
WARNING_CHARMAP_REDEF, /* Charmap entry re-definition */
WARNING_DIV, /* Division undefined behavior */
@@ -50,9 +42,6 @@ enum WarningID {
#define NB_META_WARNINGS (NB_WARNINGS_ALL - NB_WARNINGS)
};
extern enum WarningState warningStates[NB_WARNINGS];
extern bool warningsAreErrors;
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 key The key with which the element will be stored and retrieved
* @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.
@@ -41,14 +51,6 @@ void **hash_AddElement(HashMap map, char const *key, void *element);
*/
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.
* @param map The map to consider the elements of

View File

@@ -18,7 +18,6 @@
#ifdef __GNUC__ // GCC or compatible
#define 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
#ifdef NDEBUG
#define unreachable_ __builtin_unreachable
@@ -28,7 +27,6 @@
#else
// Unsupported, but no need to throw a fit
#define format_(archetype, str_index, first_arg)
#define attr_(...)
// This seems to generate similar code to __builtin_unreachable, despite different semantics
// Note that executing this is undefined behavior (declared _Noreturn, but does return)
static inline _Noreturn unreachable_(void) {}
@@ -61,6 +59,7 @@
}
#else
// FIXME: these are rarely used, and need testing...
#include <limits.h>
static inline int ctz(unsigned int x)
{

View File

@@ -21,7 +21,10 @@ struct Assertion {
struct Patch patch;
// enum AssertionType type; The `patch`'s field is instead re-used
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 Assertion *next;

View File

@@ -14,7 +14,7 @@
#define RGBDS_OBJECT_VERSION_STRING "RGB%1u"
#define RGBDS_OBJECT_VERSION_NUMBER 9U
#define RGBDS_OBJECT_REV 8U
#define RGBDS_OBJECT_REV 7U
enum AssertionType {
ASSERT_WARN,
@@ -23,44 +23,42 @@ enum AssertionType {
};
enum RPNCommand {
RPN_ADD = 0x00,
RPN_SUB = 0x01,
RPN_MUL = 0x02,
RPN_DIV = 0x03,
RPN_MOD = 0x04,
RPN_UNSUB = 0x05, // FIXME: should be renamed to "NEG" for consistency
RPN_EXP = 0x06,
RPN_ADD = 0x00,
RPN_SUB = 0x01,
RPN_MUL = 0x02,
RPN_DIV = 0x03,
RPN_MOD = 0x04,
RPN_UNSUB = 0x05,
RPN_EXP = 0x06,
RPN_OR = 0x10,
RPN_AND = 0x11,
RPN_XOR = 0x12,
RPN_UNNOT = 0x13, // FIXME: should be renamed to "NOT" for consistency
RPN_OR = 0x10,
RPN_AND = 0x11,
RPN_XOR = 0x12,
RPN_UNNOT = 0x13,
RPN_LOGAND = 0x21,
RPN_LOGOR = 0x22,
RPN_LOGUNNOT = 0x23, // FIXME: should be renamed to "LOGNOT" for consistency
RPN_LOGAND = 0x21,
RPN_LOGOR = 0x22,
RPN_LOGUNNOT = 0x23,
RPN_LOGEQ = 0x30,
RPN_LOGNE = 0x31,
RPN_LOGGT = 0x32,
RPN_LOGLT = 0x33,
RPN_LOGGE = 0x34,
RPN_LOGLE = 0x35,
RPN_LOGEQ = 0x30,
RPN_LOGNE = 0x31,
RPN_LOGGT = 0x32,
RPN_LOGLT = 0x33,
RPN_LOGGE = 0x34,
RPN_LOGLE = 0x35,
RPN_SHL = 0x40,
RPN_SHR = 0x41,
RPN_SHL = 0x40,
RPN_SHR = 0x41,
RPN_BANK_SYM = 0x50,
RPN_BANK_SECT = 0x51,
RPN_BANK_SELF = 0x52,
RPN_SIZEOF_SECT = 0x53,
RPN_STARTOF_SECT = 0x54,
RPN_BANK_SYM = 0x50,
RPN_BANK_SECT = 0x51,
RPN_BANK_SELF = 0x52,
RPN_HRAM = 0x60,
RPN_RST = 0x61,
RPN_HRAM = 0x60,
RPN_RST = 0x61,
RPN_CONST = 0x80,
RPN_SYM = 0x81
RPN_CONST = 0x80,
RPN_SYM = 0x81
};
enum SectionType {

View File

@@ -11,10 +11,9 @@
#ifndef 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
# include <string.h>
# define strcasecmp _stricmp
# define strncasecmp _strnicmp
#else
# include <strings.h>

View File

@@ -11,7 +11,8 @@
#define PACKAGE_VERSION_MAJOR 0
#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);

View File

@@ -44,35 +44,32 @@ struct Charmap {
static HashMap charmaps;
/*
* Store pointers to hashmap nodes, so that there is only one pointer to the memory block
* that gets reallocated.
*/
static struct Charmap **currentCharmap;
static struct Charmap *currentCharmap;
struct CharmapStackEntry {
struct Charmap **charmap;
struct Charmap *charmap;
struct CharmapStackEntry *next;
};
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);
}
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",
*map ? "create" : "resize", strerror(errno));
(**map).capacity = capacity;
map ? "create" : "resize", strerror(errno));
new->capacity = capacity;
return new;
}
static void initNode(struct Charnode *node)
static inline void initNode(struct Charnode *node)
{
node->isTerminal = false;
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 */
if (base) {
resizeCharmap(&charmap, base->capacity);
charmap = resizeCharmap(NULL, base->capacity);
charmap->usedNodes = base->usedNodes;
memcpy(charmap->nodes, base->nodes, sizeof(base->nodes[0]) * charmap->usedNodes);
} else {
resizeCharmap(&charmap, INITIAL_CAPACITY);
charmap = resizeCharmap(NULL, INITIAL_CAPACITY);
charmap->usedNodes = 1;
initNode(&charmap->nodes[0]); /* Init the root node */
}
charmap->name = strdup(name);
currentCharmap = (struct Charmap **)hash_AddElement(charmaps, charmap->name, charmap);
hash_AddElement(charmaps, charmap->name, charmap);
currentCharmap = charmap;
return charmap;
}
@@ -122,7 +120,7 @@ void charmap_Delete(struct Charmap *charmap)
void charmap_Set(const char *name)
{
struct Charmap **charmap = (struct Charmap **)hash_GetNode(charmaps, name);
struct Charmap *charmap = charmap_Get(name);
if (charmap == NULL)
error("Charmap '%s' doesn't exist\n", name);
@@ -160,26 +158,25 @@ void charmap_Pop(void)
void charmap_Add(char *mapping, uint8_t value)
{
struct Charmap *charmap = *currentCharmap;
struct Charnode *node = &charmap->nodes[0];
struct Charnode *node = &currentCharmap->nodes[0];
for (uint8_t c; *mapping; mapping++) {
c = *mapping - 1;
if (node->next[c]) {
node = &charmap->nodes[node->next[c]];
node = &currentCharmap->nodes[node->next[c]];
} else {
/* Register next available node */
node->next[c] = charmap->usedNodes;
node->next[c] = currentCharmap->usedNodes;
/* If no more nodes are available, get new ones */
if (charmap->usedNodes == charmap->capacity) {
charmap->capacity *= 2;
resizeCharmap(currentCharmap, charmap->capacity);
charmap = *currentCharmap;
if (currentCharmap->usedNodes == currentCharmap->capacity) {
currentCharmap->capacity *= 2;
currentCharmap = resizeCharmap(currentCharmap, currentCharmap->capacity);
hash_ReplaceElement(charmaps, currentCharmap->name, currentCharmap);
}
/* Switch to and init new node */
node = &charmap->nodes[charmap->usedNodes++];
node = &currentCharmap->nodes[currentCharmap->usedNodes++];
initNode(node);
}
}
@@ -192,16 +189,6 @@ void charmap_Add(char *mapping, uint8_t value)
}
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.
@@ -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 no match, read a UTF-8 codepoint and output that.
*/
struct Charmap const *charmap = *currentCharmap;
struct Charnode const *node = &charmap->nodes[0];
size_t outputLen = 0;
struct Charnode const *node = &currentCharmap->nodes[0];
struct Charnode const *match = NULL;
size_t rewindDistance = 0;
for (;;) {
/* 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]) {
(*input)++; /* Consume that char */
if (*input && node->next[c]) {
input++; /* Consume that char */
rewindDistance++;
node = &charmap->nodes[node->next[c]];
node = &currentCharmap->nodes[node->next[c]];
if (node->isTerminal) {
match = node;
rewindDistance = 0; /* Rewind from after the match */
}
} else {
*input -= rewindDistance; /* Rewind */
input -= rewindDistance; /* Rewind */
rewindDistance = 0;
node = &charmap->nodes[0];
node = &currentCharmap->nodes[0];
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 */
size_t codepointLen = readUTF8Char(output ? *output : NULL,
*input);
if (codepointLen == 0)
if (codepointLen == 0) {
error("Input string is not valid UTF-8!\n");
/* OK because UTF-8 has no NUL in multi-byte chars */
*input += codepointLen;
if (output)
*output += codepointLen;
return codepointLen;
} else { /* End of input */
return 0;
break;
}
input += codepointLen; /* OK because UTF-8 has no NUL in multi-byte chars */
output += codepointLen;
outputLen += codepointLen;
}
if (!*input)
break;
}
}
unreachable_();
return outputLen;
}

View File

@@ -6,7 +6,6 @@
* SPDX-License-Identifier: MIT
*/
#include <assert.h>
#include <inttypes.h>
#include <math.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 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");
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) {
memcpy(buf, value, len);
for (size_t i = len; i < totalLen; i++)
buf[i] = ' ';
strncpy(buf, value, len < bufLen ? len : bufLen);
for (size_t i = 0; i < totalLen && len + i < bufLen; i++)
buf[len + i] = ' ';
} else {
for (size_t i = 0; i < padLen; i++)
for (size_t i = 0; i < padLen && i < bufLen; i++)
buf[i] = ' ';
memcpy(buf + padLen, value, len);
if (bufLen > padLen)
strncpy(buf + padLen, value, bufLen - padLen - 1);
}
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 */
/* 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 > 255) {
error("Fractional width %zu too long, limiting to 255\n",
fracWidth);
fracWidth = 255;
}
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,
(value % 65536) / 65536.0 * pow(10, fracWidth) + 0.5);
} 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 numLen = !!sign + !!prefix + len;
size_t numLen = len;
if (sign)
numLen++;
if (prefix)
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");
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 pos = 0;
size_t padLen = fmt->width > numLen ? fmt->width - numLen : 0;
if (fmt->alignLeft) {
if (sign)
size_t pos = 0;
if (sign && pos < bufLen)
buf[pos++] = sign;
if (prefix)
if (prefix && pos < bufLen)
buf[pos++] = prefix;
memcpy(buf + pos, valueBuf, len);
for (size_t i = pos + len; i < totalLen; i++)
buf[i] = ' ';
strcpy(buf + pos, valueBuf);
pos += len;
for (size_t i = 0; i < totalLen && pos + i < bufLen; i++)
buf[pos + i] = ' ';
} else {
size_t pos = 0;
if (fmt->padZero) {
/* sign, then prefix, then zero padding */
if (sign)
if (sign && pos < bufLen)
buf[pos++] = sign;
if (prefix)
if (prefix && pos < bufLen)
buf[pos++] = prefix;
for (size_t i = 0; i < padLen; i++)
for (size_t i = 0; i < padLen && pos < bufLen; i++)
buf[pos++] = '0';
} else {
/* 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++] = ' ';
if (sign)
if (sign && pos < bufLen)
buf[pos++] = sign;
if (prefix)
if (prefix && pos < bufLen)
buf[pos++] = prefix;
}
memcpy(buf + pos, valueBuf, len);
if (bufLen > pos)
strcpy(buf + pos, valueBuf);
}
buf[totalLen] = '\0';

View File

@@ -44,7 +44,7 @@ struct Context {
static struct Context *contextStack;
static size_t contextDepth = 0;
#define DEFAULT_MAX_DEPTH 64
size_t maxRecursionDepth;
size_t nMaxRecursionDepth;
static unsigned int nbIncPaths = 0;
static char const *includePaths[MAXINCPATHS];
@@ -143,8 +143,8 @@ void fstk_AddIncludePath(char const *path)
static void printDep(char const *path)
{
if (dependfile) {
fprintf(dependfile, "%s: %s\n", targetFileName, path);
if (generatePhonyDeps)
fprintf(dependfile, "%s: %s\n", tzTargetFileName, path);
if (oGeneratePhonyDeps)
fprintf(dependfile, "%s:\n", path);
}
}
@@ -191,11 +191,6 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
break;
}
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)) {
@@ -206,18 +201,18 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
}
errno = ENOENT;
if (generatedMissingIncludes)
if (oGeneratedMissingIncludes)
printDep(path);
return false;
}
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",
ifDepth, ifDepth == 1 ? "" : "s");
nIFDepth, nIFDepth == 1 ? "" : "s");
if (contextStack->fileInfo->type == NODE_REPT) { /* The context is a REPT block, which may loop */
struct FileStackReptNode *fileInfo = (struct FileStackReptNode *)contextStack->fileInfo;
@@ -293,8 +288,8 @@ bool yywrap(void)
*/
static void newContext(struct FileStackNode *fileInfo)
{
if (++contextDepth >= maxRecursionDepth)
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
if (++contextDepth >= nMaxRecursionDepth)
fatalerror("Recursion limit (%zu) exceeded\n", nMaxRecursionDepth);
struct Context *context = malloc(sizeof(*context));
if (!context)
@@ -322,11 +317,11 @@ void fstk_RunInclude(char const *path)
if (!fstk_FindFile(path, &fullPath, &size)) {
free(fullPath);
if (generatedMissingIncludes) {
if (oGeneratedMissingIncludes) {
if (verbose)
printf("Aborting (-MG) on INCLUDE file '%s' (%s)\n",
path, strerror(errno));
failedOnMissingInclude = true;
oFailedOnMissingInclude = true;
} else {
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);
newContext((struct FileStackNode *)fileInfo);
contextStack->lexerState = lexer_OpenFileView("MACRO", macro->macro, macro->macroSize,
contextStack->lexerState = lexer_OpenFileView(macro->macro, macro->macroSize,
macro->fileLine);
if (!contextStack->lexerState)
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 */
contextStack->fileInfo->lineNo = reptLineNo;
contextStack->lexerState = lexer_OpenFileView("REPT", body, size, reptLineNo);
contextStack->lexerState = lexer_OpenFileView(body, size, reptLineNo);
if (!contextStack->lexerState)
fatalerror("Failed to set up lexer for REPT block\n");
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)
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)
return;
if (!newReptContext(reptLineNo, body, size))
@@ -528,7 +519,7 @@ bool fstk_Break(void)
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);
@@ -570,12 +561,12 @@ void fstk_Init(char const *mainPath, size_t maxDepth)
* This assumes that the rept node is larger
*/
#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 "
EXPAND_AND_STR(DEFAULT_MAX_DEPTH) "\n", DEPTH_LIMIT);
maxRecursionDepth = DEFAULT_MAX_DEPTH;
nMaxRecursionDepth = DEFAULT_MAX_DEPTH;
} else {
maxRecursionDepth = maxDepth;
nMaxRecursionDepth = maxRecursionDepth;
}
/* Make sure that the default of 64 is OK, though */
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 "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
// Unfortunately, macOS still ships 2.3, which is from 2008...
int yyparse(void);
@@ -57,13 +45,13 @@ extern int yydebug;
#endif
FILE * dependfile;
bool generatedMissingIncludes;
bool failedOnMissingInclude;
bool generatePhonyDeps;
char *targetFileName;
bool oGeneratedMissingIncludes;
bool oFailedOnMissingInclude;
bool oGeneratePhonyDeps;
char *tzTargetFileName;
bool haltnop;
bool optimizeLoads;
bool optimizeloads;
bool verbose;
bool warnings; /* True to enable warnings, false to disable them. */
@@ -170,21 +158,21 @@ int main(int argc, char *argv[])
// Set defaults
generatePhonyDeps = false;
generatedMissingIncludes = false;
failedOnMissingInclude = false;
targetFileName = NULL;
oGeneratePhonyDeps = false;
oGeneratedMissingIncludes = false;
oFailedOnMissingInclude = false;
tzTargetFileName = NULL;
opt_B("01");
opt_G("0123");
opt_P(0);
optimizeLoads = true;
optimizeloads = true;
haltnop = true;
verbose = false;
warnings = true;
sym_SetExportAll(false);
uint32_t maxDepth = 64;
size_t targetFileNameLen = 0;
uint32_t maxRecursionDepth = 64;
size_t nTargetFileNameLen = 0;
while ((ch = musl_getopt_long_only(argc, argv, optstring, longopts, NULL)) != -1) {
switch (ch) {
@@ -226,7 +214,7 @@ int main(int argc, char *argv[])
break;
case 'L':
optimizeLoads = false;
optimizeloads = false;
break;
case 'M':
@@ -256,7 +244,7 @@ int main(int argc, char *argv[])
break;
case 'r':
maxDepth = strtoul(musl_optarg, &ep, 0);
maxRecursionDepth = strtoul(musl_optarg, &ep, 0);
if (musl_optarg[0] == '\0' || *ep != '\0')
errx(1, "Invalid argument for option 'r'");
@@ -281,11 +269,11 @@ int main(int argc, char *argv[])
case 0:
switch (depType) {
case 'G':
generatedMissingIncludes = true;
oGeneratedMissingIncludes = true;
break;
case 'P':
generatePhonyDeps = true;
oGeneratePhonyDeps = true;
break;
case 'Q':
@@ -296,22 +284,22 @@ int main(int argc, char *argv[])
if (depType == 'Q')
ep = make_escape(ep);
targetFileNameLen += strlen(ep) + 1;
if (!targetFileName) {
nTargetFileNameLen += strlen(ep) + 1;
if (!tzTargetFileName) {
/* On first alloc, make an empty str */
targetFileName = malloc(targetFileNameLen + 1);
if (targetFileName)
*targetFileName = '\0';
tzTargetFileName = malloc(nTargetFileNameLen + 1);
if (tzTargetFileName)
*tzTargetFileName = '\0';
} else {
targetFileName = realloc(targetFileName,
targetFileNameLen + 1);
tzTargetFileName = realloc(tzTargetFileName,
nTargetFileNameLen + 1);
}
if (targetFileName == NULL)
if (tzTargetFileName == NULL)
err(1, "Cannot append new file to target file list");
strcat(targetFileName, ep);
strcat(tzTargetFileName, ep);
if (depType == 'Q')
free(ep);
char *ptr = targetFileName + strlen(targetFileName);
char *ptr = tzTargetFileName + strlen(tzTargetFileName);
*ptr++ = ' ';
*ptr = '\0';
@@ -326,8 +314,8 @@ int main(int argc, char *argv[])
}
}
if (targetFileName == NULL)
targetFileName = objectName;
if (tzTargetFileName == NULL)
tzTargetFileName = tzObjectname;
if (argc == musl_optind) {
fputs("FATAL: No input files\n", stderr);
@@ -343,17 +331,17 @@ int main(int argc, char *argv[])
printf("Assembling %s\n", mainFileName);
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");
fprintf(dependfile, "%s: %s\n", targetFileName, mainFileName);
fprintf(dependfile, "%s: %s\n", tzTargetFileName, mainFileName);
}
charmap_New("main", NULL);
// Init lexer and file stack, prodiving file info
lexer_Init();
fstk_Init(mainFileName, maxDepth);
fstk_Init(mainFileName, maxRecursionDepth);
// Perform parse (yyparse is auto-generated from `parser.y`)
if (yyparse() != 0 && nbErrors == 0)
@@ -369,11 +357,11 @@ int main(int argc, char *argv[])
nbErrors == 1 ? "" : "s");
// If parse aborted due to missing an include, and `-MG` was given, exit normally
if (failedOnMissingInclude)
if (oFailedOnMissingInclude)
return 0;
/* If no path specified, don't write file */
if (objectName != NULL)
if (tzObjectname != NULL)
out_WriteObject();
return 0;
}

View File

@@ -1,3 +1,4 @@
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
@@ -6,7 +7,6 @@
#include <string.h>
#include "asm/lexer.h"
#include "asm/main.h"
#include "asm/section.h"
#include "asm/warning.h"
@@ -14,10 +14,6 @@ struct OptStackEntry {
char binary[2];
char gbgfx[4];
int32_t fillByte;
bool haltnop;
bool optimizeLoads;
bool warningsAreErrors;
enum WarningState warningStates[NB_WARNINGS];
struct OptStackEntry *next;
};
@@ -38,21 +34,6 @@ void opt_P(uint8_t 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)
{
switch (s[0]) {
@@ -85,49 +66,6 @@ void opt_Parse(char *s)
}
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:
error("Unknown option '%c'\n", s[0]);
break;
@@ -152,14 +90,6 @@ void opt_Push(void)
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;
stack = entry;
}
@@ -176,13 +106,6 @@ void opt_Pop(void)
opt_B(entry->binary);
opt_G(entry->gbgfx);
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;
free(entry);
}

View File

@@ -35,12 +35,12 @@
struct Patch {
struct FileStackNode const *src;
uint32_t lineNo;
uint32_t offset;
uint32_t nOffset;
struct Section *pcSection;
uint32_t pcOffset;
uint8_t type;
uint32_t rpnSize;
uint8_t *rpn;
uint32_t nRPNSize;
uint8_t *pRPN;
struct Patch *next;
};
@@ -51,9 +51,10 @@ struct Assertion {
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 */
static struct Symbol *objectSymbols = NULL;
@@ -71,7 +72,7 @@ static uint32_t countSections(void)
{
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++;
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)
{
struct Section const *sec = sectionList;
struct Section const *sec = pSectionList;
uint32_t ID = 0;
while (sec) {
@@ -204,12 +205,12 @@ static void writepatch(struct Patch const *patch, FILE *f)
assert(patch->src->ID != -1);
putlong(patch->src->ID, f);
putlong(patch->lineNo, f);
putlong(patch->offset, f);
putlong(patch->nOffset, f);
putlong(getSectIDIfAny(patch->pcSection), f);
putlong(patch->pcOffset, f);
putc(patch->type, f);
putlong(patch->rpnSize, f);
fwrite(patch->rpn, 1, patch->rpnSize, f);
putlong(patch->nRPNSize, 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
*/
@@ -300,7 +286,7 @@ static uint32_t getSymbolID(struct Symbol *sym)
static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
uint32_t rpnlen)
{
char symName[512];
char tzSym[512];
for (size_t offset = 0; offset < rpnlen; ) {
#define popbyte() rpn[offset++]
@@ -324,14 +310,14 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
case RPN_SYM:
i = 0;
do {
symName[i] = popbyte();
} while (symName[i++]);
tzSym[i] = popbyte();
} while (tzSym[i++]);
// The symbol name is always written expanded
sym = sym_FindExactSymbol(symName);
sym = sym_FindExactSymbol(tzSym);
if (sym_IsConstant(sym)) {
writebyte(RPN_CONST);
value = sym_GetConstantValue(symName);
value = sym_GetConstantValue(tzSym);
} else {
writebyte(RPN_SYM);
value = getSymbolID(sym);
@@ -346,11 +332,11 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
case RPN_BANK_SYM:
i = 0;
do {
symName[i] = popbyte();
} while (symName[i++]);
tzSym[i] = popbyte();
} while (tzSym[i++]);
// The symbol name is always written expanded
sym = sym_FindExactSymbol(symName);
sym = sym_FindExactSymbol(tzSym);
value = getSymbolID(sym);
writebyte(RPN_BANK_SYM);
@@ -368,22 +354,6 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
} while (b != 0);
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:
writebyte(rpndata);
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)
{
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();
if (!patch)
fatalerror("No memory for patch: %s\n", strerror(errno));
patch->rpn = malloc(sizeof(*patch->rpn) * rpnSize);
if (!patch->rpn)
fatalerror("No memory for patch's RPN rpnSize: %s\n", strerror(errno));
patch->pRPN = malloc(sizeof(*patch->pRPN) * rpnSize);
if (!patch->pRPN)
fatalerror("No memory for patch's RPN expression: %s\n", strerror(errno));
patch->type = type;
patch->src = node;
out_RegisterNode(node);
patch->lineNo = lexer_GetLineNo();
patch->offset = ofs;
patch->nOffset = ofs;
patch->pcSection = sect_GetSymbolSection();
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) {
patch->rpnSize = rpnSize;
patch->nRPNSize = rpnSize;
/* Make sure to update `rpnSize` above if modifying this! */
patch->rpn[0] = RPN_CONST;
patch->rpn[1] = (uint32_t)(expr->val) & 0xFF;
patch->rpn[2] = (uint32_t)(expr->val) >> 8;
patch->rpn[3] = (uint32_t)(expr->val) >> 16;
patch->rpn[4] = (uint32_t)(expr->val) >> 24;
patch->pRPN[0] = RPN_CONST;
patch->pRPN[1] = (uint32_t)(expr->nVal) & 0xFF;
patch->pRPN[2] = (uint32_t)(expr->nVal) >> 8;
patch->pRPN[3] = (uint32_t)(expr->nVal) >> 16;
patch->pRPN[4] = (uint32_t)(expr->nVal) >> 24;
} else {
patch->rpnSize = 0;
writerpn(patch->rpn, &patch->rpnSize, expr->rpn, expr->rpnLength);
patch->nRPNSize = 0;
writerpn(patch->pRPN, &patch->nRPNSize, expr->tRPN, expr->nRPNLength);
}
assert(patch->rpnSize == rpnSize);
assert(patch->nRPNSize == rpnSize);
return patch;
}
@@ -448,8 +418,8 @@ void out_CreatePatch(uint32_t type, struct Expression const *expr, uint32_t ofs,
// before those bytes.
patch->pcOffset -= pcShift;
patch->next = currentSection->patches;
currentSection->patches = patch;
patch->next = pCurrentSection->patches;
pCurrentSection->patches = patch;
}
/**
@@ -482,13 +452,6 @@ static void writeassert(struct Assertion *assert, FILE *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)
{
putlong(node->parent ? node->parent->ID : -1, f);
@@ -522,13 +485,13 @@ static void registerUnregisteredSymbol(struct Symbol *symbol, void *arg)
void out_WriteObject(void)
{
FILE *f;
if (strcmp(objectName, "-") != 0)
f = fopen(objectName, "wb");
if (strcmp(tzObjectname, "-") != 0)
f = fopen(tzObjectname, "wb");
else
f = fdopen(1, "wb");
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 */
sym_ForEach(registerUnregisteredSymbol, NULL);
@@ -551,21 +514,13 @@ void out_WriteObject(void)
for (struct Symbol const *sym = objectSymbols; sym; sym = sym->next)
writesymbol(sym, f);
for (struct Section *sect = sectionList; sect; sect = sect->next) {
for (struct Section *sect = pSectionList; sect; sect = sect->next)
writesection(sect, f);
freesection(sect);
}
putlong(countAsserts(), f);
struct Assertion *assert = assertions;
while (assert != NULL) {
struct Assertion *next = assert->next;
for (struct Assertion *assert = assertions; assert;
assert = assert->next)
writeassert(assert, f);
freeassert(assert);
assert = next;
}
fclose(f);
}
@@ -575,7 +530,7 @@ void out_WriteObject(void)
*/
void out_SetFileName(char *s)
{
objectName = s;
tzObjectname = s;
if (verbose)
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
.Ic LD [$FF00+n8],A
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.
.It Fl M Ar depend_file , Fl Fl dependfile Ar depend_file
Print
@@ -189,7 +189,7 @@ Ignoring the
prefix, entries are listed alphabetically.
.Bl -tag -width Ds
.It Fl Wno-assert
Warn when
Warns when
.Ic WARN Ns No -type
assertions fail. (See
.Dq Aborting the assembly process
@@ -197,12 +197,6 @@ in
.Xr rgbasm 5
for
.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
Warn about incorrect arguments to built-in functions, such as
.Fn STRSUB
@@ -245,12 +239,12 @@ constant or
directive are encountered.
.It Fl Wshift
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
Warn when a shift's operand is negative or greater than 32.
.It Fl Wno-truncation
Warn when an implicit truncation (for example,
.Ic db )
.Ic LD [B @] )
loses some bits.
.It Fl Wno-user
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.
To do so, put a backslash at the end of the line:
.Bd -literal -offset indent
DB 1, 2, 3,\ \[rs]
4, 5, 6,\ \[rs]\ ;\ Put it before any comments
7, 8, 9
LD [B @:...], 1, 2, 3,\ \[rs]
4, 5, 6,\ \[rs]\ ;\ Put it before any comments
7, 8, 9
.Ed
.Pp
This works anywhere in the code except inside of strings.
@@ -73,8 +73,8 @@ To split strings it is needed to use
.Fn STRCAT
like this:
.Bd -literal -offset indent
db STRCAT("Hello ",\ \[rs]
"world!")
LD [B @:], STRCAT("Hello ",\ \[rs]
"world!")
.Ed
.Sh EXPRESSIONS
An expression can be composed of many things.
@@ -91,13 +91,13 @@ section.
The instructions in the macro-language generally require constant expressions.
.Ss 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 Hexadecimal Ta $ Ta 0123456789ABCDEF
.It Decimal Ta none Ta 0123456789
.It Octal Ta & Ta 01234567
.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 Gameboy graphics Ta \` Ta 0123
.El
@@ -226,7 +226,7 @@ For example:
; (shifted and scaled from the range [-1.0, 1.0])
ANGLE = 0.0
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
ENDR
.Ed
@@ -337,7 +337,7 @@ followed by one or more
\[en]
.Ql 9 .
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.
.El
.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 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 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 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 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
pattern replaced by interpolating the format
.Ar spec
@@ -419,9 +417,9 @@ CHARMAP "&iacute", 20
CHARMAP "A", 128
.Ed
This would result in
.Ql db \(dqAmen<LF>\(dq
.Ql ld [b @:...], \(dqAmen<LF>\(dq
being equivalent to
.Ql db 128, 109, 101, 110, 10 .
.Ql ld [b @:...], 128, 109, 101, 110, 10 .
.Pp
Any characters in a string without defined mappings will be copied directly, using the source file's encoding of characters to bytes.
.Pp
@@ -461,12 +459,6 @@ is a label, it returns the bank number the label is in.
The result may be constant if
.Nm
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
.Ar symbol
has been defined, FALSE (0) otherwise.
@@ -582,15 +574,15 @@ While
will automatically optimize
.Ic ld
instructions to the smaller and faster
.Ic ldh
.Ic ld\ h
(see
.Xr gbz80 7 )
whenever possible, it is generally unable to do so when a label is involved.
Using the
.Ic ldh
.Ic ld\ h
instruction directly is recommended.
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.
.El
.Pp
@@ -707,11 +699,11 @@ CopyCode:
ld c, RAMLocation.end - RAMLocation
\&.loop
ld a, [de]
inc de
ld de+
ld [hli], a
dec c
jr nz, .loop
ret
ld c-
ld nz pc, b .loop
ld pc,[sp++]
RAMCode:
LOAD "RAM code", WRAM0
@@ -721,13 +713,13 @@ RAMLocation:
\&.copy
ld a, [hli]
ld [de], a
inc de
and a
jr nz, .copy
ret
ld de+
ld a,a&a
ld nz pc, b .copy
ld pc,[sp++]
\&.string
db "Hello World!", 0
ld [b @:...], "Hello World!", 0
\&.end
ENDL
.Ed
@@ -937,14 +929,14 @@ references the one before the expression;
and so on.
.Bd -literal -offset indent
ld hl, :++
: ld a, [hli] ; referenced by "jr nz"
ldh [c], a
dec c
jr nz, :-
ret
: ld a, [hli] ; referenced by "ld nz pc"
ld [h c], a
ld c-
ld nz pc,b :-
ld pc,[sp++]
: ; referenced by "ld hl"
dw $7FFF, $1061, $03E0, $58A5
ld [w @:], $7FFF, $1061, $03E0, $58A5
.Ed
.Pp
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
.Ic SET
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
def SCREEN_WIDTH equ 160 ;\ In pixels
def SCREEN_HEIGHT equ 144
@@ -967,26 +959,6 @@ def SCREEN_HEIGHT equ 144
Note that colons
.Ql \&:
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
.Ic SET ,
or its synonym
@@ -1064,18 +1036,18 @@ DEF COUNTREG EQUS "[hl+]"
ld a,COUNTREG
DEF PLAYER_NAME EQUS "\[rs]"John\[rs]""
db PLAYER_NAME
ld [b @:], PLAYER_NAME
.Ed
.Pp
This will be interpreted as:
.Bd -literal -offset indent
ld a,[hl+]
db "John"
ld [b @:], "John"
.Ed
.Pp
String equates can also be used to define small one-line macros:
.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
.Pp
Note that colons
@@ -1095,7 +1067,7 @@ For example:
DEF s EQUS "Hello, "
REDEF s EQUS "{s}world!"
; prints "Hello, world!"
PRINTLN "{s}\n"
PRINTT "{s}\n"
.Ed
.Pp
.Sy Important note :
@@ -1163,7 +1135,7 @@ constructs.
.Bd -literal -offset indent
MACRO MyMacro
ld a, 80
call MyFunc
ld[--sp],pc, MyFunc
ENDM
.Ed
.Pp
@@ -1204,11 +1176,7 @@ ENDM
.Pp
Macro arguments support all the escape sequences of strings, as well as
.Ql \[rs],
to escape commas, as well as
.Ql \[rs](
and
.Ql \[rs])
to escape parentheses, since those otherwise separate and enclose arguments, respectively.
to escape commas, since those otherwise separate arguments.
.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.
.Pp
@@ -1240,9 +1208,9 @@ ExportedLabelB2:
.Ql c.asm :
.Bd -literal -compact
SECTION "C", ROM0[0]
dw LabelA
dw ExportedLabelB1
dw ExportedLabelB2
ld [w @], LabelA
ld [w @], ExportedLabelB1
ld [w @], ExportedLabelB2
.Ed
.Pp
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_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_VERSION__ Ta Ic EQUS Ta Version of RGBDS, as printed by Ql rgbasm --version
.El
.Pp
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/ .
.Sh DEFINING DATA
.Ss Declaring variables in a RAM section
.Ic DS
.Ic LD [B @:<len>]
allocates a number of empty bytes.
This is the preferred method of allocating space in a RAM section.
You can also use
.Ic DB , DW
.Ic LD [B @] , LD [W @]
and
.Ic DL
without any arguments instead (see
.Ic LD [L @]
with
.Ql \&?
instead (see
.Sx Defining constant data
below).
.Bd -literal -offset indent
DS 42 ;\ Allocates 42 bytes
LD [B @:42], ? ;\ Allocates 42 bytes
.Ed
.Pp
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
.Fl O .
.Ss Defining constant data
.Ic DB
.Ic LD [B @]
defines a list of bytes that will be stored in the final image.
Ideal for tables and text.
.Bd -literal -offset indent
DB 1,2,3,4,"This is a string"
LD [B @:...], 1,2,3,4,"This is a string"
.Ed
.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
.Ic DW
.Ic LD [W @]
to store a list of words (16-bit) or
.Ic DL
.Ic LD [L @]
to store a list of double-words/longs (32-bit).
.Pp
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.
For example, under the default charmap, the following two lines are identical:
.Bd -literal -offset indent
DW "Hello!"
DW "H", "e", "l", "l", "o", "!"
LD [W @:], "Hello!"
LD [W @:], "H", "e", "l", "l", "o", "!"
.Ed
.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.
.Pp
.Ic DS
.Ic LD [B @:<len>]
can also be used to fill a region of memory with some repeated values.
For example:
.Bd -literal -offset indent
; outputs 3 bytes: $AA, $AA, $AA
DS 3, $AA
LD [B @:3], $AA
; outputs 7 bytes: $BB, $CC, $BB, $CC, $BB, $CC, $BB
DS 7, $BB, $CC
LD [B @:7], $BB, $CC
.Ed
.Pp
You can also use
.Ic DB , DW
.Ic LD [B @] , LD [W @]
and
.Ic DL
without arguments.
.Ic LD [L @]
with
.Ql \&?
as its sole argument (in which case neither the colon nor an ellipsis may be present).
This works exactly like
.Ic DS 1 , DS 2
.Ic LD [B @:1] , LD [B @:2]
and
.Ic DS 4
.Ic LD [B @:4]
respectively.
Consequently, no-argument
.Ic DB , DW
Consequently, these forms of
.Ic LD [B @] , LD [W @]
and
.Ic DL
.Ic LD [L @]
can be used in a
.Ic WRAM0
/
@@ -1396,23 +1378,25 @@ section.
.Ss Including binary files
You probably have some graphics, level data, etc. you'd like to include.
Use
.Ic INCBIN
.Ic LD [B @:...] =
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
.Xr rgbasm 1
(see the
.Fl i
option) on the command line will be searched.
.Bd -literal -offset indent
INCBIN "titlepic.bin"
INCBIN "sprites/hero.bin"
LD [B @:] = "titlepic.bin"
LD [B @:...] = "sprites/hero.bin"
.Ed
.Pp
You can also include only part of a file with
.Ic INCBIN .
The example below includes 256 bytes from data.bin, starting from byte 78.
.Ic LD [B @:] .
The examples below include 256 bytes from data.bin, starting from byte 78.
.Bd -literal -offset indent
INCBIN "data.bin",78,256
LD [B @:] = "data.bin"[78:256]
LD [B @:256] = "data.bin"[78:]
.Ed
.Pp
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
UNION
; Here, PC = $C0DE
Name: ds 8
Name: ld [b @:8], ?
; PC = $C0E6
Nickname: ds 8
Nickname: ld [b @:8], ?
; PC = $C0EE
NEXTU
; PC is back to $C0DE
Health: dw
Health: ld [w @], ?
; PC = $C0E0
Something: ds 6
Something: ld [b @:6], ?
; And so on
Lives: db
Lives: ld [b @], ?
NEXTU
VideoBuffer: ds 19
VideoBuffer: ld [b @:19], ?
ENDU
.Ed
.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.
.Pp
Unions may be used in any section, but inside them may only be
.Ic DS -
.Ic LD\ [B\ @],\ ? Ns - Ns
like commands (see
.Sx Declaring variables in a RAM section ) .
.Sh THE MACRO LANGUAGE
.Ss Invoking macros
You execute the macro by inserting its name.
.Bd -literal -offset indent
add a,b
ld a,a+b
ld sp,hl
MyMacro ;\ This will be expanded
sub a,87
ld a,a-87
.Ed
.Pp
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.
.Bd -literal -offset indent
MACRO LoopyMacro
xor a,a
ld a,a^a
\&.loop ld [hl+],a
dec c
jr nz,.loop
ld c-
ld nz pc,b .loop
ENDM
.Ed
.Pp
@@ -1509,10 +1493,10 @@ also works in
blocks.
.Bd -literal -offset indent
MACRO LoopyMacro
xor a,a
ld a,a^a
\&.loop\[rs]@ ld [hl+],a
dec c
jr nz,.loop\[rs]@
ld c-
ld nz pc,b .loop\[rs]@
ENDM
.Ed
.Pp
@@ -1540,10 +1524,10 @@ being the first argument specified on the macro invocation.
MACRO LoopyMacro
ld hl,\[rs]1
ld c,\[rs]2
xor a,a
ld a,a^a
\&.loop\[rs]@ ld [hl+],a
dec c
jr nz,.loop\[rs]@
ld c-
ld nz pc,b .loop\[rs]@
ENDM
.Ed
.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.
However, some characters need to be escaped, as in the following example:
.Bd -literal -offset indent
MACRO PrintMacro1
PRINTLN STRCAT(\[rs]1)
ENDM
PrintMacro1 "Hello "\[rs], \[rs]
"world"
MACRO PrintMacro2
MACRO PrintMacro
PRINT \[rs]1
ENDM
PrintMacro2 STRCAT("Hello ", \[rs]
"world\[rs]n")
PrintMacro STRCAT("Hello "\[rs], \[rs]
"world\[rs]n")
.Ed
.Pp
The comma in
.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 comma needs to be escaped to avoid it being treated as separating the macro's arguments.
The backslash in
.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
Since there are only nine digits, you can only access the first nine macro arguments like this.
To use the rest, you need to put the multi-digit argument number in angle brackets, like
.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
In reality, up to 256 arguments can be passed to a macro, but you can only use the first 9 like this.
If you want to use the rest, you need to use the
.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
.Dv _NARG
by 1.
@@ -1638,14 +1594,11 @@ will get the value of
.Ic \[rs]3 ,
and so forth.
.Pp
This is the only way of accessing the value of arguments from 10 to 256.
.Pp
.Ic SHIFT
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.
.Pp
.Ic SHIFT
is useful in
.Ic REPT
blocks to repeat the same commands with multiple arguments.
.Ss Printing things during assembly
The
.Ic PRINT
@@ -1686,7 +1639,7 @@ The following example will assemble
four times:
.Bd -literal -offset indent
REPT 4
add a,c
ld a,a+c
ENDR
.Ed
.Pp
@@ -1698,7 +1651,7 @@ to generate tables on the fly:
; (shifted and scaled from the range [-1.0, 1.0])
ANGLE = 0.0
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
ENDR
.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:
.Bd -literal -offset indent
FOR N, 256
dw N * N
ld [w @], N * N
ENDR
.Ed
.Pp
It acts just as if you had done:
.Bd -literal -offset ident
N = 0
dw N * N
ld [w @], N * N
N = 1
dw N * N
ld [w @], N * N
N = 2
dw N * N
ld [w @], N * N
; ...
N = 255
dw N * N
ld [w @], N * N
N = 256
.Ed
.Pp
@@ -1827,18 +1780,18 @@ and
Syntax examples are given below:
.Bd -literal -offset indent
Function:
xor a
ld a,a^a
ASSERT LOW(Variable) == 0
ld h, HIGH(Variable)
ld l, a
ld a, [hli]
; You can also indent this!
ASSERT BANK(OtherFunction) == BANK(Function)
call OtherFunction
ld [--sp], pc,OtherFunction
; Lowercase also works
assert Variable + 1 == OtherVariable
ld c, [hl]
ret
ld pc,[sp++]
\&.end
; If you specify one, a message will be printed
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:
.Bd -literal -offset indent
PUSHO
OPT g.oOX, Wdiv, L ; acts like command-line -g.oOX -Wdiv -L
DW `..ooOOXX ; uses the graphics constant characters from OPT g
PRINTLN $80000000/-1 ; prints a warning about division
LD [$FF88], A ; encoded as LD, not LDH
OPT g.oOX ;Set the GB graphics constants to use these characters
LD [W @], `..ooOOXX
POPO
DW `00112233 ; uses the default graphics constant characters
PRINTLN $80000000/-1 ; no warning by default
LD [$FF88], A ; optimized to use LDH by default
LD [W @], `00112233
.Ed
.Pp
The options that OPT can modify are currently:
.Cm b , g , p , h , L ,
.Cm b , g
and
.Cm W .
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.
.Cm p .
.Pp
.Ic POPO
and

View File

@@ -13,7 +13,6 @@
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -46,47 +45,47 @@
static uint8_t *reserveSpace(struct Expression *expr, uint32_t size)
{
/* 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 (!expr->rpn)
expr->rpnCapacity = 256; /* Initial size */
while (expr->rpnCapacity - expr->rpnLength < size) {
if (expr->rpnCapacity >= MAXRPNLEN)
if (!expr->tRPN)
expr->nRPNCapacity = 256; /* Initial size */
while (expr->nRPNCapacity - expr->nRPNLength < size) {
if (expr->nRPNCapacity >= MAXRPNLEN)
/*
* To avoid generating humongous object files, cap the
* size of RPN expressions
*/
fatalerror("RPN expression cannot grow larger than "
EXPAND_AND_STR(MAXRPNLEN) " bytes\n");
else if (expr->rpnCapacity > MAXRPNLEN / 2)
expr->rpnCapacity = MAXRPNLEN;
else if (expr->nRPNCapacity > MAXRPNLEN / 2)
expr->nRPNCapacity = MAXRPNLEN;
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));
}
uint8_t *ptr = expr->rpn + expr->rpnLength;
uint8_t *ptr = expr->tRPN + expr->nRPNLength;
expr->rpnLength += size;
expr->nRPNLength += size;
return ptr;
}
/*
* Init a RPN expression
* Init the RPN expression
*/
static void rpn_Init(struct Expression *expr)
{
expr->reason = NULL;
expr->isKnown = true;
expr->isSymbol = false;
expr->rpn = NULL;
expr->rpnCapacity = 0;
expr->rpnLength = 0;
expr->rpnPatchSize = 0;
expr->tRPN = NULL;
expr->nRPNCapacity = 0;
expr->nRPNLength = 0;
expr->nRPNPatchSize = 0;
}
/*
@@ -94,7 +93,7 @@ static void rpn_Init(struct Expression *expr)
*/
void rpn_Free(struct Expression *expr)
{
free(expr->rpn);
free(expr->tRPN);
free(expr->reason);
rpn_Init(expr);
}
@@ -105,12 +104,12 @@ void rpn_Free(struct Expression *expr)
void rpn_Number(struct Expression *expr, uint32_t i)
{
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()) {
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;
makeUnknown(expr, sym_IsPC(sym) ? "PC is not constant at assembly time"
: "'%s' is not constant at assembly time", symName);
sym = sym_Ref(symName);
expr->rpnPatchSize += 5; /* 1-byte opcode + 4-byte symbol ID */
: "'%s' is not constant at assembly time", tzSym);
sym = sym_Ref(tzSym);
expr->nRPNPatchSize += 5; /* 1-byte opcode + 4-byte symbol ID */
size_t nameLen = strlen(sym->name) + 1; /* Don't forget NUL! */
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
*ptr++ = RPN_SYM;
memcpy(ptr, sym->name, nameLen);
} 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);
if (!currentSection) {
if (!pCurrentSection) {
error("PC has no bank outside a section\n");
expr->val = 1;
} else if (currentSection->bank == (uint32_t)-1) {
expr->nVal = 1;
} else if (pCurrentSection->bank == (uint32_t)-1) {
makeUnknown(expr, "Current section's bank is not known");
expr->rpnPatchSize++;
expr->nRPNPatchSize++;
*reserveSpace(expr, 1) = RPN_BANK_SELF;
} 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. */
if (sym_IsPC(sym)) {
@@ -163,15 +162,15 @@ void rpn_BankSymbol(struct Expression *expr, char const *symName)
if (sym && !sym_IsLabel(sym)) {
error("BANK argument must be a label\n");
} else {
sym = sym_Ref(symName);
sym = sym_Ref(tzSym);
assert(sym); // If the symbol didn't exist, it should have been created
if (sym_GetSection(sym) && sym_GetSection(sym)->bank != (uint32_t)-1) {
/* Symbol's section is known and bank is fixed */
expr->val = sym_GetSection(sym)->bank;
expr->nVal = sym_GetSection(sym)->bank;
} else {
makeUnknown(expr, "\"%s\"'s bank is not known", symName);
expr->rpnPatchSize += 5; /* opcode + 4-byte sect ID */
makeUnknown(expr, "\"%s\"'s bank is not known", tzSym);
expr->nRPNPatchSize += 5; /* opcode + 4-byte sect ID */
size_t nameLen = strlen(sym->name) + 1; /* Room for NUL! */
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);
struct Section *section = sect_FindSectionByName(sectionName);
struct Section *pSection = out_FindSectionByName(tzSectionName);
if (section && section->bank != (uint32_t)-1) {
expr->val = section->bank;
if (pSection && pSection->bank != (uint32_t)-1) {
expr->nVal = pSection->bank;
} 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);
expr->rpnPatchSize += nameLen + 1;
expr->nRPNPatchSize += nameLen + 1;
*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)
{
*expr = *src;
expr->isSymbol = false;
if (!rpn_isKnown(expr)) {
expr->rpnPatchSize++;
expr->nRPNPatchSize++;
*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 */
expr->val &= 0xFF;
} else if (expr->val < 0 || expr->val > 0xFF) {
error("Source address $%" PRIx32 " not between $FF00 to $FFFF\n", expr->val);
expr->nVal &= 0xFF;
} else if (expr->nVal < 0 || expr->nVal > 0xFF) {
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)) {
/* A valid RST address must be masked with 0x38 */
if (expr->val & ~0x38)
error("Invalid address $%" PRIx32 " for RST\n", expr->val);
if (expr->nVal & ~0x38)
error("Invalid address $%" PRIx32 " for RST\n", expr->nVal);
/* The target is in the "0x38" bits, all other bits are set */
expr->val |= 0xC7;
expr->nVal |= 0xC7;
} else {
expr->rpnPatchSize++;
expr->nRPNPatchSize++;
*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)
{
*expr = *src;
expr->isSymbol = false;
if (rpn_isKnown(expr)) {
expr->val = !expr->val;
expr->nVal = !expr->nVal;
} else {
expr->rpnPatchSize++;
expr->nRPNPatchSize++;
*reserveSpace(expr, 1) = RPN_LOGUNNOT;
}
}
@@ -303,7 +250,7 @@ struct Symbol const *rpn_SymbolOf(struct Expression const *expr)
{
if (!rpn_isSymbol(expr))
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)
@@ -336,110 +283,111 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
rpn_Init(expr); /* Init the expression to something sane */
/* 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) {
case RPN_LOGOR:
expr->val = src1->val || src2->val;
expr->nVal = src1->nVal || src2->nVal;
break;
case RPN_LOGAND:
expr->val = src1->val && src2->val;
expr->nVal = src1->nVal && src2->nVal;
break;
case RPN_LOGEQ:
expr->val = src1->val == src2->val;
expr->nVal = src1->nVal == src2->nVal;
break;
case RPN_LOGGT:
expr->val = src1->val > src2->val;
expr->nVal = src1->nVal > src2->nVal;
break;
case RPN_LOGLT:
expr->val = src1->val < src2->val;
expr->nVal = src1->nVal < src2->nVal;
break;
case RPN_LOGGE:
expr->val = src1->val >= src2->val;
expr->nVal = src1->nVal >= src2->nVal;
break;
case RPN_LOGLE:
expr->val = src1->val <= src2->val;
expr->nVal = src1->nVal <= src2->nVal;
break;
case RPN_LOGNE:
expr->val = src1->val != src2->val;
expr->nVal = src1->nVal != src2->nVal;
break;
case RPN_ADD:
expr->val = uleft + uright;
expr->nVal = uleft + uright;
break;
case RPN_SUB:
expr->val = uleft - uright;
expr->nVal = uleft - uright;
break;
case RPN_XOR:
expr->val = src1->val ^ src2->val;
expr->nVal = src1->nVal ^ src2->nVal;
break;
case RPN_OR:
expr->val = src1->val | src2->val;
expr->nVal = src1->nVal | src2->nVal;
break;
case RPN_AND:
expr->val = src1->val & src2->val;
expr->nVal = src1->nVal & src2->nVal;
break;
case RPN_SHL:
if (src2->val < 0)
if (src2->nVal < 0)
warning(WARNING_SHIFT_AMOUNT,
"Shifting left by negative amount %" PRId32 "\n",
src2->val);
src2->nVal);
if (src2->val >= 32)
if (src2->nVal >= 32)
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;
case RPN_SHR:
if (src1->val < 0)
warning(WARNING_SHIFT,
"Shifting right negative value %" PRId32 "\n", src1->val);
if (src1->nVal < 0)
warning(WARNING_SHIFT, "Shifting right negative value %"
PRId32 "\n",
src1->nVal);
if (src2->val < 0)
if (src2->nVal < 0)
warning(WARNING_SHIFT_AMOUNT,
"Shifting right by negative amount %" PRId32 "\n",
src2->val);
src2->nVal);
if (src2->val >= 32)
if (src2->nVal >= 32)
warning(WARNING_SHIFT_AMOUNT,
"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;
case RPN_MUL:
expr->val = uleft * uright;
expr->nVal = uleft * uright;
break;
case RPN_DIV:
if (src2->val == 0)
if (src2->nVal == 0)
fatalerror("Division by zero\n");
if (src1->val == INT32_MIN && src2->val == -1) {
warning(WARNING_DIV,
"Division of %" PRId32 " by -1 yields %" PRId32 "\n",
INT32_MIN, INT32_MIN);
expr->val = INT32_MIN;
if (src1->nVal == INT32_MIN && src2->nVal == -1) {
warning(WARNING_DIV, "Division of %" PRId32 " by -1 yields %"
PRId32 "\n", INT32_MIN, INT32_MIN);
expr->nVal = INT32_MIN;
} else {
expr->val = op_divide(src1->val, src2->val);
expr->nVal = op_divide(src1->nVal, src2->nVal);
}
break;
case RPN_MOD:
if (src2->val == 0)
if (src2->nVal == 0)
fatalerror("Modulo by zero\n");
if (src1->val == INT32_MIN && src2->val == -1)
expr->val = 0;
if (src1->nVal == INT32_MIN && src2->nVal == -1)
expr->nVal = 0;
else
expr->val = op_modulo(src1->val, src2->val);
expr->nVal = op_modulo(src1->nVal, src2->nVal);
break;
case RPN_EXP:
if (src2->val < 0)
if (src2->nVal < 0)
fatalerror("Exponentiation by negative power\n");
if (src1->val == INT32_MIN && src2->val == -1)
expr->val = 0;
if (src1->nVal == INT32_MIN && src2->nVal == -1)
expr->nVal = 0;
else
expr->val = op_exponent(src1->val, src2->val);
expr->nVal = op_exponent(src1->nVal, src2->nVal);
break;
case RPN_UNSUB:
@@ -448,8 +396,6 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
case RPN_BANK_SYM:
case RPN_BANK_SECT:
case RPN_BANK_SELF:
case RPN_SIZEOF_SECT:
case RPN_STARTOF_SECT:
case RPN_HRAM:
case RPN_RST:
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 *symbol2 = rpn_SymbolOf(src2);
expr->val = sym_GetValue(symbol1) - sym_GetValue(symbol2);
expr->nVal = sym_GetValue(symbol1) - sym_GetValue(symbol2);
expr->isKnown = true;
} else {
/* If it's not known, start computing the RPN expression */
/* Convert the left-hand expression if it's constant */
if (src1->isKnown) {
uint32_t lval = src1->val;
uint32_t lval = src1->nVal;
uint8_t bytes[] = {RPN_CONST, lval, lval >> 8,
lval >> 16, lval >> 24};
expr->rpnPatchSize = sizeof(bytes);
expr->rpn = NULL;
expr->rpnCapacity = 0;
expr->rpnLength = 0;
expr->nRPNPatchSize = sizeof(bytes);
expr->tRPN = NULL;
expr->nRPNCapacity = 0;
expr->nRPNLength = 0;
memcpy(reserveSpace(expr, sizeof(bytes)), bytes,
sizeof(bytes));
@@ -483,21 +429,21 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
free(src1->reason);
} else {
/* Otherwise just reuse its RPN buffer */
expr->rpnPatchSize = src1->rpnPatchSize;
expr->rpn = src1->rpn;
expr->rpnCapacity = src1->rpnCapacity;
expr->rpnLength = src1->rpnLength;
expr->nRPNPatchSize = src1->nRPNPatchSize;
expr->tRPN = src1->tRPN;
expr->nRPNCapacity = src1->nRPNCapacity;
expr->nRPNLength = src1->nRPNLength;
expr->reason = src1->reason;
free(src2->reason);
}
/* Now, merge the right expression into the left one */
uint8_t *ptr = src2->rpn; /* Pointer to the right RPN */
uint32_t len = src2->rpnLength; /* Size of the right RPN */
uint32_t patchSize = src2->rpnPatchSize;
uint8_t *ptr = src2->tRPN; /* Pointer to the right RPN */
uint32_t len = src2->nRPNLength; /* Size of the right RPN */
uint32_t patchSize = src2->nRPNPatchSize;
/* 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,
rval >> 24};
if (src2->isKnown) {
@@ -511,8 +457,8 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
memcpy(buf, ptr, len);
buf[len] = op;
free(src2->rpn); /* If there was none, this is `free(NULL)` */
expr->rpnPatchSize += patchSize + 1;
free(src2->tRPN); /* If there was none, this is `free(NULL)` */
expr->nRPNPatchSize += patchSize + 1;
}
}
@@ -522,11 +468,11 @@ void rpn_HIGH(struct Expression *expr, const struct Expression *src)
expr->isSymbol = false;
if (rpn_isKnown(expr)) {
expr->val = (uint32_t)expr->val >> 8 & 0xFF;
expr->nVal = (uint32_t)expr->nVal >> 8 & 0xFF;
} else {
uint8_t bytes[] = {RPN_CONST, 8, 0, 0, 0, RPN_SHR,
RPN_CONST, 0xFF, 0, 0, 0, RPN_AND};
expr->rpnPatchSize += sizeof(bytes);
expr->nRPNPatchSize += 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;
if (rpn_isKnown(expr)) {
expr->val = expr->val & 0xFF;
expr->nVal = expr->nVal & 0xFF;
} else {
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));
}
}
@@ -549,7 +495,7 @@ void rpn_LOW(struct Expression *expr, const struct Expression *src)
void rpn_ISCONST(struct Expression *expr, const struct Expression *src)
{
rpn_Init(expr);
expr->val = rpn_isKnown(src);
expr->nVal = rpn_isKnown(src);
expr->isKnown = true;
expr->isSymbol = false;
}
@@ -560,9 +506,9 @@ void rpn_UNNEG(struct Expression *expr, const struct Expression *src)
expr->isSymbol = false;
if (rpn_isKnown(expr)) {
expr->val = -(uint32_t)expr->val;
expr->nVal = -(uint32_t)expr->nVal;
} else {
expr->rpnPatchSize++;
expr->nRPNPatchSize++;
*reserveSpace(expr, 1) = RPN_UNSUB;
}
}
@@ -573,9 +519,9 @@ void rpn_UNNOT(struct Expression *expr, const struct Expression *src)
expr->isSymbol = false;
if (rpn_isKnown(expr)) {
expr->val = ~expr->val;
expr->nVal = ~expr->nVal;
} else {
expr->rpnPatchSize++;
expr->nRPNPatchSize++;
*reserveSpace(expr, 1) = RPN_UNNOT;
}
}

View File

@@ -22,16 +22,13 @@ uint8_t fillByte;
struct SectionStackEntry {
struct Section *section;
struct Section *loadSection;
char const *scope; /* Section's symbol scope */
uint32_t offset;
int32_t loadOffset;
struct SectionStackEntry *next;
};
struct SectionStackEntry *sectionStack;
uint32_t curOffset; /* Offset into the current section (see sect_GetSymbolOffset) */
struct Section *currentSection = NULL;
static struct Section *currentLoadSection = NULL;
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
*/
attr_(warn_unused_result) static bool checksection(void)
static inline void checksection(void)
{
if (currentSection)
return true;
error("Cannot output data outside of a SECTION\n");
return false;
if (pCurrentSection == NULL)
fatalerror("Code generation before SECTION directive\n");
}
/*
* A quick check to see if we have an initialized section that can contain
* this much initialized data
*/
attr_(warn_unused_result) static bool checkcodesection(void)
static inline void checkcodesection(void)
{
if (!checksection())
return false;
checksection();
if (sect_HasData(currentSection->type))
return true;
error("Section '%s' cannot contain code or data (not ROM0 or ROMX)\n",
currentSection->name);
return false;
if (!sect_HasData(pCurrentSection->type))
fatalerror("Section '%s' cannot contain code or data (not ROM0 or ROMX)\n",
pCurrentSection->name);
}
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];
// If the new size is reasonable, keep going
if (size <= maxSize)
return true;
error("Section '%s' grew too big (max size = 0x%" PRIX32
" bytes, reached 0x%" PRIX32 ").\n", sect->name, maxSize, size);
return false;
if (size > maxSize)
fatalerror("Section '%s' grew too big (max size = 0x%" PRIX32
" bytes, reached 0x%" PRIX32 ").\n", sect->name, maxSize, size);
}
/*
* 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
* prevent the assembler from generating huge object files or trying to allocate too much
* memory.
* This check is here to trap broken code that generates sections that
* are too big and to prevent the assembler from generating huge object
* files or trying to allocate too much memory.
* A check at the linking stage is still necessary.
*/
// If the section has already overflowed, skip the check to avoid erroring out ad nauseam
if (currentSection->size != UINT32_MAX
&& !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);
checkSectionSize(pCurrentSection, curOffset + loadOffset + delta_size);
if (currentLoadSection)
checkSectionSize(currentLoadSection, curOffset + delta_size);
}
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)
return sect;
}
@@ -363,8 +339,6 @@ static struct Section *getSection(char const *name, enum SectionType type, uint3
} else if (startaddr[type] & mask) {
error("Section \"%s\"'s alignment cannot be attained in %s\n",
name, typeNames[type]);
alignment = 0; /* Ignore it if it's unattainable */
org = 0;
} else if (alignment == 16) {
// Treat an alignment of 16 as being fixed at address 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
struct Section *sect = sect_FindSectionByName(name);
struct Section *sect = out_FindSectionByName(name);
if (sect) {
mergeSections(sect, type, org, bank, alignment, alignOffset, mod);
} else {
sect = createSection(name, type, org, bank, alignment, alignOffset, mod);
// Add the new section to the list (order doesn't matter)
sect->next = sectionList;
sectionList = sect;
sect->next = pSectionList;
pSectionList = sect;
}
return sect;
@@ -403,8 +377,8 @@ static void changeSection(void)
/*
* Set the current section by name and type
*/
void sect_NewSection(char const *name, uint32_t type, uint32_t org,
struct SectionSpec const *attribs, enum SectionModifier mod)
void out_NewSection(char const *name, uint32_t type, uint32_t org,
struct SectionSpec const *attribs, enum SectionModifier mod)
{
if (currentLoadSection)
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();
curOffset = mod == SECTION_UNION ? 0 : sect->size;
currentSection = sect;
pCurrentSection = sect;
}
/*
* Set the current section by name and type
*/
void sect_SetLoadSection(char const *name, uint32_t type, uint32_t org,
struct SectionSpec const *attribs, enum SectionModifier mod)
void out_SetLoadSection(char const *name, uint32_t type, uint32_t org,
struct SectionSpec const *attribs,
enum SectionModifier mod)
{
if (!checkcodesection())
return;
checkcodesection();
if (currentLoadSection) {
error("`LOAD` blocks cannot be nested\n");
return;
}
if (currentLoadSection)
fatalerror("`LOAD` blocks cannot be nested\n");
if (sect_HasData(type)) {
if (sect_HasData(type))
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);
@@ -453,12 +418,10 @@ void sect_SetLoadSection(char const *name, uint32_t type, uint32_t org,
currentLoadSection = sect;
}
void sect_EndLoadSection(void)
void out_EndLoadSection(void)
{
if (!currentLoadSection) {
if (!currentLoadSection)
error("Found `ENDL` outside of a `LOAD` block\n");
return;
}
changeSection();
curOffset += loadOffset;
@@ -468,7 +431,7 @@ void sect_EndLoadSection(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)
{
if (!checksection())
return;
checksection();
struct Section *sect = sect_GetSymbolSection();
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;
if (curOffset + loadOffset > currentSection->size)
currentSection->size = curOffset + loadOffset;
if (curOffset + loadOffset > pCurrentSection->size)
pCurrentSection->size = curOffset + loadOffset;
if (currentLoadSection && curOffset > currentLoadSection->size)
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);
}
static void writeword(uint16_t b)
static inline void writeword(uint16_t b)
{
writebyte(b & 0xFF);
writebyte(b >> 8);
}
static void writelong(uint32_t b)
static inline void writelong(uint32_t b)
{
writebyte(b & 0xFF);
writebyte(b >> 8);
@@ -540,21 +501,18 @@ static void writelong(uint32_t b)
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);
}
void sect_StartUnion(void)
{
if (!currentSection) {
error("UNIONs must be inside a SECTION\n");
return;
}
if (sect_HasData(currentSection->type)) {
error("Cannot use UNION inside of ROM0 or ROMX sections\n");
return;
}
if (!pCurrentSection)
fatalerror("UNIONs must be inside a SECTION\n");
if (sect_HasData(pCurrentSection->type))
fatalerror("Cannot use UNION inside of ROM0 or ROMX sections\n");
struct UnionStackEntry *entry = malloc(sizeof(*entry));
if (!entry)
@@ -576,19 +534,15 @@ static void endUnionMember(void)
void sect_NextUnionMember(void)
{
if (!unionStack) {
error("Found NEXTU outside of a UNION construct\n");
return;
}
if (!unionStack)
fatalerror("Found NEXTU outside of a UNION construct\n");
endUnionMember();
}
void sect_EndUnion(void)
{
if (!unionStack) {
error("Found ENDU outside of a UNION construct\n");
return;
}
if (!unionStack)
fatalerror("Found ENDU outside of a UNION construct\n");
endUnionMember();
curOffset += unionStack->size;
struct UnionStackEntry *next = unionStack->next;
@@ -606,44 +560,36 @@ void sect_CheckUnionClosed(void)
/*
* Output an absolute byte
*/
void sect_AbsByte(uint8_t b)
void out_AbsByte(uint8_t b)
{
if (!checkcodesection())
return;
if (!reserveSpace(1))
return;
checkcodesection();
reserveSpace(1);
writebyte(b);
}
void sect_AbsByteGroup(uint8_t const *s, int32_t length)
void out_AbsByteGroup(uint8_t const *s, int32_t length)
{
if (!checkcodesection())
return;
if (!reserveSpace(length))
return;
checkcodesection();
reserveSpace(length);
while (length--)
writebyte(*s++);
}
void sect_AbsWordGroup(uint8_t const *s, int32_t length)
void out_AbsWordGroup(uint8_t const *s, int32_t length)
{
if (!checkcodesection())
return;
if (!reserveSpace(length * 2))
return;
checkcodesection();
reserveSpace(length * 2);
while (length--)
writeword(*s++);
}
void sect_AbsLongGroup(uint8_t const *s, int32_t length)
void out_AbsLongGroup(uint8_t const *s, int32_t length)
{
if (!checkcodesection())
return;
if (!reserveSpace(length * 4))
return;
checkcodesection();
reserveSpace(length * 4);
while (length--)
writelong(*s++);
@@ -652,20 +598,19 @@ void sect_AbsLongGroup(uint8_t const *s, int32_t length)
/*
* Skip this many bytes
*/
void sect_Skip(int32_t skip, bool ds)
void out_Skip(int32_t skip, bool ds)
{
if (!checksection())
return;
if (!reserveSpace(skip))
return;
checksection();
reserveSpace(skip);
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);
} else {
if (!ds)
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
checkcodesection();
while (skip--)
writebyte(fillByte);
}
@@ -674,12 +619,10 @@ void sect_Skip(int32_t skip, bool ds)
/*
* Output a NULL terminated string (excluding the NULL-character)
*/
void sect_String(char const *s)
void out_String(char const *s)
{
if (!checkcodesection())
return;
if (!reserveSpace(strlen(s)))
return;
checkcodesection();
reserveSpace(strlen(s));
while (*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
* 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())
return;
if (!reserveSpace(1))
return;
checkcodesection();
reserveSpace(1);
if (!rpn_isKnown(expr)) {
createPatch(PATCHTYPE_BYTE, expr, pcShift);
writebyte(0);
} else {
writebyte(expr->val);
writebyte(expr->nVal);
}
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
* 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())
return;
if (!reserveSpace(n))
return;
checkcodesection();
reserveSpace(n);
for (uint32_t i = 0; i < n; i++) {
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);
writebyte(0);
} 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
* 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())
return;
if (!reserveSpace(2))
return;
checkcodesection();
reserveSpace(2);
if (!rpn_isKnown(expr)) {
createPatch(PATCHTYPE_WORD, expr, pcShift);
writeword(0);
} else {
writeword(expr->val);
writeword(expr->nVal);
}
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
* 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())
return;
if (!reserveSpace(2))
return;
checkcodesection();
reserveSpace(2);
if (!rpn_isKnown(expr)) {
createPatch(PATCHTYPE_LONG, expr, pcShift);
writelong(0);
} else {
writelong(expr->val);
writelong(expr->nVal);
}
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
* 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())
return;
if (!reserveSpace(1))
return;
checkcodesection();
reserveSpace(1);
struct Symbol const *pc = sym_GetPC();
if (!rpn_IsDiffConstant(expr, pc)) {
@@ -811,14 +744,12 @@ void sect_PCRelByte(struct Expression *expr, uint32_t pcShift)
/*
* 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) {
error("Start position cannot be negative (%" PRId32 ")\n", startPos);
startPos = 0;
}
if (!checkcodesection())
return;
char *fullPath = NULL;
size_t size = 0;
@@ -829,10 +760,10 @@ void sect_BinaryFile(char const *s, int32_t startPos)
free(fullPath);
if (!f) {
if (generatedMissingIncludes) {
if (oGeneratedMissingIncludes) {
if (verbose)
printf("Aborting (-MG) on INCBIN file '%s' (%s)\n", s, strerror(errno));
failedOnMissingInclude = true;
oFailedOnMissingInclude = true;
return;
}
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;
int byte;
checkcodesection();
if (fseek(f, 0, SEEK_END) != -1) {
fsize = ftell(f);
if (startPos > fsize) {
error("Specified start position is greater than length of file\n");
goto cleanup;
fclose(f);
return;
}
fseek(f, startPos, SEEK_SET);
if (!reserveSpace(fsize - startPos))
goto cleanup;
reserveSpace(fsize - startPos);
} else {
if (errno != ESPIPE)
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 */
while (startPos--)
(void)fgetc(f);
@@ -871,11 +803,10 @@ void sect_BinaryFile(char const *s, int32_t startPos)
if (ferror(f))
error("Error reading INCBIN file '%s': %s\n", s, strerror(errno));
cleanup:
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) {
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);
length = 0;
}
if (!checkcodesection())
return;
if (length == 0) /* Don't even bother with 0-byte slices */
return;
if (!reserveSpace(length))
return;
char *fullPath = NULL;
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))
f = fopen(fullPath, "rb");
free(fullPath);
if (!f) {
if (generatedMissingIncludes) {
free(fullPath);
if (oGeneratedMissingIncludes) {
if (verbose)
printf("Aborting (-MG) on INCBIN file '%s' (%s)\n", s, strerror(errno));
failedOnMissingInclude = true;
} else {
error("Error opening INCBIN file '%s': %s\n", s, strerror(errno));
oFailedOnMissingInclude = true;
return;
}
error("Error opening INCBIN file '%s': %s\n", s, strerror(errno));
return;
}
checkcodesection();
reserveSpace(length);
int32_t fsize;
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) {
error("Specified start position is greater than length of file\n");
goto cleanup;
return;
}
if ((start_pos + length) > fsize) {
error("Specified range in INCBIN is out of bounds (%" PRIu32 " + %" PRIu32
" > %" PRIu32 ")\n", start_pos, length, fsize);
goto cleanup;
return;
}
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);
free(fullPath);
}
/*
* Section stack routines
*/
void sect_PushSection(void)
void out_PushSection(void)
{
struct SectionStackEntry *sect = malloc(sizeof(*sect));
if (sect == NULL)
fatalerror("No memory for section stack: %s\n", strerror(errno));
sect->section = currentSection;
sect->loadSection = currentLoadSection;
sect->section = pCurrentSection;
sect->scope = sym_GetCurrentSymbolScope();
sect->offset = curOffset;
sect->loadOffset = loadOffset;
sect->next = sectionStack;
sectionStack = sect;
// Reset the section scope
currentSection = NULL;
currentLoadSection = NULL;
sym_SetCurrentSymbolScope(NULL);
/* TODO: maybe set current section to NULL? */
}
void sect_PopSection(void)
void out_PopSection(void)
{
if (!sectionStack)
fatalerror("No entries in the section stack\n");
@@ -993,11 +916,9 @@ void sect_PopSection(void)
sect = sectionStack;
changeSection();
currentSection = sect->section;
currentLoadSection = sect->loadSection;
pCurrentSection = sect->section;
sym_SetCurrentSymbolScope(sect->scope);
curOffset = sect->offset;
loadOffset = sect->loadOffset;
sectionStack = sect->next;
free(sect);

View File

@@ -43,6 +43,12 @@ static char savedTIME[256];
static char savedDATE[256];
static char savedTIMESTAMP_ISO8601_LOCAL[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;
bool sym_IsPC(struct Symbol const *sym)
@@ -51,16 +57,16 @@ bool sym_IsPC(struct Symbol const *sym)
}
struct ForEachArgs {
void (*func)(struct Symbol *sym, void *arg);
void (*func)(struct Symbol *symbol, void *arg);
void *arg;
};
static void forEachWrapper(void *_sym, void *_argWrapper)
static void forEachWrapper(void *_symbol, void *_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)
@@ -96,6 +102,8 @@ static char const *Callback__FILE__(void)
char const *fileName = fstk_GetFileName();
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 */
for (size_t i = 0; fileName[i]; i++, j++) {
/* 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 (oldSrc && oldSrc->referenced && oldSrc->ID != (uint32_t)-1)
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
*/
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)
fatalerror("Failed to create symbol '%s': %s\n", symName, strerror(errno));
if (!symbol)
fatalerror("Failed to create symbol '%s': %s\n", s, strerror(errno));
if (snprintf(sym->name, MAXSYMLEN + 1, "%s", symName) > MAXSYMLEN)
warning(WARNING_LONG_STR, "Symbol name is too long: '%s'\n", symName);
if (snprintf(symbol->name, MAXSYMLEN + 1, "%s", s) > MAXSYMLEN)
warning(WARNING_LONG_STR, "Symbol name is too long: '%s'\n", s);
sym->isExported = false;
sym->isBuiltin = false;
sym->hasCallback = false;
sym->section = NULL;
setSymbolFilename(sym);
sym->ID = -1;
sym->next = NULL;
symbol->isExported = false;
symbol->isBuiltin = false;
symbol->hasCallback = false;
symbol->section = NULL;
setSymbolFilename(symbol);
symbol->ID = -1;
symbol->next = NULL;
hash_AddElement(symbols, sym->name, sym);
return sym;
hash_AddElement(symbols, symbol->name, symbol);
return symbol;
}
/*
@@ -220,45 +228,46 @@ static void assignStringSymbol(struct Symbol *sym, char const *value)
{
char *string = strdup(value);
if (!string)
if (string == NULL)
fatalerror("No memory for string equate: %s\n", strerror(errno));
sym->type = SYM_EQUS;
/* TODO: use other fields */
sym->macro = 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, '.')) {
error("Expected non-scoped symbol name, not \"%s\"\n", symName);
if (strchr(name, '.')) {
error("Expected non-scoped symbol name, not \"%s\"\n", name);
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 (strchr(dotPtr + 1, '.'))
fatalerror("'%s' is a nonsensical reference to a nested local symbol\n",
symName);
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];
fullSymbolName(fullname, sizeof(fullname), symName, labelScope);
fullSymbolName(fullname, sizeof(fullname), name, labelScope);
return sym_FindExactSymbol(fullname);
}
}
return sym_FindExactSymbol(symName);
return sym_FindExactSymbol(name);
}
struct Symbol const *sym_GetPC(void)
@@ -266,7 +275,7 @@ struct Symbol const *sym_GetPC(void)
return PCSymbol;
}
static bool isReferenced(struct Symbol const *sym)
static inline bool isReferenced(struct Symbol const *sym)
{
return sym->ID != (uint32_t)-1;
}
@@ -276,26 +285,27 @@ static bool isReferenced(struct Symbol const *sym)
*/
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);
} else if (sym->isBuiltin) {
} else if (symbol->isBuiltin) {
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);
} else {
/* Do not keep a reference to the label's name after purging it */
if (sym->name == labelScope)
sym_SetCurrentSymbolScope(NULL);
if (symbol->name == labelScope)
labelScope = NULL;
/*
* FIXME: this leaks sym->macro for SYM_EQUS and SYM_MACRO, but this can't
* free(sym->macro) because the expansion may be purging itself.
* FIXME: this leaks symbol->macro for SYM_EQUS and SYM_MACRO, but this can't
* 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 */
free(sym);
free(symbol);
}
}
@@ -330,12 +340,12 @@ uint32_t sym_GetConstantSymValue(struct Symbol const *sym)
/*
* 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)
error("'%s' not defined\n", symName);
if (sym == NULL)
error("'%s' not defined\n", s);
else
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
* hasn't already been defined or referenced in a context that would
* 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
*/
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) {
sym = createsymbol(symName);
} else if (sym_IsDefined(sym)) {
error("'%s' already defined at ", symName);
dumpFilename(sym);
if (!symbol) {
symbol = createsymbol(symbolName);
} else if (sym_IsDefined(symbol)) {
error("'%s' already defined at ", symbolName);
dumpFilename(symbol);
putc('\n', stderr);
return NULL; // Don't allow overriding the symbol, that'd be bad!
} else if (!numeric) {
// The symbol has already been referenced, but it's not allowed
error("'%s' already referenced at ", symName);
dumpFilename(sym);
error("'%s' already referenced at ", symbolName);
dumpFilename(symbol);
putc('\n', stderr);
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;
}
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.
*
@@ -448,40 +433,32 @@ struct Symbol *sym_RedefString(char const *symName, char const *value)
{
struct Symbol *sym = sym_FindExactSymbol(symName);
if (!sym)
return sym_AddString(symName, value);
if (sym->type != SYM_EQUS) {
if (sym_IsDefined(sym))
error("'%s' already defined as non-EQUS at ", symName);
else
error("'%s' already referenced at ", symName);
if (!sym) {
sym = createsymbol(symName);
} else if (sym->type != SYM_EQUS) {
error("'%s' already defined as non-EQUS 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);
/*
* FIXME: this leaks the previous sym->macro value, but this can't
* free(sym->macro) because the expansion may be redefining itself.
*/
assignStringSymbol(sym, value);
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 = sym_FindExactSymbol(symName);
if (!sym) {
if (sym == NULL) {
sym = createsymbol(symName);
} else if (sym_IsDefined(sym) && sym->type != SYM_SET) {
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")
* @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
*/
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 */
struct Symbol *sym = sym_FindExactSymbol(symName);
assert(name[0] != '.'); /* The symbol name must have been expanded prior */
struct Symbol *sym = sym_FindExactSymbol(name);
if (!sym) {
sym = createsymbol(symName);
sym = createsymbol(name);
} else if (sym_IsDefined(sym)) {
error("'%s' already defined at ", symName);
error("'%s' already defined at ", name);
dumpFilename(sym);
putc('\n', stderr);
return NULL;
@@ -527,62 +504,62 @@ static struct Symbol *addLabel(char const *symName)
sym->section = sect_GetSymbolSection();
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;
}
/*
* 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) {
error("Local label '%s' in main scope\n", symName);
error("Local label '%s' in main scope\n", name);
return NULL;
}
char fullname[MAXSYMLEN + 1];
if (symName[0] == '.') {
if (name[0] == '.') {
/* If symbol is of the form `.name`, expand to the full `Parent.name` name */
fullSymbolName(fullname, sizeof(fullname), symName, labelScope);
symName = fullname; /* Use the expanded name instead */
fullSymbolName(fullname, sizeof(fullname), name, labelScope);
name = fullname; /* Use the expanded name instead */
} else {
size_t i = 0;
/* 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++;
/* Assuming no dots in `labelScope` */
assert(strchr(&symName[i], '.')); /* There should be at least one dot, though */
size_t parentLen = i + (strchr(&symName[i], '.') - symName);
assert(strchr(&name[i], '.')); /* There should be at least one dot, though */
size_t parentLen = i + (strchr(&name[i], '.') - name);
/*
* Check that `labelScope[i]` ended the check, guaranteeing that `symName` is at
* least as long, and then that this was the entire `Parent` part of `symName`.
* Check that `labelScope[i]` ended the check, guaranteeing that `name` is at least
* 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);
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",
symName);
name);
}
return addLabel(symName);
return addLabel(name);
}
/*
* 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 */
if (sym)
sym_SetCurrentSymbolScope(sym->name);
labelScope = sym->name;
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 = sym_FindScopedSymbol(symName);
struct Symbol *nsym = sym_FindScopedSymbol(symName);
if (!sym) {
if (nsym == NULL) {
char fullname[MAXSYMLEN + 1];
if (symName[0] == '.') {
@@ -689,11 +666,11 @@ struct Symbol *sym_Ref(char const *symName)
symName = fullname;
}
sym = createsymbol(symName);
sym->type = SYM_REF;
nsym = createsymbol(symName);
nsym->type = SYM_REF;
}
return sym;
return nsym;
}
/*
@@ -704,9 +681,20 @@ void sym_SetExportAll(bool 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->hasCallback = true;
@@ -734,18 +722,13 @@ void sym_Init(time_t now)
__LINE__Symbol->numCallback = Callback__LINE__;
__FILE__Symbol->type = SYM_EQUS;
__FILE__Symbol->strCallback = Callback__FILE__;
sym_AddSet("_RS", 0)->isBuiltin = true;
#define addNumber(name, val) sym_AddEqu(name, val)->isBuiltin = true
#define addString(name, val) sym_AddString(name, val)->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);
sym_AddEqu("__RGBDS_MAJOR__", PACKAGE_VERSION_MAJOR)->isBuiltin = true;
sym_AddEqu("__RGBDS_MINOR__", PACKAGE_VERSION_MINOR)->isBuiltin = true;
sym_AddEqu("__RGBDS_PATCH__", PACKAGE_VERSION_PATCH)->isBuiltin = true;
#ifdef PACKAGE_VERSION_RC
addNumber("__RGBDS_RC__", PACKAGE_VERSION_RC);
sym_AddEqu("__RGBDS_RC__", PACKAGE_VERSION_RC)->isBuiltin = true;
#endif
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\"",
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("__DATE__", savedDATE);
addString("__ISO_8601_LOCAL__", savedTIMESTAMP_ISO8601_LOCAL);
addString("__ISO_8601_UTC__", savedTIMESTAMP_ISO8601_UTC);
addNumber("__UTC_YEAR__", time_utc->tm_year + 1900);
addNumber("__UTC_MONTH__", time_utc->tm_mon + 1);
addNumber("__UTC_DAY__", time_utc->tm_mday);
addNumber("__UTC_HOUR__", time_utc->tm_hour);
addNumber("__UTC_MINUTE__", time_utc->tm_min);
addNumber("__UTC_SECOND__", time_utc->tm_sec);
#undef addNumber
/* This cannot start with zeros */
addString("__UTC_YEAR__", savedYEAR);
addString("__UTC_MONTH__", removeLeadingZeros(savedMONTH));
addString("__UTC_DAY__", removeLeadingZeros(savedDAY));
addString("__UTC_HOUR__", removeLeadingZeros(savedHOUR));
addString("__UTC_MINUTE__", removeLeadingZeros(savedMINUTE));
addString("__UTC_SECOND__", removeLeadingZeros(savedSECOND));
#undef addString
sym_SetCurrentSymbolScope(NULL);
labelScope = NULL;
anonLabelID = 0;
/* _PI is deprecated */

View File

@@ -15,45 +15,52 @@
#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
// "'\\n'" + '\0': 5 bytes
// "0xFF" + '\0': 5 bytes
static char buf[5];
uint32_t hash = 5381;
while (*s != 0)
hash = (hash * 33) ^ (*s++);
return hash;
}
char const *print(int c)
{
static char buf[5]; /* '\xNN' + '\0' */
if (c == EOF)
return "EOF";
if (isprint(c)) {
buf[0] = '\'';
buf[1] = c;
buf[2] = '\'';
buf[3] = '\0';
buf[0] = c;
buf[1] = '\0';
return buf;
}
buf[0] = '\\';
switch (c) {
case '\n':
buf[2] = 'n';
buf[1] = 'n';
break;
case '\r':
buf[2] = 'r';
buf[1] = 'r';
break;
case '\t':
buf[2] = 't';
buf[1] = 't';
break;
default: /* Print as hex */
buf[0] = '0';
buf[1] = 'x';
snprintf(&buf[2], 3, "%02hhX", (uint8_t)c); // includes the '\0'
sprintf(&buf[2], "%02hhx", (uint8_t)c);
return buf;
}
buf[0] = '\'';
buf[1] = '\\';
buf[3] = '\'';
buf[4] = '\0';
buf[2] = '\0';
return buf;
}
@@ -67,8 +74,7 @@ size_t readUTF8Char(uint8_t *dest, char const *src)
if (decode(&state, &codep, src[i]) == 1)
return 0;
if (dest)
dest[i] = src[i];
dest[i] = src[i];
i++;
if (state == 0)

View File

@@ -21,9 +21,15 @@
unsigned int nbErrors = 0;
enum WarningState {
WARNING_DEFAULT,
WARNING_DISABLED,
WARNING_ENABLED,
WARNING_ERROR
};
static enum WarningState const defaultWarnings[NB_WARNINGS] = {
[WARNING_ASSERT] = WARNING_ENABLED,
[WARNING_BACKWARDS_FOR] = WARNING_DISABLED,
[WARNING_BUILTIN_ARG] = WARNING_DISABLED,
[WARNING_CHARMAP_REDEF] = WARNING_DISABLED,
[WARNING_DIV] = WARNING_DISABLED,
@@ -41,9 +47,9 @@ static enum WarningState const defaultWarnings[NB_WARNINGS] = {
[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)
{
@@ -66,7 +72,6 @@ static enum WarningState warningState(enum WarningID id)
static char const *warningFlags[NB_WARNINGS_ALL] = {
"assert",
"backwards-for",
"builtin-args",
"charmap-redef",
"div",
@@ -95,7 +100,6 @@ enum MetaWarningCommand {
/* Warnings that probably indicate an error */
static uint8_t const _wallCommands[] = {
WARNING_BACKWARDS_FOR,
WARNING_BUILTIN_ARG,
WARNING_CHARMAP_REDEF,
WARNING_EMPTY_DATA_DIRECTIVE,
@@ -115,7 +119,6 @@ static uint8_t const _wextraCommands[] = {
/* Literally everything. Notably useful for testing */
static uint8_t const _weverythingCommands[] = {
WARNING_BACKWARDS_FOR,
WARNING_BUILTIN_ARG,
WARNING_DIV,
WARNING_EMPTY_DATA_DIRECTIVE,
@@ -152,7 +155,8 @@ void processWarningFlag(char const *flag)
errx(1, "Cannot make meta warning \"%s\" into an error",
flag);
uint8_t const *ptr = metaWarningCommands[id - NB_WARNINGS];
uint8_t const *ptr =
metaWarningCommands[id - NB_WARNINGS];
for (;;) {
if (*ptr == META_WARNING_DONE)
@@ -193,7 +197,7 @@ void processWarningFlag(char const *flag)
bool isNegation = !strncmp(flag, "no-", strlen("no-")) && !setError;
char const *rootFlag = isNegation ? flag + strlen("no-") : flag;
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 */
for (enum WarningID id = 0; id < NB_WARNINGS; id++) {

View File

@@ -25,7 +25,7 @@
#include "platform.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
@@ -79,20 +79,6 @@ static void printUsage(void)
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 {
ROM = 0x00,
ROM_RAM = 0x08,
@@ -134,28 +120,6 @@ enum MbcType {
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
MBC_NONE = UNSPECIFIED, // No MBC specified, do not act on it
MBC_BAD, // Specified MBC does not exist / syntax error
@@ -163,33 +127,6 @@ enum MbcType {
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
*/
@@ -214,22 +151,10 @@ static bool readMBCSlice(char const **name, char const *expected)
static enum MbcType parseMBC(char const *name)
{
if (!strcasecmp(name, "help")) {
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;
}
if (name[0] >= '0' && name[0] <= '9') {
// Parse number, and return it as-is (unless it's too large)
char *endptr;
unsigned long mbc = strtoul(name, &endptr, base);
unsigned long mbc = strtoul(name, &endptr, 0);
if (*endptr)
return MBC_BAD;
@@ -323,50 +248,10 @@ do { \
mbc = BANDAI_TAMA5;
break;
case 'T': // TAMA5 / TPP1
case 'T': // TAMA5
case 't':
switch (*ptr++) {
case 'A':
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;
}
tryReadSlice("AMA5");
mbc = BANDAI_TAMA5;
break;
case 'H': // HuC{1, 3}
@@ -395,7 +280,6 @@ do { \
#define TIMER 0x20
#define RUMBLE 0x10
#define SENSOR 0x08
#define MULTIRUMBLE 0x04
for (;;) {
// Trim off trailing whitespace
@@ -419,12 +303,6 @@ do { \
features |= BATTERY;
break;
case 'M':
case 'm':
tryReadSlice("ULTIRUMBLE");
features |= MULTIRUMBLE;
break;
case 'R': // RAM or RUMBLE
case 'r':
switch (*ptr++) {
@@ -546,22 +424,6 @@ do { \
if (features != (RAM | BATTERY)) // HuC1 expects RAM+BATTERY
return MBC_WRONG_FEATURES;
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
@@ -635,34 +497,6 @@ static char const *mbcName(enum MbcType type)
return "HUC3";
case 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
case MBC_NONE:
@@ -713,30 +547,25 @@ static bool hasRAM(enum MbcType type)
case HUC3:
case HUC1_RAM_BATTERY:
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_();
}
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[] = {
0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B,
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;
}
/**
* @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 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];
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) {
report("FATAL: Failed to read \"%s\"'s header: %s\n", name, strerror(errno));
return;
} else if (rom0Len < headerSize) {
report("FATAL: \"%s\" too short, expected at least %jd ($%jx) bytes, got only %jd\n",
name, (intmax_t)headerSize, (intmax_t)headerSize, (intmax_t)rom0Len);
} else if (rom0Len < 0x150) {
report("FATAL: \"%s\" too short, expected at least 336 ($150) bytes, got only %ld\n",
name, rom0Len);
return;
}
// Accept partial reads if the file contains at least the header
if (fixSpec & (FIX_LOGO | TRASH_LOGO)) {
warnNonZero(rom0, 0x0104, sizeof(ninLogo), "Nintendo logo");
if (fixSpec & FIX_LOGO) {
memcpy(&rom0[0x104], ninLogo, sizeof(ninLogo));
} else {
@@ -879,85 +689,36 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
}
}
if (title) {
warnNonZero(rom0, 0x134, titleLen, "title");
if (title)
memcpy(&rom0[0x134], title, titleLen);
}
if (gameID) {
warnNonZero(rom0, 0x13f, gameIDLen, "manufacturer code");
if (gameID)
memcpy(&rom0[0x13f], gameID, gameIDLen);
}
if (model != DMG) {
warnNonZero(rom0, 0x143, 1, "CGB flag");
if (model != DMG)
rom0[0x143] = model == BOTH ? 0x80 : 0xc0;
}
if (newLicensee) {
warnNonZero(rom0, 0x144, newLicenseeLen, "new licensee code");
if (newLicensee)
memcpy(&rom0[0x144], newLicensee, newLicenseeLen);
}
if (sgb) {
warnNonZero(rom0, 0x146, 1, "SGB flag");
if (sgb)
rom0[0x146] = 0x03;
}
// If a valid MBC was specified...
if (cartridgeType < MBC_NONE) {
warnNonZero(rom0, 0x147, 1, "cartridge type");
uint8_t byte = cartridgeType;
if (cartridgeType < MBC_NONE)
rom0[0x147] = cartridgeType;
if ((cartridgeType & 0xff00) == TPP1) {
// Cartridge type isn't directly actionable, translate it
byte = 0xBC;
// The other TPP1 identification bytes will be written below
}
rom0[0x147] = byte;
}
if (ramSize != UNSPECIFIED)
rom0[0x149] = ramSize;
// ROM size will be written last, after evaluating the file's size
if (!japanese)
rom0[0x14a] = 0x01;
if ((cartridgeType & 0xff00) == TPP1) {
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");
if (oldLicensee != UNSPECIFIED)
rom0[0x14b] = oldLicensee;
}
if (romVersion != UNSPECIFIED) {
warnNonZero(rom0, 0x14c, 1, "mask ROM version number");
if (romVersion != UNSPECIFIED)
rom0[0x14c] = romVersion;
}
// Remain to be handled the ROM size, and header checksum.
// 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++)
sum -= rom0[i] + 1;
warnNonZero(rom0, 0x14d, 1, "header checksum");
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)
globalSum = ~globalSum;
warnNonZero(rom0, 0x14e, 2, "global checksum");
rom0[0x14e] = globalSum >> 8;
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
// However, padding may have modified ROM0 (added padding), so don't in that case
if (padValue == UNSPECIFIED)
rom0Len = headerSize;
rom0Len = 0x150;
}
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));
goto free_romx;
} else if (writeLen < rom0Len) {
report("FATAL: Could only write %jd of \"%s\"'s %jd ROM0 bytes\n",
(intmax_t)writeLen, name, (intmax_t)rom0Len);
report("FATAL: Could only write %ld of \"%s\"'s %ld ROM0 bytes\n",
writeLen, name, rom0Len);
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));
goto free_romx;
} else if ((size_t)writeLen < totalRomxLen) {
report("FATAL: Could only write %jd of \"%s\"'s %zu ROMX bytes\n",
(intmax_t)writeLen, name, totalRomxLen);
report("FATAL: Could only write %ld of \"%s\"'s %ld ROMX bytes\n",
writeLen, name, totalRomxLen);
goto free_romx;
}
}
@@ -1156,6 +915,8 @@ free_romx:
free(romx);
}
#undef trySeek
static bool processFilename(char const *name)
{
nbErrors = 0;
@@ -1188,8 +949,8 @@ static bool processFilename(char const *name)
} else if (stat.st_size < 0x150) {
// This check is in theory redundant with the one in `processFile`, but it
// 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",
name, (intmax_t)stat.st_size);
report("FATAL: \"%s\" too short, expected at least 336 ($150) bytes, got only %ld\n",
name, stat.st_size);
} else {
processFile(input, input, name, stat.st_size);
}
@@ -1206,7 +967,7 @@ fail:
int main(int argc, char *argv[])
{
nbErrors = 0;
int ch;
char ch;
while ((ch = musl_getopt_long_only(argc, argv, optstring, longopts, NULL)) != -1) {
switch (ch) {
@@ -1254,7 +1015,7 @@ do { \
#define SPEC_H TRASH_HEADER_SUM
#define SPEC_g FIX_GLOBAL_SUM
#define SPEC_G TRASH_GLOBAL_SUM
#define overrideSpec(new, bad) \
#define or(new, bad) \
do { \
if (fixSpec & SPEC_##bad) \
fprintf(stderr, \
@@ -1262,30 +1023,30 @@ do { \
fixSpec = (fixSpec & ~SPEC_##bad) | SPEC_##new; \
} while (0)
case 'l':
overrideSpec(l, L);
or(l, L);
break;
case 'L':
overrideSpec(L, l);
or(L, l);
break;
case 'h':
overrideSpec(h, H);
or(h, H);
break;
case 'H':
overrideSpec(H, h);
or(H, h);
break;
case 'g':
overrideSpec(g, G);
or(g, G);
break;
case 'G':
overrideSpec(G, g);
or(G, g);
break;
default:
fprintf(stderr, "warning: Ignoring '%c' in fix spec\n",
*musl_optarg);
#undef overrideSpec
#undef or
}
musl_optarg++;
}
@@ -1330,13 +1091,10 @@ do { \
case 'm':
cartridgeType = parseMBC(musl_optarg);
if (cartridgeType == MBC_BAD) {
report("error: Unknown MBC \"%s\"\nAccepted MBC names:\n",
musl_optarg);
printAcceptedMBCNames();
report("error: Unknown MBC \"%s\"\n", musl_optarg);
} 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);
printAcceptedMBCNames();
} else if (cartridgeType == MBC_BAD_RANGE) {
report("error: Specified MBC ID out of range 0-255: %s\n",
musl_optarg);
@@ -1390,11 +1148,7 @@ do { \
#undef parseByte
}
if ((cartridgeType & 0xff00) == TPP1 && !japanese)
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 (ramSize != UNSPECIFIED && cartridgeType < UNSPECIFIED) {
if (cartridgeType == ROM_RAM || cartridgeType == ROM_RAM_BATTERY) {
if (ramSize != 1)
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
.Pq Ad 0x147
to a given value from 0 to 0xFF.
.Pp
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.
This value may also be an MBC name from the Pan Docs.
.It Fl n Ar rom_version , Fl Fl rom-version Ar rom_version
Set the ROM version
.Pq Ad 0x14C
@@ -185,46 +179,6 @@ sans global checksum:
.Pp
.D1 $ rgbfix -cjsv -k A4 -l 0x33 -m 0x1B -p 0xFF -r 3 -t SURVIVALKIDAVKE \
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
Please report bugs on
.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)
err(1, "%s: Failed to allocate memory for tile",
__func__);
/*
* 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++) {
for (i = 0; i < tile_size; i++) {
tile[i] = gb->data[gb_i];
gb_i++;
}

View File

@@ -46,7 +46,7 @@ static HashType hash(char const *str)
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);
HalfHashType index = hashedKey;
@@ -61,7 +61,23 @@ void **hash_AddElement(HashMap map, char const *key, void *element)
newEntry->next = map[index];
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)
@@ -83,7 +99,7 @@ bool hash_RemoveElement(HashMap map, char const *key)
return false;
}
void **hash_GetNode(HashMap const map, char const *key)
void *hash_GetElement(HashMap const map, char const *key)
{
HashType hashedKey = hash(key);
struct HashMapEntry *ptr = map[(HalfHashType)hashedKey];
@@ -91,20 +107,13 @@ void **hash_GetNode(HashMap const map, char const *key)
while (ptr) {
if (hashedKey >> HALF_HASH_NB_BITS == ptr->hash
&& !strcmp(ptr->key, key)) {
return &ptr->content;
return ptr->content;
}
ptr = ptr->next;
}
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)
{
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 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->bank = location->bank;
@@ -337,7 +338,7 @@ static void placeSection(struct Section *section)
section->name, typeNames[section->type], where);
/* If the section just can't fit the bank, report that */
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->org + section->size, endaddr(section->type) + 1);
/* Otherwise there is overlap with another section */

View File

@@ -205,7 +205,7 @@ static void cleanup(void)
int main(int argc, char *argv[])
{
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 */
/* Parse options */

View File

@@ -48,7 +48,6 @@ static struct Assertion *assertions;
do { \
FILE *tmpFile = file; \
type tmpVal = func(tmpFile); \
/* TODO: maybe mark the condition as `unlikely`; how to do that portably? */ \
if (tmpVal == (errval)) { \
errx(1, __VA_ARGS__, feof(tmpFile) \
? "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.
* 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 file The file to read from. Its position will be advanced
* @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.
* Differs from `tryGetc` in that the backing function is fgetc(1).
* 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 file The file to read from. Its position will be advanced
* @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.
* Differs from `tryGetc` in that the backing function is fgetc(1).
* 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 file The file to read from. Its position will be advanced
* @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.
* 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 file The file to read from. Its position will be advanced
* @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);
}
static struct Section *getMainSection(struct Section *section)
static inline struct Section *getMainSection(struct Section *section)
{
if (section->modifier != SECTION_NORMAL)
section = sect_GetSection(section->name);
@@ -640,33 +643,19 @@ void obj_Setup(unsigned int 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)
{
(void)arg;
do {
struct Section *next = section->nextu;
free(section->name);
if (sect_HasData(section->type)) {
free(section->data);
for (uint32_t i = 0; i < section->nbPatches; i++)
free(section->patches[i].rpnExpression);
free(section->patches);
}
free(section->symbols);
free(section);
section = next;
} while (section);
free(section->name);
if (sect_HasData(section->type)) {
free(section->data);
for (uint32_t i = 0; i < section->nbPatches; i++)
free(section->patches[i].rpnExpression);
free(section->patches);
}
free(section->symbols);
free(section);
}
static void freeSymbol(struct Symbol *symbol)
@@ -679,7 +668,8 @@ void obj_Cleanup(void)
{
for (unsigned int i = 0; i < nbObjFiles; i++) {
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);
}
@@ -690,12 +680,16 @@ void obj_Cleanup(void)
sect_ForEach(freeSection, NULL);
sect_CleanupSections();
for (struct SymbolList *list = symbolLists, *next; list; list = next) {
next = list->next;
struct SymbolList *list = symbolLists;
while (list) {
for (size_t i = 0; i < list->nbSymbols; i++)
freeSymbol(list->symbolList[i]);
free(list->symbolList);
struct SymbolList *next = list->next;
free(list);
list = next;
}
}

View File

@@ -6,7 +6,6 @@
* SPDX-License-Identifier: MIT
*/
#include <assert.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdlib.h>
@@ -34,12 +33,6 @@ struct SortedSection {
struct SortedSection *next;
};
struct SortedSymbol {
struct Symbol const *sym;
uint32_t idx;
uint16_t addr;
};
static struct {
uint32_t nbBanks;
struct SortedSections {
@@ -275,86 +268,69 @@ static struct SortedSection const **nextSection(struct SortedSection const **s1,
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
* @param bankSections The bank's sections
*/
static void writeSymBank(struct SortedSections const *bankSections,
enum SectionType type, uint32_t bank)
static void writeSymBank(struct SortedSections const *bankSections)
{
if (!symFile)
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 (struct Section const *sect = ptr->section; sect; sect = sect->nextu)
nbSymbols += sect->nbSymbols;
}
for (struct SortedSection const *ptr = bankSections->sections; ptr; ptr = ptr->next) {
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++;
}
for (;;) {
while (sectList.sections
&& sectList.i == sectList.sect->nbSymbols) {
sectList.sections = sectList.sections->next;
sectList.i = 0;
}
}
for (struct SortedSection const *ptr = bankSections->sections; 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
&& zlSectList.i == zlSectList.sect->nbSymbols) {
zlSectList.sections = zlSectList.sections->next;
zlSectList.i = 0;
}
}
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++) {
struct SortedSymbol *sym = &symList[i];
minSectList = &sectList;
} 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",
symBank, sym->addr, sym->sym->name);
minSectList->sect->bank, minSectList->addr,
minSectList->sym->name);
minSectList->i++;
}
free(symList);
#undef sect
}
/**
@@ -384,8 +360,7 @@ static uint16_t writeMapBank(struct SortedSections const *sectList,
used += sect->size;
if (sect->size != 0)
fprintf(mapFile, " SECTION: $%04" PRIx16 "-$%04x ($%04" PRIx16
" byte%s) [\"%s\"]\n",
fprintf(mapFile, " SECTION: $%04" PRIx16 "-$%04" PRIx16 " ($%04" PRIx16 " byte%s) [\"%s\"]\n",
sect->org, sect->org + sect->size - 1,
sect->size, sect->size == 1 ? "" : "s",
sect->name);
@@ -468,7 +443,7 @@ static void writeSymAndMap(void)
for (uint32_t bank = 0; bank < sections[type].nbBanks; bank++) {
struct SortedSections const *sect = &sections[type].banks[bank];
writeSymBank(sect, type, bank);
writeSymBank(sect);
usedMap[type] += writeMapBank(sect, type, bank);
}
}

View File

@@ -37,7 +37,7 @@ struct RPNStack {
size_t capacity;
} stack;
static void initRPNStack(void)
static inline void initRPNStack(void)
{
stack.capacity = 64;
stack.values = malloc(sizeof(*stack.values) * stack.capacity);
@@ -46,7 +46,7 @@ static void initRPNStack(void)
err(1, "Failed to init RPN stack");
}
static void clearRPNStack(void)
static inline void clearRPNStack(void)
{
stack.size = 0;
}
@@ -92,7 +92,7 @@ static int32_t popRPN(struct FileStackNode const *node, uint32_t lineNo)
return stack.values[stack.size];
}
static void freeRPNStack(void)
static inline void freeRPNStack(void)
{
free(stack.values);
free(stack.errorFlags);
@@ -292,11 +292,6 @@ static int32_t computeRPNExpr(struct Patch const *patch,
break;
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;
while (getRPNByte(&expression, &size, patch->src, patch->lineNo))
;
@@ -325,44 +320,6 @@ static int32_t computeRPNExpr(struct Patch const *patch,
}
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:
value = popRPN();
if (!isError && (value < 0
@@ -478,8 +435,6 @@ void patch_CheckAssertions(struct Assertion *assert)
}
struct Assertion *next = assert->next;
free(assert->patch.rpnExpression);
free(assert->message);
free(assert);
assert = next;
}

View File

@@ -19,7 +19,7 @@
#include "extern/err.h"
FILE *linkerScript;
FILE * linkerScript;
char *includeFileName;
static uint32_t lineNo;
@@ -43,7 +43,8 @@ static void pushFile(char *newFileName)
if (!fileStackSize) /* Init file stack */
fileStackSize = 4;
fileStackSize *= 2;
fileStack = realloc(fileStack, sizeof(*fileStack) * fileStackSize);
fileStack = realloc(fileStack,
sizeof(*fileStack) * fileStackSize);
if (!fileStack)
err(1, "%s(%" PRIu32 "): Internal INCLUDE error",
linkerScriptName, lineNo);
@@ -77,12 +78,12 @@ static bool popFile(void)
return true;
}
static bool isWhiteSpace(int c)
static inline bool isWhiteSpace(int c)
{
return c == ' ' || c == '\t';
}
static bool isNewline(int c)
static inline bool isNewline(int c)
{
return c == '\r' || c == '\n';
}
@@ -172,11 +173,11 @@ static char const * const commands[] = {
[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",
linkerScriptName, lineNo, __func__);
return curchar;
@@ -193,14 +194,14 @@ static struct LinkerScriptToken *nextToken(void)
/* Skip initial whitespace... */
do
curchar = nextChar();
curchar = readChar(linkerScript);
while (isWhiteSpace(curchar));
/* If this is a comment, skip to the end of the line */
if (curchar == ';') {
do {
curchar = nextChar();
} while (!isNewline(curchar) && curchar != EOF);
do
curchar = readChar(linkerScript);
while (!isNewline(curchar) && 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 */
token.type = TOKEN_NEWLINE;
if (curchar == '\r') {
/* Handle CRLF */
curchar = nextChar();
if (curchar != '\n') {
ungetc(curchar, linkerScript);
curchar = '\r';
}
}
/* FIXME: This works with CRLF newlines, but not CR-only */
if (curchar == '\r')
readChar(linkerScript); /* Read and discard LF */
} else if (curchar == '"') {
/* If we have a string start, this is a string */
token.type = TOKEN_STRING;
@@ -226,33 +222,18 @@ static struct LinkerScriptToken *nextToken(void)
size_t capacity = 16; /* Half of the default capacity */
do {
curchar = nextChar();
if (curchar == EOF || isNewline(curchar)) {
curchar = readChar(linkerScript);
if (curchar == EOF || isNewline(curchar))
errx(1, "%s(%" PRIu32 "): Unterminated string",
linkerScriptName, lineNo);
} else if (curchar == '"') {
else if (curchar == '"')
/* Quotes force a string termination */
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) {
capacity *= 2;
token.attr.string = realloc(token.attr.string, capacity);
token.attr.string = realloc(token.attr.string,
capacity);
if (!token.attr.string)
err(1, "%s: Failed to allocate memory for string",
__func__);
@@ -279,9 +260,10 @@ static struct LinkerScriptToken *nextToken(void)
if (!curchar)
break;
curchar = nextChar();
curchar = readChar(linkerScript);
/* Whitespace, a newline or a comment end the token */
if (isWhiteSpace(curchar) || isNewline(curchar) || curchar == ';') {
if (isWhiteSpace(curchar) || isNewline(curchar)
|| curchar == ';') {
ungetc(curchar, linkerScript);
curchar = '\0';
}
@@ -300,7 +282,8 @@ static struct LinkerScriptToken *nextToken(void)
if (token.type == TOKEN_INVALID) {
/* 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)) {
token.type = TOKEN_BANK;
token.attr.secttype = type;
@@ -330,7 +313,8 @@ static struct LinkerScriptToken *nextToken(void)
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) {
case COMMAND_INVALID:
@@ -475,7 +459,8 @@ struct SectionPlacement *script_NextSection(void)
errx(1, "%s(%" PRIu32 "): Command specified without an argument",
linkerScriptName, lineNo);
processCommand(attr.command, arg, &curaddr[type][bankID]);
processCommand(attr.command, arg,
&curaddr[type][bankID]);
} else { /* TOKEN_BANK */
type = attr.secttype;
/*

View File

@@ -49,8 +49,8 @@ static void checkSectUnionCompat(struct Section *target, struct Section *other)
other->name, target->org, other->org);
} else if (target->isAlignFixed) {
if ((other->org - target->alignOfs) & target->alignMask)
errx(1, "Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
PRIu16 ") and address $%04" PRIx16,
errx(1, "Section \"%s\" is defined with conflicting %" PRIu16
"-byte alignment (offset %" PRIu16 ") and address $%04" PRIx16,
other->name, target->alignMask + 1,
target->alignOfs, other->org);
}
@@ -61,14 +61,15 @@ static void checkSectUnionCompat(struct Section *target, struct Section *other)
if (target->isAddressFixed) {
if ((target->org - other->alignOfs) & other->alignMask)
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->alignMask + 1, other->alignOfs);
} else if (target->isAlignFixed
&& (other->alignMask & target->alignOfs)
!= (target->alignMask & other->alignOfs)) {
errx(1, "Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
PRIu16 ") and %d-byte alignment (offset %" PRIu16 ")",
errx(1, "Section \"%s\" is defined with conflicting %" PRIu16
"-byte alignment (offset %" PRIu16 ") and %" PRIu16
"-byte alignment (offset %" PRIu16 ")",
other->name, target->alignMask + 1, target->alignOfs,
other->alignMask + 1, other->alignOfs);
} else if (!target->isAlignFixed || (other->alignMask > target->alignMask)) {
@@ -91,8 +92,8 @@ static void checkFragmentCompat(struct Section *target, struct Section *other)
} else if (target->isAlignFixed) {
if ((org - target->alignOfs) & target->alignMask)
errx(1, "Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
PRIu16 ") and address $%04" PRIx16,
errx(1, "Section \"%s\" is defined with conflicting %" PRIu16
"-byte alignment (offset %" PRIu16 ") and address $%04" PRIx16,
other->name, target->alignMask + 1,
target->alignOfs, other->org);
}
@@ -108,14 +109,15 @@ static void checkFragmentCompat(struct Section *target, struct Section *other)
if (target->isAddressFixed) {
if ((target->org - ofs) & other->alignMask)
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->alignMask + 1, other->alignOfs);
} else if (target->isAlignFixed
&& (other->alignMask & target->alignOfs) != (target->alignMask & ofs)) {
errx(1, "Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
PRIu16 ") and %d-byte alignment (offset %" PRIu16 ")",
errx(1, "Section \"%s\" is defined with conflicting %" PRIu16
"-byte alignment (offset %" PRIu16 ") and %" PRIu16
"-byte alignment (offset %" PRIu16 ")",
other->name, target->alignMask + 1, target->alignOfs,
other->alignMask + 1, other->alignOfs);
@@ -163,9 +165,6 @@ static void mergeSections(struct Section *target, struct Section *other, enum Se
if (!target->data)
errx(1, "Failed to concatenate \"%s\"'s fragments", target->name);
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;
@@ -250,7 +249,7 @@ static void doSanityChecks(struct Section *section, void *ptr)
/* Too large an alignment may not be satisfiable */
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);
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));
if (section->org + section->size > endaddr(section->type) + 1)
fail("Section \"%s\"'s end address %#x is greater than last address %#x",
section->name, section->org + section->size,
endaddr(section->type) + 1);
fail("Section \"%s\"'s end address %#" PRIx16
" is greater than last address %#" PRIx16, section->name,
section->org + section->size, endaddr(section->type) + 1);
}
#undef fail

View File

@@ -54,7 +54,10 @@ void sym_AddSymbol(struct Symbol *symbol)
}
/* 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)

View File

@@ -226,7 +226,7 @@ with some bytes being special prefixes for integers and symbols.
.It Li $04 Ta Li % operator
.It Li $05 Ta Li unary -
.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 $12 Ta Li ^ operator
.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) ,
a null-terminated string follows.
.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 .
Checks if the value is in HRAM, ANDs it with 0xFF.
.It Li $61 Ta Li RSTCheck .

View File

@@ -18,10 +18,6 @@ $ rgbasm \-o bar.o foo.asm
$ rgblink \-o baz.gb bar.o
$ rgbfix \-v \-p 0 baz.gb
.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
.Xr rgbasm 1 ,
.Xr rgbfix 1 ,

View File

@@ -1,3 +1,2 @@
ERROR: align-pc-outside-section.asm(1):
Cannot output data outside of a SECTION
error: Assembly aborted (1 error)!
FATAL: align-pc-outside-section.asm(1):
Code generation before SECTION directive

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):
Unterminated block comment
ERROR: block-comment-termination-error.asm(1):
syntax error, unexpected end of buffer
syntax error, unexpected newline
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):
syntax error, unexpected PRINTLN, expecting newline or end of buffer
syntax error, unexpected PRINTLN, expecting newline
ERROR: code-after-endm-endr-endc.asm(7):
Macro "mac" not defined
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):
syntax error, unexpected PRINTLN, expecting newline
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):
syntax error, unexpected PRINTLN, expecting newline
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)!

View File

@@ -26,5 +26,4 @@ def constant equ 6*7 ; fails
redef string equs "there"
println "{string}"
redef constant equ 6*9
println constant
redef constant equ 6*9 ; syntax error

View File

@@ -1,3 +1,5 @@
ERROR: def.asm(23):
'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
$2A
there
$36

View File

@@ -1,62 +1,36 @@
def _ASM equ 0
_ASM equ 0
test: MACRO
; Test RGBASM
redef V equs "_ASM +"
; Test RGBASM
V equs "_ASM +"
static_assert \#
; Test RGBLINK
redef V equs "_LINK +"
PURGE V
; Test RGBLINK
V equs "_LINK +"
assert \#
PURGE V
ENDM
test_mod: MACRO
def x = \1 ; dividend
def y = \2 ; divisor
shift 2
def q = x / y ; quotient
def r = x % y ; remainder
; identity laws
test (V (q * y + r)) == (V x)
test (V (x + y) % y) == (V r)
test (V (x - y) % y) == (V r)
ENDM
for x, -300, 301
for y, -x - 1, x + 2
if y != 0
q = x / y
r = x % y
test (V (q * y + r)) == (V x)
test (V (x + y) % y) == (V r)
test (V (x - y) % y) == (V r)
endc
endr
endr
test_each_mod: MACRO
test_mod (\1), (\2)
test_mod (\1), -(\2)
test_mod -(\1), (\2)
test_mod -(\1), -(\2)
ENDM
test_pow: MACRO
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
for x, -300, 301
for p, 31
y = 2 ** p
r = x % y
m = x & (y - 1)
test (V r) == (V m)
endr
endr
SECTION "LINK", ROM0
_LINK::

View File

@@ -3,6 +3,5 @@ warning: equs-newline.asm(3): [-Wuser]
while expanding symbol "ACT"
warning: equs-newline.asm(3): [-Wuser]
Second
while expanding symbol "ACT"
warning: equs-newline.asm(4): [-Wuser]
Third

View File

@@ -1,2 +1,6 @@
recurse EQUS "recurse"
recurse EQUS "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