1
0
mirror of https://github.com/livingcomputermuseum/ContrAlto.git synced 2026-01-17 08:34:15 +00:00

1366 lines
49 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.IO;
using System.Text;
using System.Windows.Forms;
using Contralto.CPU;
using System.Threading;
using System.Drawing.Imaging;
using Contralto.IO;
using Contralto.Display;
using Contralto.Logging;
namespace Contralto
{
/// <summary>
/// A basic & hacky debugger. To be improved.
/// </summary>
public partial class Debugger : Form
{
public Debugger(AltoSystem system, ExecutionController controller)
{
_system = system;
_controller = controller;
_microcodeBreakpointEnabled = new bool[3,1024];
_novaBreakpointEnabled = new bool[65536];
_controller.StepCallback += OnExecutionStep;
_controller.ErrorCallback += OnExecutionError;
// Pick up the current execution status (if the main window hands us a running
// system, we want to know).
_execType = _controller.IsRunning ? ExecutionType.Normal : ExecutionType.None;
InitializeComponent();
InitControls();
RefreshUI();
}
public void LoadSourceCode(MicrocodeBank bank, string path)
{
if (path == null)
{
throw new ArgumentNullException(path, "Microcode path must be specified.");
}
DataGridView view = bank == MicrocodeBank.ROM0 ? _rom0SourceViewer : _rom1SourceViewer;
StreamReader sr = new StreamReader(path);
while (!sr.EndOfStream)
{
string line = sr.ReadLine();
SourceLine src = new SourceLine(line);
int i = view.Rows.Add(
false, // breakpoint
GetTextForTask(src.Task),
src.Address,
src.Text);
// Give the row a color based on the task
view.Rows[i].DefaultCellStyle.BackColor = GetColorForTask(src.Task);
// Tag the row based on the PROM address (if any) to make it easy to find.
if (!String.IsNullOrEmpty(src.Address))
{
view.Rows[i].Tag = Convert.ToUInt16(src.Address, 8);
}
}
// Ensure the UI view gets refreshed to display the current MPC source
Refresh();
}
public override void Refresh()
{
base.Refresh();
RefreshUI();
}
private void OnDebuggerClosed(object sender, FormClosedEventArgs e)
{
_controller.StepCallback -= OnExecutionStep;
_controller.ErrorCallback -= OnExecutionError;
}
private void RefreshUI()
{
// Registers
for (int i = 0; i < 32; i++)
{
_registerData.Rows[i].Cells[0].Value = Conversion.ToOctal(i, 2);
_registerData.Rows[i].Cells[1].Value = Conversion.ToOctal(_system.CPU.R[i], 6);
_registerData.Rows[i].Cells[2].Value = Conversion.ToOctal(_system.CPU.S[0][i], 6);
}
// Tasks
for (int i = 0; i < 16; i++)
{
_taskData.Rows[i].Cells[0].Value = GetTextForTask((TaskType)i);
_taskData.Rows[i].Cells[1].Value = GetTextForTaskState(_system.CPU.Tasks[i]);
_taskData.Rows[i].Cells[2].Value =
_system.CPU.Tasks[i] != null ? Conversion.ToOctal(_system.CPU.Tasks[i].MPC, 4) : String.Empty;
}
// Other registers
_otherRegs.Rows[0].Cells[1].Value = Conversion.ToOctal(_system.CPU.L, 6);
_otherRegs.Rows[1].Cells[1].Value = Conversion.ToOctal(_system.CPU.T, 6);
_otherRegs.Rows[2].Cells[1].Value = Conversion.ToOctal(_system.CPU.M, 6);
_otherRegs.Rows[3].Cells[1].Value = Conversion.ToOctal(_system.CPU.IR, 6);
_otherRegs.Rows[4].Cells[1].Value = Conversion.ToOctal(_system.CPU.ALUC0, 1);
//_otherRegs.Rows[4].Cells[1].Value = OctalHelpers.ToOctal(_system.CPU.Carry, 1);
//_otherRegs.Rows[4].Cells[1].Value = OctalHelpers.ToOctal(_system.CPU.Skip, 1);
_otherRegs.Rows[5].Cells[1].Value = Conversion.ToOctal(_system.MemoryBus.MAR, 6);
_otherRegs.Rows[6].Cells[1].Value = Conversion.ToOctal(_system.MemoryBus.MD, 6);
_otherRegs.Rows[7].Cells[1].Value = Conversion.ToOctal(_system.MemoryBus.Cycle & 0x3f, 2);
// Disk info
_diskData.Rows[0].Cells[1].Value = _system.DiskController.ClocksUntilNextSector.ToString("0.00");
_diskData.Rows[1].Cells[1].Value = _system.DiskController.Cylinder.ToString();
_diskData.Rows[2].Cells[1].Value = _system.DiskController.SeekCylinder.ToString();
_diskData.Rows[3].Cells[1].Value = _system.DiskController.Head.ToString();
_diskData.Rows[4].Cells[1].Value = _system.DiskController.Sector.ToString();
_diskData.Rows[5].Cells[1].Value = Conversion.ToOctal(_system.DiskController.KDATA, 6);
_diskData.Rows[6].Cells[1].Value = Conversion.ToOctal(_system.DiskController.KADR, 6);
_diskData.Rows[7].Cells[1].Value = Conversion.ToOctal(_system.DiskController.KCOM, 6);
_diskData.Rows[8].Cells[1].Value = Conversion.ToOctal(_system.DiskController.KSTAT, 6);
_diskData.Rows[9].Cells[1].Value = _system.DiskController.RECNO.ToString();
// Reserved memory locations
for (int i = 0; i < _reservedMemoryEntries.Length; i++)
{
_reservedMemory.Rows[i].Cells[2].Value =
Conversion.ToOctal(_system.MemoryBus.DebugReadWord(_reservedMemoryEntries[i].Address), 6);
}
//
// Select active tab based on current UCode bank
MicrocodeBank bank = UCodeMemory.GetBank(_system.CPU.CurrentTask.TaskType);
switch (bank)
{
case MicrocodeBank.ROM0:
SourceTabs.SelectedIndex = 0;
break;
case MicrocodeBank.ROM1:
SourceTabs.SelectedIndex = 1;
break;
case MicrocodeBank.RAM0:
SourceTabs.SelectedIndex = 2;
break;
}
RefreshMicrocodeDisassembly(_system.CPU.CurrentTask.MPC);
// Highlight the nova memory location corresponding to the emulator PC.
// TODO: this should be configurable
ushort pc = _system.CPU.R[6];
HighlightNovaSourceLine(pc);
// Exec state
switch (_execState)
{
case ExecutionState.Stopped:
ExecutionStateLabel.Text = "Stopped";
break;
case ExecutionState.SingleStep:
ExecutionStateLabel.Text = "Stepping";
break;
case ExecutionState.AutoStep:
ExecutionStateLabel.Text = "Stepping (auto)";
break;
case ExecutionState.Running:
ExecutionStateLabel.Text = "Running";
break;
case ExecutionState.BreakpointStop:
ExecutionStateLabel.Text = "Stopped (bkpt)";
break;
case ExecutionState.InternalError:
ExecutionStateLabel.Text = String.Format("Stopped (error {0})", _lastExceptionText);
break;
}
this.BringToFront();
}
private void RefreshMicrocodeDisassembly(ushort address)
{
// Update non-ROM code listings, depending on the currently active tab
switch (SourceTabs.SelectedIndex)
{
case 0:
// Find the right source line and highlight it.
HighlightMicrocodeSourceLine(_rom0SourceViewer, address);
break;
case 1:
HighlightMicrocodeSourceLine(_rom1SourceViewer, address);
break;
case 2:
UpdateMicrocodeDisassembly(MicrocodeBank.RAM0);
HighlightMicrocodeSourceLine(_ram0SourceViewer, address);
break;
}
}
private void InitControls()
{
for (int i = 0; i < 32; i++)
{
_registerData.Rows.Add(-1, -1 ,-1);
}
for (int i = 0; i < 16; i++)
{
_taskData.Rows.Add("0", "0", "0");
}
// TODO: handle extended memory
_memoryData.RowCount = 65536;
_otherRegs.Rows.Add("L", "0");
_otherRegs.Rows.Add("T", "0");
_otherRegs.Rows.Add("M", "0");
_otherRegs.Rows.Add("IR", "0");
_otherRegs.Rows.Add("ALUC0", "0");
//_otherRegs.Rows.Add("CARRY", "0");
//_otherRegs.Rows.Add("SKIP", "0");
_otherRegs.Rows.Add("MAR", "0");
_otherRegs.Rows.Add("MD", "0");
_otherRegs.Rows.Add("MCycle", "0");
_diskData.Rows.Add("Cycles", "0");
_diskData.Rows.Add("Cylinder", "0");
_diskData.Rows.Add("D.Cylinder", "0");
_diskData.Rows.Add("Head", "0");
_diskData.Rows.Add("Sector", "0");
_diskData.Rows.Add("KDATA", "0");
_diskData.Rows.Add("KADR", "0");
_diskData.Rows.Add("KCOM", "0");
_diskData.Rows.Add("KSTAT", "0");
_diskData.Rows.Add("RECNO", "0");
for(int i=0;i< _reservedMemoryEntries.Length;i++)
{
_reservedMemory.Rows.Add(
Conversion.ToOctal(_reservedMemoryEntries[i].Address, 3),
_reservedMemoryEntries[i].Name,
Conversion.ToOctal(0, 6));
}
}
/// <summary>
/// Handle breakpoint placement on column 0.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Rom0SourceViewCellClick(object sender, DataGridViewCellEventArgs e)
{
// Check for breakpoint column click.
if (e.ColumnIndex == 0)
{
SetBreakpointFromCellClick(MicrocodeBank.ROM0, e.RowIndex);
}
}
private void Rom1SourceViewCellClick(object sender, DataGridViewCellEventArgs e)
{
// Check for breakpoint column click.
if (e.ColumnIndex == 0)
{
SetBreakpointFromCellClick(MicrocodeBank.ROM1, e.RowIndex);
}
}
private void Ram0SourceViewCellClick(object sender, DataGridViewCellEventArgs e)
{
// Check for breakpoint column click.
if (e.ColumnIndex == 0)
{
SetBreakpointFromCellClick(MicrocodeBank.RAM0, e.RowIndex);
}
}
private void SetBreakpointFromCellClick(MicrocodeBank bank, int index)
{
DataGridView view = null;
switch(bank)
{
case MicrocodeBank.ROM0:
view = _rom0SourceViewer;
break;
case MicrocodeBank.ROM1:
view = _rom1SourceViewer;
break;
case MicrocodeBank.RAM0:
view = _ram0SourceViewer;
break;
}
// See if this is a source line, if so check/uncheck the box
// and set/unset a breakpoint for the line
if (view.Rows[index].Tag != null)
{
bool value = (bool)view.Rows[index].Cells[0].Value;
view.Rows[index].Cells[0].Value = !value;
ModifyMicrocodeBreakpoint(bank, (UInt16)view.Rows[index].Tag, !value);
}
}
private void MemoryViewCellClick(object sender, DataGridViewCellEventArgs e)
{
// Check for breakpoint column click.
if (e.ColumnIndex == 0)
{
// Check/uncheck the box and set/unset a breakpoint for the line
bool value = (bool)_memoryData.Rows[e.RowIndex].Cells[0].Value;
_memoryData.Rows[e.RowIndex].Cells[0].Value = !value;
ModifyNovaBreakpoint((UInt16)e.RowIndex, !value);
}
}
private void UpdateMicrocodeDisassembly(MicrocodeBank bank)
{
DataGridView view = null;
uint[] uCode = null;
switch (bank)
{
case MicrocodeBank.ROM1:
view = _rom1SourceViewer;
uCode = UCodeMemory.UCodeROM;
break;
case MicrocodeBank.RAM0:
view = _ram0SourceViewer;
uCode = UCodeMemory.UCodeRAM;
break;
}
bool bFirstTime = view.Rows.Count == 0;
for(int i=0;i<1024;i++)
{
int address = (bank == MicrocodeBank.RAM0) ? i : 1024 + i;
MicroInstruction instruction = new MicroInstruction(uCode[address]);
if (bFirstTime)
{
// Create new row
int index = view.Rows.Add(
false, // breakpoint
Conversion.ToOctal(address, 4),
Conversion.ToOctal((int)uCode[address], 11),
UCodeDisassembler.DisassembleInstruction(instruction, TaskType.Emulator));
view.Rows[index].Tag = (ushort)i;
}
else
{
// Update existing row
view.Rows[i].Cells[1].Value = Conversion.ToOctal(address, 4);
view.Rows[i].Cells[2].Value = Conversion.ToOctal((int)uCode[address], 11);
view.Rows[i].Cells[3].Value = UCodeDisassembler.DisassembleInstruction(instruction, TaskType.Emulator);
}
}
}
private void HighlightMicrocodeSourceLine(DataGridView view, UInt16 address)
{
foreach (DataGridViewRow row in view.Rows)
{
if (row.Tag != null &&
(ushort)(row.Tag) == address)
{
view.ClearSelection();
row.Selected = true;
view.CurrentCell = row.Cells[0];
break;
}
}
}
private void HighlightNovaSourceLine(UInt16 address)
{
if (address < _memoryData.Rows.Count)
{
_memoryData.ClearSelection();
_memoryData.Rows[address].Selected = true;
_memoryData.CurrentCell = _memoryData.Rows[address].Cells[0];
}
}
/// <summary>
/// Fill in memory view on demand.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnMemoryCellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
{
// TODO: handle extended memory
if (e.RowIndex > 65535)
{
// Top of memory, nothing to do
return;
}
switch(_memoryData.Columns[e.ColumnIndex].Name)
{
case "Bkpt":
e.Value = GetNovaBreakpoint((UInt16)e.RowIndex);
break;
case "Address":
e.Value = Conversion.ToOctal(e.RowIndex, 6);
break;
case "Data":
e.Value = Conversion.ToOctal(_system.MemoryBus.DebugReadWord((ushort)e.RowIndex), 6);
break;
case "Disassembly":
e.Value = CPU.Nova.NovaDisassembler.DisassembleInstruction(
(ushort)e.RowIndex,
_system.MemoryBus.DebugReadWord((ushort)e.RowIndex));
break;
}
}
private void ModifyMicrocodeBreakpoint(MicrocodeBank bank, UInt16 address, bool set)
{
_microcodeBreakpointEnabled[(int)bank,address] = set;
}
private bool GetNovaBreakpoint(UInt16 address)
{
return _novaBreakpointEnabled[address];
}
private void ModifyNovaBreakpoint(UInt16 address, bool set)
{
_novaBreakpointEnabled[address] = set;
}
private string GetTextForTaskState(AltoCPU.Task task)
{
if (task == null)
{
return String.Empty;
}
else
{
// Wakeup bit
string status = task.Wakeup ? "W" : String.Empty;
// Run bit
if (task == _system.CPU.CurrentTask)
{
status += "R";
}
return status;
}
}
private string GetTextForTask(TaskType task)
{
string[] taskText =
{
"EM", // 0 - emulator
String.Empty,
String.Empty,
String.Empty,
"KS", // 4 - disk sector
String.Empty,
String.Empty,
"EN", // 7 - ethernet
"MR", // 8 - memory refresh
"DW", // 9 - display word
"CU", // 10 - cursor
"DH", // 11 - display horizontal
"DV", // 12 - display vertical
"PA", // 13 - parity
"KW", // 14 - disk word
String.Empty,
};
if (task == TaskType.Invalid)
{
return String.Empty;
}
else
{
return taskText[(int)task];
}
}
private Color GetColorForTask(TaskType task)
{
Color[] taskColors =
{
Color.LightBlue, // 0 - emulator
Color.LightGray, // 1 - unused
Color.LightGray, // 2 - unused
Color.LightGray, // 3 - unused
Color.LightGreen, // 4 - disk sector
Color.LightGray, // 5 - unused
Color.LightGray, // 6 - unused
Color.LightSalmon, // 7 - ethernet
Color.LightSeaGreen,// 8 - memory refresh
Color.LightYellow, // 9 - display word
Color.LightPink, // 10 - cursor
Color.Chartreuse, // 11 - display horizontal
Color.LightCoral, // 12 - display vertical
Color.LightSteelBlue, // 13 - parity
Color.Gray, // 14 - disk word
Color.LightGray, // 15 - unused
};
if (task == TaskType.Invalid)
{
return Color.White;
}
else
{
return taskColors[(int)task];
}
}
private struct SourceLine
{
public SourceLine(string sourceText)
{
//
// Mangle "<-" found in the source into the unicode arrow character, just to be neat.
//
sourceText = sourceText.Replace("<-", _arrowChar.ToString());
// See if line begins with something of the form "TNxxxxx>".
// If it does then we have extra metadata to parse out.
string[] tokens = sourceText.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
bool annotated = false;
// Make the compiler happy
Text = sourceText;
Address = String.Empty;
Task = TaskType.Invalid;
if (tokens.Length > 0 &&
tokens[0].Length == 7 &&
tokens[0].EndsWith(">"))
{
// Close enough. Look for the task tag and parse out the (octal) address
switch(tokens[0].Substring(0,2))
{
case "EM":
Task = TaskType.Emulator;
break;
case "SE":
Task = TaskType.DiskSector;
break;
case "EN":
Task = TaskType.Ethernet;
break;
case "MR":
Task = TaskType.MemoryRefresh;
break;
case "DW":
Task = TaskType.DisplayWord;
break;
case "CU":
Task = TaskType.Cursor;
break;
case "DH":
Task = TaskType.DisplayHorizontal;
break;
case "DV":
Task = TaskType.DisplayVertical;
break;
case "PA":
Task = TaskType.Parity;
break;
case "KW":
Task = TaskType.DiskWord;
break;
case "XM": //XMesa code, which runs in the Emulator task
Task = TaskType.Emulator;
break;
default:
Task = TaskType.Invalid;
break;
}
if (Task != TaskType.Invalid)
{
try
{
// Belongs to a task, so we can grab the address out as well
Address = sourceText.Substring(2, 4);
}
catch
{
// That didn't work for whatever reason, just treat this as a normal source line.
annotated = false;
}
Text = sourceText.Substring(tokens[0].Length + 1, sourceText.Length - tokens[0].Length -1);
annotated = true;
}
else
{
// We will just display this as a non-source line
annotated = false;
}
}
if (!annotated)
{
Text = sourceText;
Address = String.Empty;
Task = TaskType.Invalid;
}
}
public string Text;
public string Address;
public TaskType Task;
}
private void OnTabChanged(object sender, EventArgs e)
{
RefreshMicrocodeDisassembly(_system.CPU.CurrentTask.MPC);
}
private void Debugger_Load(object sender, EventArgs e)
{
}
private void OnJumpAddressKeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Return ||
e.KeyCode == Keys.Enter)
{
try
{
UInt16 address = Convert.ToUInt16(JumpToAddress.Text, 8);
// find the source address that matches this, if any.
RefreshMicrocodeDisassembly(address);
}
catch
{
// eh, just do nothing for now
}
}
}
private void OnMemoryJumpAddressKeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Return ||
e.KeyCode == Keys.Enter)
{
try
{
UInt16 address = Convert.ToUInt16(MemoryJumpToAddress.Text, 8);
// find the source address that matches this, if any.
HighlightNovaSourceLine(address);
}
catch
{
// eh, just do nothing for now
}
}
}
private void OnStepButtonClicked(object sender, EventArgs e)
{
_execType = ExecutionType.Step;
SetExecutionState(ExecutionState.SingleStep);
_controller.StartExecution(AlternateBootType.None);
}
private void OnAutoStepButtonClicked(object sender, EventArgs e)
{
//
// Continuously step (and update the UI)
// until the "Stop" button is pressed or something bad happens.
//
_execType = ExecutionType.Auto;
SetExecutionState(ExecutionState.AutoStep);
_controller.StartExecution(AlternateBootType.None);
}
private void RunButton_Click(object sender, EventArgs e)
{
//
// Continuously execute, but do not update UI
// until the "Stop" button is pressed or something bad happens.
//
_execType = ExecutionType.Normal;
SetExecutionState(ExecutionState.Running);
_controller.StartExecution(AlternateBootType.None);
}
private void RunToNextTaskButton_Click(object sender, EventArgs e)
{
_execType = ExecutionType.NextTask;
SetExecutionState(ExecutionState.Running);
_controller.StartExecution(AlternateBootType.None);
}
/// <summary>
/// Runs microcode until next Nova instruction is started
/// This is done by simply breaking whenever the uPC for the emulator
/// task returns to 20(octal) -- this is the restart point for the emulator
/// task.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void NovaStep_Click(object sender, EventArgs e)
{
_execType = ExecutionType.NextNovaInstruction;
SetExecutionState(ExecutionState.Running);
_controller.StartExecution(AlternateBootType.None);
}
private void OnStopButtonClicked(object sender, EventArgs e)
{
_controller.StopExecution();
Refresh();
}
private void ResetButton_Click(object sender, EventArgs e)
{
_controller.Reset(AlternateBootType.None);
Refresh();
}
private void OnExecutionError(Exception e)
{
_lastExceptionText = e.Message;
SetExecutionState(ExecutionState.InternalError);
}
private bool OnExecutionStep()
{
switch (_execType)
{
case ExecutionType.Auto:
{
// Execute a single step, then update UI and
// sleep to give messages time to run.
this.BeginInvoke(new StepDelegate(RefreshUI));
this.BeginInvoke(new StepDelegate(Invalidate));
System.Threading.Thread.Sleep(10);
return true; /* break always */
}
case ExecutionType.Step:
return true; /* break always */
case ExecutionType.Normal:
case ExecutionType.NextTask:
case ExecutionType.NextNovaInstruction:
// For debugging floating point microcode:
#if FLOAT_DEBUG
if (_system.CPU.CurrentTask.MPC == 0x10) // MPC is 20(octal) meaning a new Nova instruction.
{
if (_lastFPInstruction == 0)
{
// check for new floating instruction
FloatDebugPre(_system.MemoryBus.DebugReadWord(TaskType.Emulator, _system.CPU.R[6]));
}
else
{
// last instruction was a floating point instruction, check the result
FloatDebugPost();
// And see if this new one is also a floating point instruction...
FloatDebugPre(_system.MemoryBus.DebugReadWord(TaskType.Emulator, _system.CPU.R[6]));
}
}
#endif
if (_system.CPU.CurrentTask.MPC == 0x10) // MPC is 20(octal) meaning a new Nova instruction.
{
// count nova instructions (for profiling)
_system._novaInst++;
}
// See if we need to stop here
if (_execAbort || // The Stop button was hit
_microcodeBreakpointEnabled[(int)UCodeMemory.GetBank(_system.CPU.CurrentTask.TaskType),_system.CPU.CurrentTask.MPC] || // A microcode breakpoint was hit
(_execType == ExecutionType.NextTask &&
_system.CPU.NextTask != null &&
_system.CPU.NextTask != _system.CPU.CurrentTask) || // The next task was switched to
(_system.CPU.CurrentTask.MPC == 0x10 && // MPC is 20(octal) meaning a new Nova instruction and...
(_novaBreakpointEnabled[_system.CPU.R[6]] || // A breakpoint is set here
_execType == ExecutionType.NextNovaInstruction))) // or we're running only a single Nova instruction.
{
// Stop here as we've hit a breakpoint or have been stopped
// Update UI to indicate where we stopped.
this.BeginInvoke(new StepDelegate(RefreshUI));
this.BeginInvoke(new StepDelegate(Invalidate));
if (!_execAbort)
{
SetExecutionState(ExecutionState.BreakpointStop);
}
_execAbort = false;
return true;
}
break;
}
return false;
}
#if FLOAT_DEBUG
// vars for float debug
ushort _lastFPInstruction;
ushort _ac0;
ushort _ac1;
ushort _ac2;
ushort _ac3;
ushort _fpRegAddr;
ushort _fpRegCount;
int _fpInstructionCount;
/// <summary>
/// Temporary, for debugging floating point ucode issues
/// </summary>
/// <param name="instruction"></param>
private void FloatDebugPre(ushort instruction)
{
_lastFPInstruction = 0;
// Float instructions are from 70001-70022 octal
if (instruction >= 0x7000 && instruction <= 0x7012)
{
_fpInstructionCount++;
// Save instruction
_lastFPInstruction = instruction;
// Save ACs
_ac0 = _system.CPU.R[3];
_ac1 = _system.CPU.R[2];
_ac2 = _system.CPU.R[1];
_ac3 = _system.CPU.R[0];
Console.Write("{0}:FP: ", _fpInstructionCount);
switch (instruction)
{
case 0x7000:
Console.WriteLine("FPSetup");
_fpRegAddr = _system.CPU.R[3];
_fpRegCount = _system.MemoryBus.DebugReadWord(TaskType.Emulator, _fpRegAddr);
Console.WriteLine(" FP register address {0}, count {1}", Conversion.ToOctal(_fpRegAddr), _fpRegCount);
break;
case 0x7001:
Console.WriteLine("FML {0},{1} ({2},{3})", _ac0, _ac1, GetFloat(_ac0), GetFloat(_ac1));
break;
case 0x7002:
Console.WriteLine("FDV {0},{1} ({2},{3})", _ac0, _ac1, GetFloat(_ac0), GetFloat(_ac1));
break;
case 0x7003:
Console.WriteLine("FAD {0},{1} ({2},{3})", _ac0, _ac1, GetFloat(_ac0), GetFloat(_ac1));
break;
case 0x7004:
Console.WriteLine("FSB {0},{1} ({2},{3})", _ac0, _ac1, GetFloat(_ac0), GetFloat(_ac1));
break;
case 0x7005:
Console.WriteLine("FLD {0},{1} (src {2})", _ac0, _ac1, GetFloat(_ac1));
break;
case 0x7006:
Console.WriteLine("FLDV {0},{1} (src {2})", _ac0, _ac1, GetFloatFromInternalFormat(_ac1));
break;
case 0x7007:
Console.WriteLine("FSTV {0},{1} (src {2})", _ac0, _ac1, GetFloat(_ac0));
break;
case 0x7008:
Console.WriteLine("FLDI {0},{1}", _ac0, Conversion.ToOctal(_ac1));
break;
case 0x7009:
Console.WriteLine("FTR {0} ({1})", _ac0, GetFloat(_ac0));
break;
case 0x700a:
Console.WriteLine("FNEG {0} ({1})", _ac0, GetFloat(_ac0));
break;
case 0x700b:
Console.WriteLine("FSN {0} ({1})", _ac0, GetFloat(_ac0));
break;
case 0x700c:
Console.WriteLine("FCM {0} ({1})", _ac0, GetFloat(_ac0));
break;
case 0x700d:
Console.WriteLine("FST");
break;
case 0x700e:
Console.WriteLine("FLDDP");
break;
case 0x700f:
Console.WriteLine("FSTDP");
break;
case 0x7010:
Console.WriteLine("DPAD");
break;
case 0x7011:
Console.WriteLine("DPSB");
break;
case 0x7012:
Console.WriteLine("FEXP {0},{1} ({2})", _ac0, _ac1, GetFloat(_ac0));
break;
}
/*
Console.WriteLine(" AC0={0} AC1={1} AC2={2} AC3={3}",
Conversion.ToOctal(_ac0),
Conversion.ToOctal(_ac1),
Conversion.ToOctal(_ac2),
Conversion.ToOctal(_ac3)); */
}
}
private void FloatDebugPost()
{
Console.Write("{0}:Post: ", _fpInstructionCount);
switch (_lastFPInstruction)
{
case 0x7000:
Console.WriteLine("FPSetup done.");
break;
case 0x7001:
Console.WriteLine("Result {0}", GetFloat(_ac0));
break;
case 0x7002:
Console.WriteLine("Result {0}", GetFloat(_ac0));
break;
case 0x7003:
Console.WriteLine("Result {0}", GetFloat(_ac0));
break;
case 0x7004:
Console.WriteLine("Result {0}", GetFloat(_ac0));
break;
case 0x7005:
Console.WriteLine("Loaded {0}", GetFloat(_ac0));
break;
case 0x7006:
Console.WriteLine("Loaded {0}", GetFloat(_ac0));
break;
case 0x7007:
Console.WriteLine("FSTV done.");
break;
case 0x7008:
Console.WriteLine("Loaded {0}", GetFloat(_ac0));
break;
case 0x7009:
Console.WriteLine("Result {0}", GetFloat(_ac0));
break;
case 0x700a:
Console.WriteLine("Result {0}", GetFloat(_ac0));
break;
case 0x700b:
Console.WriteLine("Result {0}", _ac3);
break;
case 0x700c:
Console.WriteLine("Result {0}", _ac3);
break;
case 0x700d:
Console.WriteLine("FST done.");
break;
case 0x700e:
Console.WriteLine("FLDDP done.");
break;
case 0x700f:
Console.WriteLine("FSTDP done.");
break;
case 0x7010:
Console.WriteLine("DPAD done.");
break;
case 0x7011:
Console.WriteLine("DPSB done.");
break;
case 0x7012:
Console.WriteLine("Result {0}", GetFloat(_ac0));
break;
default:
throw new InvalidOperationException("Unexpected op for post.");
}
_lastFPInstruction = 0;
}
private double GetFloat(ushort arg)
{
// If arg is less than the number of registers, it's assumed
// to be a register; otherwise an address
if (arg < _fpRegCount)
{
return GetFloatFromUcode(arg);
}
else
{
return GetFloatFromPackedFormat(arg);
}
}
/// <summary>
/// Gets a float from memory in "packed" format
/// </summary>
/// <param name="addr"></param>
/// <returns></returns>
private double GetFloatFromPackedFormat(ushort addr)
{
//
// Packed format is only two words:
// structure FP: [
// sign bit 1 //1 if negative.
// expon bit 8 //excess 128 format (complemented if number <0)
// mantissa1 bit 7 //High order 7 bits of mantissa
// mantissa2 bit 16 //Low order 16 bits of mantissa
// ]
//
uint packedWord =
(uint)(_system.MemoryBus.DebugReadWord(TaskType.Emulator, addr) << 16) |
(uint)(_system.MemoryBus.DebugReadWord(TaskType.Emulator, (ushort)(addr + 1)));
double sign = (packedWord & 0x80000000) != 0 ? -1.0 : 1.0;
uint exponent = (packedWord & 0x7f800000) >> 23;
uint mantissa = (packedWord & 0x007fffff);
double val = 0.0;
for (int i = 0; i < 23; i++)
{
double bit = (mantissa & (0x00400000 >> i)) != 0 ? 1.0 : 0.0;
val += (bit * (1.0 / Math.Pow(2.0, (double)i)));
}
double adjustedExponent = exponent - 128.0;
val = sign * (val) * Math.Pow(2.0, adjustedExponent);
Console.WriteLine("packed: {0}", val);
return val;
}
/// <summary>
/// Gets the float value for register, from alto code
/// </summary>
/// <param name="reg"></param>
/// <returns></returns>
private double GetFloatFromInternalFormat(ushort addr)
{
// Internal format is 4 words long:
// Word 0: sign
// Word 1: exponent
// Word 2-3: mantissa
//
double sign = (_system.MemoryBus.DebugReadWord(TaskType.Emulator, addr)) != 0 ? -1.0 : 1.0;
int exponent = (int)(short)(_system.MemoryBus.DebugReadWord(TaskType.Emulator, (ushort)(addr + 1)));
uint mantissa =
(uint)(_system.MemoryBus.DebugReadWord(TaskType.Emulator, (ushort)(addr + 2)) << 16) |
(uint)(_system.MemoryBus.DebugReadWord(TaskType.Emulator, (ushort)(addr + 3)));
double valMantissa = 0.0;
for (int i = 0; i < 32; i++)
{
double bit = (mantissa & (0x80000000 >> i)) != 0 ? 1.0 : 0.0;
valMantissa += (bit * (1.0 / Math.Pow(2.0, (double)i)));
}
double val = sign * (valMantissa) * Math.Pow(2.0, exponent - 1);
//if (double.IsInfinity(val) || double.IsNaN(val))
{
Console.WriteLine(" Vec: sign {0} exp {1} mantissa {2:x} ({3}) value {4}",
sign,
exponent,
mantissa,
valMantissa,
val);
}
return val;
}
/// <summary>
/// Gets the float value for register, from ucode store
/// </summary>
/// <param name="reg"></param>
/// <returns></returns>
private double GetFloatFromUcode(ushort reg)
{
// Internal format is 4 words long:
// Word 0: sign
// Word 1: exponent
// Word 2-3: mantissa
//
// In current ucode, each word is staggered across the FP buffer space rather than being in linear order to prevent having to do multiplies.
// For FP register N of M total registers starting at offset O:
// Word 0 is at O + N + 1
// Word 1 is at O + N + M + 1
// Word 2 is at O + N + 2*M + 1
// Word 3 is at O + N + 3*M + 1
ushort oreg = reg;
reg += _fpRegAddr;
reg++;
//Console.WriteLine("reg base {0}, num {1} addr {2}", _fpRegAddr, oreg, Conversion.ToOctal(reg));
// reg is now an address; read things in...
double sign = (_system.MemoryBus.DebugReadWord(TaskType.Emulator, reg)) != 0 ? -1.0 : 1.0;
int exponent = (int)(short)(_system.MemoryBus.DebugReadWord(TaskType.Emulator, (ushort)(reg + _fpRegCount)));
uint mantissa =
(uint)(_system.MemoryBus.DebugReadWord(TaskType.Emulator, (ushort)(reg + 2 * _fpRegCount)) << 16) |
(uint)(_system.MemoryBus.DebugReadWord(TaskType.Emulator, (ushort)(reg + 3 * _fpRegCount)));
double valMantissa = 0.0;
for (int i = 0; i < 32; i++)
{
double bit = (mantissa & (0x80000000 >> i)) != 0 ? 1.0 : 0.0;
valMantissa += (bit * (1.0 / Math.Pow(2.0, (double)i)));
}
double val = sign * (valMantissa) * Math.Pow(2.0, exponent - 1);
//if (double.IsInfinity(val) || double.IsNaN(val))
{
Console.WriteLine(" UCode: sign {0} exp {1} mantissa {2:x} ({3}) value {4}",
sign,
exponent,
mantissa,
valMantissa,
val);
}
return val;
}
#endif
private void SetExecutionState(ExecutionState state)
{
_execState = state;
this.BeginInvoke(new StepDelegate(RefreshUI));
}
private enum ExecutionType
{
None = 0,
Step,
Auto,
Normal,
NextTask,
NextNovaInstruction,
}
private enum ExecutionState
{
Stopped = 0,
SingleStep,
AutoStep,
Running,
BreakpointStop,
InternalError,
}
private struct ReservedMemoryEntry
{
public ReservedMemoryEntry(ushort address, string name)
{
Address = address;
Name = name;
}
public ushort Address;
public string Name;
}
private ReservedMemoryEntry[] _reservedMemoryEntries =
{
new ReservedMemoryEntry(0x110, "DASTART"),
new ReservedMemoryEntry(0x111, "V.INT"),
new ReservedMemoryEntry(0x112, "ITQUAN"),
new ReservedMemoryEntry(0x113, "ITBITS"),
new ReservedMemoryEntry(0x114, "MOUSEX"),
new ReservedMemoryEntry(0x115, "MOUSEY"),
new ReservedMemoryEntry(0x116, "CURSORX"),
new ReservedMemoryEntry(0x117, "CURSORY"),
new ReservedMemoryEntry(0x118, "RTC"),
new ReservedMemoryEntry(0x119, "CURMAP0"),
new ReservedMemoryEntry(0x11a, "CURMAP1"),
new ReservedMemoryEntry(0x11b, "CURMAP2"),
new ReservedMemoryEntry(0x11c, "CURMAP3"),
new ReservedMemoryEntry(0x11d, "CURMAP4"),
new ReservedMemoryEntry(0x11e, "CURMAP5"),
new ReservedMemoryEntry(0x11f, "CURMAP6"),
new ReservedMemoryEntry(0x120, "CURMAP7"),
new ReservedMemoryEntry(0x121, "CURMAP8"),
new ReservedMemoryEntry(0x122, "CURMAP9"),
new ReservedMemoryEntry(0x123, "CURMAP10"),
new ReservedMemoryEntry(0x124, "CURMAP11"),
new ReservedMemoryEntry(0x125, "CURMAP12"),
new ReservedMemoryEntry(0x126, "CURMAP13"),
new ReservedMemoryEntry(0x127, "CURMAP14"),
new ReservedMemoryEntry(0x128, "CURMAP15"),
new ReservedMemoryEntry(0x12a, "WW"),
new ReservedMemoryEntry(0x12b, "ACTIVE"),
new ReservedMemoryEntry(0x140, "PCLOC"),
new ReservedMemoryEntry(0x141, "INTVEC0"),
new ReservedMemoryEntry(0x142, "INTVEC1"),
new ReservedMemoryEntry(0x143, "INTVEC2"),
new ReservedMemoryEntry(0x144, "INTVEC3"),
new ReservedMemoryEntry(0x145, "INTVEC4"),
new ReservedMemoryEntry(0x146, "INTVEC5"),
new ReservedMemoryEntry(0x147, "INTVEC6"),
new ReservedMemoryEntry(0x148, "INTVEC7"),
new ReservedMemoryEntry(0x149, "INTVEC8"),
new ReservedMemoryEntry(0x14a, "INTVEC9"),
new ReservedMemoryEntry(0x14b, "INTVEC10"),
new ReservedMemoryEntry(0x14c, "INTVEC11"),
new ReservedMemoryEntry(0x14d, "INTVEC12"),
new ReservedMemoryEntry(0x14e, "INTVEC13"),
new ReservedMemoryEntry(0x14f, "INTVEC14"),
new ReservedMemoryEntry(0x151, "KBLK"),
new ReservedMemoryEntry(0x152, "KSTAT"),
new ReservedMemoryEntry(0x153, "KADDR"),
new ReservedMemoryEntry(0x154, "S.INTBM"),
new ReservedMemoryEntry(0x155, "ITTIM"),
new ReservedMemoryEntry(0x156, "TRAPPC"),
new ReservedMemoryEntry(0x180, "EPLOC"),
new ReservedMemoryEntry(0x181, "EBLOC"),
new ReservedMemoryEntry(0x182, "EELOC"),
new ReservedMemoryEntry(0x183, "ELLOC"),
new ReservedMemoryEntry(0x184, "EICLOC"),
new ReservedMemoryEntry(0x185, "EIPLOC"),
new ReservedMemoryEntry(0x186, "EOCLOC"),
new ReservedMemoryEntry(0x187, "EOPLOC"),
new ReservedMemoryEntry(0x188, "EHLOC"),
};
private AltoSystem _system;
private ExecutionController _controller;
// Unicode character for the Arrow used by Alto microcode
private const char _arrowChar = (char)0x2190;
// Execution / error state
private bool _execAbort;
private ExecutionState _execState;
private ExecutionType _execType;
private string _lastExceptionText;
private delegate void StepDelegate();
// Microcode Debugger breakpoints; one entry per address since we only need
// to worry about a 10 bit address space, this is fast and uses little memory.
private bool[,] _microcodeBreakpointEnabled;
// Nova Debugger breakpoints; same as above
private bool[] _novaBreakpointEnabled;
private void HackButton_Click(object sender, EventArgs e)
{
//_system.CPU.HAX = true;
Log.Write(Logging.LogComponent.Debug, "***** HACK HIT ******");
}
}
}