From 4b89348c0046e69c3419a587d2bc19e1c411e39c Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 4 Jun 2018 18:05:12 -0500 Subject: [PATCH 01/12] GPU: Implemented the F2I_R shader instruction. --- src/video_core/engines/shader_bytecode.h | 24 ++++++++-- .../renderer_opengl/gl_shader_decompiler.cpp | 47 +++++++++++++++++-- 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 22c122fcc5..93654eb662 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -173,6 +173,13 @@ enum class SubOp : u64 { Min = 0x8, }; +enum class FloatRoundingOp : u64 { + None = 0, + Floor = 1, + Ceil = 2, + Trunc = 3, +}; + union Instruction { Instruction& operator=(const Instruction& instr) { value = instr.value; @@ -277,11 +284,20 @@ union Instruction { union { BitField<10, 2, Register::Size> size; - BitField<13, 1, u64> is_signed; + BitField<12, 1, u64> is_output_signed; + BitField<13, 1, u64> is_input_signed; BitField<41, 2, u64> selector; BitField<45, 1, u64> negate_a; BitField<49, 1, u64> abs_a; BitField<50, 1, u64> saturate_a; + + union { + BitField<39, 2, FloatRoundingOp> rounding; + } f2i; + + union { + BitField<39, 4, u64> rounding; + } f2f; } conversion; union { @@ -535,9 +551,9 @@ private: INST("0100110010101---", Id::F2F_C, Type::Conversion, "F2F_C"), INST("0101110010101---", Id::F2F_R, Type::Conversion, "F2F_R"), INST("0011100-10101---", Id::F2F_IMM, Type::Conversion, "F2F_IMM"), - INST("0100110010110---", Id::F2I_C, Type::Arithmetic, "F2I_C"), - INST("0101110010110---", Id::F2I_R, Type::Arithmetic, "F2I_R"), - INST("0011100-10110---", Id::F2I_IMM, Type::Arithmetic, "F2I_IMM"), + INST("0100110010110---", Id::F2I_C, Type::Conversion, "F2I_C"), + INST("0101110010110---", Id::F2I_R, Type::Conversion, "F2I_R"), + INST("0011100-10110---", Id::F2I_IMM, Type::Conversion, "F2I_IMM"), INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"), INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"), INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"), diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 9943394c69..4b14bb47ec 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -929,18 +929,20 @@ private: ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); std::string op_a = - regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_signed); + regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_input_signed); if (instr.conversion.abs_a) { op_a = "abs(" + op_a + ')'; } - regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_signed, 0, op_a, 1, 1); + regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, + 1); break; } case OpCode::Id::I2F_R: { + ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); std::string op_a = - regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_signed); + regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_input_signed); if (instr.conversion.abs_a) { op_a = "abs(" + op_a + ')'; @@ -950,6 +952,8 @@ private: break; } case OpCode::Id::F2F_R: { + // TODO(Subv): Implement rounding operations. + ASSERT_MSG(instr.conversion.f2f.rounding == 0, "Unimplemented rounding operation"); std::string op_a = regs.GetRegisterAsFloat(instr.gpr20); if (instr.conversion.abs_a) { @@ -959,6 +963,43 @@ private: regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); break; } + case OpCode::Id::F2I_R: { + std::string op_a = regs.GetRegisterAsFloat(instr.gpr20); + + if (instr.conversion.abs_a) { + op_a = "abs(" + op_a + ')'; + } + + using Tegra::Shader::FloatRoundingOp; + switch (instr.conversion.f2i.rounding) { + case FloatRoundingOp::None: + break; + case FloatRoundingOp::Floor: + op_a = "floor(" + op_a + ')'; + break; + case FloatRoundingOp::Ceil: + op_a = "ceil(" + op_a + ')'; + break; + case FloatRoundingOp::Trunc: + op_a = "trunc(" + op_a + ')'; + break; + default: + NGLOG_CRITICAL(HW_GPU, "Unimplemented f2i rounding mode {}", + static_cast(instr.conversion.f2i.rounding.Value())); + UNREACHABLE(); + break; + } + + if (instr.conversion.is_output_signed) { + op_a = "int(" + op_a + ')'; + } else { + op_a = "uint(" + op_a + ')'; + } + + regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, + 1); + break; + } default: { NGLOG_CRITICAL(HW_GPU, "Unhandled conversion instruction: {}", opcode->GetName()); UNREACHABLE(); From 8d70d1ea455f7dbf3a599a4feffd5335cc15efe4 Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 5 Jun 2018 21:07:40 -0500 Subject: [PATCH 02/12] GPU: Allow the usage of RGBA32_FLOAT in the texture copy engine. --- src/video_core/gpu.cpp | 2 ++ src/video_core/gpu.h | 1 + 2 files changed, 3 insertions(+) diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 756518ee70..1c9a523fc0 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -26,6 +26,8 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) { ASSERT(format != RenderTargetFormat::NONE); switch (format) { + case RenderTargetFormat::RGBA32_FLOAT: + return 16; case RenderTargetFormat::RGBA8_UNORM: case RenderTargetFormat::RGB10_A2_UNORM: return 4; diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index f168a51712..9f84654442 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -15,6 +15,7 @@ namespace Tegra { enum class RenderTargetFormat : u32 { NONE = 0x0, + RGBA32_FLOAT = 0xC0, RGBA16_FLOAT = 0xCA, RGB10_A2_UNORM = 0xD1, RGBA8_UNORM = 0xD5, From 14afc704d4706ce8aa1f195a107ec30e3d9a8c6d Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 5 Jun 2018 21:55:17 -0500 Subject: [PATCH 03/12] GPU: Fixed the compression factor for RGBA16F textures. They're not compressed. --- src/video_core/renderer_opengl/gl_rasterizer_cache.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 6f08678ab2..f9735ba716 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -104,7 +104,7 @@ struct SurfaceParams { 1, // A2B10G10R10 1, // A1B5G5R5 1, // R8 - 2, // RGBA16F + 1, // RGBA16F 4, // DXT1 4, // DXT23 4, // DXT45 From c531a92edaae409275d9e7309d7b315d6fccf190 Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 5 Jun 2018 21:57:16 -0500 Subject: [PATCH 04/12] GPU: Implemented the R11FG11FB10F texture and rendertarget formats. --- src/video_core/gpu.h | 1 + .../renderer_opengl/gl_rasterizer_cache.cpp | 19 ++++++++++++------- .../renderer_opengl/gl_rasterizer_cache.h | 18 ++++++++++++++---- src/video_core/textures/decoders.cpp | 3 +++ 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index f168a51712..2762a22ecd 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -19,6 +19,7 @@ enum class RenderTargetFormat : u32 { RGB10_A2_UNORM = 0xD1, RGBA8_UNORM = 0xD5, RGBA8_SRGB = 0xD6, + R11G11B10_FLOAT = 0xE0, }; /// Returns the number of bytes per pixel of each rendertarget format. diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index d6048f6392..9164d7f344 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -50,6 +50,7 @@ static constexpr std::array tex_form {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, false}, // A1B5G5R5 {GL_R8, GL_RED, GL_UNSIGNED_BYTE, false}, // R8 {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, false}, // RGBA16F + {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, false}, // R11FG11FB10F {GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT1 {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT23 {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT45 @@ -60,8 +61,10 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType const SurfaceType type = SurfaceParams::GetFormatType(pixel_format); if (type == SurfaceType::ColorTexture) { ASSERT(static_cast(pixel_format) < tex_format_tuples.size()); - // For now only UNORM components are supported, or RGBA16F which is type FLOAT - ASSERT(component_type == ComponentType::UNorm || pixel_format == PixelFormat::RGBA16F); + // For now only UNORM components are supported, or either R11FG11FB10F or RGBA16F which are + // type FLOAT + ASSERT(component_type == ComponentType::UNorm || pixel_format == PixelFormat::RGBA16F || + pixel_format == PixelFormat::R11FG11FB10F); return tex_format_tuples[static_cast(pixel_format)]; } else if (type == SurfaceType::Depth || type == SurfaceType::DepthStencil) { // TODO(Subv): Implement depth formats @@ -110,11 +113,12 @@ static constexpr std::array morton_to_gl_fns = { - MortonCopy, MortonCopy, - MortonCopy, MortonCopy, - MortonCopy, MortonCopy, - MortonCopy, MortonCopy, - MortonCopy, MortonCopy, + MortonCopy, MortonCopy, + MortonCopy, MortonCopy, + MortonCopy, MortonCopy, + MortonCopy, MortonCopy, + MortonCopy, MortonCopy, + MortonCopy, }; static constexpr std::array, MortonCopy, MortonCopy, + MortonCopy, // TODO(Subv): Swizzling the DXT1/DXT23/DXT45/DXN1 formats is not yet supported nullptr, nullptr, diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index f9735ba716..0f43e863d0 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -60,10 +60,11 @@ struct SurfaceParams { A1B5G5R5 = 3, R8 = 4, RGBA16F = 5, - DXT1 = 6, - DXT23 = 7, - DXT45 = 8, - DXN1 = 9, // This is also known as BC4 + R11FG11FB10F = 6, + DXT1 = 7, + DXT23 = 8, + DXT45 = 9, + DXN1 = 10, // This is also known as BC4 Max, Invalid = 255, @@ -105,6 +106,7 @@ struct SurfaceParams { 1, // A1B5G5R5 1, // R8 1, // RGBA16F + 1, // R11FG11FB10F 4, // DXT1 4, // DXT23 4, // DXT45 @@ -129,6 +131,7 @@ struct SurfaceParams { 16, // A1B5G5R5 8, // R8 64, // RGBA16F + 32, // R11FG11FB10F 64, // DXT1 128, // DXT23 128, // DXT45 @@ -151,6 +154,8 @@ struct SurfaceParams { return PixelFormat::A2B10G10R10; case Tegra::RenderTargetFormat::RGBA16_FLOAT: return PixelFormat::RGBA16F; + case Tegra::RenderTargetFormat::R11G11B10_FLOAT: + return PixelFormat::R11FG11FB10F; default: NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast(format)); UNREACHABLE(); @@ -182,6 +187,8 @@ struct SurfaceParams { return PixelFormat::R8; case Tegra::Texture::TextureFormat::R16_G16_B16_A16: return PixelFormat::RGBA16F; + case Tegra::Texture::TextureFormat::BF10GF11RF11: + return PixelFormat::R11FG11FB10F; case Tegra::Texture::TextureFormat::DXT1: return PixelFormat::DXT1; case Tegra::Texture::TextureFormat::DXT23: @@ -211,6 +218,8 @@ struct SurfaceParams { return Tegra::Texture::TextureFormat::R8; case PixelFormat::RGBA16F: return Tegra::Texture::TextureFormat::R16_G16_B16_A16; + case PixelFormat::R11FG11FB10F: + return Tegra::Texture::TextureFormat::BF10GF11RF11; case PixelFormat::DXT1: return Tegra::Texture::TextureFormat::DXT1; case PixelFormat::DXT23: @@ -243,6 +252,7 @@ struct SurfaceParams { case Tegra::RenderTargetFormat::RGB10_A2_UNORM: return ComponentType::UNorm; case Tegra::RenderTargetFormat::RGBA16_FLOAT: + case Tegra::RenderTargetFormat::R11G11B10_FLOAT: return ComponentType::Float; default: NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast(format)); diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 2d2af55548..7bf9c4c4b1 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -55,6 +55,7 @@ u32 BytesPerPixel(TextureFormat format) { return 16; case TextureFormat::A8R8G8B8: case TextureFormat::A2B10G10R10: + case TextureFormat::BF10GF11RF11: return 4; case TextureFormat::A1B5G5R5: case TextureFormat::B5G6R5: @@ -92,6 +93,7 @@ std::vector UnswizzleTexture(VAddr address, TextureFormat format, u32 width, case TextureFormat::B5G6R5: case TextureFormat::R8: case TextureFormat::R16_G16_B16_A16: + case TextureFormat::BF10GF11RF11: CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data, unswizzled_data.data(), true, block_height); break; @@ -118,6 +120,7 @@ std::vector DecodeTexture(const std::vector& texture_data, TextureFormat case TextureFormat::A1B5G5R5: case TextureFormat::B5G6R5: case TextureFormat::R8: + case TextureFormat::BF10GF11RF11: // TODO(Subv): For the time being just forward the same data without any decoding. rgba_data = texture_data; break; From adf47cd59a08a87fc922411826118020eed92c98 Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 5 Jun 2018 22:01:20 -0500 Subject: [PATCH 05/12] GPU: Allow the usage of RGBA16_FLOAT in the texture copy engine. --- src/video_core/gpu.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 1c9a523fc0..66351fe6e4 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -28,6 +28,8 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) { switch (format) { case RenderTargetFormat::RGBA32_FLOAT: return 16; + case RenderTargetFormat::RGBA16_FLOAT: + return 8; case RenderTargetFormat::RGBA8_UNORM: case RenderTargetFormat::RGB10_A2_UNORM: return 4; From 2abe5e39fc428f27727b1f4b1b4c4f65477ffbbc Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 5 Jun 2018 18:44:01 -0400 Subject: [PATCH 06/12] am: Stub out IApplicationFunctions::GetPseudoDeviceId. --- src/core/hle/service/am/am.cpp | 13 ++++++++++++- src/core/hle/service/am/am.h | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 12954556d2..b8d6b8d4d0 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -561,7 +561,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF {32, nullptr, "BeginBlockingHomeButton"}, {33, nullptr, "EndBlockingHomeButton"}, {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, - {50, nullptr, "GetPseudoDeviceId"}, + {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"}, {60, nullptr, "SetMediaPlaybackStateForApplication"}, {65, nullptr, "IsGamePlayRecordingSupported"}, {66, &IApplicationFunctions::InitializeGamePlayRecording, "InitializeGamePlayRecording"}, @@ -684,6 +684,17 @@ void IApplicationFunctions::NotifyRunning(Kernel::HLERequestContext& ctx) { NGLOG_WARNING(Service_AM, "(STUBBED) called"); } +void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(RESULT_SUCCESS); + + // Returns a 128-bit UUID + rb.Push(0); + rb.Push(0); + + NGLOG_WARNING(Service_AM, "(STUBBED) called"); +} + void InstallInterfaces(SM::ServiceManager& service_manager, std::shared_ptr nvflinger) { std::make_shared(nvflinger)->InstallAsService(service_manager); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 301a6c7988..1da79fd015 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -138,6 +138,7 @@ private: void InitializeGamePlayRecording(Kernel::HLERequestContext& ctx); void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx); void NotifyRunning(Kernel::HLERequestContext& ctx); + void GetPseudoDeviceId(Kernel::HLERequestContext& ctx); }; class IHomeMenuFunctions final : public ServiceFramework { From c5684411a0786d4f5e6a800932fded1207c46a07 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 5 Jun 2018 18:43:43 -0400 Subject: [PATCH 07/12] nifm: Stub out IRequest::SetConnectionConfirmationOption. --- src/core/hle/service/nifm/nifm.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index eee92cfcde..62489c7fee 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -38,7 +38,7 @@ public: {8, nullptr, "SetPriority"}, {9, nullptr, "SetNetworkProfileId"}, {10, nullptr, "SetRejectable"}, - {11, nullptr, "SetConnectionConfirmationOption"}, + {11, &IRequest::SetConnectionConfirmationOption, "SetConnectionConfirmationOption"}, {12, nullptr, "SetPersistent"}, {13, nullptr, "SetInstant"}, {14, nullptr, "SetSustainable"}, @@ -67,23 +67,32 @@ private: rb.Push(RESULT_SUCCESS); rb.Push(0); } + void GetResult(Kernel::HLERequestContext& ctx) { NGLOG_WARNING(Service_NIFM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } + void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) { NGLOG_WARNING(Service_NIFM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2, 2}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(event1, event2); } + void Cancel(Kernel::HLERequestContext& ctx) { NGLOG_WARNING(Service_NIFM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } + void SetConnectionConfirmationOption(Kernel::HLERequestContext& ctx) { + NGLOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + Kernel::SharedPtr event1, event2; }; From bf0543af231a96cd3c5932d5fd361a2317cfff2c Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 5 Jun 2018 21:49:57 -0400 Subject: [PATCH 08/12] maxwell_to_gl: Implement WrapMode Mirror. --- src/video_core/renderer_opengl/maxwell_to_gl.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index a630610d80..cf11983cf8 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -100,6 +100,8 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { switch (wrap_mode) { case Tegra::Texture::WrapMode::Wrap: return GL_REPEAT; + case Tegra::Texture::WrapMode::Mirror: + return GL_MIRRORED_REPEAT; case Tegra::Texture::WrapMode::ClampToEdge: return GL_CLAMP_TO_EDGE; case Tegra::Texture::WrapMode::ClampOGL: From 566f97b580877f9a81631fdb45d4b866d6bb0eb9 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 5 Jun 2018 18:38:25 -0400 Subject: [PATCH 09/12] gl_shader_decompiler: Fix un/signed mismatch with SHL. --- src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 4a41e77983..7a59ecccf3 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -895,7 +895,7 @@ private: } case OpCode::Type::Shift: { - std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, false); + std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true); std::string op_b; if (instr.is_b_imm) { From 39fb3e362cf71d0542a5807c97039cd474ef3716 Mon Sep 17 00:00:00 2001 From: Hedges Date: Wed, 6 Jun 2018 05:20:47 +0100 Subject: [PATCH 10/12] GDB Stub Improvements (#508) * GDB Stub should work now. * Applied clang-format. * Replaced htonll with swap64. * Tidy up. --- src/core/arm/unicorn/arm_unicorn.cpp | 38 +++++- src/core/arm/unicorn/arm_unicorn.h | 4 + src/core/gdbstub/gdbstub.cpp | 170 +++++++++++++++++++++++---- src/core/gdbstub/gdbstub.h | 9 ++ 4 files changed, 194 insertions(+), 27 deletions(-) diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index c0cc62f03a..ce6c5616d6 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -35,6 +35,17 @@ LoadDll LoadDll::g_load_dll; } \ } while (0) +static void CodeHook(uc_engine* uc, uint64_t address, uint32_t size, void* user_data) { + GDBStub::BreakpointAddress bkpt = + GDBStub::GetNextBreakpointFromAddress(address, GDBStub::BreakpointType::Execute); + if (GDBStub::IsMemoryBreak() || + (bkpt.type != GDBStub::BreakpointType::None && address == bkpt.address)) { + auto core = static_cast(user_data); + core->RecordBreak(bkpt); + uc_emu_stop(uc); + } +} + static void InterruptHook(uc_engine* uc, u32 intNo, void* user_data) { u32 esr{}; CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr)); @@ -67,6 +78,10 @@ ARM_Unicorn::ARM_Unicorn() { uc_hook hook{}; CHECKED(uc_hook_add(uc, &hook, UC_HOOK_INTR, (void*)InterruptHook, this, 0, -1)); CHECKED(uc_hook_add(uc, &hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, this, 0, -1)); + if (GDBStub::IsServerEnabled()) { + CHECKED(uc_hook_add(uc, &hook, UC_HOOK_CODE, (void*)CodeHook, this, 0, -1)); + last_bkpt_hit = false; + } } ARM_Unicorn::~ARM_Unicorn() { @@ -155,7 +170,11 @@ void ARM_Unicorn::SetTlsAddress(VAddr base) { } void ARM_Unicorn::Run() { - ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0)); + if (GDBStub::IsServerEnabled()) { + ExecuteInstructions(std::max(4000000, 0)); + } else { + ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0)); + } } void ARM_Unicorn::Step() { @@ -168,6 +187,18 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) { MICROPROFILE_SCOPE(ARM_Jit); CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); CoreTiming::AddTicks(num_instructions); + if (GDBStub::IsServerEnabled()) { + if (last_bkpt_hit) { + uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); + } + Kernel::Thread* thread = Kernel::GetCurrentThread(); + SaveContext(thread->context); + if (last_bkpt_hit) { + last_bkpt_hit = false; + GDBStub::Break(); + } + GDBStub::SendTrap(thread, 5); + } } void ARM_Unicorn::SaveContext(ARM_Interface::ThreadContext& ctx) { @@ -233,3 +264,8 @@ void ARM_Unicorn::PrepareReschedule() { } void ARM_Unicorn::ClearInstructionCache() {} + +void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) { + last_bkpt = bkpt; + last_bkpt_hit = true; +} diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index b99b58e4c6..a482a2aa36 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h @@ -7,6 +7,7 @@ #include #include "common/common_types.h" #include "core/arm/arm_interface.h" +#include "core/gdbstub/gdbstub.h" class ARM_Unicorn final : public ARM_Interface { public: @@ -35,7 +36,10 @@ public: void Step() override; void ClearInstructionCache() override; void PageTableChanged() override{}; + void RecordBreak(GDBStub::BreakpointAddress bkpt); private: uc_engine* uc{}; + GDBStub::BreakpointAddress last_bkpt{}; + bool last_bkpt_hit; }; diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 6c5a40ba8c..2603192fea 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -32,9 +32,13 @@ #include "common/logging/log.h" #include "common/string_util.h" +#include "common/swap.h" #include "core/arm/arm_interface.h" #include "core/core.h" +#include "core/core_cpu.h" #include "core/gdbstub/gdbstub.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/scheduler.h" #include "core/loader/loader.h" #include "core/memory.h" @@ -137,15 +141,17 @@ static u8 command_buffer[GDB_BUFFER_SIZE]; static u32 command_length; static u32 latest_signal = 0; -static bool step_break = false; static bool memory_break = false; +static Kernel::Thread* current_thread = nullptr; + // Binding to a port within the reserved ports range (0-1023) requires root permissions, // so default to a port outside of that range. static u16 gdbstub_port = 24689; static bool halt_loop = true; static bool step_loop = false; +static bool send_trap = false; // If set to false, the server will never be started and no // gdbstub-related functions will be executed. @@ -165,6 +171,53 @@ static std::map breakpoints_execute; static std::map breakpoints_read; static std::map breakpoints_write; +static Kernel::Thread* FindThreadById(int id) { + for (int core = 0; core < Core::NUM_CPU_CORES; core++) { + auto threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); + for (auto thread : threads) { + if (thread->GetThreadId() == id) { + current_thread = thread.get(); + return current_thread; + } + } + } + return nullptr; +} + +static u64 RegRead(int id, Kernel::Thread* thread = nullptr) { + if (!thread) { + return 0; + } + + if (id < SP_REGISTER) { + return thread->context.cpu_registers[id]; + } else if (id == SP_REGISTER) { + return thread->context.sp; + } else if (id == PC_REGISTER) { + return thread->context.pc; + } else if (id == CPSR_REGISTER) { + return thread->context.cpsr; + } else { + return 0; + } +} + +static void RegWrite(int id, u64 val, Kernel::Thread* thread = nullptr) { + if (!thread) { + return; + } + + if (id < SP_REGISTER) { + thread->context.cpu_registers[id] = val; + } else if (id == SP_REGISTER) { + thread->context.sp = val; + } else if (id == PC_REGISTER) { + thread->context.pc = val; + } else if (id == CPSR_REGISTER) { + thread->context.cpsr = val; + } +} + /** * Turns hex string character into the equivalent byte. * @@ -193,7 +246,7 @@ static u8 NibbleToHex(u8 n) { if (n < 0xA) { return '0' + n; } else { - return 'A' + n - 0xA; + return 'a' + n - 0xA; } } @@ -439,6 +492,8 @@ static void SendReply(const char* reply) { return; } + NGLOG_DEBUG(Debug_GDBStub, "Reply: {}", reply); + memset(command_buffer, 0, sizeof(command_buffer)); command_length = static_cast(strlen(reply)); @@ -483,6 +538,22 @@ static void HandleQuery() { } else if (strncmp(query, "Xfer:features:read:target.xml:", strlen("Xfer:features:read:target.xml:")) == 0) { SendReply(target_xml); + } else if (strncmp(query, "Offsets", strlen("Offsets")) == 0) { + std::string buffer = fmt::format("TextSeg={:0x}", Memory::PROCESS_IMAGE_VADDR); + SendReply(buffer.c_str()); + } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) { + std::string val = "m"; + for (int core = 0; core < Core::NUM_CPU_CORES; core++) { + auto threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); + for (auto thread : threads) { + val += fmt::format("{:x}", thread->GetThreadId()); + val += ","; + } + } + val.pop_back(); + SendReply(val.c_str()); + } else if (strncmp(query, "sThreadInfo", strlen("sThreadInfo")) == 0) { + SendReply("l"); } else { SendReply(""); } @@ -490,11 +561,40 @@ static void HandleQuery() { /// Handle set thread command from gdb client. static void HandleSetThread() { - if (memcmp(command_buffer, "Hg0", 3) == 0 || memcmp(command_buffer, "Hc-1", 4) == 0 || - memcmp(command_buffer, "Hc0", 4) == 0 || memcmp(command_buffer, "Hc1", 4) == 0) { - return SendReply("OK"); + if (memcmp(command_buffer, "Hc", 2) == 0 || memcmp(command_buffer, "Hg", 2) == 0) { + int thread_id = -1; + if (command_buffer[2] != '-') { + thread_id = static_cast(HexToInt( + command_buffer + 2, + command_length - 2 /*strlen(reinterpret_cast(command_buffer) + 2)*/)); + } + if (thread_id >= 1) { + current_thread = FindThreadById(thread_id); + } + if (!current_thread) { + thread_id = 1; + current_thread = FindThreadById(thread_id); + } + if (current_thread) { + SendReply("OK"); + return; + } } + SendReply("E01"); +} +/// Handle thread alive command from gdb client. +static void HandleThreadAlive() { + int thread_id = static_cast( + HexToInt(command_buffer + 1, + command_length - 1 /*strlen(reinterpret_cast(command_buffer) + 1)*/)); + if (thread_id == 0) { + thread_id = 1; + } + if (FindThreadById(thread_id)) { + SendReply("OK"); + return; + } SendReply("E01"); } @@ -503,15 +603,24 @@ static void HandleSetThread() { * * @param signal Signal to be sent to client. */ -static void SendSignal(u32 signal) { +static void SendSignal(Kernel::Thread* thread, u32 signal, bool full = true) { if (gdbserver_socket == -1) { return; } latest_signal = signal; - std::string buffer = fmt::format("T{:02x}", latest_signal); - NGLOG_DEBUG(Debug_GDBStub, "Response: {}", buffer); + std::string buffer; + if (full) { + buffer = fmt::format("T{:02x}{:02x}:{:016x};{:02x}:{:016x};", latest_signal, PC_REGISTER, + Common::swap64(RegRead(PC_REGISTER, thread)), SP_REGISTER, + Common::swap64(RegRead(SP_REGISTER, thread))); + } else { + buffer = fmt::format("T{:02x};", latest_signal); + } + + buffer += fmt::format("thread:{:x};", thread->GetThreadId()); + SendReply(buffer.c_str()); } @@ -527,7 +636,7 @@ static void ReadCommand() { } else if (c == 0x03) { NGLOG_INFO(Debug_GDBStub, "gdb: found break command"); halt_loop = true; - SendSignal(SIGTRAP); + SendSignal(current_thread, SIGTRAP); return; } else if (c != GDB_STUB_START) { NGLOG_DEBUG(Debug_GDBStub, "gdb: read invalid byte {:02X}", c); @@ -598,11 +707,11 @@ static void ReadRegister() { } if (id <= SP_REGISTER) { - LongToGdbHex(reply, Core::CurrentArmInterface().GetReg(static_cast(id))); + LongToGdbHex(reply, RegRead(id, current_thread)); } else if (id == PC_REGISTER) { - LongToGdbHex(reply, Core::CurrentArmInterface().GetPC()); + LongToGdbHex(reply, RegRead(id, current_thread)); } else if (id == CPSR_REGISTER) { - IntToGdbHex(reply, Core::CurrentArmInterface().GetCPSR()); + IntToGdbHex(reply, (u32)RegRead(id, current_thread)); } else { return SendReply("E01"); } @@ -618,16 +727,16 @@ static void ReadRegisters() { u8* bufptr = buffer; for (int reg = 0; reg <= SP_REGISTER; reg++) { - LongToGdbHex(bufptr + reg * 16, Core::CurrentArmInterface().GetReg(reg)); + LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread)); } bufptr += (32 * 16); - LongToGdbHex(bufptr, Core::CurrentArmInterface().GetPC()); + LongToGdbHex(bufptr, RegRead(PC_REGISTER, current_thread)); bufptr += 16; - IntToGdbHex(bufptr, Core::CurrentArmInterface().GetCPSR()); + IntToGdbHex(bufptr, (u32)RegRead(CPSR_REGISTER, current_thread)); bufptr += 8; @@ -646,11 +755,11 @@ static void WriteRegister() { } if (id <= SP_REGISTER) { - Core::CurrentArmInterface().SetReg(id, GdbHexToLong(buffer_ptr)); + RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); } else if (id == PC_REGISTER) { - Core::CurrentArmInterface().SetPC(GdbHexToLong(buffer_ptr)); + RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); } else if (id == CPSR_REGISTER) { - Core::CurrentArmInterface().SetCPSR(GdbHexToInt(buffer_ptr)); + RegWrite(id, GdbHexToInt(buffer_ptr), current_thread); } else { return SendReply("E01"); } @@ -667,11 +776,11 @@ static void WriteRegisters() { for (int i = 0, reg = 0; reg <= CPSR_REGISTER; i++, reg++) { if (reg <= SP_REGISTER) { - Core::CurrentArmInterface().SetReg(reg, GdbHexToLong(buffer_ptr + i * 16)); + RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread); } else if (reg == PC_REGISTER) { - Core::CurrentArmInterface().SetPC(GdbHexToLong(buffer_ptr + i * 16)); + RegWrite(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread); } else if (reg == CPSR_REGISTER) { - Core::CurrentArmInterface().SetCPSR(GdbHexToInt(buffer_ptr + i * 16)); + RegWrite(CPSR_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread); } else { UNIMPLEMENTED(); } @@ -734,7 +843,7 @@ static void WriteMemory() { void Break(bool is_memory_break) { if (!halt_loop) { halt_loop = true; - SendSignal(SIGTRAP); + send_trap = true; } memory_break = is_memory_break; @@ -744,10 +853,10 @@ void Break(bool is_memory_break) { static void Step() { step_loop = true; halt_loop = true; - step_break = true; - SendSignal(SIGTRAP); + send_trap = true; } +/// Tell the CPU if we hit a memory breakpoint. bool IsMemoryBreak() { if (IsConnected()) { return false; @@ -759,7 +868,6 @@ bool IsMemoryBreak() { /// Tell the CPU to continue executing. static void Continue() { memory_break = false; - step_break = false; step_loop = false; halt_loop = false; } @@ -898,7 +1006,7 @@ void HandlePacket() { HandleSetThread(); break; case '?': - SendSignal(latest_signal); + SendSignal(current_thread, latest_signal); break; case 'k': Shutdown(); @@ -935,6 +1043,9 @@ void HandlePacket() { case 'Z': AddBreakpoint(); break; + case 'T': + HandleThreadAlive(); + break; default: SendReply(""); break; @@ -1079,4 +1190,11 @@ bool GetCpuStepFlag() { void SetCpuStepFlag(bool is_step) { step_loop = is_step; } + +void SendTrap(Kernel::Thread* thread, int trap) { + if (send_trap) { + send_trap = false; + SendSignal(thread, trap); + } +} }; // namespace GDBStub diff --git a/src/core/gdbstub/gdbstub.h b/src/core/gdbstub/gdbstub.h index 201fca0957..f2418c9e44 100644 --- a/src/core/gdbstub/gdbstub.h +++ b/src/core/gdbstub/gdbstub.h @@ -7,6 +7,7 @@ #pragma once #include "common/common_types.h" +#include "core/hle/kernel/thread.h" namespace GDBStub { @@ -91,4 +92,12 @@ bool GetCpuStepFlag(); * @param is_step */ void SetCpuStepFlag(bool is_step); + +/** + * Send trap signal from thread back to the gdbstub server. + * + * @param thread Sending thread. + * @param trap Trap no. + */ +void SendTrap(Kernel::Thread* thread, int trap); } // namespace GDBStub From be09dfeed9da7a55d52f0a1b0e45068d2a165ef4 Mon Sep 17 00:00:00 2001 From: greggameplayer <33609333+greggameplayer@users.noreply.github.com> Date: Wed, 6 Jun 2018 15:31:17 +0200 Subject: [PATCH 11/12] nvdrv/devices/nvidia_ctrl_gpu : add IoctlCommands with their params (#524) * add IoctlCommands with their params in nvidia_ctrl_gpu.h * add function related to the changes done previously * fix clang-format * delete trailing whitespace * correct mistake --- .../service/nvdrv/devices/nvhost_ctrl_gpu.cpp | 22 +++++++++++++ .../service/nvdrv/devices/nvhost_ctrl_gpu.h | 31 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index a9538ff435..0abc0de834 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -26,6 +26,10 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector& input, std::vec return ZCullGetInfo(input, output); case IoctlCommand::IocZbcSetTable: return ZBCSetTable(input, output); + case IoctlCommand::IocZbcQueryTable: + return ZBCQueryTable(input, output); + case IoctlCommand::IocFlushL2: + return FlushL2(input, output); } UNIMPLEMENTED_MSG("Unimplemented ioctl"); return 0; @@ -136,4 +140,22 @@ u32 nvhost_ctrl_gpu::ZBCSetTable(const std::vector& input, std::vector& return 0; } +u32 nvhost_ctrl_gpu::ZBCQueryTable(const std::vector& input, std::vector& output) { + NGLOG_WARNING(Service_NVDRV, "(STUBBED) called"); + IoctlZbcQueryTable params{}; + std::memcpy(¶ms, input.data(), input.size()); + // TODO : To implement properly + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_ctrl_gpu::FlushL2(const std::vector& input, std::vector& output) { + NGLOG_WARNING(Service_NVDRV, "(STUBBED) called"); + IoctlFlushL2 params{}; + std::memcpy(¶ms, input.data(), input.size()); + // TODO : To implement properly + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index 1d5ba2e672..f09113e678 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h @@ -26,6 +26,18 @@ private: IocZcullGetCtxSizeCommand = 0x80044701, IocZcullGetInfo = 0x80284702, IocZbcSetTable = 0x402C4703, + IocZbcQueryTable = 0xC0344704, + IocFlushL2 = 0x40084707, + IocInvalICache = 0x4008470D, + IocSetMmudebugMode = 0x4008470E, + IocSetSmDebugMode = 0x4010470F, + IocWaitForPause = 0xC0084710, + IocGetTcpExceptionEnStatus = 0x80084711, + IocNumVsms = 0x80084712, + IocVsmsMapping = 0xC0044713, + IocGetErrorChannelUserData = 0xC008471B, + IocGetGpuTime = 0xC010471C, + IocGetCpuTimeCorrelationInfo = 0xC108471D, }; struct IoctlGpuCharacteristics { @@ -127,12 +139,31 @@ private: }; static_assert(sizeof(IoctlZbcSetTable) == 44, "IoctlZbcSetTable is incorrect size"); + struct IoctlZbcQueryTable { + u32_le color_ds[4]; + u32_le color_l2[4]; + u32_le depth; + u32_le ref_cnt; + u32_le format; + u32_le type; + u32_le index_size; + }; + static_assert(sizeof(IoctlZbcQueryTable) == 52, "IoctlZbcQueryTable is incorrect size"); + + struct IoctlFlushL2 { + u32_le flush; // l2_flush | l2_invalidate << 1 | fb_flush << 2 + u32_le reserved; + }; + static_assert(sizeof(IoctlFlushL2) == 8, "IoctlFlushL2 is incorrect size"); + u32 GetCharacteristics(const std::vector& input, std::vector& output); u32 GetTPCMasks(const std::vector& input, std::vector& output); u32 GetActiveSlotMask(const std::vector& input, std::vector& output); u32 ZCullGetCtxSize(const std::vector& input, std::vector& output); u32 ZCullGetInfo(const std::vector& input, std::vector& output); u32 ZBCSetTable(const std::vector& input, std::vector& output); + u32 ZBCQueryTable(const std::vector& input, std::vector& output); + u32 FlushL2(const std::vector& input, std::vector& output); }; } // namespace Service::Nvidia::Devices From 2f61f8ff13a4ac6ad451f98c0c62c3842617472b Mon Sep 17 00:00:00 2001 From: Jarek Syrylak Date: Wed, 6 Jun 2018 15:05:23 +0100 Subject: [PATCH 12/12] Only send traps from GDB's current thread, otherwise we will always be thrown back to main thread when stepping. --- src/core/gdbstub/gdbstub.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 2603192fea..10755abc1e 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -144,6 +144,7 @@ static u32 latest_signal = 0; static bool memory_break = false; static Kernel::Thread* current_thread = nullptr; +static int thread_id = -1; // Binding to a port within the reserved ports range (0-1023) requires root permissions, // so default to a port outside of that range. @@ -562,11 +563,9 @@ static void HandleQuery() { /// Handle set thread command from gdb client. static void HandleSetThread() { if (memcmp(command_buffer, "Hc", 2) == 0 || memcmp(command_buffer, "Hg", 2) == 0) { - int thread_id = -1; + thread_id = -1; if (command_buffer[2] != '-') { - thread_id = static_cast(HexToInt( - command_buffer + 2, - command_length - 2 /*strlen(reinterpret_cast(command_buffer) + 2)*/)); + thread_id = static_cast(HexToInt(command_buffer + 2, command_length - 2)); } if (thread_id >= 1) { current_thread = FindThreadById(thread_id); @@ -585,9 +584,7 @@ static void HandleSetThread() { /// Handle thread alive command from gdb client. static void HandleThreadAlive() { - int thread_id = static_cast( - HexToInt(command_buffer + 1, - command_length - 1 /*strlen(reinterpret_cast(command_buffer) + 1)*/)); + int thread_id = static_cast(HexToInt(command_buffer + 1, command_length - 1)); if (thread_id == 0) { thread_id = 1; } @@ -1192,7 +1189,9 @@ void SetCpuStepFlag(bool is_step) { } void SendTrap(Kernel::Thread* thread, int trap) { - if (send_trap) { + //NGLOG_ERROR(Debug_GDBStub, "SendTrap {} {} {} {}", thread->GetThreadId(), thread_id, trap, + // send_trap); + if (send_trap && (thread == current_thread)) { send_trap = false; SendSignal(thread, trap); }