mirror of
https://github.com/gbdev/rgbds.git
synced 2026-10-06 04:05:38 +00:00
Merge branch 'master' into features-1.1.0
This commit is contained in:
commit
9b7d4bf613
17 files changed
+484
-388
No files matched your search
@@ -102,7 +102,7 @@ jobs:
|
|||||||
tee -a <<<"hash=${hash%-}" $GITHUB_OUTPUT
|
tee -a <<<"hash=${hash%-}" $GITHUB_OUTPUT
|
||||||
- name: Check test dependency repositories cache
|
- name: Check test dependency repositories cache
|
||||||
id: test-deps-cache
|
id: test-deps-cache
|
||||||
uses: actions/cache@v5
|
uses: actions/cache@v6
|
||||||
with:
|
with:
|
||||||
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
||||||
key: ${{ matrix.os }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
key: ${{ matrix.os }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
||||||
@@ -129,7 +129,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
.github/scripts/install-deps.sh macos lld
|
.github/scripts/install-deps.sh macos lld
|
||||||
- name: Cache library deps
|
- name: Cache library deps
|
||||||
uses: actions/cache@v5
|
uses: actions/cache@v6
|
||||||
with:
|
with:
|
||||||
path: ${{ env.DEPS_ROOT_DIR }}/*-tmp/
|
path: ${{ env.DEPS_ROOT_DIR }}/*-tmp/
|
||||||
key: dep-srcs-${{ hashFiles('cmake/deps.cmake') }}
|
key: dep-srcs-${{ hashFiles('cmake/deps.cmake') }}
|
||||||
@@ -164,7 +164,7 @@ jobs:
|
|||||||
tee -a <<<"hash=${hash%-}" $GITHUB_OUTPUT
|
tee -a <<<"hash=${hash%-}" $GITHUB_OUTPUT
|
||||||
- name: Check test dependency repositories cache
|
- name: Check test dependency repositories cache
|
||||||
id: test-deps-cache
|
id: test-deps-cache
|
||||||
uses: actions/cache@v5
|
uses: actions/cache@v6
|
||||||
with:
|
with:
|
||||||
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
||||||
key: macos-26-${{ steps.test-deps-cache-params.outputs.hash }}
|
key: macos-26-${{ steps.test-deps-cache-params.outputs.hash }}
|
||||||
@@ -191,7 +191,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
bash .github/scripts/install-deps.sh ${{ matrix.os }}
|
bash .github/scripts/install-deps.sh ${{ matrix.os }}
|
||||||
- name: Cache library deps
|
- name: Cache library deps
|
||||||
uses: actions/cache@v5
|
uses: actions/cache@v6
|
||||||
with:
|
with:
|
||||||
path: ${{ env.DEPS_ROOT_DIR }}/*-tmp/
|
path: ${{ env.DEPS_ROOT_DIR }}/*-tmp/
|
||||||
key: dep-srcs-${{ hashFiles('cmake/deps.cmake') }}
|
key: dep-srcs-${{ hashFiles('cmake/deps.cmake') }}
|
||||||
@@ -227,7 +227,7 @@ jobs:
|
|||||||
tee -a <<<"hash=${hash%-}" $GITHUB_OUTPUT
|
tee -a <<<"hash=${hash%-}" $GITHUB_OUTPUT
|
||||||
- name: Check test dependency repositories cache
|
- name: Check test dependency repositories cache
|
||||||
id: test-deps-cache
|
id: test-deps-cache
|
||||||
uses: actions/cache@v5
|
uses: actions/cache@v6
|
||||||
with:
|
with:
|
||||||
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
||||||
key: ${{ matrix.os }}-${{ matrix.bits }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
key: ${{ matrix.os }}-${{ matrix.bits }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
||||||
@@ -253,7 +253,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
.github/scripts/install-deps.sh ubuntu mingw${{ matrix.bits }}
|
.github/scripts/install-deps.sh ubuntu mingw${{ matrix.bits }}
|
||||||
- name: Cache library deps
|
- name: Cache library deps
|
||||||
uses: actions/cache@v5
|
uses: actions/cache@v6
|
||||||
with:
|
with:
|
||||||
path: ${{ env.DEPS_ROOT_DIR }}/*-tmp/
|
path: ${{ env.DEPS_ROOT_DIR }}/*-tmp/
|
||||||
key: dep-srcs-${{ hashFiles('cmake/deps.cmake') }}
|
key: dep-srcs-${{ hashFiles('cmake/deps.cmake') }}
|
||||||
@@ -321,7 +321,7 @@ jobs:
|
|||||||
tee -a <<<"hash=${hash%-}" $GITHUB_OUTPUT
|
tee -a <<<"hash=${hash%-}" $GITHUB_OUTPUT
|
||||||
- name: Check test dependency repositories cache
|
- name: Check test dependency repositories cache
|
||||||
id: test-deps-cache
|
id: test-deps-cache
|
||||||
uses: actions/cache@v5
|
uses: actions/cache@v6
|
||||||
with:
|
with:
|
||||||
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
||||||
key: mingw-${{ matrix.bits }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
key: mingw-${{ matrix.bits }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
||||||
@@ -405,7 +405,7 @@ jobs:
|
|||||||
- name: Check test dependency repositories cache
|
- name: Check test dependency repositories cache
|
||||||
if: matrix.arch == 'x86_64'
|
if: matrix.arch == 'x86_64'
|
||||||
id: test-deps-cache
|
id: test-deps-cache
|
||||||
uses: actions/cache@v5
|
uses: actions/cache@v6
|
||||||
with:
|
with:
|
||||||
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
|
||||||
key: cygwin-${{ matrix.bits }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
key: cygwin-${{ matrix.bits }}-${{ steps.test-deps-cache-params.outputs.hash }}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ enum StateFeature { STATE_EQU, STATE_VAR, STATE_EQUS, STATE_CHAR, STATE_MACRO, N
|
|||||||
|
|
||||||
void out_RegisterNode(std::shared_ptr<FileStackNode> node);
|
void out_RegisterNode(std::shared_ptr<FileStackNode> node);
|
||||||
void out_RegisterSymbol(Symbol &sym);
|
void out_RegisterSymbol(Symbol &sym);
|
||||||
void out_CreatePatch(uint32_t type, Expression const &expr, uint32_t ofs, uint32_t pcShift);
|
void out_CreatePatch(PatchType type, Expression const &expr, uint32_t ofs, uint32_t pcShift);
|
||||||
void out_CreateAssert(
|
void out_CreateAssert(
|
||||||
AssertionType type, Expression const &expr, std::string const &message, uint32_t ofs
|
AssertionType type, Expression const &expr, std::string const &message, uint32_t ofs
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ struct Patch {
|
|||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
Section *pcSection;
|
Section *pcSection;
|
||||||
uint32_t pcOffset;
|
uint32_t pcOffset;
|
||||||
uint8_t type;
|
PatchType type;
|
||||||
std::vector<uint8_t> rpn;
|
std::vector<uint8_t> rpn;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -11,10 +11,12 @@
|
|||||||
struct Symbol;
|
struct Symbol;
|
||||||
|
|
||||||
struct Assertion {
|
struct Assertion {
|
||||||
Patch patch; // Also used for its `.type`
|
Expression rpn;
|
||||||
|
uint32_t offset;
|
||||||
|
AssertionType type;
|
||||||
std::string message;
|
std::string message;
|
||||||
// This would be redundant with `patch.pcSection->fileSymbols`, but `section` is sometimes
|
// This would be redundant with `rpn.pcSection->fileSymbols`,
|
||||||
// `nullptr`!
|
// but `rpn.pcSection` is sometimes `nullptr`!
|
||||||
std::vector<Symbol> *fileSymbols;
|
std::vector<Symbol> *fileSymbols;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -15,15 +15,19 @@ struct FileStackNode;
|
|||||||
struct Section;
|
struct Section;
|
||||||
struct Symbol;
|
struct Symbol;
|
||||||
|
|
||||||
struct Patch {
|
struct Expression {
|
||||||
FileStackNode const *src;
|
FileStackNode const *src;
|
||||||
uint32_t lineNo;
|
uint32_t lineNo;
|
||||||
uint32_t offset;
|
|
||||||
Section const *pcSection;
|
Section const *pcSection;
|
||||||
uint32_t pcSectionID;
|
uint32_t pcSectionID;
|
||||||
uint32_t pcOffset;
|
uint32_t pcOffset;
|
||||||
|
std::vector<uint8_t> rpn;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Patch {
|
||||||
|
Expression rpn;
|
||||||
|
uint32_t offset;
|
||||||
PatchType type;
|
PatchType type;
|
||||||
std::vector<uint8_t> rpnExpression;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Section {
|
struct Section {
|
||||||
|
|||||||
@@ -67,6 +67,13 @@
|
|||||||
#define setmode(fd, mode) (0)
|
#define setmode(fd, mode) (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Windows has 32-bit `long`, which limits `fseek` and `ftell` to 2 GiB
|
||||||
|
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||||
|
#include <stdio.h> // IWYU pragma: export
|
||||||
|
#define fseek _fseeki64
|
||||||
|
#define ftell _ftelli64
|
||||||
|
#endif
|
||||||
|
|
||||||
// Apple has deprecated `sprintf` since Xcode 14 (for macOS 13), but we use it solely in
|
// Apple has deprecated `sprintf` since Xcode 14 (for macOS 13), but we use it solely in
|
||||||
// contexts where both the size of the buffer *and* max size of the printed string are
|
// contexts where both the size of the buffer *and* max size of the printed string are
|
||||||
// known statically, which GCC thus checks for.
|
// known statically, which GCC thus checks for.
|
||||||
|
|||||||
+16
-6
@@ -86,13 +86,23 @@ void FileStackNode::printBacktrace(uint32_t curLineNo) const {
|
|||||||
traceNodes.reserve(items.size());
|
traceNodes.reserve(items.size());
|
||||||
for (auto &[node, itemLineNo] : reversed(items)) {
|
for (auto &[node, itemLineNo] : reversed(items)) {
|
||||||
if (std::holds_alternative<std::vector<uint32_t>>(node->data)) {
|
if (std::holds_alternative<std::vector<uint32_t>>(node->data)) {
|
||||||
assume(!traceNodes.empty()); // REPT nodes use their parent's name
|
// REPT nodes use their first non-REPT parent's name (which may be a quiet node and
|
||||||
std::string reptName = traceNodes.back().first;
|
// thus not in `traceNodes`), followed by their REPT parents' iteration counts,
|
||||||
if (std::vector<uint32_t> const &nodeIters = node->iters(); !nodeIters.empty()) {
|
// followed by their own iteration count.
|
||||||
reptName.append(NODE_SEPARATOR REPT_NODE_PREFIX);
|
std::string reptName;
|
||||||
reptName.append(std::to_string(nodeIters.front()));
|
for (FileStackNode const *ancestor = node->parent.get();;
|
||||||
|
ancestor = ancestor->parent.get()) {
|
||||||
|
assume(ancestor != nullptr); // The top level context cannot be a REPT
|
||||||
|
if (ancestor->type != NODE_REPT) {
|
||||||
|
reptName.append(ancestor->name());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
traceNodes.emplace_back(reptName, itemLineNo);
|
}
|
||||||
|
for (uint32_t iter : reversed(node->iters())) {
|
||||||
|
reptName.append(NODE_SEPARATOR REPT_NODE_PREFIX);
|
||||||
|
reptName.append(std::to_string(iter));
|
||||||
|
}
|
||||||
|
traceNodes.emplace_back(std::move(reptName), itemLineNo);
|
||||||
} else {
|
} else {
|
||||||
traceNodes.emplace_back(node->name(), itemLineNo);
|
traceNodes.emplace_back(node->name(), itemLineNo);
|
||||||
}
|
}
|
||||||
|
|||||||
+43
-32
@@ -31,8 +31,13 @@
|
|||||||
#include "asm/warning.hpp"
|
#include "asm/warning.hpp"
|
||||||
|
|
||||||
struct Assertion {
|
struct Assertion {
|
||||||
Patch patch;
|
std::shared_ptr<FileStackNode> src;
|
||||||
Section *section;
|
uint32_t lineNo;
|
||||||
|
uint32_t offset;
|
||||||
|
Section *pcSection;
|
||||||
|
uint32_t pcOffset;
|
||||||
|
AssertionType type;
|
||||||
|
std::vector<uint8_t> rpn;
|
||||||
std::string message;
|
std::string message;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -66,19 +71,6 @@ void out_RegisterNode(std::shared_ptr<FileStackNode> node) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writePatch(Patch const &patch, FILE *file) {
|
|
||||||
assume(patch.src->ID != UINT32_MAX);
|
|
||||||
|
|
||||||
putLong(patch.src->ID, file);
|
|
||||||
putLong(patch.lineNo, file);
|
|
||||||
putLong(patch.offset, file);
|
|
||||||
putLong(patch.pcSection ? patch.pcSection->getID() : UINT32_MAX, file);
|
|
||||||
putLong(patch.pcOffset, file);
|
|
||||||
putc(patch.type, file);
|
|
||||||
putLong(patch.rpn.size(), file);
|
|
||||||
fwrite(patch.rpn.data(), 1, patch.rpn.size(), file);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void writeSection(Section const §, FILE *file) {
|
static void writeSection(Section const §, FILE *file) {
|
||||||
assume(sect.src->ID != UINT32_MAX);
|
assume(sect.src->ID != UINT32_MAX);
|
||||||
|
|
||||||
@@ -104,7 +96,16 @@ static void writeSection(Section const §, FILE *file) {
|
|||||||
putLong(sect.patches.size(), file);
|
putLong(sect.patches.size(), file);
|
||||||
|
|
||||||
for (Patch const &patch : sect.patches) {
|
for (Patch const &patch : sect.patches) {
|
||||||
writePatch(patch, file);
|
assume(patch.src->ID != UINT32_MAX);
|
||||||
|
|
||||||
|
putLong(patch.src->ID, file);
|
||||||
|
putLong(patch.lineNo, file);
|
||||||
|
putLong(patch.offset, file);
|
||||||
|
putLong(patch.pcSection ? patch.pcSection->getID() : UINT32_MAX, file);
|
||||||
|
putLong(patch.pcOffset, file);
|
||||||
|
putc(patch.type, file);
|
||||||
|
putLong(patch.rpn.size(), file);
|
||||||
|
fwrite(patch.rpn.data(), 1, patch.rpn.size(), file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,7 +136,11 @@ void out_RegisterSymbol(Symbol &sym) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void initPatch(Patch &patch, uint32_t type, Expression const &expr, uint32_t ofs) {
|
void out_CreatePatch(PatchType type, Expression const &expr, uint32_t ofs, uint32_t pcShift) {
|
||||||
|
assume(sect_GetSymbolSection() != nullptr);
|
||||||
|
|
||||||
|
// Add the patch to the list
|
||||||
|
Patch &patch = *sect_AddOutputPatch();
|
||||||
patch.type = type;
|
patch.type = type;
|
||||||
patch.src = fstk_GetFileStack();
|
patch.src = fstk_GetFileStack();
|
||||||
// All patches are assumed to eventually be written, so the file stack node is registered
|
// All patches are assumed to eventually be written, so the file stack node is registered
|
||||||
@@ -145,18 +150,8 @@ static void initPatch(Patch &patch, uint32_t type, Expression const &expr, uint3
|
|||||||
patch.pcSection = sect_GetSymbolSection();
|
patch.pcSection = sect_GetSymbolSection();
|
||||||
patch.pcOffset = sect_GetSymbolOffset();
|
patch.pcOffset = sect_GetSymbolOffset();
|
||||||
expr.encode(patch.rpn);
|
expr.encode(patch.rpn);
|
||||||
}
|
// If the patch had a quantity of bytes output before it, PC is not at the patch's location,
|
||||||
|
// but at the location before those bytes.
|
||||||
void out_CreatePatch(uint32_t type, Expression const &expr, uint32_t ofs, uint32_t pcShift) {
|
|
||||||
// Add the patch to the list
|
|
||||||
assume(sect_GetSymbolSection() != nullptr);
|
|
||||||
Patch &patch = *sect_AddOutputPatch();
|
|
||||||
|
|
||||||
initPatch(patch, type, expr, ofs);
|
|
||||||
|
|
||||||
// If the patch had a quantity of bytes output before it,
|
|
||||||
// PC is not at the patch's location, but at the location
|
|
||||||
// before those bytes.
|
|
||||||
patch.pcOffset -= pcShift;
|
patch.pcOffset -= pcShift;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,13 +163,29 @@ void out_CreateAssert(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Assertion &assertion = assertions.emplace_front();
|
Assertion &assertion = assertions.emplace_front();
|
||||||
|
assertion.type = type;
|
||||||
initPatch(assertion.patch, type, expr, ofs);
|
assertion.src = fstk_GetFileStack();
|
||||||
|
// All assertions are assumed to eventually be written, so the file stack node is registered
|
||||||
|
out_RegisterNode(assertion.src);
|
||||||
|
assertion.lineNo = lexer_GetLineNo();
|
||||||
|
assertion.offset = ofs;
|
||||||
|
assertion.pcSection = sect_GetSymbolSection();
|
||||||
|
assertion.pcOffset = sect_GetSymbolOffset();
|
||||||
|
expr.encode(assertion.rpn);
|
||||||
assertion.message = message;
|
assertion.message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writeAssert(Assertion const &assert, FILE *file) {
|
static void writeAssert(Assertion const &assert, FILE *file) {
|
||||||
writePatch(assert.patch, file);
|
assume(assert.src->ID != UINT32_MAX);
|
||||||
|
|
||||||
|
putLong(assert.src->ID, file);
|
||||||
|
putLong(assert.lineNo, file);
|
||||||
|
putLong(assert.offset, file);
|
||||||
|
putLong(assert.pcSection ? assert.pcSection->getID() : UINT32_MAX, file);
|
||||||
|
putLong(assert.pcOffset, file);
|
||||||
|
putc(assert.type, file);
|
||||||
|
putLong(assert.rpn.size(), file);
|
||||||
|
fwrite(assert.rpn.data(), 1, assert.rpn.size(), file);
|
||||||
putString(assert.message, file);
|
putString(assert.message, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -287,24 +287,6 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class RawTiles {
|
|
||||||
// A tile which only contains indices into the image's global palette
|
|
||||||
class RawTile {
|
|
||||||
std::array<std::array<size_t, 8>, 8> _pixelIndices{};
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Not super clean, but it's closer to matrix notation
|
|
||||||
size_t &operator()(size_t x, size_t y) { return _pixelIndices[y][x]; }
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<RawTile> _tiles;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Creates a new raw tile, and returns a reference to it so it can be filled in
|
|
||||||
RawTile &newTile() { return _tiles.emplace_back(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AttrmapEntry {
|
struct AttrmapEntry {
|
||||||
// This field can either be a color set ID, or `transparent` to indicate that the
|
// This field can either be a color set ID, or `transparent` to indicate that the
|
||||||
// corresponding tile is fully transparent. If you are looking to get the palette ID for this
|
// corresponding tile is fully transparent. If you are looking to get the palette ID for this
|
||||||
|
|||||||
+16
-6
@@ -41,13 +41,23 @@ void FileStackNode::printBacktrace(uint32_t curLineNo) const {
|
|||||||
traceNodes.reserve(items.size());
|
traceNodes.reserve(items.size());
|
||||||
for (auto &[node, itemLineNo] : reversed(items)) {
|
for (auto &[node, itemLineNo] : reversed(items)) {
|
||||||
if (std::holds_alternative<std::vector<uint32_t>>(node->data)) {
|
if (std::holds_alternative<std::vector<uint32_t>>(node->data)) {
|
||||||
assume(!traceNodes.empty()); // REPT nodes use their parent's name
|
// REPT nodes use their first non-REPT parent's name (which may be a quiet node and
|
||||||
std::string reptName = traceNodes.back().first;
|
// thus not in `traceNodes`), followed by their REPT parents' iteration counts,
|
||||||
if (std::vector<uint32_t> const &nodeIters = node->iters(); !nodeIters.empty()) {
|
// followed by their own iteration count.
|
||||||
reptName.append(NODE_SEPARATOR REPT_NODE_PREFIX);
|
std::string reptName;
|
||||||
reptName.append(std::to_string(nodeIters.back()));
|
for (FileStackNode const *ancestor = node->parent;; ancestor = ancestor->parent) {
|
||||||
|
// The top level context cannot be a REPT (guaranteed by `readFileStackNode`)
|
||||||
|
assume(ancestor != nullptr);
|
||||||
|
if (ancestor->type != NODE_REPT) {
|
||||||
|
reptName.append(ancestor->name());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
traceNodes.emplace_back(reptName, itemLineNo);
|
}
|
||||||
|
for (uint32_t iter : node->iters()) {
|
||||||
|
reptName.append(NODE_SEPARATOR REPT_NODE_PREFIX);
|
||||||
|
reptName.append(std::to_string(iter));
|
||||||
|
}
|
||||||
|
traceNodes.emplace_back(std::move(reptName), itemLineNo);
|
||||||
} else {
|
} else {
|
||||||
traceNodes.emplace_back(node->name(), itemLineNo);
|
traceNodes.emplace_back(node->name(), itemLineNo);
|
||||||
}
|
}
|
||||||
|
|||||||
+179
-123
@@ -205,111 +205,6 @@ static void readSymbol(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reads a patch from a file.
|
|
||||||
static void readPatch(
|
|
||||||
FILE *file,
|
|
||||||
Patch &patch,
|
|
||||||
char const *fileName,
|
|
||||||
std::string const §Name,
|
|
||||||
uint32_t patchID,
|
|
||||||
std::vector<FileStackNode> const &fileNodes
|
|
||||||
) {
|
|
||||||
uint32_t nodeID;
|
|
||||||
tryReadLong(
|
|
||||||
nodeID,
|
|
||||||
file,
|
|
||||||
"%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s node ID: %s",
|
|
||||||
fileName,
|
|
||||||
sectName.c_str(),
|
|
||||||
patchID
|
|
||||||
);
|
|
||||||
if (nodeID >= fileNodes.size()) {
|
|
||||||
fatal(
|
|
||||||
"%s: \"%s\"'s patch #%" PRIu32 " has invalid node ID #%" PRIu32,
|
|
||||||
fileName,
|
|
||||||
sectName.c_str(),
|
|
||||||
patchID,
|
|
||||||
nodeID
|
|
||||||
);
|
|
||||||
}
|
|
||||||
patch.src = &fileNodes[nodeID];
|
|
||||||
|
|
||||||
tryReadLong(
|
|
||||||
patch.lineNo,
|
|
||||||
file,
|
|
||||||
"%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s line number: %s",
|
|
||||||
fileName,
|
|
||||||
sectName.c_str(),
|
|
||||||
patchID
|
|
||||||
);
|
|
||||||
tryReadLong(
|
|
||||||
patch.offset,
|
|
||||||
file,
|
|
||||||
"%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s offset: %s",
|
|
||||||
fileName,
|
|
||||||
sectName.c_str(),
|
|
||||||
patchID
|
|
||||||
);
|
|
||||||
tryReadLong(
|
|
||||||
patch.pcSectionID,
|
|
||||||
file,
|
|
||||||
"%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
|
|
||||||
fileName,
|
|
||||||
sectName.c_str(),
|
|
||||||
patchID
|
|
||||||
);
|
|
||||||
tryReadLong(
|
|
||||||
patch.pcOffset,
|
|
||||||
file,
|
|
||||||
"%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
|
|
||||||
fileName,
|
|
||||||
sectName.c_str(),
|
|
||||||
patchID
|
|
||||||
);
|
|
||||||
|
|
||||||
uint8_t type;
|
|
||||||
tryGetc(
|
|
||||||
type,
|
|
||||||
file,
|
|
||||||
"%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s type: %s",
|
|
||||||
fileName,
|
|
||||||
sectName.c_str(),
|
|
||||||
patchID
|
|
||||||
);
|
|
||||||
if (type >= PATCHTYPE_INVALID) {
|
|
||||||
fatal(
|
|
||||||
"%s: \"%s\"'s patch #%" PRIu32 " has unknown type 0x%02x",
|
|
||||||
fileName,
|
|
||||||
sectName.c_str(),
|
|
||||||
patchID,
|
|
||||||
type
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
patch.type = PatchType(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t rpnSize;
|
|
||||||
tryReadLong(
|
|
||||||
rpnSize,
|
|
||||||
file,
|
|
||||||
"%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN size: %s",
|
|
||||||
fileName,
|
|
||||||
sectName.c_str(),
|
|
||||||
patchID
|
|
||||||
);
|
|
||||||
|
|
||||||
patch.rpnExpression.resize(rpnSize);
|
|
||||||
if (fread(patch.rpnExpression.data(), 1, rpnSize, file) != rpnSize) {
|
|
||||||
fatal(
|
|
||||||
"%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN expression: %s",
|
|
||||||
fileName,
|
|
||||||
sectName.c_str(),
|
|
||||||
patchID,
|
|
||||||
feof(file) ? "Unexpected end of file" : strerror(errno)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reads a section from a file.
|
// Reads a section from a file.
|
||||||
static void readSection(
|
static void readSection(
|
||||||
FILE *file, Section §ion, char const *fileName, std::vector<FileStackNode> const &fileNodes
|
FILE *file, Section §ion, char const *fileName, std::vector<FileStackNode> const &fileNodes
|
||||||
@@ -386,7 +281,10 @@ static void readSection(
|
|||||||
}
|
}
|
||||||
section.alignOfs = tmp;
|
section.alignOfs = tmp;
|
||||||
|
|
||||||
if (sectTypeHasData(section.type)) {
|
if (!sectTypeHasData(section.type)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (section.size) {
|
if (section.size) {
|
||||||
section.data.resize(section.size);
|
section.data.resize(section.size);
|
||||||
if (fread(section.data.data(), 1, section.size, file) != section.size) {
|
if (fread(section.data.data(), 1, section.size, file) != section.size) {
|
||||||
@@ -409,8 +307,101 @@ static void readSection(
|
|||||||
);
|
);
|
||||||
|
|
||||||
section.patches.resize(nbPatches);
|
section.patches.resize(nbPatches);
|
||||||
for (uint32_t i = 0; i < nbPatches; ++i) {
|
for (uint32_t patchID = 0; patchID < nbPatches; ++patchID) {
|
||||||
readPatch(file, section.patches[i], fileName, section.name, i, fileNodes);
|
Patch &patch = section.patches[patchID];
|
||||||
|
|
||||||
|
tryReadLong(
|
||||||
|
nodeID,
|
||||||
|
file,
|
||||||
|
"%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s node ID: %s",
|
||||||
|
fileName,
|
||||||
|
section.name.c_str(),
|
||||||
|
patchID
|
||||||
|
);
|
||||||
|
if (nodeID >= fileNodes.size()) {
|
||||||
|
fatal(
|
||||||
|
"%s: \"%s\"'s patch #%" PRIu32 " has invalid node ID #%" PRIu32,
|
||||||
|
fileName,
|
||||||
|
section.name.c_str(),
|
||||||
|
patchID,
|
||||||
|
nodeID
|
||||||
|
);
|
||||||
|
}
|
||||||
|
patch.rpn.src = &fileNodes[nodeID];
|
||||||
|
|
||||||
|
tryReadLong(
|
||||||
|
patch.rpn.lineNo,
|
||||||
|
file,
|
||||||
|
"%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s line number: %s",
|
||||||
|
fileName,
|
||||||
|
section.name.c_str(),
|
||||||
|
patchID
|
||||||
|
);
|
||||||
|
tryReadLong(
|
||||||
|
patch.offset,
|
||||||
|
file,
|
||||||
|
"%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s offset: %s",
|
||||||
|
fileName,
|
||||||
|
section.name.c_str(),
|
||||||
|
patchID
|
||||||
|
);
|
||||||
|
tryReadLong(
|
||||||
|
patch.rpn.pcSectionID,
|
||||||
|
file,
|
||||||
|
"%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
|
||||||
|
fileName,
|
||||||
|
section.name.c_str(),
|
||||||
|
patchID
|
||||||
|
);
|
||||||
|
tryReadLong(
|
||||||
|
patch.rpn.pcOffset,
|
||||||
|
file,
|
||||||
|
"%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
|
||||||
|
fileName,
|
||||||
|
section.name.c_str(),
|
||||||
|
patchID
|
||||||
|
);
|
||||||
|
|
||||||
|
uint8_t type;
|
||||||
|
tryGetc(
|
||||||
|
type,
|
||||||
|
file,
|
||||||
|
"%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s type: %s",
|
||||||
|
fileName,
|
||||||
|
section.name.c_str(),
|
||||||
|
patchID
|
||||||
|
);
|
||||||
|
if (type >= PATCHTYPE_INVALID) {
|
||||||
|
fatal(
|
||||||
|
"%s: \"%s\"'s patch #%" PRIu32 " has unknown type 0x%02x",
|
||||||
|
fileName,
|
||||||
|
section.name.c_str(),
|
||||||
|
patchID,
|
||||||
|
type
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
patch.type = PatchType(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t rpnSize;
|
||||||
|
tryReadLong(
|
||||||
|
rpnSize,
|
||||||
|
file,
|
||||||
|
"%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN size: %s",
|
||||||
|
fileName,
|
||||||
|
section.name.c_str(),
|
||||||
|
patchID
|
||||||
|
);
|
||||||
|
|
||||||
|
patch.rpn.rpn.resize(rpnSize);
|
||||||
|
if (fread(patch.rpn.rpn.data(), 1, rpnSize, file) != rpnSize) {
|
||||||
|
fatal(
|
||||||
|
"%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN expression: %s",
|
||||||
|
fileName,
|
||||||
|
section.name.c_str(),
|
||||||
|
patchID,
|
||||||
|
feof(file) ? "Unexpected end of file" : strerror(errno)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -423,11 +414,76 @@ static void readAssertion(
|
|||||||
uint32_t assertID,
|
uint32_t assertID,
|
||||||
std::vector<FileStackNode> const &fileNodes
|
std::vector<FileStackNode> const &fileNodes
|
||||||
) {
|
) {
|
||||||
std::string assertName("Assertion #");
|
uint32_t nodeID;
|
||||||
|
tryReadLong(
|
||||||
|
nodeID, file, "%s: Cannot read assertion #%" PRIu32 "'s node ID: %s", fileName, assertID
|
||||||
|
);
|
||||||
|
if (nodeID >= fileNodes.size()) {
|
||||||
|
fatal(
|
||||||
|
"%s: assertion #%" PRIu32 " has invalid node ID #%" PRIu32, fileName, assertID, nodeID
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assert.rpn.src = &fileNodes[nodeID];
|
||||||
|
|
||||||
assertName += std::to_string(assertID);
|
tryReadLong(
|
||||||
readPatch(file, assert.patch, fileName, assertName, 0, fileNodes);
|
assert.rpn.lineNo,
|
||||||
tryReadString(assert.message, file, "%s: Cannot read assertion's message: %s", fileName);
|
file,
|
||||||
|
"%s: Cannot read assertion #%" PRIu32 "'s line number: %s",
|
||||||
|
fileName,
|
||||||
|
assertID
|
||||||
|
);
|
||||||
|
tryReadLong(
|
||||||
|
assert.offset,
|
||||||
|
file,
|
||||||
|
"%s: Cannot read assertion #%" PRIu32 "'s offset: %s",
|
||||||
|
fileName,
|
||||||
|
assertID
|
||||||
|
);
|
||||||
|
tryReadLong(
|
||||||
|
assert.rpn.pcSectionID,
|
||||||
|
file,
|
||||||
|
"%s: Cannot read assertion #%" PRIu32 "'s PC offset: %s",
|
||||||
|
fileName,
|
||||||
|
assertID
|
||||||
|
);
|
||||||
|
tryReadLong(
|
||||||
|
assert.rpn.pcOffset,
|
||||||
|
file,
|
||||||
|
"%s: Cannot read assertion #%" PRIu32 "'s PC offset: %s",
|
||||||
|
fileName,
|
||||||
|
assertID
|
||||||
|
);
|
||||||
|
|
||||||
|
uint8_t type;
|
||||||
|
tryGetc(type, file, "%s: Cannot read assertion #%" PRIu32 "'s type: %s", fileName, assertID);
|
||||||
|
if (type > ASSERT_FATAL) {
|
||||||
|
fatal("%s: assertion #%" PRIu32 " has unknown type 0x%02x", fileName, assertID, type);
|
||||||
|
} else {
|
||||||
|
assert.type = AssertionType(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t rpnSize;
|
||||||
|
tryReadLong(
|
||||||
|
rpnSize, file, "%s: Cannot read assertion #%" PRIu32 "'s RPN size: %s", fileName, assertID
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.rpn.rpn.resize(rpnSize);
|
||||||
|
if (fread(assert.rpn.rpn.data(), 1, rpnSize, file) != rpnSize) {
|
||||||
|
fatal(
|
||||||
|
"%s: Cannot read assertion #%" PRIu32 "'s RPN expression: %s",
|
||||||
|
fileName,
|
||||||
|
assertID,
|
||||||
|
feof(file) ? "Unexpected end of file" : strerror(errno)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
tryReadString(
|
||||||
|
assert.message,
|
||||||
|
file,
|
||||||
|
"%s: Cannot read assertion #%" PRIu32 "'s message: %s",
|
||||||
|
fileName,
|
||||||
|
assertID
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void obj_ReadFile(std::string const &filePath, size_t fileID) {
|
void obj_ReadFile(std::string const &filePath, size_t fileID) {
|
||||||
@@ -557,17 +613,17 @@ void obj_ReadFile(std::string const &filePath, size_t fileID) {
|
|||||||
|
|
||||||
readAssertion(file, assertion, fileName, i, nodes[fileID]);
|
readAssertion(file, assertion, fileName, i, nodes[fileID]);
|
||||||
|
|
||||||
if (assertion.patch.pcSectionID == UINT32_MAX) {
|
if (assertion.rpn.pcSectionID == UINT32_MAX) {
|
||||||
assertion.patch.pcSection = nullptr;
|
assertion.rpn.pcSection = nullptr;
|
||||||
} else if (assertion.patch.pcSectionID >= fileSections.size()) {
|
} else if (assertion.rpn.pcSectionID >= fileSections.size()) {
|
||||||
fatal(
|
fatal(
|
||||||
"%s: Assertion #%" PRIu32 "'s patch has invalid section ID #%" PRIu32,
|
"%s: Assertion #%" PRIu32 " has invalid section ID #%" PRIu32,
|
||||||
fileName,
|
fileName,
|
||||||
i,
|
i,
|
||||||
assertion.patch.pcSectionID
|
assertion.rpn.pcSectionID
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
assertion.patch.pcSection = fileSections[assertion.patch.pcSectionID].get();
|
assertion.rpn.pcSection = fileSections[assertion.rpn.pcSectionID].get();
|
||||||
}
|
}
|
||||||
|
|
||||||
assertion.fileSymbols = &fileSymbols;
|
assertion.fileSymbols = &fileSymbols;
|
||||||
@@ -579,18 +635,18 @@ void obj_ReadFile(std::string const &filePath, size_t fileID) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < sect->patches.size(); ++i) {
|
for (size_t i = 0; i < sect->patches.size(); ++i) {
|
||||||
if (Patch &patch = sect->patches[i]; patch.pcSectionID == UINT32_MAX) {
|
if (Expression &rpn = sect->patches[i].rpn; rpn.pcSectionID == UINT32_MAX) {
|
||||||
patch.pcSection = nullptr;
|
rpn.pcSection = nullptr;
|
||||||
} else if (patch.pcSectionID >= fileSections.size()) {
|
} else if (rpn.pcSectionID >= fileSections.size()) {
|
||||||
fatal(
|
fatal(
|
||||||
"%s: \"%s\"'s patch #%zu has invalid section ID #%" PRIu32,
|
"%s: \"%s\"'s patch #%zu has invalid section ID #%" PRIu32,
|
||||||
fileName,
|
fileName,
|
||||||
sect->name.c_str(),
|
sect->name.c_str(),
|
||||||
i,
|
i,
|
||||||
patch.pcSectionID
|
rpn.pcSectionID
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
patch.pcSection = fileSections[patch.pcSectionID].get();
|
rpn.pcSection = fileSections[rpn.pcSectionID].get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+130
-133
@@ -36,11 +36,11 @@ static void pushRPN(int32_t value, bool comesFromError) {
|
|||||||
// has popped any values with the error flag set.
|
// has popped any values with the error flag set.
|
||||||
static bool isError = false;
|
static bool isError = false;
|
||||||
|
|
||||||
#define diagnosticAt(patch, id, ...) \
|
#define diagnosticAt(expr, id, ...) \
|
||||||
do { \
|
do { \
|
||||||
bool errorDiag = warnings.getWarningBehavior(id) == WarningBehavior::ERROR; \
|
bool errorDiag = warnings.getWarningBehavior(id) == WarningBehavior::ERROR; \
|
||||||
if (!isError || !errorDiag) { \
|
if (!isError || !errorDiag) { \
|
||||||
warningAt(patch, id, __VA_ARGS__); \
|
warningAt(expr, id, __VA_ARGS__); \
|
||||||
} \
|
} \
|
||||||
if (errorDiag) { \
|
if (errorDiag) { \
|
||||||
isError = true; \
|
isError = true; \
|
||||||
@@ -60,9 +60,9 @@ static bool isError = false;
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
static int32_t popRPN(Patch const &patch) {
|
static int32_t popRPN(Expression const &expr) {
|
||||||
if (rpnStack.empty()) {
|
if (rpnStack.empty()) {
|
||||||
fatalAt(patch, "Internal error, RPN stack empty");
|
fatalAt(expr, "Internal error, RPN stack empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
RPNStackEntry entry = rpnStack.front();
|
RPNStackEntry entry = rpnStack.front();
|
||||||
@@ -74,12 +74,12 @@ static int32_t popRPN(Patch const &patch) {
|
|||||||
|
|
||||||
// RPN operators
|
// RPN operators
|
||||||
|
|
||||||
static uint32_t getRPNByte(uint8_t const *&expression, int32_t &size, Patch const &patch) {
|
static uint32_t getRPNByte(uint8_t const *&rpnBuffer, int32_t &size, Expression const &expr) {
|
||||||
if (!size--) {
|
if (!size--) {
|
||||||
fatalAt(patch, "Internal error, RPN expression overread");
|
fatalAt(expr, "Internal error, RPN expression overread");
|
||||||
}
|
}
|
||||||
|
|
||||||
return *expression++;
|
return *rpnBuffer++;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Symbol const *getSymbol(std::vector<Symbol> const &symbolList, uint32_t index) {
|
static Symbol const *getSymbol(std::vector<Symbol> const &symbolList, uint32_t index) {
|
||||||
@@ -95,15 +95,15 @@ static Symbol const *getSymbol(std::vector<Symbol> const &symbolList, uint32_t i
|
|||||||
return &symbol;
|
return &symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute a patch's value from its RPN string.
|
// Compute an Expression's value from its RPN string.
|
||||||
static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fileSymbols) {
|
static int32_t computeRPNExpr(Expression const &expr, std::vector<Symbol> const &fileSymbols) {
|
||||||
uint8_t const *expression = patch.rpnExpression.data();
|
uint8_t const *rpnBuffer = expr.rpn.data();
|
||||||
int32_t size = static_cast<int32_t>(patch.rpnExpression.size());
|
int32_t size = static_cast<int32_t>(expr.rpn.size());
|
||||||
|
|
||||||
rpnStack.clear();
|
rpnStack.clear();
|
||||||
|
|
||||||
while (size > 0) {
|
while (size > 0) {
|
||||||
RPNCommand command = static_cast<RPNCommand>(getRPNByte(expression, size, patch));
|
RPNCommand command = static_cast<RPNCommand>(getRPNByte(rpnBuffer, size, expr));
|
||||||
|
|
||||||
isError = false;
|
isError = false;
|
||||||
|
|
||||||
@@ -114,24 +114,24 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
int32_t value;
|
int32_t value;
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case RPN_ADD:
|
case RPN_ADD:
|
||||||
value = op_add(popRPN(patch), popRPN(patch));
|
value = op_add(popRPN(expr), popRPN(expr));
|
||||||
break;
|
break;
|
||||||
case RPN_SUB:
|
case RPN_SUB:
|
||||||
value = popRPN(patch);
|
value = popRPN(expr);
|
||||||
value = op_sub(popRPN(patch), value);
|
value = op_sub(popRPN(expr), value);
|
||||||
break;
|
break;
|
||||||
case RPN_MUL:
|
case RPN_MUL:
|
||||||
value = op_mul(popRPN(patch), popRPN(patch));
|
value = op_mul(popRPN(expr), popRPN(expr));
|
||||||
break;
|
break;
|
||||||
case RPN_DIV:
|
case RPN_DIV:
|
||||||
value = popRPN(patch);
|
value = popRPN(expr);
|
||||||
if (value == 0) {
|
if (value == 0) {
|
||||||
firstErrorAt(patch, "Division by 0");
|
firstErrorAt(expr, "Division by 0");
|
||||||
popRPN(patch);
|
popRPN(expr);
|
||||||
value = 0;
|
value = 0;
|
||||||
} else if (int32_t lval = popRPN(patch); lval == INT32_MIN && value == -1) {
|
} else if (int32_t lval = popRPN(expr); lval == INT32_MIN && value == -1) {
|
||||||
diagnosticAt(
|
diagnosticAt(
|
||||||
patch,
|
expr,
|
||||||
WARNING_DIV,
|
WARNING_DIV,
|
||||||
"Division of %" PRId32 " by -1 yields %" PRId32,
|
"Division of %" PRId32 " by -1 yields %" PRId32,
|
||||||
INT32_MIN,
|
INT32_MIN,
|
||||||
@@ -143,156 +143,156 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RPN_MOD:
|
case RPN_MOD:
|
||||||
value = popRPN(patch);
|
value = popRPN(expr);
|
||||||
if (value == 0) {
|
if (value == 0) {
|
||||||
firstErrorAt(patch, "Modulo by 0");
|
firstErrorAt(expr, "Modulo by 0");
|
||||||
popRPN(patch);
|
popRPN(expr);
|
||||||
value = 0;
|
value = 0;
|
||||||
} else if (int32_t lval = popRPN(patch); lval == INT32_MIN && value == -1) {
|
} else if (int32_t lval = popRPN(expr); lval == INT32_MIN && value == -1) {
|
||||||
diagnosticAt(patch, WARNING_DIV, "Modulo of %" PRId32 " by -1 yields 0", INT32_MIN);
|
diagnosticAt(expr, WARNING_DIV, "Modulo of %" PRId32 " by -1 yields 0", INT32_MIN);
|
||||||
value = 0;
|
value = 0;
|
||||||
} else {
|
} else {
|
||||||
value = op_modulo(lval, value);
|
value = op_modulo(lval, value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RPN_NEG:
|
case RPN_NEG:
|
||||||
value = op_neg(popRPN(patch));
|
value = op_neg(popRPN(expr));
|
||||||
break;
|
break;
|
||||||
case RPN_EXP:
|
case RPN_EXP:
|
||||||
value = popRPN(patch);
|
value = popRPN(expr);
|
||||||
if (value < 0) {
|
if (value < 0) {
|
||||||
firstErrorAt(patch, "Exponent by negative value %" PRId32, value);
|
firstErrorAt(expr, "Exponent by negative value %" PRId32, value);
|
||||||
popRPN(patch);
|
popRPN(expr);
|
||||||
value = 0;
|
value = 0;
|
||||||
} else {
|
} else {
|
||||||
value = op_exponent(popRPN(patch), value);
|
value = op_exponent(popRPN(expr), value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_HIGH:
|
case RPN_HIGH:
|
||||||
value = op_high(popRPN(patch));
|
value = op_high(popRPN(expr));
|
||||||
break;
|
break;
|
||||||
case RPN_LOW:
|
case RPN_LOW:
|
||||||
value = op_low(popRPN(patch));
|
value = op_low(popRPN(expr));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_BITWIDTH:
|
case RPN_BITWIDTH:
|
||||||
value = op_bitwidth(popRPN(patch));
|
value = op_bitwidth(popRPN(expr));
|
||||||
break;
|
break;
|
||||||
case RPN_TZCOUNT:
|
case RPN_TZCOUNT:
|
||||||
value = op_tzcount(popRPN(patch));
|
value = op_tzcount(popRPN(expr));
|
||||||
break;
|
break;
|
||||||
case RPN_POPCOUNT:
|
case RPN_POPCOUNT:
|
||||||
value = op_popcount(popRPN(patch));
|
value = op_popcount(popRPN(expr));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_OR:
|
case RPN_OR:
|
||||||
value = popRPN(patch) | popRPN(patch);
|
value = popRPN(expr) | popRPN(expr);
|
||||||
break;
|
break;
|
||||||
case RPN_AND:
|
case RPN_AND:
|
||||||
value = popRPN(patch) & popRPN(patch);
|
value = popRPN(expr) & popRPN(expr);
|
||||||
break;
|
break;
|
||||||
case RPN_XOR:
|
case RPN_XOR:
|
||||||
value = popRPN(patch) ^ popRPN(patch);
|
value = popRPN(expr) ^ popRPN(expr);
|
||||||
break;
|
break;
|
||||||
case RPN_NOT:
|
case RPN_NOT:
|
||||||
value = ~popRPN(patch);
|
value = ~popRPN(expr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_LOGAND:
|
case RPN_LOGAND:
|
||||||
value = popRPN(patch);
|
value = popRPN(expr);
|
||||||
value = popRPN(patch) && value;
|
value = popRPN(expr) && value;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGOR:
|
case RPN_LOGOR:
|
||||||
value = popRPN(patch);
|
value = popRPN(expr);
|
||||||
value = popRPN(patch) || value;
|
value = popRPN(expr) || value;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGNOT:
|
case RPN_LOGNOT:
|
||||||
value = !popRPN(patch);
|
value = !popRPN(expr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_LOGEQ:
|
case RPN_LOGEQ:
|
||||||
value = popRPN(patch) == popRPN(patch);
|
value = popRPN(expr) == popRPN(expr);
|
||||||
break;
|
break;
|
||||||
case RPN_LOGNE:
|
case RPN_LOGNE:
|
||||||
value = popRPN(patch) != popRPN(patch);
|
value = popRPN(expr) != popRPN(expr);
|
||||||
break;
|
break;
|
||||||
case RPN_LOGGT:
|
case RPN_LOGGT:
|
||||||
value = popRPN(patch);
|
value = popRPN(expr);
|
||||||
value = popRPN(patch) > value;
|
value = popRPN(expr) > value;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGLT:
|
case RPN_LOGLT:
|
||||||
value = popRPN(patch);
|
value = popRPN(expr);
|
||||||
value = popRPN(patch) < value;
|
value = popRPN(expr) < value;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGGE:
|
case RPN_LOGGE:
|
||||||
value = popRPN(patch);
|
value = popRPN(expr);
|
||||||
value = popRPN(patch) >= value;
|
value = popRPN(expr) >= value;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGLE:
|
case RPN_LOGLE:
|
||||||
value = popRPN(patch);
|
value = popRPN(expr);
|
||||||
value = popRPN(patch) <= value;
|
value = popRPN(expr) <= value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_SHL:
|
case RPN_SHL:
|
||||||
value = popRPN(patch);
|
value = popRPN(expr);
|
||||||
if (value < 0) {
|
if (value < 0) {
|
||||||
diagnosticAt(
|
diagnosticAt(
|
||||||
patch, WARNING_SHIFT_AMOUNT, "Shifting left by negative amount %" PRId32, value
|
expr, WARNING_SHIFT_AMOUNT, "Shifting left by negative amount %" PRId32, value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (value >= 32) {
|
if (value >= 32) {
|
||||||
diagnosticAt(
|
diagnosticAt(
|
||||||
patch, WARNING_SHIFT_AMOUNT, "Shifting left by large amount %" PRId32, value
|
expr, WARNING_SHIFT_AMOUNT, "Shifting left by large amount %" PRId32, value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
value = op_shift_left(popRPN(patch), value);
|
value = op_shift_left(popRPN(expr), value);
|
||||||
break;
|
break;
|
||||||
case RPN_SHR: {
|
case RPN_SHR: {
|
||||||
value = popRPN(patch);
|
value = popRPN(expr);
|
||||||
int32_t lval = popRPN(patch);
|
int32_t lval = popRPN(expr);
|
||||||
if (lval < 0) {
|
if (lval < 0) {
|
||||||
diagnosticAt(patch, WARNING_SHIFT, "Shifting right negative value %" PRId32, lval);
|
diagnosticAt(expr, WARNING_SHIFT, "Shifting right negative value %" PRId32, lval);
|
||||||
}
|
}
|
||||||
if (value < 0) {
|
if (value < 0) {
|
||||||
diagnosticAt(
|
diagnosticAt(
|
||||||
patch, WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32, value
|
expr, WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32, value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (value >= 32) {
|
if (value >= 32) {
|
||||||
diagnosticAt(
|
diagnosticAt(
|
||||||
patch, WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32, value
|
expr, WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32, value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
value = op_shift_right(lval, value);
|
value = op_shift_right(lval, value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RPN_USHR:
|
case RPN_USHR:
|
||||||
value = popRPN(patch);
|
value = popRPN(expr);
|
||||||
if (value < 0) {
|
if (value < 0) {
|
||||||
diagnosticAt(
|
diagnosticAt(
|
||||||
patch, WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32, value
|
expr, WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32, value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (value >= 32) {
|
if (value >= 32) {
|
||||||
diagnosticAt(
|
diagnosticAt(
|
||||||
patch, WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32, value
|
expr, WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32, value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
value = op_shift_right_unsigned(popRPN(patch), value);
|
value = op_shift_right_unsigned(popRPN(expr), value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_BANK_SYM: {
|
case RPN_BANK_SYM: {
|
||||||
uint32_t symID = 0;
|
uint32_t symID = 0;
|
||||||
for (uint8_t shift = 0; shift < 32; shift += 8) {
|
for (uint8_t shift = 0; shift < 32; shift += 8) {
|
||||||
symID |= getRPNByte(expression, size, patch) << shift;
|
symID |= getRPNByte(rpnBuffer, size, expr) << shift;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (symID >= fileSymbols.size()) {
|
if (symID >= fileSymbols.size()) {
|
||||||
fatalAt(patch, "Requested `BANK()` of invalid symbol ID #%" PRIu32, symID);
|
fatalAt(expr, "Requested `BANK()` of invalid symbol ID #%" PRIu32, symID);
|
||||||
} else if (Symbol const *symbol = getSymbol(fileSymbols, symID); !symbol) {
|
} else if (Symbol const *symbol = getSymbol(fileSymbols, symID); !symbol) {
|
||||||
rpnErrorAt(
|
rpnErrorAt(
|
||||||
patch,
|
expr,
|
||||||
"Requested `BANK()` of undefined symbol `%s`",
|
"Requested `BANK()` of undefined symbol `%s`",
|
||||||
fileSymbols[symID].name.c_str()
|
fileSymbols[symID].name.c_str()
|
||||||
);
|
);
|
||||||
@@ -300,7 +300,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
} else if (std::holds_alternative<Label>(symbol->data)) {
|
} else if (std::holds_alternative<Label>(symbol->data)) {
|
||||||
if (Label const &label = std::get<Label>(symbol->data); !label.section) {
|
if (Label const &label = std::get<Label>(symbol->data); !label.section) {
|
||||||
rpnErrorAt(
|
rpnErrorAt(
|
||||||
patch,
|
expr,
|
||||||
"Requested `BANK()` of label `%s` outside of a section",
|
"Requested `BANK()` of label `%s` outside of a section",
|
||||||
fileSymbols[symID].name.c_str()
|
fileSymbols[symID].name.c_str()
|
||||||
);
|
);
|
||||||
@@ -310,7 +310,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rpnErrorAt(
|
rpnErrorAt(
|
||||||
patch,
|
expr,
|
||||||
"Requested `BANK()` of non-label symbol `%s`",
|
"Requested `BANK()` of non-label symbol `%s`",
|
||||||
fileSymbols[symID].name.c_str()
|
fileSymbols[symID].name.c_str()
|
||||||
);
|
);
|
||||||
@@ -320,13 +320,13 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
}
|
}
|
||||||
|
|
||||||
case RPN_BANK_SECT: {
|
case RPN_BANK_SECT: {
|
||||||
// `expression` is not guaranteed to be '\0'-terminated. If it is not,
|
// `rpnBuffer` is not guaranteed to be '\0'-terminated. If it is not,
|
||||||
// `getRPNByte` will have a fatal internal error.
|
// `getRPNByte` will have a fatal internal error.
|
||||||
char const *name = reinterpret_cast<char const *>(expression);
|
char const *name = reinterpret_cast<char const *>(rpnBuffer);
|
||||||
while (getRPNByte(expression, size, patch)) {}
|
while (getRPNByte(rpnBuffer, size, expr)) {}
|
||||||
|
|
||||||
if (Section const *sect = sect_GetSection(name); !sect) {
|
if (Section const *sect = sect_GetSection(name); !sect) {
|
||||||
rpnErrorAt(patch, "Requested `BANK()` of undefined section \"%s\"", name);
|
rpnErrorAt(expr, "Requested `BANK()` of undefined section \"%s\"", name);
|
||||||
value = 1;
|
value = 1;
|
||||||
} else {
|
} else {
|
||||||
value = sect->bank;
|
value = sect->bank;
|
||||||
@@ -335,21 +335,21 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
}
|
}
|
||||||
|
|
||||||
case RPN_BANK_SELF:
|
case RPN_BANK_SELF:
|
||||||
if (!patch.pcSection) {
|
if (!expr.pcSection) {
|
||||||
rpnErrorAt(patch, "PC has no bank outside of a section");
|
rpnErrorAt(expr, "PC has no bank outside of a section");
|
||||||
value = 1;
|
value = 1;
|
||||||
} else {
|
} else {
|
||||||
value = patch.pcSection->bank;
|
value = expr.pcSection->bank;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_SIZEOF_SECT: {
|
case RPN_SIZEOF_SECT: {
|
||||||
// This has assumptions commented in the `RPN_BANK_SECT` case above.
|
// This has assumptions commented in the `RPN_BANK_SECT` case above.
|
||||||
char const *name = reinterpret_cast<char const *>(expression);
|
char const *name = reinterpret_cast<char const *>(rpnBuffer);
|
||||||
while (getRPNByte(expression, size, patch)) {}
|
while (getRPNByte(rpnBuffer, size, expr)) {}
|
||||||
|
|
||||||
if (Section const *sect = sect_GetSection(name); !sect) {
|
if (Section const *sect = sect_GetSection(name); !sect) {
|
||||||
rpnErrorAt(patch, "Requested `SIZEOF()` of undefined section \"%s\"", name);
|
rpnErrorAt(expr, "Requested `SIZEOF()` of undefined section \"%s\"", name);
|
||||||
value = 1;
|
value = 1;
|
||||||
} else {
|
} else {
|
||||||
value = sect->size;
|
value = sect->size;
|
||||||
@@ -359,11 +359,11 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
|
|
||||||
case RPN_STARTOF_SECT: {
|
case RPN_STARTOF_SECT: {
|
||||||
// This has assumptions commented in the `RPN_BANK_SECT` case above.
|
// This has assumptions commented in the `RPN_BANK_SECT` case above.
|
||||||
char const *name = reinterpret_cast<char const *>(expression);
|
char const *name = reinterpret_cast<char const *>(rpnBuffer);
|
||||||
while (getRPNByte(expression, size, patch)) {}
|
while (getRPNByte(rpnBuffer, size, expr)) {}
|
||||||
|
|
||||||
if (Section const *sect = sect_GetSection(name); !sect) {
|
if (Section const *sect = sect_GetSection(name); !sect) {
|
||||||
rpnErrorAt(patch, "Requested `STARTOF()` of undefined section \"%s\"", name);
|
rpnErrorAt(expr, "Requested `STARTOF()` of undefined section \"%s\"", name);
|
||||||
value = 1;
|
value = 1;
|
||||||
} else {
|
} else {
|
||||||
assume(sect->offset == 0);
|
assume(sect->offset == 0);
|
||||||
@@ -373,9 +373,9 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
}
|
}
|
||||||
|
|
||||||
case RPN_SIZEOF_SECTTYPE:
|
case RPN_SIZEOF_SECTTYPE:
|
||||||
value = getRPNByte(expression, size, patch);
|
value = getRPNByte(rpnBuffer, size, expr);
|
||||||
if (value < 0 || value >= SECTTYPE_INVALID) {
|
if (value < 0 || value >= SECTTYPE_INVALID) {
|
||||||
rpnErrorAt(patch, "Requested `SIZEOF()` of an invalid section type");
|
rpnErrorAt(expr, "Requested `SIZEOF()` of an invalid section type");
|
||||||
value = 0;
|
value = 0;
|
||||||
} else {
|
} else {
|
||||||
value = sectionTypeInfo[value].size;
|
value = sectionTypeInfo[value].size;
|
||||||
@@ -383,9 +383,9 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_STARTOF_SECTTYPE:
|
case RPN_STARTOF_SECTTYPE:
|
||||||
value = getRPNByte(expression, size, patch);
|
value = getRPNByte(rpnBuffer, size, expr);
|
||||||
if (value < 0 || value >= SECTTYPE_INVALID) {
|
if (value < 0 || value >= SECTTYPE_INVALID) {
|
||||||
rpnErrorAt(patch, "Requested `STARTOF()` of an invalid section type");
|
rpnErrorAt(expr, "Requested `STARTOF()` of an invalid section type");
|
||||||
value = 0;
|
value = 0;
|
||||||
} else {
|
} else {
|
||||||
value = sectionTypeInfo[value].startAddr;
|
value = sectionTypeInfo[value].startAddr;
|
||||||
@@ -393,10 +393,10 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_HRAM:
|
case RPN_HRAM:
|
||||||
value = popRPN(patch);
|
value = popRPN(expr);
|
||||||
if (value < 0xFF00 || value > 0xFFFF) {
|
if (value < 0xFF00 || value > 0xFFFF) {
|
||||||
firstErrorAt(
|
firstErrorAt(
|
||||||
patch,
|
expr,
|
||||||
"Address $%" PRIx32 " for `LDH` is not in HRAM range; use `LD` instead",
|
"Address $%" PRIx32 " for `LDH` is not in HRAM range; use `LD` instead",
|
||||||
value
|
value
|
||||||
);
|
);
|
||||||
@@ -406,11 +406,11 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_RST:
|
case RPN_RST:
|
||||||
value = popRPN(patch);
|
value = popRPN(expr);
|
||||||
// Acceptable values are 0x00, 0x08, 0x10, ..., 0x38
|
// Acceptable values are 0x00, 0x08, 0x10, ..., 0x38
|
||||||
if (value & ~0x38) {
|
if (value & ~0x38) {
|
||||||
firstErrorAt(
|
firstErrorAt(
|
||||||
patch, "Value $%" PRIx32 " is not a `RST` vector; use `CALL` instead", value
|
expr, "Value $%" PRIx32 " is not a `RST` vector; use `CALL` instead", value
|
||||||
);
|
);
|
||||||
value = 0;
|
value = 0;
|
||||||
}
|
}
|
||||||
@@ -418,11 +418,11 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_BIT_INDEX: {
|
case RPN_BIT_INDEX: {
|
||||||
value = popRPN(patch);
|
value = popRPN(expr);
|
||||||
int32_t mask = getRPNByte(expression, size, patch);
|
int32_t mask = getRPNByte(rpnBuffer, size, expr);
|
||||||
// Acceptable values are 0 to 7
|
// Acceptable values are 0 to 7
|
||||||
if (value & ~0x07) {
|
if (value & ~0x07) {
|
||||||
firstErrorAt(patch, "Value $%" PRIx32 " is not a bit index", value);
|
firstErrorAt(expr, "Value $%" PRIx32 " is not a bit index", value);
|
||||||
value = 0;
|
value = 0;
|
||||||
}
|
}
|
||||||
value = mask | (value << 3);
|
value = mask | (value << 3);
|
||||||
@@ -432,33 +432,33 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
case RPN_CONST:
|
case RPN_CONST:
|
||||||
value = 0;
|
value = 0;
|
||||||
for (uint8_t shift = 0; shift < 32; shift += 8) {
|
for (uint8_t shift = 0; shift < 32; shift += 8) {
|
||||||
value |= getRPNByte(expression, size, patch) << shift;
|
value |= getRPNByte(rpnBuffer, size, expr) << shift;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_SYM: {
|
case RPN_SYM: {
|
||||||
uint32_t symID = 0;
|
uint32_t symID = 0;
|
||||||
for (uint8_t shift = 0; shift < 32; shift += 8) {
|
for (uint8_t shift = 0; shift < 32; shift += 8) {
|
||||||
symID |= getRPNByte(expression, size, patch) << shift;
|
symID |= getRPNByte(rpnBuffer, size, expr) << shift;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (symID == UINT32_MAX) { // PC
|
if (symID == UINT32_MAX) { // PC
|
||||||
if (patch.pcSection) {
|
if (expr.pcSection) {
|
||||||
value = patch.pcOffset + patch.pcSection->org;
|
value = expr.pcOffset + expr.pcSection->org;
|
||||||
} else {
|
} else {
|
||||||
rpnErrorAt(patch, "PC has no value outside of a section");
|
rpnErrorAt(expr, "PC has no value outside of a section");
|
||||||
value = 0;
|
value = 0;
|
||||||
}
|
}
|
||||||
} else if (symID >= fileSymbols.size()) {
|
} else if (symID >= fileSymbols.size()) {
|
||||||
fatalAt(patch, "Invalid symbol ID #%" PRIu32, symID);
|
fatalAt(expr, "Invalid symbol ID #%" PRIu32, symID);
|
||||||
} else if (Symbol const *symbol = getSymbol(fileSymbols, symID); !symbol) {
|
} else if (Symbol const *symbol = getSymbol(fileSymbols, symID); !symbol) {
|
||||||
rpnErrorAt(patch, "Undefined symbol `%s`", fileSymbols[symID].name.c_str());
|
rpnErrorAt(expr, "Undefined symbol `%s`", fileSymbols[symID].name.c_str());
|
||||||
sym_TraceLocalAliasedSymbols(fileSymbols[symID].name);
|
sym_TraceLocalAliasedSymbols(fileSymbols[symID].name);
|
||||||
value = 0;
|
value = 0;
|
||||||
} else if (std::holds_alternative<Label>(symbol->data)) {
|
} else if (std::holds_alternative<Label>(symbol->data)) {
|
||||||
if (Label const &label = std::get<Label>(symbol->data); !label.section) {
|
if (Label const &label = std::get<Label>(symbol->data); !label.section) {
|
||||||
rpnErrorAt(
|
rpnErrorAt(
|
||||||
patch,
|
expr,
|
||||||
"Requested value of label `%s` outside of a section",
|
"Requested value of label `%s` outside of a section",
|
||||||
fileSymbols[symID].name.c_str()
|
fileSymbols[symID].name.c_str()
|
||||||
);
|
);
|
||||||
@@ -474,7 +474,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
|
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
default:
|
default:
|
||||||
fatalAt(patch, "Invalid RPN command $%02x", static_cast<uint32_t>(command));
|
fatalAt(expr, "Invalid RPN command $%02x", static_cast<uint32_t>(command));
|
||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -482,11 +482,11 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (rpnStack.size() > 1) {
|
if (rpnStack.size() > 1) {
|
||||||
rpnErrorAt(patch, "RPN stack has %zu entries on exit, not 1", rpnStack.size());
|
rpnErrorAt(expr, "RPN stack has %zu entries on exit, not 1", rpnStack.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
isError = false;
|
isError = false;
|
||||||
return popRPN(patch);
|
return popRPN(expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
Assertion &patch_AddAssertion() {
|
Assertion &patch_AddAssertion() {
|
||||||
@@ -497,36 +497,33 @@ void patch_CheckAssertions() {
|
|||||||
verbosePrint(VERB_NOTICE, "Checking assertions...\n");
|
verbosePrint(VERB_NOTICE, "Checking assertions...\n");
|
||||||
|
|
||||||
for (Assertion &assert : assertions) {
|
for (Assertion &assert : assertions) {
|
||||||
int32_t value = computeRPNExpr(assert.patch, *assert.fileSymbols);
|
if (int32_t value = computeRPNExpr(assert.rpn, *assert.fileSymbols); !isError && !value) {
|
||||||
AssertionType type = static_cast<AssertionType>(assert.patch.type);
|
switch (assert.type) {
|
||||||
|
|
||||||
if (!isError && !value) {
|
|
||||||
switch (type) {
|
|
||||||
case ASSERT_FATAL:
|
case ASSERT_FATAL:
|
||||||
fatalAt(
|
fatalAt(
|
||||||
assert.patch,
|
assert.rpn,
|
||||||
"%s",
|
"%s",
|
||||||
!assert.message.empty() ? assert.message.c_str() : "assert failure"
|
!assert.message.empty() ? assert.message.c_str() : "assert failure"
|
||||||
);
|
);
|
||||||
case ASSERT_ERROR:
|
case ASSERT_ERROR:
|
||||||
rpnErrorAt(
|
rpnErrorAt(
|
||||||
assert.patch,
|
assert.rpn,
|
||||||
"%s",
|
"%s",
|
||||||
!assert.message.empty() ? assert.message.c_str() : "assert failure"
|
!assert.message.empty() ? assert.message.c_str() : "assert failure"
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case ASSERT_WARN:
|
case ASSERT_WARN:
|
||||||
warningAt(
|
warningAt(
|
||||||
assert.patch,
|
assert.rpn,
|
||||||
WARNING_ASSERT,
|
WARNING_ASSERT,
|
||||||
"%s",
|
"%s",
|
||||||
!assert.message.empty() ? assert.message.c_str() : "assert failure"
|
!assert.message.empty() ? assert.message.c_str() : "assert failure"
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (isError && type == ASSERT_FATAL) {
|
} else if (isError && assert.type == ASSERT_FATAL) {
|
||||||
fatalAt(
|
fatalAt(
|
||||||
assert.patch,
|
assert.rpn,
|
||||||
"Failed to evaluate assertion%s%s",
|
"Failed to evaluate assertion%s%s",
|
||||||
!assert.message.empty() ? ": " : "",
|
!assert.message.empty() ? ": " : "",
|
||||||
assert.message.c_str()
|
assert.message.c_str()
|
||||||
@@ -535,13 +532,13 @@ void patch_CheckAssertions() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void checkPatchSize(Patch const &patch, int32_t v, uint8_t n) {
|
static void checkPatchSize(Expression const &expr, int32_t v, uint8_t n) {
|
||||||
assume(n != 0); // That doesn't make sense
|
assume(n != 0); // That doesn't make sense
|
||||||
assume(n < CHAR_BIT * sizeof(int) - 1); // Otherwise `1 << n` is UB
|
assume(n < CHAR_BIT * sizeof(int) - 1); // Otherwise `1 << n` is UB
|
||||||
|
|
||||||
if (v < -(1 << n) || v >= 1 << n) {
|
if (v < -(1 << n) || v >= 1 << n) {
|
||||||
diagnosticAt(
|
diagnosticAt(
|
||||||
patch,
|
expr,
|
||||||
WARNING_TRUNCATION_1,
|
WARNING_TRUNCATION_1,
|
||||||
"Value $%" PRIx32 "%s is not %u-bit",
|
"Value $%" PRIx32 "%s is not %u-bit",
|
||||||
v,
|
v,
|
||||||
@@ -550,7 +547,7 @@ static void checkPatchSize(Patch const &patch, int32_t v, uint8_t n) {
|
|||||||
);
|
);
|
||||||
} else if (v < -(1 << (n - 1))) {
|
} else if (v < -(1 << (n - 1))) {
|
||||||
diagnosticAt(
|
diagnosticAt(
|
||||||
patch,
|
expr,
|
||||||
WARNING_TRUNCATION_2,
|
WARNING_TRUNCATION_2,
|
||||||
"Value $%" PRIx32 "%s is not %u-bit",
|
"Value $%" PRIx32 "%s is not %u-bit",
|
||||||
v,
|
v,
|
||||||
@@ -560,18 +557,18 @@ static void checkPatchSize(Patch const &patch, int32_t v, uint8_t n) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void checkSignedPatchSize(Patch const &patch, int32_t v, uint8_t n) {
|
static void checkSignedPatchSize(Expression const &expr, int32_t v, uint8_t n) {
|
||||||
assume(n != 0); // That doesn't make sense
|
assume(n != 0); // That doesn't make sense
|
||||||
assume(n < CHAR_BIT * sizeof(int) - 1); // Otherwise `1 << n` is UB
|
assume(n < CHAR_BIT * sizeof(int) - 1); // Otherwise `1 << n` is UB
|
||||||
|
|
||||||
if (v < -(1 << (n - 1)) || v >= 1 << (n - 1)) {
|
if (v < -(1 << (n - 1)) || v >= 1 << (n - 1)) {
|
||||||
if (v < 0) {
|
if (v < 0) {
|
||||||
diagnosticAt(
|
diagnosticAt(
|
||||||
patch, WARNING_TRUNCATION_1, "Value -$%" PRIx32 " is not signed %u-bit", -v, n
|
expr, WARNING_TRUNCATION_1, "Value -$%" PRIx32 " is not signed %u-bit", -v, n
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
diagnosticAt(
|
diagnosticAt(
|
||||||
patch, WARNING_TRUNCATION_1, "Value $%" PRIx32 " is not signed %u-bit", v, n
|
expr, WARNING_TRUNCATION_1, "Value $%" PRIx32 " is not signed %u-bit", v, n
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -581,7 +578,7 @@ static void checkSignedPatchSize(Patch const &patch, int32_t v, uint8_t n) {
|
|||||||
static void applyFilePatches(Section §ion, Section &dataSection) {
|
static void applyFilePatches(Section §ion, Section &dataSection) {
|
||||||
verbosePrint(VERB_INFO, "Patching section \"%s\"...\n", section.name.c_str());
|
verbosePrint(VERB_INFO, "Patching section \"%s\"...\n", section.name.c_str());
|
||||||
for (Patch &patch : section.patches) {
|
for (Patch &patch : section.patches) {
|
||||||
int32_t value = computeRPNExpr(patch, *section.fileSymbols);
|
int32_t value = computeRPNExpr(patch.rpn, *section.fileSymbols);
|
||||||
uint32_t offset = patch.offset + section.offset;
|
uint32_t offset = patch.offset + section.offset;
|
||||||
|
|
||||||
uint8_t typeSizes[PATCHTYPE_INVALID] = {
|
uint8_t typeSizes[PATCHTYPE_INVALID] = {
|
||||||
@@ -595,30 +592,30 @@ static void applyFilePatches(Section §ion, Section &dataSection) {
|
|||||||
|
|
||||||
if (dataSection.data.size() < offset + typeSize) {
|
if (dataSection.data.size() < offset + typeSize) {
|
||||||
rpnErrorAt(
|
rpnErrorAt(
|
||||||
patch,
|
patch.rpn,
|
||||||
"Patch would write %zu bytes past the end of section \"%s\" (%zu bytes long)",
|
"Patch would write %zu bytes past the end of section \"%s\" (%zu bytes long)",
|
||||||
offset + typeSize - dataSection.data.size(),
|
offset + typeSize - dataSection.data.size(),
|
||||||
dataSection.name.c_str(),
|
dataSection.name.c_str(),
|
||||||
dataSection.data.size()
|
dataSection.data.size()
|
||||||
);
|
);
|
||||||
} else if (patch.type == PATCHTYPE_JR) {
|
} else if (patch.type == PATCHTYPE_JR) {
|
||||||
if (!patch.pcSection) {
|
if (!patch.rpn.pcSection) {
|
||||||
rpnErrorAt(patch, "PC has no value outside of a section");
|
rpnErrorAt(patch.rpn, "PC has no value outside of a section");
|
||||||
dataSection.data[offset] = 0;
|
dataSection.data[offset] = 0;
|
||||||
} else {
|
} else {
|
||||||
// A `jr` is *encoded* in ROM as a 1-byte (8-bit) offset, so here `typeSize == 1`,
|
// A `jr` is *encoded* in ROM as a 1-byte (8-bit) offset, so here `typeSize == 1`,
|
||||||
// but the object's *value* size is a 16-bit absolute address, so we pass 16 here.
|
// but the object's *value* size is a 16-bit absolute address, so we pass 16 here.
|
||||||
checkPatchSize(patch, value, 16);
|
checkPatchSize(patch.rpn, value, 16);
|
||||||
// Offset is relative to the byte *after* the operand
|
// Offset is relative to the byte *after* the operand
|
||||||
// PC as operand to `jr` is lower than reference PC by 2
|
// PC as operand to `jr` is lower than reference PC by 2
|
||||||
uint16_t address = patch.pcSection->org + patch.pcOffset + 2;
|
uint16_t address = patch.rpn.pcSection->org + patch.rpn.pcOffset + 2;
|
||||||
// The 16-bit truncation of `value - address` is intentional, since
|
// The 16-bit truncation of `value - address` is intentional, since
|
||||||
// a low ROM0 address may `jr` backwards to a high HRAM one.
|
// a low ROM0 address may `jr` backwards to a high HRAM one.
|
||||||
int16_t jumpOffset = static_cast<int16_t>(value - address);
|
int16_t jumpOffset = static_cast<int16_t>(value - address);
|
||||||
|
|
||||||
if (jumpOffset < -128 || jumpOffset > 127) {
|
if (jumpOffset < -128 || jumpOffset > 127) {
|
||||||
firstErrorAt(
|
firstErrorAt(
|
||||||
patch,
|
patch.rpn,
|
||||||
"`JR` target must be between -128 and 127 bytes away, not %" PRId16
|
"`JR` target must be between -128 and 127 bytes away, not %" PRId16
|
||||||
"; use `JP` instead",
|
"; use `JP` instead",
|
||||||
jumpOffset
|
jumpOffset
|
||||||
@@ -629,9 +626,9 @@ static void applyFilePatches(Section §ion, Section &dataSection) {
|
|||||||
} else {
|
} else {
|
||||||
// Patch a certain number of bytes
|
// Patch a certain number of bytes
|
||||||
if (patch.type == PATCHTYPE_SIGNED_BYTE) {
|
if (patch.type == PATCHTYPE_SIGNED_BYTE) {
|
||||||
checkSignedPatchSize(patch, value, typeSize * 8);
|
checkSignedPatchSize(patch.rpn, value, typeSize * 8);
|
||||||
} else if (typeSize < sizeof(int)) {
|
} else if (typeSize < sizeof(int)) {
|
||||||
checkPatchSize(patch, value, typeSize * 8);
|
checkPatchSize(patch.rpn, value, typeSize * 8);
|
||||||
}
|
}
|
||||||
for (uint8_t i = 0; i < typeSize; ++i) {
|
for (uint8_t i = 0; i < typeSize; ++i) {
|
||||||
dataSection.data[offset + i] = value & 0xFF;
|
dataSection.data[offset + i] = value & 0xFF;
|
||||||
|
|||||||
+49
-53
@@ -586,8 +586,8 @@ void sdobj_ReadFile(FileStackNode const &src, FILE *file, std::vector<Symbol> &f
|
|||||||
// Turn this into a Patch
|
// Turn this into a Patch
|
||||||
Patch &patch = section->patches.emplace_back();
|
Patch &patch = section->patches.emplace_back();
|
||||||
|
|
||||||
patch.src = where.src;
|
patch.rpn.src = where.src;
|
||||||
patch.lineNo = where.lineNo;
|
patch.rpn.lineNo = where.lineNo;
|
||||||
patch.offset = offset - writtenOfs + *writeIndex;
|
patch.offset = offset - writtenOfs + *writeIndex;
|
||||||
if (section->patches.size() > 1) {
|
if (section->patches.size() > 1) {
|
||||||
uint32_t prevOffset = section->patches[section->patches.size() - 2].offset;
|
uint32_t prevOffset = section->patches[section->patches.size() - 2].offset;
|
||||||
@@ -601,8 +601,8 @@ void sdobj_ReadFile(FileStackNode const &src, FILE *file, std::vector<Symbol> &f
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
patch.pcSection = section; // No need to fill `pcSectionID`, then
|
patch.rpn.pcSection = section; // No need to fill `pcSectionID`, then
|
||||||
patch.pcOffset = patch.offset - 1; // For `jr`s
|
patch.rpn.pcOffset = patch.offset - 1; // For `jr`s
|
||||||
|
|
||||||
patch.type = (flags & 1 << RELOC_SIZE) ? PATCHTYPE_BYTE : PATCHTYPE_WORD;
|
patch.type = (flags & 1 << RELOC_SIZE) ? PATCHTYPE_BYTE : PATCHTYPE_WORD;
|
||||||
uint8_t nbBaseBytes = patch.type == PATCHTYPE_BYTE ? addrSize : 2;
|
uint8_t nbBaseBytes = patch.type == PATCHTYPE_BYTE ? addrSize : 2;
|
||||||
@@ -654,35 +654,35 @@ void sdobj_ReadFile(FileStackNode const &src, FILE *file, std::vector<Symbol> &f
|
|||||||
&sym.name.c_str()[1]
|
&sym.name.c_str()[1]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
patch.rpnExpression.resize(5);
|
patch.rpn.rpn.resize(5);
|
||||||
patch.rpnExpression[0] = RPN_BANK_SYM;
|
patch.rpn.rpn[0] = RPN_BANK_SYM;
|
||||||
patch.rpnExpression[1] = idx;
|
patch.rpn.rpn[1] = idx;
|
||||||
patch.rpnExpression[2] = idx >> 8;
|
patch.rpn.rpn[2] = idx >> 8;
|
||||||
patch.rpnExpression[3] = 0;
|
patch.rpn.rpn[3] = 0;
|
||||||
patch.rpnExpression[4] = 0;
|
patch.rpn.rpn[4] = 0;
|
||||||
} else if (sym.name.starts_with("l_")) {
|
} else if (sym.name.starts_with("l_")) {
|
||||||
patch.rpnExpression.resize(1 + sym.name.length() - 2 + 1);
|
patch.rpn.rpn.resize(1 + sym.name.length() - 2 + 1);
|
||||||
patch.rpnExpression[0] = RPN_SIZEOF_SECT;
|
patch.rpn.rpn[0] = RPN_SIZEOF_SECT;
|
||||||
memcpy(
|
memcpy(
|
||||||
reinterpret_cast<char *>(&patch.rpnExpression[1]),
|
reinterpret_cast<char *>(&patch.rpn.rpn[1]),
|
||||||
&sym.name.c_str()[2],
|
&sym.name.c_str()[2],
|
||||||
sym.name.length() - 2 + 1
|
sym.name.length() - 2 + 1
|
||||||
);
|
);
|
||||||
} else if (sym.name.starts_with("s_")) {
|
} else if (sym.name.starts_with("s_")) {
|
||||||
patch.rpnExpression.resize(1 + sym.name.length() - 2 + 1);
|
patch.rpn.rpn.resize(1 + sym.name.length() - 2 + 1);
|
||||||
patch.rpnExpression[0] = RPN_STARTOF_SECT;
|
patch.rpn.rpn[0] = RPN_STARTOF_SECT;
|
||||||
memcpy(
|
memcpy(
|
||||||
reinterpret_cast<char *>(&patch.rpnExpression[1]),
|
reinterpret_cast<char *>(&patch.rpn.rpn[1]),
|
||||||
&sym.name.c_str()[2],
|
&sym.name.c_str()[2],
|
||||||
sym.name.length() - 2 + 1
|
sym.name.length() - 2 + 1
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
patch.rpnExpression.resize(5);
|
patch.rpn.rpn.resize(5);
|
||||||
patch.rpnExpression[0] = RPN_SYM;
|
patch.rpn.rpn[0] = RPN_SYM;
|
||||||
patch.rpnExpression[1] = idx;
|
patch.rpn.rpn[1] = idx;
|
||||||
patch.rpnExpression[2] = idx >> 8;
|
patch.rpn.rpn[2] = idx >> 8;
|
||||||
patch.rpnExpression[3] = 0;
|
patch.rpn.rpn[3] = 0;
|
||||||
patch.rpnExpression[4] = 0;
|
patch.rpn.rpn[4] = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (idx >= fileSections.size()) {
|
if (idx >= fileSections.size()) {
|
||||||
@@ -717,22 +717,20 @@ void sdobj_ReadFile(FileStackNode const &src, FILE *file, std::vector<Symbol> &f
|
|||||||
if (other) {
|
if (other) {
|
||||||
baseValue += other->size;
|
baseValue += other->size;
|
||||||
}
|
}
|
||||||
patch.rpnExpression.resize(1 + name.length() + 1);
|
patch.rpn.rpn.resize(1 + name.length() + 1);
|
||||||
patch.rpnExpression[0] = RPN_STARTOF_SECT;
|
patch.rpn.rpn[0] = RPN_STARTOF_SECT;
|
||||||
// The cast is fine, it's just different signedness
|
// The cast is fine, it's just different signedness
|
||||||
memcpy(
|
memcpy(
|
||||||
reinterpret_cast<char *>(&patch.rpnExpression[1]),
|
reinterpret_cast<char *>(&patch.rpn.rpn[1]), name.c_str(), name.length() + 1
|
||||||
name.c_str(),
|
|
||||||
name.length() + 1
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
patch.rpnExpression.push_back(RPN_CONST);
|
patch.rpn.rpn.push_back(RPN_CONST);
|
||||||
patch.rpnExpression.push_back(baseValue);
|
patch.rpn.rpn.push_back(baseValue);
|
||||||
patch.rpnExpression.push_back(baseValue >> 8);
|
patch.rpn.rpn.push_back(baseValue >> 8);
|
||||||
patch.rpnExpression.push_back(baseValue >> 16);
|
patch.rpn.rpn.push_back(baseValue >> 16);
|
||||||
patch.rpnExpression.push_back(baseValue >> 24);
|
patch.rpn.rpn.push_back(baseValue >> 24);
|
||||||
patch.rpnExpression.push_back(RPN_ADD);
|
patch.rpn.rpn.push_back(RPN_ADD);
|
||||||
|
|
||||||
if (patch.type == PATCHTYPE_BYTE) {
|
if (patch.type == PATCHTYPE_BYTE) {
|
||||||
// Despite the flag's name, as soon as it is set, 3 bytes
|
// Despite the flag's name, as soon as it is set, 3 bytes
|
||||||
@@ -761,31 +759,29 @@ void sdobj_ReadFile(FileStackNode const &src, FILE *file, std::vector<Symbol> &f
|
|||||||
patch.type = PATCHTYPE_JR;
|
patch.type = PATCHTYPE_JR;
|
||||||
// TODO: check the other flags?
|
// TODO: check the other flags?
|
||||||
} else if (flags & 1 << RELOC_EXPR24 && flags & 1 << RELOC_BANKBYTE) {
|
} else if (flags & 1 << RELOC_EXPR24 && flags & 1 << RELOC_BANKBYTE) {
|
||||||
patch.rpnExpression.push_back(RPN_CONST);
|
patch.rpn.rpn.push_back(RPN_CONST);
|
||||||
patch.rpnExpression.push_back(16);
|
patch.rpn.rpn.push_back(16);
|
||||||
patch.rpnExpression.push_back(16 >> 8);
|
patch.rpn.rpn.push_back(16 >> 8);
|
||||||
patch.rpnExpression.push_back(16 >> 16);
|
patch.rpn.rpn.push_back(16 >> 16);
|
||||||
patch.rpnExpression.push_back(16 >> 24);
|
patch.rpn.rpn.push_back(16 >> 24);
|
||||||
patch.rpnExpression.push_back(
|
patch.rpn.rpn.push_back((flags & 1 << RELOC_SIGNED) ? RPN_SHR : RPN_USHR);
|
||||||
(flags & 1 << RELOC_SIGNED) ? RPN_SHR : RPN_USHR
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
if (flags & 1 << RELOC_EXPR16 && flags & 1 << RELOC_WHICHBYTE) {
|
if (flags & 1 << RELOC_EXPR16 && flags & 1 << RELOC_WHICHBYTE) {
|
||||||
patch.rpnExpression.push_back(RPN_CONST);
|
patch.rpn.rpn.push_back(RPN_CONST);
|
||||||
patch.rpnExpression.push_back(8);
|
patch.rpn.rpn.push_back(8);
|
||||||
patch.rpnExpression.push_back(8 >> 8);
|
patch.rpn.rpn.push_back(8 >> 8);
|
||||||
patch.rpnExpression.push_back(8 >> 16);
|
patch.rpn.rpn.push_back(8 >> 16);
|
||||||
patch.rpnExpression.push_back(8 >> 24);
|
patch.rpn.rpn.push_back(8 >> 24);
|
||||||
patch.rpnExpression.push_back(
|
patch.rpn.rpn.push_back(
|
||||||
(flags & 1 << RELOC_SIGNED) ? RPN_SHR : RPN_USHR
|
(flags & 1 << RELOC_SIGNED) ? RPN_SHR : RPN_USHR
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
patch.rpnExpression.push_back(RPN_CONST);
|
patch.rpn.rpn.push_back(RPN_CONST);
|
||||||
patch.rpnExpression.push_back(0xFF);
|
patch.rpn.rpn.push_back(0xFF);
|
||||||
patch.rpnExpression.push_back(0xFF >> 8);
|
patch.rpn.rpn.push_back(0xFF >> 8);
|
||||||
patch.rpnExpression.push_back(0xFF >> 16);
|
patch.rpn.rpn.push_back(0xFF >> 16);
|
||||||
patch.rpnExpression.push_back(0xFF >> 24);
|
patch.rpn.rpn.push_back(0xFF >> 24);
|
||||||
patch.rpnExpression.push_back(RPN_AND);
|
patch.rpn.rpn.push_back(RPN_AND);
|
||||||
}
|
}
|
||||||
} else if (flags & 1 << RELOC_ISPCREL) {
|
} else if (flags & 1 << RELOC_ISPCREL) {
|
||||||
assume(patch.type == PATCHTYPE_WORD);
|
assume(patch.type == PATCHTYPE_WORD);
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ static void mergeSections(Section &target, std::unique_ptr<Section> &&other) {
|
|||||||
target.data.insert(target.data.end(), RANGE(other->data));
|
target.data.insert(target.data.end(), RANGE(other->data));
|
||||||
// Adjust patches' PC offsets
|
// Adjust patches' PC offsets
|
||||||
for (Patch &patch : other->patches) {
|
for (Patch &patch : other->patches) {
|
||||||
patch.pcOffset += other->offset;
|
patch.rpn.pcOffset += other->offset;
|
||||||
}
|
}
|
||||||
} else if (!target.data.empty()) {
|
} else if (!target.data.empty()) {
|
||||||
assume(other->size == 0);
|
assume(other->size == 0);
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
INCLUDE "rept-node-names.inc"
|
||||||
|
quiet
|
||||||
|
loud
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
warning: $0 [-Wuser]
|
||||||
|
at rept-node-names.inc::quiet::REPT~1(3) <- rept-node-names.asm(2)
|
||||||
|
warning: $1 [-Wuser]
|
||||||
|
at rept-node-names.inc::quiet::REPT~2(3) <- rept-node-names.asm(2)
|
||||||
|
warning: $0 [-Wuser]
|
||||||
|
at rept-node-names.inc::loud::REPT~1(8) <- rept-node-names.inc::loud(7) <- rept-node-names.asm(3)
|
||||||
|
warning: $1 [-Wuser]
|
||||||
|
at rept-node-names.inc::loud::REPT~2(8) <- rept-node-names.inc::loud(7) <- rept-node-names.asm(3)
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
MACRO? quiet
|
||||||
|
FOR i, 2
|
||||||
|
WARN "{i}"
|
||||||
|
ENDR
|
||||||
|
ENDM
|
||||||
|
MACRO loud
|
||||||
|
FOR i, 2
|
||||||
|
WARN "{i}"
|
||||||
|
ENDR
|
||||||
|
ENDM
|
||||||
Reference in new issue
Block a user