Move RPN buffer encoding logic into rpn.cpp

This commit is contained in:
Rangi42
2025-09-03 22:36:00 -04:00
parent c798500563
commit c5c2800f17
4 changed files with 89 additions and 77 deletions

View File

@@ -12,10 +12,12 @@
struct Expression; struct Expression;
struct FileStackNode; struct FileStackNode;
struct Symbol;
enum StateFeature { STATE_EQU, STATE_VAR, STATE_EQUS, STATE_CHAR, STATE_MACRO, NB_STATE_FEATURES }; enum StateFeature { STATE_EQU, STATE_VAR, STATE_EQUS, STATE_CHAR, STATE_MACRO, NB_STATE_FEATURES };
void out_RegisterNode(std::shared_ptr<FileStackNode> node); void out_RegisterNode(std::shared_ptr<FileStackNode> node);
void out_RegisterSymbol(Symbol &sym);
void out_CreatePatch(uint32_t type, Expression const &expr, uint32_t ofs, uint32_t pcShift); void out_CreatePatch(uint32_t 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

View File

@@ -13,8 +13,10 @@
struct Symbol; struct Symbol;
struct RPNValue { struct RPNValue {
RPNCommand command; RPNCommand command; // The RPN_* command ID
std::variant<std::monostate, uint8_t, uint32_t, std::string> data; std::variant<std::monostate, uint8_t, uint32_t, std::string> data; // Data after the ID, if any
void appendEncoded(std::vector<uint8_t> &buffer) const;
}; };
struct Expression { struct Expression {
@@ -48,6 +50,8 @@ struct Expression {
void addCheckBitIndex(uint8_t mask); void addCheckBitIndex(uint8_t mask);
void checkNBit(uint8_t n) const; void checkNBit(uint8_t n) const;
void encode(std::vector<uint8_t> &buffer) const;
}; };
bool checkNBit(int32_t v, uint8_t n, char const *name); bool checkNBit(int32_t v, uint8_t n, char const *name);

View File

@@ -124,7 +124,7 @@ static void writeSymbol(Symbol const &sym, FILE *file) {
} }
} }
static void registerUnregisteredSymbol(Symbol &sym) { void out_RegisterSymbol(Symbol &sym) {
// Check for `sym.src`, to skip any built-in symbol from rgbasm // Check for `sym.src`, to skip any built-in symbol from rgbasm
if (sym.src && sym.ID == UINT32_MAX && !sym_IsPC(&sym)) { if (sym.src && sym.ID == UINT32_MAX && !sym_IsPC(&sym)) {
sym.ID = objectSymbols.size(); // Set the symbol's ID within the object file sym.ID = objectSymbols.size(); // Set the symbol's ID within the object file
@@ -142,79 +142,7 @@ static void initPatch(Patch &patch, uint32_t type, Expression const &expr, uint3
patch.offset = ofs; patch.offset = ofs;
patch.pcSection = sect_GetSymbolSection(); patch.pcSection = sect_GetSymbolSection();
patch.pcOffset = sect_GetSymbolOffset(); patch.pcOffset = sect_GetSymbolOffset();
expr.encode(patch.rpn);
if (expr.isKnown()) {
// If the RPN expr's value is known, output a constant directly
uint32_t val = expr.value();
patch.rpn.resize(5);
patch.rpn[0] = RPN_CONST;
patch.rpn[1] = val & 0xFF;
patch.rpn[2] = val >> 8;
patch.rpn[3] = val >> 16;
patch.rpn[4] = val >> 24;
return;
}
// If the RPN expr's value is not known, serialize its RPN values
patch.rpn.clear();
patch.rpn.reserve(expr.rpn.size() * 2); // Rough estimate of the serialized size
for (RPNValue const &value : expr.rpn) {
// Every command starts with its own ID
patch.rpn.push_back(value.command);
switch (value.command) {
case RPN_CONST: {
// The command ID is followed by a four-byte integer
assume(std::holds_alternative<uint32_t>(value.data));
uint32_t v = std::get<uint32_t>(value.data);
patch.rpn.push_back(v & 0xFF);
patch.rpn.push_back(v >> 8);
patch.rpn.push_back(v >> 16);
patch.rpn.push_back(v >> 24);
break;
}
case RPN_SYM:
case RPN_BANK_SYM: {
// The command ID is followed by a four-byte symbol ID
assume(std::holds_alternative<std::string>(value.data));
// The symbol name is always written expanded
Symbol *sym = sym_FindExactSymbol(std::get<std::string>(value.data));
registerUnregisteredSymbol(*sym); // Ensure that `sym->ID` is set
patch.rpn.push_back(sym->ID & 0xFF);
patch.rpn.push_back(sym->ID >> 8);
patch.rpn.push_back(sym->ID >> 16);
patch.rpn.push_back(sym->ID >> 24);
break;
}
case RPN_BANK_SECT:
case RPN_SIZEOF_SECT:
case RPN_STARTOF_SECT: {
// The command ID is followed by a NUL-terminated section name string
assume(std::holds_alternative<std::string>(value.data));
for (char c : std::get<std::string>(value.data)) {
patch.rpn.push_back(c);
}
patch.rpn.push_back('\0');
break;
}
case RPN_SIZEOF_SECTTYPE:
case RPN_STARTOF_SECTTYPE:
case RPN_BIT_INDEX:
// The command ID is followed by a byte value
assume(std::holds_alternative<uint8_t>(value.data));
patch.rpn.push_back(std::get<uint8_t>(value.data));
break;
default:
// Other command IDs are not followed by anything
assume(std::holds_alternative<std::monostate>(value.data));
break;
}
}
} }
void out_CreatePatch(uint32_t type, Expression const &expr, uint32_t ofs, uint32_t pcShift) { void out_CreatePatch(uint32_t type, Expression const &expr, uint32_t ofs, uint32_t pcShift) {
@@ -286,7 +214,7 @@ void out_WriteObject() {
Defer closeFile{[&] { fclose(file); }}; Defer closeFile{[&] { fclose(file); }};
// Also write symbols that weren't written above // Also write symbols that weren't written above
sym_ForEach(registerUnregisteredSymbol); sym_ForEach(out_RegisterSymbol);
fputs(RGBDS_OBJECT_VERSION_STRING, file); fputs(RGBDS_OBJECT_VERSION_STRING, file);
putLong(RGBDS_OBJECT_REV, file); putLong(RGBDS_OBJECT_REV, file);

View File

@@ -512,3 +512,81 @@ bool checkNBit(int32_t v, uint8_t n, char const *name) {
return true; return true;
} }
void Expression::encode(std::vector<uint8_t> &buffer) const {
assume(buffer.empty());
if (isKnown()) {
// If the RPN expression's value is known, output a constant directly
uint32_t val = value();
buffer.resize(5);
buffer[0] = RPN_CONST;
buffer[1] = val & 0xFF;
buffer[2] = val >> 8;
buffer[3] = val >> 16;
buffer[4] = val >> 24;
} else {
// If the RPN expression's value is not known, serialize its RPN values
buffer.reserve(rpn.size() * 2); // Rough estimate of the serialized size
for (RPNValue const &val : rpn) {
val.appendEncoded(buffer);
}
}
}
void RPNValue::appendEncoded(std::vector<uint8_t> &buffer) const {
// Every command starts with its own ID
buffer.push_back(command);
switch (command) {
case RPN_CONST: {
// The command ID is followed by a four-byte integer
assume(std::holds_alternative<uint32_t>(data));
uint32_t val = std::get<uint32_t>(data);
buffer.push_back(val & 0xFF);
buffer.push_back(val >> 8);
buffer.push_back(val >> 16);
buffer.push_back(val >> 24);
break;
}
case RPN_SYM:
case RPN_BANK_SYM: {
// The command ID is followed by a four-byte symbol ID
assume(std::holds_alternative<std::string>(data));
// The symbol name is always written expanded
Symbol *sym = sym_FindExactSymbol(std::get<std::string>(data));
out_RegisterSymbol(*sym); // Ensure that `sym->ID` is set
buffer.push_back(sym->ID & 0xFF);
buffer.push_back(sym->ID >> 8);
buffer.push_back(sym->ID >> 16);
buffer.push_back(sym->ID >> 24);
break;
}
case RPN_BANK_SECT:
case RPN_SIZEOF_SECT:
case RPN_STARTOF_SECT: {
// The command ID is followed by a NUL-terminated section name string
assume(std::holds_alternative<std::string>(data));
std::string const &name = std::get<std::string>(data);
buffer.reserve(buffer.size() + name.length() + 1);
buffer.insert(buffer.end(), RANGE(name));
buffer.push_back('\0');
break;
}
case RPN_SIZEOF_SECTTYPE:
case RPN_STARTOF_SECTTYPE:
case RPN_BIT_INDEX:
// The command ID is followed by a byte value
assume(std::holds_alternative<uint8_t>(data));
buffer.push_back(std::get<uint8_t>(data));
break;
default:
// Other command IDs are not followed by anything
assume(std::holds_alternative<std::monostate>(data));
break;
}
}