//---------------------------------------------------------------------------// // 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)"; } } //--- // Instruction management //--- AsmInstruction::AsmInstruction(char const *mn): opcode {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; } //--- // Instruction classes //--- bool AsmInstruction::isterminal() const noexcept { if(!strcmp(mnemonic, "rte") || !strcmp(mnemonic, "rts")) return true; /* Also jmp @rn which is regarded as a terminal call */ if(!strcmp(mnemonic, "jmp") && args[0].kind == AsmArgument::Deref) return true; /* Same for braf because we can't analyse further */ if(!strcmp(mnemonic, "braf")) return true; return false; } bool AsmInstruction::isjump() const noexcept { return !strcmp(mnemonic, "bra"); } bool AsmInstruction::iscondjump() const noexcept { char const *v[] = { "bf", "bf.s", "bf/s", "bt", "bt.s", "bt/s", NULL, }; for(int i = 0; v[i]; i++) { if(!strcmp(mnemonic, v[i])) return true; } return false; } bool AsmInstruction::iscall() const noexcept { return !strcmp(mnemonic, "jsr") || !strcmp(mnemonic, "bsr") || !strcmp(mnemonic, "bsrf"); } bool AsmInstruction::isdelayed() const noexcept { char const *v[] = { "rte", "rts", "jmp", "jsr", "bra", "braf", "bsr", "bsrf", "bf.s", "bf/s", "bt.s", "bt/s", NULL, }; for(int i = 0; v[i]; i++) { if(!strcmp(mnemonic, v[i])) return true; } return false; } bool AsmInstruction::isvaliddelayslot() const noexcept { // TODO: PC-relative move is a valid delay slot but it doesn't work return !isdelayed() && !isterminal() && !isjump() && !iscondjump() && strcmp(this->mnemonic, "mova") != 0; } } /* namespace FxOS */