c# side to jag debugging improvements

This commit is contained in:
CasualPokePlayer 2022-09-24 04:15:05 -07:00
parent 740cd1f8d4
commit c9618b3f92
6 changed files with 309 additions and 37 deletions

View File

@ -8,20 +8,167 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
{
public partial class JaguarDisassembler : VerifiedDisassembler
{
private readonly MC68000 _disassembler = new();
private readonly MC68000 _m68kDisassembler = new();
public override string PCRegisterName => "PC";
public override IEnumerable<string> AvailableCpus => new[] { "M68000" };
public override string Disassemble(MemoryDomain m, uint addr, out int length)
public string DisassembleM68K(MemoryDomain m, uint addr, out int length)
{
_disassembler.ReadByte = a => (sbyte)m.PeekByte(a);
_disassembler.ReadWord = a => (short)m.PeekUshort(a, true);
_disassembler.ReadLong = a => (int)m.PeekUint(a, true);
var info = _disassembler.Disassemble((int)addr);
_m68kDisassembler.ReadByte = a => (sbyte)m.PeekByte(a);
_m68kDisassembler.ReadWord = a => (short)m.PeekUshort(a, true);
_m68kDisassembler.ReadLong = a => (int)m.PeekUint(a, true);
var info = _m68kDisassembler.Disassemble((int)addr);
length = info.Length;
return $"{info.RawBytes.Substring(0, 4):X4} {info.Mnemonic,-7} {info.Args}";
}
// TOM and JERRY RISC processors are mostly similar, only 6 instructions differ
// most of this is taken from virtualjaguar's dasmjag function
public string DisassembleRISC(bool gpu, MemoryDomain m, uint addr, out int length)
{
var opcode = m.PeekUshort(addr, true);
var arg1 = (opcode >> 5) & 0x1F;
var arg2 = opcode & 0x1F;
length = (opcode >> 10) == 0x26 ? 6 : 2;
string argRR() => $"r{arg1:d02}, r{arg2:d02}";
string argCZIR() => $"${(arg1 == 0 ? 32 : arg1):X02}, r{arg2:d02}";
string argIR() => $"${arg1:X02}, r{arg2:d02}";
string argR2() => $"r{arg2:d02}";
string argSR()
{
var s1 = (short)(arg1 << 11) >> 11;
if (s1 < 0)
{
return $"-${-s1:X02}, r{arg2:d02}";
}
else
{
return $"${s1:X02}, r{arg2:d02}";
}
}
string argDRR() => $"(r{arg1:d02}), r{arg2:d02}";
string argRDR() => $"r{arg2:d02}, (r{arg1:d02})";
string argDROR(int r) => $"(r{r:d02} + ${(arg1 == 0 ? 128 : arg1 * 4):X02}), r{arg2:d02}";
string argRDRO(int r) => $"r{arg2:d02}, (r{r:d02} + ${(arg1 == 0 ? 128 : arg1 * 4):X02})";
string argDRORR(int r) => $"(r{r:d02} + r{arg1:d02}), r{arg2:d02}";
string argRDROR(int r) => $"r{arg1:d02}, (r{r:d02} + r{arg2:d02})";
string argCC()
{
return arg2 switch
{
0x00 => "",
0x01 => "nz, ",
0x02 => "z, ",
0x04 => "nc, ",
0x05 => "nc nz, ",
0x06 => "nc z, ",
0x08 => "c, ",
0x09 => "c nz, ",
0x0A => "c z, ",
0x14 => "nn, ",
0x15 => "nn nz, ",
0x16 => "nn z, ",
0x18 => "n, ",
0x19 => "n nz, ",
0x1A => "n z, ",
0x1F => "never, ",
_ => "???, ",
};
}
return (opcode >> 10) switch
{
0x00 => $"add {argRR()}",
0x01 => $"addc {argRR()}",
0x02 => $"addq {argCZIR()}",
0x03 => $"addqt {argCZIR()}",
0x04 => $"sub {argRR()}",
0x05 => $"subc {argRR()}",
0x06 => $"subq {argCZIR()}",
0x07 => $"subqt {argCZIR()}",
0x08 => $"neg {argR2()}",
0x09 => $"and {argRR()}",
0x0A => $"or {argRR()}",
0x0B => $"xor {argRR()}",
0x0C => $"not {argR2()}",
0x0D => $"btet {argIR()}",
0x0E => $"bset {argIR()}",
0x0F => $"bclr {argIR()}",
0x10 => $"mult {argRR()}",
0x11 => $"imult {argRR()}",
0x12 => $"imultn {argRR()}",
0x13 => $"resmac {argR2()}",
0x14 => $"imacn {argRR()}",
0x15 => $"div {argRR()}",
0x16 => $"abs {argR2()}",
0x17 => $"sh {argRR()}",
0x18 => $"shlq ${32 - arg1:X02}, {argR2()}",
0x19 => $"shrq {argCZIR()}",
0x1A => $"sha {argRR()}",
0x1B => $"sharq {argCZIR()}",
0x1C => $"ror {argRR()}",
0x1D => $"rorq {argCZIR()}",
0x1E => $"cmp {argRR()}",
0x1F => $"cmpq {argSR()}",
0x20 => gpu ? $"sat8 {argR2()}" : $"subqmod {argCZIR()}",
0x21 => $"{(gpu ? "sat16" : "sub16s")} {argR2()}",
0x22 => $"move {argRR()}",
0x23 => $"moveq {argIR()}",
0x24 => $"moveta {argRR()}",
0x25 => $"movefa {argRR()}",
0x26 => $"movei ${m.PeekUshort(addr + 2, true) | (m.PeekUshort(addr + 4, true) << 16):X04} {argR2()}",
0x27 => $"loadb {argDRR()}",
0x28 => $"loadw {argDRR()}",
0x29 => $"load {argDRR()}",
0x2A => gpu ? $"loadp {argDRR()}" : $"sat32s {argR2()}",
0x2B => $"load {argDROR(14)}",
0x2C => $"load {argDROR(15)}",
0x2D => $"storeb {argRDR()}",
0x2E => $"storew {argRDR()}",
0x2F => $"store {argRDR()}",
0x30 => gpu ? $"storep {argRDR()}" : $"mirror {argR2()}",
0x31 => $"store {argRDRO(14)}",
0x32 => $"store {argRDRO(15)}",
0x33 => $"move pc, {argR2()}",
0x34 => $"jump {argCC()}(r{arg1:d02})",
0x35 => $"jr {argCC()}${addr + 2 + ((sbyte)(arg1 << 3) >> 2):X02}",
0x36 => $"mmult {argRR()}",
0x37 => $"mtoi {argRR()}",
0x38 => $"normi {argRR()}",
0x39 => $"nop",
0x3A => $"load {argDRORR(14)}",
0x3B => $"load {argDRORR(15)}",
0x3C => $"store {argRDROR(14)}",
0x3D => $"store {argRDROR(15)}",
0x3E => gpu ? $"sat24 {argR2()}" : $"illegal [{arg1:d02}, {arg2:d02}]",
0x3F => gpu ? $"{(arg1 == 0 ? "pack" : "unpack")} {argR2()}" : $"addqmod {argCZIR()}",
_ => throw new InvalidOperationException(),
};
}
public override string PCRegisterName => Cpu switch
{
"M68000" => "M68K PC",
"TOM" => "GPU PC",
"JERRY" => "DSP PC",
_ => throw new InvalidOperationException(),
};
public override IEnumerable<string> AvailableCpus => new[]
{
"M68000",
"TOM",
"JERRY",
};
public override string Disassemble(MemoryDomain m, uint addr, out int length)
{
return Cpu switch
{
"M68000" => DisassembleM68K(m, addr, out length),
"TOM" => DisassembleRISC(true, m, addr, out length),
"JERRY" => DisassembleRISC(false, m, addr, out length),
_ => throw new InvalidOperationException(),
};
}
}
}

View File

@ -110,15 +110,18 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
public abstract void SetMemoryCallback(int which, MemoryCallback callback);
[UnmanagedFunctionPointer(CC)]
public delegate void TraceCallback(IntPtr regs);
public delegate void M68KTraceCallback(IntPtr regs);
[UnmanagedFunctionPointer(CC)]
public delegate void RISCTraceCallback(uint pc, IntPtr regs);
[BizImport(CC)]
public abstract void SetTraceCallback(TraceCallback callback);
public abstract void SetTraceCallbacks(M68KTraceCallback cpuTraceCallback, RISCTraceCallback gpuTraceCallback, RISCTraceCallback dspTraceCallback);
[BizImport(CC)]
public abstract void GetRegisters(uint[] regs);
public abstract void GetRegisters(IntPtr regs);
public enum M68KRegisters : uint
public enum M68KRegisters : int
{
D0, D1, D2, D3, D4, D5, D6, D7,
A0, A1, A2, A3, A4, A5, A6, A7,
@ -126,6 +129,6 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
}
[BizImport(CC)]
public abstract void SetRegister(M68KRegisters which, int val);
public abstract void SetRegister(int which, int val);
}
}

View File

@ -7,24 +7,69 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
{
partial class VirtualJaguar : IDebuggable
{
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
public unsafe IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
{
uint[] regs = new uint[18];
_core.GetRegisters(regs);
// 148 registers, oh my
uint* regs = stackalloc uint[18 + 32 + 32 + 32 + 32 + 2];
_core.GetRegisters((IntPtr)regs);
var ret = new Dictionary<string, RegisterValue>();
for (int i = 0; i < 8; i++)
{
ret[$"D{i}"] = regs[i];
ret[$"A{i}"] = regs[8 + i];
ret[$"M68K D{i}"] = regs[i];
ret[$"M68K A{i}"] = regs[8 + i];
}
ret["PC"] = regs[16];
ret["SR"] = regs[17];
ret["M68K PC"] = regs[16];
ret["M68K SR"] = regs[17];
for (int i = 0; i < 64; i++)
{
// registers aren't really 0-63, but it's two banks of 32 registers
ret[$"GPU R{i}"] = regs[18 + i];
ret[$"DSP R{i}"] = regs[82 + i];
}
ret["GPU PC"] = regs[146];
ret["DSP PC"] = regs[147];
return ret;
}
public void SetCpuRegister(string register, int value)
=> _core.SetRegister((LibVirtualJaguar.M68KRegisters)Enum.Parse(typeof(LibVirtualJaguar.M68KRegisters), register.ToUpperInvariant()), value);
{
register = register.ToUpperInvariant();
if (register.StartsWith("M68K "))
{
var reg = Enum.Parse(typeof(LibVirtualJaguar.M68KRegisters), register.Remove(0, 5));
_core.SetRegister((int)reg, value);
}
else if (register.StartsWith("GPU ") || register.StartsWith("DSP "))
{
bool gpu = register.StartsWith("GPU ");
var regName = register.Remove(0, 4);
if (regName == "PC")
{
_core.SetRegister(gpu ? 146 : 147, value);
}
else if (regName.StartsWith("R"))
{
var offset = gpu ? 18 : 82;
var reg = int.Parse(regName.Remove(0, 1));
if (reg > 63)
{
throw new ArgumentException("Invalid register", nameof(register));
}
_core.SetRegister(offset + reg, value);
}
else
{
throw new ArgumentException("Invalid register", nameof(register));
}
}
else
{
throw new ArgumentException("Invalid register", nameof(register));
}
}
public bool CanStep(StepType type)
=> false;

View File

@ -5,15 +5,19 @@ using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Atari.Jaguar
{
public partial class VirtualJaguar : ISettable<object, VirtualJaguar.VirtualJaguarSyncSettings>
public partial class VirtualJaguar : ISettable<VirtualJaguar.VirtualJaguarSettings, VirtualJaguar.VirtualJaguarSyncSettings>
{
private VirtualJaguarSettings _settings;
private VirtualJaguarSyncSettings _syncSettings;
public object GetSettings()
=> null;
public VirtualJaguarSettings GetSettings()
=> _settings.Clone();
public PutSettingsDirtyBits PutSettings(object o)
=> PutSettingsDirtyBits.None;
public PutSettingsDirtyBits PutSettings(VirtualJaguarSettings o)
{
_settings = o;
return PutSettingsDirtyBits.None;
}
public VirtualJaguarSyncSettings GetSyncSettings()
=> _syncSettings.Clone();
@ -25,6 +29,30 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
return ret ? PutSettingsDirtyBits.RebootCore : PutSettingsDirtyBits.None;
}
public class VirtualJaguarSettings
{
[DisplayName("Trace M68K (CPU)")]
[Description("")]
[DefaultValue(true)]
public bool TraceCPU { get; set; }
[DisplayName("Trace TOM (GPU)")]
[Description("")]
[DefaultValue(false)]
public bool TraceGPU { get; set; }
[DisplayName("Trace JERRY (DSP)")]
[Description("")]
[DefaultValue(false)]
public bool TraceDSP { get; set; }
public VirtualJaguarSettings()
=> SettingsUtil.SetDefaultValues(this);
public VirtualJaguarSettings Clone()
=> (VirtualJaguarSettings)MemberwiseClone();
}
public class VirtualJaguarSyncSettings
{
[DisplayName("Player 1 Controller Connected")]
@ -43,7 +71,7 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
public bool NTSC { get; set; }
[DisplayName("Skip BIOS")]
[Description("BIOS file must still be present")]
[Description("BIOS file must still be present. Ignored (set to true) for Jaguar CD")]
[DefaultValue(true)]
public bool SkipBIOS { get; set; }

View File

@ -7,13 +7,15 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
public partial class VirtualJaguar
{
private ITraceable Tracer { get; }
private readonly LibVirtualJaguar.TraceCallback _traceCallback;
private readonly LibVirtualJaguar.M68KTraceCallback _cpuTraceCallback;
private readonly LibVirtualJaguar.RISCTraceCallback _gpuTraceCallback;
private readonly LibVirtualJaguar.RISCTraceCallback _dspTraceCallback;
private unsafe void MakeTrace(IntPtr r)
private unsafe void MakeCPUTrace(IntPtr r)
{
uint* regs = (uint*)r;
var pc = regs[16] & 0xFFFFFF;
var disasm = _disassembler.Disassemble(this.AsMemoryDomains().SystemBus, pc, out _);
var disasm = _disassembler.DisassembleM68K(this.AsMemoryDomains().SystemBus, pc, out _);
var regInfo = string.Empty;
for (int i = 0; i < 8; i++)
{
@ -31,6 +33,37 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
(sr & 4) > 0 ? "Z" : "z",
(sr & 2) > 0 ? "V" : "v",
(sr & 1) > 0 ? "C" : "c");
regInfo += " (M68K)";
Tracer.Put(new(disassembly: $"{pc:X6}: {disasm}".PadRight(50), registerInfo: regInfo));
}
private unsafe void MakeGPUTrace(uint pc, IntPtr r)
{
uint* regs = (uint*)r;
pc &= 0xFFFFFF;
var disasm = _disassembler.DisassembleRISC(true, this.AsMemoryDomains().SystemBus, pc, out _);
var regInfo = string.Empty;
for (int i = 0; i < 32; i++)
{
regInfo += $"r{i}:{regs[i]:X8} ";
}
regInfo += "(GPU)";
Tracer.Put(new(disassembly: $"{pc:X6}: {disasm}".PadRight(50), registerInfo: regInfo));
}
private unsafe void MakeDSPTrace(uint pc, IntPtr r)
{
uint* regs = (uint*)r;
pc &= 0xFFFFFF;
var disasm = _disassembler.DisassembleRISC(false, this.AsMemoryDomains().SystemBus, pc, out _);
var regInfo = string.Empty;
for (int i = 0; i < 32; i++)
{
regInfo += $"r{i}:{regs[i]:X8} ";
}
regInfo += "(DSP)";
Tracer.Put(new(disassembly: $"{pc:X6}: {disasm}".PadRight(50), registerInfo: regInfo));
}

View File

@ -18,7 +18,7 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
private readonly JaguarDisassembler _disassembler;
[CoreConstructor(VSystemID.Raw.JAG)]
public VirtualJaguar(CoreLoadParameters<object, VirtualJaguarSyncSettings> lp)
public VirtualJaguar(CoreLoadParameters<VirtualJaguarSettings, VirtualJaguarSyncSettings> lp)
: base(lp.Comm, new Configuration
{
DefaultWidth = 326,
@ -31,6 +31,7 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
SystemId = VSystemID.Raw.JAG,
})
{
_settings = lp.Settings ?? new();
_syncSettings = lp.SyncSettings ?? new();
ControllerDefinition = CreateControllerDefinition(_syncSettings.P1Active, _syncSettings.P2Active);
@ -38,7 +39,9 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
VsyncNumerator = _syncSettings.NTSC ? 60 : 50;
InitMemoryCallbacks();
_traceCallback = MakeTrace;
_cpuTraceCallback = MakeCPUTrace;
_gpuTraceCallback = MakeGPUTrace;
_dspTraceCallback = MakeDSPTrace;
_cdTocCallback = CDTOCCallback;
_cdReadCallback = CDReadCallback;
@ -52,7 +55,7 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
MmapHeapSizeKB = 64 * 1024,
SkipCoreConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxCoreConsistencyCheck),
SkipMemoryConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck),
}, new Delegate[] { _readCallback, _writeCallback, _execCallback, _traceCallback, _cdTocCallback, _cdReadCallback, });
}, new Delegate[] { _readCallback, _writeCallback, _execCallback, _cpuTraceCallback, _gpuTraceCallback, _dspTraceCallback, _cdTocCallback, _cdReadCallback, });
var bios = CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("Jaguar", "Bios"));
if (bios.Length != 0x20000)
@ -117,7 +120,9 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
_disassembler = new();
_serviceProvider.Register<IDisassemblable>(_disassembler);
const string TRACE_HEADER = "M68K: PC, machine code, mnemonic, operands, registers (D0-D7, A0-A7, SR), flags (XNZVC)";
// bleh
const string TRACE_HEADER = "M68K: PC, machine code, mnemonic, operands, registers (D0-D7, A0-A7, SR), flags (XNZVC)\r\n"
+ "GPU/DSP: PC, machine code, mnemonic, operands, registers (r0-r32)";
Tracer = new TraceBuffer(TRACE_HEADER);
_serviceProvider.Register(Tracer);
}
@ -198,7 +203,18 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound)
{
_core.SetTraceCallback(Tracer.IsEnabled() ? _traceCallback : null);
if (Tracer.IsEnabled())
{
_core.SetTraceCallbacks(
_settings.TraceCPU ? _cpuTraceCallback : null,
_settings.TraceGPU ? _gpuTraceCallback : null,
_settings.TraceDSP ? _dspTraceCallback : null);
}
else
{
_core.SetTraceCallbacks(null, null, null);
}
DriveLightOn = false;
return new LibVirtualJaguar.FrameInfo()