VFP: Implement VADD.{F32,F64}

This commit is contained in:
MerryMage 2016-08-06 17:21:29 +01:00
parent 8ff414ee0e
commit 4b31ea25a7
14 changed files with 350 additions and 27 deletions

View file

@ -11,6 +11,7 @@
#include <tuple>
#include <type_traits>
#include "common/bit_util.h"
#include "common/common_types.h"
namespace Dynarmic {
@ -96,6 +97,8 @@ struct LocationDescriptor {
bool TFlag() const { return tflag; }
bool EFlag() const { return eflag; }
u32 FPSCR() const { return fpscr; }
bool FPSCR_FTZ() const { return Common::Bit<24>(fpscr); }
bool FPSCR_DN() const { return Common::Bit<25>(fpscr); }
bool operator == (const LocationDescriptor& o) const {
return std::tie(arm_pc, tflag, eflag, fpscr) == std::tie(o.arm_pc, o.tflag, o.eflag, o.fpscr);

109
src/frontend/decoder/vfp2.h Normal file
View file

@ -0,0 +1,109 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2032 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 VFP2Matcher {
using CallRetT = typename mp::MemFnInfo<decltype(&Visitor::vfp2_VADD)>::return_type;
VFP2Matcher(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 VFP2Matcher<V>&> DecodeVFP2(u32 instruction) {
const static std::vector<VFP2Matcher<V>> table = {
#define INST(fn, name, bitstring) detail::detail<VFP2Matcher, u32, 32>::GetMatcher<decltype(fn)>(fn, name, bitstring)
// cccc1110________----101-__-0----
// Floating-point three-register data processing instructions
// VMLA
// VMLS
// VNMLA
// VNMLS
// VNMUL
// VMUL
INST(&V::vfp2_VADD, "VADD", "cccc11100D11nnnndddd101zN0M0mmmm"),
// VSUB
// VDIV
// Floating-point other instructions
// VMOV_imm
// VMOV_reg
// VABS
// VNEG
// VSQRT
// VCMP
// VCMPE
// VCVT
// VCVTR
// Extension register load-store instructions
// VSTR
// VSTM
// VSTMDB
// VPUSH
// VLDR
// VLDM
// VLDMDB
// VPOP
#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 VFP2Matcher<V>&>(*iter) : boost::none;
}
} // namespace Arm
} // namespace Dynarmic

View file

@ -11,6 +11,7 @@
#include "common/string_util.h"
#include "frontend/arm_types.h"
#include "frontend/decoder/arm.h"
#include "frontend/decoder/vfp2.h"
namespace Dynarmic {
namespace Arm {
@ -81,6 +82,16 @@ public:
return "<internal error>";
}
std::string FPRegStr(bool dp_operation, size_t base, bool bit) {
size_t reg_num;
if (dp_operation) {
reg_num = base + (bit ? 16 : 0);
} else {
reg_num = (base << 1) + (bit ? 1 : 0);
}
return Common::StringFromFormat("%c%zu", dp_operation ? 'd' : 's', reg_num);
}
// Branch instructions
std::string arm_B(Cond cond, Imm24 imm24) {
s32 offset = Common::SignExtend<26, s32>(imm24 << 2) + 8;
@ -497,12 +508,22 @@ public:
std::string arm_RFE() { return "ice"; }
std::string arm_SETEND(bool E) { return "ice"; }
std::string arm_SRS() { return "ice"; }
// Floating point arithmetic instructions
std::string vfp2_VADD(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
return Common::StringFromFormat("vadd%s.%s %s, %s, %s", CondToString(cond), sz ? "f64" : "f32", FPRegStr(sz, Vd, D).c_str(), FPRegStr(sz, Vn, N).c_str(), FPRegStr(sz, Vm, M).c_str());
}
};
std::string DisassembleArm(u32 instruction) {
DisassemblerVisitor visitor;
auto decoder = DecodeArm<DisassemblerVisitor>(instruction);
return !decoder ? Common::StringFromFormat("UNKNOWN: %x", instruction) : decoder->call(visitor, instruction);
if (auto vfp_decoder = DecodeVFP2<DisassemblerVisitor>(instruction)) {
return vfp_decoder->call(visitor, instruction);
} else if (auto decoder = DecodeArm<DisassemblerVisitor>(instruction)) {
return decoder->call(visitor, instruction);
} else {
return Common::StringFromFormat("UNKNOWN: %x", instruction);
}
}
} // namespace Arm

View file

@ -282,6 +282,16 @@ IR::Value IREmitter::ByteReverseDual(const IR::Value& a) {
return Inst(IR::Opcode::ByteReverseDual, {a});
}
IR::Value IREmitter::FPAdd32(const IR::Value& a, const IR::Value& b, bool fpscr_controlled) {
ASSERT(fpscr_controlled);
return Inst(IR::Opcode::FPAdd32, {a, b});
}
IR::Value IREmitter::FPAdd64(const IR::Value& a, const IR::Value& b, bool fpscr_controlled) {
ASSERT(fpscr_controlled);
return Inst(IR::Opcode::FPAdd64, {a, b});
}
IR::Value IREmitter::ReadMemory8(const IR::Value& vaddr) {
return Inst(IR::Opcode::ReadMemory8, {vaddr});
}

View file

@ -93,6 +93,9 @@ public:
IR::Value ByteReverseHalf(const IR::Value& a);
IR::Value ByteReverseDual(const IR::Value& a);
IR::Value FPAdd32(const IR::Value& a, const IR::Value& b, bool fpscr_controlled);
IR::Value FPAdd64(const IR::Value& a, const IR::Value& b, bool fpscr_controlled);
IR::Value ReadMemory8(const IR::Value& vaddr);
IR::Value ReadMemory16(const IR::Value& vaddr);
IR::Value ReadMemory32(const IR::Value& vaddr);

View file

@ -58,6 +58,10 @@ OPCODE(ByteReverseWord, T::U32, T::U32
OPCODE(ByteReverseHalf, T::U16, T::U16 )
OPCODE(ByteReverseDual, T::U64, T::U64 )
// Floating-point
OPCODE(FPAdd32, T::F32, T::F32, T::F32 )
OPCODE(FPAdd64, T::F64, T::F64, T::F64 )
// Memory access
OPCODE(ReadMemory8, T::U8, T::U32 )
OPCODE(ReadMemory16, T::U16, T::U32 )

View file

@ -7,6 +7,7 @@
#include "common/assert.h"
#include "frontend/arm_types.h"
#include "frontend/decoder/arm.h"
#include "frontend/decoder/vfp2.h"
#include "frontend/ir/ir.h"
#include "frontend/translate/translate.h"
#include "frontend/translate/translate_arm/translate_arm.h"
@ -22,8 +23,9 @@ IR::Block TranslateArm(LocationDescriptor descriptor, MemoryRead32FuncType memor
const u32 arm_pc = visitor.ir.current_location.PC();
const u32 arm_instruction = (*memory_read_32)(arm_pc);
const auto decoder = DecodeArm<ArmTranslatorVisitor>(arm_instruction);
if (decoder) {
if (auto vfp_decoder = DecodeVFP2<ArmTranslatorVisitor>(arm_instruction)) {
should_continue = vfp_decoder->call(visitor, arm_instruction);
} else if (auto decoder = DecodeArm<ArmTranslatorVisitor>(arm_instruction)) {
should_continue = decoder->call(visitor, arm_instruction);
} else {
should_continue = visitor.arm_UDF();

View file

@ -317,6 +317,9 @@ struct ArmTranslatorVisitor final {
bool arm_RFE() { return InterpretThisInstruction(); }
bool arm_SETEND(bool E) { return InterpretThisInstruction(); }
bool arm_SRS() { return InterpretThisInstruction(); }
// Floating-point three-register data processing instructions
bool vfp2_VADD(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
};
} // namespace Arm

View file

@ -0,0 +1,38 @@
/* 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 "translate_arm.h"
namespace Dynarmic {
namespace Arm {
static ExtReg ToExtReg(bool sz, size_t base, bool bit) {
if (sz) {
return static_cast<ExtReg>(static_cast<size_t>(ExtReg::D0) + base + (bit ? 16 : 0));
} else {
return static_cast<ExtReg>(static_cast<size_t>(ExtReg::S0) + (base << 1) + (bit ? 1 : 0));
}
}
bool ArmTranslatorVisitor::vfp2_VADD(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
// TODO: if (FSPCR.len || FPSCR.stride) return InterpretThisInstruction();
ExtReg d = ToExtReg(sz, Vd, D);
ExtReg n = ToExtReg(sz, Vn, N);
ExtReg m = ToExtReg(sz, Vm, M);
// VADD.{F32,F64} <{S,D}d>, <{S,D}n>, <{S,D}m>
if (ConditionPassed(cond)) {
auto a = ir.GetExtendedRegister(n);
auto b = ir.GetExtendedRegister(m);
auto result = sz
? ir.FPAdd64(a, b, true)
: ir.FPAdd32(a, b, true);
ir.SetExtendedRegister(d, result);
}
return true;
}
} // namespace Arm
} // namespace Dynarmic