// SPDX-License-Identifier: MIT #include "link/patch.hpp" #include #include #include #include #include "helpers.hpp" // assume, clz, ctz #include "linkdefs.hpp" #include "opmath.hpp" #include "link/main.hpp" #include "link/section.hpp" #include "link/symbol.hpp" std::deque assertions; struct RPNStackEntry { int32_t value; bool errorFlag; // Whether the value is a placeholder inserted for error recovery }; std::deque rpnStack; static void pushRPN(int32_t value, bool comesFromError) { rpnStack.push_front({.value = value, .errorFlag = comesFromError}); } // This flag tracks whether the RPN op that is currently being evaluated // has popped any values with the error flag set. static bool isError = false; static int32_t popRPN(Patch const &patch) { if (rpnStack.empty()) { fatal(patch.src, patch.lineNo, "Internal error, RPN stack empty"); } RPNStackEntry entry = rpnStack.front(); rpnStack.pop_front(); isError |= entry.errorFlag; return entry.value; } // RPN operators static uint32_t getRPNByte(uint8_t const *&expression, int32_t &size, Patch const &patch) { if (!size--) { fatal(patch.src, patch.lineNo, "Internal error, RPN expression overread"); } return *expression++; } static Symbol const *getSymbol(std::vector const &symbolList, uint32_t index) { assume(index != UINT32_MAX); // PC needs to be handled specially, not here Symbol const &symbol = symbolList[index]; // If the symbol is defined elsewhere... if (symbol.type == SYMTYPE_IMPORT) { return sym_GetSymbol(symbol.name); } return &symbol; } // Compute a patch's value from its RPN string. static int32_t computeRPNExpr(Patch const &patch, std::vector const &fileSymbols) { uint8_t const *expression = patch.rpnExpression.data(); int32_t size = static_cast(patch.rpnExpression.size()); rpnStack.clear(); while (size > 0) { RPNCommand command = static_cast(getRPNByte(expression, size, patch)); int32_t value; isError = false; // Be VERY careful with two `popRPN` in the same expression. // C++ does not guarantee order of evaluation of operands! // So, if there are two `popRPN` in the same expression, make // sure the operation is commutative. switch (command) { case RPN_ADD: value = popRPN(patch) + popRPN(patch); break; case RPN_SUB: value = popRPN(patch); value = popRPN(patch) - value; break; case RPN_MUL: value = popRPN(patch) * popRPN(patch); break; case RPN_DIV: value = popRPN(patch); if (value == 0) { if (!isError) { error(patch.src, patch.lineNo, "Division by 0"); isError = true; } popRPN(patch); value = INT32_MAX; } else { value = op_divide(popRPN(patch), value); } break; case RPN_MOD: value = popRPN(patch); if (value == 0) { if (!isError) { error(patch.src, patch.lineNo, "Modulo by 0"); isError = true; } popRPN(patch); value = 0; } else { value = op_modulo(popRPN(patch), value); } break; case RPN_NEG: value = -popRPN(patch); break; case RPN_EXP: value = popRPN(patch); if (value < 0) { if (!isError) { error(patch.src, patch.lineNo, "Exponent by negative value %" PRId32, value); isError = true; } popRPN(patch); value = 0; } else { value = op_exponent(popRPN(patch), value); } break; case RPN_HIGH: value = (popRPN(patch) >> 8) & 0xFF; break; case RPN_LOW: value = popRPN(patch) & 0xFF; break; case RPN_BITWIDTH: value = popRPN(patch); value = value != 0 ? 32 - clz(static_cast(value)) : 0; break; case RPN_TZCOUNT: value = popRPN(patch); value = value != 0 ? ctz(static_cast(value)) : 32; break; case RPN_OR: value = popRPN(patch) | popRPN(patch); break; case RPN_AND: value = popRPN(patch) & popRPN(patch); break; case RPN_XOR: value = popRPN(patch) ^ popRPN(patch); break; case RPN_NOT: value = ~popRPN(patch); break; case RPN_LOGAND: value = popRPN(patch); value = popRPN(patch) && value; break; case RPN_LOGOR: value = popRPN(patch); value = popRPN(patch) || value; break; case RPN_LOGNOT: value = !popRPN(patch); break; case RPN_LOGEQ: value = popRPN(patch) == popRPN(patch); break; case RPN_LOGNE: value = popRPN(patch) != popRPN(patch); break; case RPN_LOGGT: value = popRPN(patch); value = popRPN(patch) > value; break; case RPN_LOGLT: value = popRPN(patch); value = popRPN(patch) < value; break; case RPN_LOGGE: value = popRPN(patch); value = popRPN(patch) >= value; break; case RPN_LOGLE: value = popRPN(patch); value = popRPN(patch) <= value; break; case RPN_SHL: value = popRPN(patch); value = op_shift_left(popRPN(patch), value); break; case RPN_SHR: value = popRPN(patch); value = op_shift_right(popRPN(patch), value); break; case RPN_USHR: value = popRPN(patch); value = op_shift_right_unsigned(popRPN(patch), value); break; case RPN_BANK_SYM: value = 0; for (uint8_t shift = 0; shift < 32; shift += 8) { value |= getRPNByte(expression, size, patch) << shift; } if (Symbol const *symbol = getSymbol(fileSymbols, value); !symbol) { error( patch.src, patch.lineNo, "Requested BANK() of symbol \"%s\", which was not found", fileSymbols[value].name.c_str() ); isError = true; value = 1; } else if (symbol->data.holds