//---------------------------------------------------------------------------// // 1100101 |_ mov #0, r4 __ // // 11 |_ <0xb380 %5c4> / _|_ _____ ___ // // 0110 |_ 3.50 -> 3.60 | _\ \ / _ (_-< // // |_ base# + offset |_| /_\_\___/__/ // //---------------------------------------------------------------------------// #include #include #include #include #include namespace FxOS { //--- // CPU registers //--- // clang-format off char const *regnames[] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r0_bank", "r1_bank", "r2_bank", "r3_bank", "r4_bank", "r5_bank", "r6_bank", "r7_bank", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "mach", "macl", "pr", "pc", "sr", "ssr", "spc", "gbr", "vbr", "dbr", "sgr" }; // clang-format on /* Construction from string */ CpuRegister::CpuRegister(std::string name) { int regcount = (sizeof regnames / sizeof regnames[0]); char const *name_c = name.c_str(); for(int i = 0; i < regcount; i++) { if(!strcmp(regnames[i], name_c)) { m_name = CpuRegisterName(i + 1); return; } } m_name = CpuRegister::UNDEFINED; } /* Conversion to string */ std::string CpuRegister::str() const noexcept { int regcount = (sizeof regnames / sizeof regnames[0]); int i = m_name - 1; if(i < 0 || i >= regcount) return format("", i + 1); return regnames[i]; } //--- // Instruction operands //--- AsmOperand::AsmOperand(Kind kind, CpuRegister base): m_kind {kind}, m_base {base} { } AsmOperand::AsmOperand(int disp, i8 opsize, CpuRegister base): m_kind {StructDeref}, m_base {base}, m_opsize {opsize}, m_disp_imm {disp} { } AsmOperand::AsmOperand(CpuRegister index, CpuRegister base): m_kind {ArrayDeref}, m_base {base}, m_index {index} { } AsmOperand::AsmOperand(Kind kind, int disp_imm, i8 opsize): m_kind {kind}, m_opsize {opsize}, m_disp_imm {disp_imm} { } std::string AsmOperand::str() const { switch(m_kind) { case Reg: return m_base.str(); case Deref: return format("@%s", m_base.str()); case PostInc: return format("@%s+", m_base.str()); case PreDec: return format("@-%s", m_base.str()); case StructDeref: return format("@(%d,%s)", m_disp_imm, m_base.str().c_str()); case ArrayDeref: return format("@(%s,%s)", m_index.str().c_str(), m_base.str().c_str()); case PcRel: return format("@(%d,pc)", m_disp_imm); case PcJump: return format("pc+%d", m_disp_imm); case PcAddr: return format("pc+%u", m_disp_imm); case Imm: return format("#%d", m_disp_imm); default: return "(invalid)"; } } u32 AsmOperand::getPCRelativeTarget(u32 pc, int size) const { size = size + (size == 0); if(m_kind == AsmOperand::PcRel) return (pc & -size) + 4 + m_disp_imm; if(m_kind == AsmOperand::PcJump) return pc + 4 + m_disp_imm; if(m_kind == AsmOperand::PcAddr) return (pc & -4) + 4 + m_disp_imm; /* SH3 manual says that mova uses the target address of the jump when in a delay slot. SH4AL-DSP makes it invalid. Supporting this would be very tricky since the target PC is often dynamic (eg. rts). */ return -1; } //--- // Instruction management //--- static char const *instructionMnemonics[] = { #define GENDEFS_INSN(NAME, STR) [AsmInstruction::SH_##NAME] = STR, #include "gendefs/insn.h" #undef GENDEFS_INSN }; AsmInstruction::AsmInstruction(u32 encoding, char const *mnemonic, int tags, int opCount, AsmOperand op1, AsmOperand op2): m_encoding {encoding}, m_opsize {0}, m_opCount {(u8)opCount}, m_tags {(u16)tags}, m_ops {op1, op2} { std::string mn {mnemonic}; if(mn.ends_with(".b")) m_opsize = 1; else if(mn.ends_with(".w")) m_opsize = 2; else if(mn.ends_with(".l")) m_opsize = 4; if(m_opsize != 0) mn = mn.substr(0, mn.size() - 2); int i; for(i = 0; i < SH_MAX; i++) { if(mn == instructionMnemonics[i]) { m_opcode = i; break; } } assert(i < SH_MAX && "AsmInstruction with unknown opcode string"); } char const *AsmInstruction::mnemonic() const { assert(m_opcode < SH_MAX); return instructionMnemonics[m_opcode]; } u32 AsmInstruction::getPCRelativeTarget(u32 pc) const { for(AsmOperand const &op: operands()) { if(op.usesPCRelativeAddressing()) return op.getPCRelativeTarget(pc, m_opsize); } return -1; } } /* namespace FxOS */