104 lines
3.6 KiB
C#
104 lines
3.6 KiB
C#
// CpuTrace — ring buffer storing last N executed instructions with register snapshots
|
|
// Used for crash/corruption analysis in the WC emulator
|
|
using System;
|
|
using System.Text;
|
|
|
|
namespace InflateDebugger
|
|
{
|
|
/// <summary>Snapshot of CPU state at one instruction</summary>
|
|
public struct TraceEntry
|
|
{
|
|
public long Tact;
|
|
public ushort PC;
|
|
public byte Opcode; // first byte of the instruction
|
|
public ushort AF, BC, DE, HL;
|
|
public ushort AF2, BC2, DE2, HL2; // shadow regs
|
|
public ushort IX, IY, SP;
|
|
public byte Win0, Win1, Win2, Win3; // page window mapping
|
|
|
|
// Optional: what was written (0xFFFF = no write)
|
|
public ushort WriteAddr;
|
|
public byte WriteVal;
|
|
|
|
// WC API trap info (0xFF = not a WC call)
|
|
public byte WcFuncNum;
|
|
|
|
public override string ToString()
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendFormat("[{0,10}] PC={1:X4} op={2:X2}", Tact, PC, Opcode);
|
|
sb.AppendFormat(" AF={0:X4} BC={1:X4} DE={2:X4} HL={3:X4}", AF, BC, DE, HL);
|
|
sb.AppendFormat(" IX={0:X4} SP={1:X4}", IX, SP);
|
|
sb.AppendFormat(" W[{0:X2},{1:X2},{2:X2},{3:X2}]", Win0, Win1, Win2, Win3);
|
|
if (WcFuncNum != 0xFF)
|
|
sb.AppendFormat(" WC_CALL=0x{0:X2}", WcFuncNum);
|
|
if (WriteAddr != 0xFFFF)
|
|
sb.AppendFormat(" WR[{0:X4}]={1:X2}", WriteAddr, WriteVal);
|
|
return sb.ToString();
|
|
}
|
|
}
|
|
|
|
/// <summary>Fixed-size ring buffer of CPU trace entries</summary>
|
|
public class CpuTrace
|
|
{
|
|
private readonly TraceEntry[] _buf;
|
|
private readonly int _capacity;
|
|
private int _head; // next write position
|
|
private int _count; // total entries stored (capped at capacity)
|
|
|
|
public CpuTrace(int capacity = 200)
|
|
{
|
|
_capacity = capacity;
|
|
_buf = new TraceEntry[capacity];
|
|
_head = 0;
|
|
_count = 0;
|
|
}
|
|
|
|
public int Count => _count;
|
|
public int Capacity => _capacity;
|
|
|
|
/// <summary>Record a new trace entry (overwrites oldest when full)</summary>
|
|
public void Record(ref TraceEntry entry)
|
|
{
|
|
_buf[_head] = entry;
|
|
_head = (_head + 1) % _capacity;
|
|
if (_count < _capacity) _count++;
|
|
}
|
|
|
|
/// <summary>Get entry by age: 0 = most recent, 1 = one before, etc.</summary>
|
|
public TraceEntry Get(int age)
|
|
{
|
|
if (age < 0 || age >= _count)
|
|
throw new IndexOutOfRangeException($"age={age}, count={_count}");
|
|
int idx = (_head - 1 - age + _capacity * 2) % _capacity;
|
|
return _buf[idx];
|
|
}
|
|
|
|
/// <summary>Dump the last N entries (most recent first) to a string</summary>
|
|
public string Dump(int maxEntries = 0)
|
|
{
|
|
if (maxEntries <= 0) maxEntries = _count;
|
|
int n = Math.Min(maxEntries, _count);
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine($"=== CPU Trace (last {n} of {_count}) ===");
|
|
// Print oldest-first for readability
|
|
for (int i = n - 1; i >= 0; i--)
|
|
sb.AppendLine(Get(i).ToString());
|
|
return sb.ToString();
|
|
}
|
|
|
|
/// <summary>Dump trace to file</summary>
|
|
public void DumpToFile(string path, int maxEntries = 0)
|
|
{
|
|
System.IO.File.WriteAllText(path, Dump(maxEntries));
|
|
}
|
|
|
|
/// <summary>Clear all entries</summary>
|
|
public void Clear()
|
|
{
|
|
_head = 0;
|
|
_count = 0;
|
|
}
|
|
}
|
|
}
|