// 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 { /// Snapshot of CPU state at one instruction 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(); } } /// Fixed-size ring buffer of CPU trace entries 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; /// Record a new trace entry (overwrites oldest when full) public void Record(ref TraceEntry entry) { _buf[_head] = entry; _head = (_head + 1) % _capacity; if (_count < _capacity) _count++; } /// Get entry by age: 0 = most recent, 1 = one before, etc. 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]; } /// Dump the last N entries (most recent first) to a string 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(); } /// Dump trace to file public void DumpToFile(string path, int maxEntries = 0) { System.IO.File.WriteAllText(path, Dump(maxEntries)); } /// Clear all entries public void Clear() { _head = 0; _count = 0; } } }