diff --git a/src/ARMeilleure/State/DebugState.cs b/src/ARMeilleure/State/DebugState.cs
new file mode 100644
index 000000000..dd05ddb61
--- /dev/null
+++ b/src/ARMeilleure/State/DebugState.cs
@@ -0,0 +1,9 @@
+namespace ARMeilleure.State
+{
+ enum DebugState
+ {
+ Running,
+ Stopping,
+ Stopped,
+ }
+}
diff --git a/src/ARMeilleure/State/ExecutionContext.cs b/src/ARMeilleure/State/ExecutionContext.cs
index ce10a591c..2d57df725 100644
--- a/src/ARMeilleure/State/ExecutionContext.cs
+++ b/src/ARMeilleure/State/ExecutionContext.cs
@@ -1,5 +1,8 @@
using ARMeilleure.Memory;
using System;
+using System.Collections.Concurrent;
+using System.Diagnostics;
+using System.Threading;
namespace ARMeilleure.State
{
@@ -96,6 +99,14 @@ namespace ARMeilleure.State
private readonly ExceptionCallback _supervisorCallback;
private readonly ExceptionCallback _undefinedCallback;
+ internal int _debugState = (int)DebugState.Running;
+ internal int _shouldStep = 0;
+ internal ManualResetEvent _debugHalt = new ManualResetEvent(true);
+ internal Barrier _stepBarrier = new Barrier(2);
+
+ // This is only valid while debugging is enabled.
+ public ulong DebugPc;
+
public ExecutionContext(
IJitMemoryAllocator allocator,
ICounter counter,
@@ -145,6 +156,33 @@ namespace ARMeilleure.State
_interrupted = true;
}
+ public void DebugStop()
+ {
+ _debugHalt.Reset();
+ Interlocked.CompareExchange(ref _debugState, (int)DebugState.Stopping, (int)DebugState.Running);
+ }
+
+ public bool DebugStep()
+ {
+ if (_debugState != (int)DebugState.Stopped)
+ {
+ return false;
+ }
+
+ _shouldStep = 1;
+ _debugHalt.Set();
+ _stepBarrier.SignalAndWait();
+ _debugHalt.Reset();
+ _stepBarrier.SignalAndWait();
+
+ return true;
+ }
+
+ public void DebugContinue()
+ {
+ _debugHalt.Set();
+ }
+
internal void OnBreak(ulong address, int imm)
{
_breakCallback?.Invoke(this, address, imm);
diff --git a/src/Ryujinx.Cpu/AppleHv/DebugState.cs b/src/Ryujinx.Cpu/AppleHv/DebugState.cs
new file mode 100644
index 000000000..5a5f2ba03
--- /dev/null
+++ b/src/Ryujinx.Cpu/AppleHv/DebugState.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.Cpu.AppleHv
+{
+ enum DebugState
+ {
+ Running,
+ Stopping,
+ Stopped,
+ }
+}
diff --git a/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs b/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs
index fc2b76d15..8ce358898 100644
--- a/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs
+++ b/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs
@@ -127,6 +127,15 @@ namespace Ryujinx.Cpu.AppleHv
return Interlocked.Exchange(ref _interruptRequested, 0) != 0;
}
+ ///
+ public void DebugStop() => _impl.DebugStop();
+
+ ///
+ public bool DebugStep() => _impl.DebugStep();
+
+ ///
+ public void DebugContinue() => _impl.DebugContinue();
+
///
public void StopRunning()
{
diff --git a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs
index 6ce8e1800..b4e96b561 100644
--- a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs
+++ b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs
@@ -46,5 +46,27 @@ namespace Ryujinx.Cpu.AppleHv
{
_v[index] = value;
}
+
+ public void RequestInterrupt()
+ {
+ }
+
+ public void DebugStop()
+ {
+ }
+
+ public bool DebugStep()
+ {
+ return false;
+ }
+
+ public void DebugContinue()
+ {
+ }
+
+ public bool GetAndClearInterruptRequested()
+ {
+ return false;
+ }
}
}
diff --git a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs
index bb232940d..a55a2aba2 100644
--- a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs
+++ b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs
@@ -2,11 +2,10 @@ using ARMeilleure.State;
using Ryujinx.Memory;
using System;
using System.Runtime.InteropServices;
-using System.Runtime.Versioning;
+using System.Threading;
namespace Ryujinx.Cpu.AppleHv
{
- [SupportedOSPlatform("macos")]
class HvExecutionContextVcpu : IHvExecutionContext
{
private static readonly MemoryBlock _setSimdFpRegFuncMem;
@@ -14,6 +13,14 @@ namespace Ryujinx.Cpu.AppleHv
private static readonly SetSimdFpReg _setSimdFpReg;
private static readonly IntPtr _setSimdFpRegNativePtr;
+ internal int _debugState = (int)DebugState.Running;
+ internal int _shouldStep = 0;
+ internal ManualResetEvent _debugHalt = new ManualResetEvent(true);
+ internal Barrier _stepBarrier = new Barrier(2);
+
+ // This is only valid while debugging is enabled.
+ public ulong DebugPc;
+
static HvExecutionContextVcpu()
{
// .NET does not support passing vectors by value, so we need to pass a pointer and use a native
@@ -136,6 +143,7 @@ namespace Ryujinx.Cpu.AppleHv
}
private readonly ulong _vcpu;
+ private int _interruptRequested;
public HvExecutionContextVcpu(ulong vcpu)
{
@@ -181,8 +189,46 @@ namespace Ryujinx.Cpu.AppleHv
public void RequestInterrupt()
{
+ if (Interlocked.Exchange(ref _interruptRequested, 1) == 0)
+ {
+ ulong vcpu = _vcpu;
+ HvApi.hv_vcpus_exit(ref vcpu, 1);
+ }
+ }
+
+ public void DebugStop()
+ {
+ _debugHalt.Reset();
+ Interlocked.CompareExchange(ref _debugState, (int)DebugState.Stopping, (int)DebugState.Running);
ulong vcpu = _vcpu;
HvApi.hv_vcpus_exit(ref vcpu, 1);
}
+
+ public bool DebugStep()
+ {
+ if (_debugState != (int)DebugState.Stopped)
+ {
+ return false;
+ }
+
+ _shouldStep = 1;
+ _debugHalt.Set();
+ _stepBarrier.SignalAndWait();
+ _debugHalt.Reset();
+ _stepBarrier.SignalAndWait();
+
+ return true;
+ }
+
+ public void DebugContinue()
+ {
+ _debugHalt.Set();
+ HvApi.hv_vcpu_run(_vcpu);
+ }
+
+ public bool GetAndClearInterruptRequested()
+ {
+ return Interlocked.Exchange(ref _interruptRequested, 0) != 0;
+ }
}
}
diff --git a/src/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs b/src/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs
index 54b73acc6..b385cd78e 100644
--- a/src/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs
+++ b/src/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs
@@ -39,5 +39,13 @@ namespace Ryujinx.Cpu.AppleHv
SetV(i, context.GetV(i));
}
}
+
+ void RequestInterrupt();
+ bool GetAndClearInterruptRequested();
+
+ // TODO: comments
+ void DebugStop();
+ bool DebugStep();
+ void DebugContinue();
}
}
diff --git a/src/Ryujinx.Cpu/IExecutionContext.cs b/src/Ryujinx.Cpu/IExecutionContext.cs
index c38210800..ad07a2766 100644
--- a/src/Ryujinx.Cpu/IExecutionContext.cs
+++ b/src/Ryujinx.Cpu/IExecutionContext.cs
@@ -108,5 +108,10 @@ namespace Ryujinx.Cpu
/// If you only need to pause the thread temporarily, use instead.
///
void StopRunning();
+
+ // TODO: comments
+ void DebugStop();
+ bool DebugStep();
+ void DebugContinue();
}
}
diff --git a/src/Ryujinx.Cpu/Jit/JitExecutionContext.cs b/src/Ryujinx.Cpu/Jit/JitExecutionContext.cs
index f15486e68..a3224c366 100644
--- a/src/Ryujinx.Cpu/Jit/JitExecutionContext.cs
+++ b/src/Ryujinx.Cpu/Jit/JitExecutionContext.cs
@@ -109,6 +109,15 @@ namespace Ryujinx.Cpu.Jit
_impl.RequestInterrupt();
}
+ ///
+ public void DebugStop() => _impl.DebugStop();
+
+ ///
+ public bool DebugStep() => _impl.DebugStep();
+
+ ///
+ public void DebugContinue() => _impl.DebugContinue();
+
///
public void StopRunning()
{
diff --git a/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs b/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs
new file mode 100644
index 000000000..c86d4acd5
--- /dev/null
+++ b/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs
@@ -0,0 +1,12 @@
+using Ryujinx.Memory;
+
+namespace Ryujinx.HLE.Debugger
+{
+ public interface IDebuggableProcess
+ {
+ public void DebugStopAllThreads();
+ public ulong[] DebugGetThreadUids();
+ public Ryujinx.Cpu.IExecutionContext DebugGetThreadContext(ulong threadUid);
+ public IVirtualMemoryManager CpuMemory { get; }
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs
index 64b08e309..69086c0bc 100644
--- a/src/Ryujinx.HLE/HOS/Horizon.cs
+++ b/src/Ryujinx.HLE/HOS/Horizon.cs
@@ -473,5 +473,13 @@ namespace Ryujinx.HLE.HOS
}
IsPaused = pause;
}
+
+ public Debugger.IDebuggableProcess DebugGetApplicationProcess()
+ {
+ lock (KernelContext.Processes)
+ {
+ return KernelContext.Processes.Values.Where(x => x.IsApplication).First();
+ }
+ }
}
}
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
index 422f03c64..0c0004be5 100644
--- a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
+++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
@@ -14,7 +14,7 @@ using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
- class KProcess : KSynchronizationObject
+ class KProcess : KSynchronizationObject, Debugger.IDebuggableProcess
{
public const uint KernelVersionMajor = 10;
public const uint KernelVersionMinor = 4;
@@ -1175,5 +1175,32 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
return Capabilities.IsSvcPermitted(svcId);
}
+
+ public void DebugStopAllThreads()
+ {
+ lock (_threadingLock)
+ {
+ foreach (KThread thread in _threads)
+ {
+ thread.Context.DebugStop();
+ }
+ }
+ }
+
+ public ulong[] DebugGetThreadUids()
+ {
+ lock (_threadingLock)
+ {
+ return _threads.Select(x => x.ThreadUid).ToArray();
+ }
+ }
+
+ public Ryujinx.Cpu.IExecutionContext DebugGetThreadContext(ulong threadUid)
+ {
+ lock (_threadingLock)
+ {
+ return _threads.Where(x => x.ThreadUid == threadUid).FirstOrDefault()?.Context;
+ }
+ }
}
}
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs
index b8118fbb4..6050f88f2 100644
--- a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs
+++ b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs
@@ -31,6 +31,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
}
+ public void DebugStop()
+ {
+ }
+
+ public bool DebugStep()
+ {
+ return false;
+ }
+
+ public void DebugContinue()
+ {
+ }
+
public void StopRunning()
{
Running = false;