Implement Thumb PUSH instruction
This commit is contained in:
parent
9109b226af
commit
f7e3d7b8d2
10 changed files with 191 additions and 54 deletions
|
@ -106,7 +106,7 @@ Gen::X64Reg RegAlloc::UseRegister(IR::Value* use_value, std::initializer_list<Ho
|
||||||
} else if (HostLocIsRegister(current_location)) {
|
} else if (HostLocIsRegister(current_location)) {
|
||||||
ASSERT(hostloc_state[current_location] == HostLocState::Idle);
|
ASSERT(hostloc_state[current_location] == HostLocState::Idle);
|
||||||
|
|
||||||
code->XCHG(32, Gen::R(hostloc_to_x64.at(current_location)), Gen::R(hostloc_to_x64.at(new_location)));
|
code->XCHG(32, Gen::R(hostloc_to_x64.at(new_location)), Gen::R(hostloc_to_x64.at(current_location)));
|
||||||
|
|
||||||
hostloc_state[new_location] = HostLocState::Use;
|
hostloc_state[new_location] = HostLocState::Use;
|
||||||
std::swap(hostloc_to_value[new_location], hostloc_to_value[current_location]);
|
std::swap(hostloc_to_value[new_location], hostloc_to_value[current_location]);
|
||||||
|
@ -118,6 +118,43 @@ Gen::X64Reg RegAlloc::UseRegister(IR::Value* use_value, std::initializer_list<Ho
|
||||||
return hostloc_to_x64.at(new_location);
|
return hostloc_to_x64.at(new_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Gen::X64Reg RegAlloc::UseScratchRegister(IR::Value* use_value, std::initializer_list<HostLoc> desired_locations) {
|
||||||
|
ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
||||||
|
ASSERT_MSG(remaining_uses.find(use_value) != remaining_uses.end(), "use_value has not been defined");
|
||||||
|
ASSERT_MSG(!ValueLocations(use_value).empty(), "use_value has not been defined");
|
||||||
|
ASSERT_MSG(remaining_uses[use_value] != 0, "use_value ran out of uses. (Use-d an IR::Value* too many times)");
|
||||||
|
|
||||||
|
HostLoc current_location = ValueLocations(use_value).front();
|
||||||
|
HostLoc new_location = SelectARegister(desired_locations);
|
||||||
|
|
||||||
|
if (HostLocIsSpill(current_location)) {
|
||||||
|
if (IsRegisterOccupied(new_location)) {
|
||||||
|
SpillRegister(new_location);
|
||||||
|
}
|
||||||
|
|
||||||
|
code->MOV(32, Gen::R(hostloc_to_x64.at(new_location)), SpillToOpArg(current_location));
|
||||||
|
|
||||||
|
hostloc_state[new_location] = HostLocState::Scratch;
|
||||||
|
remaining_uses[use_value]--;
|
||||||
|
} else if (HostLocIsRegister(current_location)) {
|
||||||
|
ASSERT(hostloc_state[current_location] == HostLocState::Idle);
|
||||||
|
|
||||||
|
if (IsRegisterOccupied(new_location)) {
|
||||||
|
SpillRegister(new_location);
|
||||||
|
}
|
||||||
|
|
||||||
|
code->MOV(32, Gen::R(hostloc_to_x64.at(new_location)), Gen::R(hostloc_to_x64.at(current_location)));
|
||||||
|
|
||||||
|
hostloc_state[new_location] = HostLocState::Scratch;
|
||||||
|
remaining_uses[use_value]--;
|
||||||
|
} else {
|
||||||
|
ASSERT_MSG(0, "Invalid current_location");
|
||||||
|
}
|
||||||
|
|
||||||
|
return hostloc_to_x64.at(new_location);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Gen::X64Reg RegAlloc::ScratchRegister(std::initializer_list<HostLoc> desired_locations) {
|
Gen::X64Reg RegAlloc::ScratchRegister(std::initializer_list<HostLoc> desired_locations) {
|
||||||
ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
||||||
|
|
||||||
|
@ -161,7 +198,7 @@ void RegAlloc::HostCall(IR::Value* result_def, IR::Value* arg0_use, IR::Value* a
|
||||||
|
|
||||||
for (size_t i = 0; i < AbiArgs.size(); i++) {
|
for (size_t i = 0; i < AbiArgs.size(); i++) {
|
||||||
if (args[i]) {
|
if (args[i]) {
|
||||||
UseRegister(args[i], {AbiArgs[i]});
|
UseScratchRegister(args[i], {AbiArgs[i]});
|
||||||
} else {
|
} else {
|
||||||
ScratchRegister({AbiArgs[i]});
|
ScratchRegister({AbiArgs[i]});
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,8 @@ public:
|
||||||
Gen::X64Reg UseDefRegister(IR::Value* use_value, IR::Value* def_value, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
Gen::X64Reg UseDefRegister(IR::Value* use_value, IR::Value* def_value, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
||||||
/// Early-use
|
/// Early-use
|
||||||
Gen::X64Reg UseRegister(IR::Value* use_value, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
Gen::X64Reg UseRegister(IR::Value* use_value, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
||||||
|
/// Early-use, Destroyed
|
||||||
|
Gen::X64Reg UseScratchRegister(IR::Value* use_value, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
||||||
/// Early-def, Late-use, single-use
|
/// Early-def, Late-use, single-use
|
||||||
Gen::X64Reg ScratchRegister(std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
Gen::X64Reg ScratchRegister(std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,11 @@
|
||||||
|
|
||||||
#include <climits>
|
#include <climits>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning(push)
|
#include <intrin.h>
|
||||||
#pragma warning(disable:4554)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace Dynarmic {
|
namespace Dynarmic {
|
||||||
|
@ -35,6 +35,10 @@ constexpr T Bits(const T value) {
|
||||||
return (value >> begin_bit) & ((1 << (end_bit - begin_bit + 1)) - 1);
|
return (value >> begin_bit) & ((1 << (end_bit - begin_bit + 1)) - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable:4554)
|
||||||
|
#endif
|
||||||
/// Extracts a single bit at bit_position from value of type T.
|
/// Extracts a single bit at bit_position from value of type T.
|
||||||
template<size_t bit_position, typename T>
|
template<size_t bit_position, typename T>
|
||||||
constexpr bool Bit(const T value) {
|
constexpr bool Bit(const T value) {
|
||||||
|
@ -43,6 +47,17 @@ constexpr bool Bit(const T value) {
|
||||||
return ((value >> bit_position) & 1) != 0;
|
return ((value >> bit_position) & 1) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extracts a single bit at bit_position from value of type T.
|
||||||
|
template<typename T>
|
||||||
|
constexpr bool Bit(size_t bit_position, const T value) {
|
||||||
|
ASSERT_MSG(bit_position < BitSize<T>(), "bit_position must be smaller than size of T");
|
||||||
|
|
||||||
|
return ((value >> bit_position) & 1) != 0;
|
||||||
|
}
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
/// Sign-extends a value that has NBits bits to the full bitwidth of type T.
|
/// Sign-extends a value that has NBits bits to the full bitwidth of type T.
|
||||||
template<size_t bit_count, typename T>
|
template<size_t bit_count, typename T>
|
||||||
inline T SignExtend(const T value) {
|
inline T SignExtend(const T value) {
|
||||||
|
@ -56,9 +71,13 @@ inline T SignExtend(const T value) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline size_t BitCount(u32 value) {
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
return __popcnt(value);
|
||||||
|
#else
|
||||||
|
return __builtin_popcount(value);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
} // namespace Dynarmic
|
} // namespace Dynarmic
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -124,7 +124,7 @@ boost::optional<const Thumb16Matcher<V>&> DecodeThumb16(u16 instruction) {
|
||||||
INST(&V::thumb16_SXTB, "SXTB", "1011001001mmmddd"), // v6
|
INST(&V::thumb16_SXTB, "SXTB", "1011001001mmmddd"), // v6
|
||||||
INST(&V::thumb16_UXTH, "UXTH", "1011001010mmmddd"), // v6
|
INST(&V::thumb16_UXTH, "UXTH", "1011001010mmmddd"), // v6
|
||||||
INST(&V::thumb16_UXTB, "UXTB", "1011001011mmmddd"), // v6
|
INST(&V::thumb16_UXTB, "UXTB", "1011001011mmmddd"), // v6
|
||||||
//INST(&V::thumb16_PUSH, "PUSH", "1011010rxxxxxxxx"), // v4T
|
INST(&V::thumb16_PUSH, "PUSH", "1011010Mxxxxxxxx"), // v4T
|
||||||
//INST(&V::thumb16_POP, "POP", "1011110rxxxxxxxx"), // v4T
|
//INST(&V::thumb16_POP, "POP", "1011110rxxxxxxxx"), // v4T
|
||||||
//INST(&V::thumb16_SETEND, "SETEND", "101101100101x000"), // v6
|
//INST(&V::thumb16_SETEND, "SETEND", "101101100101x000"), // v6
|
||||||
//INST(&V::thumb16_CPS, "CPS", "10110110011m0aif"), // v6
|
//INST(&V::thumb16_CPS, "CPS", "10110110011m0aif"), // v6
|
||||||
|
|
|
@ -280,6 +280,24 @@ public:
|
||||||
return Common::StringFromFormat("uxtb %s, %s", RegStr(d), RegStr(m));
|
return Common::StringFromFormat("uxtb %s, %s", RegStr(d), RegStr(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<Reg>(i));
|
||||||
|
first_reg = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
std::string thumb16_REV(Reg m, Reg d) {
|
std::string thumb16_REV(Reg m, Reg d) {
|
||||||
return Common::StringFromFormat("rev %s, %s", RegStr(d), RegStr(m));
|
return Common::StringFromFormat("rev %s, %s", RegStr(d), RegStr(m));
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,6 +155,10 @@ IREmitter::ResultAndCarryAndOverflow IREmitter::SubWithCarry(IR::ValuePtr a, IR:
|
||||||
return {result, carry_out, overflow};
|
return {result, carry_out, overflow};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IR::ValuePtr IREmitter::Sub(IR::ValuePtr a, IR::ValuePtr b) {
|
||||||
|
return Inst(IR::Opcode::SubWithCarry, {a, b, Imm1(1)});
|
||||||
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::And(IR::ValuePtr a, IR::ValuePtr b) {
|
IR::ValuePtr IREmitter::And(IR::ValuePtr a, IR::ValuePtr b) {
|
||||||
return Inst(IR::Opcode::And, {a, b});
|
return Inst(IR::Opcode::And, {a, b});
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,7 @@ public:
|
||||||
ResultAndCarryAndOverflow AddWithCarry(IR::ValuePtr a, IR::ValuePtr b, IR::ValuePtr carry_in);
|
ResultAndCarryAndOverflow AddWithCarry(IR::ValuePtr a, IR::ValuePtr b, IR::ValuePtr carry_in);
|
||||||
IR::ValuePtr Add(IR::ValuePtr a, IR::ValuePtr b);
|
IR::ValuePtr Add(IR::ValuePtr a, IR::ValuePtr b);
|
||||||
ResultAndCarryAndOverflow SubWithCarry(IR::ValuePtr a, IR::ValuePtr b, IR::ValuePtr carry_in);
|
ResultAndCarryAndOverflow SubWithCarry(IR::ValuePtr a, IR::ValuePtr b, IR::ValuePtr carry_in);
|
||||||
|
IR::ValuePtr Sub(IR::ValuePtr a, IR::ValuePtr b);
|
||||||
IR::ValuePtr And(IR::ValuePtr a, IR::ValuePtr b);
|
IR::ValuePtr And(IR::ValuePtr a, IR::ValuePtr b);
|
||||||
IR::ValuePtr Eor(IR::ValuePtr a, IR::ValuePtr b);
|
IR::ValuePtr Eor(IR::ValuePtr a, IR::ValuePtr b);
|
||||||
IR::ValuePtr Or(IR::ValuePtr a, IR::ValuePtr b);
|
IR::ValuePtr Or(IR::ValuePtr a, IR::ValuePtr b);
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/bit_util.h"
|
||||||
#include "frontend/arm_types.h"
|
#include "frontend/arm_types.h"
|
||||||
#include "frontend/decoder/thumb16.h"
|
#include "frontend/decoder/thumb16.h"
|
||||||
#include "frontend/ir/ir_emitter.h"
|
#include "frontend/ir/ir_emitter.h"
|
||||||
|
@ -507,6 +508,28 @@ struct ThumbTranslatorVisitor final {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool thumb16_PUSH(bool M, RegList reg_list) {
|
||||||
|
if (M) reg_list |= 1 << 14;
|
||||||
|
if (Common::BitCount(reg_list) < 1) {
|
||||||
|
return UnpredictableInstruction();
|
||||||
|
}
|
||||||
|
// PUSH <reg_list>
|
||||||
|
// reg_list cannot encode for R15.
|
||||||
|
u32 num_bytes_to_push = static_cast<u32>(4 * Common::BitCount(reg_list));
|
||||||
|
const auto final_address = ir.Sub(ir.GetRegister(Reg::SP), ir.Imm32(num_bytes_to_push));
|
||||||
|
auto address = final_address;
|
||||||
|
for (size_t i = 0; i < 16; 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(Reg::SP, final_address);
|
||||||
|
// TODO(optimization): Possible location for an RSB push.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool thumb16_REV(Reg m, Reg d) {
|
bool thumb16_REV(Reg m, Reg d) {
|
||||||
// REV <Rd>, <Rm>
|
// REV <Rd>, <Rm>
|
||||||
// Rd cannot encode R15.
|
// Rd cannot encode R15.
|
||||||
|
|
|
@ -115,9 +115,9 @@ static Dynarmic::UserCallbacks GetUserCallbacks() {
|
||||||
return user_callbacks;
|
return user_callbacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InstructionGenerator final {
|
struct ThumbInstGen final {
|
||||||
public:
|
public:
|
||||||
InstructionGenerator(const char* format, std::function<bool(u16)> is_valid = [](u16){ return true; }) : is_valid(is_valid) {
|
ThumbInstGen(const char* format, std::function<bool(u16)> is_valid = [](u16){ return true; }) : is_valid(is_valid) {
|
||||||
REQUIRE(strlen(format) == 16);
|
REQUIRE(strlen(format) == 16);
|
||||||
|
|
||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
|
@ -138,10 +138,14 @@ public:
|
||||||
}
|
}
|
||||||
u16 Generate() const {
|
u16 Generate() const {
|
||||||
u16 inst;
|
u16 inst;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
u16 random = RandInt<u16>(0, 0xFFFF);
|
u16 random = RandInt<u16>(0, 0xFFFF);
|
||||||
inst = bits | (random & ~mask);
|
inst = bits | (random & ~mask);
|
||||||
} while (!is_valid(inst));
|
} while (!is_valid(inst));
|
||||||
|
|
||||||
|
ASSERT((inst & mask) == bits);
|
||||||
|
|
||||||
return inst;
|
return inst;
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
|
@ -223,6 +227,16 @@ void FuzzJitThumb(const size_t instruction_count, const size_t instructions_to_e
|
||||||
}
|
}
|
||||||
printf("CPSR: %08x %08x %s\n", interp.Cpsr, jit.Cpsr(), interp.Cpsr != jit.Cpsr() ? "*" : "");
|
printf("CPSR: %08x %08x %s\n", interp.Cpsr, jit.Cpsr(), interp.Cpsr != jit.Cpsr() ? "*" : "");
|
||||||
|
|
||||||
|
printf("\nInterp Write Records:\n");
|
||||||
|
for (auto& record : interp_write_records) {
|
||||||
|
printf("%zu [%x] = %llx\n", record.size, record.address, record.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\nJIT Write Records:\n");
|
||||||
|
for (auto& record : jit_write_records) {
|
||||||
|
printf("%zu [%x] = %llx\n", record.size, record.address, record.data);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
__debugbreak();
|
__debugbreak();
|
||||||
#endif
|
#endif
|
||||||
|
@ -234,33 +248,35 @@ void FuzzJitThumb(const size_t instruction_count, const size_t instructions_to_e
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") {
|
TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") {
|
||||||
const std::array<InstructionGenerator, 23> instructions = {{
|
const std::array<ThumbInstGen, 24> instructions = {{
|
||||||
InstructionGenerator("00000xxxxxxxxxxx"), // LSL <Rd>, <Rm>, #<imm5>
|
ThumbInstGen("00000xxxxxxxxxxx"), // LSL <Rd>, <Rm>, #<imm5>
|
||||||
InstructionGenerator("00001xxxxxxxxxxx"), // LSR <Rd>, <Rm>, #<imm5>
|
ThumbInstGen("00001xxxxxxxxxxx"), // LSR <Rd>, <Rm>, #<imm5>
|
||||||
InstructionGenerator("00010xxxxxxxxxxx"), // ASR <Rd>, <Rm>, #<imm5>
|
ThumbInstGen("00010xxxxxxxxxxx"), // ASR <Rd>, <Rm>, #<imm5>
|
||||||
InstructionGenerator("000110oxxxxxxxxx"), // ADD/SUB_reg
|
ThumbInstGen("000110oxxxxxxxxx"), // ADD/SUB_reg
|
||||||
InstructionGenerator("000111oxxxxxxxxx"), // ADD/SUB_imm
|
ThumbInstGen("000111oxxxxxxxxx"), // ADD/SUB_imm
|
||||||
InstructionGenerator("001ooxxxxxxxxxxx"), // ADD/SUB/CMP/MOV_imm
|
ThumbInstGen("001ooxxxxxxxxxxx"), // ADD/SUB/CMP/MOV_imm
|
||||||
InstructionGenerator("010000ooooxxxxxx"), // Data Processing
|
ThumbInstGen("010000ooooxxxxxx"), // Data Processing
|
||||||
InstructionGenerator("010001000hxxxxxx"), // ADD (high registers)
|
ThumbInstGen("010001000hxxxxxx"), // ADD (high registers)
|
||||||
InstructionGenerator("0100010101xxxxxx", // CMP (high registers)
|
ThumbInstGen("0100010101xxxxxx", // CMP (high registers)
|
||||||
[](u16 inst){ return Dynarmic::Common::Bits<3, 5>(inst) != 0b111; }), // R15 is UNPREDICTABLE
|
[](u16 inst){ return Dynarmic::Common::Bits<3, 5>(inst) != 0b111; }), // R15 is UNPREDICTABLE
|
||||||
InstructionGenerator("0100010110xxxxxx", // CMP (high registers)
|
ThumbInstGen("0100010110xxxxxx", // CMP (high registers)
|
||||||
[](u16 inst){ return Dynarmic::Common::Bits<0, 2>(inst) != 0b111; }), // R15 is UNPREDICTABLE
|
[](u16 inst){ return Dynarmic::Common::Bits<0, 2>(inst) != 0b111; }), // R15 is UNPREDICTABLE
|
||||||
InstructionGenerator("010001100hxxxxxx"), // MOV (high registers)
|
ThumbInstGen("010001100hxxxxxx"), // MOV (high registers)
|
||||||
InstructionGenerator("10110000oxxxxxxx"), // Adjust stack pointer
|
ThumbInstGen("10110000oxxxxxxx"), // Adjust stack pointer
|
||||||
InstructionGenerator("10110010ooxxxxxx"), // SXT/UXT
|
ThumbInstGen("10110010ooxxxxxx"), // SXT/UXT
|
||||||
InstructionGenerator("1011101000xxxxxx"), // REV
|
ThumbInstGen("1011101000xxxxxx"), // REV
|
||||||
InstructionGenerator("1011101001xxxxxx"), // REV16
|
ThumbInstGen("1011101001xxxxxx"), // REV16
|
||||||
InstructionGenerator("1011101011xxxxxx"), // REVSH
|
ThumbInstGen("1011101011xxxxxx"), // REVSH
|
||||||
InstructionGenerator("01001xxxxxxxxxxx"), // LDR Rd, [PC, #]
|
ThumbInstGen("01001xxxxxxxxxxx"), // LDR Rd, [PC, #]
|
||||||
InstructionGenerator("0101oooxxxxxxxxx"), // LDR/STR Rd, [Rn, Rm]
|
ThumbInstGen("0101oooxxxxxxxxx"), // LDR/STR Rd, [Rn, Rm]
|
||||||
InstructionGenerator("011xxxxxxxxxxxxx"), // LDR(B)/STR(B) Rd, [Rn, #]
|
ThumbInstGen("011xxxxxxxxxxxxx"), // LDR(B)/STR(B) Rd, [Rn, #]
|
||||||
InstructionGenerator("1000xxxxxxxxxxxx"), // LDRH/STRH Rd, [Rn, #offset]
|
ThumbInstGen("1000xxxxxxxxxxxx"), // LDRH/STRH Rd, [Rn, #offset]
|
||||||
InstructionGenerator("1001xxxxxxxxxxxx"), // LDR/STR Rd, [SP, #]
|
ThumbInstGen("1001xxxxxxxxxxxx"), // LDR/STR Rd, [SP, #]
|
||||||
InstructionGenerator("1011x100xxxxxxxx"), // PUSH/POP (R = 0)
|
ThumbInstGen("10110100xxxxxxxx", // PUSH (R = 0)
|
||||||
InstructionGenerator("1100xxxxxxxxxxxx"), // STMIA/LDMIA
|
[](u16 inst){ return Dynarmic::Common::Bits<0, 7>(inst) != 0; }), // Empty reg_list is UNPREDICTABLE
|
||||||
//InstructionGenerator("101101100101x000"), // SETEND
|
ThumbInstGen("10111100xxxxxxxx"), // POP (R = 0)
|
||||||
|
ThumbInstGen("1100xxxxxxxxxxxx"), // STMIA/LDMIA
|
||||||
|
//ThumbInstGen("101101100101x000"), // SETEND
|
||||||
}};
|
}};
|
||||||
|
|
||||||
auto instruction_select = [&]() -> u16 {
|
auto instruction_select = [&]() -> u16 {
|
||||||
|
@ -283,22 +299,22 @@ TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") {
|
||||||
}
|
}
|
||||||
|
|
||||||
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<InstructionGenerator, 7> instructions = {{
|
const std::array<ThumbInstGen, 7> instructions = {{
|
||||||
InstructionGenerator("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);
|
||||||
return Rm != 15;
|
return Rm != 15;
|
||||||
}),
|
}),
|
||||||
InstructionGenerator("1010oxxxxxxxxxxx"), // add to pc/sp
|
ThumbInstGen("1010oxxxxxxxxxxx"), // add to pc/sp
|
||||||
InstructionGenerator("11100xxxxxxxxxxx"), // B
|
ThumbInstGen("11100xxxxxxxxxxx"), // B
|
||||||
InstructionGenerator("01000100h0xxxxxx"), // ADD (high registers)
|
ThumbInstGen("01000100h0xxxxxx"), // ADD (high registers)
|
||||||
InstructionGenerator("01000110h0xxxxxx"), // MOV (high registers)
|
ThumbInstGen("01000110h0xxxxxx"), // MOV (high registers)
|
||||||
InstructionGenerator("1101ccccxxxxxxxx", // B<cond>
|
ThumbInstGen("1101ccccxxxxxxxx", // B<cond>
|
||||||
[](u16 inst){
|
[](u16 inst){
|
||||||
u32 c = Dynarmic::Common::Bits<9, 12>(inst);
|
u32 c = Dynarmic::Common::Bits<9, 12>(inst);
|
||||||
return c < 0b1110; // Don't want SWI or undefined instructions.
|
return c < 0b1110; // Don't want SWI or undefined instructions.
|
||||||
}),
|
}),
|
||||||
InstructionGenerator("10110110011x0xxx"), // CPS
|
ThumbInstGen("10110110011x0xxx"), // CPS
|
||||||
}};
|
}};
|
||||||
|
|
||||||
auto instruction_select = [&]() -> u16 {
|
auto instruction_select = [&]() -> u16 {
|
||||||
|
|
|
@ -103,3 +103,20 @@ TEST_CASE( "thumb: revsh r4, r3", "[thumb]" ) {
|
||||||
REQUIRE( jit.Regs()[15] == 2 );
|
REQUIRE( jit.Regs()[15] == 2 );
|
||||||
REQUIRE( jit.Cpsr() == 0x00000030 ); // Thumb, User-mode
|
REQUIRE( jit.Cpsr() == 0x00000030 ); // Thumb, User-mode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE( "thumb: ldr r3, [r3, #28]", "[thumb]" ) {
|
||||||
|
Dynarmic::Jit jit{GetUserCallbacks()};
|
||||||
|
code_mem.fill({});
|
||||||
|
code_mem[0] = 0x69DB; // ldr r3, [r3, #28]
|
||||||
|
code_mem[1] = 0xE7FE; // b +#0
|
||||||
|
|
||||||
|
jit.Regs()[3] = 0x12345678;
|
||||||
|
jit.Regs()[15] = 0; // PC = 0
|
||||||
|
jit.Cpsr() = 0x00000030; // Thumb, User-mode
|
||||||
|
|
||||||
|
jit.Run(1);
|
||||||
|
|
||||||
|
REQUIRE( jit.Regs()[3] == 0x12345694 );
|
||||||
|
REQUIRE( jit.Regs()[15] == 2 );
|
||||||
|
REQUIRE( jit.Cpsr() == 0x00000030 ); // Thumb, User-mode
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue