Proper implementation of Arm::Translate
This commit is contained in:
parent
d743adf518
commit
14388ea690
29 changed files with 276 additions and 90 deletions
|
@ -14,6 +14,7 @@ set(SRCS
|
||||||
frontend/disassembler_arm.cpp
|
frontend/disassembler_arm.cpp
|
||||||
frontend/ir/ir.cpp
|
frontend/ir/ir.cpp
|
||||||
frontend/ir_emitter.cpp
|
frontend/ir_emitter.cpp
|
||||||
|
frontend/translate.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(HEADERS
|
set(HEADERS
|
||||||
|
@ -43,7 +44,9 @@ set(HEADERS
|
||||||
frontend/ir/ir.h
|
frontend/ir/ir.h
|
||||||
frontend/ir/opcodes.h
|
frontend/ir/opcodes.h
|
||||||
frontend/ir_emitter.h
|
frontend/ir_emitter.h
|
||||||
frontend/translate_thumb.h
|
frontend/translate.h
|
||||||
|
frontend/translate_arm.cpp
|
||||||
|
frontend/translate_thumb.cpp
|
||||||
interface/interface.h
|
interface/interface.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "backend_x64/emit_x64.h"
|
#include "backend_x64/emit_x64.h"
|
||||||
#include "common/x64/emitter.h"
|
#include "common/x64/emitter.h"
|
||||||
|
#include "frontend/arm_types.h"
|
||||||
|
|
||||||
// TODO: More optimal use of immediates.
|
// TODO: More optimal use of immediates.
|
||||||
// TODO: Have ARM flags in host flags and not have them use up GPR registers unless necessary.
|
// TODO: Have ARM flags in host flags and not have them use up GPR registers unless necessary.
|
||||||
|
@ -34,6 +35,9 @@ static IR::Inst* FindUseWithOpcode(IR::Inst* inst, IR::Opcode opcode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
CodePtr EmitX64::Emit(Arm::LocationDescriptor descriptor, Dynarmic::IR::Block block) {
|
CodePtr EmitX64::Emit(Arm::LocationDescriptor descriptor, Dynarmic::IR::Block block) {
|
||||||
|
inhibit_emission.clear();
|
||||||
|
reg_alloc.Reset();
|
||||||
|
|
||||||
code->INT3();
|
code->INT3();
|
||||||
CodePtr code_ptr = code->GetCodePtr();
|
CodePtr code_ptr = code->GetCodePtr();
|
||||||
|
|
||||||
|
@ -46,6 +50,7 @@ CodePtr EmitX64::Emit(Arm::LocationDescriptor descriptor, Dynarmic::IR::Block bl
|
||||||
reg_alloc.EndOfAllocScope();
|
reg_alloc.EndOfAllocScope();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EmitAddCycles(block.cycle_count);
|
||||||
EmitReturnToDispatch();
|
EmitReturnToDispatch();
|
||||||
|
|
||||||
return code_ptr;
|
return code_ptr;
|
||||||
|
@ -378,6 +383,11 @@ void EmitX64::EmitArithmeticShiftRight(IR::Value* value_) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmitX64::EmitAddCycles(size_t cycles) {
|
||||||
|
ASSERT(cycles < std::numeric_limits<u32>::max());
|
||||||
|
code->SUB(64, MDisp(R15, offsetof(JitState, cycles_remaining)), Imm32(cycles));
|
||||||
|
}
|
||||||
|
|
||||||
void EmitX64::EmitReturnToDispatch() {
|
void EmitX64::EmitReturnToDispatch() {
|
||||||
// TODO: Update cycle counts
|
// TODO: Update cycle counts
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "backend_x64/reg_alloc.h"
|
#include "backend_x64/reg_alloc.h"
|
||||||
#include "backend_x64/routines.h"
|
#include "backend_x64/routines.h"
|
||||||
|
@ -19,10 +20,15 @@ namespace BackendX64 {
|
||||||
|
|
||||||
class EmitX64 final {
|
class EmitX64 final {
|
||||||
public:
|
public:
|
||||||
EmitX64(Gen::XEmitter* code, Routines* routines, UserCallbacks cb) : code(code), reg_alloc(code), routines(routines), cb(cb) {}
|
EmitX64(Gen::XEmitter* code, Routines* routines, UserCallbacks cb)
|
||||||
|
: reg_alloc(code), code(code), routines(routines), cb(cb) {}
|
||||||
|
|
||||||
CodePtr Emit(Arm::LocationDescriptor descriptor, IR::Block ir);
|
CodePtr Emit(Arm::LocationDescriptor descriptor, IR::Block ir);
|
||||||
CodePtr GetBasicBlock(Arm::LocationDescriptor descriptor);
|
|
||||||
|
CodePtr GetBasicBlock(Arm::LocationDescriptor descriptor) {
|
||||||
|
auto iter = basic_blocks.find(descriptor);
|
||||||
|
return iter != basic_blocks.end() ? iter->second : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void EmitImmU1(IR::Value* value);
|
void EmitImmU1(IR::Value* value);
|
||||||
void EmitImmU8(IR::Value* value);
|
void EmitImmU8(IR::Value* value);
|
||||||
|
@ -46,14 +52,17 @@ public:
|
||||||
void EmitLogicalShiftRight(IR::Value* value);
|
void EmitLogicalShiftRight(IR::Value* value);
|
||||||
void EmitArithmeticShiftRight(IR::Value* value);
|
void EmitArithmeticShiftRight(IR::Value* value);
|
||||||
|
|
||||||
|
void EmitAddCycles(size_t cycles);
|
||||||
void EmitReturnToDispatch();
|
void EmitReturnToDispatch();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::set<IR::Value*> inhibit_emission;
|
std::set<IR::Value*> inhibit_emission;
|
||||||
Gen::XEmitter* code;
|
|
||||||
RegAlloc reg_alloc;
|
RegAlloc reg_alloc;
|
||||||
|
|
||||||
|
Gen::XEmitter* code;
|
||||||
Routines* routines;
|
Routines* routines;
|
||||||
UserCallbacks cb;
|
UserCallbacks cb;
|
||||||
|
std::unordered_map<Arm::LocationDescriptor, CodePtr, Arm::LocationDescriptorHash> basic_blocks;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace BackendX64
|
} // namespace BackendX64
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/scope_exit.h"
|
#include "common/scope_exit.h"
|
||||||
#include "frontend/arm_types.h"
|
#include "frontend/arm_types.h"
|
||||||
|
#include "frontend/translate.h"
|
||||||
#include "interface/interface.h"
|
#include "interface/interface.h"
|
||||||
|
|
||||||
namespace Dynarmic {
|
namespace Dynarmic {
|
||||||
|
@ -27,12 +28,13 @@ struct BlockOfCode : Gen::XCodeBlock {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Jit::Impl {
|
struct Jit::Impl {
|
||||||
Impl(UserCallbacks callbacks) : emitter(&block_of_code, &routines, callbacks) {}
|
Impl(UserCallbacks callbacks) : emitter(&block_of_code, &routines, callbacks), callbacks(callbacks) {}
|
||||||
|
|
||||||
JitState jit_state{};
|
JitState jit_state{};
|
||||||
Routines routines{};
|
Routines routines{};
|
||||||
BlockOfCode block_of_code{};
|
BlockOfCode block_of_code{};
|
||||||
EmitX64 emitter;
|
EmitX64 emitter;
|
||||||
|
const UserCallbacks callbacks;
|
||||||
|
|
||||||
size_t Execute(size_t cycle_count) {
|
size_t Execute(size_t cycle_count) {
|
||||||
u32 pc = jit_state.Reg[15];
|
u32 pc = jit_state.Reg[15];
|
||||||
|
@ -50,7 +52,7 @@ private:
|
||||||
if (code_ptr)
|
if (code_ptr)
|
||||||
return code_ptr;
|
return code_ptr;
|
||||||
|
|
||||||
IR::Block ir_block = IR::Block({0, false, false}); // TODO: Do this.
|
IR::Block ir_block = Arm::Translate(descriptor, callbacks.MemoryRead32);
|
||||||
return emitter.Emit(descriptor, ir_block);
|
return emitter.Emit(descriptor, ir_block);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -201,5 +201,12 @@ void RegAlloc::EndOfAllocScope() {
|
||||||
iter.second = nullptr;
|
iter.second = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RegAlloc::Reset() {
|
||||||
|
hostloc_to_value.clear();
|
||||||
|
hostloc_state.clear();
|
||||||
|
remaining_uses.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace BackendX64
|
} // namespace BackendX64
|
||||||
} // namespace Dynarmic
|
} // namespace Dynarmic
|
||||||
|
|
|
@ -78,6 +78,8 @@ public:
|
||||||
|
|
||||||
void EndOfAllocScope();
|
void EndOfAllocScope();
|
||||||
|
|
||||||
|
void Reset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HostLoc SelectARegister(std::initializer_list<HostLoc> desired_locations) const;
|
HostLoc SelectARegister(std::initializer_list<HostLoc> desired_locations) const;
|
||||||
std::vector<HostLoc> ValueLocations(IR::Value* value) const;
|
std::vector<HostLoc> ValueLocations(IR::Value* value) const;
|
||||||
|
|
|
@ -72,5 +72,13 @@ struct LocationDescriptor {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct LocationDescriptorHash {
|
||||||
|
size_t operator()(const LocationDescriptor& x) const {
|
||||||
|
return std::hash<u64>()(static_cast<u64>(x.arm_pc)
|
||||||
|
^ (static_cast<u64>(x.TFlag) << 32)
|
||||||
|
^ (static_cast<u64>(x.EFlag) << 33));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace Arm
|
} // namespace Arm
|
||||||
} // namespace Dynarmic
|
} // namespace Dynarmic
|
||||||
|
|
|
@ -43,6 +43,9 @@ private:
|
||||||
expect |= 1 << bit_position;
|
expect |= 1 << bit_position;
|
||||||
mask |= 1 << bit_position;
|
mask |= 1 << bit_position;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
// Ignore
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return std::make_tuple(mask, expect);
|
return std::make_tuple(mask, expect);
|
||||||
|
|
|
@ -56,7 +56,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename V>
|
template <typename V>
|
||||||
static const std::array<Thumb1Matcher<V>, 6> g_thumb1_instruction_table {{
|
static const std::array<Thumb1Matcher<V>, 7> g_thumb1_instruction_table {{
|
||||||
|
|
||||||
#define INST(fn, name, bitstring) detail::detail<Thumb1Matcher, u16, 16>::GetMatcher<decltype(fn), fn>(name, bitstring)
|
#define INST(fn, name, bitstring) detail::detail<Thumb1Matcher, u16, 16>::GetMatcher<decltype(fn), fn>(name, bitstring)
|
||||||
|
|
||||||
|
@ -138,7 +138,7 @@ static const std::array<Thumb1Matcher<V>, 6> g_thumb1_instruction_table {{
|
||||||
// Branch instructions
|
// Branch instructions
|
||||||
//{ INST(&V::thumb1_BX, "BX (reg)", "010001110mmmm000") }, // v4T
|
//{ INST(&V::thumb1_BX, "BX (reg)", "010001110mmmm000") }, // v4T
|
||||||
//{ INST(&V::thumb1_BLX, "BLX (reg)", "010001111mmmm000") }, // v5T
|
//{ INST(&V::thumb1_BLX, "BLX (reg)", "010001111mmmm000") }, // v5T
|
||||||
//{ INST(&V::thumb1_UDF, "UDF", "11011110--------") },
|
{ INST(&V::thumb1_UDF, "UDF", "11011110--------") },
|
||||||
//{ INST(&V::thumb1_SWI, "SWI", "11011111xxxxxxxx") },
|
//{ INST(&V::thumb1_SWI, "SWI", "11011111xxxxxxxx") },
|
||||||
//{ INST(&V::thumb1_B_cond, "B (cond)", "1101ccccxxxxxxxx") },
|
//{ INST(&V::thumb1_B_cond, "B (cond)", "1101ccccxxxxxxxx") },
|
||||||
//{ INST(&V::thumb1_B_imm, "B (imm)", "11100xxxxxxxxxxx") },
|
//{ INST(&V::thumb1_B_imm, "B (imm)", "11100xxxxxxxxxxx") },
|
||||||
|
|
|
@ -161,6 +161,7 @@ public:
|
||||||
|
|
||||||
Arm::LocationDescriptor location;
|
Arm::LocationDescriptor location;
|
||||||
std::list<ValuePtr> instructions;
|
std::list<ValuePtr> instructions;
|
||||||
|
size_t cycle_count = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,10 @@ namespace Arm {
|
||||||
|
|
||||||
class IREmitter {
|
class IREmitter {
|
||||||
public:
|
public:
|
||||||
IR::Block block = Dynarmic::IR::Block({0, false, false});
|
explicit IREmitter(LocationDescriptor descriptor) : block(descriptor), current_location(descriptor) {}
|
||||||
|
|
||||||
|
IR::Block block;
|
||||||
|
LocationDescriptor current_location;
|
||||||
|
|
||||||
struct ResultAndCarry {
|
struct ResultAndCarry {
|
||||||
IR::ValuePtr result;
|
IR::ValuePtr result;
|
||||||
|
|
22
src/frontend/translate.cpp
Normal file
22
src/frontend/translate.cpp
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/* This file is part of the dynarmic project.
|
||||||
|
* Copyright (c) 2016 MerryMage
|
||||||
|
* This software may be used and distributed according to the terms of the GNU
|
||||||
|
* General Public License version 2 or any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "frontend/arm_types.h"
|
||||||
|
#include "frontend/ir/ir.h"
|
||||||
|
#include "frontend/translate.h"
|
||||||
|
|
||||||
|
namespace Dynarmic {
|
||||||
|
namespace Arm {
|
||||||
|
|
||||||
|
IR::Block TranslateArm(LocationDescriptor descriptor, MemoryRead32FuncType memory_read_32);
|
||||||
|
IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryRead32FuncType memory_read_32);
|
||||||
|
|
||||||
|
IR::Block Translate(LocationDescriptor descriptor, MemoryRead32FuncType memory_read_32) {
|
||||||
|
return (descriptor.TFlag ? TranslateThumb : TranslateArm)(descriptor, memory_read_32);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Arm
|
||||||
|
} // namespace Dynarmic
|
19
src/frontend/translate.h
Normal file
19
src/frontend/translate.h
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
/* This file is part of the dynarmic project.
|
||||||
|
* Copyright (c) 2016 MerryMage
|
||||||
|
* This software may be used and distributed according to the terms of the GNU
|
||||||
|
* General Public License version 2 or any later version.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "frontend/arm_types.h"
|
||||||
|
#include "frontend/ir/ir.h"
|
||||||
|
|
||||||
|
namespace Dynarmic {
|
||||||
|
namespace Arm {
|
||||||
|
|
||||||
|
using MemoryRead32FuncType = u32 (*)(u32 vaddr);
|
||||||
|
|
||||||
|
IR::Block Translate(LocationDescriptor descriptor, MemoryRead32FuncType memory_read_32);
|
||||||
|
|
||||||
|
} // namespace Arm
|
||||||
|
} // namespace Dynarmic
|
21
src/frontend/translate_arm.cpp
Normal file
21
src/frontend/translate_arm.cpp
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
/* This file is part of the dynarmic project.
|
||||||
|
* Copyright (c) 2016 MerryMage
|
||||||
|
* This software may be used and distributed according to the terms of the GNU
|
||||||
|
* General Public License version 2 or any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "frontend/arm_types.h"
|
||||||
|
#include "frontend/ir/ir.h"
|
||||||
|
#include "frontend/translate.h"
|
||||||
|
|
||||||
|
namespace Dynarmic {
|
||||||
|
namespace Arm {
|
||||||
|
|
||||||
|
IR::Block TranslateArm(LocationDescriptor descriptor, MemoryRead32FuncType memory_read_32) {
|
||||||
|
ASSERT_MSG(0, "Unimplemented");
|
||||||
|
return IR::Block(descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Arm
|
||||||
|
} // namespace Dynarmic
|
|
@ -4,19 +4,25 @@
|
||||||
* General Public License version 2 or any later version.
|
* General Public License version 2 or any later version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#include <tuple>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
#include "frontend/arm_types.h"
|
#include "frontend/arm_types.h"
|
||||||
|
#include "frontend/decoder/thumb1.h"
|
||||||
#include "frontend/ir_emitter.h"
|
#include "frontend/ir_emitter.h"
|
||||||
|
#include "frontend/translate.h"
|
||||||
|
|
||||||
namespace Dynarmic {
|
namespace Dynarmic {
|
||||||
namespace Arm {
|
namespace Arm {
|
||||||
|
|
||||||
class TranslatorVisitor {
|
struct TranslatorVisitor final {
|
||||||
public:
|
explicit TranslatorVisitor(LocationDescriptor descriptor) : ir(descriptor) {
|
||||||
|
ASSERT_MSG(descriptor.TFlag, "The processor must be in Thumb mode");
|
||||||
|
}
|
||||||
|
|
||||||
IREmitter ir;
|
IREmitter ir;
|
||||||
|
|
||||||
void thumb1_LSL_imm(Imm5 imm5, Reg m, Reg d) {
|
bool thumb1_LSL_imm(Imm5 imm5, Reg m, Reg d) {
|
||||||
u8 shift_n = imm5;
|
u8 shift_n = imm5;
|
||||||
// LSLS <Rd>, <Rm>, #<imm5>
|
// LSLS <Rd>, <Rm>, #<imm5>
|
||||||
auto cpsr_c = ir.GetCFlag();
|
auto cpsr_c = ir.GetCFlag();
|
||||||
|
@ -25,8 +31,9 @@ public:
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||||
ir.SetZFlag(ir.IsZero(result.result));
|
ir.SetZFlag(ir.IsZero(result.result));
|
||||||
ir.SetCFlag(result.carry);
|
ir.SetCFlag(result.carry);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
void thumb1_LSR_imm(Imm5 imm5, Reg m, Reg d) {
|
bool thumb1_LSR_imm(Imm5 imm5, Reg m, Reg d) {
|
||||||
u8 shift_n = imm5 != 0 ? imm5 : 32;
|
u8 shift_n = imm5 != 0 ? imm5 : 32;
|
||||||
// LSRS <Rd>, <Rm>, #<imm5>
|
// LSRS <Rd>, <Rm>, #<imm5>
|
||||||
auto cpsr_c = ir.GetCFlag();
|
auto cpsr_c = ir.GetCFlag();
|
||||||
|
@ -35,8 +42,9 @@ public:
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||||
ir.SetZFlag(ir.IsZero(result.result));
|
ir.SetZFlag(ir.IsZero(result.result));
|
||||||
ir.SetCFlag(result.carry);
|
ir.SetCFlag(result.carry);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
void thumb1_ASR_imm(Imm5 imm5, Reg m, Reg d) {
|
bool thumb1_ASR_imm(Imm5 imm5, Reg m, Reg d) {
|
||||||
u8 shift_n = imm5 != 0 ? imm5 : 32;
|
u8 shift_n = imm5 != 0 ? imm5 : 32;
|
||||||
// ASRS <Rd>, <Rm>, #<imm5>
|
// ASRS <Rd>, <Rm>, #<imm5>
|
||||||
auto cpsr_c = ir.GetCFlag();
|
auto cpsr_c = ir.GetCFlag();
|
||||||
|
@ -45,8 +53,10 @@ public:
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||||
ir.SetZFlag(ir.IsZero(result.result));
|
ir.SetZFlag(ir.IsZero(result.result));
|
||||||
ir.SetCFlag(result.carry);
|
ir.SetCFlag(result.carry);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
void thumb1_LSL_reg(Reg m, Reg d_n) {
|
|
||||||
|
bool thumb1_LSL_reg(Reg m, Reg d_n) {
|
||||||
const Reg d = d_n, n = d_n;
|
const Reg d = d_n, n = d_n;
|
||||||
// LSLS <Rdn>, <Rm>
|
// LSLS <Rdn>, <Rm>
|
||||||
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m));
|
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m));
|
||||||
|
@ -56,8 +66,9 @@ public:
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result_carry.result));
|
ir.SetNFlag(ir.MostSignificantBit(result_carry.result));
|
||||||
ir.SetZFlag(ir.IsZero(result_carry.result));
|
ir.SetZFlag(ir.IsZero(result_carry.result));
|
||||||
ir.SetCFlag(result_carry.carry);
|
ir.SetCFlag(result_carry.carry);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
void thumb1_LSR_reg(Reg m, Reg d_n) {
|
bool thumb1_LSR_reg(Reg m, Reg d_n) {
|
||||||
const Reg d = d_n, n = d_n;
|
const Reg d = d_n, n = d_n;
|
||||||
// LSRS <Rdn>, <Rm>
|
// LSRS <Rdn>, <Rm>
|
||||||
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m));
|
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m));
|
||||||
|
@ -67,8 +78,9 @@ public:
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||||
ir.SetZFlag(ir.IsZero(result.result));
|
ir.SetZFlag(ir.IsZero(result.result));
|
||||||
ir.SetCFlag(result.carry);
|
ir.SetCFlag(result.carry);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
void thumb1_ASR_reg(Reg m, Reg d_n) {
|
bool thumb1_ASR_reg(Reg m, Reg d_n) {
|
||||||
const Reg d = d_n, n = d_n;
|
const Reg d = d_n, n = d_n;
|
||||||
// ASRS <Rdn>, <Rm>
|
// ASRS <Rdn>, <Rm>
|
||||||
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m));
|
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m));
|
||||||
|
@ -78,11 +90,73 @@ public:
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||||
ir.SetZFlag(ir.IsZero(result.result));
|
ir.SetZFlag(ir.IsZero(result.result));
|
||||||
ir.SetCFlag(result.carry);
|
ir.SetCFlag(result.carry);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool thumb1_UDF() {
|
||||||
void thumb1_UDF() {}
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class ThumbInstSize {
|
||||||
|
Thumb16, Thumb32
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::tuple<u32, ThumbInstSize> ReadThumbInstruction(u32 arm_pc, MemoryRead32FuncType memory_read_32) {
|
||||||
|
u32 first_part = (*memory_read_32)(arm_pc & 0xFFFFFFFC);
|
||||||
|
if ((arm_pc & 0x2) != 0)
|
||||||
|
first_part >>= 16;
|
||||||
|
first_part &= 0xFFFF;
|
||||||
|
|
||||||
|
if ((first_part & 0xF800) != 0xE800 && (first_part & 0xF000) != 0xF000) {
|
||||||
|
// 16-bit thumb instruction
|
||||||
|
return std::make_tuple(first_part, ThumbInstSize::Thumb16);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 32-bit thumb instruction
|
||||||
|
|
||||||
|
u32 second_part = (*memory_read_32)((arm_pc+2) & 0xFFFFFFFC);
|
||||||
|
if (((arm_pc+2) & 0x2) != 0)
|
||||||
|
second_part >>= 16;
|
||||||
|
second_part &= 0xFFFF;
|
||||||
|
|
||||||
|
return std::make_tuple(static_cast<u32>((first_part << 16) | second_part), ThumbInstSize::Thumb32);
|
||||||
|
}
|
||||||
|
|
||||||
|
IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryRead32FuncType memory_read_32) {
|
||||||
|
TranslatorVisitor visitor{descriptor};
|
||||||
|
|
||||||
|
bool should_continue = true;
|
||||||
|
while (should_continue) {
|
||||||
|
const u32 arm_pc = visitor.ir.current_location.arm_pc;
|
||||||
|
|
||||||
|
u32 thumb_instruction;
|
||||||
|
ThumbInstSize inst_size;
|
||||||
|
std::tie(thumb_instruction, inst_size) = ReadThumbInstruction(arm_pc, memory_read_32);
|
||||||
|
|
||||||
|
if (inst_size == ThumbInstSize::Thumb16) {
|
||||||
|
auto decoder = DecodeThumb1<TranslatorVisitor>(static_cast<u16>(thumb_instruction));
|
||||||
|
if (decoder) {
|
||||||
|
should_continue = decoder->call(visitor, static_cast<u16>(thumb_instruction));
|
||||||
|
} else {
|
||||||
|
should_continue = visitor.thumb1_UDF();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/*auto decoder = DecodeThumb2<TranslatorVisitor>(thumb_instruction);
|
||||||
|
if (decoder) {
|
||||||
|
should_continue = decoder->call(visitor, thumb_instruction);
|
||||||
|
} else {
|
||||||
|
should_continue = visitor.thumb2_UDF();
|
||||||
|
}*/
|
||||||
|
ASSERT_MSG(0, "Unimplemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
visitor.ir.current_location.arm_pc += inst_size == ThumbInstSize::Thumb16 ? 2 : 4;
|
||||||
|
visitor.ir.block.cycle_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return visitor.ir.block;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Arm
|
} // namespace Arm
|
||||||
} // namepsace Dynarmic
|
} // namepsace Dynarmic
|
|
@ -1,4 +1,4 @@
|
||||||
include_directories(.)
|
include_directories(. ../src)
|
||||||
|
|
||||||
set(SRCS
|
set(SRCS
|
||||||
arm/fuzz_thumb.cpp
|
arm/fuzz_thumb.cpp
|
||||||
|
|
|
@ -6,54 +6,56 @@
|
||||||
|
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
|
|
||||||
#include "backend_x64/emit_x64.h"
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "frontend/decoder/thumb1.h"
|
#include "interface/interface.h"
|
||||||
#include "frontend/translate_thumb.h"
|
|
||||||
|
|
||||||
struct TinyBlockOfCode : Gen::XCodeBlock {
|
std::array<u32, 1024> code_mem{};
|
||||||
TinyBlockOfCode() {
|
|
||||||
AllocCodeSpace(256);
|
u32 MemoryRead32(u32 vaddr) {
|
||||||
|
if (vaddr < code_mem.size() * sizeof(u32)) {
|
||||||
|
return code_mem[vaddr / sizeof(u32)];
|
||||||
}
|
}
|
||||||
};
|
return vaddr;
|
||||||
|
}
|
||||||
|
|
||||||
void RunSingleThumbInstruction(u16 thumb_instruction, Dynarmic::BackendX64::JitState* jit_state_ptr) {
|
Dynarmic::UserCallbacks GetUserCallbacks() {
|
||||||
Dynarmic::Arm::TranslatorVisitor visitor;
|
Dynarmic::UserCallbacks user_callbacks{};
|
||||||
auto decoder = Dynarmic::Arm::DecodeThumb1<Dynarmic::Arm::TranslatorVisitor>(thumb_instruction);
|
user_callbacks.MemoryRead32 = &MemoryRead32;
|
||||||
REQUIRE(!!decoder);
|
return user_callbacks;
|
||||||
decoder->call(visitor, thumb_instruction);
|
|
||||||
|
|
||||||
TinyBlockOfCode block_of_code;
|
|
||||||
Dynarmic::BackendX64::Routines routines;
|
|
||||||
Dynarmic::UserCallbacks callbacks{};
|
|
||||||
Dynarmic::BackendX64::EmitX64 emitter(&block_of_code, &routines, callbacks);
|
|
||||||
|
|
||||||
Dynarmic::BackendX64::CodePtr code = emitter.Emit({0, true, false}, visitor.ir.block);
|
|
||||||
routines.RunCode(jit_state_ptr, code, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE( "thumb: lsls r0, r1, #2", "[thumb]" ) {
|
TEST_CASE( "thumb: lsls r0, r1, #2", "[thumb]" ) {
|
||||||
Dynarmic::BackendX64::JitState jit_state;
|
Dynarmic::Jit jit{GetUserCallbacks()};
|
||||||
jit_state.Reg[0] = 1;
|
code_mem.fill({});
|
||||||
jit_state.Reg[1] = 2;
|
code_mem[0] = 0x0088; // lsls r0, r1, #2
|
||||||
jit_state.Cpsr = 0;
|
code_mem[1] = 0xDE00; // udf #0
|
||||||
|
|
||||||
RunSingleThumbInstruction(0x0088, &jit_state);
|
jit.Regs()[0] = 1;
|
||||||
|
jit.Regs()[1] = 2;
|
||||||
|
jit.Regs()[15] = 0; // PC = 0
|
||||||
|
jit.Cpsr() = 0x00000030; // Thumb, User-mode
|
||||||
|
|
||||||
REQUIRE( jit_state.Reg[0] == 8 );
|
jit.Run(1);
|
||||||
REQUIRE( jit_state.Reg[1] == 2 );
|
|
||||||
REQUIRE( jit_state.Cpsr == 0 );
|
REQUIRE( jit.Regs()[0] == 8 );
|
||||||
|
REQUIRE( jit.Regs()[1] == 2 );
|
||||||
|
REQUIRE( jit.Cpsr() == 0x00000030 );
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE( "thumb: lsls r0, r1, #31", "[thumb]" ) {
|
TEST_CASE( "thumb: lsls r0, r1, #31", "[thumb]" ) {
|
||||||
Dynarmic::BackendX64::JitState jit_state;
|
Dynarmic::Jit jit{GetUserCallbacks()};
|
||||||
jit_state.Reg[0] = 1;
|
code_mem.fill({});
|
||||||
jit_state.Reg[1] = 0xFFFFFFFF;
|
code_mem[0] = 0x07C8; // lsls r0, r1, #31
|
||||||
jit_state.Cpsr = 0;
|
code_mem[1] = 0xDE00; // udf #0
|
||||||
|
|
||||||
RunSingleThumbInstruction(0x07C8, &jit_state);
|
jit.Regs()[0] = 1;
|
||||||
|
jit.Regs()[1] = 0xFFFFFFFF;
|
||||||
|
jit.Regs()[15] = 0; // PC = 0
|
||||||
|
jit.Cpsr() = 0x00000030; // Thumb, User-mode
|
||||||
|
|
||||||
REQUIRE( jit_state.Reg[0] == 0x80000000 );
|
jit.Run(1);
|
||||||
REQUIRE( jit_state.Reg[1] == 0xffffffff );
|
|
||||||
REQUIRE( jit_state.Cpsr == 0x20000000 );
|
REQUIRE( jit.Regs()[0] == 0x80000000 );
|
||||||
|
REQUIRE( jit.Regs()[1] == 0xffffffff );
|
||||||
|
REQUIRE( jit.Cpsr() == 0x20000030 ); // C flag, Thumb, User-mode
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include "tests/skyeye_interpreter/dyncom/arm_dyncom_dec.h"
|
#include "skyeye_interpreter/dyncom/arm_dyncom_dec.h"
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/armsupp.h"
|
#include "skyeye_interpreter/skyeye_common/armsupp.h"
|
||||||
|
|
||||||
const InstructionSetEncodingItem arm_instruction[] = {
|
const InstructionSetEncodingItem arm_instruction[] = {
|
||||||
{ "vmla", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x0, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 }},
|
{ "vmla", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x0, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 }},
|
||||||
|
|
|
@ -10,13 +10,13 @@
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
|
||||||
#include "tests/skyeye_interpreter/dyncom/arm_dyncom_dec.h"
|
#include "skyeye_interpreter/dyncom/arm_dyncom_dec.h"
|
||||||
#include "tests/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h"
|
#include "skyeye_interpreter/dyncom/arm_dyncom_interpreter.h"
|
||||||
#include "tests/skyeye_interpreter/dyncom/arm_dyncom_thumb.h"
|
#include "skyeye_interpreter/dyncom/arm_dyncom_thumb.h"
|
||||||
#include "tests/skyeye_interpreter/dyncom/arm_dyncom_run.h"
|
#include "skyeye_interpreter/dyncom/arm_dyncom_run.h"
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/armstate.h"
|
#include "skyeye_interpreter/skyeye_common/armstate.h"
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/armsupp.h"
|
#include "skyeye_interpreter/skyeye_common/armsupp.h"
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/vfp/vfp.h"
|
#include "skyeye_interpreter/skyeye_common/vfp/vfp.h"
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
COND = (1 << 0),
|
COND = (1 << 0),
|
||||||
|
@ -3184,11 +3184,11 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(yield)(unsigned int inst, int index)
|
||||||
// Floating point VFPv3 structures and instructions
|
// Floating point VFPv3 structures and instructions
|
||||||
|
|
||||||
#define VFP_INTERPRETER_STRUCT
|
#define VFP_INTERPRETER_STRUCT
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/vfp/vfpinstr.cpp"
|
#include "skyeye_interpreter/skyeye_common/vfp/vfpinstr.cpp"
|
||||||
#undef VFP_INTERPRETER_STRUCT
|
#undef VFP_INTERPRETER_STRUCT
|
||||||
|
|
||||||
#define VFP_INTERPRETER_TRANS
|
#define VFP_INTERPRETER_TRANS
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/vfp/vfpinstr.cpp"
|
#include "skyeye_interpreter/skyeye_common/vfp/vfpinstr.cpp"
|
||||||
#undef VFP_INTERPRETER_TRANS
|
#undef VFP_INTERPRETER_TRANS
|
||||||
|
|
||||||
typedef ARM_INST_PTR (*transop_fp_t)(unsigned int, int);
|
typedef ARM_INST_PTR (*transop_fp_t)(unsigned int, int);
|
||||||
|
@ -6859,7 +6859,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#define VFP_INTERPRETER_IMPL
|
#define VFP_INTERPRETER_IMPL
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/vfp/vfpinstr.cpp"
|
#include "skyeye_interpreter/skyeye_common/vfp/vfpinstr.cpp"
|
||||||
#undef VFP_INTERPRETER_IMPL
|
#undef VFP_INTERPRETER_IMPL
|
||||||
|
|
||||||
END:
|
END:
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/armstate.h"
|
#include "skyeye_interpreter/skyeye_common/armstate.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the PC is being read, and if so, word-aligns it.
|
* Checks if the PC is being read, and if so, word-aligns it.
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
// We can provide simple Thumb simulation by decoding the Thumb instruction into its corresponding
|
// We can provide simple Thumb simulation by decoding the Thumb instruction into its corresponding
|
||||||
// ARM instruction, and using the existing ARM simulator.
|
// ARM instruction, and using the existing ARM simulator.
|
||||||
|
|
||||||
#include "tests/skyeye_interpreter/dyncom/arm_dyncom_thumb.h"
|
#include "skyeye_interpreter/dyncom/arm_dyncom_thumb.h"
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/armsupp.h"
|
#include "skyeye_interpreter/skyeye_common/armsupp.h"
|
||||||
|
|
||||||
// Decode a 16bit Thumb instruction. The instruction is in the low 16-bits of the tinstr field,
|
// Decode a 16bit Thumb instruction. The instruction is in the low 16-bits of the tinstr field,
|
||||||
// with the following Thumb instruction held in the high 16-bits. Passing in two Thumb instructions
|
// with the following Thumb instruction held in the high 16-bits. Passing in two Thumb instructions
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/armstate.h"
|
#include "skyeye_interpreter/skyeye_common/armstate.h"
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/vfp/vfp.h"
|
#include "skyeye_interpreter/skyeye_common/vfp/vfp.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
inline u16 swap16(u16 data) {return (data >> 8) | (data << 8);}
|
inline u16 swap16(u16 data) {return (data >> 8) | (data << 8);}
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "interface/interface.h"
|
#include "interface/interface.h"
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/arm_regformat.h"
|
#include "skyeye_interpreter/skyeye_common/arm_regformat.h"
|
||||||
|
|
||||||
// Signal levels
|
// Signal levels
|
||||||
enum {
|
enum {
|
||||||
|
|
|
@ -17,9 +17,9 @@
|
||||||
|
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/arm_regformat.h"
|
#include "skyeye_interpreter/skyeye_common/arm_regformat.h"
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/armstate.h"
|
#include "skyeye_interpreter/skyeye_common/armstate.h"
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/armsupp.h"
|
#include "skyeye_interpreter/skyeye_common/armsupp.h"
|
||||||
|
|
||||||
// Unsigned sum of absolute difference
|
// Unsigned sum of absolute difference
|
||||||
u8 ARMul_UnsignedAbsoluteDifference(u8 left, u8 right)
|
u8 ARMul_UnsignedAbsoluteDifference(u8 left, u8 right)
|
||||||
|
|
|
@ -23,9 +23,9 @@
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/armstate.h"
|
#include "skyeye_interpreter/skyeye_common/armstate.h"
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/vfp/asm_vfp.h"
|
#include "skyeye_interpreter/skyeye_common/vfp/asm_vfp.h"
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/vfp/vfp.h"
|
#include "skyeye_interpreter/skyeye_common/vfp/vfp.h"
|
||||||
|
|
||||||
void VFPInit(ARMul_State* state)
|
void VFPInit(ARMul_State* state)
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/vfp/vfp_helper.h" /* for references to cdp SoftFloat functions */
|
#include "skyeye_interpreter/skyeye_common/vfp/vfp_helper.h" /* for references to cdp SoftFloat functions */
|
||||||
|
|
||||||
#define VFP_DEBUG_UNTESTED(x) LOG_TRACE(Core_ARM11, "in func %s, " #x " untested", __FUNCTION__);
|
#define VFP_DEBUG_UNTESTED(x) LOG_TRACE(Core_ARM11, "in func %s, " #x " untested", __FUNCTION__);
|
||||||
#define CHECK_VFP_ENABLED
|
#define CHECK_VFP_ENABLED
|
||||||
|
|
|
@ -34,8 +34,8 @@
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/armstate.h"
|
#include "skyeye_interpreter/skyeye_common/armstate.h"
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/vfp/asm_vfp.h"
|
#include "skyeye_interpreter/skyeye_common/vfp/asm_vfp.h"
|
||||||
|
|
||||||
#define do_div(n, base) {n/=base;}
|
#define do_div(n, base) {n/=base;}
|
||||||
|
|
||||||
|
|
|
@ -53,9 +53,9 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/vfp/vfp.h"
|
#include "skyeye_interpreter/skyeye_common/vfp/vfp.h"
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/vfp/vfp_helper.h"
|
#include "skyeye_interpreter/skyeye_common/vfp/vfp_helper.h"
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/vfp/asm_vfp.h"
|
#include "skyeye_interpreter/skyeye_common/vfp/asm_vfp.h"
|
||||||
|
|
||||||
static struct vfp_double vfp_double_default_qnan = {
|
static struct vfp_double vfp_double_default_qnan = {
|
||||||
2047,
|
2047,
|
||||||
|
|
|
@ -57,9 +57,9 @@
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/vfp/vfp_helper.h"
|
#include "skyeye_interpreter/skyeye_common/vfp/vfp_helper.h"
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/vfp/asm_vfp.h"
|
#include "skyeye_interpreter/skyeye_common/vfp/asm_vfp.h"
|
||||||
#include "tests/skyeye_interpreter/skyeye_common/vfp/vfp.h"
|
#include "skyeye_interpreter/skyeye_common/vfp/vfp.h"
|
||||||
|
|
||||||
static struct vfp_single vfp_single_default_qnan = {
|
static struct vfp_single vfp_single_default_qnan = {
|
||||||
255,
|
255,
|
||||||
|
|
Loading…
Reference in a new issue