/* BSD 2-Clause License Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018 All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using D.CP; using D.IO; using D.IOP; using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Windows.Forms; namespace D.Debugger { public enum DebuggerReason { UserInvoked, Error, } public partial class DebuggerMain : Form { public DebuggerMain(DSystem system, DebuggerReason reason, string message) { InitializeComponent(); _reason = reason; _entryMessage = message; _system = system; } protected override void OnLoad(EventArgs e) { // // Pop up some debugger source windows. // _iopDebugger = new IOPDebugger(_system); _iopDebugger.Show(); _cpDebugger = new CPDebugger(_system); _cpDebugger.Show(); this.BringToFront(); switch(_reason) { case DebuggerReason.UserInvoked: WriteLine(_entryMessage); break; case DebuggerReason.Error: WriteLine(_entryMessage); PrintIOPStatus(); PrintCPStatus(); PrintMesaStatus(); break; } base.OnLoad(e); } protected override void OnKeyDown(KeyEventArgs e) { // We just intercept Ctrl+C here if (e.Control && e.KeyCode == Keys.C && _system.IsExecuting) { WriteLine("*user break*"); StopExecution(); DisplayCurrentCode(); e.Handled = true; } else { base.OnKeyDown(e); } } private void DebuggerInput_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) { if (e.KeyCode == Keys.Enter) { WriteLine(String.Format("> {0}", DebuggerInput.Text)); try { ExecuteCommand(DebuggerInput.Text); } catch(Exception ex) { WriteLine(ex.Message); } // Clear input line for next input DebuggerInput.Text = String.Empty; } } private void ExecuteCommand(string command) { if (string.IsNullOrWhiteSpace(command)) { command = _lastCommand; } string[] tokens = command.Trim().Split(' '); if (tokens.Length == 0) { // Nothing to do return; } switch (tokens[0].ToLowerInvariant()) { case ".": // redisplay status DisplayCurrentCode(); PrintIOPStatus(); PrintCPStatus(); break; case "i": // single step IOP StartExecution(true, false, false); StopExecution(); // wait for exec to finish DisplayCurrentCode(); PrintIOPStatus(); break; case "s": // single step CP _stepCount = 0; StartExecution(false, true, false); StopExecution(); // wait for exec to finish DisplayCurrentCode(); PrintCPStatus(); break; case "m": // single step Mesa macrocode StartExecution(false, false, true); StopExecution(); // wait for exec to finish DisplayCurrentCode(); PrintMesaStatus(); break; case "g": // run system StartExecution(false, false, false /* normal execution */); break; case "r": // reset system _system.Reset(); WriteLine("System reset."); break; case "tpc": // print TPC registers DisplayTPCRegisters(); break; case "task": // select Task to debug (skips over microcode execution for other tasks while single-stepping) switch (tokens.Length) { case 2: if (tokens[1].ToLowerInvariant() != "all") { // Step through a specific task only. _debugTask = (TaskType)Enum.Parse(typeof(TaskType), tokens[1], true); _debugSpecificTask = true; } else { // Step through all tasks _debugSpecificTask = false; } break; default: WriteLine("task "); break; } break; case "id": // dump IOP memory switch(tokens.Length) { case 2: DumpMemory(Convert.ToUInt16(tokens[1], 16), 1, null); break; case 3: DumpMemory(Convert.ToUInt16(tokens[1], 16), Convert.ToUInt16(tokens[2], 16), null); break; case 4: DumpMemory(Convert.ToUInt16(tokens[1], 16), Convert.ToUInt16(tokens[2], 16), tokens[3]); break; default: WriteLine("id
[count] [file]"); break; } break; case "cd": // dump CP memory switch (tokens.Length) { case 2: DumpCPMemory(Convert.ToUInt32(tokens[1], 16), 1, null); break; case 3: DumpCPMemory(Convert.ToUInt32(tokens[1], 16), Convert.ToUInt16(tokens[2], 16), null); break; case 4: DumpCPMemory(Convert.ToUInt32(tokens[1], 16), Convert.ToUInt16(tokens[2], 16), tokens[3]); break; default: WriteLine("cd
[count] [file]"); break; } break; case "fd": // dump floppy sector switch(tokens.Length) { case 4: DumpFloppySector(Convert.ToUInt16(tokens[1], 10), Convert.ToUInt16(tokens[2], 10), Convert.ToUInt16(tokens[3], 10), null); break; case 5: DumpFloppySector(Convert.ToUInt16(tokens[1], 10), Convert.ToUInt16(tokens[2], 10), Convert.ToUInt16(tokens[3], 10), tokens[4]); break; default: WriteLine("id [file]"); break; } break; case "u": // dump U register switch (tokens.Length) { case 2: DumpURegister(Convert.ToUInt16(tokens[1], 16)); break; default: WriteLine("u "); break; } break; case "mapv": // Map virtual address to physical switch (tokens.Length) { case 2: TranslateVirtualAddress(Convert.ToInt32(tokens[1], 16)); break; default: WriteLine("mapv "); break; } break; case "mapp": // Map physical address to virtual switch (tokens.Length) { case 2: TranslatePhysicalAddress(Convert.ToInt32(tokens[1], 16)); break; default: WriteLine("mapp "); break; } break; case "mapd": // Dump map entry switch (tokens.Length) { case 2: DumpMapEntry(Convert.ToInt32(tokens[1], 16)); break; default: WriteLine("mapd "); break; } break; case "mbs": // Set Mesa breakpoint switch (tokens.Length) { case 2: SetMesaBreakpoint(Convert.ToInt32(tokens[1], 16)); break; default: WriteLine("mbs "); break; } break; case "mbc": // Clear Mesa breakpoint switch (tokens.Length) { case 2: ClearMesaBreakpoint(Convert.ToInt32(tokens[1], 16)); break; default: WriteLine("mbs "); break; } break; case "da": // analyze disk AnalyzeDisk(); break; case "dt": // he can do stupid things switch (tokens.Length) { case 3: DumpTrack(Convert.ToInt32(tokens[1], 10), Convert.ToInt32(tokens[2], 10)); break; default: WriteLine("dt "); break; } break; case "save": // commit source annotations _iopDebugger.Save(); _cpDebugger.Save(); WriteLine("Source annotations saved."); break; case "clear": DebugOutput.Clear(); break; case "?": case "help": DisplayHelp(); break; default: WriteLine("?"); // for DMR break; } _lastCommand = command; } private void StartExecution(bool singleStepIOP, bool singleStepCP, bool singleStepMesa) { _singleStepIOP = singleStepIOP; _singleStepCP = singleStepCP; _singleStepMesa = singleStepMesa; SystemExecutionContext context = new SystemExecutionContext(StepCallback8085, StepCallbackCP, StepCallbackMesa, ErrorCallback); _system.StartExecution(context); if (!_singleStepCP && !_singleStepIOP && !_singleStepMesa) { WriteLine("System started."); } } private void StopExecution() { _system.StopExecution(); WriteLine("System stopped."); } private bool StepCallback8085() { bool stopExecution = _singleStepIOP; // Check for execution breakpoints if (BreakpointManager.TestBreakpoint(BreakpointProcessor.IOP, BreakpointType.Execution, _system.IOP.CPU.PC)) { BeginInvoke(new StatusDelegate(RefreshPostExecution), String.Format("* IOP Execution breakpoint hit at PC=${0:x4} *", _system.IOP.CPU.PC)); stopExecution = true; } // IOP processor still running? if (_system.IOP.CPU.Halted) { BeginInvoke(new StatusDelegate(RefreshPostExecution), String.Format("* 8085 halted at PC=${0:x4} *", _system.IOP.CPU.PC)); stopExecution = true; } return stopExecution; } private bool StepCallbackCP() { bool stopExecution = false; if (!_debugSpecificTask) { // Stop after every step stopExecution = _singleStepCP; } else { // // Stop only if the current task is the task we're debugging or we've exhausted our cycle count // waiting for the task to wake up again. // _stepCount++; stopExecution = _singleStepCP && (_system.CP.CurrentTask == _debugTask || _stepCount > 1000); if (_stepCount > 1000) { BeginInvoke(new StatusDelegate(RefreshPostExecution), String.Format("Timeout waiting for task {0} to wake.", _debugTask)); } } if (!_system.CP.IOPWait) { // Check for execution breakpoints int tpc = _system.CP.TPC[(int)_system.CP.CurrentTask]; if (BreakpointManager.TestBreakpoint(BreakpointProcessor.CP, BreakpointType.Execution, (ushort)tpc)) { BeginInvoke(new StatusDelegate(RefreshPostExecution), String.Format("* CP Execution breakpoint hit at TPC=0x{0:x3} *", tpc)); stopExecution = true; } } return stopExecution; } private bool StepCallbackMesa() { bool stopExecution = _singleStepMesa; if (!_system.CP.IOPWait) { // Check for execution breakpoints int mesaPC = ((((_system.CP.RH[5] & 0xf) << 16) | _system.CP.ALU.R[5]) << 1) | (_system.CP.PC16 ? 1 : 0); if (BreakpointManager.TestBreakpoint(BreakpointProcessor.Mesa, BreakpointType.Execution, mesaPC)) { BeginInvoke(new StatusDelegate(RefreshPostExecution), String.Format("* Mesa Execution breakpoint hit at PC=0x{0:x6} *", mesaPC)); stopExecution = true; } } return stopExecution; } private void ErrorCallback(Exception e) { // TODO: be more helpful. BeginInvoke(new StatusDelegate(RefreshPostExecution), String.Format("* Execution Error {0} *", e.Message)); } /// /// Invoked on the UI thread /// /// private void RefreshPostExecution(string message) { PrintIOPStatus(); WriteLine(String.Empty); PrintCPStatus(); WriteLine(message); DisplayCurrentCode(); } private void DumpMemory(ushort address, ushort length, string outFile) { int byteNum = 0; StringBuilder line = new StringBuilder(); line.AppendFormat("{0:x4}: ", address); FileStream fs = null; if (outFile != null) { fs = new FileStream(outFile, FileMode.Create, FileAccess.Write); } for (ushort i = address; i < address + length; i++) { byte val = _system.IOP.Memory.ReadByte(i); line.AppendFormat("{0:x2} ", val); byteNum++; if ((byteNum % 16) == 0) { WriteLine(line.ToString()); line.Clear(); line.AppendFormat("{0:x4}: ", i + 1); byteNum = 0; } if (fs != null) { fs.WriteByte(val); } } if (byteNum > 0) { WriteLine(line.ToString()); } if (fs != null) { fs.Close(); } } private void DumpCPMemory(uint address, uint length, string outFile) { int byteNum = 0; StringBuilder line = new StringBuilder(); line.AppendFormat("{0:x5}: ", address); FileStream fs = null; if (outFile != null) { fs = new FileStream(outFile, FileMode.Create, FileAccess.Write); } for (uint i = address; i < address + length; i++) { bool valid = false; ushort val = _system.MemoryController.DebugMemory.ReadWord((int)i, out valid); line.AppendFormat("{0:x4} ", val); byteNum++; if ((byteNum % 16) == 0) { WriteLine(line.ToString()); line.Clear(); line.AppendFormat("{0:x5}: ", i + 1); byteNum = 0; } if (fs != null) { // fs.WriteByte(val); } } if (byteNum > 0) { WriteLine(line.ToString()); } if (fs != null) { fs.Close(); } } private void DumpFloppySector(int cylinder, int head, int sector, string outFile) { int byteNum = 0; StringBuilder line = new StringBuilder(); line.AppendFormat("{0:x4}: ", 0); FloppyDisk disk = _system.IOP.FloppyController.Drive.Disk; if (cylinder < 0 || cylinder > 76) { WriteLine("Invalid cylinder spec."); return; } if (head < 0 || head > 1) { WriteLine("Invalid head spec."); return; } if (disk == null) { WriteLine("No disk loaded."); } else { Sector sectorData = disk.GetSector(cylinder, head, sector - 1); WriteLine(String.Format("Sector format {0}, length {1}", sectorData.Format, sectorData.Data.Length)); FileStream fs = null; if (outFile != null) { fs = new FileStream(outFile, FileMode.Create, FileAccess.Write); } for (int i = 0; i < sectorData.Data.Length; i++) { byte val = sectorData.Data[i]; line.AppendFormat("{0:x2} ", val); byteNum++; if ((byteNum % 16) == 0) { WriteLine(line.ToString()); line.Clear(); line.AppendFormat("{0:x4}: ", i + 1); byteNum = 0; } if (fs != null) { fs.WriteByte(val); } } if (byteNum > 0) { WriteLine(line.ToString()); } if (fs != null) { fs.Close(); } } } private void DumpURegister(int regNum) { if (regNum < 0 || regNum > 255) { WriteLine("Invalid U regster."); return; } WriteLine(String.Format("U{0:x2}=0x{1:x4}", regNum, _system.CP.U[regNum])); } private void DumpMapEntry(int address) { if (address < 0x10000 || address > 0x13fff) { WriteLine("Invalid address."); return; } bool valid = false; ushort entryWord = _system.MemoryController.DebugMemory.ReadWord(address, out valid); MapEntry entry = new MapEntry(entryWord); WriteLine(String.Format("Map entry at 0x{0:x5} - {1}", address, entry.ToString())); } private void TranslateVirtualAddress(int vAddress) { if (vAddress < 0 || vAddress > 0x1000000) { WriteLine("Invalid address."); return; } int mapAddr = 0x10000 + (vAddress >> 8); int pageOffset = vAddress & 0xff; bool valid = false; ushort entryWord = _system.MemoryController.DebugMemory.ReadWord(mapAddr, out valid); MapEntry entry = new MapEntry(entryWord); WriteLine(String.Format("Map entry at 0x{0:x5} - {1}", mapAddr, entry.ToString())); WriteLine(String.Format("VA 0x{0:x6} maps to PA 0x{1:x5}", vAddress, pageOffset + (entry.PageNumber << 8))); } private void TranslatePhysicalAddress(int pAddress) { if (pAddress < 0 || pAddress > _system.MemoryController.DebugMemory.Size - 1) { WriteLine("Invalid address."); return; } int found = 0; for (int mapAddr = 0x10000; mapAddr < 0x14000; mapAddr++) { bool valid = false; ushort entryWord = _system.MemoryController.DebugMemory.ReadWord(mapAddr, out valid); MapEntry entry = new MapEntry(entryWord); if ((pAddress >> 8) == entry.PageNumber) { int vAddress = ((mapAddr & 0xffff) << 8) | (pAddress & 0xff); WriteLine(String.Format("Map entry at 0x{0:x5} - {1}", mapAddr, entry.ToString())); WriteLine(String.Format("PA 0x{0:x5} maps to VA 0x{1:x6}", pAddress, vAddress)); found++; } } if (found == 0) { WriteLine("No map entry for physical address."); } } private struct MapEntry { public MapEntry(ushort entryWord) { PageNumber = ((entryWord & 0xf) << 8) | (entryWord >> 8); DP = (entryWord & 0x80) != 0; W = (entryWord & 0x40) != 0; D = (entryWord & 0x20) != 0; RP = (entryWord & 0x10) != 0; } public int PageNumber; public bool DP; // Dirty & Present public bool W; // Write Protected public bool D; // Dirty public bool RP; // Referenced & Present public override string ToString() { return String.Format("Page 0x{0:x4} {1}{2}{3}{4}", PageNumber, DP ? "dp " : String.Empty, W ? "w " : String.Empty, D ? "d " : String.Empty, RP ? "rp " : String.Empty); } } private void SetMesaBreakpoint(int address) { BreakpointManager.SetBreakpoint(new BreakpointEntry(BreakpointProcessor.Mesa, BreakpointType.Execution, address)); } private void ClearMesaBreakpoint(int address) { BreakpointManager.SetBreakpoint(new BreakpointEntry(BreakpointProcessor.Mesa, BreakpointType.None, address)); } private void AnalyzeDisk() { SA1000Drive drive = _system.HardDrive; WriteLine(String.Format("Drive is type {0}", drive.Type)); for (int cylinder = 0; cylinder < drive.Geometry.Cylinders; cylinder++) { for (int head = 0; head < drive.Geometry.Heads; head++) { int sector = -1; int field = 0; for (int word = 0; word < drive.WordsPerTrack; word++) { // // Walk the track, looking for sector marks. // There should be 16 sets of: // - Header mark // - Label mark // - Data mark // - CRC mark // In exactly that order // // Verify the header data // uint data = drive.DebugRead(cylinder, head, word); bool isAddressMark = (data & 0x10000) != 0; bool isCRC = (data & 0x20000) != 0; switch (field) { case 0: if (data == 0x1a141) // header { // This is the header mark, increment the sector count // move to next field. sector++; field++; } else if (data == 0x1a143) // label / data { // Unexpected here. WriteLine(String.Format("Unexpected label/data mark before header mark at c/h/s (w) {0}/{1}/{2} ({3})", cylinder, head, sector, word)); } else if (data == 0x2beef) { /* // Unexpected here. WriteLine(String.Format("Unexpected CRC before header mark at c/h/s (w) {0}/{1}/{2} ({3})", cylinder, head, sector, word)); */ } break; case 1: if (data == 0x1a143) // label / data { // This is the label mark, move to next field. field++; } else if (data == 0x1a141) // header { // Unexpected header mark here. WriteLine(String.Format("Unexpected header mark after header mark at c/h/s (w) {0}/{1}/{2} ({3})", cylinder, head, sector, word)); } else if (data == 0x2beef) { /* // Unexpected here. WriteLine(String.Format("Unexpected CRC after header mark at c/h/s (w) {0}/{1}/{2} ({3})", cylinder, head, sector, word)); */ } break; case 2: if (data == 0x1a143) // label / data { // This is the data mark, move to CRC. field++; } else if (data == 0x1a141) // header { // Unexpected header mark here. WriteLine(String.Format("Unexpected header mark after label mark at c/h/s (w) {0}/{1}/{2} ({3})", cylinder, head, sector, word)); } else if (data == 0x2beef) { /* // Unexpected here. WriteLine(String.Format("Unexpected CRC after label mark at c/h/s (w) {0}/{1}/{2} ({3})", cylinder, head, sector, word)); */ } break; case 3: if (data == 0x2beef) // CRC { // This is the CRC, move back to header state. field = 0; } else if (isAddressMark) { // Unexpected header mark here. WriteLine(String.Format("Unexpected address mark after data mark at c/h/s (w) {0}/{1}/{2} ({3})", cylinder, head, sector, word)); } break; } } // Check sector count. Should be 15. if (sector != 15) { WriteLine(String.Format("Unexpected sector count {0} at c/h {1}/{2}", sector, cylinder, head)); } } } } private void DumpTrack(int cylinder, int head) { SA1000Drive drive = _system.HardDrive; int sector = 0; int wordIndex = 0; uint data = 0; List sectorData = new List(); // Print lead-in to first sector while (wordIndex < drive.WordsPerTrack) { data = drive.DebugRead(cylinder, head, wordIndex); wordIndex++; if (data == 0x1a141) { break; } else { sectorData.Add(data); } } WriteLine("Lead-in to sector 0:"); PrintSectorData(sectorData); sectorData.Clear(); // Print sector data while (wordIndex < drive.WordsPerTrack) { data = drive.DebugRead(cylinder, head, wordIndex); wordIndex++; if (data == 0x1a141 || wordIndex == drive.WordsPerTrack - 1) { WriteLine(String.Format("Sector {0}:", sector)); PrintSectorData(sectorData); sectorData.Clear(); sector++; } else { sectorData.Add(data); } } } private void PrintSectorData(List data) { int byteNum = 0; StringBuilder line = new StringBuilder(); line.AppendFormat("000: "); for (int i = 0; i < data.Count; i++) { line.AppendFormat("{0:x5} ", data[i]); byteNum++; if ((byteNum % 16) == 0) { WriteLine(line.ToString()); line.Clear(); line.AppendFormat("{0:x3}: ", i + 1); byteNum = 0; } } if (byteNum > 0) { WriteLine(line.ToString()); } } private void DisplayCurrentCode() { _iopDebugger.DisplayCurrentCode(); _cpDebugger.DisplayCurrentCode(); } private void DisplayHelp() { WriteLine(@" ?, help - Display this message. . - Display IOP and CP status. i - Single step IOP. s - Single step CP. m - Single step macrocode. g - Start/continue system execution. tpc - Display TPC registers. task - Select Task to debug (skips over microcode execution for other tasks while single-stepping.) id
[count] [toFile] - Dump IOP memory. cd
[count] [toFile] - Dump CP memory. fd - Dump floppy sector. u - Display specified U register. mapv - Map virtual address to physical. mapp - Map physical address to virtual. mapd - Dump map entry at specified map address. mbs - Set Macroinstruction breakpoint. mbc - Clear Macroinstruction breakpoint. dt - Dump hard disk track. save - Commit source annotations. clear - Clear debugger scrollback. " ); } private void WriteLine(string line) { DebugOutput.Text += line + "\r\n"; DebugOutput.Select(DebugOutput.TextLength - 1, 1); DebugOutput.ScrollToCaret(); } private void Write(string line) { DebugOutput.Text += line; DebugOutput.Select(DebugOutput.TextLength - 1, 1); DebugOutput.ScrollToCaret(); } private void PrintIOPStatus() { WriteLine(String.Format("IOP PC=${0:x4} SP=${1:x4} AF=${2:x4} BC=${3:x4} DE=${4:x4} HL=${5:x4}", _system.IOP.CPU.PC, _system.IOP.CPU.SP, _system.IOP.CPU.AF, _system.IOP.CPU.BC, _system.IOP.CPU.DE, _system.IOP.CPU.HL)); WriteLine(String.Format("Flags {0}", GetStringForFlags(_system.IOP.CPU.F))); SourceEntry entry = _iopDebugger.SourceMap.GetSourceForAddress(_system.IOP.CPU.PC); string symbolName = null; string currentSymbol = String.Empty; if (entry != null) { if (entry.SymbolNames.Length == 0 || entry.SymbolNames[0] == "*none*" || // TODO: move to constant entry.Address != _system.IOP.CPU.PC) { // No symbol name associated with this entry, find the nearest. SourceEntry symbolEntry = _iopDebugger.SourceMap.GetNearestSymbolForAddress(_system.IOP.CPU.PC); if (symbolEntry != null) { symbolName = String.Format("{0}+${1:x}", symbolEntry.SymbolNames[0], _system.IOP.CPU.PC - symbolEntry.Address); } } else { symbolName = entry.SymbolNames[0]; } if (symbolName != null) { currentSymbol = String.Format("{0},{1} line {2}", symbolName, entry.SourcePath, entry.LineNumber); } } WriteLine(String.Format("${0:x4} ({1})\r\n {2}", _system.IOP.CPU.PC, currentSymbol, _system.IOP.CPU.Disassemble(_system.IOP.CPU.PC))); // Update the IOP debugger's title bar with current MP value _iopDebugger.Text = String.Format("IOP Debugger - MP {0}", _system.IOP.MiscIO.MPanelBlank ? "" : _system.IOP.MiscIO.MPanelValue.ToString()); } private void PrintCPStatus() { int tpc = _system.CP.TPC[(int)_system.CP.CurrentTask]; SourceEntry symbolEntry = _cpDebugger.GetSymbolForAddress(tpc); string currentSymbol = String.Empty; if (symbolEntry != null) { string symbolName = symbolEntry.SymbolNames.Length > 0 && symbolEntry.SymbolNames[0] != "*none*" ? symbolEntry.SymbolNames[0] : String.Empty; currentSymbol = String.Format("{0},{1} line {2}", symbolName, symbolEntry.SourcePath, symbolEntry.LineNumber); } WriteLine(String.Format("CP Task={0} TPC={1:x3} {2}", _system.CP.CurrentTask, tpc, currentSymbol)); StringBuilder regString = new StringBuilder(); for (int i = 0; i < 16; i++) { regString.AppendFormat(" R{0:x}=0x{1:x4} ", i, _system.CP.ALU.R[i]); if (((i+1) % 8) == 0) { WriteLine(regString.ToString()); regString.Clear(); } } StringBuilder reghString = new StringBuilder(); for (int i = 0; i < 16; i++) { reghString.AppendFormat(" RH{0:x}=0x{1:x2} ", i, _system.CP.RH[i]); if (((i + 1) % 8) == 0) { WriteLine(reghString.ToString()); reghString.Clear(); } } WriteLine(reghString.ToString()); StringBuilder stackString = new StringBuilder(); // By convention, R0 is TOS in Mesa. stackString.AppendFormat("0x{0:x4} ", _system.CP.ALU.R[0]); for (int i = _system.CP.StackP; i > 0; i--) { stackString.AppendFormat("{0:x4} ", _system.CP.U[i]); } WriteLine(String.Format(" stackP=0x{0:x1} stack: {1}", _system.CP.StackP, stackString)); WriteLine(String.Format( " Q=0x{0:x4} MAR=0x{1:x5} MD=0x{2:x4} pc16={3} ibPtr={4} ibFront=0x{5:x2} ib[0]=0x{6:x2} ib[1]=0x{7:x2}", _system.CP.ALU.Q, _system.MemoryController.MAR, _system.MemoryController.MD, _system.CP.PC16 ? 1 : 0, _system.CP.IBPtr, _system.CP.IBFront, _system.CP.IB[0], _system.CP.IB[1])); WriteLine(String.Format(" Z={0} N={1} Nb={2} Pg={3} C={4} O={5}", _system.CP.ALU.Zero, _system.CP.ALU.Neg, _system.CP.ALU.NibCarry, _system.CP.ALU.PgCarry, _system.CP.ALU.CarryOut, _system.CP.ALU.Overflow)); Microinstruction inst = new Microinstruction(_system.CP.MicrocodeRam[tpc]); int nia = inst.INIA | _system.CP.NIAModifier; WriteLine(String.Format("{0:x3} {1:x12} - {2} (NIA={3:x3}) [c{4}]", tpc, _system.CP.MicrocodeRam[tpc], inst.Disassemble(_system.CP.Cycle), nia, _system.CP.Cycle)); } private void PrintMesaStatus() { int pc = ((((_system.CP.RH[5] & 0xf) << 16) | _system.CP.ALU.R[5]) << 1) | (_system.CP.PC16 ? 1 : 0); WriteLine(String.Format("Mesa PC=0x{0:x5} (physical address 0x{1:x5})", pc, pc >> 1)); StringBuilder stackString = new StringBuilder(); // By convention, R0 is TOS in Mesa. stackString.AppendFormat("0x{0:x4} ", _system.CP.ALU.R[0]); for (int i = _system.CP.StackP; i > 0; i--) { stackString.AppendFormat("0x{0:x4} ", _system.CP.U[i]); } WriteLine(String.Format(" stackP=0x{0:x1} stack: {1}", _system.CP.StackP, stackString)); WriteLine(String.Format( " ibPtr={0} ibFront=0x{1:x2} ib[0]=0x{2:x2} ib[1]=0x{3:x2}", _system.CP.IBPtr, _system.CP.IBFront, _system.CP.IB[0], _system.CP.IB[1])); // Since this breakpoint should always happen the microinstruction after an IBDisp has taken place, // the TPC is always pointing to the dispatch address for the bytecode, which is not coincidentally // the bytecode itself. (The value of ibFront that caused the dispatch has since been discarded, or // we'd use that.) byte byteCode = (byte)(_system.CP.TPC[(int)TaskType.Emulator]); MacroInstruction mInst = MacroInstruction.GetInstruction(MacroType.Lisp, byteCode); string operand = string.Empty; switch (mInst.Operand) { case MacroOperand.None: // No operands. break; case MacroOperand.Byte: operand = String.Format("0x{0:x2}", _system.CP.IBFront); break; case MacroOperand.SignedByte: operand = String.Format("{0}", (sbyte)_system.CP.IBFront); break; case MacroOperand.Pair: operand = String.Format("0x{0:x1},,0x{1:x1}", _system.CP.IBFront >> 4, _system.CP.IBFront & 0xf); break; case MacroOperand.TwoByte: operand = String.Format("0x{0:x2},,0x{1:x2}", _system.CP.IBFront, _system.CP.IBPtr == IBState.Word ? _system.CP.IB[1] : _system.CP.IB[0]); break; case MacroOperand.Word: operand = String.Format("0x{0:x4}", (_system.CP.IBFront << 8) | (_system.CP.IBPtr == IBState.Word ? _system.CP.IB[1] : _system.CP.IB[0])); break; } WriteLine(String.Format("Bytecode 0x{0:x2} - {1} {2}", byteCode, mInst.Mnemonic, operand)); } private void DisplayTPCRegisters() { for(int i=0;i<8;i++) { WriteLine(String.Format("{0} - 0x{1:x3}", (TaskType)i, _system.CP.TPC[i])); } } private void InjectKeystroke(int keycode) { _system.IOP.Keyboard.KeyDown((KeyCode)keycode); _system.IOP.Keyboard.KeyUp((KeyCode)keycode); } private string GetStringForFlags(byte f) { string flags = String.Empty; if (f == 0) { flags = "NONE"; return flags; } if ((f & 0x80) != 0) { flags += "S "; } if ((f & 0x40) != 0) { flags += "Z "; } if ((f & 0x10) != 0) { flags += "AC "; } if ((f & 0x04) != 0) { flags += "P "; } if ((f & 0x01) != 0) { flags += "CY "; } return flags; } private void OnFormClosing(object sender, FormClosingEventArgs e) { _iopDebugger.Close(); _cpDebugger.Close(); } private DSystem _system; private string _lastCommand; private bool _singleStepIOP; private bool _singleStepCP; private bool _singleStepMesa; private TaskType _debugTask; private bool _debugSpecificTask; private int _stepCount; private DebuggerReason _reason; private string _entryMessage; private IOPDebugger _iopDebugger; private CPDebugger _cpDebugger; private delegate void StatusDelegate(string message); } }