early-access version 2799
This commit is contained in:
parent
b216437217
commit
10e9e7d9ae
39 changed files with 381 additions and 200 deletions
|
@ -1,7 +1,7 @@
|
||||||
yuzu emulator early access
|
yuzu emulator early access
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This is the source code for early-access 2798.
|
This is the source code for early-access 2799.
|
||||||
|
|
||||||
## Legal Notice
|
## Legal Notice
|
||||||
|
|
||||||
|
|
2
externals/dynarmic/CMakeLists.txt
vendored
2
externals/dynarmic/CMakeLists.txt
vendored
|
@ -1,5 +1,5 @@
|
||||||
cmake_minimum_required(VERSION 3.8)
|
cmake_minimum_required(VERSION 3.8)
|
||||||
project(dynarmic LANGUAGES C CXX ASM VERSION 5)
|
project(dynarmic LANGUAGES C CXX ASM VERSION 6.0.1)
|
||||||
|
|
||||||
# Determine if we're built as a subproject (using add_subdirectory)
|
# Determine if we're built as a subproject (using add_subdirectory)
|
||||||
# or if this is the master project.
|
# or if this is the master project.
|
||||||
|
|
|
@ -97,13 +97,13 @@ constexpr u8 SNaN = 0b10000000;
|
||||||
|
|
||||||
// Opcodes for use with vfixupimm
|
// Opcodes for use with vfixupimm
|
||||||
enum class FpFixup : u8 {
|
enum class FpFixup : u8 {
|
||||||
A = 0b0000, // A
|
Dest = 0b0000, // Preserve destination
|
||||||
B = 0b0001, // B
|
Norm_Src = 0b0001, // Source operand (Denormal as positive-zero)
|
||||||
QNaN_B = 0b0010, // QNaN with sign of B
|
QNaN_Src = 0b0010, // QNaN with sign of source (Denormal as positive-zero)
|
||||||
IndefNaN = 0b0011, // Indefinite QNaN (Negative QNaN with no payload on x86)
|
IndefNaN = 0b0011, // Indefinite QNaN (Negative QNaN with no payload on x86)
|
||||||
NegInf = 0b0100, // -Infinity
|
NegInf = 0b0100, // -Infinity
|
||||||
PosInf = 0b0101, // +Infinity
|
PosInf = 0b0101, // +Infinity
|
||||||
Inf_B = 0b0110, // Infinity with sign of B
|
Inf_Src = 0b0110, // Infinity with sign of source (Denormal as positive-zero)
|
||||||
NegZero = 0b0111, // -0.0
|
NegZero = 0b0111, // -0.0
|
||||||
PosZero = 0b1000, // +0.0
|
PosZero = 0b1000, // +0.0
|
||||||
NegOne = 0b1001, // -1.0
|
NegOne = 0b1001, // -1.0
|
||||||
|
@ -116,14 +116,14 @@ enum class FpFixup : u8 {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generates 32-bit LUT for vfixupimm instruction
|
// Generates 32-bit LUT for vfixupimm instruction
|
||||||
constexpr u32 FixupLUT(FpFixup src_qnan = FpFixup::A,
|
constexpr u32 FixupLUT(FpFixup src_qnan = FpFixup::Dest,
|
||||||
FpFixup src_snan = FpFixup::A,
|
FpFixup src_snan = FpFixup::Dest,
|
||||||
FpFixup src_zero = FpFixup::A,
|
FpFixup src_zero = FpFixup::Dest,
|
||||||
FpFixup src_posone = FpFixup::A,
|
FpFixup src_posone = FpFixup::Dest,
|
||||||
FpFixup src_neginf = FpFixup::A,
|
FpFixup src_neginf = FpFixup::Dest,
|
||||||
FpFixup src_posinf = FpFixup::A,
|
FpFixup src_posinf = FpFixup::Dest,
|
||||||
FpFixup src_pos = FpFixup::A,
|
FpFixup src_pos = FpFixup::Dest,
|
||||||
FpFixup src_neg = FpFixup::A) {
|
FpFixup src_neg = FpFixup::Dest) {
|
||||||
u32 fixup_lut = 0;
|
u32 fixup_lut = 0;
|
||||||
fixup_lut = mcl::bit::set_bits<0, 3, u32>(fixup_lut, static_cast<u32>(src_qnan));
|
fixup_lut = mcl::bit::set_bits<0, 3, u32>(fixup_lut, static_cast<u32>(src_qnan));
|
||||||
fixup_lut = mcl::bit::set_bits<4, 7, u32>(fixup_lut, static_cast<u32>(src_snan));
|
fixup_lut = mcl::bit::set_bits<4, 7, u32>(fixup_lut, static_cast<u32>(src_snan));
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <mcl/mp/typelist/lower_to_tuple.hpp>
|
#include <mcl/mp/typelist/lower_to_tuple.hpp>
|
||||||
#include <mcl/stdint.hpp>
|
#include <mcl/stdint.hpp>
|
||||||
#include <mcl/type_traits/integer_of_size.hpp>
|
#include <mcl/type_traits/integer_of_size.hpp>
|
||||||
|
#include <xbyak/xbyak.h>
|
||||||
|
|
||||||
#include "dynarmic/backend/x64/abi.h"
|
#include "dynarmic/backend/x64/abi.h"
|
||||||
#include "dynarmic/backend/x64/block_of_code.h"
|
#include "dynarmic/backend/x64/block_of_code.h"
|
||||||
|
@ -79,6 +80,27 @@ constexpr u64 f64_max_s64_lim = 0x43e0000000000000u; // 2^63 as a double (actua
|
||||||
template<size_t fsize>
|
template<size_t fsize>
|
||||||
void DenormalsAreZero(BlockOfCode& code, EmitContext& ctx, std::initializer_list<Xbyak::Xmm> to_daz) {
|
void DenormalsAreZero(BlockOfCode& code, EmitContext& ctx, std::initializer_list<Xbyak::Xmm> to_daz) {
|
||||||
if (ctx.FPCR().FZ()) {
|
if (ctx.FPCR().FZ()) {
|
||||||
|
if (code.HasHostFeature(HostFeature::AVX512_OrthoFloat)) {
|
||||||
|
constexpr u32 denormal_to_zero = FixupLUT(
|
||||||
|
FpFixup::Norm_Src,
|
||||||
|
FpFixup::Norm_Src,
|
||||||
|
FpFixup::Norm_Src,
|
||||||
|
FpFixup::Norm_Src,
|
||||||
|
FpFixup::Norm_Src,
|
||||||
|
FpFixup::Norm_Src,
|
||||||
|
FpFixup::Norm_Src,
|
||||||
|
FpFixup::Norm_Src);
|
||||||
|
constexpr u64 denormal_to_zero64 = mcl::bit::replicate_element<fsize, u64>(denormal_to_zero);
|
||||||
|
|
||||||
|
const Xbyak::Xmm tmp = xmm16;
|
||||||
|
FCODE(vmovap)(tmp, code.MConst(xword, u64(denormal_to_zero64), u64(denormal_to_zero64)));
|
||||||
|
|
||||||
|
for (const Xbyak::Xmm& xmm : to_daz) {
|
||||||
|
FCODE(vfixupimms)(xmm, xmm, tmp, u8(0));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (const Xbyak::Xmm& xmm : to_daz) {
|
for (const Xbyak::Xmm& xmm : to_daz) {
|
||||||
code.movaps(xmm0, code.MConst(xword, fsize == 32 ? f32_non_sign_mask : f64_non_sign_mask));
|
code.movaps(xmm0, code.MConst(xword, fsize == 32 ? f32_non_sign_mask : f64_non_sign_mask));
|
||||||
code.andps(xmm0, xmm);
|
code.andps(xmm0, xmm);
|
||||||
|
@ -114,7 +136,11 @@ void ZeroIfNaN(BlockOfCode& code, Xbyak::Xmm xmm_value, Xbyak::Xmm xmm_scratch)
|
||||||
|
|
||||||
template<size_t fsize>
|
template<size_t fsize>
|
||||||
void ForceToDefaultNaN(BlockOfCode& code, Xbyak::Xmm result) {
|
void ForceToDefaultNaN(BlockOfCode& code, Xbyak::Xmm result) {
|
||||||
if (code.HasHostFeature(HostFeature::AVX)) {
|
if (code.HasHostFeature(HostFeature::AVX512_OrthoFloat)) {
|
||||||
|
const Xbyak::Opmask nan_mask = k1;
|
||||||
|
FCODE(vfpclasss)(nan_mask, result, u8(FpClass::QNaN | FpClass::SNaN));
|
||||||
|
FCODE(vblendmp)(result | nan_mask, result, code.MConst(ptr_b, fsize == 32 ? f32_nan : f64_nan));
|
||||||
|
} else if (code.HasHostFeature(HostFeature::AVX)) {
|
||||||
FCODE(vcmpunords)(xmm0, result, result);
|
FCODE(vcmpunords)(xmm0, result, result);
|
||||||
FCODE(blendvp)(result, code.MConst(xword, fsize == 32 ? f32_nan : f64_nan));
|
FCODE(blendvp)(result, code.MConst(xword, fsize == 32 ? f32_nan : f64_nan));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <mcl/mp/typelist/lower_to_tuple.hpp>
|
#include <mcl/mp/typelist/lower_to_tuple.hpp>
|
||||||
#include <mcl/type_traits/function_info.hpp>
|
#include <mcl/type_traits/function_info.hpp>
|
||||||
#include <mcl/type_traits/integer_of_size.hpp>
|
#include <mcl/type_traits/integer_of_size.hpp>
|
||||||
|
#include <xbyak/xbyak.h>
|
||||||
|
|
||||||
#include "dynarmic/backend/x64/abi.h"
|
#include "dynarmic/backend/x64/abi.h"
|
||||||
#include "dynarmic/backend/x64/block_of_code.h"
|
#include "dynarmic/backend/x64/block_of_code.h"
|
||||||
|
@ -189,11 +190,16 @@ Xbyak::Address GetVectorOf(BlockOfCode& code) {
|
||||||
template<size_t fsize>
|
template<size_t fsize>
|
||||||
void ForceToDefaultNaN(BlockOfCode& code, FP::FPCR fpcr, Xbyak::Xmm result) {
|
void ForceToDefaultNaN(BlockOfCode& code, FP::FPCR fpcr, Xbyak::Xmm result) {
|
||||||
if (fpcr.DN()) {
|
if (fpcr.DN()) {
|
||||||
const Xbyak::Xmm nan_mask = xmm0;
|
if (code.HasHostFeature(HostFeature::AVX512_OrthoFloat)) {
|
||||||
if (code.HasHostFeature(HostFeature::AVX)) {
|
const Xbyak::Opmask nan_mask = k1;
|
||||||
|
FCODE(vfpclassp)(nan_mask, result, u8(FpClass::QNaN | FpClass::SNaN));
|
||||||
|
FCODE(vblendmp)(result | nan_mask, result, GetNaNVector<fsize>(code));
|
||||||
|
} else if (code.HasHostFeature(HostFeature::AVX)) {
|
||||||
|
const Xbyak::Xmm nan_mask = xmm0;
|
||||||
FCODE(vcmpunordp)(nan_mask, result, result);
|
FCODE(vcmpunordp)(nan_mask, result, result);
|
||||||
FCODE(blendvp)(result, GetNaNVector<fsize>(code));
|
FCODE(blendvp)(result, GetNaNVector<fsize>(code));
|
||||||
} else {
|
} else {
|
||||||
|
const Xbyak::Xmm nan_mask = xmm0;
|
||||||
code.movaps(nan_mask, result);
|
code.movaps(nan_mask, result);
|
||||||
FCODE(cmpordp)(nan_mask, nan_mask);
|
FCODE(cmpordp)(nan_mask, nan_mask);
|
||||||
code.andps(result, nan_mask);
|
code.andps(result, nan_mask);
|
||||||
|
@ -223,6 +229,26 @@ void ZeroIfNaN(BlockOfCode& code, Xbyak::Xmm result) {
|
||||||
template<size_t fsize>
|
template<size_t fsize>
|
||||||
void DenormalsAreZero(BlockOfCode& code, FP::FPCR fpcr, std::initializer_list<Xbyak::Xmm> to_daz, Xbyak::Xmm tmp) {
|
void DenormalsAreZero(BlockOfCode& code, FP::FPCR fpcr, std::initializer_list<Xbyak::Xmm> to_daz, Xbyak::Xmm tmp) {
|
||||||
if (fpcr.FZ()) {
|
if (fpcr.FZ()) {
|
||||||
|
if (code.HasHostFeature(HostFeature::AVX512_OrthoFloat)) {
|
||||||
|
constexpr u32 denormal_to_zero = FixupLUT(
|
||||||
|
FpFixup::Norm_Src,
|
||||||
|
FpFixup::Norm_Src,
|
||||||
|
FpFixup::Norm_Src,
|
||||||
|
FpFixup::Norm_Src,
|
||||||
|
FpFixup::Norm_Src,
|
||||||
|
FpFixup::Norm_Src,
|
||||||
|
FpFixup::Norm_Src,
|
||||||
|
FpFixup::Norm_Src);
|
||||||
|
constexpr u64 denormal_to_zero64 = mcl::bit::replicate_element<fsize, u64>(denormal_to_zero);
|
||||||
|
|
||||||
|
FCODE(vmovap)(tmp, code.MConst(xword, u64(denormal_to_zero64), u64(denormal_to_zero64)));
|
||||||
|
|
||||||
|
for (const Xbyak::Xmm& xmm : to_daz) {
|
||||||
|
FCODE(vfixupimmp)(xmm, xmm, tmp, u8(0));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (fpcr.RMode() != FP::RoundingMode::TowardsMinusInfinity) {
|
if (fpcr.RMode() != FP::RoundingMode::TowardsMinusInfinity) {
|
||||||
code.movaps(tmp, GetNegativeZeroVector<fsize>(code));
|
code.movaps(tmp, GetNegativeZeroVector<fsize>(code));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -28,19 +28,22 @@ IR::Block TranslateArm(LocationDescriptor descriptor, TranslateCallbacks* tcb, c
|
||||||
bool should_continue = true;
|
bool should_continue = true;
|
||||||
do {
|
do {
|
||||||
const u32 arm_pc = visitor.ir.current_location.PC();
|
const u32 arm_pc = visitor.ir.current_location.PC();
|
||||||
const u32 arm_instruction = tcb->MemoryReadCode(arm_pc);
|
|
||||||
visitor.current_instruction_size = 4;
|
visitor.current_instruction_size = 4;
|
||||||
|
|
||||||
tcb->PreCodeTranslationHook(false, arm_pc, visitor.ir);
|
if (const auto arm_instruction = tcb->MemoryReadCode(arm_pc)) {
|
||||||
|
tcb->PreCodeTranslationHook(false, arm_pc, visitor.ir);
|
||||||
|
|
||||||
if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor>(arm_instruction)) {
|
if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor>(*arm_instruction)) {
|
||||||
should_continue = vfp_decoder->get().call(visitor, arm_instruction);
|
should_continue = vfp_decoder->get().call(visitor, *arm_instruction);
|
||||||
} else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor>(arm_instruction)) {
|
} else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor>(*arm_instruction)) {
|
||||||
should_continue = asimd_decoder->get().call(visitor, arm_instruction);
|
should_continue = asimd_decoder->get().call(visitor, *arm_instruction);
|
||||||
} else if (const auto decoder = DecodeArm<TranslatorVisitor>(arm_instruction)) {
|
} else if (const auto decoder = DecodeArm<TranslatorVisitor>(*arm_instruction)) {
|
||||||
should_continue = decoder->get().call(visitor, arm_instruction);
|
should_continue = decoder->get().call(visitor, *arm_instruction);
|
||||||
|
} else {
|
||||||
|
should_continue = visitor.arm_UDF();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
should_continue = visitor.arm_UDF();
|
should_continue = visitor.RaiseException(Exception::NoExecuteFault);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (visitor.cond_state == ConditionalState::Break) {
|
if (visitor.cond_state == ConditionalState::Break) {
|
||||||
|
|
|
@ -4,18 +4,19 @@
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <mcl/stdint.hpp>
|
#include <cstdint>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace Dynarmic::A32 {
|
namespace Dynarmic::A32 {
|
||||||
|
|
||||||
using VAddr = u32;
|
using VAddr = std::uint32_t;
|
||||||
|
|
||||||
class IREmitter;
|
class IREmitter;
|
||||||
|
|
||||||
struct TranslateCallbacks {
|
struct TranslateCallbacks {
|
||||||
// All reads through this callback are 4-byte aligned.
|
// All reads through this callback are 4-byte aligned.
|
||||||
// Memory must be interpreted as little endian.
|
// Memory must be interpreted as little endian.
|
||||||
virtual std::uint32_t MemoryReadCode(VAddr vaddr) = 0;
|
virtual std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) = 0;
|
||||||
|
|
||||||
// Thus function is called before the instruction at pc is interpreted.
|
// Thus function is called before the instruction at pc is interpreted.
|
||||||
// IR code can be emitted by the callee prior to translation of the instruction.
|
// IR code can be emitted by the callee prior to translation of the instruction.
|
||||||
|
|
|
@ -44,28 +44,40 @@ bool IsUnconditionalInstruction(bool is_thumb_16, u32 instruction) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<u32, ThumbInstSize> ReadThumbInstruction(u32 arm_pc, TranslateCallbacks* tcb) {
|
std::optional<std::tuple<u32, ThumbInstSize>> ReadThumbInstruction(u32 arm_pc, TranslateCallbacks* tcb) {
|
||||||
u32 first_part = tcb->MemoryReadCode(arm_pc & 0xFFFFFFFC);
|
u32 instruction;
|
||||||
if ((arm_pc & 0x2) != 0) {
|
|
||||||
first_part >>= 16;
|
|
||||||
}
|
|
||||||
first_part &= 0xFFFF;
|
|
||||||
|
|
||||||
if (IsThumb16(static_cast<u16>(first_part))) {
|
const std::optional<u32> first_part = tcb->MemoryReadCode(arm_pc & 0xFFFFFFFC);
|
||||||
|
if (!first_part)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
if ((arm_pc & 0x2) != 0) {
|
||||||
|
instruction = *first_part >> 16;
|
||||||
|
} else {
|
||||||
|
instruction = *first_part & 0xFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsThumb16(static_cast<u16>(instruction))) {
|
||||||
// 16-bit thumb instruction
|
// 16-bit thumb instruction
|
||||||
return std::make_tuple(first_part, ThumbInstSize::Thumb16);
|
return std::make_tuple(instruction, ThumbInstSize::Thumb16);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 32-bit thumb instruction
|
// 32-bit thumb instruction
|
||||||
// These always start with 0b11101, 0b11110 or 0b11111.
|
// These always start with 0b11101, 0b11110 or 0b11111.
|
||||||
|
|
||||||
u32 second_part = tcb->MemoryReadCode((arm_pc + 2) & 0xFFFFFFFC);
|
instruction <<= 16;
|
||||||
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);
|
const std::optional<u32> second_part = tcb->MemoryReadCode((arm_pc + 2) & 0xFFFFFFFC);
|
||||||
|
if (!second_part)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
if (((arm_pc + 2) & 0x2) != 0) {
|
||||||
|
instruction |= *second_part >> 16;
|
||||||
|
} else {
|
||||||
|
instruction |= *second_part & 0xFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_tuple(instruction, ThumbInstSize::Thumb32);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert from thumb ASIMD format to ARM ASIMD format.
|
// Convert from thumb ASIMD format to ARM ASIMD format.
|
||||||
|
@ -97,43 +109,48 @@ IR::Block TranslateThumb(LocationDescriptor descriptor, TranslateCallbacks* tcb,
|
||||||
bool should_continue = true;
|
bool should_continue = true;
|
||||||
do {
|
do {
|
||||||
const u32 arm_pc = visitor.ir.current_location.PC();
|
const u32 arm_pc = visitor.ir.current_location.PC();
|
||||||
const auto [thumb_instruction, inst_size] = ReadThumbInstruction(arm_pc, tcb);
|
if (const auto maybe_instruction = ReadThumbInstruction(arm_pc, tcb)) {
|
||||||
const bool is_thumb_16 = inst_size == ThumbInstSize::Thumb16;
|
const auto [thumb_instruction, inst_size] = *maybe_instruction;
|
||||||
visitor.current_instruction_size = is_thumb_16 ? 2 : 4;
|
const bool is_thumb_16 = inst_size == ThumbInstSize::Thumb16;
|
||||||
|
visitor.current_instruction_size = is_thumb_16 ? 2 : 4;
|
||||||
|
|
||||||
tcb->PreCodeTranslationHook(false, arm_pc, visitor.ir);
|
tcb->PreCodeTranslationHook(false, arm_pc, visitor.ir);
|
||||||
|
|
||||||
if (IsUnconditionalInstruction(is_thumb_16, thumb_instruction) || visitor.ThumbConditionPassed()) {
|
if (IsUnconditionalInstruction(is_thumb_16, thumb_instruction) || visitor.ThumbConditionPassed()) {
|
||||||
if (is_thumb_16) {
|
if (is_thumb_16) {
|
||||||
if (const auto decoder = DecodeThumb16<TranslatorVisitor>(static_cast<u16>(thumb_instruction))) {
|
if (const auto decoder = DecodeThumb16<TranslatorVisitor>(static_cast<u16>(thumb_instruction))) {
|
||||||
should_continue = decoder->get().call(visitor, static_cast<u16>(thumb_instruction));
|
should_continue = decoder->get().call(visitor, static_cast<u16>(thumb_instruction));
|
||||||
|
} else {
|
||||||
|
should_continue = visitor.thumb16_UDF();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
should_continue = visitor.thumb16_UDF();
|
if (MaybeVFPOrASIMDInstruction(thumb_instruction)) {
|
||||||
}
|
if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor>(thumb_instruction)) {
|
||||||
} else {
|
should_continue = vfp_decoder->get().call(visitor, thumb_instruction);
|
||||||
if (MaybeVFPOrASIMDInstruction(thumb_instruction)) {
|
} else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor>(ConvertASIMDInstruction(thumb_instruction))) {
|
||||||
if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor>(thumb_instruction)) {
|
should_continue = asimd_decoder->get().call(visitor, ConvertASIMDInstruction(thumb_instruction));
|
||||||
should_continue = vfp_decoder->get().call(visitor, thumb_instruction);
|
} else if (const auto decoder = DecodeThumb32<TranslatorVisitor>(thumb_instruction)) {
|
||||||
} else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor>(ConvertASIMDInstruction(thumb_instruction))) {
|
should_continue = decoder->get().call(visitor, thumb_instruction);
|
||||||
should_continue = asimd_decoder->get().call(visitor, ConvertASIMDInstruction(thumb_instruction));
|
} else {
|
||||||
|
should_continue = visitor.thumb32_UDF();
|
||||||
|
}
|
||||||
} else if (const auto decoder = DecodeThumb32<TranslatorVisitor>(thumb_instruction)) {
|
} else if (const auto decoder = DecodeThumb32<TranslatorVisitor>(thumb_instruction)) {
|
||||||
should_continue = decoder->get().call(visitor, thumb_instruction);
|
should_continue = decoder->get().call(visitor, thumb_instruction);
|
||||||
} else {
|
} else {
|
||||||
should_continue = visitor.thumb32_UDF();
|
should_continue = visitor.thumb32_UDF();
|
||||||
}
|
}
|
||||||
} else if (const auto decoder = DecodeThumb32<TranslatorVisitor>(thumb_instruction)) {
|
|
||||||
should_continue = decoder->get().call(visitor, thumb_instruction);
|
|
||||||
} else {
|
|
||||||
should_continue = visitor.thumb32_UDF();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
visitor.current_instruction_size = 2;
|
||||||
|
should_continue = visitor.RaiseException(Exception::NoExecuteFault);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (visitor.cond_state == ConditionalState::Break) {
|
if (visitor.cond_state == ConditionalState::Break) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitor.ir.current_location = visitor.ir.current_location.AdvancePC(is_thumb_16 ? 2 : 4).AdvanceIT();
|
visitor.ir.current_location = visitor.ir.current_location.AdvancePC(static_cast<int>(visitor.current_instruction_size)).AdvanceIT();
|
||||||
block.CycleCount()++;
|
block.CycleCount()++;
|
||||||
} while (should_continue && CondCanContinue(visitor.cond_state, visitor.ir) && !single_step);
|
} while (should_continue && CondCanContinue(visitor.cond_state, visitor.ir) && !single_step);
|
||||||
|
|
||||||
|
|
|
@ -22,12 +22,15 @@ IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType memory
|
||||||
bool should_continue = true;
|
bool should_continue = true;
|
||||||
do {
|
do {
|
||||||
const u64 pc = visitor.ir.current_location->PC();
|
const u64 pc = visitor.ir.current_location->PC();
|
||||||
const u32 instruction = memory_read_code(pc);
|
|
||||||
|
|
||||||
if (auto decoder = Decode<TranslatorVisitor>(instruction)) {
|
if (const auto instruction = memory_read_code(pc)) {
|
||||||
should_continue = decoder->get().call(visitor, instruction);
|
if (auto decoder = Decode<TranslatorVisitor>(*instruction)) {
|
||||||
|
should_continue = decoder->get().call(visitor, *instruction);
|
||||||
|
} else {
|
||||||
|
should_continue = visitor.InterpretThisInstruction();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
should_continue = visitor.InterpretThisInstruction();
|
should_continue = visitor.RaiseException(Exception::NoExecuteFault);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitor.ir.current_location = visitor.ir.current_location->AdvancePC(4);
|
visitor.ir.current_location = visitor.ir.current_location->AdvancePC(4);
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include <mcl/stdint.hpp>
|
#include <mcl/stdint.hpp>
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ namespace A64 {
|
||||||
|
|
||||||
class LocationDescriptor;
|
class LocationDescriptor;
|
||||||
|
|
||||||
using MemoryReadCodeFuncType = std::function<u32(u64 vaddr)>;
|
using MemoryReadCodeFuncType = std::function<std::optional<u32>(u64 vaddr)>;
|
||||||
|
|
||||||
struct TranslationOptions {
|
struct TranslationOptions {
|
||||||
/// This changes what IR we emit when we translate an unpredictable instruction.
|
/// This changes what IR we emit when we translate an unpredictable instruction.
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include "dynarmic/frontend/A32/translate/translate_callbacks.h"
|
#include "dynarmic/frontend/A32/translate/translate_callbacks.h"
|
||||||
#include "dynarmic/interface/A32/arch_version.h"
|
#include "dynarmic/interface/A32/arch_version.h"
|
||||||
|
@ -51,6 +52,9 @@ enum class Exception {
|
||||||
PreloadDataWithIntentToWrite,
|
PreloadDataWithIntentToWrite,
|
||||||
/// A PLI instruction was executed. (Hint instruction.)
|
/// A PLI instruction was executed. (Hint instruction.)
|
||||||
PreloadInstruction,
|
PreloadInstruction,
|
||||||
|
/// Attempted to execute a code block at an address for which MemoryReadCode returned std::nullopt.
|
||||||
|
/// (Intended to be used to emulate memory protection faults.)
|
||||||
|
NoExecuteFault,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// These function pointers may be inserted into compiled code.
|
/// These function pointers may be inserted into compiled code.
|
||||||
|
@ -59,7 +63,7 @@ struct UserCallbacks : public TranslateCallbacks {
|
||||||
|
|
||||||
// All reads through this callback are 4-byte aligned.
|
// All reads through this callback are 4-byte aligned.
|
||||||
// Memory must be interpreted as little endian.
|
// Memory must be interpreted as little endian.
|
||||||
std::uint32_t MemoryReadCode(VAddr vaddr) override { return MemoryRead32(vaddr); }
|
std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) override { return MemoryRead32(vaddr); }
|
||||||
|
|
||||||
// Thus function is called before the instruction at pc is interpreted.
|
// Thus function is called before the instruction at pc is interpreted.
|
||||||
// IR code can be emitted by the callee prior to translation of the instruction.
|
// IR code can be emitted by the callee prior to translation of the instruction.
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include "dynarmic/interface/optimization_flags.h"
|
#include "dynarmic/interface/optimization_flags.h"
|
||||||
|
|
||||||
|
@ -45,6 +46,9 @@ enum class Exception {
|
||||||
Yield,
|
Yield,
|
||||||
/// A BRK instruction was executed. (Hint instruction.)
|
/// A BRK instruction was executed. (Hint instruction.)
|
||||||
Breakpoint,
|
Breakpoint,
|
||||||
|
/// Attempted to execute a code block at an address for which MemoryReadCode returned std::nullopt.
|
||||||
|
/// (Intended to be used to emulate memory protection faults.)
|
||||||
|
NoExecuteFault,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class DataCacheOperation {
|
enum class DataCacheOperation {
|
||||||
|
@ -82,7 +86,7 @@ struct UserCallbacks {
|
||||||
|
|
||||||
// All reads through this callback are 4-byte aligned.
|
// All reads through this callback are 4-byte aligned.
|
||||||
// Memory must be interpreted as little endian.
|
// Memory must be interpreted as little endian.
|
||||||
virtual std::uint32_t MemoryReadCode(VAddr vaddr) { return MemoryRead32(vaddr); }
|
virtual std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) { return MemoryRead32(vaddr); }
|
||||||
|
|
||||||
// Reads through these callbacks may not be aligned.
|
// Reads through these callbacks may not be aligned.
|
||||||
virtual std::uint8_t MemoryRead8(VAddr vaddr) = 0;
|
virtual std::uint8_t MemoryRead8(VAddr vaddr) = 0;
|
||||||
|
|
|
@ -16,10 +16,12 @@ namespace Dynarmic::Optimization {
|
||||||
|
|
||||||
void A64MergeInterpretBlocksPass(IR::Block& block, A64::UserCallbacks* cb) {
|
void A64MergeInterpretBlocksPass(IR::Block& block, A64::UserCallbacks* cb) {
|
||||||
const auto is_interpret_instruction = [cb](A64::LocationDescriptor location) {
|
const auto is_interpret_instruction = [cb](A64::LocationDescriptor location) {
|
||||||
const u32 instruction = cb->MemoryReadCode(location.PC());
|
const auto instruction = cb->MemoryReadCode(location.PC());
|
||||||
|
if (!instruction)
|
||||||
|
return false;
|
||||||
|
|
||||||
IR::Block new_block{location};
|
IR::Block new_block{location};
|
||||||
A64::TranslateSingleInstruction(new_block, location, instruction);
|
A64::TranslateSingleInstruction(new_block, location, *instruction);
|
||||||
|
|
||||||
if (!new_block.Instructions().empty())
|
if (!new_block.Instructions().empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
8
externals/dynarmic/tests/A32/testenv.h
vendored
8
externals/dynarmic/tests/A32/testenv.h
vendored
|
@ -48,7 +48,7 @@ public:
|
||||||
return vaddr < sizeof(InstructionType) * code_mem.size();
|
return vaddr < sizeof(InstructionType) * code_mem.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint32_t MemoryReadCode(u32 vaddr) override {
|
std::optional<std::uint32_t> MemoryReadCode(u32 vaddr) override {
|
||||||
if (IsInCodeMem(vaddr)) {
|
if (IsInCodeMem(vaddr)) {
|
||||||
u32 value;
|
u32 value;
|
||||||
std::memcpy(&value, &code_mem[vaddr / sizeof(InstructionType)], sizeof(u32));
|
std::memcpy(&value, &code_mem[vaddr / sizeof(InstructionType)], sizeof(u32));
|
||||||
|
@ -95,11 +95,11 @@ public:
|
||||||
MemoryWrite32(vaddr + 4, static_cast<u32>(value >> 32));
|
MemoryWrite32(vaddr + 4, static_cast<u32>(value >> 32));
|
||||||
}
|
}
|
||||||
|
|
||||||
void InterpreterFallback(u32 pc, size_t num_instructions) override { ASSERT_MSG(false, "InterpreterFallback({:08x}, {}) code = {:08x}", pc, num_instructions, MemoryReadCode(pc)); }
|
void InterpreterFallback(u32 pc, size_t num_instructions) override { ASSERT_MSG(false, "InterpreterFallback({:08x}, {}) code = {:08x}", pc, num_instructions, *MemoryReadCode(pc)); }
|
||||||
|
|
||||||
void CallSVC(std::uint32_t swi) override { ASSERT_MSG(false, "CallSVC({})", swi); }
|
void CallSVC(std::uint32_t swi) override { ASSERT_MSG(false, "CallSVC({})", swi); }
|
||||||
|
|
||||||
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception /*exception*/) override { ASSERT_MSG(false, "ExceptionRaised({:08x}) code = {:08x}", pc, MemoryReadCode(pc)); }
|
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception /*exception*/) override { ASSERT_MSG(false, "ExceptionRaised({:08x}) code = {:08x}", pc, *MemoryReadCode(pc)); }
|
||||||
|
|
||||||
void AddTicks(std::uint64_t ticks) override {
|
void AddTicks(std::uint64_t ticks) override {
|
||||||
if (ticks > ticks_left) {
|
if (ticks > ticks_left) {
|
||||||
|
@ -135,7 +135,7 @@ public:
|
||||||
memcpy(backing_memory + vaddr, &value, sizeof(T));
|
memcpy(backing_memory + vaddr, &value, sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint32_t MemoryReadCode(std::uint32_t vaddr) override {
|
std::optional<std::uint32_t> MemoryReadCode(std::uint32_t vaddr) override {
|
||||||
return read<std::uint32_t>(vaddr);
|
return read<std::uint32_t>(vaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
4
externals/dynarmic/tests/A64/testenv.h
vendored
4
externals/dynarmic/tests/A64/testenv.h
vendored
|
@ -30,7 +30,7 @@ public:
|
||||||
return vaddr >= code_mem_start_address && vaddr < code_mem_start_address + code_mem.size() * 4;
|
return vaddr >= code_mem_start_address && vaddr < code_mem_start_address + code_mem.size() * 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint32_t MemoryReadCode(u64 vaddr) override {
|
std::optional<std::uint32_t> MemoryReadCode(u64 vaddr) override {
|
||||||
if (!IsInCodeMem(vaddr)) {
|
if (!IsInCodeMem(vaddr)) {
|
||||||
return 0x14000000; // B .
|
return 0x14000000; // B .
|
||||||
}
|
}
|
||||||
|
@ -145,7 +145,7 @@ public:
|
||||||
memcpy(backing_memory + vaddr, &value, sizeof(T));
|
memcpy(backing_memory + vaddr, &value, sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint32_t MemoryReadCode(u64 vaddr) override {
|
std::optional<std::uint32_t> MemoryReadCode(u64 vaddr) override {
|
||||||
return read<std::uint32_t>(vaddr);
|
return read<std::uint32_t>(vaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
externals/dynarmic/tests/print_info.cpp
vendored
2
externals/dynarmic/tests/print_info.cpp
vendored
|
@ -157,7 +157,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void InterpreterFallback(u32 pc, size_t num_instructions) override {
|
void InterpreterFallback(u32 pc, size_t num_instructions) override {
|
||||||
fmt::print("> InterpreterFallback({:08x}, {}) code = {:08x}\n", pc, num_instructions, MemoryReadCode(pc));
|
fmt::print("> InterpreterFallback({:08x}, {}) code = {:08x}\n", pc, num_instructions, *MemoryReadCode(pc));
|
||||||
}
|
}
|
||||||
void CallSVC(std::uint32_t swi) override {
|
void CallSVC(std::uint32_t swi) override {
|
||||||
fmt::print("> CallSVC({})\n", swi);
|
fmt::print("> CallSVC({})\n", swi);
|
||||||
|
|
|
@ -52,7 +52,7 @@ void A32Unicorn<TestEnvironment>::Run() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (auto cerr_ = uc_emu_start(uc, pc, END_ADDRESS, 0, 1)) {
|
if (auto cerr_ = uc_emu_start(uc, pc, END_ADDRESS, 0, 1)) {
|
||||||
fmt::print("uc_emu_start failed @ {:08x} (code = {:08x}) with error {} ({})", pc, testenv.MemoryReadCode(pc), cerr_, uc_strerror(cerr_));
|
fmt::print("uc_emu_start failed @ {:08x} (code = {:08x}) with error {} ({})", pc, *testenv.MemoryReadCode(pc), cerr_, uc_strerror(cerr_));
|
||||||
throw "A32Unicorn::Run() failure";
|
throw "A32Unicorn::Run() failure";
|
||||||
}
|
}
|
||||||
testenv.ticks_left--;
|
testenv.ticks_left--;
|
||||||
|
|
|
@ -95,7 +95,7 @@ void ARM_Interface::Run() {
|
||||||
using Kernel::SuspendType;
|
using Kernel::SuspendType;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
Kernel::KThread* current_thread{system.Kernel().CurrentScheduler()->GetCurrentThread()};
|
Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())};
|
||||||
Dynarmic::HaltReason hr{};
|
Dynarmic::HaltReason hr{};
|
||||||
|
|
||||||
// Notify the debugger and go to sleep if a step was performed
|
// Notify the debugger and go to sleep if a step was performed
|
||||||
|
@ -119,16 +119,23 @@ void ARM_Interface::Run() {
|
||||||
}
|
}
|
||||||
system.ExitDynarmicProfile();
|
system.ExitDynarmicProfile();
|
||||||
|
|
||||||
// Notify the debugger and go to sleep if a breakpoint was hit.
|
// Notify the debugger and go to sleep if a breakpoint was hit,
|
||||||
if (Has(hr, breakpoint)) {
|
// or if the thread is unable to continue for any reason.
|
||||||
|
if (Has(hr, breakpoint) || Has(hr, no_execute)) {
|
||||||
RewindBreakpointInstruction();
|
RewindBreakpointInstruction();
|
||||||
system.GetDebugger().NotifyThreadStopped(current_thread);
|
if (system.DebuggerEnabled()) {
|
||||||
current_thread->RequestSuspend(SuspendType::Debug);
|
system.GetDebugger().NotifyThreadStopped(current_thread);
|
||||||
|
}
|
||||||
|
current_thread->RequestSuspend(Kernel::SuspendType::Debug);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Notify the debugger and go to sleep if a watchpoint was hit.
|
||||||
if (Has(hr, watchpoint)) {
|
if (Has(hr, watchpoint)) {
|
||||||
RewindBreakpointInstruction();
|
RewindBreakpointInstruction();
|
||||||
system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint());
|
if (system.DebuggerEnabled()) {
|
||||||
|
system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint());
|
||||||
|
}
|
||||||
current_thread->RequestSuspend(SuspendType::Debug);
|
current_thread->RequestSuspend(SuspendType::Debug);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -204,6 +204,7 @@ public:
|
||||||
static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
|
static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
|
||||||
static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4;
|
static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4;
|
||||||
static constexpr Dynarmic::HaltReason watchpoint = Dynarmic::HaltReason::UserDefined5;
|
static constexpr Dynarmic::HaltReason watchpoint = Dynarmic::HaltReason::UserDefined5;
|
||||||
|
static constexpr Dynarmic::HaltReason no_execute = Dynarmic::HaltReason::UserDefined6;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// System context that this ARM interface is running under.
|
/// System context that this ARM interface is running under.
|
||||||
|
|
|
@ -48,6 +48,12 @@ public:
|
||||||
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
|
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
|
||||||
return memory.Read64(vaddr);
|
return memory.Read64(vaddr);
|
||||||
}
|
}
|
||||||
|
std::optional<u32> MemoryReadCode(u32 vaddr) override {
|
||||||
|
if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return MemoryRead32(vaddr);
|
||||||
|
}
|
||||||
|
|
||||||
void MemoryWrite8(u32 vaddr, u8 value) override {
|
void MemoryWrite8(u32 vaddr, u8 value) override {
|
||||||
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
|
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
|
||||||
|
@ -89,21 +95,28 @@ public:
|
||||||
|
|
||||||
void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
|
void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
|
||||||
parent.LogBacktrace();
|
parent.LogBacktrace();
|
||||||
UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc,
|
LOG_ERROR(Core_ARM,
|
||||||
MemoryReadCode(pc));
|
"Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
|
||||||
|
num_instructions, MemoryRead32(pc));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
|
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
|
||||||
if (debugger_enabled) {
|
switch (exception) {
|
||||||
parent.SaveContext(parent.breakpoint_context);
|
case Dynarmic::A32::Exception::NoExecuteFault:
|
||||||
parent.jit.load()->HaltExecution(ARM_Interface::breakpoint);
|
LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#08x}", pc);
|
||||||
|
ReturnException(pc, ARM_Interface::no_execute);
|
||||||
return;
|
return;
|
||||||
}
|
default:
|
||||||
|
if (debugger_enabled) {
|
||||||
|
ReturnException(pc, ARM_Interface::breakpoint);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
parent.LogBacktrace();
|
parent.LogBacktrace();
|
||||||
LOG_CRITICAL(Core_ARM,
|
LOG_CRITICAL(Core_ARM,
|
||||||
"ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
|
"ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
|
||||||
exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
|
exception, pc, MemoryRead32(pc), parent.IsInThumbMode());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CallSVC(u32 swi) override {
|
void CallSVC(u32 swi) override {
|
||||||
|
@ -148,15 +161,20 @@ public:
|
||||||
|
|
||||||
const auto match{parent.MatchingWatchpoint(addr, size, type)};
|
const auto match{parent.MatchingWatchpoint(addr, size, type)};
|
||||||
if (match) {
|
if (match) {
|
||||||
parent.SaveContext(parent.breakpoint_context);
|
|
||||||
parent.jit.load()->HaltExecution(ARM_Interface::watchpoint);
|
|
||||||
parent.halted_watchpoint = match;
|
parent.halted_watchpoint = match;
|
||||||
|
ReturnException(parent.jit.load()->Regs()[15], ARM_Interface::watchpoint);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ReturnException(u32 pc, Dynarmic::HaltReason hr) {
|
||||||
|
parent.SaveContext(parent.breakpoint_context);
|
||||||
|
parent.breakpoint_context.cpu_registers[15] = pc;
|
||||||
|
parent.jit.load()->HaltExecution(hr);
|
||||||
|
}
|
||||||
|
|
||||||
ARM_Dynarmic_32& parent;
|
ARM_Dynarmic_32& parent;
|
||||||
Core::Memory::Memory& memory;
|
Core::Memory::Memory& memory;
|
||||||
std::size_t num_interpreted_instructions{};
|
std::size_t num_interpreted_instructions{};
|
||||||
|
|
|
@ -52,6 +52,12 @@ public:
|
||||||
CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
|
CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
|
||||||
return {memory.Read64(vaddr), memory.Read64(vaddr + 8)};
|
return {memory.Read64(vaddr), memory.Read64(vaddr + 8)};
|
||||||
}
|
}
|
||||||
|
std::optional<u32> MemoryReadCode(u64 vaddr) override {
|
||||||
|
if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return MemoryRead32(vaddr);
|
||||||
|
}
|
||||||
|
|
||||||
void MemoryWrite8(u64 vaddr, u8 value) override {
|
void MemoryWrite8(u64 vaddr, u8 value) override {
|
||||||
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
|
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
|
||||||
|
@ -105,7 +111,7 @@ public:
|
||||||
parent.LogBacktrace();
|
parent.LogBacktrace();
|
||||||
LOG_ERROR(Core_ARM,
|
LOG_ERROR(Core_ARM,
|
||||||
"Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
|
"Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
|
||||||
num_instructions, MemoryReadCode(pc));
|
num_instructions, MemoryRead32(pc));
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op,
|
void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op,
|
||||||
|
@ -138,16 +144,19 @@ public:
|
||||||
case Dynarmic::A64::Exception::SendEventLocal:
|
case Dynarmic::A64::Exception::SendEventLocal:
|
||||||
case Dynarmic::A64::Exception::Yield:
|
case Dynarmic::A64::Exception::Yield:
|
||||||
return;
|
return;
|
||||||
|
case Dynarmic::A64::Exception::NoExecuteFault:
|
||||||
|
LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#016x}", pc);
|
||||||
|
ReturnException(pc, ARM_Interface::no_execute);
|
||||||
|
return;
|
||||||
default:
|
default:
|
||||||
if (debugger_enabled) {
|
if (debugger_enabled) {
|
||||||
parent.SaveContext(parent.breakpoint_context);
|
ReturnException(pc, ARM_Interface::breakpoint);
|
||||||
parent.jit.load()->HaltExecution(ARM_Interface::breakpoint);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
parent.LogBacktrace();
|
parent.LogBacktrace();
|
||||||
ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
|
LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
|
||||||
static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
|
static_cast<std::size_t>(exception), pc, MemoryRead32(pc));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,15 +204,20 @@ public:
|
||||||
|
|
||||||
const auto match{parent.MatchingWatchpoint(addr, size, type)};
|
const auto match{parent.MatchingWatchpoint(addr, size, type)};
|
||||||
if (match) {
|
if (match) {
|
||||||
parent.SaveContext(parent.breakpoint_context);
|
|
||||||
parent.jit.load()->HaltExecution(ARM_Interface::watchpoint);
|
|
||||||
parent.halted_watchpoint = match;
|
parent.halted_watchpoint = match;
|
||||||
|
ReturnException(parent.jit.load()->GetPC(), ARM_Interface::watchpoint);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ReturnException(u64 pc, Dynarmic::HaltReason hr) {
|
||||||
|
parent.SaveContext(parent.breakpoint_context);
|
||||||
|
parent.breakpoint_context.pc = pc;
|
||||||
|
parent.jit.load()->HaltExecution(hr);
|
||||||
|
}
|
||||||
|
|
||||||
ARM_Dynarmic_64& parent;
|
ARM_Dynarmic_64& parent;
|
||||||
Core::Memory::Memory& memory;
|
Core::Memory::Memory& memory;
|
||||||
u64 tpidrro_el0 = 0;
|
u64 tpidrro_el0 = 0;
|
||||||
|
|
|
@ -95,7 +95,7 @@ void* CpuManager::GetStartFuncParameter() {
|
||||||
void CpuManager::MultiCoreRunGuestThread() {
|
void CpuManager::MultiCoreRunGuestThread() {
|
||||||
auto& kernel = system.Kernel();
|
auto& kernel = system.Kernel();
|
||||||
kernel.CurrentScheduler()->OnThreadStart();
|
kernel.CurrentScheduler()->OnThreadStart();
|
||||||
auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
|
auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread();
|
||||||
auto& host_context = thread->GetHostContext();
|
auto& host_context = thread->GetHostContext();
|
||||||
host_context->SetRewindPoint(GuestRewindFunction, this);
|
host_context->SetRewindPoint(GuestRewindFunction, this);
|
||||||
MultiCoreRunGuestLoop();
|
MultiCoreRunGuestLoop();
|
||||||
|
@ -132,7 +132,7 @@ void CpuManager::MultiCoreRunIdleThread() {
|
||||||
void CpuManager::SingleCoreRunGuestThread() {
|
void CpuManager::SingleCoreRunGuestThread() {
|
||||||
auto& kernel = system.Kernel();
|
auto& kernel = system.Kernel();
|
||||||
kernel.CurrentScheduler()->OnThreadStart();
|
kernel.CurrentScheduler()->OnThreadStart();
|
||||||
auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
|
auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread();
|
||||||
auto& host_context = thread->GetHostContext();
|
auto& host_context = thread->GetHostContext();
|
||||||
host_context->SetRewindPoint(GuestRewindFunction, this);
|
host_context->SetRewindPoint(GuestRewindFunction, this);
|
||||||
SingleCoreRunGuestLoop();
|
SingleCoreRunGuestLoop();
|
||||||
|
@ -172,7 +172,7 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
|
||||||
{
|
{
|
||||||
auto& kernel = system.Kernel();
|
auto& kernel = system.Kernel();
|
||||||
auto& scheduler = kernel.Scheduler(current_core);
|
auto& scheduler = kernel.Scheduler(current_core);
|
||||||
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
|
Kernel::KThread* current_thread = scheduler.GetSchedulerCurrentThread();
|
||||||
if (idle_count >= 4 || from_running_enviroment) {
|
if (idle_count >= 4 || from_running_enviroment) {
|
||||||
if (!from_running_enviroment) {
|
if (!from_running_enviroment) {
|
||||||
system.CoreTiming().Idle();
|
system.CoreTiming().Idle();
|
||||||
|
@ -184,7 +184,7 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
|
||||||
}
|
}
|
||||||
current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES);
|
current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES);
|
||||||
system.CoreTiming().ResetTicks();
|
system.CoreTiming().ResetTicks();
|
||||||
scheduler.Unload(scheduler.GetCurrentThread());
|
scheduler.Unload(scheduler.GetSchedulerCurrentThread());
|
||||||
|
|
||||||
auto& next_scheduler = kernel.Scheduler(current_core);
|
auto& next_scheduler = kernel.Scheduler(current_core);
|
||||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), *next_scheduler.ControlContext());
|
Common::Fiber::YieldTo(current_thread->GetHostContext(), *next_scheduler.ControlContext());
|
||||||
|
@ -193,10 +193,8 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
|
||||||
// May have changed scheduler
|
// May have changed scheduler
|
||||||
{
|
{
|
||||||
auto& scheduler = system.Kernel().Scheduler(current_core);
|
auto& scheduler = system.Kernel().Scheduler(current_core);
|
||||||
scheduler.Reload(scheduler.GetCurrentThread());
|
scheduler.Reload(scheduler.GetSchedulerCurrentThread());
|
||||||
if (!scheduler.IsIdle()) {
|
idle_count = 0;
|
||||||
idle_count = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,7 +235,8 @@ void CpuManager::RunThread(std::size_t core) {
|
||||||
system.GPU().ObtainContext();
|
system.GPU().ObtainContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
|
auto* current_thread = system.Kernel().CurrentScheduler()->GetIdleThread();
|
||||||
|
Kernel::SetCurrentThread(system.Kernel(), current_thread);
|
||||||
Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
|
Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -272,6 +272,7 @@ enum class VibrationDeviceType : u32 {
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
LinearResonantActuator = 1,
|
LinearResonantActuator = 1,
|
||||||
GcErm = 2,
|
GcErm = 2,
|
||||||
|
N64 = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
// This is nn::hid::VibrationGcErmCommand
|
// This is nn::hid::VibrationGcErmCommand
|
||||||
|
|
|
@ -234,7 +234,7 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
|
||||||
|
|
||||||
ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
|
ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
|
||||||
// Prepare to wait.
|
// Prepare to wait.
|
||||||
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
|
KThread* cur_thread = GetCurrentThreadPointer(kernel);
|
||||||
ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
|
ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -287,7 +287,7 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
|
||||||
|
|
||||||
ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
|
ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
|
||||||
// Prepare to wait.
|
// Prepare to wait.
|
||||||
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
|
KThread* cur_thread = GetCurrentThreadPointer(kernel);
|
||||||
ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
|
ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -106,7 +106,7 @@ KConditionVariable::KConditionVariable(Core::System& system_)
|
||||||
KConditionVariable::~KConditionVariable() = default;
|
KConditionVariable::~KConditionVariable() = default;
|
||||||
|
|
||||||
ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
|
ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
|
||||||
KThread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread();
|
KThread* owner_thread = GetCurrentThreadPointer(kernel);
|
||||||
|
|
||||||
// Signal the address.
|
// Signal the address.
|
||||||
{
|
{
|
||||||
|
@ -147,7 +147,7 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
|
ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
|
||||||
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
|
KThread* cur_thread = GetCurrentThreadPointer(kernel);
|
||||||
ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel);
|
ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel);
|
||||||
|
|
||||||
// Wait for the address.
|
// Wait for the address.
|
||||||
|
|
|
@ -15,8 +15,7 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& scheduler = kernel.Scheduler(core_id);
|
auto& current_thread = GetCurrentThread(kernel);
|
||||||
auto& current_thread = *scheduler.GetCurrentThread();
|
|
||||||
|
|
||||||
// If the user disable count is set, we may need to pin the current thread.
|
// If the user disable count is set, we may need to pin the current thread.
|
||||||
if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) {
|
if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) {
|
||||||
|
@ -26,7 +25,7 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) {
|
||||||
process->PinCurrentThread(core_id);
|
process->PinCurrentThread(core_id);
|
||||||
|
|
||||||
// Set the interrupt flag for the thread.
|
// Set the interrupt flag for the thread.
|
||||||
scheduler.GetCurrentThread()->SetInterruptFlag();
|
GetCurrentThread(kernel).SetInterruptFlag();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -176,7 +176,8 @@ void KProcess::PinCurrentThread(s32 core_id) {
|
||||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||||
|
|
||||||
// Get the current thread.
|
// Get the current thread.
|
||||||
KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread();
|
KThread* cur_thread =
|
||||||
|
kernel.Scheduler(static_cast<std::size_t>(core_id)).GetSchedulerCurrentThread();
|
||||||
|
|
||||||
// If the thread isn't terminated, pin it.
|
// If the thread isn't terminated, pin it.
|
||||||
if (!cur_thread->IsTerminationRequested()) {
|
if (!cur_thread->IsTerminationRequested()) {
|
||||||
|
@ -193,7 +194,8 @@ void KProcess::UnpinCurrentThread(s32 core_id) {
|
||||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||||
|
|
||||||
// Get the current thread.
|
// Get the current thread.
|
||||||
KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread();
|
KThread* cur_thread =
|
||||||
|
kernel.Scheduler(static_cast<std::size_t>(core_id)).GetSchedulerCurrentThread();
|
||||||
|
|
||||||
// Unpin it.
|
// Unpin it.
|
||||||
cur_thread->Unpin();
|
cur_thread->Unpin();
|
||||||
|
@ -420,11 +422,11 @@ void KProcess::PrepareForTermination() {
|
||||||
ChangeStatus(ProcessStatus::Exiting);
|
ChangeStatus(ProcessStatus::Exiting);
|
||||||
|
|
||||||
const auto stop_threads = [this](const std::vector<KThread*>& in_thread_list) {
|
const auto stop_threads = [this](const std::vector<KThread*>& in_thread_list) {
|
||||||
for (auto& thread : in_thread_list) {
|
for (auto* thread : in_thread_list) {
|
||||||
if (thread->GetOwnerProcess() != this)
|
if (thread->GetOwnerProcess() != this)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (thread == kernel.CurrentScheduler()->GetCurrentThread())
|
if (thread == GetCurrentThreadPointer(kernel))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// TODO(Subv): When are the other running/ready threads terminated?
|
// TODO(Subv): When are the other running/ready threads terminated?
|
||||||
|
|
|
@ -317,7 +317,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
|
||||||
|
|
||||||
{
|
{
|
||||||
KThread* best_thread = priority_queue.GetScheduledFront(cpu_core_id);
|
KThread* best_thread = priority_queue.GetScheduledFront(cpu_core_id);
|
||||||
if (best_thread == GetCurrentThread()) {
|
if (best_thread == GetCurrentThreadPointer(kernel)) {
|
||||||
best_thread = priority_queue.GetScheduledNext(cpu_core_id, best_thread);
|
best_thread = priority_queue.GetScheduledNext(cpu_core_id, best_thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,7 +424,7 @@ void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) {
|
||||||
ASSERT(kernel.CurrentProcess() != nullptr);
|
ASSERT(kernel.CurrentProcess() != nullptr);
|
||||||
|
|
||||||
// Get the current thread and process.
|
// Get the current thread and process.
|
||||||
KThread& cur_thread = Kernel::GetCurrentThread(kernel);
|
KThread& cur_thread = GetCurrentThread(kernel);
|
||||||
KProcess& cur_process = *kernel.CurrentProcess();
|
KProcess& cur_process = *kernel.CurrentProcess();
|
||||||
|
|
||||||
// If the thread's yield count matches, there's nothing for us to do.
|
// If the thread's yield count matches, there's nothing for us to do.
|
||||||
|
@ -463,7 +463,7 @@ void KScheduler::YieldWithCoreMigration(KernelCore& kernel) {
|
||||||
ASSERT(kernel.CurrentProcess() != nullptr);
|
ASSERT(kernel.CurrentProcess() != nullptr);
|
||||||
|
|
||||||
// Get the current thread and process.
|
// Get the current thread and process.
|
||||||
KThread& cur_thread = Kernel::GetCurrentThread(kernel);
|
KThread& cur_thread = GetCurrentThread(kernel);
|
||||||
KProcess& cur_process = *kernel.CurrentProcess();
|
KProcess& cur_process = *kernel.CurrentProcess();
|
||||||
|
|
||||||
// If the thread's yield count matches, there's nothing for us to do.
|
// If the thread's yield count matches, there's nothing for us to do.
|
||||||
|
@ -551,7 +551,7 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) {
|
||||||
ASSERT(kernel.CurrentProcess() != nullptr);
|
ASSERT(kernel.CurrentProcess() != nullptr);
|
||||||
|
|
||||||
// Get the current thread and process.
|
// Get the current thread and process.
|
||||||
KThread& cur_thread = Kernel::GetCurrentThread(kernel);
|
KThread& cur_thread = GetCurrentThread(kernel);
|
||||||
KProcess& cur_process = *kernel.CurrentProcess();
|
KProcess& cur_process = *kernel.CurrentProcess();
|
||||||
|
|
||||||
// If the thread's yield count matches, there's nothing for us to do.
|
// If the thread's yield count matches, there's nothing for us to do.
|
||||||
|
@ -642,7 +642,7 @@ KScheduler::~KScheduler() {
|
||||||
ASSERT(!idle_thread);
|
ASSERT(!idle_thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
KThread* KScheduler::GetCurrentThread() const {
|
KThread* KScheduler::GetSchedulerCurrentThread() const {
|
||||||
if (auto result = current_thread.load(); result) {
|
if (auto result = current_thread.load(); result) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -654,7 +654,7 @@ u64 KScheduler::GetLastContextSwitchTicks() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void KScheduler::RescheduleCurrentCore() {
|
void KScheduler::RescheduleCurrentCore() {
|
||||||
ASSERT(GetCurrentThread()->GetDisableDispatchCount() == 1);
|
ASSERT(GetCurrentThread(system.Kernel()).GetDisableDispatchCount() == 1);
|
||||||
|
|
||||||
auto& phys_core = system.Kernel().PhysicalCore(core_id);
|
auto& phys_core = system.Kernel().PhysicalCore(core_id);
|
||||||
if (phys_core.IsInterrupted()) {
|
if (phys_core.IsInterrupted()) {
|
||||||
|
@ -665,7 +665,7 @@ void KScheduler::RescheduleCurrentCore() {
|
||||||
if (state.needs_scheduling.load()) {
|
if (state.needs_scheduling.load()) {
|
||||||
Schedule();
|
Schedule();
|
||||||
} else {
|
} else {
|
||||||
GetCurrentThread()->EnableDispatch();
|
GetCurrentThread(system.Kernel()).EnableDispatch();
|
||||||
guard.Unlock();
|
guard.Unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -718,13 +718,18 @@ void KScheduler::Reload(KThread* thread) {
|
||||||
|
|
||||||
void KScheduler::SwitchContextStep2() {
|
void KScheduler::SwitchContextStep2() {
|
||||||
// Load context of new thread
|
// Load context of new thread
|
||||||
Reload(GetCurrentThread());
|
Reload(GetCurrentThreadPointer(system.Kernel()));
|
||||||
|
|
||||||
RescheduleCurrentCore();
|
RescheduleCurrentCore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void KScheduler::Schedule() {
|
||||||
|
ASSERT(GetCurrentThread(system.Kernel()).GetDisableDispatchCount() == 1);
|
||||||
|
this->ScheduleImpl();
|
||||||
|
}
|
||||||
|
|
||||||
void KScheduler::ScheduleImpl() {
|
void KScheduler::ScheduleImpl() {
|
||||||
KThread* previous_thread = GetCurrentThread();
|
KThread* previous_thread = GetCurrentThreadPointer(system.Kernel());
|
||||||
KThread* next_thread = state.highest_priority_thread;
|
KThread* next_thread = state.highest_priority_thread;
|
||||||
|
|
||||||
state.needs_scheduling.store(false);
|
state.needs_scheduling.store(false);
|
||||||
|
@ -762,6 +767,7 @@ void KScheduler::ScheduleImpl() {
|
||||||
old_context = &previous_thread->GetHostContext();
|
old_context = &previous_thread->GetHostContext();
|
||||||
|
|
||||||
// Set the new thread.
|
// Set the new thread.
|
||||||
|
SetCurrentThread(system.Kernel(), next_thread);
|
||||||
current_thread.store(next_thread);
|
current_thread.store(next_thread);
|
||||||
|
|
||||||
guard.Unlock();
|
guard.Unlock();
|
||||||
|
@ -805,6 +811,7 @@ void KScheduler::SwitchToCurrent() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto thread = next_thread ? next_thread : idle_thread;
|
auto thread = next_thread ? next_thread : idle_thread;
|
||||||
|
SetCurrentThread(system.Kernel(), thread);
|
||||||
Common::Fiber::YieldTo(switch_fiber, *thread->GetHostContext());
|
Common::Fiber::YieldTo(switch_fiber, *thread->GetHostContext());
|
||||||
} while (!is_switch_pending());
|
} while (!is_switch_pending());
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,18 +48,13 @@ public:
|
||||||
void Reload(KThread* thread);
|
void Reload(KThread* thread);
|
||||||
|
|
||||||
/// Gets the current running thread
|
/// Gets the current running thread
|
||||||
[[nodiscard]] KThread* GetCurrentThread() const;
|
[[nodiscard]] KThread* GetSchedulerCurrentThread() const;
|
||||||
|
|
||||||
/// Gets the idle thread
|
/// Gets the idle thread
|
||||||
[[nodiscard]] KThread* GetIdleThread() const {
|
[[nodiscard]] KThread* GetIdleThread() const {
|
||||||
return idle_thread;
|
return idle_thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the scheduler is idle
|
|
||||||
[[nodiscard]] bool IsIdle() const {
|
|
||||||
return GetCurrentThread() == idle_thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the timestamp for the last context switch in ticks.
|
/// Gets the timestamp for the last context switch in ticks.
|
||||||
[[nodiscard]] u64 GetLastContextSwitchTicks() const;
|
[[nodiscard]] u64 GetLastContextSwitchTicks() const;
|
||||||
|
|
||||||
|
@ -149,10 +144,7 @@ private:
|
||||||
|
|
||||||
void RotateScheduledQueue(s32 cpu_core_id, s32 priority);
|
void RotateScheduledQueue(s32 cpu_core_id, s32 priority);
|
||||||
|
|
||||||
void Schedule() {
|
void Schedule();
|
||||||
ASSERT(GetCurrentThread()->GetDisableDispatchCount() == 1);
|
|
||||||
this->ScheduleImpl();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Switches the CPU's active thread context to that of the specified thread
|
/// Switches the CPU's active thread context to that of the specified thread
|
||||||
void ScheduleImpl();
|
void ScheduleImpl();
|
||||||
|
|
|
@ -382,7 +382,7 @@ void KThread::FinishTermination() {
|
||||||
for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) {
|
for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) {
|
||||||
KThread* core_thread{};
|
KThread* core_thread{};
|
||||||
do {
|
do {
|
||||||
core_thread = kernel.Scheduler(i).GetCurrentThread();
|
core_thread = kernel.Scheduler(i).GetSchedulerCurrentThread();
|
||||||
} while (core_thread == this);
|
} while (core_thread == this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -631,7 +631,7 @@ ResultCode KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) {
|
||||||
s32 thread_core;
|
s32 thread_core;
|
||||||
for (thread_core = 0; thread_core < static_cast<s32>(Core::Hardware::NUM_CPU_CORES);
|
for (thread_core = 0; thread_core < static_cast<s32>(Core::Hardware::NUM_CPU_CORES);
|
||||||
++thread_core) {
|
++thread_core) {
|
||||||
if (kernel.Scheduler(thread_core).GetCurrentThread() == this) {
|
if (kernel.Scheduler(thread_core).GetSchedulerCurrentThread() == this) {
|
||||||
thread_is_current = true;
|
thread_is_current = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -756,7 +756,7 @@ void KThread::WaitUntilSuspended() {
|
||||||
for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) {
|
for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) {
|
||||||
KThread* core_thread{};
|
KThread* core_thread{};
|
||||||
do {
|
do {
|
||||||
core_thread = kernel.Scheduler(i).GetCurrentThread();
|
core_thread = kernel.Scheduler(i).GetSchedulerCurrentThread();
|
||||||
} while (core_thread == this);
|
} while (core_thread == this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -822,7 +822,7 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
|
||||||
// Check if the thread is currently running.
|
// Check if the thread is currently running.
|
||||||
// If it is, we'll need to retry.
|
// If it is, we'll need to retry.
|
||||||
for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
|
for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
|
||||||
if (kernel.Scheduler(i).GetCurrentThread() == this) {
|
if (kernel.Scheduler(i).GetSchedulerCurrentThread() == this) {
|
||||||
thread_is_current = true;
|
thread_is_current = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1175,6 +1175,10 @@ std::shared_ptr<Common::Fiber>& KThread::GetHostContext() {
|
||||||
return host_context;
|
return host_context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetCurrentThread(KernelCore& kernel, KThread* thread) {
|
||||||
|
kernel.SetCurrentEmuThread(thread);
|
||||||
|
}
|
||||||
|
|
||||||
KThread* GetCurrentThreadPointer(KernelCore& kernel) {
|
KThread* GetCurrentThreadPointer(KernelCore& kernel) {
|
||||||
return kernel.GetCurrentEmuThread();
|
return kernel.GetCurrentEmuThread();
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,6 +106,7 @@ enum class StepState : u32 {
|
||||||
StepPerformed, ///< Thread has stepped, waiting to be scheduled again
|
StepPerformed, ///< Thread has stepped, waiting to be scheduled again
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void SetCurrentThread(KernelCore& kernel, KThread* thread);
|
||||||
[[nodiscard]] KThread* GetCurrentThreadPointer(KernelCore& kernel);
|
[[nodiscard]] KThread* GetCurrentThreadPointer(KernelCore& kernel);
|
||||||
[[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel);
|
[[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel);
|
||||||
[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
|
[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
|
||||||
|
|
|
@ -331,6 +331,8 @@ struct KernelCore::Impl {
|
||||||
return is_shutting_down.load(std::memory_order_relaxed);
|
return is_shutting_down.load(std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline thread_local KThread* current_thread{nullptr};
|
||||||
|
|
||||||
KThread* GetCurrentEmuThread() {
|
KThread* GetCurrentEmuThread() {
|
||||||
// If we are shutting down the kernel, none of this is relevant anymore.
|
// If we are shutting down the kernel, none of this is relevant anymore.
|
||||||
if (IsShuttingDown()) {
|
if (IsShuttingDown()) {
|
||||||
|
@ -341,7 +343,12 @@ struct KernelCore::Impl {
|
||||||
if (thread_id >= Core::Hardware::NUM_CPU_CORES) {
|
if (thread_id >= Core::Hardware::NUM_CPU_CORES) {
|
||||||
return GetHostDummyThread();
|
return GetHostDummyThread();
|
||||||
}
|
}
|
||||||
return schedulers[thread_id]->GetCurrentThread();
|
|
||||||
|
return current_thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetCurrentEmuThread(KThread* thread) {
|
||||||
|
current_thread = thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeriveInitialMemoryLayout() {
|
void DeriveInitialMemoryLayout() {
|
||||||
|
@ -1024,6 +1031,10 @@ KThread* KernelCore::GetCurrentEmuThread() const {
|
||||||
return impl->GetCurrentEmuThread();
|
return impl->GetCurrentEmuThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void KernelCore::SetCurrentEmuThread(KThread* thread) {
|
||||||
|
impl->SetCurrentEmuThread(thread);
|
||||||
|
}
|
||||||
|
|
||||||
KMemoryManager& KernelCore::MemoryManager() {
|
KMemoryManager& KernelCore::MemoryManager() {
|
||||||
return *impl->memory_manager;
|
return *impl->memory_manager;
|
||||||
}
|
}
|
||||||
|
|
|
@ -226,6 +226,9 @@ public:
|
||||||
/// Gets the current host_thread/guest_thread pointer.
|
/// Gets the current host_thread/guest_thread pointer.
|
||||||
KThread* GetCurrentEmuThread() const;
|
KThread* GetCurrentEmuThread() const;
|
||||||
|
|
||||||
|
/// Sets the current guest_thread pointer.
|
||||||
|
void SetCurrentEmuThread(KThread* thread);
|
||||||
|
|
||||||
/// Gets the current host_thread handle.
|
/// Gets the current host_thread handle.
|
||||||
u32 GetCurrentHostThreadID() const;
|
u32 GetCurrentHostThreadID() const;
|
||||||
|
|
||||||
|
|
|
@ -327,7 +327,6 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
|
||||||
|
|
||||||
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
|
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
|
||||||
|
|
||||||
auto thread = kernel.CurrentScheduler()->GetCurrentThread();
|
|
||||||
{
|
{
|
||||||
KScopedSchedulerLock lock(kernel);
|
KScopedSchedulerLock lock(kernel);
|
||||||
|
|
||||||
|
@ -337,7 +336,7 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
|
||||||
session->SendSyncRequest(&GetCurrentThread(kernel), system.Memory(), system.CoreTiming());
|
session->SendSyncRequest(&GetCurrentThread(kernel), system.Memory(), system.CoreTiming());
|
||||||
}
|
}
|
||||||
|
|
||||||
return thread->GetWaitResult();
|
return GetCurrentThread(kernel).GetWaitResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {
|
static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {
|
||||||
|
@ -624,7 +623,7 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
|
||||||
|
|
||||||
handle_debug_buffer(info1, info2);
|
handle_debug_buffer(info1, info2);
|
||||||
|
|
||||||
auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
|
auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
|
||||||
const auto thread_processor_id = current_thread->GetActiveCore();
|
const auto thread_processor_id = current_thread->GetActiveCore();
|
||||||
system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
|
system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
|
||||||
}
|
}
|
||||||
|
@ -884,7 +883,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
|
||||||
|
|
||||||
const auto& core_timing = system.CoreTiming();
|
const auto& core_timing = system.CoreTiming();
|
||||||
const auto& scheduler = *system.Kernel().CurrentScheduler();
|
const auto& scheduler = *system.Kernel().CurrentScheduler();
|
||||||
const auto* const current_thread = scheduler.GetCurrentThread();
|
const auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
|
||||||
const bool same_thread = current_thread == thread.GetPointerUnsafe();
|
const bool same_thread = current_thread == thread.GetPointerUnsafe();
|
||||||
|
|
||||||
const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks();
|
const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks();
|
||||||
|
@ -1103,7 +1102,7 @@ static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Hand
|
||||||
if (thread->GetRawState() != ThreadState::Runnable) {
|
if (thread->GetRawState() != ThreadState::Runnable) {
|
||||||
bool current = false;
|
bool current = false;
|
||||||
for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
|
for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
|
||||||
if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetCurrentThread()) {
|
if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetSchedulerCurrentThread()) {
|
||||||
current = true;
|
current = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1851,7 +1850,7 @@ static ResultCode StartThread32(Core::System& system, Handle thread_handle) {
|
||||||
static void ExitThread(Core::System& system) {
|
static void ExitThread(Core::System& system) {
|
||||||
LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
|
LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
|
||||||
|
|
||||||
auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
|
auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
|
||||||
system.GlobalSchedulerContext().RemoveThread(current_thread);
|
system.GlobalSchedulerContext().RemoveThread(current_thread);
|
||||||
current_thread->Exit();
|
current_thread->Exit();
|
||||||
system.Kernel().UnregisterInUseObject(current_thread);
|
system.Kernel().UnregisterInUseObject(current_thread);
|
||||||
|
@ -2993,7 +2992,7 @@ void Call(Core::System& system, u32 immediate) {
|
||||||
auto& kernel = system.Kernel();
|
auto& kernel = system.Kernel();
|
||||||
kernel.EnterSVCProfile();
|
kernel.EnterSVCProfile();
|
||||||
|
|
||||||
auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
|
auto* thread = GetCurrentThreadPointer(kernel);
|
||||||
thread->SetIsCallingSvc();
|
thread->SetIsCallingSvc();
|
||||||
|
|
||||||
const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate)
|
const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate)
|
||||||
|
|
|
@ -49,28 +49,42 @@ bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) {
|
ResultCode Controller_NPad::IsDeviceHandleValid(
|
||||||
|
const Core::HID::VibrationDeviceHandle& device_handle) {
|
||||||
const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
|
const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
|
||||||
const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
|
const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
|
||||||
const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
|
const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
|
||||||
return npad_id && npad_type && device_index;
|
|
||||||
|
if (npad_type) {
|
||||||
|
return VibrationInvalidStyleIndex;
|
||||||
|
}
|
||||||
|
if (npad_id) {
|
||||||
|
return VibrationInvalidNpadId;
|
||||||
|
}
|
||||||
|
if (device_index) {
|
||||||
|
return VibrationDeviceIndexOutOfRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode Controller_NPad::VerifyValidSixAxisSensorHandle(
|
ResultCode Controller_NPad::VerifyValidSixAxisSensorHandle(
|
||||||
const Core::HID::SixAxisSensorHandle& device_handle) {
|
const Core::HID::SixAxisSensorHandle& device_handle) {
|
||||||
const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
|
const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
|
||||||
|
const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
|
||||||
|
const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
|
||||||
|
|
||||||
if (!npad_id) {
|
if (!npad_id) {
|
||||||
return InvalidNpadId;
|
return InvalidNpadId;
|
||||||
}
|
}
|
||||||
const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
|
|
||||||
if (!device_index) {
|
if (!device_index) {
|
||||||
return NpadDeviceIndexOutOfRange;
|
return NpadDeviceIndexOutOfRange;
|
||||||
}
|
}
|
||||||
// This doesn't get validated on nnsdk
|
// This doesn't get validated on nnsdk
|
||||||
const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
|
|
||||||
if (!npad_type) {
|
if (!npad_type) {
|
||||||
return NpadInvalidHandle;
|
return NpadInvalidHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -705,6 +719,12 @@ Controller_NPad::NpadJoyHoldType Controller_NPad::GetHoldType() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) {
|
void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) {
|
||||||
|
if (activation_mode != NpadHandheldActivationMode::None &&
|
||||||
|
activation_mode != NpadHandheldActivationMode::Single) {
|
||||||
|
ASSERT_MSG(false, "Activation mode should be always None or Single");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
handheld_activation_mode = activation_mode;
|
handheld_activation_mode = activation_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -840,7 +860,7 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
|
||||||
void Controller_NPad::VibrateController(
|
void Controller_NPad::VibrateController(
|
||||||
const Core::HID::VibrationDeviceHandle& vibration_device_handle,
|
const Core::HID::VibrationDeviceHandle& vibration_device_handle,
|
||||||
const Core::HID::VibrationValue& vibration_value) {
|
const Core::HID::VibrationValue& vibration_value) {
|
||||||
if (!IsDeviceHandleValid(vibration_device_handle)) {
|
if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -903,7 +923,7 @@ void Controller_NPad::VibrateControllers(
|
||||||
|
|
||||||
Core::HID::VibrationValue Controller_NPad::GetLastVibration(
|
Core::HID::VibrationValue Controller_NPad::GetLastVibration(
|
||||||
const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
|
const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
|
||||||
if (!IsDeviceHandleValid(vibration_device_handle)) {
|
if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -914,7 +934,7 @@ Core::HID::VibrationValue Controller_NPad::GetLastVibration(
|
||||||
|
|
||||||
void Controller_NPad::InitializeVibrationDevice(
|
void Controller_NPad::InitializeVibrationDevice(
|
||||||
const Core::HID::VibrationDeviceHandle& vibration_device_handle) {
|
const Core::HID::VibrationDeviceHandle& vibration_device_handle) {
|
||||||
if (!IsDeviceHandleValid(vibration_device_handle)) {
|
if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -941,7 +961,7 @@ void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) {
|
||||||
|
|
||||||
bool Controller_NPad::IsVibrationDeviceMounted(
|
bool Controller_NPad::IsVibrationDeviceMounted(
|
||||||
const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
|
const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
|
||||||
if (!IsDeviceHandleValid(vibration_device_handle)) {
|
if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -197,7 +197,7 @@ public:
|
||||||
Core::HID::NpadButton GetAndResetPressState();
|
Core::HID::NpadButton GetAndResetPressState();
|
||||||
|
|
||||||
static bool IsNpadIdValid(Core::HID::NpadIdType npad_id);
|
static bool IsNpadIdValid(Core::HID::NpadIdType npad_id);
|
||||||
static bool IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
|
static ResultCode IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
|
||||||
static ResultCode VerifyValidSixAxisSensorHandle(
|
static ResultCode VerifyValidSixAxisSensorHandle(
|
||||||
const Core::HID::SixAxisSensorHandle& device_handle);
|
const Core::HID::SixAxisSensorHandle& device_handle);
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,9 @@ namespace Service::HID {
|
||||||
|
|
||||||
constexpr ResultCode NpadInvalidHandle{ErrorModule::HID, 100};
|
constexpr ResultCode NpadInvalidHandle{ErrorModule::HID, 100};
|
||||||
constexpr ResultCode NpadDeviceIndexOutOfRange{ErrorModule::HID, 107};
|
constexpr ResultCode NpadDeviceIndexOutOfRange{ErrorModule::HID, 107};
|
||||||
|
constexpr ResultCode VibrationInvalidStyleIndex{ErrorModule::HID, 122};
|
||||||
|
constexpr ResultCode VibrationInvalidNpadId{ErrorModule::HID, 123};
|
||||||
|
constexpr ResultCode VibrationDeviceIndexOutOfRange{ErrorModule::HID, 124};
|
||||||
constexpr ResultCode InvalidSixAxisFusionRange{ErrorModule::HID, 423};
|
constexpr ResultCode InvalidSixAxisFusionRange{ErrorModule::HID, 423};
|
||||||
constexpr ResultCode NpadIsDualJoycon{ErrorModule::HID, 601};
|
constexpr ResultCode NpadIsDualJoycon{ErrorModule::HID, 601};
|
||||||
constexpr ResultCode NpadIsSameType{ErrorModule::HID, 602};
|
constexpr ResultCode NpadIsSameType{ErrorModule::HID, 602};
|
||||||
|
|
|
@ -778,7 +778,7 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
bool is_at_rest{};
|
bool is_at_rest{};
|
||||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||||
const auto result = controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest);
|
controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest);
|
||||||
|
|
||||||
LOG_DEBUG(Service_HID,
|
LOG_DEBUG(Service_HID,
|
||||||
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
|
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
|
||||||
|
@ -786,7 +786,7 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
|
||||||
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
|
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
rb.Push(result);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push(is_at_rest);
|
rb.Push(is_at_rest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -803,8 +803,8 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c
|
||||||
|
|
||||||
bool is_firmware_available{};
|
bool is_firmware_available{};
|
||||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||||
const auto result = controller.IsFirmwareUpdateAvailableForSixAxisSensor(
|
controller.IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle,
|
||||||
parameters.sixaxis_handle, is_firmware_available);
|
is_firmware_available);
|
||||||
|
|
||||||
LOG_WARNING(
|
LOG_WARNING(
|
||||||
Service_HID,
|
Service_HID,
|
||||||
|
@ -813,7 +813,7 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c
|
||||||
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
|
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
rb.Push(result);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push(is_firmware_available);
|
rb.Push(is_firmware_available);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1083,13 +1083,13 @@ void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
|
||||||
const auto parameters{rp.PopRaw<Parameters>()};
|
const auto parameters{rp.PopRaw<Parameters>()};
|
||||||
|
|
||||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||||
const auto result = controller.DisconnectNpad(parameters.npad_id);
|
controller.DisconnectNpad(parameters.npad_id);
|
||||||
|
|
||||||
LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
|
LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
|
||||||
parameters.applet_resource_user_id);
|
parameters.applet_resource_user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(result);
|
rb.Push(ResultSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
|
void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -1165,15 +1165,14 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx
|
||||||
const auto parameters{rp.PopRaw<Parameters>()};
|
const auto parameters{rp.PopRaw<Parameters>()};
|
||||||
|
|
||||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||||
const auto result =
|
controller.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left,
|
||||||
controller.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left,
|
Controller_NPad::NpadJoyAssignmentMode::Single);
|
||||||
Controller_NPad::NpadJoyAssignmentMode::Single);
|
|
||||||
|
|
||||||
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
|
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
|
||||||
parameters.applet_resource_user_id);
|
parameters.applet_resource_user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(result);
|
rb.Push(ResultSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
|
void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -1189,15 +1188,15 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
|
||||||
const auto parameters{rp.PopRaw<Parameters>()};
|
const auto parameters{rp.PopRaw<Parameters>()};
|
||||||
|
|
||||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||||
const auto result = controller.SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type,
|
controller.SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type,
|
||||||
Controller_NPad::NpadJoyAssignmentMode::Single);
|
Controller_NPad::NpadJoyAssignmentMode::Single);
|
||||||
|
|
||||||
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
|
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
|
||||||
parameters.npad_id, parameters.applet_resource_user_id,
|
parameters.npad_id, parameters.applet_resource_user_id,
|
||||||
parameters.npad_joy_device_type);
|
parameters.npad_joy_device_type);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(result);
|
rb.Push(ResultSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
|
void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -1212,14 +1211,13 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
|
||||||
const auto parameters{rp.PopRaw<Parameters>()};
|
const auto parameters{rp.PopRaw<Parameters>()};
|
||||||
|
|
||||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||||
const auto result = controller.SetNpadMode(parameters.npad_id, {},
|
controller.SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual);
|
||||||
Controller_NPad::NpadJoyAssignmentMode::Dual);
|
|
||||||
|
|
||||||
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
|
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
|
||||||
parameters.applet_resource_user_id);
|
parameters.applet_resource_user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(result);
|
rb.Push(ResultSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
|
void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -1412,8 +1410,11 @@ void Hid::ClearNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx) {
|
||||||
void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
|
void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
|
const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
|
||||||
|
const auto& controller =
|
||||||
|
GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||||
|
|
||||||
Core::HID::VibrationDeviceInfo vibration_device_info;
|
Core::HID::VibrationDeviceInfo vibration_device_info;
|
||||||
|
bool check_device_index = false;
|
||||||
|
|
||||||
switch (vibration_device_handle.npad_type) {
|
switch (vibration_device_handle.npad_type) {
|
||||||
case Core::HID::NpadStyleIndex::ProController:
|
case Core::HID::NpadStyleIndex::ProController:
|
||||||
|
@ -1421,34 +1422,46 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
|
||||||
case Core::HID::NpadStyleIndex::JoyconDual:
|
case Core::HID::NpadStyleIndex::JoyconDual:
|
||||||
case Core::HID::NpadStyleIndex::JoyconLeft:
|
case Core::HID::NpadStyleIndex::JoyconLeft:
|
||||||
case Core::HID::NpadStyleIndex::JoyconRight:
|
case Core::HID::NpadStyleIndex::JoyconRight:
|
||||||
default:
|
|
||||||
vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator;
|
vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator;
|
||||||
|
check_device_index = true;
|
||||||
break;
|
break;
|
||||||
case Core::HID::NpadStyleIndex::GameCube:
|
case Core::HID::NpadStyleIndex::GameCube:
|
||||||
vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm;
|
vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm;
|
||||||
break;
|
break;
|
||||||
case Core::HID::NpadStyleIndex::Pokeball:
|
case Core::HID::NpadStyleIndex::N64:
|
||||||
|
vibration_device_info.type = Core::HID::VibrationDeviceType::N64;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown;
|
vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (vibration_device_handle.device_index) {
|
vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
|
||||||
case Core::HID::DeviceIndex::Left:
|
if (check_device_index) {
|
||||||
vibration_device_info.position = Core::HID::VibrationDevicePosition::Left;
|
switch (vibration_device_handle.device_index) {
|
||||||
break;
|
case Core::HID::DeviceIndex::Left:
|
||||||
case Core::HID::DeviceIndex::Right:
|
vibration_device_info.position = Core::HID::VibrationDevicePosition::Left;
|
||||||
vibration_device_info.position = Core::HID::VibrationDevicePosition::Right;
|
break;
|
||||||
break;
|
case Core::HID::DeviceIndex::Right:
|
||||||
case Core::HID::DeviceIndex::None:
|
vibration_device_info.position = Core::HID::VibrationDevicePosition::Right;
|
||||||
default:
|
break;
|
||||||
ASSERT_MSG(false, "DeviceIndex should never be None!");
|
case Core::HID::DeviceIndex::None:
|
||||||
vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
|
default:
|
||||||
break;
|
ASSERT_MSG(false, "DeviceIndex should never be None!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}",
|
LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}",
|
||||||
vibration_device_info.type, vibration_device_info.position);
|
vibration_device_info.type, vibration_device_info.position);
|
||||||
|
|
||||||
|
const auto result = controller.IsDeviceHandleValid(vibration_device_handle);
|
||||||
|
if (result.IsError()) {
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.PushRaw(vibration_device_info);
|
rb.PushRaw(vibration_device_info);
|
||||||
|
|
|
@ -52,8 +52,8 @@ QtNXWebEngineView::QtNXWebEngineView(QWidget* parent, Core::System& system,
|
||||||
: QWebEngineView(parent), input_subsystem{input_subsystem_},
|
: QWebEngineView(parent), input_subsystem{input_subsystem_},
|
||||||
url_interceptor(std::make_unique<UrlRequestInterceptor>()),
|
url_interceptor(std::make_unique<UrlRequestInterceptor>()),
|
||||||
input_interpreter(std::make_unique<InputInterpreter>(system)),
|
input_interpreter(std::make_unique<InputInterpreter>(system)),
|
||||||
default_profile{QWebEngineProfile::defaultProfile()},
|
default_profile{QWebEngineProfile::defaultProfile()}, global_settings{
|
||||||
global_settings{QWebEngineSettings::globalSettings()} {
|
default_profile->settings()} {
|
||||||
default_profile->setPersistentStoragePath(QString::fromStdString(Common::FS::PathToUTF8String(
|
default_profile->setPersistentStoragePath(QString::fromStdString(Common::FS::PathToUTF8String(
|
||||||
Common::FS::GetYuzuPath(Common::FS::YuzuPath::YuzuDir) / "qtwebengine")));
|
Common::FS::GetYuzuPath(Common::FS::YuzuPath::YuzuDir) / "qtwebengine")));
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ QtNXWebEngineView::QtNXWebEngineView(QWidget* parent, Core::System& system,
|
||||||
default_profile->scripts()->insert(gamepad);
|
default_profile->scripts()->insert(gamepad);
|
||||||
default_profile->scripts()->insert(window_nx);
|
default_profile->scripts()->insert(window_nx);
|
||||||
|
|
||||||
default_profile->setRequestInterceptor(url_interceptor.get());
|
default_profile->setUrlRequestInterceptor(url_interceptor.get());
|
||||||
|
|
||||||
global_settings->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, true);
|
global_settings->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, true);
|
||||||
global_settings->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
|
global_settings->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
|
||||||
|
|
Loading…
Reference in a new issue