Do not reuse Patch struct for outputting assertions (#2184)

This commit is contained in:
Rangi authored and GitHub committed 2026-10-03 19:24:49 +02:00
1 parent 6c76606839
commit 9095c5b320
9 files changed
+427 -361

No files matched your search

+1 -1
View File
@@ -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
); );
+1 -1
View File
@@ -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;
}; };
+5 -3
View File
@@ -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;
}; };
+7 -3
View File
@@ -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 {
+43 -32
View File
@@ -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 &sect, FILE *file) { static void writeSection(Section const &sect, FILE *file) {
assume(sect.src->ID != UINT32_MAX); assume(sect.src->ID != UINT32_MAX);
@@ -104,7 +96,16 @@ static void writeSection(Section const &sect, 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);
} }
+195 -139
View File
@@ -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 &sectName,
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 &section, char const *fileName, std::vector<FileStackNode> const &fileNodes FILE *file, Section &section, char const *fileName, std::vector<FileStackNode> const &fileNodes
@@ -386,31 +281,127 @@ static void readSection(
} }
section.alignOfs = tmp; section.alignOfs = tmp;
if (sectTypeHasData(section.type)) { if (!sectTypeHasData(section.type)) {
if (section.size) { return;
section.data.resize(section.size); }
if (fread(section.data.data(), 1, section.size, file) != section.size) {
fatal( if (section.size) {
"%s: Cannot read \"%s\"'s data: %s", section.data.resize(section.size);
fileName, if (fread(section.data.data(), 1, section.size, file) != section.size) {
section.name.c_str(), fatal(
feof(file) ? "Unexpected end of file" : strerror(errno) "%s: Cannot read \"%s\"'s data: %s",
); fileName,
} section.name.c_str(),
} feof(file) ? "Unexpected end of file" : strerror(errno)
);
}
}
uint32_t nbPatches;
tryReadLong(
nbPatches,
file,
"%s: Cannot read \"%s\"'s number of patches: %s",
fileName,
section.name.c_str()
);
section.patches.resize(nbPatches);
for (uint32_t patchID = 0; patchID < nbPatches; ++patchID) {
Patch &patch = section.patches[patchID];
uint32_t nbPatches;
tryReadLong( tryReadLong(
nbPatches, nodeID,
file, file,
"%s: Cannot read \"%s\"'s number of patches: %s", "%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s node ID: %s",
fileName, fileName,
section.name.c_str() 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
); );
section.patches.resize(nbPatches); uint8_t type;
for (uint32_t i = 0; i < nbPatches; ++i) { tryGetc(
readPatch(file, section.patches[i], fileName, section.name, i, fileNodes); 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();
} }
} }
} }
+125 -128
View File
@@ -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,153 +143,153 @@ 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_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()
); );
@@ -297,7 +297,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()
); );
@@ -307,7 +307,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()
); );
@@ -317,13 +317,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;
@@ -332,21 +332,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;
@@ -356,11 +356,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);
@@ -370,9 +370,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;
@@ -380,9 +380,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;
@@ -390,10 +390,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
); );
@@ -403,11 +403,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;
} }
@@ -415,11 +415,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);
@@ -429,33 +429,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()
); );
@@ -471,7 +471,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
} }
@@ -479,11 +479,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() {
@@ -494,36 +494,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()
@@ -532,13 +529,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,
@@ -547,7 +544,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,
@@ -561,7 +558,7 @@ static void checkPatchSize(Patch const &patch, int32_t v, uint8_t n) {
static void applyFilePatches(Section &section, Section &dataSection) { static void applyFilePatches(Section &section, 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] = {
@@ -574,30 +571,30 @@ static void applyFilePatches(Section &section, 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 == 8`, // A `jr` is *encoded* in ROM as a 1-byte (8-bit) offset, so here `typeSize == 8`,
// 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
@@ -608,7 +605,7 @@ static void applyFilePatches(Section &section, Section &dataSection) {
} else { } else {
// Patch a certain number of bytes // Patch a certain number of bytes
if (typeSize < sizeof(int)) { 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
View File
@@ -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);
+1 -1
View File
@@ -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);