mirror of
https://git.suyu.dev/suyu/dynarmic.git
synced 2026-01-03 21:24:38 +01:00
VFP: Implement VADD.{F32,F64}
This commit is contained in:
parent
8ff414ee0e
commit
4b31ea25a7
14 changed files with 350 additions and 27 deletions
|
|
@ -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
109
src/frontend/decoder/vfp2.h
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
38
src/frontend/translate/translate_arm/vfp2.cpp
Normal file
38
src/frontend/translate/translate_arm/vfp2.cpp
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue