From dd43723e208a3b1f35bfb9c64cb2a7f40babe32c Mon Sep 17 00:00:00 2001 From: Sylvie <35663410+Rangi42@users.noreply.github.com> Date: Fri, 22 Mar 2024 04:41:04 -0400 Subject: [PATCH] Use methods for RPN `Expression` (#1372) --- include/asm/rpn.hpp | 74 ++--- src/asm/parser.y | 146 +++++----- src/asm/rpn.cpp | 619 +++++++++++++++++++----------------------- src/link/sdas_obj.cpp | 4 +- 4 files changed, 397 insertions(+), 446 deletions(-) diff --git a/include/asm/rpn.hpp b/include/asm/rpn.hpp index c2e47046..53155ba1 100644 --- a/include/asm/rpn.hpp +++ b/include/asm/rpn.hpp @@ -12,16 +12,12 @@ struct Symbol; struct Expression { - int32_t val; // If the expression's value is known, it's here - std::string reason; // Why the expression is not known, if it isn't - bool isKnown; // Whether the expression's value is known at assembly time - bool isSymbol; // Whether the expression represents a symbol suitable for const diffing - std::vector rpn; // Bytes serializing the RPN expression - uint32_t rpnPatchSize; // Size the expression will take in the object file - - int32_t getConstVal() const; - Symbol const *symbolOf() const; - bool isDiffConstant(Symbol const *symName) const; + int32_t val = 0; // If the expression's value is known, it's here + std::string reason{}; // Why the expression is not known, if it isn't + bool isKnown = true; // Whether the expression's value is known at assembly time + bool isSymbol = false; // Whether the expression represents a symbol suitable for const diffing + std::vector rpn{}; // Bytes serializing the RPN expression + uint32_t rpnPatchSize = 0; // Size the expression will take in the object file Expression() = default; Expression(Expression &&) = default; @@ -31,27 +27,43 @@ struct Expression { #endif Expression &operator=(Expression &&) = default; + + int32_t getConstVal() const; + Symbol const *symbolOf() const; + bool isDiffConstant(Symbol const *symName) const; + + void makeNumber(uint32_t value); + void makeSymbol(std::string const &symName); + void makeBankSymbol(std::string const &symName); + void makeBankSection(std::string const §Name); + void makeSizeOfSection(std::string const §Name); + void makeStartOfSection(std::string const §Name); + void makeSizeOfSectionType(SectionType type); + void makeStartOfSectionType(SectionType type); + void makeHigh(); + void makeLow(); + void makeNeg(); + void makeNot(); + void makeLogicNot(); + void makeBinaryOp(RPNCommand op, Expression &&src1, Expression const &src2); + + void makeCheckHRAM(); + void makeCheckRST(); + + void checkNBit(uint8_t n) const; + +private: + void clear(); + uint8_t *reserveSpace(uint32_t size); + uint8_t *reserveSpace(uint32_t size, uint32_t patchSize); + + // Makes an expression "not known", also setting its error message + template + void makeUnknown(Ts... parts) { + isKnown = false; + reason.clear(); + (reason.append(parts), ...); + } }; -void rpn_Number(Expression &expr, uint32_t val); -void rpn_Symbol(Expression &expr, std::string const &symName); -void rpn_LOGNOT(Expression &expr, Expression &&src); -void rpn_BinaryOp(RPNCommand op, Expression &expr, Expression &&src1, Expression const &src2); -void rpn_HIGH(Expression &expr, Expression &&src); -void rpn_LOW(Expression &expr, Expression &&src); -void rpn_ISCONST(Expression &expr, Expression const &src); -void rpn_NEG(Expression &expr, Expression &&src); -void rpn_NOT(Expression &expr, Expression &&src); -void rpn_BankSymbol(Expression &expr, std::string const &symName); -void rpn_BankSection(Expression &expr, std::string const §ionName); -void rpn_BankSelf(Expression &expr); -void rpn_SizeOfSection(Expression &expr, std::string const §ionName); -void rpn_StartOfSection(Expression &expr, std::string const §ionName); -void rpn_SizeOfSectionType(Expression &expr, SectionType type); -void rpn_StartOfSectionType(Expression &expr, SectionType type); - -void rpn_CheckHRAM(Expression &expr); -void rpn_CheckRST(Expression &expr); -void rpn_CheckNBit(Expression const &expr, uint8_t n); - #endif // RGBDS_ASM_RPN_H diff --git a/src/asm/parser.y b/src/asm/parser.y index c70737df..8fe0dc1d 100644 --- a/src/asm/parser.y +++ b/src/asm/parser.y @@ -1272,40 +1272,41 @@ constlist_32bit_entry: reloc_8bit: relocexpr { - rpn_CheckNBit($1, 8); $$ = std::move($1); + $$.checkNBit(8); } ; reloc_8bit_no_str: relocexpr_no_str { - rpn_CheckNBit($1, 8); $$ = std::move($1); + $$.checkNBit(8); } ; reloc_8bit_offset: OP_ADD relocexpr { - rpn_CheckNBit($2, 8); $$ = std::move($2); + $$.checkNBit(8); } | OP_SUB relocexpr { - rpn_NEG($$, std::move($2)); - rpn_CheckNBit($$, 8); + $$ = std::move($2); + $$.makeNeg(); + $$.checkNBit(8); } ; reloc_16bit: relocexpr { - rpn_CheckNBit($1, 16); $$ = std::move($1); + $$.checkNBit(16); } ; reloc_16bit_no_str: relocexpr_no_str { - rpn_CheckNBit($1, 16); $$ = std::move($1); + $$.checkNBit(16); } ; @@ -1317,189 +1318,194 @@ relocexpr: std::vector output; charmap_Convert($1, output); - rpn_Number($$, str2int2(output)); + $$.makeNumber(str2int2(output)); } ; relocexpr_no_str: scoped_anon_id { - rpn_Symbol($$, $1); + $$.makeSymbol($1); } | NUMBER { - rpn_Number($$, $1); + $$.makeNumber($1); } | OP_LOGICNOT relocexpr %prec NEG { - rpn_LOGNOT($$, std::move($2)); + $$ = std::move($2); + $$.makeLogicNot(); } | relocexpr OP_LOGICOR relocexpr { - rpn_BinaryOp(RPN_LOGOR, $$, std::move($1), $3); + $$.makeBinaryOp(RPN_LOGOR, std::move($1), $3); } | relocexpr OP_LOGICAND relocexpr { - rpn_BinaryOp(RPN_LOGAND, $$, std::move($1), $3); + $$.makeBinaryOp(RPN_LOGAND, std::move($1), $3); } | relocexpr OP_LOGICEQU relocexpr { - rpn_BinaryOp(RPN_LOGEQ, $$, std::move($1), $3); + $$.makeBinaryOp(RPN_LOGEQ, std::move($1), $3); } | relocexpr OP_LOGICGT relocexpr { - rpn_BinaryOp(RPN_LOGGT, $$, std::move($1), $3); + $$.makeBinaryOp(RPN_LOGGT, std::move($1), $3); } | relocexpr OP_LOGICLT relocexpr { - rpn_BinaryOp(RPN_LOGLT, $$, std::move($1), $3); + $$.makeBinaryOp(RPN_LOGLT, std::move($1), $3); } | relocexpr OP_LOGICGE relocexpr { - rpn_BinaryOp(RPN_LOGGE, $$, std::move($1), $3); + $$.makeBinaryOp(RPN_LOGGE, std::move($1), $3); } | relocexpr OP_LOGICLE relocexpr { - rpn_BinaryOp(RPN_LOGLE, $$, std::move($1), $3); + $$.makeBinaryOp(RPN_LOGLE, std::move($1), $3); } | relocexpr OP_LOGICNE relocexpr { - rpn_BinaryOp(RPN_LOGNE, $$, std::move($1), $3); + $$.makeBinaryOp(RPN_LOGNE, std::move($1), $3); } | relocexpr OP_ADD relocexpr { - rpn_BinaryOp(RPN_ADD, $$, std::move($1), $3); + $$.makeBinaryOp(RPN_ADD, std::move($1), $3); } | relocexpr OP_SUB relocexpr { - rpn_BinaryOp(RPN_SUB, $$, std::move($1), $3); + $$.makeBinaryOp(RPN_SUB, std::move($1), $3); } | relocexpr OP_XOR relocexpr { - rpn_BinaryOp(RPN_XOR, $$, std::move($1), $3); + $$.makeBinaryOp(RPN_XOR, std::move($1), $3); } | relocexpr OP_OR relocexpr { - rpn_BinaryOp(RPN_OR, $$, std::move($1), $3); + $$.makeBinaryOp(RPN_OR, std::move($1), $3); } | relocexpr OP_AND relocexpr { - rpn_BinaryOp(RPN_AND, $$, std::move($1), $3); + $$.makeBinaryOp(RPN_AND, std::move($1), $3); } | relocexpr OP_SHL relocexpr { - rpn_BinaryOp(RPN_SHL, $$, std::move($1), $3); + $$.makeBinaryOp(RPN_SHL, std::move($1), $3); } | relocexpr OP_SHR relocexpr { - rpn_BinaryOp(RPN_SHR, $$, std::move($1), $3); + $$.makeBinaryOp(RPN_SHR, std::move($1), $3); } | relocexpr OP_USHR relocexpr { - rpn_BinaryOp(RPN_USHR, $$, std::move($1), $3); + $$.makeBinaryOp(RPN_USHR, std::move($1), $3); } | relocexpr OP_MUL relocexpr { - rpn_BinaryOp(RPN_MUL, $$, std::move($1), $3); + $$.makeBinaryOp(RPN_MUL, std::move($1), $3); } | relocexpr OP_DIV relocexpr { - rpn_BinaryOp(RPN_DIV, $$, std::move($1), $3); + $$.makeBinaryOp(RPN_DIV, std::move($1), $3); } | relocexpr OP_MOD relocexpr { - rpn_BinaryOp(RPN_MOD, $$, std::move($1), $3); + $$.makeBinaryOp(RPN_MOD, std::move($1), $3); } | relocexpr OP_EXP relocexpr { - rpn_BinaryOp(RPN_EXP, $$, std::move($1), $3); + $$.makeBinaryOp(RPN_EXP, std::move($1), $3); } | OP_ADD relocexpr %prec NEG { $$ = std::move($2); } | OP_SUB relocexpr %prec NEG { - rpn_NEG($$, std::move($2)); + $$ = std::move($2); + $$.makeNeg(); } | OP_NOT relocexpr %prec NEG { - rpn_NOT($$, std::move($2)); + $$ = std::move($2); + $$.makeNot(); } | OP_HIGH LPAREN relocexpr RPAREN { - rpn_HIGH($$, std::move($3)); + $$ = std::move($3); + $$.makeHigh(); } | OP_LOW LPAREN relocexpr RPAREN { - rpn_LOW($$, std::move($3)); + $$ = std::move($3); + $$.makeLow(); } | OP_ISCONST LPAREN relocexpr RPAREN { - rpn_ISCONST($$, $3); + $$.makeNumber($3.isKnown); } | OP_BANK LPAREN scoped_anon_id RPAREN { // '@' is also an ID; it is handled here - rpn_BankSymbol($$, $3); + $$.makeBankSymbol($3); } | OP_BANK LPAREN string RPAREN { - rpn_BankSection($$, $3); + $$.makeBankSection($3); } | OP_SIZEOF LPAREN string RPAREN { - rpn_SizeOfSection($$, $3); + $$.makeSizeOfSection($3); } | OP_STARTOF LPAREN string RPAREN { - rpn_StartOfSection($$, $3); + $$.makeStartOfSection($3); } | OP_SIZEOF LPAREN sect_type RPAREN { - rpn_SizeOfSectionType($$, (SectionType)$3); + $$.makeSizeOfSectionType((SectionType)$3); } | OP_STARTOF LPAREN sect_type RPAREN { - rpn_StartOfSectionType($$, (SectionType)$3); + $$.makeStartOfSectionType((SectionType)$3); } | OP_DEF { lexer_ToggleStringExpansion(false); } LPAREN scoped_anon_id RPAREN { - rpn_Number($$, sym_FindScopedValidSymbol($4) != nullptr); + $$.makeNumber(sym_FindScopedValidSymbol($4) != nullptr); lexer_ToggleStringExpansion(true); } | OP_ROUND LPAREN const opt_q_arg RPAREN { - rpn_Number($$, fix_Round($3, $4)); + $$.makeNumber(fix_Round($3, $4)); } | OP_CEIL LPAREN const opt_q_arg RPAREN { - rpn_Number($$, fix_Ceil($3, $4)); + $$.makeNumber(fix_Ceil($3, $4)); } | OP_FLOOR LPAREN const opt_q_arg RPAREN { - rpn_Number($$, fix_Floor($3, $4)); + $$.makeNumber(fix_Floor($3, $4)); } | OP_FDIV LPAREN const COMMA const opt_q_arg RPAREN { - rpn_Number($$, fix_Div($3, $5, $6)); + $$.makeNumber(fix_Div($3, $5, $6)); } | OP_FMUL LPAREN const COMMA const opt_q_arg RPAREN { - rpn_Number($$, fix_Mul($3, $5, $6)); + $$.makeNumber(fix_Mul($3, $5, $6)); } | OP_FMOD LPAREN const COMMA const opt_q_arg RPAREN { - rpn_Number($$, fix_Mod($3, $5, $6)); + $$.makeNumber(fix_Mod($3, $5, $6)); } | OP_POW LPAREN const COMMA const opt_q_arg RPAREN { - rpn_Number($$, fix_Pow($3, $5, $6)); + $$.makeNumber(fix_Pow($3, $5, $6)); } | OP_LOG LPAREN const COMMA const opt_q_arg RPAREN { - rpn_Number($$, fix_Log($3, $5, $6)); + $$.makeNumber(fix_Log($3, $5, $6)); } | OP_SIN LPAREN const opt_q_arg RPAREN { - rpn_Number($$, fix_Sin($3, $4)); + $$.makeNumber(fix_Sin($3, $4)); } | OP_COS LPAREN const opt_q_arg RPAREN { - rpn_Number($$, fix_Cos($3, $4)); + $$.makeNumber(fix_Cos($3, $4)); } | OP_TAN LPAREN const opt_q_arg RPAREN { - rpn_Number($$, fix_Tan($3, $4)); + $$.makeNumber(fix_Tan($3, $4)); } | OP_ASIN LPAREN const opt_q_arg RPAREN { - rpn_Number($$, fix_ASin($3, $4)); + $$.makeNumber(fix_ASin($3, $4)); } | OP_ACOS LPAREN const opt_q_arg RPAREN { - rpn_Number($$, fix_ACos($3, $4)); + $$.makeNumber(fix_ACos($3, $4)); } | OP_ATAN LPAREN const opt_q_arg RPAREN { - rpn_Number($$, fix_ATan($3, $4)); + $$.makeNumber(fix_ATan($3, $4)); } | OP_ATAN2 LPAREN const COMMA const opt_q_arg RPAREN { - rpn_Number($$, fix_ATan2($3, $5, $6)); + $$.makeNumber(fix_ATan2($3, $5, $6)); } | OP_STRCMP LPAREN string COMMA string RPAREN { - rpn_Number($$, $3.compare($5)); + $$.makeNumber($3.compare($5)); } | OP_STRIN LPAREN string COMMA string RPAREN { auto pos = $3.find($5); - rpn_Number($$, pos != std::string::npos ? pos + 1 : 0); + $$.makeNumber(pos != std::string::npos ? pos + 1 : 0); } | OP_STRRIN LPAREN string COMMA string RPAREN { auto pos = $3.rfind($5); - rpn_Number($$, pos != std::string::npos ? pos + 1 : 0); + $$.makeNumber(pos != std::string::npos ? pos + 1 : 0); } | OP_STRLEN LPAREN string RPAREN { - rpn_Number($$, strlenUTF8($3)); + $$.makeNumber(strlenUTF8($3)); } | OP_CHARLEN LPAREN string RPAREN { - rpn_Number($$, charlenUTF8($3)); + $$.makeNumber(charlenUTF8($3)); } | OP_INCHARMAP LPAREN string RPAREN { - rpn_Number($$, charmap_HasChar($3)); + $$.makeNumber(charmap_HasChar($3)); } | LPAREN relocexpr RPAREN { $$ = std::move($2); @@ -1941,13 +1947,13 @@ z80_ldd: z80_ldio: Z80_LDH MODE_A COMMA op_mem_ind { - rpn_CheckHRAM($4); + $4.makeCheckHRAM(); sect_AbsByte(0xF0); sect_RelByte($4, 1); } | Z80_LDH op_mem_ind COMMA MODE_A { - rpn_CheckHRAM($2); + $2.makeCheckHRAM(); sect_AbsByte(0xE0); sect_RelByte($2, 1); @@ -2200,7 +2206,7 @@ z80_rrca: z80_rst: Z80_RST reloc_8bit { - rpn_CheckRST($2); + $2.makeCheckRST(); if (!$2.isKnown) sect_RelByte($2, 0); else @@ -2720,9 +2726,9 @@ static void compoundAssignment(std::string const &symName, RPNCommand op, int32_ Expression oldExpr, constExpr, newExpr; int32_t newValue; - rpn_Symbol(oldExpr, symName); - rpn_Number(constExpr, constValue); - rpn_BinaryOp(op, newExpr, std::move(oldExpr), constExpr); + oldExpr.makeSymbol(symName); + constExpr.makeNumber(constValue); + newExpr.makeBinaryOp(op, std::move(oldExpr), constExpr); newValue = newExpr.getConstVal(); sym_AddVar(symName, newValue); } diff --git a/src/asm/rpn.cpp b/src/asm/rpn.cpp index 97a10d71..5ad88522 100644 --- a/src/asm/rpn.cpp +++ b/src/asm/rpn.cpp @@ -20,231 +20,24 @@ #include "asm/symbol.hpp" #include "asm/warning.hpp" -// Init a RPN expression -static void initExpression(Expression &expr) { - expr.val = 0; - expr.reason.clear(); - expr.isKnown = true; - expr.isSymbol = false; - expr.rpn.clear(); - expr.rpnPatchSize = 0; +void Expression::clear() { + val = 0; + reason.clear(); + isKnown = true; + isSymbol = false; + rpn.clear(); + rpnPatchSize = 0; } -// Makes an expression "not known", also setting its error message -template -static void makeUnknown(Expression &expr, Ts... parts) { - expr.isKnown = false; - expr.reason.clear(); - (expr.reason.append(parts), ...); +uint8_t *Expression::reserveSpace(uint32_t size) { + return reserveSpace(size, size); } -static uint8_t *reserveSpace(Expression &expr, uint32_t size) { - size_t curSize = expr.rpn.size(); - - expr.rpn.resize(curSize + size); - return &expr.rpn[curSize]; -} - -// Add symbols, constants and operators to expression -void rpn_Number(Expression &expr, uint32_t val) { - initExpression(expr); - expr.val = val; -} - -void rpn_Symbol(Expression &expr, std::string const &symName) { - initExpression(expr); - - Symbol *sym = sym_FindScopedSymbol(symName); - - if (sym_IsPC(sym) && !sect_GetSymbolSection()) { - error("PC has no value outside a section\n"); - expr.val = 0; - } else if (!sym || !sym->isConstant()) { - expr.isSymbol = true; - - if (sym_IsPC(sym)) - makeUnknown(expr, "PC is not constant at assembly time"); - else - makeUnknown(expr, "'", symName, "' is not constant at assembly time"); - sym = sym_Ref(symName); - expr.rpnPatchSize += 5; // 1-byte opcode + 4-byte symbol ID - - size_t nameLen = sym->name.length() + 1; // Don't forget NUL! - uint8_t *ptr = reserveSpace(expr, nameLen + 1); - *ptr++ = RPN_SYM; - memcpy(ptr, sym->name.c_str(), nameLen); - } else { - expr.val = sym_GetConstantValue(symName); - } -} - -static void bankSelf(Expression &expr) { - initExpression(expr); - - if (!currentSection) { - error("PC has no bank outside a section\n"); - expr.val = 1; - } else if (currentSection->bank == (uint32_t)-1) { - makeUnknown(expr, "Current section's bank is not known"); - expr.rpnPatchSize++; - *reserveSpace(expr, 1) = RPN_BANK_SELF; - } else { - expr.val = currentSection->bank; - } -} - -void rpn_BankSymbol(Expression &expr, std::string const &symName) { - Symbol const *sym = sym_FindScopedSymbol(symName); - - // The @ symbol is treated differently. - if (sym_IsPC(sym)) { - bankSelf(expr); - return; - } - - initExpression(expr); - if (sym && !sym->isLabel()) { - error("BANK argument must be a label\n"); - expr.val = 1; - } else { - sym = sym_Ref(symName); - assert(sym); // If the symbol didn't exist, it should have been created - - if (sym->getSection() && sym->getSection()->bank != (uint32_t)-1) { - // Symbol's section is known and bank is fixed - expr.val = sym->getSection()->bank; - } else { - makeUnknown(expr, "\"", symName, "\"'s bank is not known"); - expr.rpnPatchSize += 5; // opcode + 4-byte sect ID - - size_t nameLen = sym->name.length() + 1; // Room for NUL! - uint8_t *ptr = reserveSpace(expr, nameLen + 1); - - *ptr++ = RPN_BANK_SYM; - memcpy(ptr, sym->name.c_str(), nameLen); - } - } -} - -void rpn_BankSection(Expression &expr, std::string const §ionName) { - initExpression(expr); - - Section *section = sect_FindSectionByName(sectionName); - - if (section && section->bank != (uint32_t)-1) { - expr.val = section->bank; - } else { - makeUnknown(expr, "Section \"", sectionName, "\"'s bank is not known"); - - size_t nameLen = sectionName.length() + 1; // Room for NUL! - uint8_t *ptr = reserveSpace(expr, nameLen + 1); - - expr.rpnPatchSize += nameLen + 1; - *ptr++ = RPN_BANK_SECT; - memcpy(ptr, sectionName.data(), nameLen); - } -} - -void rpn_SizeOfSection(Expression &expr, std::string const §ionName) { - initExpression(expr); - - Section *section = sect_FindSectionByName(sectionName); - - if (section && section->isSizeKnown()) { - expr.val = section->size; - } else { - makeUnknown(expr, "Section \"", sectionName, "\"'s size is not known"); - - size_t nameLen = sectionName.length() + 1; // Room for NUL! - uint8_t *ptr = reserveSpace(expr, nameLen + 1); - - expr.rpnPatchSize += nameLen + 1; - *ptr++ = RPN_SIZEOF_SECT; - memcpy(ptr, sectionName.data(), nameLen); - } -} - -void rpn_StartOfSection(Expression &expr, std::string const §ionName) { - initExpression(expr); - - Section *section = sect_FindSectionByName(sectionName); - - if (section && section->org != (uint32_t)-1) { - expr.val = section->org; - } else { - makeUnknown(expr, "Section \"", sectionName, "\"'s start is not known"); - - size_t nameLen = sectionName.length() + 1; // Room for NUL! - uint8_t *ptr = reserveSpace(expr, nameLen + 1); - - expr.rpnPatchSize += nameLen + 1; - *ptr++ = RPN_STARTOF_SECT; - memcpy(ptr, sectionName.data(), nameLen); - } -} - -void rpn_SizeOfSectionType(Expression &expr, SectionType type) { - initExpression(expr); - makeUnknown(expr, "Section type's size is not known"); - - uint8_t *ptr = reserveSpace(expr, 2); - - expr.rpnPatchSize += 2; - *ptr++ = RPN_SIZEOF_SECTTYPE; - *ptr++ = type; -} - -void rpn_StartOfSectionType(Expression &expr, SectionType type) { - initExpression(expr); - makeUnknown(expr, "Section type's start is not known"); - - uint8_t *ptr = reserveSpace(expr, 2); - - expr.rpnPatchSize += 2; - *ptr++ = RPN_STARTOF_SECTTYPE; - *ptr++ = type; -} - -void rpn_CheckHRAM(Expression &expr) { - expr.isSymbol = false; - - if (!expr.isKnown) { - expr.rpnPatchSize++; - *reserveSpace(expr, 1) = RPN_HRAM; - } else if (expr.val >= 0xFF00 && expr.val <= 0xFFFF) { - // That range is valid, but only keep the lower byte - expr.val &= 0xFF; - } else if (expr.val < 0 || expr.val > 0xFF) { - error("Source address $%" PRIx32 " not between $FF00 to $FFFF\n", expr.val); - } -} - -void rpn_CheckRST(Expression &expr) { - if (expr.isKnown) { - // A valid RST address must be masked with 0x38 - if (expr.val & ~0x38) - error("Invalid address $%" PRIx32 " for RST\n", expr.val); - // The target is in the "0x38" bits, all other bits are set - expr.val |= 0xC7; - } else { - expr.rpnPatchSize++; - *reserveSpace(expr, 1) = RPN_RST; - } -} - -// Checks that an RPN expression's value fits within N bits (signed or unsigned) -void rpn_CheckNBit(Expression const &expr, uint8_t n) { - assert(n != 0); // That doesn't make sense - assert(n < CHAR_BIT * sizeof(int)); // Otherwise `1 << n` is UB - - if (expr.isKnown) { - int32_t val = expr.val; - - if (val < -(1 << n) || val >= 1 << n) - warning(WARNING_TRUNCATION_1, "Expression must be %u-bit\n", n); - else if (val < -(1 << (n - 1))) - warning(WARNING_TRUNCATION_2, "Expression must be %u-bit\n", n); - } +uint8_t *Expression::reserveSpace(uint32_t size, uint32_t patchSize) { + rpnPatchSize += patchSize; + size_t curSize = rpn.size(); + rpn.resize(curSize + size); + return &rpn[curSize]; } int32_t Expression::getConstVal() const { @@ -255,18 +48,6 @@ int32_t Expression::getConstVal() const { return val; } -void rpn_LOGNOT(Expression &expr, Expression &&src) { - expr = std::move(src); - expr.isSymbol = false; - - if (expr.isKnown) { - expr.val = !expr.val; - } else { - expr.rpnPatchSize++; - *reserveSpace(expr, 1) = RPN_LOGNOT; - } -} - Symbol const *Expression::symbolOf() const { if (!isSymbol) return nullptr; @@ -280,9 +61,140 @@ bool Expression::isDiffConstant(Symbol const *sym) const { if (!sym1 || !sym || sym1->type != SYM_LABEL || sym->type != SYM_LABEL) return false; - Section const *section1 = sym1->getSection(); - Section const *section2 = sym->getSection(); - return section1 && (section1 == section2); + Section const *sect1 = sym1->getSection(); + Section const *sect2 = sym->getSection(); + return sect1 && (sect1 == sect2); +} + +void Expression::makeNumber(uint32_t value) { + clear(); + val = value; +} + +void Expression::makeSymbol(std::string const &symName) { + clear(); + if (Symbol *sym = sym_FindScopedSymbol(symName); sym_IsPC(sym) && !sect_GetSymbolSection()) { + error("PC has no value outside a section\n"); + val = 0; + } else if (!sym || !sym->isConstant()) { + isSymbol = true; + + if (sym_IsPC(sym)) + makeUnknown("PC is not constant at assembly time"); + else + makeUnknown("'", symName, "' is not constant at assembly time"); + sym = sym_Ref(symName); + + size_t nameLen = sym->name.length() + 1; // Don't forget NUL! + + // 1-byte opcode + 4-byte symbol ID + uint8_t *ptr = reserveSpace(nameLen + 1, 5); + *ptr++ = RPN_SYM; + memcpy(ptr, sym->name.c_str(), nameLen); + } else { + val = sym_GetConstantValue(symName); + } +} + +void Expression::makeBankSymbol(std::string const &symName) { + clear(); + if (Symbol const *sym = sym_FindScopedSymbol(symName); sym_IsPC(sym)) { + // The @ symbol is treated differently. + if (!currentSection) { + error("PC has no bank outside a section\n"); + val = 1; + } else if (currentSection->bank == (uint32_t)-1) { + makeUnknown("Current section's bank is not known"); + + *reserveSpace(1) = RPN_BANK_SELF; + } else { + val = currentSection->bank; + } + return; + } else if (sym && !sym->isLabel()) { + error("BANK argument must be a label\n"); + val = 1; + } else { + sym = sym_Ref(symName); + assert(sym); // If the symbol didn't exist, it should have been created + + if (sym->getSection() && sym->getSection()->bank != (uint32_t)-1) { + // Symbol's section is known and bank is fixed + val = sym->getSection()->bank; + } else { + makeUnknown("\"", symName, "\"'s bank is not known"); + + size_t nameLen = sym->name.length() + 1; // Room for NUL! + + // 1-byte opcode + 4-byte sect ID + uint8_t *ptr = reserveSpace(nameLen + 1, 5); + *ptr++ = RPN_BANK_SYM; + memcpy(ptr, sym->name.c_str(), nameLen); + } + } +} + +void Expression::makeBankSection(std::string const §Name) { + clear(); + if (Section *sect = sect_FindSectionByName(sectName); sect && sect->bank != (uint32_t)-1) { + val = sect->bank; + } else { + makeUnknown("Section \"", sectName, "\"'s bank is not known"); + + size_t nameLen = sectName.length() + 1; // Room for NUL! + + uint8_t *ptr = reserveSpace(nameLen + 1); + *ptr++ = RPN_BANK_SECT; + memcpy(ptr, sectName.data(), nameLen); + } +} + +void Expression::makeSizeOfSection(std::string const §Name) { + clear(); + if (Section *sect = sect_FindSectionByName(sectName); sect && sect->isSizeKnown()) { + val = sect->size; + } else { + makeUnknown("Section \"", sectName, "\"'s size is not known"); + + size_t nameLen = sectName.length() + 1; // Room for NUL! + + uint8_t *ptr = reserveSpace(nameLen + 1); + *ptr++ = RPN_SIZEOF_SECT; + memcpy(ptr, sectName.data(), nameLen); + } +} + +void Expression::makeStartOfSection(std::string const §Name) { + clear(); + if (Section *sect = sect_FindSectionByName(sectName); sect && sect->org != (uint32_t)-1) { + val = sect->org; + } else { + makeUnknown("Section \"", sectName, "\"'s start is not known"); + + size_t nameLen = sectName.length() + 1; // Room for NUL! + + uint8_t *ptr = reserveSpace(nameLen + 1); + *ptr++ = RPN_STARTOF_SECT; + memcpy(ptr, sectName.data(), nameLen); + } +} + +void Expression::makeSizeOfSectionType(SectionType type) { + clear(); + makeUnknown("Section type's size is not known"); + + uint8_t *ptr = reserveSpace(2); + *ptr++ = RPN_SIZEOF_SECTTYPE; + *ptr = type; +} + +void Expression::makeStartOfSectionType(SectionType type) { + clear(); + makeUnknown("Section type's start is not known"); + + uint8_t *ptr = reserveSpace(2); + *ptr++ = RPN_STARTOF_SECTTYPE; + *ptr = type; } /* @@ -325,56 +237,102 @@ static int32_t tryConstMask(Expression const &lhs, Expression const &rhs) { return (symbolOfs + sect.alignOfs) & ~unknownBits; } -void rpn_BinaryOp(RPNCommand op, Expression &expr, Expression &&src1, Expression const &src2) { - initExpression(expr); - expr.isSymbol = false; - int32_t constMaskVal; +void Expression::makeHigh() { + isSymbol = false; + if (isKnown) { + val = (uint32_t)val >> 8 & 0xFF; + } else { + uint8_t bytes[] = {RPN_CONST, 8, 0, 0, 0, RPN_SHR, RPN_CONST, 0xFF, 0, 0, 0, RPN_AND}; + memcpy(reserveSpace(sizeof(bytes)), bytes, sizeof(bytes)); + } +} + +void Expression::makeLow() { + isSymbol = false; + if (isKnown) { + val = val & 0xFF; + } else { + uint8_t bytes[] = {RPN_CONST, 0xFF, 0, 0, 0, RPN_AND}; + + memcpy(reserveSpace(sizeof(bytes)), bytes, sizeof(bytes)); + } +} + +void Expression::makeNeg() { + isSymbol = false; + if (isKnown) { + val = -(uint32_t)val; + } else { + *reserveSpace(1) = RPN_NEG; + } +} + +void Expression::makeNot() { + isSymbol = false; + if (isKnown) { + val = ~val; + } else { + *reserveSpace(1) = RPN_NOT; + } +} + +void Expression::makeLogicNot() { + isSymbol = false; + if (isKnown) { + val = !val; + } else { + *reserveSpace(1) = RPN_LOGNOT; + } +} + +void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const &src2) { + clear(); // First, check if the expression is known - expr.isKnown = src1.isKnown && src2.isKnown; - if (expr.isKnown) { + isKnown = src1.isKnown && src2.isKnown; + if (isKnown) { // If both expressions are known, just compute the value uint32_t uleft = src1.val, uright = src2.val; switch (op) { case RPN_LOGOR: - expr.val = src1.val || src2.val; + val = src1.val || src2.val; break; case RPN_LOGAND: - expr.val = src1.val && src2.val; + val = src1.val && src2.val; break; case RPN_LOGEQ: - expr.val = src1.val == src2.val; + val = src1.val == src2.val; break; case RPN_LOGGT: - expr.val = src1.val > src2.val; + val = src1.val > src2.val; break; case RPN_LOGLT: - expr.val = src1.val < src2.val; + val = src1.val < src2.val; break; case RPN_LOGGE: - expr.val = src1.val >= src2.val; + val = src1.val >= src2.val; break; case RPN_LOGLE: - expr.val = src1.val <= src2.val; + val = src1.val <= src2.val; break; case RPN_LOGNE: - expr.val = src1.val != src2.val; + val = src1.val != src2.val; break; case RPN_ADD: - expr.val = uleft + uright; + val = uleft + uright; break; case RPN_SUB: - expr.val = uleft - uright; + val = uleft - uright; break; case RPN_XOR: - expr.val = src1.val ^ src2.val; + val = src1.val ^ src2.val; break; case RPN_OR: - expr.val = src1.val | src2.val; + val = src1.val | src2.val; break; case RPN_AND: - expr.val = src1.val & src2.val; + val = src1.val & src2.val; break; case RPN_SHL: if (src2.val < 0) @@ -387,7 +345,7 @@ void rpn_BinaryOp(RPNCommand op, Expression &expr, Expression &&src1, Expression WARNING_SHIFT_AMOUNT, "Shifting left by large amount %" PRId32 "\n", src2.val ); - expr.val = op_shift_left(src1.val, src2.val); + val = op_shift_left(src1.val, src2.val); break; case RPN_SHR: if (src1.val < 0) @@ -405,7 +363,7 @@ void rpn_BinaryOp(RPNCommand op, Expression &expr, Expression &&src1, Expression WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32 "\n", src2.val ); - expr.val = op_shift_right(src1.val, src2.val); + val = op_shift_right(src1.val, src2.val); break; case RPN_USHR: if (src2.val < 0) @@ -420,10 +378,10 @@ void rpn_BinaryOp(RPNCommand op, Expression &expr, Expression &&src1, Expression WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32 "\n", src2.val ); - expr.val = op_shift_right_unsigned(src1.val, src2.val); + val = op_shift_right_unsigned(src1.val, src2.val); break; case RPN_MUL: - expr.val = uleft * uright; + val = uleft * uright; break; case RPN_DIV: if (src2.val == 0) @@ -436,9 +394,9 @@ void rpn_BinaryOp(RPNCommand op, Expression &expr, Expression &&src1, Expression INT32_MIN, INT32_MIN ); - expr.val = INT32_MIN; + val = INT32_MIN; } else { - expr.val = op_divide(src1.val, src2.val); + val = op_divide(src1.val, src2.val); } break; case RPN_MOD: @@ -446,15 +404,15 @@ void rpn_BinaryOp(RPNCommand op, Expression &expr, Expression &&src1, Expression fatalerror("Modulo by zero\n"); if (src1.val == INT32_MIN && src2.val == -1) - expr.val = 0; + val = 0; else - expr.val = op_modulo(src1.val, src2.val); + val = op_modulo(src1.val, src2.val); break; case RPN_EXP: if (src2.val < 0) fatalerror("Exponentiation by negative power\n"); - expr.val = op_exponent(src1.val, src2.val); + val = op_exponent(src1.val, src2.val); break; case RPN_NEG: @@ -473,16 +431,15 @@ void rpn_BinaryOp(RPNCommand op, Expression &expr, Expression &&src1, Expression case RPN_SYM: fatalerror("%d is not a binary operator\n", op); } - } else if (op == RPN_SUB && src1.isDiffConstant(src2.symbolOf())) { Symbol const &symbol1 = *src1.symbolOf(); Symbol const &symbol2 = *src2.symbolOf(); - expr.val = symbol1.getValue() - symbol2.getValue(); - expr.isKnown = true; - } else if (op == RPN_AND && (constMaskVal = tryConstMask(src1, src2)) != -1) { - expr.val = constMaskVal; - expr.isKnown = true; + val = symbol1.getValue() - symbol2.getValue(); + isKnown = true; + } else if (int32_t constVal; op == RPN_AND && (constVal = tryConstMask(src1, src2)) != -1) { + val = constVal; + isKnown = true; } else { // If it's not known, start computing the RPN expression @@ -496,23 +453,23 @@ void rpn_BinaryOp(RPNCommand op, Expression &expr, Expression &&src1, Expression (uint8_t)(lval >> 16), (uint8_t)(lval >> 24), }; - expr.rpnPatchSize = sizeof(bytes); - expr.rpn.clear(); - memcpy(reserveSpace(expr, sizeof(bytes)), bytes, sizeof(bytes)); + rpn.clear(); + rpnPatchSize = 0; + memcpy(reserveSpace(sizeof(bytes)), bytes, sizeof(bytes)); // Use the other expression's un-const reason - expr.reason = src2.reason; + reason = src2.reason; } else { // Otherwise just reuse its RPN buffer - expr.rpnPatchSize = src1.rpnPatchSize; - std::swap(expr.rpn, src1.rpn); - expr.reason = src1.reason; + rpnPatchSize = src1.rpnPatchSize; + std::swap(rpn, src1.rpn); + reason = src1.reason; } // Now, merge the right expression into the left one - uint8_t const *ptr = nullptr; - uint32_t len = 0; - uint32_t patchSize = 0; + uint8_t const *srcBytes = nullptr; + uint32_t srcLen = 0; + uint32_t srcPatchSize = 0; // If the right expression is constant, merge a shim instead uint32_t rval = src2.val; @@ -524,80 +481,56 @@ void rpn_BinaryOp(RPNCommand op, Expression &expr, Expression &&src1, Expression (uint8_t)(rval >> 24), }; if (src2.isKnown) { - ptr = bytes; - len = sizeof(bytes); - patchSize = sizeof(bytes); + srcBytes = bytes; + srcLen = sizeof(bytes); + srcPatchSize = sizeof(bytes); } else { - ptr = src2.rpn.data(); // Pointer to the right RPN - len = src2.rpn.size(); // Size of the right RPN - patchSize = src2.rpnPatchSize; + srcBytes = src2.rpn.data(); // Pointer to the right RPN + srcLen = src2.rpn.size(); // Size of the right RPN + srcPatchSize = src2.rpnPatchSize; } // Copy the right RPN and append the operator - uint8_t *buf = reserveSpace(expr, len + 1); - - if (ptr) - // If there was none, `memcpy(buf, nullptr, 0)` would be UB - memcpy(buf, ptr, len); - buf[len] = op; - - expr.rpnPatchSize += patchSize + 1; + uint8_t *ptr = reserveSpace(srcLen + 1, srcPatchSize + 1); + if (srcBytes) + // If there were no `srcBytes`, then `memcpy(ptr, nullptr, 0)` would be UB + memcpy(ptr, srcBytes, srcLen); + ptr[srcLen] = op; } } -void rpn_HIGH(Expression &expr, Expression &&src) { - expr = std::move(src); - expr.isSymbol = false; +void Expression::makeCheckHRAM() { + isSymbol = false; + if (!isKnown) { + *reserveSpace(1) = RPN_HRAM; + } else if (val >= 0xFF00 && val <= 0xFFFF) { + // That range is valid, but only keep the lower byte + val &= 0xFF; + } else if (val < 0 || val > 0xFF) { + error("Source address $%" PRIx32 " not between $FF00 to $FFFF\n", val); + } +} - if (expr.isKnown) { - expr.val = (uint32_t)expr.val >> 8 & 0xFF; +void Expression::makeCheckRST() { + if (isKnown) { + // A valid RST address must be masked with 0x38 + if (val & ~0x38) + error("Invalid address $%" PRIx32 " for RST\n", val); + // The target is in the "0x38" bits, all other bits are set + val |= 0xC7; } else { - uint8_t bytes[] = {RPN_CONST, 8, 0, 0, 0, RPN_SHR, RPN_CONST, 0xFF, 0, 0, 0, RPN_AND}; - expr.rpnPatchSize += sizeof(bytes); - memcpy(reserveSpace(expr, sizeof(bytes)), bytes, sizeof(bytes)); + *reserveSpace(1) = RPN_RST; } } -void rpn_LOW(Expression &expr, Expression &&src) { - expr = std::move(src); - expr.isSymbol = false; +// Checks that an RPN expression's value fits within N bits (signed or unsigned) +void Expression::checkNBit(uint8_t n) const { + assert(n != 0); // That doesn't make sense + assert(n < CHAR_BIT * sizeof(int)); // Otherwise `1 << n` is UB - if (expr.isKnown) { - expr.val = expr.val & 0xFF; - } else { - uint8_t bytes[] = {RPN_CONST, 0xFF, 0, 0, 0, RPN_AND}; - - expr.rpnPatchSize += sizeof(bytes); - memcpy(reserveSpace(expr, sizeof(bytes)), bytes, sizeof(bytes)); - } -} - -void rpn_ISCONST(Expression &expr, Expression const &src) { - initExpression(expr); - expr.val = src.isKnown; - expr.isKnown = true; - expr.isSymbol = false; -} - -void rpn_NEG(Expression &expr, Expression &&src) { - expr = std::move(src); - expr.isSymbol = false; - - if (expr.isKnown) { - expr.val = -(uint32_t)expr.val; - } else { - expr.rpnPatchSize++; - *reserveSpace(expr, 1) = RPN_NEG; - } -} - -void rpn_NOT(Expression &expr, Expression &&src) { - expr = std::move(src); - expr.isSymbol = false; - - if (expr.isKnown) { - expr.val = ~expr.val; - } else { - expr.rpnPatchSize++; - *reserveSpace(expr, 1) = RPN_NOT; + if (isKnown) { + if (val < -(1 << n) || val >= 1 << n) + warning(WARNING_TRUNCATION_1, "Expression must be %u-bit\n", n); + else if (val < -(1 << (n - 1))) + warning(WARNING_TRUNCATION_2, "Expression must be %u-bit\n", n); } } diff --git a/src/link/sdas_obj.cpp b/src/link/sdas_obj.cpp index c83fda97..05395d59 100644 --- a/src/link/sdas_obj.cpp +++ b/src/link/sdas_obj.cpp @@ -264,7 +264,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector if (!strcmp(token, entry.section->name.c_str())) fatal(&where, lineNo, "Area \"%s\" already defined earlier", token); } - char const *sectionName = token; // We'll deal with the section's name depending on type + char const *sectName = token; // We'll deal with the section's name depending on type expectToken("size", 'A'); @@ -297,7 +297,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector curSection->name.append(where.name()); curSection->name.append(" "); } - curSection->name.append(sectionName); + curSection->name.append(sectName); expectToken("addr", 'A');