Compare commits

..

49 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
Rangi
d05703c692 Release 0.5.0-rc2 2021-03-28 17:15:44 -04:00
Rangi
5dbfafcc55 Update man page copyrights to 2021 2021-03-28 16:37:15 -04:00
Rangi
a265b85d9d Report "1 error", not "1 errors", when assembly is aborted
This matches other such pluralized error messages
2021-03-28 15:55:32 -04:00
Rangi
28abf03c0a Output USED space, not FREE space, in the .map file
Fixes #808
2021-03-28 15:49:44 -04:00
Rangi
7e7f92f18c Assign section locations to all UNIONs/FRAGMENTs
Fixes the test case from #800

The `out.gb` output was corrected, since the two "test"
fragments have a different order in ROM than in SRAM.
It is effectively:

; ROM0[$0000], fragments ordered by size
    jr Label
    dw Label
    db 0

; SRAM[$a000], fragments ordered by .o order
    ds 1  ; db 0
    ds 2  ; jr Label
Label:    ; $a003
    ds 2  ; dw Label
2021-03-28 15:21:17 -04:00
Eldred Habert
7461170956 Add LOAD FRAGMENT pc test (#800)
Reproduces a reported problem, fix pending
2021-03-28 15:11:20 -04:00
Rangi
57d966d6e0 Merge pull request #804 from Rangi42/normal-backslash
Backslash in normal lexer mode must be a line continuation
2021-03-26 13:33:11 -04:00
Rangi
17752d7094 Backslash in normal lexer mode must be a line continuation
Macro args were already handled by `peek`, and character escapes
do not exist outside of string literals.

This only affects the error message printed when a non-whitespace
character comes after the backslash. Instead of "Illegal character
escape '%s'", it will print "Begun line continuation, but
encountered character '%s'".
2021-03-26 13:23:05 -04:00
ISSOtm
4216f0a9b0 Init top-level fstack node line number
It still gets written to the object file, generating Valgrind warnings
about using uninitialized memory. To silence those errors, and make
output more reproducible, init the line no to a dummy (0) value.
2021-03-25 11:26:58 +01:00
ISSOtm
e3fde986ad Remove hashmap collision warning
It was only used to check hashmap collision rate in the wild,
but it no longer has a purpose and produces spurious messages
2021-03-23 21:34:08 +01:00
Rangi
aa99ed056c Do not evaluate an untaken ELIF's condition
Fixes #764
2021-03-23 16:20:24 +01:00
ISSOtm
46d6652df1 Fix missing .sym/.map symbols in SECTION UNION/FRAGMENTs
Only the first "slice"'s symbols were considered, forgetting some symbols
2021-03-22 23:23:06 +01:00
ISSOtm
5406674cdd Install groff to build PDFs correctly in CI 2021-03-20 02:09:47 +01:00
ISSOtm
5d2e2e2182 Make docs update script also produce PDFs
See rgbds-www#12
2021-03-20 01:53:26 +01:00
ISSOtm
a929f36bc5 Replace UTF-8 hyphens with ASCII ones in man pages 2021-03-20 01:25:17 +01:00
ISSOtm
bdb84a901f Use sub-sections for the different symbol types
This will make them appear in the ToC, and generate HTML anchors for them
2021-03-19 11:04:22 +01:00
Rangi
093631ca0b Revise rgbasm(5) string symbol documentation
Clarify the differences between EQUS expansion and {interpolation}
2021-03-19 01:48:36 +01:00
Rangi
7e127a4e52 Don't expand string symbols in MACRO and FOR symbol names
Explicit {interpolation} can still achieve this, but
to match DEF, REDEF, and PURGE, these new directives that
define symbols do not expand string EQUS.
2021-03-19 01:48:36 +01:00
Rangi
b8093847dc New definition syntax with leading DEF keyword
This will enable fixing #457 later once the old
definition syntax is removed.
2021-03-19 01:48:36 +01:00
Rangi
8d1b56bcf5 rgblink identifies patches' PC sections after reading all sections
Fixes #794
2021-03-18 23:53:54 +01:00
ISSOtm
5fb7fcf461 Fix description of rgbgfx -h
The old description was backwards and mostly confusing.
2021-03-18 15:35:01 +01:00
Rangi
ee900ae7a3 Add a missing newline to a verbosePrint 2021-03-17 20:53:25 -04:00
ISSOtm
3ca58e13dc Fix verbose messages claiming non-existent errors
They were confusing when trying to debug other things
2021-03-14 18:52:16 +01:00
ISSOtm
aaa4e17454 Add verbose messages for early exit from -MG
Should help debugging Make invocations
2021-03-14 18:28:05 +01:00
daid
a81d383f75 Alignment mask was incorrectly checked for 1 instead of 0
This caused an `ALIGN[1]` to be ignored.
2021-03-11 13:24:59 +01:00
ISSOtm
60019cf476 Fix a bunch of Clang warnings
As reported by #789
Should avoid relying on 32-bit int (for implicit conversions)
and account for more extreme uses of RGBDS.
2021-03-10 10:56:57 +01:00
ISSOtm
5a6a44cbc1 Fix master documentation updater
Its path was not synced with a recent change
2021-03-10 01:15:51 +01:00
93 changed files with 1579 additions and 1163 deletions

View File

@@ -53,8 +53,8 @@ PAGES=(
WWWPATH="/docs" WWWPATH="/docs"
mkdir -p "$1/_documentation/$2" mkdir -p "$1/_documentation/$2"
# `mandoc` uses a different format for referring to man pages present in the **current** directory # `mandoc` uses a different format for referring to man pages present in the **current** directory.
# We want that format for RGBDS man pages, and the other one for the t=rest; # We want that format for RGBDS man pages, and the other one for the rest;
# we thus need to copy all pages to a temporary directory, and process them there. # we thus need to copy all pages to a temporary directory, and process them there.
# Copy all pages to current dir # Copy all pages to current dir
@@ -77,6 +77,7 @@ EOF
options+=,toc options+=,toc
fi fi
mandoc -Thtml -I os=Linux -O$options "${PAGES[$page]##*/}" | .github/actions/doc_postproc.awk >> "$1/_documentation/$2/$page" mandoc -Thtml -I os=Linux -O$options "${PAGES[$page]##*/}" | .github/actions/doc_postproc.awk >> "$1/_documentation/$2/$page"
groff -Tpdf -mdoc -wall "${PAGES[$page]##*/}" >"$1/_documentation/$2/$stem.pdf"
if [ $is_release -ne 0 ]; then if [ $is_release -ne 0 ]; then
cat - >"$1/_documentation/$page" <<EOF cat - >"$1/_documentation/$page" <<EOF
--- ---
@@ -88,6 +89,7 @@ description: RGBDS latest stable — $descr
EOF EOF
fi fi
done done
cat - >"$1/_documentation/$2/index.html" <<EOF cat - >"$1/_documentation/$2/index.html" <<EOF
--- ---
layout: doc_index layout: doc_index
@@ -97,6 +99,7 @@ description: RGBDS $2 - Online manual
--- ---
EOF EOF
# If making a release, add a new entry right after `master` # If making a release, add a new entry right after `master`
if [ $is_release -ne 0 ]; then if [ $is_release -ne 0 ]; then
awk '{ print } awk '{ print }
@@ -105,5 +108,6 @@ if [ $is_release -ne 0 ]; then
mv "$1/_data/doc.json"{.tmp,} mv "$1/_data/doc.json"{.tmp,}
fi fi
# Clean up # Clean up
rm "${PAGES[@]##*/}" rm "${PAGES[@]##*/}"

View File

@@ -18,10 +18,10 @@ jobs:
repository: ${{ github.repository_owner }}/rgbds-www repository: ${{ github.repository_owner }}/rgbds-www
path: rgbds-www path: rgbds-www
# `-O toc` was added in 1.14.5, but the repos only have 1.14.4 # `-O toc` was added in 1.14.5, but the repos only have 1.14.4
- name: Build and install mandoc - name: Build and install mandoc + install groff
run: | run: |
sudo apt-get -qq update sudo apt-get -qq update
sudo apt-get install -yq zlib1g-dev sudo apt-get install -yq groff zlib1g-dev
wget 'http://mandoc.bsd.lv/snapshots/mandoc-1.14.5.tar.gz' wget 'http://mandoc.bsd.lv/snapshots/mandoc-1.14.5.tar.gz'
tar xf mandoc-1.14.5.tar.gz tar xf mandoc-1.14.5.tar.gz
cd mandoc-1.14.5 cd mandoc-1.14.5

View File

@@ -31,10 +31,10 @@ jobs:
repository: gbdev/rgbds-www repository: gbdev/rgbds-www
ref: master ref: master
path: rgbds-www path: rgbds-www
- name: Build and install mandoc - name: Build and install mandoc + install groff
run: | run: |
sudo apt-get -qq update sudo apt-get -qq update
sudo apt-get install -yq zlib1g-dev sudo apt-get install -yq groff zlib1g-dev
wget 'http://mandoc.bsd.lv/snapshots/mandoc-1.14.5.tar.gz' wget 'http://mandoc.bsd.lv/snapshots/mandoc-1.14.5.tar.gz'
tar xf mandoc-1.14.5.tar.gz tar xf mandoc-1.14.5.tar.gz
cd mandoc-1.14.5 cd mandoc-1.14.5
@@ -44,7 +44,7 @@ jobs:
- name: Update pages - name: Update pages
working-directory: rgbds working-directory: rgbds
run: | run: |
./.github/actions/get-pages.sh ../rgbds-www/_documentation master ./.github/actions/get-pages.sh ../rgbds-www master
- name: Push new pages - name: Push new pages
working-directory: rgbds-www working-directory: rgbds-www
run: | run: |

View File

@@ -75,7 +75,7 @@ static inline bool sym_IsConstant(struct Symbol const *sym)
if (sym->type == SYM_LABEL) { if (sym->type == SYM_LABEL) {
struct Section const *sect = sym_GetSection(sym); struct Section const *sect = sym_GetSection(sym);
return sect && sect->org != -1; return sect && sect->org != (uint32_t)-1;
} }
return sym->type == SYM_EQU || sym->type == SYM_SET; return sym->type == SYM_EQU || sym->type == SYM_SET;
} }

View File

@@ -30,11 +30,11 @@ struct AttachedSymbol {
struct Patch { struct Patch {
struct FileStackNode const *src; struct FileStackNode const *src;
uint32_t lineNo; uint32_t lineNo;
int32_t offset; uint32_t offset;
uint32_t pcSectionID; uint32_t pcSectionID;
uint32_t pcOffset; uint32_t pcOffset;
enum PatchType type; enum PatchType type;
int32_t rpnSize; uint32_t rpnSize;
uint8_t *rpnExpression; uint8_t *rpnExpression;
struct Section const *pcSection; struct Section const *pcSection;

View File

@@ -1,7 +1,7 @@
/* /*
* This file is part of RGBDS. * This file is part of RGBDS.
* *
* Copyright (c) 2017-2018, Antonio Nino Diaz and RGBDS contributors. * Copyright (c) 2017-2021, Antonio Nino Diaz and RGBDS contributors.
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@@ -12,7 +12,7 @@
#define PACKAGE_VERSION_MAJOR 0 #define PACKAGE_VERSION_MAJOR 0
#define PACKAGE_VERSION_MINOR 5 #define PACKAGE_VERSION_MINOR 5
#define PACKAGE_VERSION_PATCH 0 #define PACKAGE_VERSION_PATCH 0
#define PACKAGE_VERSION_RC 1 #define PACKAGE_VERSION_RC 2
const char *get_package_version_string(void); const char *get_package_version_string(void);

View File

@@ -175,8 +175,14 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
char const *incPath = i ? includePaths[i - 1] : ""; char const *incPath = i ? includePaths[i - 1] : "";
int len = snprintf(*fullPath, *size, "%s%s", incPath, path); int len = snprintf(*fullPath, *size, "%s%s", incPath, path);
if (len < 0) {
error("snprintf error during include path search: %s\n",
strerror(errno));
break;
}
/* Oh how I wish `asnprintf` was standard... */ /* Oh how I wish `asnprintf` was standard... */
if (len >= *size) { /* `len` doesn't include the terminator, `size` does */ if ((size_t)len >= *size) { /* `len` doesn't include the terminator, `size` does */
*size = len + 1; *size = len + 1;
*fullPath = realloc(*fullPath, *size); *fullPath = realloc(*fullPath, *size);
if (!*fullPath) { if (!*fullPath) {
@@ -187,10 +193,7 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
len = sprintf(*fullPath, "%s%s", incPath, path); len = sprintf(*fullPath, "%s%s", incPath, path);
} }
if (len < 0) { if (isPathValid(*fullPath)) {
error("snprintf error during include path search: %s\n",
strerror(errno));
} else if (isPathValid(*fullPath)) {
printDep(*fullPath); printDep(*fullPath);
return true; return true;
} }
@@ -314,10 +317,14 @@ void fstk_RunInclude(char const *path)
if (!fstk_FindFile(path, &fullPath, &size)) { if (!fstk_FindFile(path, &fullPath, &size)) {
free(fullPath); free(fullPath);
if (oGeneratedMissingIncludes) if (oGeneratedMissingIncludes) {
if (verbose)
printf("Aborting (-MG) on INCLUDE file '%s' (%s)\n",
path, strerror(errno));
oFailedOnMissingInclude = true; oFailedOnMissingInclude = true;
else } else {
error("Unable to open included file '%s': %s\n", path, strerror(errno)); error("Unable to open included file '%s': %s\n", path, strerror(errno));
}
return; return;
} }
dbgPrint("Full path: \"%s\"\n", fullPath); dbgPrint("Full path: \"%s\"\n", fullPath);
@@ -532,6 +539,7 @@ void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
context->fileInfo = (struct FileStackNode *)fileInfo; context->fileInfo = (struct FileStackNode *)fileInfo;
/* lineNo and reptIter are unused on the top-level context */ /* lineNo and reptIter are unused on the top-level context */
context->fileInfo->parent = NULL; context->fileInfo->parent = NULL;
context->fileInfo->lineNo = 0; // This still gets written to the object file, so init it
context->fileInfo->referenced = false; context->fileInfo->referenced = false;
context->fileInfo->type = NODE_FILE; context->fileInfo->type = NODE_FILE;
memcpy(fileInfo->name, fileName, len + 1); memcpy(fileInfo->name, fileName, len + 1);

View File

@@ -163,16 +163,20 @@ static struct KeywordMapping {
{"DE", T_MODE_DE}, {"DE", T_MODE_DE},
{"HL", T_MODE_HL}, {"HL", T_MODE_HL},
{"SP", T_MODE_SP}, {"SP", T_MODE_SP},
{"PC", T_MODE_PC},
{"HLD", T_MODE_HL_DEC}, {"HLD", T_MODE_HL_DEC},
{"HLI", T_MODE_HL_INC}, {"HLI", T_MODE_HL_INC},
{"IME", T_MODE_IME},
{"A", T_TOKEN_A}, {"A", T_TOKEN_A},
{"F", T_TOKEN_F},
{"B", T_TOKEN_B}, {"B", T_TOKEN_B},
{"C", T_TOKEN_C}, {"C", T_TOKEN_C},
{"D", T_TOKEN_D}, {"D", T_TOKEN_D},
{"E", T_TOKEN_E}, {"E", T_TOKEN_E},
{"H", T_TOKEN_H}, {"H", T_TOKEN_H},
{"L", T_TOKEN_L}, {"L", T_TOKEN_L},
{"W", T_TOKEN_W},
{"DEF", T_OP_DEF}, {"DEF", T_OP_DEF},
@@ -275,10 +279,10 @@ static struct KeywordMapping {
{"RW", T_POP_RW}, {"RW", T_POP_RW},
/* Handled before as T_Z80_RL */ /* Handled before as T_Z80_RL */
/* {"RL", T_POP_RL}, */ /* {"RL", T_POP_RL}, */
{"EQU", T_POP_EQU}, {"EQU", T_POP_EQU},
{"EQUS", T_POP_EQUS}, {"EQUS", T_POP_EQUS},
{"REDEF", T_POP_REDEF}, {"REDEF", T_POP_REDEF},
/* Handled before as T_Z80_SET */ /* Handled before as T_Z80_SET */
/* {"SET", T_POP_SET}, */ /* {"SET", T_POP_SET}, */
@@ -290,6 +294,7 @@ static struct KeywordMapping {
{"OPT", T_POP_OPT}, {"OPT", T_POP_OPT},
{".", T_PERIOD}, {".", T_PERIOD},
{"...", T_ELLIPSIS},
}; };
static bool isWhitespace(int c) static bool isWhitespace(int c)
@@ -350,6 +355,7 @@ struct LexerState {
uint32_t lineNo; uint32_t lineNo;
uint32_t colNo; uint32_t colNo;
int lastToken; int lastToken;
int nextToken;
struct IfStack *ifStack; struct IfStack *ifStack;
@@ -374,6 +380,7 @@ static void initState(struct LexerState *state)
state->mode = LEXER_NORMAL; state->mode = LEXER_NORMAL;
state->atLineStart = true; /* yylex() will init colNo due to this */ state->atLineStart = true; /* yylex() will init colNo due to this */
state->lastToken = T_EOF; state->lastToken = T_EOF;
state->nextToken = 0;
state->ifStack = NULL; state->ifStack = NULL;
@@ -504,9 +511,15 @@ struct LexerState *lexer_OpenFile(char const *path)
} }
if (!state->isMmapped) { if (!state->isMmapped) {
/* Sometimes mmap() fails or isn't available, so have a fallback */ /* Sometimes mmap() fails or isn't available, so have a fallback */
if (verbose) if (verbose) {
if (isStdin)
printf("Opening stdin\n");
else if (fileInfo.st_size == 0)
printf("File %s is empty\n", path);
else
printf("File %s opened as regular, errno reports \"%s\"\n", printf("File %s opened as regular, errno reports \"%s\"\n",
path, strerror(errno)); path, strerror(errno));
}
state->index = 0; state->index = 0;
state->nbChars = 0; state->nbChars = 0;
} }
@@ -580,7 +593,7 @@ struct KeywordDictNode {
uint16_t children[0x60 - ' ']; uint16_t children[0x60 - ' '];
struct KeywordMapping const *keyword; struct KeywordMapping const *keyword;
/* Since the keyword structure is invariant, the min number of nodes is known at compile time */ /* Since the keyword structure is invariant, the min number of nodes is known at compile time */
} keywordDict[351] = {0}; /* Make sure to keep this correct when adding keywords! */ } keywordDict[356] = {0}; /* Make sure to keep this correct when adding keywords! */
/* Convert a char into its index into the dict */ /* Convert a char into its index into the dict */
static inline uint8_t dictIndex(char c) static inline uint8_t dictIndex(char c)
@@ -866,8 +879,9 @@ static int peekInternal(uint8_t distance)
size_t nbExpectedChars = LEXER_BUF_SIZE - writeIndex; size_t nbExpectedChars = LEXER_BUF_SIZE - writeIndex;
readChars(nbExpectedChars); readChars(nbExpectedChars);
/* If the read was incomplete, don't perform a second read */ // If the read was incomplete, don't perform a second read
if (nbCharsRead < nbExpectedChars) // `nbCharsRead` cannot be negative, so it's fine to cast to `size_t`
if ((size_t)nbCharsRead < nbExpectedChars)
target = 0; target = 0;
} }
if (target != 0) if (target != 0)
@@ -1849,10 +1863,20 @@ static char const *reportGarbageChar(unsigned char firstByte)
/* Lexer core */ /* Lexer core */
static int yylex_SKIP_TO_ENDC(void); // forward declaration for yylex_NORMAL
static int yylex_NORMAL(void) static int yylex_NORMAL(void)
{ {
dbgPrint("Lexing in normal mode, line=%" PRIu32 ", col=%" PRIu32 "\n", dbgPrint("Lexing in normal mode, line=%" PRIu32 ", col=%" PRIu32 "\n",
lexer_GetLineNo(), lexer_GetColNo()); lexer_GetLineNo(), lexer_GetColNo());
if (lexerState->nextToken) {
int token = lexerState->nextToken;
lexerState->nextToken = 0;
return token;
}
for (;;) { for (;;) {
int c = nextChar(); int c = nextChar();
char secondChar; char secondChar;
@@ -1881,7 +1905,7 @@ static int yylex_NORMAL(void)
case '@': case '@':
yylval.tzSym[0] = '@'; yylval.tzSym[0] = '@';
yylval.tzSym[1] = '\0'; yylval.tzSym[1] = '\0';
return T_ID; return T_TOKEN_AT;
case '[': case '[':
return T_LBRACK; return T_LBRACK;
@@ -1894,6 +1918,11 @@ static int yylex_NORMAL(void)
case ',': case ',':
return T_COMMA; return T_COMMA;
case '\'':
return T_PRIME;
case '?':
return T_QUESTION;
/* Handle ambiguous 1- or 2-char tokens */ /* Handle ambiguous 1- or 2-char tokens */
case '*': /* Either MUL or EXP */ case '*': /* Either MUL or EXP */
@@ -1944,6 +1973,10 @@ static int yylex_NORMAL(void)
return T_OP_LOGICGE; return T_OP_LOGICGE;
case '>': case '>':
shiftChars(1); shiftChars(1);
if (peek(0) == '>') {
shiftChars(1);
return T_OP_SHRL;
}
return T_OP_SHR; return T_OP_SHR;
default: default:
return T_OP_LOGICGT; return T_OP_LOGICGT;
@@ -1971,25 +2004,6 @@ static int yylex_NORMAL(void)
case '$': case '$':
yylval.nConstValue = 0; yylval.nConstValue = 0;
readHexNumber(); readHexNumber();
/* Attempt to match `$ff00+c` */
if (yylval.nConstValue == 0xff00) {
/* Whitespace is ignored anyways */
while (isWhitespace(c = peek(0)))
shiftChars(1);
if (c == '+') {
/* FIXME: not great due to large lookahead */
uint8_t distance = 1;
do {
c = peek(distance++);
} while (isWhitespace(c));
if (c == 'c' || c == 'C') {
shiftChars(distance);
return T_MODE_HW_C;
}
}
}
return T_NUMBER; return T_NUMBER;
case '0': /* Decimal number */ case '0': /* Decimal number */
@@ -2050,34 +2064,101 @@ static int yylex_NORMAL(void)
case EOF: case EOF:
return T_EOF; return T_EOF;
/* Handle escapes */ /* Handle line continuations */
case '\\': case '\\':
c = peek(0); // Macro args were handled by `peek`, and character escapes do not exist
// outside of string literals, so this must be a line continuation.
switch (c) {
case ' ':
case '\r':
case '\n':
readLineContinuation(); readLineContinuation();
break; break;
case EOF: /* Handle 8-bit registers followed by a period */
error("Illegal character escape at end of input\n");
break;
default: case 'A':
case 'a':
if (peek(0) == '.') {
shiftChars(1); shiftChars(1);
error("Illegal character escape '%s'\n", print(c)); lexerState->nextToken = T_PERIOD;
return T_TOKEN_A;
} }
break; goto normal_identifier;
/* Handle identifiers and escapes... or error out */ case 'F':
case 'f':
if (peek(0) == '.') {
shiftChars(1);
lexerState->nextToken = T_PERIOD;
return T_TOKEN_F;
}
goto normal_identifier;
case 'B':
case 'b':
if (peek(0) == '.') {
shiftChars(1);
lexerState->nextToken = T_PERIOD;
return T_TOKEN_B;
}
goto normal_identifier;
case 'C':
case 'c':
if (peek(0) == '.') {
shiftChars(1);
lexerState->nextToken = T_PERIOD;
return T_TOKEN_C;
}
goto normal_identifier;
case 'D':
case 'd':
if (peek(0) == '.') {
shiftChars(1);
lexerState->nextToken = T_PERIOD;
return T_TOKEN_D;
}
goto normal_identifier;
case 'E':
case 'e':
if (peek(0) == '.') {
shiftChars(1);
lexerState->nextToken = T_PERIOD;
return T_TOKEN_E;
}
goto normal_identifier;
case 'H':
case 'h':
if (peek(0) == '.') {
shiftChars(1);
lexerState->nextToken = T_PERIOD;
return T_TOKEN_H;
}
goto normal_identifier;
case 'L':
case 'l':
if (peek(0) == '.') {
shiftChars(1);
lexerState->nextToken = T_PERIOD;
return T_TOKEN_L;
}
/* fallthrough */
/* Handle identifiers... or report garbage characters */
normal_identifier:
default: default:
if (startsIdentifier(c)) { if (startsIdentifier(c)) {
int tokenType = readIdentifier(c); int tokenType = readIdentifier(c);
/* An ELIF after a taken IF needs to not evaluate its condition */
if (tokenType == T_POP_ELIF && lexerState->lastToken == T_NEWLINE
&& lexer_GetIFDepth() > 0 && lexer_RanIFBlock()
&& !lexer_ReachedELSEBlock())
return yylex_SKIP_TO_ENDC();
/* If a keyword, don't try to expand */ /* If a keyword, don't try to expand */
if (tokenType != T_ID && tokenType != T_LOCAL_ID) if (tokenType != T_ID && tokenType != T_LOCAL_ID)
return tokenType; return tokenType;
@@ -2258,7 +2339,7 @@ static int skipIfBlock(bool toEndc)
{ {
dbgPrint("Skipping IF block (toEndc = %s)\n", toEndc ? "true" : "false"); dbgPrint("Skipping IF block (toEndc = %s)\n", toEndc ? "true" : "false");
lexer_SetMode(LEXER_NORMAL); lexer_SetMode(LEXER_NORMAL);
int startingDepth = lexer_GetIFDepth(); uint32_t startingDepth = lexer_GetIFDepth();
int token; int token;
bool atLineStart = lexerState->atLineStart; bool atLineStart = lexerState->atLineStart;

View File

@@ -170,12 +170,12 @@ void macro_ShiftCurrentArgs(int32_t count)
{ {
if (!macroArgs) { if (!macroArgs) {
error("Cannot shift macro arguments outside of a macro\n"); error("Cannot shift macro arguments outside of a macro\n");
} else if (count > 0 && (count > macroArgs->nbArgs } else if (count > 0 && ((uint32_t)count > macroArgs->nbArgs
|| macroArgs->shift > macroArgs->nbArgs - count)) { || macroArgs->shift > macroArgs->nbArgs - count)) {
warning(WARNING_MACRO_SHIFT, warning(WARNING_MACRO_SHIFT,
"Cannot shift macro arguments past their end\n"); "Cannot shift macro arguments past their end\n");
macroArgs->shift = macroArgs->nbArgs; macroArgs->shift = macroArgs->nbArgs;
} else if (count < 0 && macroArgs->shift < -count) { } else if (count < 0 && macroArgs->shift < (uint32_t)-count) {
warning(WARNING_MACRO_SHIFT, warning(WARNING_MACRO_SHIFT,
"Cannot shift macro arguments past their beginning\n"); "Cannot shift macro arguments past their beginning\n");
macroArgs->shift = 0; macroArgs->shift = 0;

View File

@@ -353,7 +353,8 @@ int main(int argc, char *argv[])
sect_CheckUnionClosed(); sect_CheckUnionClosed();
if (nbErrors != 0) if (nbErrors != 0)
errx(1, "Assembly aborted (%u errors)!", nbErrors); errx(1, "Assembly aborted (%u error%s)!", nbErrors,
nbErrors == 1 ? "" : "s");
// If parse aborted due to missing an include, and `-MG` was given, exit normally // If parse aborted due to missing an include, and `-MG` was given, exit normally
if (oFailedOnMissingInclude) if (oFailedOnMissingInclude)

View File

@@ -136,9 +136,9 @@ static uint32_t getNbFileStackNodes(void)
void out_RegisterNode(struct FileStackNode *node) void out_RegisterNode(struct FileStackNode *node)
{ {
/* If node is not already registered, register it (and parents), and give it a unique ID */ /* If node is not already registered, register it (and parents), and give it a unique ID */
while (node->ID == -1) { while (node->ID == (uint32_t)-1) {
node->ID = getNbFileStackNodes(); node->ID = getNbFileStackNodes();
if (node->ID == -1) if (node->ID == (uint32_t)-1)
fatalerror("Reached too many file stack nodes; try splitting the file up\n"); fatalerror("Reached too many file stack nodes; try splitting the file up\n");
node->next = fileStackNodes; node->next = fileStackNodes;
fileStackNodes = node; fileStackNodes = node;
@@ -266,7 +266,7 @@ static void registerSymbol(struct Symbol *sym)
*objectSymbolsTail = sym; *objectSymbolsTail = sym;
objectSymbolsTail = &sym->next; objectSymbolsTail = &sym->next;
out_RegisterNode(sym->src); out_RegisterNode(sym->src);
if (nbSymbols == -1) if (nbSymbols == (uint32_t)-1)
fatalerror("Registered too many symbols (%" PRIu32 fatalerror("Registered too many symbols (%" PRIu32
"); try splitting up your files\n", (uint32_t)-1); "); try splitting up your files\n", (uint32_t)-1);
sym->ID = nbSymbols++; sym->ID = nbSymbols++;
@@ -278,7 +278,7 @@ static void registerSymbol(struct Symbol *sym)
*/ */
static uint32_t getSymbolID(struct Symbol *sym) static uint32_t getSymbolID(struct Symbol *sym)
{ {
if (sym->ID == -1 && !sym_IsPC(sym)) if (sym->ID == (uint32_t)-1 && !sym_IsPC(sym))
registerSymbol(sym); registerSymbol(sym);
return sym->ID; return sym->ID;
} }
@@ -474,7 +474,7 @@ static void registerUnregisteredSymbol(struct Symbol *symbol, void *arg)
(void)arg; // sym_ForEach requires a void* parameter, but we are not using it. (void)arg; // sym_ForEach requires a void* parameter, but we are not using it.
// Check for symbol->src, to skip any built-in symbol from rgbasm // Check for symbol->src, to skip any built-in symbol from rgbasm
if (symbol->src && symbol->ID == -1) { if (symbol->src && symbol->ID == (uint32_t)-1) {
registerSymbol(symbol); registerSymbol(symbol);
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,11 @@
.\" .\"
.\" This file is part of RGBDS. .\" This file is part of RGBDS.
.\" .\"
.\" Copyright (c) 2010-2019, Anthony J. Bentley and RGBDS contributors. .\" Copyright (c) 2010-2021, Anthony J. Bentley and RGBDS contributors.
.\" .\"
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd July 8, 2019 .Dd March 28, 2021
.Dt RGBASM 1 .Dt RGBASM 1
.Os .Os
.Sh NAME .Sh NAME
@@ -83,7 +83,7 @@ Add an include path.
Disable the optimization that turns loads of the form Disable the optimization that turns loads of the form
.Ic LD [$FF00+n8],A .Ic LD [$FF00+n8],A
into the opcode into the opcode
.Ic LDH [$FF00+n8],A .Ic LD [H $FF00+n8],A
in order to have full control of the result in the final ROM. in order to have full control of the result in the final ROM.
.It Fl M Ar depend_file , Fl Fl dependfile Ar depend_file .It Fl M Ar depend_file , Fl Fl dependfile Ar depend_file
Print Print
@@ -244,7 +244,7 @@ Use a division by 2^N instead.
Warn when a shift's operand is negative or greater than 32. Warn when a shift's operand is negative or greater than 32.
.It Fl Wno-truncation .It Fl Wno-truncation
Warn when an implicit truncation (for example, Warn when an implicit truncation (for example,
.Ic db ) .Ic LD [B @] )
loses some bits. loses some bits.
.It Fl Wno-user .It Fl Wno-user
Warn when the Warn when the

View File

@@ -1,11 +1,11 @@
.\" .\"
.\" This file is part of RGBDS. .\" This file is part of RGBDS.
.\" .\"
.\" Copyright (c) 2017-2018, Antonio Nino Diaz and RGBDS contributors. .\" Copyright (c) 2017-2021, Antonio Nino Diaz and RGBDS contributors.
.\" .\"
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd December 5, 2019 .Dd March 28, 2021
.Dt RGBASM 5 .Dt RGBASM 5
.Os .Os
.Sh NAME .Sh NAME
@@ -25,11 +25,11 @@ Generally,
.Dq the linker .Dq the linker
will refer to will refer to
.Xr rgblink 1 , .Xr rgblink 1 ,
but any program that processes RGB object files (described in but any program that processes RGBDS object files (described in
.Xr rgbds 5 ) .Xr rgbds 5 )
can be used in its place. can be used in its place.
.Sh SYNTAX .Sh SYNTAX
The syntax is linebased, just as in any other assembler, meaning that you do one instruction or directive per line: The syntax is line-based, just as in any other assembler, meaning that you do one instruction or directive per line:
.Pp .Pp
.Dl Oo Ar label Oc Oo Ar instruction Oc Oo Ar ;\ comment Oc .Dl Oo Ar label Oc Oo Ar instruction Oc Oo Ar ;\ comment Oc
.Pp .Pp
@@ -38,7 +38,7 @@ Example:
John: ld a,87 ;Weee John: ld a,87 ;Weee
.Ed .Ed
.Pp .Pp
All reserved keywords (directives, mnemonics, registers, etc.) are caseinsensitive; All reserved keywords (directives, mnemonics, registers, etc.) are case-insensitive;
all identifiers (symbol names) are case-sensitive. all identifiers (symbol names) are case-sensitive.
.Pp .Pp
Comments are used to give humans information about the code, such as explanations. Comments are used to give humans information about the code, such as explanations.
@@ -63,7 +63,7 @@ X = /* the value of x
Sometimes lines can be too long and it may be necessary to split them. Sometimes lines can be too long and it may be necessary to split them.
To do so, put a backslash at the end of the line: To do so, put a backslash at the end of the line:
.Bd -literal -offset indent .Bd -literal -offset indent
DB 1, 2, 3,\ \[rs] LD [B @:...], 1, 2, 3,\ \[rs]
4, 5, 6,\ \[rs]\ ;\ Put it before any comments 4, 5, 6,\ \[rs]\ ;\ Put it before any comments
7, 8, 9 7, 8, 9
.Ed .Ed
@@ -73,7 +73,7 @@ To split strings it is needed to use
.Fn STRCAT .Fn STRCAT
like this: like this:
.Bd -literal -offset indent .Bd -literal -offset indent
db STRCAT("Hello ",\ \[rs] LD [B @:], STRCAT("Hello ",\ \[rs]
"world!") "world!")
.Ed .Ed
.Sh EXPRESSIONS .Sh EXPRESSIONS
@@ -177,8 +177,8 @@ and
.Pp .Pp
.Ic \&! .Ic \&!
returns 1 if the operand was 0, and 0 otherwise. returns 1 if the operand was 0, and 0 otherwise.
.Ss Fixedpoint Expressions .Ss Fixed-point Expressions
Fixed-point numbers are basically normal (32-bit) integers, which count 65536th's instead of entire units, offering better precision than integers but limiting the range of values. Fixed-point numbers are basically normal (32-bit) integers, which count 65536ths instead of entire units, offering better precision than integers but limiting the range of values.
The upper 16 bits are used for the integer part and the lower 16 bits are used for the fraction (65536ths). The upper 16 bits are used for the integer part and the lower 16 bits are used for the fraction (65536ths).
Since they are still akin to integers, you can use them in normal integer expressions, and some integer operators like Since they are still akin to integers, you can use them in normal integer expressions, and some integer operators like
.Sq + .Sq +
@@ -226,7 +226,7 @@ For example:
; (shifted and scaled from the range [-1.0, 1.0]) ; (shifted and scaled from the range [-1.0, 1.0])
ANGLE = 0.0 ANGLE = 0.0
REPT 256 REPT 256
db (MUL(64.0, SIN(ANGLE)) + 64.0) >> 16 ld [b @], (MUL(64.0, SIN(ANGLE)) + 64.0) >> 16
ANGLE = ANGLE + 256.0 ; 256.0 = 65536 degrees / 256 entries ANGLE = ANGLE + 256.0 ; 256.0 = 65536 degrees / 256 entries
ENDR ENDR
.Ed .Ed
@@ -266,21 +266,37 @@ A funky feature is
.Ql {symbol} .Ql {symbol}
within a string, called within a string, called
.Dq symbol interpolation . .Dq symbol interpolation .
This will paste This will paste the contents of
.Ar symbol Ap s .Ql symbol
contents as a string. as if they were part of the source file.
If it's a string symbol, the string is simply inserted. If it's a string symbol, its characters are simply inserted.
If it's a numeric symbol, its value is converted to hexadecimal notation with a dollar sign If it's a numerical symbol, its value is converted to hexadecimal notation with a dollar sign
.Sq $ .Sq $
prepended. prepended.
.Bd -literal -offset indent .Pp
TOPIC equs "life, the universe, and \[rs]"everything\[rs]"" Symbols can be
ANSWER = 42 .Em interpolated
;\ Prints "The answer to life, the universe, and "everything" is $2A" even in the contexts that disable
PRINTLN "The answer to {TOPIC} is {ANSWER}" .Em expansion
.Ed of string equates:
.Ql DEF({name}) ,
.Ql DEF {name} EQU/SET/EQUS/etc ... ,
.Ql PURGE {name} ,
and
.Ql MACRO {name}
will all interpolate the contents of
.Ql {name} .
.Pp .Pp
Symbol interpolations can be nested, too! Symbol interpolations can be nested, too!
.Bd -literal -offset indent
DEF topic EQUS "life, the universe, and \[rs]"everything\[rs]""
DEF meaning EQUS "answer"
;\ Defines answer = 42
DEF {meaning} = 42
;\ Prints "The answer to life, the universe, and "everything" is 42"
PRINTLN "The {meaning} to {topic} is {d:{meaning}}"
PURGE topic, meaning, {meaning}
.Ed
.Pp .Pp
It's possible to change the way symbols are converted by specifying a print format like so: It's possible to change the way symbols are converted by specifying a print format like so:
.Ql {fmt:symbol} . .Ql {fmt:symbol} .
@@ -355,14 +371,14 @@ HINT: The
construct can also be used outside strings. construct can also be used outside strings.
The symbol's value is again inserted directly. The symbol's value is again inserted directly.
.Bd -literal -offset indent .Bd -literal -offset indent
NAME equs "ITEM" def NAME equs "ITEM"
FMT equs "d" def FMT equs "d"
ZERO_NUM equ 0 def ZERO_NUM equ 0
ZERO_STR equs "0" def ZERO_STR equs "0"
;\ Defines INDEX as 100 ;\ Defines INDEX as 100
INDEX = 1{ZERO_STR}{{FMT}:ZERO_NUM} INDEX = 1{ZERO_STR}{{FMT}:ZERO_NUM}
;\ Defines ITEM_100 as "\[rs]"hundredth\[rs]"" ;\ Defines ITEM_100 as "\[rs]"hundredth\[rs]""
{NAME}_{d:INDEX} equs "\[rs]"hundredth\[rs]"" def {NAME}_{d:INDEX} equs "\[rs]"hundredth\[rs]""
;\ Prints "ITEM_100 is hundredth" ;\ Prints "ITEM_100 is hundredth"
PRINTLN STRCAT("{NAME}_{d:INDEX} is ", {NAME}_{d:INDEX}) PRINTLN STRCAT("{NAME}_{d:INDEX} is ", {NAME}_{d:INDEX})
;\ Purges ITEM_100 ;\ Purges ITEM_100
@@ -401,9 +417,9 @@ CHARMAP "&iacute", 20
CHARMAP "A", 128 CHARMAP "A", 128
.Ed .Ed
This would result in This would result in
.Ql db \(dqAmen<LF>\(dq .Ql ld [b @:...], \(dqAmen<LF>\(dq
being equivalent to being equivalent to
.Ql db 128, 109, 101, 110, 10 . .Ql ld [b @:...], 128, 109, 101, 110, 10 .
.Pp .Pp
Any characters in a string without defined mappings will be copied directly, using the source file's encoding of characters to bytes. Any characters in a string without defined mappings will be copied directly, using the source file's encoding of characters to bytes.
.Pp .Pp
@@ -446,7 +462,7 @@ is able to compute it.
.It Fn DEF symbol Ta Returns TRUE (1) if .It Fn DEF symbol Ta Returns TRUE (1) if
.Ar symbol .Ar symbol
has been defined, FALSE (0) otherwise. has been defined, FALSE (0) otherwise.
String symbols are not expanded within the parentheses. String equates are not expanded within the parentheses.
.It Fn HIGH arg Ta Returns the top 8 bits of the operand if Ar arg No is a label or constant, or the top 8-bit register if it is a 16-bit register. .It Fn HIGH arg Ta Returns the top 8 bits of the operand if Ar arg No is a label or constant, or the top 8-bit register if it is a 16-bit register.
.It Fn LOW arg Ta Returns the bottom 8 bits of the operand if Ar arg No is a label or constant, or the bottom 8-bit register if it is a 16-bit register Pq Cm AF No isn't a valid register for this function . .It Fn LOW arg Ta Returns the bottom 8 bits of the operand if Ar arg No is a label or constant, or the bottom 8-bit register if it is a 16-bit register Pq Cm AF No isn't a valid register for this function .
.It Fn ISCONST arg Ta Returns 1 if Ar arg Ap s value is known by RGBASM (e.g. if it can be an argument to .It Fn ISCONST arg Ta Returns 1 if Ar arg Ap s value is known by RGBASM (e.g. if it can be an argument to
@@ -558,15 +574,15 @@ While
will automatically optimize will automatically optimize
.Ic ld .Ic ld
instructions to the smaller and faster instructions to the smaller and faster
.Ic ldh .Ic ld\ h
(see (see
.Xr gbz80 7 ) .Xr gbz80 7 )
whenever possible, it is generally unable to do so when a label is involved. whenever possible, it is generally unable to do so when a label is involved.
Using the Using the
.Ic ldh .Ic ld\ h
instruction directly is recommended. instruction directly is recommended.
This forces the assembler to emit a This forces the assembler to emit a
.Ic ldh .Ic ld\ h
instruction and the linker to check if the value is in the correct range. instruction and the linker to check if the value is in the correct range.
.El .El
.Pp .Pp
@@ -590,7 +606,7 @@ depending on
.It Ic ALIGN Ns Bq Ar align , offset .It Ic ALIGN Ns Bq Ar align , offset
Place the section at an address whose Place the section at an address whose
.Ar align .Ar align
leastsignificant bits are equal to least-significant bits are equal to
.Ar offset . .Ar offset .
(Note that (Note that
.Ic ALIGN Ns Bq Ar align .Ic ALIGN Ns Bq Ar align
@@ -683,11 +699,11 @@ CopyCode:
ld c, RAMLocation.end - RAMLocation ld c, RAMLocation.end - RAMLocation
\&.loop \&.loop
ld a, [de] ld a, [de]
inc de ld de+
ld [hli], a ld [hli], a
dec c ld c-
jr nz, .loop ld nz pc, b .loop
ret ld pc,[sp++]
RAMCode: RAMCode:
LOAD "RAM code", WRAM0 LOAD "RAM code", WRAM0
@@ -697,13 +713,13 @@ RAMLocation:
\&.copy \&.copy
ld a, [hli] ld a, [hli]
ld [de], a ld [de], a
inc de ld de+
and a ld a,a&a
jr nz, .copy ld nz pc, b .copy
ret ld pc,[sp++]
\&.string \&.string
db "Hello World!", 0 ld [b @:...], "Hello World!", 0
\&.end \&.end
ENDL ENDL
.Ed .Ed
@@ -853,16 +869,7 @@ Periods
.Sq \&. .Sq \&.
are allowed exclusively in labels, as described below. are allowed exclusively in labels, as described below.
A symbol cannot have the same name as a reserved keyword. A symbol cannot have the same name as a reserved keyword.
.Pp .Ss Label declaration
Constants and string equates
.Em must not
have any whitespace before their name when they are defined;
otherwise
.Nm
will treat them as a macro invocation.
Label and macro definitions may have whitespace before them, since a leading period or a following colon distinguishes them from invoking a macro.
.Bl -tag -width indent
.It Sy Label declaration
One of the assembler's main tasks is to keep track of addresses for you, so you can work with meaningful names instead of "magic" numbers. One of the assembler's main tasks is to keep track of addresses for you, so you can work with meaningful names instead of "magic" numbers.
.Pp .Pp
This can be done in a number of ways: This can be done in a number of ways:
@@ -922,14 +929,14 @@ references the one before the expression;
and so on. and so on.
.Bd -literal -offset indent .Bd -literal -offset indent
ld hl, :++ ld hl, :++
: ld a, [hli] ; referenced by "jr nz" : ld a, [hli] ; referenced by "ld nz pc"
ldh [c], a ld [h c], a
dec c ld c-
jr nz, :- ld nz pc,b :-
ret ld pc,[sp++]
: ; referenced by "ld hl" : ; referenced by "ld hl"
dw $7FFF, $1061, $03E0, $58A5 ld [w @:], $7FFF, $1061, $03E0, $58A5
.Ed .Ed
.Pp .Pp
A label's location (and thus value) is usually not determined until the linking stage, so labels usually cannot be used as constants. A label's location (and thus value) is usually not determined until the linking stage, so labels usually cannot be used as constants.
@@ -937,56 +944,57 @@ However, if the section in which the label is declared has a fixed base address,
.Pp .Pp
.Nm .Nm
is able to compute the subtraction of two labels either if both are constant as described above, or if both belong to the same section. is able to compute the subtraction of two labels either if both are constant as described above, or if both belong to the same section.
.It Ic EQU .Ss Immutable constants
.Ic EQU .Ic EQU
allows defining constant symbols. is used to define numerical constant symbols.
Unlike Unlike
.Ic SET .Ic SET
below, constants defined this way cannot be redefined. below, constants defined this way cannot be redefined.
They can, for example, be used for things such as bit definitions of hardware registers. They can, for example, be used for things such as bit definitions of hardware registers.
.Bd -literal -offset indent .Bd -literal -offset indent
SCREEN_WIDTH equ 160 ;\ In pixels def SCREEN_WIDTH equ 160 ;\ In pixels
SCREEN_HEIGHT equ 144 def SCREEN_HEIGHT equ 144
.Ed .Ed
.Pp .Pp
Note that colons Note that colons
.Ql \&: .Ql \&:
following the name are not allowed. following the name are not allowed.
.It Ic SET .Ss Mutable constants
.Ic SET , .Ic SET ,
or its synonym or its synonym
.Ic = , .Ic = ,
defines constant symbols like is used to define numerical symbols like
.Ic EQU , .Ic EQU ,
but those constants can be redefined. but these symbols can be redefined.
This is useful for variables in macros, for counters, etc. This is useful for variables in macros, for counters, etc.
.Bd -literal -offset indent .Bd -literal -offset indent
ARRAY_SIZE EQU 4 DEF ARRAY_SIZE EQU 4
COUNT SET 2 DEF COUNT SET 2
COUNT SET ARRAY_SIZE+COUNT DEF COUNT SET 3
;\ COUNT now has the value 6 REDEF COUNT SET ARRAY_SIZE+COUNT
COUNT = COUNT + 1 COUNT = COUNT*2
;\ COUNT now has the value 14
.Ed .Ed
.Pp .Pp
Note that colons Note that colons
.Ql \&: .Ql \&:
following the name are not allowed. following the name are not allowed.
.It Ic RSSET , RSRESET , RB , RW .Ss Offset constants
The RS group of commands is a handy way of defining structures: The RS group of commands is a handy way of defining structure offsets:
.Bd -literal -offset indent .Bd -literal -offset indent
RSRESET RSRESET
str_pStuff RW 1 DEF str_pStuff RW 1
str_tData RB 256 DEF str_tData RB 256
str_bCount RB 1 DEF str_bCount RB 1
str_SIZEOF RB 0 DEF str_SIZEOF RB 0
.Ed .Ed
.Pp .Pp
The example defines four constants as if by: The example defines four constants as if by:
.Bd -literal -offset indent .Bd -literal -offset indent
str_pStuff EQU 0 DEF str_pStuff EQU 0
str_tData EQU 2 DEF str_tData EQU 2
str_bCount EQU 258 DEF str_bCount EQU 258
str_SIZEOF EQU 259 DEF str_SIZEOF EQU 259
.Ed .Ed
.Pp .Pp
There are five commands in the RS group of commands: There are five commands in the RS group of commands:
@@ -1008,29 +1016,38 @@ is omitted, it's assumed to be 1.
Note that colons Note that colons
.Ql \&: .Ql \&:
following the name are not allowed. following the name are not allowed.
.It Ic EQUS .Ss String equates
.Ic EQUS .Ic EQUS
is used to define string symbols. is used to define string equate symbols.
Wherever the assembler meets a string symbol its name is replaced with its value. Wherever the assembler reads a string equate, it gets
If you are familiar with C you can think of it as similar to .Em expanded :
the symbol's name is replaced with its contents.
If you are familiar with C, you can think of it as similar to
.Fd #define . .Fd #define .
This expansion is disabled in a few contexts:
.Ql DEF(name) ,
.Ql DEF name EQU/SET/EQUS/etc ... ,
.Ql PURGE name ,
and
.Ql MACRO name
will not expand string equates in their names.
.Bd -literal -offset indent .Bd -literal -offset indent
COUNTREG EQUS "[hl+]" DEF COUNTREG EQUS "[hl+]"
ld a,COUNTREG ld a,COUNTREG
PLAYER_NAME EQUS "\[rs]"John\[rs]"" DEF PLAYER_NAME EQUS "\[rs]"John\[rs]""
db PLAYER_NAME ld [b @:], PLAYER_NAME
.Ed .Ed
.Pp .Pp
This will be interpreted as: This will be interpreted as:
.Bd -literal -offset indent .Bd -literal -offset indent
ld a,[hl+] ld a,[hl+]
db "John" ld [b @:], "John"
.Ed .Ed
.Pp .Pp
String symbols can also be used to define small one-line macros: String equates can also be used to define small one-line macros:
.Bd -literal -offset indent .Bd -literal -offset indent
pusha EQUS "push af\[rs]npush bc\[rs]npush de\[rs]npush hl\[rs]n" DEF ld_de_hl EQUS "ld d,h\[rs]n ld e,l\[rs]n"
.Ed .Ed
.Pp .Pp
Note that colons Note that colons
@@ -1047,7 +1064,7 @@ However, the
keyword will define or redefine a string symbol. keyword will define or redefine a string symbol.
For example: For example:
.Bd -literal -offset indent .Bd -literal -offset indent
s EQUS "Hello, " DEF s EQUS "Hello, "
REDEF s EQUS "{s}world!" REDEF s EQUS "{s}world!"
; prints "Hello, world!" ; prints "Hello, world!"
PRINTT "{s}\n" PRINTT "{s}\n"
@@ -1070,7 +1087,47 @@ command-line option in
Also, a macro can contain an Also, a macro can contain an
.Ic EQUS .Ic EQUS
which calls the same macro, which causes the same problem. which calls the same macro, which causes the same problem.
.It Ic MACRO .Pp
The examples above for
.Ql EQU ,
.Ql SET
or
.Ql = ,
.Ql RB ,
.Ql RW ,
.Ql RL ,
and
.Ql EQUS
all start with
.Ql DEF .
(A
.Ql SET
or
.Ql =
definition may start with
.Ql REDEF
instead, since they are redefinable.)
You may use the older syntax without
.Ql DEF ,
but then the name being defined
.Em must not
have any whitespace before it;
otherwise
.Nm
will treat it as a macro invocation.
Furthermore, without the
.Ql DEF
keyword,
string equates may be expanded for the name.
This can lead to surprising results:
.Bd -literal -offset indent
X EQUS "Y"
; this defines Y, not X!
X EQU 42
; prints "Y $2A"
PRINTLN "{X} {Y}"
.Ed
.Ss Macros
One of the best features of an assembler is the ability to write macros for it. One of the best features of an assembler is the ability to write macros for it.
Macros can be called with arguments, and can react depending on input using Macros can be called with arguments, and can react depending on input using
.Ic IF .Ic IF
@@ -1078,13 +1135,14 @@ constructs.
.Bd -literal -offset indent .Bd -literal -offset indent
MACRO MyMacro MACRO MyMacro
ld a, 80 ld a, 80
call MyFunc ld[--sp],pc, MyFunc
ENDM ENDM
.Ed .Ed
.Pp .Pp
The example above defines The example above defines
.Ql MyMacro .Ql MyMacro
as a new macro. as a new macro.
String equates are not expanded within the name of the macro.
You may use the older syntax You may use the older syntax
.Ql MyMacro: MACRO .Ql MyMacro: MACRO
instead of instead of
@@ -1092,6 +1150,8 @@ instead of
with a single colon with a single colon
.Ql \&: .Ql \&:
following the macro's name. following the macro's name.
With the older syntax, string equates may be expanded for the name.
.Pp
Macros can't be exported or imported. Macros can't be exported or imported.
.Pp .Pp
Plainly nesting macro definitions is not allowed, but this can be worked around using Plainly nesting macro definitions is not allowed, but this can be worked around using
@@ -1108,12 +1168,11 @@ ENDM
But this will: But this will:
.Bd -literal -offset indent .Bd -literal -offset indent
MACRO outer MACRO outer
definition EQUS "MACRO inner\[rs]nPRINTLN \[rs]"Hello!\[rs]"\[rs]nENDM" DEF definition EQUS "MACRO inner\[rs]nPRINTLN \[rs]"Hello!\[rs]"\[rs]nENDM"
definition definition
PURGE definition PURGE definition
ENDM ENDM
.Ed .Ed
.El
.Pp .Pp
Macro arguments support all the escape sequences of strings, as well as Macro arguments support all the escape sequences of strings, as well as
.Ql \[rs], .Ql \[rs],
@@ -1149,9 +1208,9 @@ ExportedLabelB2:
.Ql c.asm : .Ql c.asm :
.Bd -literal -compact .Bd -literal -compact
SECTION "C", ROM0[0] SECTION "C", ROM0[0]
dw LabelA ld [w @], LabelA
dw ExportedLabelB1 ld [w @], ExportedLabelB1
dw ExportedLabelB2 ld [w @], ExportedLabelB2
.Ed .Ed
.Pp .Pp
Then Then
@@ -1184,15 +1243,12 @@ I can't stress this enough,
DON'T purge a symbol that you use in expressions the linker needs to calculate. DON'T purge a symbol that you use in expressions the linker needs to calculate.
When not sure, it's probably not safe to purge anything other than string symbols, macros, and constants. When not sure, it's probably not safe to purge anything other than string symbols, macros, and constants.
.Bd -literal -offset indent .Bd -literal -offset indent
Kamikaze EQUS "I don't want to live anymore" DEF Kamikaze EQUS "I don't want to live anymore"
AOLer EQUS "Me too" DEF AOLer EQUS "Me too"
PURGE Kamikaze, AOLer PURGE Kamikaze, AOLer
.Ed .Ed
.Pp .Pp
Note that, as an exception, string symbols in the argument list of a String equates are not expanded within the symbol names.
.Ic PURGE
command
.Em will not be expanded .
.Ss Predeclared Symbols .Ss Predeclared Symbols
The following symbols are defined by the assembler: The following symbols are defined by the assembler:
.Bl -column -offset indent "EQUS" "__ISO_8601_LOCAL__" .Bl -column -offset indent "EQUS" "__ISO_8601_LOCAL__"
@@ -1225,18 +1281,20 @@ Refer to the spec at
.Lk https://reproducible-builds.org/docs/source-date-epoch/ . .Lk https://reproducible-builds.org/docs/source-date-epoch/ .
.Sh DEFINING DATA .Sh DEFINING DATA
.Ss Declaring variables in a RAM section .Ss Declaring variables in a RAM section
.Ic DS .Ic LD [B @:<len>]
allocates a number of empty bytes. allocates a number of empty bytes.
This is the preferred method of allocating space in a RAM section. This is the preferred method of allocating space in a RAM section.
You can also use You can also use
.Ic DB , DW .Ic LD [B @] , LD [W @]
and and
.Ic DL .Ic LD [L @]
without any arguments instead (see with
.Ql \&?
instead (see
.Sx Defining constant data .Sx Defining constant data
below). below).
.Bd -literal -offset indent .Bd -literal -offset indent
DS 42 ;\ Allocates 42 bytes LD [B @:42], ? ;\ Allocates 42 bytes
.Ed .Ed
.Pp .Pp
Empty space in RAM sections will not be initialized. Empty space in RAM sections will not be initialized.
@@ -1245,17 +1303,24 @@ In ROM sections, it will be filled with the value passed to the
command-line option, except when using overlays with command-line option, except when using overlays with
.Fl O . .Fl O .
.Ss Defining constant data .Ss Defining constant data
.Ic DB .Ic LD [B @]
defines a list of bytes that will be stored in the final image. defines a list of bytes that will be stored in the final image.
Ideal for tables and text. Ideal for tables and text.
.Bd -literal -offset indent .Bd -literal -offset indent
DB 1,2,3,4,"This is a string" LD [B @:...], 1,2,3,4,"This is a string"
.Ed .Ed
.Pp .Pp
Python slice syntax is used: for a single byte, close the brackets immediately after
.Ql @ ;
if more than one entry follows, a colon must be added after the
.Ql @ ,
optionally followed by an ellipsis
.Ql ... .
.Pp
Alternatively, you can use Alternatively, you can use
.Ic DW .Ic LD [W @]
to store a list of words (16-bit) or to store a list of words (16-bit) or
.Ic DL .Ic LD [L @]
to store a list of double-words/longs (32-bit). to store a list of double-words/longs (32-bit).
.Pp .Pp
Strings are handled a little specially: they first undergo charmap conversion (see Strings are handled a little specially: they first undergo charmap conversion (see
@@ -1263,36 +1328,42 @@ Strings are handled a little specially: they first undergo charmap conversion (s
then each resulting character is output individually. then each resulting character is output individually.
For example, under the default charmap, the following two lines are identical: For example, under the default charmap, the following two lines are identical:
.Bd -literal -offset indent .Bd -literal -offset indent
DW "Hello!" LD [W @:], "Hello!"
DW "H", "e", "l", "l", "o", "!" LD [W @:], "H", "e", "l", "l", "o", "!"
.Ed .Ed
.Pp .Pp
Note that strings require a colon after the
.Ql @
in the directive.
.Pp
If you do not want this special handling, enclose the string in parentheses. If you do not want this special handling, enclose the string in parentheses.
.Pp .Pp
.Ic DS .Ic LD [B @:<len>]
can also be used to fill a region of memory with some repeated values. can also be used to fill a region of memory with some repeated values.
For example: For example:
.Bd -literal -offset indent .Bd -literal -offset indent
; outputs 3 bytes: $AA, $AA, $AA ; outputs 3 bytes: $AA, $AA, $AA
DS 3, $AA LD [B @:3], $AA
; outputs 7 bytes: $BB, $CC, $BB, $CC, $BB, $CC, $BB ; outputs 7 bytes: $BB, $CC, $BB, $CC, $BB, $CC, $BB
DS 7, $BB, $CC LD [B @:7], $BB, $CC
.Ed .Ed
.Pp .Pp
You can also use You can also use
.Ic DB , DW .Ic LD [B @] , LD [W @]
and and
.Ic DL .Ic LD [L @]
without arguments. with
.Ql \&?
as its sole argument (in which case neither the colon nor an ellipsis may be present).
This works exactly like This works exactly like
.Ic DS 1 , DS 2 .Ic LD [B @:1] , LD [B @:2]
and and
.Ic DS 4 .Ic LD [B @:4]
respectively. respectively.
Consequently, no-argument Consequently, these forms of
.Ic DB , DW .Ic LD [B @] , LD [W @]
and and
.Ic DL .Ic LD [L @]
can be used in a can be used in a
.Ic WRAM0 .Ic WRAM0
/ /
@@ -1307,23 +1378,25 @@ section.
.Ss Including binary files .Ss Including binary files
You probably have some graphics, level data, etc. you'd like to include. You probably have some graphics, level data, etc. you'd like to include.
Use Use
.Ic INCBIN .Ic LD [B @:...] =
to include a raw binary file as it is. to include a raw binary file as it is.
.Pq The ellipsis are optional.
If the file isn't found in the current directory, the include-path list passed to If the file isn't found in the current directory, the include-path list passed to
.Xr rgbasm 1 .Xr rgbasm 1
(see the (see the
.Fl i .Fl i
option) on the command line will be searched. option) on the command line will be searched.
.Bd -literal -offset indent .Bd -literal -offset indent
INCBIN "titlepic.bin" LD [B @:] = "titlepic.bin"
INCBIN "sprites/hero.bin" LD [B @:...] = "sprites/hero.bin"
.Ed .Ed
.Pp .Pp
You can also include only part of a file with You can also include only part of a file with
.Ic INCBIN . .Ic LD [B @:] .
The example below includes 256 bytes from data.bin, starting from byte 78. The examples below include 256 bytes from data.bin, starting from byte 78.
.Bd -literal -offset indent .Bd -literal -offset indent
INCBIN "data.bin",78,256 LD [B @:] = "data.bin"[78:256]
LD [B @:256] = "data.bin"[78:]
.Ed .Ed
.Pp .Pp
The length argument is optional. The length argument is optional.
@@ -1343,19 +1416,19 @@ separates each block of allocations, and you may use it as many times within a u
; Let's say PC = $C0DE here ; Let's say PC = $C0DE here
UNION UNION
; Here, PC = $C0DE ; Here, PC = $C0DE
Name: ds 8 Name: ld [b @:8], ?
; PC = $C0E6 ; PC = $C0E6
Nickname: ds 8 Nickname: ld [b @:8], ?
; PC = $C0EE ; PC = $C0EE
NEXTU NEXTU
; PC is back to $C0DE ; PC is back to $C0DE
Health: dw Health: ld [w @], ?
; PC = $C0E0 ; PC = $C0E0
Something: ds 6 Something: ld [b @:6], ?
; And so on ; And so on
Lives: db Lives: ld [b @], ?
NEXTU NEXTU
VideoBuffer: ds 19 VideoBuffer: ld [b @:19], ?
ENDU ENDU
.Ed .Ed
.Pp .Pp
@@ -1375,17 +1448,17 @@ The size of this union is 19 bytes, as this is the size of the largest block (th
Nesting unions is possible, with each inner union's size being considered as described above. Nesting unions is possible, with each inner union's size being considered as described above.
.Pp .Pp
Unions may be used in any section, but inside them may only be Unions may be used in any section, but inside them may only be
.Ic DS - .Ic LD\ [B\ @],\ ? Ns - Ns
like commands (see like commands (see
.Sx Declaring variables in a RAM section ) . .Sx Declaring variables in a RAM section ) .
.Sh THE MACRO LANGUAGE .Sh THE MACRO LANGUAGE
.Ss Invoking macros .Ss Invoking macros
You execute the macro by inserting its name. You execute the macro by inserting its name.
.Bd -literal -offset indent .Bd -literal -offset indent
add a,b ld a,a+b
ld sp,hl ld sp,hl
MyMacro ;\ This will be expanded MyMacro ;\ This will be expanded
sub a,87 ld a,a-87
.Ed .Ed
.Pp .Pp
It's valid to call a macro from a macro (yes, even the same one). It's valid to call a macro from a macro (yes, even the same one).
@@ -1402,10 +1475,10 @@ it will insert the macro definition (the code enclosed in
Suppose your macro contains a loop. Suppose your macro contains a loop.
.Bd -literal -offset indent .Bd -literal -offset indent
MACRO LoopyMacro MACRO LoopyMacro
xor a,a ld a,a^a
\&.loop ld [hl+],a \&.loop ld [hl+],a
dec c ld c-
jr nz,.loop ld nz pc,b .loop
ENDM ENDM
.Ed .Ed
.Pp .Pp
@@ -1420,10 +1493,10 @@ also works in
blocks. blocks.
.Bd -literal -offset indent .Bd -literal -offset indent
MACRO LoopyMacro MACRO LoopyMacro
xor a,a ld a,a^a
\&.loop\[rs]@ ld [hl+],a \&.loop\[rs]@ ld [hl+],a
dec c ld c-
jr nz,.loop\[rs]@ ld nz pc,b .loop\[rs]@
ENDM ENDM
.Ed .Ed
.Pp .Pp
@@ -1451,14 +1524,14 @@ being the first argument specified on the macro invocation.
MACRO LoopyMacro MACRO LoopyMacro
ld hl,\[rs]1 ld hl,\[rs]1
ld c,\[rs]2 ld c,\[rs]2
xor a,a ld a,a^a
\&.loop\[rs]@ ld [hl+],a \&.loop\[rs]@ ld [hl+],a
dec c ld c-
jr nz,.loop\[rs]@ ld nz pc,b .loop\[rs]@
ENDM ENDM
.Ed .Ed
.Pp .Pp
Now I can call the macro specifying two arguments, the first being the address and the second being a byte count. Now you can call the macro specifying two arguments, the first being the address and the second being a byte count.
The generated code will then reset all bytes in this range. The generated code will then reset all bytes in this range.
.Bd -literal -offset indent .Bd -literal -offset indent
LoopyMacro MyVars,54 LoopyMacro MyVars,54
@@ -1566,7 +1639,7 @@ The following example will assemble
four times: four times:
.Bd -literal -offset indent .Bd -literal -offset indent
REPT 4 REPT 4
add a,c ld a,a+c
ENDR ENDR
.Ed .Ed
.Pp .Pp
@@ -1578,7 +1651,7 @@ to generate tables on the fly:
; (shifted and scaled from the range [-1.0, 1.0]) ; (shifted and scaled from the range [-1.0, 1.0])
ANGLE = 0.0 ANGLE = 0.0
REPT 256 REPT 256
db (MUL(64.0, SIN(ANGLE)) + 64.0) >> 16 ld [b @], (MUL(64.0, SIN(ANGLE)) + 64.0) >> 16
ANGLE = ANGLE + 256.0 ; 256.0 = 65536 degrees / 256 entries ANGLE = ANGLE + 256.0 ; 256.0 = 65536 degrees / 256 entries
ENDR ENDR
.Ed .Ed
@@ -1598,24 +1671,25 @@ Everything between
and the matching and the matching
.Ic ENDR .Ic ENDR
will be repeated for each value of a given symbol. will be repeated for each value of a given symbol.
String equates are not expanded within the symbol name.
For example, this code will produce a table of squared values from 0 to 255: For example, this code will produce a table of squared values from 0 to 255:
.Bd -literal -offset indent .Bd -literal -offset indent
FOR N, 256 FOR N, 256
dw N * N ld [w @], N * N
ENDR ENDR
.Ed .Ed
.Pp .Pp
It acts just as if you had done: It acts just as if you had done:
.Bd -literal -offset ident .Bd -literal -offset ident
N = 0 N = 0
dw N * N ld [w @], N * N
N = 1 N = 1
dw N * N ld [w @], N * N
N = 2 N = 2
dw N * N ld [w @], N * N
; ... ; ...
N = 255 N = 255
dw N * N ld [w @], N * N
N = 256 N = 256
.Ed .Ed
.Pp .Pp
@@ -1624,9 +1698,9 @@ You can customize the range of
values: values:
.Bl -column "FOR V, start, stop, step" .Bl -column "FOR V, start, stop, step"
.It Sy Code Ta Sy Range .It Sy Code Ta Sy Range
.It Ic FOR Ar V , stop Ta Ar V No increments from 0 to Ar stop No .It Ic FOR Ar V , stop Ta Ar V No increments from 0 to Ar stop
.It Ic FOR Ar V , start , stop Ta Ar V No increments from Ar start No to Ar stop No .It Ic FOR Ar V , start , stop Ta Ar V No increments from Ar start No to Ar stop
.It Ic FOR Ar V , start , stop , step Ta Ar V No goes from Ar start No to Ar stop No by Ar step No .It Ic FOR Ar V , start , stop , step Ta Ar V No goes from Ar start No to Ar stop No by Ar step
.El .El
.Pp .Pp
The The
@@ -1706,18 +1780,18 @@ and
Syntax examples are given below: Syntax examples are given below:
.Bd -literal -offset indent .Bd -literal -offset indent
Function: Function:
xor a ld a,a^a
ASSERT LOW(Variable) == 0 ASSERT LOW(Variable) == 0
ld h, HIGH(Variable) ld h, HIGH(Variable)
ld l, a ld l, a
ld a, [hli] ld a, [hli]
; You can also indent this! ; You can also indent this!
ASSERT BANK(OtherFunction) == BANK(Function) ASSERT BANK(OtherFunction) == BANK(Function)
call OtherFunction ld [--sp], pc,OtherFunction
; Lowercase also works ; Lowercase also works
assert Variable + 1 == OtherVariable assert Variable + 1 == OtherVariable
ld c, [hl] ld c, [hl]
ret ld pc,[sp++]
\&.end \&.end
; If you specify one, a message will be printed ; If you specify one, a message will be printed
STATIC_ASSERT .end - Function < 256, "Function is too large!" STATIC_ASSERT .end - Function < 256, "Function is too large!"
@@ -1826,9 +1900,9 @@ takes a comma-separated list of options as its argument:
.Bd -literal -offset indent .Bd -literal -offset indent
PUSHO PUSHO
OPT g.oOX ;Set the GB graphics constants to use these characters OPT g.oOX ;Set the GB graphics constants to use these characters
DW `..ooOOXX LD [W @], `..ooOOXX
POPO POPO
DW `00112233 LD [W @], `00112233
.Ed .Ed
.Pp .Pp
The options that OPT can modify are currently: The options that OPT can modify are currently:

View File

@@ -139,7 +139,7 @@ void rpn_BankSelf(struct Expression *expr)
if (!pCurrentSection) { if (!pCurrentSection) {
error("PC has no bank outside a section\n"); error("PC has no bank outside a section\n");
expr->nVal = 1; expr->nVal = 1;
} else if (pCurrentSection->bank == -1) { } else if (pCurrentSection->bank == (uint32_t)-1) {
makeUnknown(expr, "Current section's bank is not known"); makeUnknown(expr, "Current section's bank is not known");
expr->nRPNPatchSize++; expr->nRPNPatchSize++;
*reserveSpace(expr, 1) = RPN_BANK_SELF; *reserveSpace(expr, 1) = RPN_BANK_SELF;
@@ -165,7 +165,7 @@ void rpn_BankSymbol(struct Expression *expr, char const *tzSym)
sym = sym_Ref(tzSym); sym = sym_Ref(tzSym);
assert(sym); // If the symbol didn't exist, it should have been created assert(sym); // If the symbol didn't exist, it should have been created
if (sym_GetSection(sym) && sym_GetSection(sym)->bank != -1) { if (sym_GetSection(sym) && sym_GetSection(sym)->bank != (uint32_t)-1) {
/* Symbol's section is known and bank is fixed */ /* Symbol's section is known and bank is fixed */
expr->nVal = sym_GetSection(sym)->bank; expr->nVal = sym_GetSection(sym)->bank;
} else { } else {
@@ -186,7 +186,7 @@ void rpn_BankSection(struct Expression *expr, char const *tzSectionName)
struct Section *pSection = out_FindSectionByName(tzSectionName); struct Section *pSection = out_FindSectionByName(tzSectionName);
if (pSection && pSection->bank != -1) { if (pSection && pSection->bank != (uint32_t)-1) {
expr->nVal = pSection->bank; expr->nVal = pSection->bank;
} else { } else {
makeUnknown(expr, "Section \"%s\"'s bank is not known", makeUnknown(expr, "Section \"%s\"'s bank is not known",

View File

@@ -114,9 +114,9 @@ static unsigned int mergeSectUnion(struct Section *sect, enum SectionType type,
if (sect_HasData(type)) if (sect_HasData(type))
fail("Cannot declare ROM sections as UNION\n"); fail("Cannot declare ROM sections as UNION\n");
if (org != -1) { if (org != (uint32_t)-1) {
/* If both are fixed, they must be the same */ /* If both are fixed, they must be the same */
if (sect->org != -1 && sect->org != org) if (sect->org != (uint32_t)-1 && sect->org != org)
fail("Section already declared as fixed at different address $%04" fail("Section already declared as fixed at different address $%04"
PRIx32 "\n", sect->org); PRIx32 "\n", sect->org);
else if (sect->align != 0 && (mask(sect->align) & (org - sect->alignOfs))) else if (sect->align != 0 && (mask(sect->align) & (org - sect->alignOfs)))
@@ -128,7 +128,7 @@ static unsigned int mergeSectUnion(struct Section *sect, enum SectionType type,
} else if (alignment != 0) { } else if (alignment != 0) {
/* Make sure any fixed address given is compatible */ /* Make sure any fixed address given is compatible */
if (sect->org != -1) { if (sect->org != (uint32_t)-1) {
if ((sect->org - alignOffset) & mask(alignment)) if ((sect->org - alignOffset) & mask(alignment))
fail("Section already declared as fixed at incompatible address $%04" fail("Section already declared as fixed at incompatible address $%04"
PRIx32 "\n", sect->org); PRIx32 "\n", sect->org);
@@ -160,11 +160,11 @@ static unsigned int mergeFragments(struct Section *sect, enum SectionType type,
* combination of both. * combination of both.
* The merging is however performed at the *end* of the original section! * The merging is however performed at the *end* of the original section!
*/ */
if (org != -1) { if (org != (uint32_t)-1) {
uint16_t curOrg = org - sect->size; uint16_t curOrg = org - sect->size;
/* If both are fixed, they must be the same */ /* If both are fixed, they must be the same */
if (sect->org != -1 && sect->org != curOrg) if (sect->org != (uint32_t)-1 && sect->org != curOrg)
fail("Section already declared as fixed at incompatible address $%04" fail("Section already declared as fixed at incompatible address $%04"
PRIx32 " (cur addr = %04" PRIx32 ")\n", PRIx32 " (cur addr = %04" PRIx32 ")\n",
sect->org, sect->org + sect->size); sect->org, sect->org + sect->size);
@@ -182,7 +182,7 @@ static unsigned int mergeFragments(struct Section *sect, enum SectionType type,
curOfs += 1U << alignment; curOfs += 1U << alignment;
/* Make sure any fixed address given is compatible */ /* Make sure any fixed address given is compatible */
if (sect->org != -1) { if (sect->org != (uint32_t)-1) {
if ((sect->org - curOfs) & mask(alignment)) if ((sect->org - curOfs) & mask(alignment))
fail("Section already declared as fixed at incompatible address $%04" fail("Section already declared as fixed at incompatible address $%04"
PRIx32 "\n", sect->org); PRIx32 "\n", sect->org);
@@ -221,10 +221,10 @@ static void mergeSections(struct Section *sect, enum SectionType type, uint32_t
// Common checks // Common checks
/* If the section's bank is unspecified, override it */ /* If the section's bank is unspecified, override it */
if (sect->bank == -1) if (sect->bank == (uint32_t)-1)
sect->bank = bank; sect->bank = bank;
/* If both specify a bank, it must be the same one */ /* If both specify a bank, it must be the same one */
else if (bank != -1 && sect->bank != bank) else if (bank != (uint32_t)-1 && sect->bank != bank)
fail("Section already declared with different bank %" PRIu32 "\n", fail("Section already declared with different bank %" PRIu32 "\n",
sect->bank); sect->bank);
break; break;
@@ -296,7 +296,7 @@ static struct Section *getSection(char const *name, enum SectionType type, uint3
// First, validate parameters, and normalize them if applicable // First, validate parameters, and normalize them if applicable
if (bank != -1) { if (bank != (uint32_t)-1) {
if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM
&& type != SECTTYPE_SRAM && type != SECTTYPE_WRAMX) && type != SECTTYPE_SRAM && type != SECTTYPE_WRAMX)
error("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections\n"); error("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections\n");
@@ -316,7 +316,7 @@ static struct Section *getSection(char const *name, enum SectionType type, uint3
alignOffset = 0; alignOffset = 0;
} }
if (org != -1) { if (org != (uint32_t)-1) {
if (org < startaddr[type] || org > endaddr(type)) if (org < startaddr[type] || org > endaddr(type))
error("Section \"%s\"'s fixed address %#" PRIx32 error("Section \"%s\"'s fixed address %#" PRIx32
" is outside of range [%#" PRIx16 "; %#" PRIx16 "]\n", " is outside of range [%#" PRIx16 "; %#" PRIx16 "]\n",
@@ -331,7 +331,7 @@ static struct Section *getSection(char const *name, enum SectionType type, uint3
/* It doesn't make sense to have both alignment and org set */ /* It doesn't make sense to have both alignment and org set */
uint32_t mask = mask(alignment); uint32_t mask = mask(alignment);
if (org != -1) { if (org != (uint32_t)-1) {
if ((org - alignOffset) & mask) if ((org - alignOffset) & mask)
error("Section \"%s\"'s fixed address doesn't match its alignment\n", error("Section \"%s\"'s fixed address doesn't match its alignment\n",
name); name);
@@ -453,7 +453,7 @@ void sect_AlignPC(uint8_t alignment, uint16_t offset)
struct Section *sect = sect_GetSymbolSection(); struct Section *sect = sect_GetSymbolSection();
uint16_t alignSize = 1 << alignment; // Size of an aligned "block" uint16_t alignSize = 1 << alignment; // Size of an aligned "block"
if (sect->org != -1) { if (sect->org != (uint32_t)-1) {
if ((sym_GetPCValue() - offset) % alignSize) if ((sym_GetPCValue() - offset) % alignSize)
error("Section's fixed address fails required alignment (PC = $%04" PRIx32 error("Section's fixed address fails required alignment (PC = $%04" PRIx32
")\n", sym_GetPCValue()); ")\n", sym_GetPCValue());
@@ -761,6 +761,8 @@ void out_BinaryFile(char const *s, int32_t startPos)
if (!f) { if (!f) {
if (oGeneratedMissingIncludes) { if (oGeneratedMissingIncludes) {
if (verbose)
printf("Aborting (-MG) on INCBIN file '%s' (%s)\n", s, strerror(errno));
oFailedOnMissingInclude = true; oFailedOnMissingInclude = true;
return; return;
} }
@@ -828,6 +830,8 @@ void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
if (!f) { if (!f) {
free(fullPath); free(fullPath);
if (oGeneratedMissingIncludes) { if (oGeneratedMissingIncludes) {
if (verbose)
printf("Aborting (-MG) on INCBIN file '%s' (%s)\n", s, strerror(errno));
oFailedOnMissingInclude = true; oFailedOnMissingInclude = true;
return; return;
} }

View File

@@ -179,7 +179,7 @@ static void updateSymbolFilename(struct Symbol *sym)
setSymbolFilename(sym); setSymbolFilename(sym);
/* If the old node was referenced, ensure the new one is */ /* If the old node was referenced, ensure the new one is */
if (oldSrc && oldSrc->referenced && oldSrc->ID != -1) if (oldSrc && oldSrc->referenced && oldSrc->ID != (uint32_t)-1)
out_RegisterNode(sym->src); out_RegisterNode(sym->src);
/* TODO: unref the old node, and use `out_ReplaceNode` instead if deleting it */ /* TODO: unref the old node, and use `out_ReplaceNode` instead if deleting it */
} }
@@ -277,7 +277,7 @@ struct Symbol const *sym_GetPC(void)
static inline bool isReferenced(struct Symbol const *sym) static inline bool isReferenced(struct Symbol const *sym)
{ {
return sym->ID != -1; return sym->ID != (uint32_t)-1;
} }
/* /*
@@ -315,7 +315,7 @@ uint32_t sym_GetPCValue(void)
if (!sect) if (!sect)
error("PC has no value outside a section\n"); error("PC has no value outside a section\n");
else if (sect->org == -1) else if (sect->org == (uint32_t)-1)
error("Expected constant PC but section is not fixed\n"); error("Expected constant PC but section is not fixed\n");
else else
return CallbackPC(); return CallbackPC();

View File

@@ -57,7 +57,7 @@ char const *print(int c)
default: /* Print as hex */ default: /* Print as hex */
buf[1] = 'x'; buf[1] = 'x';
sprintf(&buf[2], "%02hhx", c); sprintf(&buf[2], "%02hhx", (uint8_t)c);
return buf; return buf;
} }
buf[2] = '\0'; buf[2] = '\0';

View File

@@ -870,11 +870,13 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
// Output ROMX if it was buffered // Output ROMX if it was buffered
if (romx) { if (romx) {
// The value returned is either -1, or smaller than `totalRomxLen`,
// so it's fine to cast to `size_t`
writeLen = writeBytes(output, romx, totalRomxLen); writeLen = writeBytes(output, romx, totalRomxLen);
if (writeLen == -1) { if (writeLen == -1) {
report("FATAL: Failed to write \"%s\"'s ROMX: %s\n", name, strerror(errno)); report("FATAL: Failed to write \"%s\"'s ROMX: %s\n", name, strerror(errno));
goto free_romx; goto free_romx;
} else if (writeLen < totalRomxLen) { } else if ((size_t)writeLen < totalRomxLen) {
report("FATAL: Could only write %ld of \"%s\"'s %ld ROMX bytes\n", report("FATAL: Could only write %ld of \"%s\"'s %ld ROMX bytes\n",
writeLen, name, totalRomxLen); writeLen, name, totalRomxLen);
goto free_romx; goto free_romx;
@@ -898,7 +900,9 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
size_t thisLen = len > sizeof(bank) ? sizeof(bank) : len; size_t thisLen = len > sizeof(bank) ? sizeof(bank) : len;
ssize_t ret = writeBytes(output, bank, thisLen); ssize_t ret = writeBytes(output, bank, thisLen);
if (ret != thisLen) { // The return value is either -1, or at most `thisLen`,
// so it's fine to cast to `size_t`
if ((size_t)ret != thisLen) {
report("FATAL: Failed to write \"%s\"'s padding: %s\n", report("FATAL: Failed to write \"%s\"'s padding: %s\n",
name, strerror(errno)); name, strerror(errno));
break; break;

View File

@@ -1,11 +1,11 @@
.\" .\"
.\" This file is part of RGBDS. .\" This file is part of RGBDS.
.\" .\"
.\" Copyright (c) 2010-2017, Anthony J. Bentley and RGBDS contributors. .\" Copyright (c) 2010-2021, Anthony J. Bentley and RGBDS contributors.
.\" .\"
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd December 5, 2019 .Dd March 28, 2021
.Dt RGBFIX 1 .Dt RGBFIX 1
.Os .Os
.Sh NAME .Sh NAME

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,11 @@
.\" .\"
.\" This file is part of RGBDS. .\" This file is part of RGBDS.
.\" .\"
.\" Copyright (c) 2013-2018, stag019 and RGBDS contributors. .\" Copyright (c) 2013-2021, stag019 and RGBDS contributors.
.\" .\"
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd December 5, 2019 .Dd March 28, 2021
.Dt RGBGFX 1 .Dt RGBGFX 1
.Os .Os
.Sh NAME .Sh NAME
@@ -78,7 +78,8 @@ Same as
.Fl f , .Fl f ,
but additionally, the supplied command line parameters are saved within the PNG and will be loaded and automatically used next time. but additionally, the supplied command line parameters are saved within the PNG and will be loaded and automatically used next time.
.It Fl h , Fl Fl horizontal .It Fl h , Fl Fl horizontal
Lay out tiles horizontally rather than vertically. Lay out tiles in column-major order (column by column), instead of the default row-major order (line by line).
Especially useful for "8x16" OBJ mode, if the input image is 16 pixels tall.
.It Fl m , Fl Fl mirror-tiles .It Fl m , Fl Fl mirror-tiles
Truncate tiles by checking for tiles that are mirrored versions of others and omitting these from the output file. Truncate tiles by checking for tiles that are mirrored versions of others and omitting these from the output file.
Useful with tilemaps and attrmaps together to keep track of the duplicated tiles and the dimension mirrored. Useful with tilemaps and attrmaps together to keep track of the duplicated tiles and the dimension mirrored.

View File

@@ -112,6 +112,13 @@ static inline void assignSection(struct Section *section,
section->org = location->address; section->org = location->address;
section->bank = location->bank; section->bank = location->bank;
// Propagate the assigned location to all UNIONs/FRAGMENTs
// so `jr` patches in them will have the correct offset
for (struct Section *next = section->nextu; next != NULL; next = next->nextu) {
next->org = section->org;
next->bank = section->bank;
}
nbSectionsToAssign--; nbSectionsToAssign--;
out_AddSection(section); out_AddSection(section);

View File

@@ -188,7 +188,7 @@ static void readFileStackNode(FILE *file, struct FileStackNode fileNodes[], uint
tryReadlong(parentID, file, tryReadlong(parentID, file,
"%s: Cannot read node #%" PRIu32 "'s parent ID: %s", fileName, i); "%s: Cannot read node #%" PRIu32 "'s parent ID: %s", fileName, i);
fileNodes[i].parent = parentID == -1 ? NULL : &fileNodes[parentID]; fileNodes[i].parent = parentID == (uint32_t)-1 ? NULL : &fileNodes[parentID];
tryReadlong(fileNodes[i].lineNo, file, tryReadlong(fileNodes[i].lineNo, file,
"%s: Cannot read node #%" PRIu32 "'s line number: %s", fileName, i); "%s: Cannot read node #%" PRIu32 "'s line number: %s", fileName, i);
tryGetc(fileNodes[i].type, file, "%s: Cannot read node #%" PRIu32 "'s type: %s", tryGetc(fileNodes[i].type, file, "%s: Cannot read node #%" PRIu32 "'s type: %s",
@@ -261,7 +261,7 @@ static void readSymbol(FILE *file, struct Symbol *symbol,
* @param i The number of the patch to report in errors * @param i The number of the patch to report in errors
*/ */
static void readPatch(FILE *file, struct Patch *patch, char const *fileName, char const *sectName, static void readPatch(FILE *file, struct Patch *patch, char const *fileName, char const *sectName,
uint32_t i, struct Section *fileSections[], struct FileStackNode fileNodes[]) uint32_t i, struct FileStackNode fileNodes[])
{ {
uint32_t nodeID; uint32_t nodeID;
uint8_t type; uint8_t type;
@@ -279,7 +279,6 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName, cha
tryReadlong(patch->pcSectionID, file, tryReadlong(patch->pcSectionID, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s", "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
fileName, sectName, i); fileName, sectName, i);
patch->pcSection = patch->pcSectionID == -1 ? NULL : fileSections[patch->pcSectionID];
tryReadlong(patch->pcOffset, file, tryReadlong(patch->pcOffset, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s", "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
fileName, sectName, i); fileName, sectName, i);
@@ -304,6 +303,16 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName, cha
feof(file) ? "Unexpected end of file" : strerror(errno)); feof(file) ? "Unexpected end of file" : strerror(errno));
} }
/**
* Sets a patch's pcSection from its pcSectionID.
* @param patch The struct to fix
*/
static void linkPatchToPCSect(struct Patch *patch, struct Section *fileSections[])
{
patch->pcSection = patch->pcSectionID == (uint32_t)-1 ? NULL
: fileSections[patch->pcSectionID];
}
/** /**
* Reads a section from a file. * Reads a section from a file.
* @param file The file to read from * @param file The file to read from
@@ -311,7 +320,7 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName, cha
* @param fileName The filename to report in errors * @param fileName The filename to report in errors
*/ */
static void readSection(FILE *file, struct Section *section, char const *fileName, static void readSection(FILE *file, struct Section *section, char const *fileName,
struct Section *fileSections[], struct FileStackNode fileNodes[]) struct FileStackNode fileNodes[])
{ {
int32_t tmp; int32_t tmp;
uint8_t byte; uint8_t byte;
@@ -388,12 +397,9 @@ static void readSection(FILE *file, struct Section *section, char const *fileNam
malloc(sizeof(*patches) * section->nbPatches + 1); malloc(sizeof(*patches) * section->nbPatches + 1);
if (!patches) if (!patches)
err(1, "%s: Unable to read \"%s\"'s patches", fileName, err(1, "%s: Unable to read \"%s\"'s patches", fileName, section->name);
section->name); for (uint32_t i = 0; i < section->nbPatches; i++)
for (uint32_t i = 0; i < section->nbPatches; i++) { readPatch(file, &patches[i], fileName, section->name, i, fileNodes);
readPatch(file, &patches[i], fileName, section->name,
i, fileSections, fileNodes);
}
section->patches = patches; section->patches = patches;
} }
} }
@@ -435,13 +441,13 @@ static void linkSymToSect(struct Symbol const *symbol, struct Section *section)
*/ */
static void readAssertion(FILE *file, struct Assertion *assert, static void readAssertion(FILE *file, struct Assertion *assert,
char const *fileName, uint32_t i, char const *fileName, uint32_t i,
struct Section *fileSections[], struct FileStackNode fileNodes[]) struct FileStackNode fileNodes[])
{ {
char assertName[sizeof("Assertion #4294967295")]; // UINT32_MAX char assertName[sizeof("Assertion #4294967295")]; // UINT32_MAX
snprintf(assertName, sizeof(assertName), "Assertion #%" PRIu32, i); snprintf(assertName, sizeof(assertName), "Assertion #%" PRIu32, i);
readPatch(file, &assert->patch, fileName, assertName, 0, fileSections, fileNodes); readPatch(file, &assert->patch, fileName, assertName, 0, fileNodes);
tryReadstr(assert->message, file, "%s: Cannot read assertion's message: %s", tryReadstr(assert->message, file, "%s: Cannot read assertion's message: %s",
fileName); fileName);
} }
@@ -549,7 +555,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
err(1, "%s: Couldn't create new section", fileName); err(1, "%s: Couldn't create new section", fileName);
fileSections[i]->nextu = NULL; fileSections[i]->nextu = NULL;
readSection(file, fileSections[i], fileName, fileSections, nodes[fileID].nodes); readSection(file, fileSections[i], fileName, nodes[fileID].nodes);
fileSections[i]->fileSymbols = fileSymbols; fileSections[i]->fileSymbols = fileSymbols;
if (nbSymPerSect[i]) { if (nbSymPerSect[i]) {
fileSections[i]->symbols = malloc(nbSymPerSect[i] fileSections[i]->symbols = malloc(nbSymPerSect[i]
@@ -567,7 +573,15 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
free(nbSymPerSect); free(nbSymPerSect);
/* Give symbols pointers to their sections */ /* Give patches' PC section pointers to their sections */
for (uint32_t i = 0; i < nbSections; i++) {
if (sect_HasData(fileSections[i]->type)) {
for (uint32_t j = 0; j < fileSections[i]->nbPatches; j++)
linkPatchToPCSect(&fileSections[i]->patches[j], fileSections);
}
}
/* Give symbols' section pointers to their sections */
for (uint32_t i = 0; i < nbSymbols; i++) { for (uint32_t i = 0; i < nbSymbols; i++) {
int32_t sectionID = fileSymbols[i]->sectionID; int32_t sectionID = fileSymbols[i]->sectionID;
@@ -599,7 +613,8 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
if (!assertion) if (!assertion)
err(1, "%s: Couldn't create new assertion", fileName); err(1, "%s: Couldn't create new assertion", fileName);
readAssertion(file, assertion, fileName, i, fileSections, nodes[fileID].nodes); readAssertion(file, assertion, fileName, i, nodes[fileID].nodes);
linkPatchToPCSect(&assertion->patch, fileSections);
assertion->fileSymbols = fileSymbols; assertion->fileSymbols = fileSymbols;
assertion->next = assertions; assertion->next = assertions;
assertions = assertion; assertions = assertion;
@@ -635,7 +650,7 @@ static void freeSection(struct Section *section, void *arg)
free(section->name); free(section->name);
if (sect_HasData(section->type)) { if (sect_HasData(section->type)) {
free(section->data); free(section->data);
for (int32_t i = 0; i < section->nbPatches; i++) for (uint32_t i = 0; i < section->nbPatches; i++)
free(section->patches[i].rpnExpression); free(section->patches[i].rpnExpression);
free(section->patches); free(section->patches);
} }

View File

@@ -336,7 +336,7 @@ static void writeSymBank(struct SortedSections const *bankSections)
/** /**
* Write a bank's contents to the map file * Write a bank's contents to the map file
* @param bankSections The bank's sections * @param bankSections The bank's sections
* @return The bank's slack space * @return The bank's used space
*/ */
static uint16_t writeMapBank(struct SortedSections const *sectList, static uint16_t writeMapBank(struct SortedSections const *sectList,
enum SectionType type, uint32_t bank) enum SectionType type, uint32_t bank)
@@ -350,14 +350,14 @@ static uint16_t writeMapBank(struct SortedSections const *sectList,
fprintf(mapFile, "%s bank #%" PRIu32 ":\n", typeNames[type], fprintf(mapFile, "%s bank #%" PRIu32 ":\n", typeNames[type],
bank + bankranges[type][0]); bank + bankranges[type][0]);
uint16_t slack = maxsize[type]; uint16_t used = 0;
while (section || zeroLenSection) { while (section || zeroLenSection) {
struct SortedSection const **pickedSection = struct SortedSection const **pickedSection =
nextSection(&section, &zeroLenSection); nextSection(&section, &zeroLenSection);
struct Section const *sect = (*pickedSection)->section; struct Section const *sect = (*pickedSection)->section;
slack -= sect->size; used += sect->size;
if (sect->size != 0) if (sect->size != 0)
fprintf(mapFile, " SECTION: $%04" PRIx16 "-$%04" PRIx16 " ($%04" PRIx16 " byte%s) [\"%s\"]\n", fprintf(mapFile, " SECTION: $%04" PRIx16 "-$%04" PRIx16 " ($%04" PRIx16 " byte%s) [\"%s\"]\n",
@@ -368,44 +368,54 @@ static uint16_t writeMapBank(struct SortedSections const *sectList,
fprintf(mapFile, " SECTION: $%04" PRIx16 " (0 bytes) [\"%s\"]\n", fprintf(mapFile, " SECTION: $%04" PRIx16 " (0 bytes) [\"%s\"]\n",
sect->org, sect->name); sect->org, sect->name);
uint16_t org = sect->org;
while (sect) {
for (size_t i = 0; i < sect->nbSymbols; i++) for (size_t i = 0; i < sect->nbSymbols; i++)
fprintf(mapFile, " $%04" PRIx32 " = %s\n", fprintf(mapFile, " $%04" PRIx32 " = %s\n",
sect->symbols[i]->offset + sect->org, sect->symbols[i]->offset + org,
sect->symbols[i]->name); sect->symbols[i]->name);
sect = sect->nextu; // Also print symbols in the following "pieces"
}
*pickedSection = (*pickedSection)->next; *pickedSection = (*pickedSection)->next;
} }
if (slack == maxsize[type])
if (used == 0) {
fputs(" EMPTY\n\n", mapFile); fputs(" EMPTY\n\n", mapFile);
else } else {
uint16_t slack = maxsize[type] - used;
fprintf(mapFile, " SLACK: $%04" PRIx16 " byte%s\n\n", slack, fprintf(mapFile, " SLACK: $%04" PRIx16 " byte%s\n\n", slack,
slack == 1 ? "" : "s"); slack == 1 ? "" : "s");
}
return slack; return used;
} }
/** /**
* Write the total slack space by section type to the map file * Write the total used space by section type to the map file
* @param slackMap The total slack space by section type * @param usedMap The total used space by section type
*/ */
static void writeMapSlack(uint32_t slackMap[MIN_NB_ELMS(SECTTYPE_INVALID)]) static void writeMapUsed(uint32_t usedMap[MIN_NB_ELMS(SECTTYPE_INVALID)])
{ {
if (!mapFile) if (!mapFile)
return; return;
fputs("FREE:\n", mapFile); fputs("USED:\n", mapFile);
for (uint8_t i = 0; i < SECTTYPE_INVALID; i++) { for (uint8_t i = 0; i < SECTTYPE_INVALID; i++) {
enum SectionType type = typeMap[i]; enum SectionType type = typeMap[i];
// Do not output slack space for VRAM or OAM // Do not output used space for VRAM or OAM
if (type == SECTTYPE_VRAM || type == SECTTYPE_OAM) if (type == SECTTYPE_VRAM || type == SECTTYPE_OAM)
continue; continue;
if (sections[type].nbBanks > 0) { if (sections[type].nbBanks > 0) {
fprintf(mapFile, " %s: $%04" PRIx32 " byte%s in %" PRIu32 " bank%s\n", fprintf(mapFile, " %s: $%04" PRIx32 " byte%s in %" PRIu32 " bank%s\n",
typeNames[type], slackMap[type], slackMap[type] == 1 ? "" : "s", typeNames[type], usedMap[type], usedMap[type] == 1 ? "" : "s",
sections[type].nbBanks, sections[type].nbBanks == 1 ? "" : "s"); sections[type].nbBanks, sections[type].nbBanks == 1 ? "" : "s");
} }
} }
@@ -419,7 +429,7 @@ static void writeSymAndMap(void)
if (!symFileName && !mapFileName) if (!symFileName && !mapFileName)
return; return;
uint32_t slackMap[SECTTYPE_INVALID] = {0}; uint32_t usedMap[SECTTYPE_INVALID] = {0};
symFile = openFile(symFileName, "w"); symFile = openFile(symFileName, "w");
mapFile = openFile(mapFileName, "w"); mapFile = openFile(mapFileName, "w");
@@ -434,11 +444,11 @@ static void writeSymAndMap(void)
struct SortedSections const *sect = &sections[type].banks[bank]; struct SortedSections const *sect = &sections[type].banks[bank];
writeSymBank(sect); writeSymBank(sect);
slackMap[type] += writeMapBank(sect, type, bank); usedMap[type] += writeMapBank(sect, type, bank);
} }
} }
writeMapSlack(slackMap); writeMapUsed(usedMap);
closeFile(symFile); closeFile(symFile);
closeFile(mapFile); closeFile(mapFile);

View File

@@ -400,7 +400,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
void patch_CheckAssertions(struct Assertion *assert) void patch_CheckAssertions(struct Assertion *assert)
{ {
verbosePrint("Checking assertions..."); verbosePrint("Checking assertions...\n");
initRPNStack(); initRPNStack();
while (assert) { while (assert) {

View File

@@ -1,11 +1,11 @@
.\" .\"
.\" This file is part of RGBDS. .\" This file is part of RGBDS.
.\" .\"
.\" Copyright (c) 2010-2019, Anthony J. Bentley and RGBDS contributors. .\" Copyright (c) 2010-2021, Anthony J. Bentley and RGBDS contributors.
.\" .\"
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd November 26, 2019 .Dd March 28, 2021
.Dt RGBLINK 1 .Dt RGBLINK 1
.Os .Os
.Sh NAME .Sh NAME

View File

@@ -1,11 +1,11 @@
.\" .\"
.\" This file is part of RGBDS. .\" This file is part of RGBDS.
.\" .\"
.\" Copyright (c) 2017-2018, Antonio Nino Diaz and RGBDS contributors. .\" Copyright (c) 2017-2021, Antonio Nino Diaz and RGBDS contributors.
.\" .\"
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd November 26, 2019 .Dd March 28, 2021
.Dt RGBLINK 5 .Dt RGBLINK 5
.Os .Os
.Sh NAME .Sh NAME

View File

@@ -194,11 +194,7 @@ void sect_AddSection(struct Section *section)
section->name, typeNames[section->type]); section->name, typeNames[section->type]);
} else { } else {
/* If not, add it */ /* If not, add it */
bool collided = hash_AddElement(sections, section->name, hash_AddElement(sections, section->name, section);
section);
if (beVerbose && collided)
warnx("Section hashmap collision occurred!");
} }
} }
@@ -248,7 +244,7 @@ static void doSanityChecks(struct Section *section, void *ptr)
* Check if alignment is reasonable, this is important to avoid UB * Check if alignment is reasonable, this is important to avoid UB
* An alignment of zero is equivalent to no alignment, basically * An alignment of zero is equivalent to no alignment, basically
*/ */
if (section->isAlignFixed && section->alignMask == 1) if (section->isAlignFixed && section->alignMask == 0)
section->isAlignFixed = false; section->isAlignFixed = false;
/* Too large an alignment may not be satisfiable */ /* Too large an alignment may not be satisfiable */

View File

@@ -1,11 +1,11 @@
.\" .\"
.\" This file is part of RGBDS. .\" This file is part of RGBDS.
.\" .\"
.\" Copyright (c) 2017-2020, Antonio Nino Diaz and RGBDS contributors. .\" Copyright (c) 2017-2021, Antonio Nino Diaz and RGBDS contributors.
.\" .\"
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd January 26, 2018 .Dd March 28, 2021
.Dt RGBDS 5 .Dt RGBDS 5
.Os .Os
.Sh NAME .Sh NAME
@@ -22,11 +22,11 @@ This toolchain is in development and new features may require adding more inform
The following types are used: The following types are used:
.Pp .Pp
.Ar LONG .Ar LONG
is a 32bit integer stored in littleendian format. is a 32-bit integer stored in little-endian format.
.Ar BYTE .Ar BYTE
is an 8bit integer. is an 8-bit integer.
.Ar STRING .Ar STRING
is a 0terminated string of is a 0-terminated string of
.Ar BYTE . .Ar BYTE .
.Bd -literal .Bd -literal
; Header ; Header

View File

@@ -1,11 +1,11 @@
.\" .\"
.\" This file is part of RGBDS. .\" This file is part of RGBDS.
.\" .\"
.\" Copyright (c) 2010-2018, Anthony J. Bentley and RGBDS contributors. .\" Copyright (c) 2010-2021, Anthony J. Bentley and RGBDS contributors.
.\" .\"
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd March 7, 2018 .Dd March 28, 2021
.Dt RGBDS 7 .Dt RGBDS 7
.Os .Os
.Sh NAME .Sh NAME

View File

@@ -1,3 +1,3 @@
ERROR: align-large-ofs.asm(2): ERROR: align-large-ofs.asm(2):
Alignment offset (2) must be smaller than alignment size (2) Alignment offset (2) must be smaller than alignment size (2)
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

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

View File

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

29
test/asm/def.asm Normal file
View File

@@ -0,0 +1,29 @@
def variable = 1
println variable
def variable set 2
println variable
redef variable = 3
println variable
redef variable set 4
println variable
DEF constant EQU 42
println constant
DEF string EQUS "here"
println "{string}"
rsreset
def _x rb
def _y rw 2
def _z rl
def _size rb 0
println "{_x} {_y} {_z} {_size}"
def constant equ 6*7 ; fails
println constant
redef string equs "there"
println "{string}"
redef constant equ 6*9 ; syntax error

5
test/asm/def.err Normal file
View File

@@ -0,0 +1,5 @@
ERROR: def.asm(23):
'constant' already defined at def.asm(10)
ERROR: def.asm(29):
syntax error, unexpected EQU, expecting SET or = or EQUS
error: Assembly aborted (2 errors)!

9
test/asm/def.out Normal file
View File

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

5
test/asm/def.simple.err Normal file
View File

@@ -0,0 +1,5 @@
ERROR: def.asm(23):
'constant' already defined at def.asm(10)
ERROR: def.asm(29):
syntax error
error: Assembly aborted (2 errors)!

View File

@@ -2,4 +2,4 @@ warning: deprecated-pi.asm(2): [-Wobsolete]
`_PI` is deprecated; use 3.14159 `_PI` is deprecated; use 3.14159
ERROR: deprecated-pi.asm(3): ERROR: deprecated-pi.asm(3):
Built-in symbol '_PI' cannot be purged Built-in symbol '_PI' cannot be purged
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -2,4 +2,4 @@ ERROR: ds-bad.asm(3):
Expected constant expression: 'unknown' is not constant at assembly time Expected constant expression: 'unknown' is not constant at assembly time
warning: ds-bad.asm(4): [-Wtruncation] warning: ds-bad.asm(4): [-Wtruncation]
Expression must be 8-bit Expression must be 8-bit
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -0,0 +1,23 @@
if 1
println "taken if"
elif 2 / 0 ; avoided fatal "Division by zero" error
println "untaken elif"
elif 3 / 0 ; avoided fatal "Division by zero" error
println "untaken after untaken"
endc
if 0
println "untaken if"
elif 1
println "taken elif"
elif !@#$ ; avoided fatal syntax error
println "untaken elif"
elif %^&* ; avoided fatal syntax error
println "untaken after untaken"
endc
if 0
println "untaken if"
elif 1 / 0 ; fatal "Division by zero" error
println "unreached elif"
endc

View File

@@ -0,0 +1,2 @@
FATAL: elif-after-taken-if.asm(21):
Division by zero

View File

@@ -0,0 +1,2 @@
taken if
taken elif

View File

@@ -33,7 +33,7 @@ endr
println "-> {d:q}" println "-> {d:q}"
s EQUS "x" s EQUS "x"
for s, 3, 30, 3 for {s}, 3, 30, 3
print "{d:x} " print "{d:x} "
endr endr
println "-> {d:x}" println "-> {d:x}"

View File

@@ -1,3 +1,3 @@
ERROR: fragment-align.asm(25): ERROR: fragment-align.asm(25):
Section's alignment fails required alignment (offset from section start = $0004) Section's alignment fails required alignment (offset from section start = $0004)
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: garbage_char.asm(1): ERROR: garbage_char.asm(1):
Unknown character 0xFF Unknown character 0xFF
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: if@-no-sect.asm(1): ERROR: if@-no-sect.asm(1):
PC has no value outside a section PC has no value outside a section
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: incbin-empty-bad.asm(3): ERROR: incbin-empty-bad.asm(3):
Specified range in INCBIN is out of bounds (0 + 1 > 0) Specified range in INCBIN is out of bounds (0 + 1 > 0)
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: incbin-end-bad.asm(3): ERROR: incbin-end-bad.asm(3):
Specified range in INCBIN is out of bounds (123 + 1 > 123) Specified range in INCBIN is out of bounds (123 + 1 > 123)
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: label-outside-section.asm(1): ERROR: label-outside-section.asm(1):
Label "bad" created outside of a SECTION Label "bad" created outside of a SECTION
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: label-redefinition.asm(7): ERROR: label-redefinition.asm(7):
'Sym' already defined at label-redefinition.asm(6) -> label-redefinition.asm::m(4) 'Sym' already defined at label-redefinition.asm(6) -> label-redefinition.asm::m(4)
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: line-continuation-whitespace.asm(7): ERROR: line-continuation-whitespace.asm(7):
Label "foo" created outside of a SECTION Label "foo" created outside of a SECTION
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: line-continuation.asm(7): ERROR: line-continuation.asm(7):
Label "foo" created outside of a SECTION Label "foo" created outside of a SECTION
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: load-rom.asm(3): ERROR: load-rom.asm(3):
`LOAD` blocks cannot create a ROM section `LOAD` blocks cannot create a ROM section
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: local-purge.asm(8): ERROR: local-purge.asm(8):
Interpolated symbol ".loc" does not exist Interpolated symbol ".loc" does not exist
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: local-without-parent.asm(2): ERROR: local-without-parent.asm(2):
Local label '.test' in main scope Local label '.test' in main scope
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

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

View File

@@ -1,3 +1,3 @@
ERROR: macro-arg-in-string.asm(12): ERROR: macro-arg-in-string.asm(12):
Illegal character escape '!' Illegal character escape '!'
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -2,4 +2,4 @@ ERROR: multi-line-strings.asm(23):
Unterminated string Unterminated string
warning: multi-line-strings.asm(35): [-Wuser] warning: multi-line-strings.asm(35): [-Wuser]
check the line number check the line number
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: narg-nosect.asm(1): ERROR: narg-nosect.asm(1):
_NARG does not make sense outside of a macro _NARG does not make sense outside of a macro
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: nested-brackets.asm(5): ERROR: nested-brackets.asm(5):
Missing } Missing }
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: null-in-macro.asm(4) -> null-in-macro.asm::foo(2): ERROR: null-in-macro.asm(4) -> null-in-macro.asm::foo(2):
Unknown character 0x00 Unknown character 0x00
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: period.asm(5): ERROR: period.asm(5):
syntax error, unexpected . syntax error, unexpected .
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: period.asm(5): ERROR: period.asm(5):
syntax error syntax error
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: purge-refs.asm(6): ERROR: purge-refs.asm(6):
Symbol "Floating" is referenced and thus cannot be purged Symbol "Floating" is referenced and thus cannot be purged
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: purge.asm(9): ERROR: purge.asm(9):
Symbol "Referenced" is referenced and thus cannot be purged Symbol "Referenced" is referenced and thus cannot be purged
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: redef-equs.asm(23): ERROR: redef-equs.asm(23):
'N' already defined as non-EQUS at redef-equs.asm(22) 'N' already defined as non-EQUS at redef-equs.asm(22)
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -3,7 +3,7 @@ mac: MACRO
println "small \1" println "small \1"
elif (\1) > 100 elif (\1) > 100
println "large \1" println "large \1"
elif (\1) / 0 == 42 ; only evaluated if the "large" condition was taken elif (\1) / 0 == 42 ; only evaluated if neither "small" nor "large" was taken
println "division by zero!?" println "division by zero!?"
elif syntax! error? elif syntax! error?
println "X_X" println "X_X"
@@ -15,3 +15,4 @@ ENDM
mac 2 + 2 mac 2 + 2
mac STRLEN("abcdef") mac STRLEN("abcdef")
mac 101 mac 101
mac 23

View File

@@ -1,2 +1,2 @@
FATAL: skip-elif-condition.asm(17) -> skip-elif-condition.asm::mac(6): FATAL: skip-elif-condition.asm(18) -> skip-elif-condition.asm::mac(6):
Division by zero Division by zero

View File

@@ -1,3 +1,3 @@
ERROR: string-formatting.asm(10): ERROR: string-formatting.asm(10):
Formatting string with prefix flag '#' Formatting string with prefix flag '#'
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: sym-collision.asm(26): ERROR: sym-collision.asm(26):
Interpolated symbol "dork" does not exist Interpolated symbol "dork" does not exist
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: syntax-error-eof-newline.asm(5) -> syntax-error-eof-newline.inc(1): ERROR: syntax-error-eof-newline.asm(5) -> syntax-error-eof-newline.inc(1):
syntax error, unexpected newline syntax error, unexpected newline
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: syntax-error-eof-newline.asm(5) -> syntax-error-eof-newline.inc(1): ERROR: syntax-error-eof-newline.asm(5) -> syntax-error-eof-newline.inc(1):
syntax error syntax error
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: syntax-error.asm(2): ERROR: syntax-error.asm(2):
syntax error, unexpected a syntax error, unexpected a
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: syntax-error.asm(2): ERROR: syntax-error.asm(2):
syntax error syntax error
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -0,0 +1,12 @@
SECTION "main", ROM0
LOAD FRAGMENT "test", SRAM
ENDL
; The RPN patch for 'jr Label' in section "alt" refers to section "test",
; but the object file puts section "test" after section "alt".
; This case needs to be handled when identifying patches' PC sections.
SECTION "alt", ROM0
LOAD FRAGMENT "test", SRAM
jr Label
Label:
ENDL

View File

Binary file not shown.

View File

@@ -0,0 +1,4 @@
SECTION "main", ROM0
LOAD FRAGMENT "test", SRAM
db 0
ENDL

View File

@@ -0,0 +1,6 @@
SECTION "SECTION2", ROM0
LOAD FRAGMENT "test", SRAM
jr Label
Label:
dw Label
ENDL

View File

Binary file not shown.

View File

@@ -3,4 +3,4 @@ Linking failed with 1 error
--- ---
ERROR: <stdin>(30): ERROR: <stdin>(30):
Assertion failed: Force failing the build Assertion failed: Force failing the build
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -39,7 +39,7 @@ tryCmp () {
fi fi
} }
rgblink() { rgblinkQuiet () {
out="$(env $RGBLINK "$@")" || return $? out="$(env $RGBLINK "$@")" || return $?
if [[ -n "$out" ]]; then if [[ -n "$out" ]]; then
echo "$bold${red}Linking shouldn't produce anything on stdout!$rescolors$resbold" echo "$bold${red}Linking shouldn't produce anything on stdout!$rescolors$resbold"
@@ -55,13 +55,13 @@ for i in *.asm; do
ran_flag= ran_flag=
for flag in '-d' '-t' '-w'; do for flag in '-d' '-t' '-w'; do
if [ -f ${i%.asm}-no${flag}.out ]; then if [ -f ${i%.asm}-no${flag}.out ]; then
rgblink -o $gbtemp $otemp > $outtemp 2>&1 rgblinkQuiet -o $gbtemp $otemp > $outtemp 2>&1
tryDiff ${i%.asm}-no${flag}.out $outtemp tryDiff ${i%.asm}-no${flag}.out $outtemp
rc=$(($? || $rc)) rc=$(($? || $rc))
ran_flag=1 ran_flag=1
fi fi
if [ -f ${i%.asm}${flag}.out ]; then if [ -f ${i%.asm}${flag}.out ]; then
rgblink ${flag} -o $gbtemp $otemp > $outtemp 2>&1 rgblinkQuiet ${flag} -o $gbtemp $otemp > $outtemp 2>&1
tryDiff ${i%.asm}${flag}.out $outtemp tryDiff ${i%.asm}${flag}.out $outtemp
rc=$(($? || $rc)) rc=$(($? || $rc))
ran_flag=1 ran_flag=1
@@ -73,7 +73,7 @@ for i in *.asm; do
# Other tests have several linker scripts # Other tests have several linker scripts
find . -name "${i%.asm}*.link" | while read script; do find . -name "${i%.asm}*.link" | while read script; do
rgblink -l $script -o $gbtemp $otemp > $outtemp 2>&1 rgblinkQuiet -l $script -o $gbtemp $otemp > $outtemp 2>&1
tryDiff ${script%.link}.out $outtemp tryDiff ${script%.link}.out $outtemp
rc=$(($? || $rc)) rc=$(($? || $rc))
ran_flag=1 ran_flag=1
@@ -83,7 +83,7 @@ for i in *.asm; do
fi fi
# The rest of the tests just links a file, and maybe checks the binary # The rest of the tests just links a file, and maybe checks the binary
rgblink -o $gbtemp $otemp > $outtemp 2>&1 rgblinkQuiet -o $gbtemp $otemp > $outtemp 2>&1
if [ -f ${i%.asm}.out ]; then if [ -f ${i%.asm}.out ]; then
tryDiff ${i%.asm}.out $outtemp tryDiff ${i%.asm}.out $outtemp
rc=$(($? || $rc)) rc=$(($? || $rc))
@@ -103,7 +103,7 @@ i="bank-const.asm"
startTest startTest
$RGBASM -o $otemp bank-const/a.asm $RGBASM -o $otemp bank-const/a.asm
$RGBASM -o $gbtemp2 bank-const/b.asm $RGBASM -o $gbtemp2 bank-const/b.asm
rgblink -o $gbtemp $gbtemp2 $otemp > $outtemp 2>&1 rgblinkQuiet -o $gbtemp $gbtemp2 $otemp > $outtemp 2>&1
tryDiff bank-const/out.err $outtemp tryDiff bank-const/out.err $outtemp
rc=$(($? || $rc)) rc=$(($? || $rc))
@@ -111,7 +111,7 @@ for i in fragment-align/*; do
startTest startTest
$RGBASM -o $otemp $i/a.asm $RGBASM -o $otemp $i/a.asm
$RGBASM -o $gbtemp2 $i/b.asm $RGBASM -o $gbtemp2 $i/b.asm
rgblink -o $gbtemp $otemp $gbtemp2 2>$outtemp rgblinkQuiet -o $gbtemp $otemp $gbtemp2 2>$outtemp
tryDiff $i/out.err $outtemp tryDiff $i/out.err $outtemp
rc=$(($? || $rc)) rc=$(($? || $rc))
if [[ -f $i/out.gb ]]; then if [[ -f $i/out.gb ]]; then
@@ -124,19 +124,30 @@ done
i="high-low.asm" i="high-low.asm"
startTest startTest
$RGBASM -o $otemp high-low/a.asm $RGBASM -o $otemp high-low/a.asm
rgblink -o $gbtemp $otemp rgblinkQuiet -o $gbtemp $otemp
$RGBASM -o $otemp high-low/b.asm $RGBASM -o $otemp high-low/b.asm
rgblink -o $gbtemp2 $otemp rgblinkQuiet -o $gbtemp2 $otemp
tryCmp $gbtemp $gbtemp2 tryCmp $gbtemp $gbtemp2
rc=$(($? || $rc)) rc=$(($? || $rc))
i="load-fragment.asm"
startTest
$RGBASM -o $otemp load-fragment/a.asm
$RGBASM -o $gbtemp2 load-fragment/b.asm
rgblinkQuiet -o $gbtemp $otemp $gbtemp2 2>$outtemp
tryDiff load-fragment/out.err $outtemp
rc=$(($? || $rc))
dd if=$gbtemp count=1 bs=$(printf %s $(wc -c < load-fragment/out.gb)) > $otemp 2>/dev/null
tryCmp load-fragment/out.gb $otemp
rc=$(($? || $rc))
i="overlay.asm" i="overlay.asm"
startTest startTest
$RGBASM -o $otemp overlay/a.asm $RGBASM -o $otemp overlay/a.asm
rgblink -o $gbtemp -t -O overlay/overlay.gb $otemp > $outtemp 2>&1 rgblinkQuiet -o $gbtemp -t -O overlay/overlay.gb $otemp > $outtemp 2>&1
# This test does not trim its output with 'dd' because it needs to verify the correct output size
tryDiff overlay/out.err $outtemp tryDiff overlay/out.err $outtemp
rc=$(($? || $rc)) rc=$(($? || $rc))
# This test does not trim its output with 'dd' because it needs to verify the correct output size
tryCmp overlay/out.gb $gbtemp tryCmp overlay/out.gb $gbtemp
rc=$(($? || $rc)) rc=$(($? || $rc))
@@ -144,7 +155,7 @@ i="section-union/good.asm"
startTest startTest
$RGBASM -o $otemp section-union/good/a.asm $RGBASM -o $otemp section-union/good/a.asm
$RGBASM -o $gbtemp2 section-union/good/b.asm $RGBASM -o $gbtemp2 section-union/good/b.asm
rgblink -o $gbtemp -l section-union/good/script.link $otemp $gbtemp2 rgblinkQuiet -o $gbtemp -l section-union/good/script.link $otemp $gbtemp2
dd if=$gbtemp count=1 bs=$(printf %s $(wc -c < section-union/good/ref.out.bin)) > $otemp 2>/dev/null dd if=$gbtemp count=1 bs=$(printf %s $(wc -c < section-union/good/ref.out.bin)) > $otemp 2>/dev/null
tryCmp section-union/good/ref.out.bin $otemp tryCmp section-union/good/ref.out.bin $otemp
rc=$(($? || $rc)) rc=$(($? || $rc))
@@ -153,7 +164,7 @@ i="section-union/fragments.asm"
startTest startTest
$RGBASM -o $otemp section-union/fragments/a.asm $RGBASM -o $otemp section-union/fragments/a.asm
$RGBASM -o $gbtemp2 section-union/fragments/b.asm $RGBASM -o $gbtemp2 section-union/fragments/b.asm
rgblink -o $gbtemp $otemp $gbtemp2 rgblinkQuiet -o $gbtemp $otemp $gbtemp2
dd if=$gbtemp count=1 bs=$(printf %s $(wc -c < section-union/fragments/ref.out.bin)) > $otemp 2>/dev/null dd if=$gbtemp count=1 bs=$(printf %s $(wc -c < section-union/fragments/ref.out.bin)) > $otemp 2>/dev/null
tryCmp section-union/fragments/ref.out.bin $otemp tryCmp section-union/fragments/ref.out.bin $otemp
rc=$(($? || $rc)) rc=$(($? || $rc))
@@ -162,7 +173,7 @@ for i in section-union/*.asm; do
startTest startTest
$RGBASM -o $otemp $i $RGBASM -o $otemp $i
$RGBASM -o $gbtemp2 $i -DSECOND $RGBASM -o $gbtemp2 $i -DSECOND
if rgblink $otemp $gbtemp2 2>$outtemp; then if rgblinkQuiet $otemp $gbtemp2 2>$outtemp; then
echo -e "${bold}${red}$i didn't fail to link!${rescolors}${resbold}" echo -e "${bold}${red}$i didn't fail to link!${rescolors}${resbold}"
rc=1 rc=1
fi fi