//---------------------------------------------------------------------------// // 1100101 |_ mov #0, r4 __ // // 11 |_ <0xb380 %5c4> / _|_ _____ ___ // // 0110 |_ 3.50 -> 3.60 | _\ \ / _ (_-< // // |_ base# + offset |_| /_\_\___/__/ // //---------------------------------------------------------------------------// #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 arguments //--- /* External constructors */ AsmArgument AsmArgument_Reg(CpuRegister base) { AsmArgument arg; arg.kind = AsmArgument::Reg; arg.base = base; return arg; } AsmArgument AsmArgument_Deref(CpuRegister base) { AsmArgument arg; arg.kind = AsmArgument::Deref; arg.base = base; return arg; } AsmArgument AsmArgument_PostInc(CpuRegister base) { AsmArgument arg; arg.kind = AsmArgument::PostInc; arg.base = base; return arg; } AsmArgument AsmArgument_PreDec(CpuRegister base) { AsmArgument arg; arg.kind = AsmArgument::PreDec; arg.base = base; return arg; } AsmArgument AsmArgument_StructDeref(int disp, int opsize, CpuRegister base) { AsmArgument arg; arg.kind = AsmArgument::StructDeref; arg.base = base; arg.disp = disp; arg.opsize = opsize; return arg; } AsmArgument AsmArgument_ArrayDeref(CpuRegister index, CpuRegister base) { AsmArgument arg; arg.kind = AsmArgument::ArrayDeref; arg.base = base; arg.index = index; return arg; } AsmArgument AsmArgument_PcRel(int disp, int opsize) { AsmArgument arg; arg.kind = AsmArgument::PcRel; arg.disp = disp; arg.opsize = opsize; return arg; } AsmArgument AsmArgument_PcJump(int disp) { AsmArgument arg; arg.kind = AsmArgument::PcJump; arg.disp = disp; return arg; } AsmArgument AsmArgument_PcAddr(int disp) { AsmArgument arg; arg.kind = AsmArgument::PcAddr; arg.disp = disp; return arg; } AsmArgument AsmArgument_Imm(int imm) { AsmArgument arg; arg.kind = AsmArgument::Imm; arg.imm = imm; return arg; } /* String representation */ std::string AsmArgument::str() const { switch(kind) { case AsmArgument::Reg: return base.str(); case AsmArgument::Deref: return format("@%s", base.str()); case AsmArgument::PostInc: return format("@%s+", base.str()); case AsmArgument::PreDec: return format("@-%s", base.str()); case AsmArgument::StructDeref: return format("@(%d,%s)", disp, base.str().c_str()); case AsmArgument::ArrayDeref: return format("@(%s,%s)", index.str().c_str(), base.str().c_str()); case AsmArgument::PcRel: return format("@(%d,pc)", disp); case AsmArgument::PcJump: return format("pc+%d", disp); case AsmArgument::PcAddr: return format("pc+%u", disp); case AsmArgument::Imm: return format("#%d", imm); default: return "(invalid)"; } } u32 AsmArgument::getPCRelativeTarget(u32 pc, int size) const { size = size + (size == 0); if(this->kind == AsmArgument::PcRel) return (pc & -size) + 4 + this->disp; if(this->kind == AsmArgument::PcJump) return pc + 4 + this->disp; if(this->kind == AsmArgument::PcAddr) return (pc & -4) + 4 + this->disp; /* 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 0xffffffff; } //--- // Instruction management //--- AsmInstruction::AsmInstruction(char const *mn): encoding {0}, opsize {0}, arg_count {0} { int len = strlen(mn); int pos = std::max(0, len - 2); if(!strncmp(mn + pos, ".b", 2)) { opsize = 1; len -= 2; } else if(!strncmp(mn + pos, ".w", 2)) { opsize = 2; len -= 2; } else if(!strncmp(mn + pos, ".l", 2)) { opsize = 4; len -= 2; } len = std::min(len, 11); strncpy(mnemonic, mn, len); mnemonic[len] = 0; } AsmInstruction::AsmInstruction(char const *mn, AsmArgument arg): AsmInstruction(mn) { args[0] = arg; arg_count = 1; } AsmInstruction::AsmInstruction( char const *mn, AsmArgument arg1, AsmArgument arg2): AsmInstruction(mn) { args[0] = arg1; args[1] = arg2; arg_count = 2; } u32 AsmInstruction::getPCRelativeTarget(u32 pc) const { for(int i = 0; i < this->arg_count; i++) { u32 target = this->args[i].getPCRelativeTarget(pc, this->opsize); if(target != 0xffffffff) return target; } return 0xffffffff; } } /* namespace FxOS */