mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Compare commits
49 Commits
v0.5.0-rc1
...
v0.5.0-rcC
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76c1995559 | ||
|
|
ae84570f04 | ||
|
|
094a31ef8c | ||
|
|
291dcf3b6c | ||
|
|
a890bd072b | ||
|
|
2f6c808ccb | ||
|
|
e80907abd0 | ||
|
|
25d39155d3 | ||
|
|
77021d229b | ||
|
|
1b250b90b2 | ||
|
|
e2b4723489 | ||
|
|
2507413162 | ||
|
|
e023a84d04 | ||
|
|
34c127d9c3 | ||
|
|
9a930988c2 | ||
|
|
8c4204c542 | ||
|
|
663c1930ec | ||
|
|
30ccf43f44 | ||
|
|
fdc17adbcb | ||
|
|
cc196954f3 | ||
|
|
55b6cfff84 | ||
|
|
1fc73b04eb | ||
|
|
d05703c692 | ||
|
|
5dbfafcc55 | ||
|
|
a265b85d9d | ||
|
|
28abf03c0a | ||
|
|
7e7f92f18c | ||
|
|
7461170956 | ||
|
|
57d966d6e0 | ||
|
|
17752d7094 | ||
|
|
4216f0a9b0 | ||
|
|
e3fde986ad | ||
|
|
aa99ed056c | ||
|
|
46d6652df1 | ||
|
|
5406674cdd | ||
|
|
5d2e2e2182 | ||
|
|
a929f36bc5 | ||
|
|
bdb84a901f | ||
|
|
093631ca0b | ||
|
|
7e127a4e52 | ||
|
|
b8093847dc | ||
|
|
8d1b56bcf5 | ||
|
|
5fb7fcf461 | ||
|
|
ee900ae7a3 | ||
|
|
3ca58e13dc | ||
|
|
aaa4e17454 | ||
|
|
a81d383f75 | ||
|
|
60019cf476 | ||
|
|
5a6a44cbc1 |
@@ -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[@]##*/}"
|
||||||
@@ -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
|
||||||
@@ -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: |
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
161
src/asm/lexer.c
161
src/asm/lexer.c
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1029
src/asm/parser.y
1029
src/asm/parser.y
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||||
|
|||||||
406
src/asm/rgbasm.5
406
src/asm/rgbasm.5
@@ -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 line‐based, 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 case‐insensitive;
|
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 Fixed‐point 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 "í", 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
|
||||||
least‐significant 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:
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
543
src/gbz80.7
543
src/gbz80.7
File diff suppressed because it is too large
Load Diff
@@ -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.
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(§ion, &zeroLenSection);
|
nextSection(§ion, &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 = §ions[type].banks[bank];
|
struct SortedSections const *sect = §ions[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);
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
10
src/rgbds.5
10
src/rgbds.5
@@ -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 32‐bit integer stored in little‐endian format.
|
is a 32-bit integer stored in little-endian format.
|
||||||
.Ar BYTE
|
.Ar BYTE
|
||||||
is an 8‐bit integer.
|
is an 8-bit integer.
|
||||||
.Ar STRING
|
.Ar STRING
|
||||||
is a 0‐terminated string of
|
is a 0-terminated string of
|
||||||
.Ar BYTE .
|
.Ar BYTE .
|
||||||
.Bd -literal
|
.Bd -literal
|
||||||
; Header
|
; Header
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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
29
test/asm/def.asm
Normal 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
5
test/asm/def.err
Normal 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
9
test/asm/def.out
Normal 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
5
test/asm/def.simple.err
Normal 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)!
|
||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
23
test/asm/elif-after-taken-if.asm
Normal file
23
test/asm/elif-after-taken-if.asm
Normal 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
|
||||||
2
test/asm/elif-after-taken-if.err
Normal file
2
test/asm/elif-after-taken-if.err
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
FATAL: elif-after-taken-if.asm(21):
|
||||||
|
Division by zero
|
||||||
2
test/asm/elif-after-taken-if.out
Normal file
2
test/asm/elif-after-taken-if.out
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
taken if
|
||||||
|
taken elif
|
||||||
@@ -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}"
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
@@ -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)!
|
||||||
|
|||||||
12
test/link/load-fragment-jr.asm
Normal file
12
test/link/load-fragment-jr.asm
Normal 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
|
||||||
0
test/link/load-fragment-jr.out
Normal file
0
test/link/load-fragment-jr.out
Normal file
BIN
test/link/load-fragment-jr.out.bin
Normal file
BIN
test/link/load-fragment-jr.out.bin
Normal file
Binary file not shown.
4
test/link/load-fragment/a.asm
Normal file
4
test/link/load-fragment/a.asm
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
SECTION "main", ROM0
|
||||||
|
LOAD FRAGMENT "test", SRAM
|
||||||
|
db 0
|
||||||
|
ENDL
|
||||||
6
test/link/load-fragment/b.asm
Normal file
6
test/link/load-fragment/b.asm
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
SECTION "SECTION2", ROM0
|
||||||
|
LOAD FRAGMENT "test", SRAM
|
||||||
|
jr Label
|
||||||
|
Label:
|
||||||
|
dw Label
|
||||||
|
ENDL
|
||||||
0
test/link/load-fragment/out.err
Normal file
0
test/link/load-fragment/out.err
Normal file
BIN
test/link/load-fragment/out.gb
Normal file
BIN
test/link/load-fragment/out.gb
Normal file
Binary file not shown.
@@ -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)!
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user