Implement thumb STM, LDM. Fix thumb POP implementation for P=1.
This commit is contained in:
parent
8a310777a1
commit
2363759c62
4 changed files with 78 additions and 39 deletions
|
@ -137,8 +137,8 @@ boost::optional<const Thumb16Matcher<V>&> DecodeThumb16(u16 instruction) {
|
||||||
//INST(&V::thumb16_BKPT, "BKPT", "10111110xxxxxxxx"), // v5
|
//INST(&V::thumb16_BKPT, "BKPT", "10111110xxxxxxxx"), // v5
|
||||||
|
|
||||||
// Store/Load multiple registers
|
// Store/Load multiple registers
|
||||||
//INST(&V::thumb16_STMIA, "STMIA", "11000nnnxxxxxxxx"),
|
INST(&V::thumb16_STMIA, "STMIA", "11000nnnxxxxxxxx"),
|
||||||
//INST(&V::thumb16_LDMIA, "LDMIA", "11001nnnxxxxxxxx"),
|
INST(&V::thumb16_LDMIA, "LDMIA", "11001nnnxxxxxxxx"),
|
||||||
|
|
||||||
// Branch instructions
|
// Branch instructions
|
||||||
//INST(&V::thumb16_BX, "BX (reg)", "010001110mmmm000"), // v4T
|
//INST(&V::thumb16_BX, "BX (reg)", "010001110mmmm000"), // v4T
|
||||||
|
|
|
@ -102,6 +102,20 @@ public:
|
||||||
return "<internal error>";
|
return "<internal error>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<Reg>(i));
|
||||||
|
first_reg = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
std::string thumb16_LSL_imm(Imm5 imm5, Reg m, Reg d) {
|
std::string thumb16_LSL_imm(Imm5 imm5, Reg m, Reg d) {
|
||||||
return Common::StringFromFormat("lsls %s, %s, #%u", RegStr(d), RegStr(m), imm5);
|
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) {
|
std::string thumb16_PUSH(bool M, RegList reg_list) {
|
||||||
if (M)
|
if (M) reg_list |= 1 << 14;
|
||||||
reg_list |= 1 << 14;
|
return "push " + RegListStr(reg_list);
|
||||||
|
|
||||||
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<Reg>(i));
|
|
||||||
first_reg = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string thumb16_POP(bool P, RegList reg_list) {
|
std::string thumb16_POP(bool P, RegList reg_list) {
|
||||||
if (P)
|
if (P) reg_list |= 1 << 15;
|
||||||
reg_list |= 1 << 15;
|
return "pop " + RegListStr(reg_list);
|
||||||
|
|
||||||
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<Reg>(i));
|
|
||||||
first_reg = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string thumb16_REV(Reg m, Reg d) {
|
std::string thumb16_REV(Reg m, Reg d) {
|
||||||
|
@ -383,6 +371,16 @@ public:
|
||||||
return Common::StringFromFormat("revsh %s, %s", RegStr(d), RegStr(m));
|
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<size_t>(n), reg_list);
|
||||||
|
return Common::StringFromFormat("ldm %s%s, %s", RegStr(n), write_back ? "!" : "", RegListStr(reg_list).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string thumb16_UDF() {
|
std::string thumb16_UDF() {
|
||||||
return Common::StringFromFormat("udf");
|
return Common::StringFromFormat("udf");
|
||||||
}
|
}
|
||||||
|
|
|
@ -661,9 +661,18 @@ struct ThumbTranslatorVisitor final {
|
||||||
address = ir.Add(address, ir.Imm32(4));
|
address = ir.Add(address, ir.Imm32(4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ir.SetRegister(Reg::SP, address);
|
if (Common::Bit<15>(reg_list)) {
|
||||||
// TODO(optimization): Possible location for an RSB push.
|
// TODO(optimization): Possible location for an RSB pop.
|
||||||
return true;
|
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) {
|
bool thumb16_REV(Reg m, Reg d) {
|
||||||
|
@ -696,6 +705,37 @@ struct ThumbTranslatorVisitor final {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool thumb16_STMIA(Reg n, RegList reg_list) {
|
||||||
|
// STM <Rn>!, <reg_list>
|
||||||
|
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<Reg>(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<size_t>(n), reg_list);
|
||||||
|
// STM <Rn>!, <reg_list>
|
||||||
|
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<Reg>(i), data);
|
||||||
|
address = ir.Add(address, ir.Imm32(4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (write_back) {
|
||||||
|
ir.SetRegister(n, address);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool thumb16_UDF() {
|
bool thumb16_UDF() {
|
||||||
return InterpretThisInstruction();
|
return InterpretThisInstruction();
|
||||||
}
|
}
|
||||||
|
|
|
@ -271,9 +271,9 @@ TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") {
|
||||||
ThumbInstGen("011xxxxxxxxxxxxx"), // LDR(B)/STR(B) Rd, [Rn, #]
|
ThumbInstGen("011xxxxxxxxxxxxx"), // LDR(B)/STR(B) Rd, [Rn, #]
|
||||||
ThumbInstGen("1000xxxxxxxxxxxx"), // LDRH/STRH Rd, [Rn, #offset]
|
ThumbInstGen("1000xxxxxxxxxxxx"), // LDRH/STRH Rd, [Rn, #offset]
|
||||||
ThumbInstGen("1001xxxxxxxxxxxx"), // LDR/STR Rd, [SP, #]
|
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
|
[](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
|
[](u16 inst){ return Dynarmic::Common::Bits<0, 7>(inst) != 0; }), // Empty reg_list is UNPREDICTABLE
|
||||||
ThumbInstGen("1100xxxxxxxxxxxx"), // STMIA/LDMIA
|
ThumbInstGen("1100xxxxxxxxxxxx"), // STMIA/LDMIA
|
||||||
//ThumbInstGen("101101100101x000"), // SETEND
|
//ThumbInstGen("101101100101x000"), // SETEND
|
||||||
|
@ -294,12 +294,12 @@ TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") {
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("long blocks") {
|
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]") {
|
TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb]") {
|
||||||
const std::array<ThumbInstGen, 7> instructions = {{
|
const std::array<ThumbInstGen, 8> instructions = {{
|
||||||
ThumbInstGen("01000111xmmmm000", // BLX/BX
|
ThumbInstGen("01000111xmmmm000", // BLX/BX
|
||||||
[](u16 inst){
|
[](u16 inst){
|
||||||
u32 Rm = Dynarmic::Common::Bits<3, 6>(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.
|
return c < 0b1110; // Don't want SWI or undefined instructions.
|
||||||
}),
|
}),
|
||||||
ThumbInstGen("10110110011x0xxx"), // CPS
|
ThumbInstGen("10110110011x0xxx"), // CPS
|
||||||
|
ThumbInstGen("10111101xxxxxxxx"), // POP (R = 1)
|
||||||
}};
|
}};
|
||||||
|
|
||||||
auto instruction_select = [&]() -> u16 {
|
auto instruction_select = [&]() -> u16 {
|
||||||
|
|
Loading…
Reference in a new issue