diff --git a/src/frontend/decoder/thumb16.h b/src/frontend/decoder/thumb16.h index 3292d270..76d0385d 100644 --- a/src/frontend/decoder/thumb16.h +++ b/src/frontend/decoder/thumb16.h @@ -137,8 +137,8 @@ boost::optional&> DecodeThumb16(u16 instruction) { //INST(&V::thumb16_BKPT, "BKPT", "10111110xxxxxxxx"), // v5 // Store/Load multiple registers - //INST(&V::thumb16_STMIA, "STMIA", "11000nnnxxxxxxxx"), - //INST(&V::thumb16_LDMIA, "LDMIA", "11001nnnxxxxxxxx"), + INST(&V::thumb16_STMIA, "STMIA", "11000nnnxxxxxxxx"), + INST(&V::thumb16_LDMIA, "LDMIA", "11001nnnxxxxxxxx"), // Branch instructions //INST(&V::thumb16_BX, "BX (reg)", "010001110mmmm000"), // v4T diff --git a/src/frontend/disassembler/disassembler_thumb.cpp b/src/frontend/disassembler/disassembler_thumb.cpp index 780b7f56..8a55a722 100644 --- a/src/frontend/disassembler/disassembler_thumb.cpp +++ b/src/frontend/disassembler/disassembler_thumb.cpp @@ -102,6 +102,20 @@ public: return ""; } + std::string RegListStr(RegList reg_list) { + std::string ret = ""; + bool first_reg = true; + for (size_t i = 0; i < 16; i++) { + if (Common::Bit(i, reg_list)) { + if (!first_reg) + ret += ", "; + ret += RegStr(static_cast(i)); + first_reg = false; + } + } + return ret; + } + std::string thumb16_LSL_imm(Imm5 imm5, Reg m, Reg d) { return Common::StringFromFormat("lsls %s, %s, #%u", RegStr(d), RegStr(m), imm5); } @@ -336,39 +350,13 @@ public: } std::string thumb16_PUSH(bool M, RegList reg_list) { - if (M) - reg_list |= 1 << 14; - - std::string ret = "PUSH "; - bool first_reg = true; - for (size_t i = 0; i < 16; i++) { - if (Common::Bit(i, reg_list)) { - if (!first_reg) - ret += ", "; - ret += RegStr(static_cast(i)); - first_reg = false; - } - } - - return ret; + if (M) reg_list |= 1 << 14; + return "push " + RegListStr(reg_list); } std::string thumb16_POP(bool P, RegList reg_list) { - if (P) - reg_list |= 1 << 15; - - std::string ret = "PUSH "; - bool first_reg = true; - for (size_t i = 0; i < 16; i++) { - if (Common::Bit(i, reg_list)) { - if (!first_reg) - ret += ", "; - ret += RegStr(static_cast(i)); - first_reg = false; - } - } - - return ret; + if (P) reg_list |= 1 << 15; + return "pop " + RegListStr(reg_list); } std::string thumb16_REV(Reg m, Reg d) { @@ -383,6 +371,16 @@ public: return Common::StringFromFormat("revsh %s, %s", RegStr(d), RegStr(m)); } + std::string thumb16_STMIA(Reg n, RegList reg_list) { + return Common::StringFromFormat("stm %s!, %s", RegStr(n), RegListStr(reg_list).c_str()); + } + + std::string thumb16_LDMIA(Reg n, RegList reg_list) { + bool write_back = !Dynarmic::Common::Bit(static_cast(n), reg_list); + return Common::StringFromFormat("ldm %s%s, %s", RegStr(n), write_back ? "!" : "", RegListStr(reg_list).c_str()); + } + + std::string thumb16_UDF() { return Common::StringFromFormat("udf"); } diff --git a/src/frontend/translate/translate_thumb.cpp b/src/frontend/translate/translate_thumb.cpp index 64d2f799..98cd1ef2 100644 --- a/src/frontend/translate/translate_thumb.cpp +++ b/src/frontend/translate/translate_thumb.cpp @@ -661,9 +661,18 @@ struct ThumbTranslatorVisitor final { address = ir.Add(address, ir.Imm32(4)); } } - ir.SetRegister(Reg::SP, address); - // TODO(optimization): Possible location for an RSB push. - return true; + if (Common::Bit<15>(reg_list)) { + // TODO(optimization): Possible location for an RSB pop. + auto data = ir.ReadMemory32(address); + ir.LoadWritePC(data); + address = ir.Add(address, ir.Imm32(4)); + ir.SetRegister(Reg::SP, address); + ir.SetTerm(IR::Term::ReturnToDispatch{}); + return false; + } else { + ir.SetRegister(Reg::SP, address); + return true; + } } bool thumb16_REV(Reg m, Reg d) { @@ -696,6 +705,37 @@ struct ThumbTranslatorVisitor final { return true; } + bool thumb16_STMIA(Reg n, RegList reg_list) { + // STM !, + auto address = ir.GetRegister(n); + for (size_t i = 0; i < 8; i++) { + if (Common::Bit(i, reg_list)) { + auto Ri = ir.GetRegister(static_cast(i)); + ir.WriteMemory32(address, Ri); + address = ir.Add(address, ir.Imm32(4)); + } + } + ir.SetRegister(n, address); + return true; + } + + bool thumb16_LDMIA(Reg n, RegList reg_list) { + bool write_back = !Dynarmic::Common::Bit(static_cast(n), reg_list); + // STM !, + auto address = ir.GetRegister(n); + for (size_t i = 0; i < 8; i++) { + if (Common::Bit(i, reg_list)) { + auto data = ir.ReadMemory32(address); + ir.SetRegister(static_cast(i), data); + address = ir.Add(address, ir.Imm32(4)); + } + } + if (write_back) { + ir.SetRegister(n, address); + } + return true; + } + bool thumb16_UDF() { return InterpretThisInstruction(); } diff --git a/tests/arm/fuzz_thumb.cpp b/tests/arm/fuzz_thumb.cpp index 49d0b3a9..b7a8cdb1 100644 --- a/tests/arm/fuzz_thumb.cpp +++ b/tests/arm/fuzz_thumb.cpp @@ -271,9 +271,9 @@ TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") { ThumbInstGen("011xxxxxxxxxxxxx"), // LDR(B)/STR(B) Rd, [Rn, #] ThumbInstGen("1000xxxxxxxxxxxx"), // LDRH/STRH Rd, [Rn, #offset] ThumbInstGen("1001xxxxxxxxxxxx"), // LDR/STR Rd, [SP, #] - ThumbInstGen("10110100xxxxxxxx", // PUSH (R = 0) + ThumbInstGen("1011010xxxxxxxxx", // PUSH [](u16 inst){ return Dynarmic::Common::Bits<0, 7>(inst) != 0; }), // Empty reg_list is UNPREDICTABLE - ThumbInstGen("10111100xxxxxxxx", // POP (R = 0) + ThumbInstGen("10111100xxxxxxxx", // POP (P = 0) [](u16 inst){ return Dynarmic::Common::Bits<0, 7>(inst) != 0; }), // Empty reg_list is UNPREDICTABLE ThumbInstGen("1100xxxxxxxxxxxx"), // STMIA/LDMIA //ThumbInstGen("101101100101x000"), // SETEND @@ -294,12 +294,12 @@ TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") { } SECTION("long blocks") { - FuzzJitThumb(1024, 1025, 25, instruction_select); + FuzzJitThumb(1024, 1025, 2, instruction_select); } } TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb]") { - const std::array instructions = {{ + const std::array instructions = {{ ThumbInstGen("01000111xmmmm000", // BLX/BX [](u16 inst){ u32 Rm = Dynarmic::Common::Bits<3, 6>(inst); @@ -315,6 +315,7 @@ TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb]") { return c < 0b1110; // Don't want SWI or undefined instructions. }), ThumbInstGen("10110110011x0xxx"), // CPS + ThumbInstGen("10111101xxxxxxxx"), // POP (R = 1) }}; auto instruction_select = [&]() -> u16 {