Implement Thumb Instructions: BLX (imm), BL (imm)
This commit is contained in:
parent
e0d6e28b67
commit
3f11a149d7
5 changed files with 153 additions and 4 deletions
|
@ -38,6 +38,7 @@ set(HEADERS
|
||||||
frontend/decoder/arm.h
|
frontend/decoder/arm.h
|
||||||
frontend/decoder/decoder_detail.h
|
frontend/decoder/decoder_detail.h
|
||||||
frontend/decoder/thumb16.h
|
frontend/decoder/thumb16.h
|
||||||
|
frontend/decoder/thumb32.h
|
||||||
frontend/disassembler/disassembler.h
|
frontend/disassembler/disassembler.h
|
||||||
frontend/ir/ir.h
|
frontend/ir/ir.h
|
||||||
frontend/ir/ir_emitter.h
|
frontend/ir/ir_emitter.h
|
||||||
|
|
|
@ -922,10 +922,16 @@ void EmitX64::EmitTerminalReturnToDispatch(IR::Term::ReturnToDispatch, Arm::Loca
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitX64::EmitTerminalLinkBlock(IR::Term::LinkBlock terminal, Arm::LocationDescriptor initial_location) {
|
void EmitX64::EmitTerminalLinkBlock(IR::Term::LinkBlock terminal, Arm::LocationDescriptor initial_location) {
|
||||||
ASSERT_MSG(terminal.next.TFlag == initial_location.TFlag, "Unimplemented");
|
|
||||||
ASSERT_MSG(terminal.next.EFlag == initial_location.EFlag, "Unimplemented");
|
ASSERT_MSG(terminal.next.EFlag == initial_location.EFlag, "Unimplemented");
|
||||||
|
|
||||||
code->MOV(32, MJitStateReg(Arm::Reg::PC), Imm32(terminal.next.arm_pc));
|
code->MOV(32, MJitStateReg(Arm::Reg::PC), Imm32(terminal.next.arm_pc));
|
||||||
|
if (terminal.next.TFlag != initial_location.TFlag) {
|
||||||
|
if (terminal.next.TFlag) {
|
||||||
|
code->OR(32, MJitStateCpsr(), Imm32(1 << 5));
|
||||||
|
} else {
|
||||||
|
code->AND(32, MJitStateCpsr(), Imm32(~(1 << 5)));
|
||||||
|
}
|
||||||
|
}
|
||||||
routines->GenReturnFromRunCode(code); // TODO: Check cycles, Properly do a link
|
routines->GenReturnFromRunCode(code); // TODO: Check cycles, Properly do a link
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
82
src/frontend/decoder/thumb32.h
Normal file
82
src/frontend/decoder/thumb32.h
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
/* 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 <array>
|
||||||
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "frontend/decoder/decoder_detail.h"
|
||||||
|
|
||||||
|
namespace Dynarmic {
|
||||||
|
namespace Arm {
|
||||||
|
|
||||||
|
template <typename Visitor>
|
||||||
|
struct Thumb32Matcher {
|
||||||
|
using CallRetT = typename mp::MemFnInfo<decltype(&Visitor::thumb32_UDF), &Visitor::thumb32_UDF>::return_type;
|
||||||
|
|
||||||
|
Thumb32Matcher(const char* const name, u32 mask, u32 expect, std::function<CallRetT(Visitor&, u32)> fn)
|
||||||
|
: name(name), mask(mask), expect(expect), fn(fn) {}
|
||||||
|
|
||||||
|
/// Gets the name of this type of instruction.
|
||||||
|
const char* GetName() const {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests to see if the instruction is this type of instruction.
|
||||||
|
* @param instruction The instruction to test
|
||||||
|
* @returns true if the instruction is
|
||||||
|
*/
|
||||||
|
bool Matches(u32 instruction) const {
|
||||||
|
return (instruction & mask) == expect;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the corresponding instruction handler on visitor for this type of instruction.
|
||||||
|
* @param v The visitor to use
|
||||||
|
* @param instruction The instruction to decode.
|
||||||
|
*/
|
||||||
|
CallRetT call(Visitor& v, u32 instruction) const {
|
||||||
|
assert(Matches(instruction));
|
||||||
|
return fn(v, instruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const char* name;
|
||||||
|
u32 mask, expect;
|
||||||
|
std::function<CallRetT(Visitor&, u32)> fn;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename V>
|
||||||
|
boost::optional<const Thumb32Matcher<V>&> DecodeThumb32(u32 instruction) {
|
||||||
|
const static std::vector<Thumb32Matcher<V>> table = {
|
||||||
|
|
||||||
|
#define INST(fn, name, bitstring) detail::detail<Thumb32Matcher, u32, 32>::GetMatcher<decltype(fn), fn>(name, bitstring)
|
||||||
|
|
||||||
|
// Branch instructions
|
||||||
|
INST(&V::thumb32_BL_imm, "BL (imm)", "11110vvvvvvvvvvv11111vvvvvvvvvvv"), // v4T
|
||||||
|
INST(&V::thumb32_BLX_imm, "BLX (imm)", "11110vvvvvvvvvvv11101vvvvvvvvvvv"), // v5T
|
||||||
|
|
||||||
|
// Misc instructions
|
||||||
|
INST(&V::thumb32_UDF, "UDF", "111101111111----1010------------"), // v6T2
|
||||||
|
|
||||||
|
#undef INST
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto matches_instruction = [instruction](const auto& matcher){ return matcher.Matches(instruction); };
|
||||||
|
|
||||||
|
auto iter = std::find_if(table.begin(), table.end(), matches_instruction);
|
||||||
|
return iter != table.end() ? boost::make_optional<const Thumb32Matcher<V>&>(*iter) : boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Arm
|
||||||
|
} // namespace Dynarmic
|
|
@ -10,6 +10,7 @@
|
||||||
#include "common/bit_util.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/decoder/thumb32.h"
|
||||||
#include "frontend/ir/ir_emitter.h"
|
#include "frontend/ir/ir_emitter.h"
|
||||||
#include "frontend/translate/translate.h"
|
#include "frontend/translate/translate.h"
|
||||||
|
|
||||||
|
@ -786,6 +787,34 @@ struct ThumbTranslatorVisitor final {
|
||||||
ir.SetTerm(IR::Term::LinkBlock{next_location});
|
ir.SetTerm(IR::Term::LinkBlock{next_location});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool thumb32_BL_imm(Imm11 hi, Imm11 lo) {
|
||||||
|
s32 imm32 = Common::SignExtend<32, s32>((hi << 12) | (lo << 1));
|
||||||
|
// BL <label>
|
||||||
|
ir.SetRegister(Reg::LR, ir.Imm32((ir.current_location.arm_pc + 4) | 1));
|
||||||
|
auto new_location = ir.current_location;
|
||||||
|
new_location.arm_pc = ir.PC() + imm32;
|
||||||
|
ir.SetTerm(IR::Term::LinkBlock{new_location});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool thumb32_BLX_imm(Imm11 hi, Imm11 lo) {
|
||||||
|
s32 imm32 = Common::SignExtend<32, s32>((hi << 12) | (lo << 1));
|
||||||
|
if ((lo & 1) != 0) {
|
||||||
|
return UnpredictableInstruction();
|
||||||
|
}
|
||||||
|
// BLX <label>
|
||||||
|
ir.SetRegister(Reg::LR, ir.Imm32((ir.current_location.arm_pc + 4) | 1));
|
||||||
|
auto new_location = ir.current_location;
|
||||||
|
new_location.arm_pc = ir.AlignPC(4) + imm32;
|
||||||
|
new_location.TFlag = false;
|
||||||
|
ir.SetTerm(IR::Term::LinkBlock{new_location});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool thumb32_UDF() {
|
||||||
|
return thumb16_UDF();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ThumbInstSize {
|
enum class ThumbInstSize {
|
||||||
|
@ -835,13 +864,12 @@ IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryRead32FuncType mem
|
||||||
should_continue = visitor.thumb16_UDF();
|
should_continue = visitor.thumb16_UDF();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/*auto decoder = DecodeThumb32<ThumbTranslatorVisitor>(thumb_instruction);
|
auto decoder = DecodeThumb32<ThumbTranslatorVisitor>(thumb_instruction);
|
||||||
if (decoder) {
|
if (decoder) {
|
||||||
should_continue = decoder->call(visitor, thumb_instruction);
|
should_continue = decoder->call(visitor, thumb_instruction);
|
||||||
} else {
|
} else {
|
||||||
should_continue = visitor.thumb32_UDF();
|
should_continue = visitor.thumb32_UDF();
|
||||||
}*/
|
}
|
||||||
should_continue = visitor.InterpretThisInstruction();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
visitor.ir.current_location.arm_pc += (inst_size == ThumbInstSize::Thumb16) ? 2 : 4;
|
visitor.ir.current_location.arm_pc += (inst_size == ThumbInstSize::Thumb16) ? 2 : 4;
|
||||||
|
|
|
@ -120,3 +120,35 @@ TEST_CASE( "thumb: ldr r3, [r3, #28]", "[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: blx +#67712", "[thumb]" ) {
|
||||||
|
Dynarmic::Jit jit{GetUserCallbacks()};
|
||||||
|
code_mem.fill({});
|
||||||
|
code_mem[0] = 0xF010; code_mem[1] = 0xEC3E; // blx +#67712
|
||||||
|
code_mem[2] = 0xE7FE; // b +#0
|
||||||
|
|
||||||
|
jit.Regs()[15] = 0; // PC = 0
|
||||||
|
jit.Cpsr() = 0x00000030; // Thumb, User-mode
|
||||||
|
|
||||||
|
jit.Run(1);
|
||||||
|
|
||||||
|
REQUIRE( jit.Regs()[14] == (0x4 | 1) );
|
||||||
|
REQUIRE( jit.Regs()[15] == 0x10880 );
|
||||||
|
REQUIRE( jit.Cpsr() == 0x00000010 ); // User-mode
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE( "thumb: bl +#234584", "[thumb]" ) {
|
||||||
|
Dynarmic::Jit jit{GetUserCallbacks()};
|
||||||
|
code_mem.fill({});
|
||||||
|
code_mem[0] = 0xF039; code_mem[1] = 0xFA2A; // bl +#234584
|
||||||
|
code_mem[2] = 0xE7FE; // b +#0
|
||||||
|
|
||||||
|
jit.Regs()[15] = 0; // PC = 0
|
||||||
|
jit.Cpsr() = 0x00000030; // Thumb, User-mode
|
||||||
|
|
||||||
|
jit.Run(1);
|
||||||
|
|
||||||
|
REQUIRE( jit.Regs()[14] == (0x4 | 1) );
|
||||||
|
REQUIRE( jit.Regs()[15] == 0x39458 );
|
||||||
|
REQUIRE( jit.Cpsr() == 0x00000030 ); // Thumb, User-mode
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue