1
0
mirror of https://github.com/livingcomputermuseum/ContrAlto.git synced 2026-01-21 02:07:30 +00:00

Minor fixes for debugger interface and disassembler; started hooking Ethernet interface up to real host Ethernet (because MazeWar.)

This commit is contained in:
Josh Dersch 2016-01-12 17:29:42 -08:00
parent 30ed445764
commit 898abdc906
11 changed files with 339 additions and 49 deletions

View File

@ -185,6 +185,12 @@ namespace Contralto.CPU
int c = (_output & 0x1) << 15;
_output = (ushort)((_output >> 1) | c);
}
if (_dns)
{
// Should never happen
throw new InvalidOperationException("DNS on Rotate Right, not possible.");
}
break;
default:

View File

@ -117,7 +117,7 @@ namespace Contralto.CPU
// Dispatch to the appropriate device.
// The Ethernet controller is the only common device that is documented
// to have used STARTF, so we'll just go there directly; if other
// hardware is discovered we'll put together a more flexible dispatch.
// hardware is discovered to be worth emulating we'll put together a more flexible dispatch.
//
if (_busData < 4)
{
@ -186,9 +186,6 @@ namespace Contralto.CPU
//
// "...DNS also addresses R from (3-IR[3 - 4])..."
//
// TODO: is S reg select impacted by this?
//_srSelect =
_rSelect = (_rSelect & 0xfffc) | ((((uint)_cpu._ir & 0x1800) >> 11) ^ 3);
break;

View File

@ -27,6 +27,11 @@ namespace Contralto.CPU
get { return (int)_taskType; }
}
public TaskType TaskType
{
get { return _taskType; }
}
public bool Wakeup
{
get { return _wakeup; }

View File

@ -38,12 +38,12 @@ namespace Contralto.CPU
break;
case BusSource.LoadR:
source = "0 ";
source = "L ";
loadR = true;
break;
case BusSource.None:
source = "177777 ";
source = "177777(no source) ";
break;
case BusSource.TaskSpecific1:
@ -289,7 +289,7 @@ namespace Contralto.CPU
default:
loadS = false;
return String.Format("BS{0}", Conversion.ToOctal((int)instruction.BS));
return String.Format("BS {0}", Conversion.ToOctal((int)instruction.BS));
}
}
@ -301,7 +301,7 @@ namespace Contralto.CPU
return DisassembleEmulatorSpecialFunction1(instruction);
default:
return String.Format("F1{0}", Conversion.ToOctal((int)instruction.F1));
return String.Format("F1 {0}", Conversion.ToOctal((int)instruction.F1));
}
}
@ -313,7 +313,7 @@ namespace Contralto.CPU
return DisassembleEmulatorSpecialFunction2(instruction);
default:
return String.Format("F2{0}", Conversion.ToOctal((int)instruction.F2));
return String.Format("F2 {0}", Conversion.ToOctal((int)instruction.F2));
}
}
@ -367,7 +367,7 @@ namespace Contralto.CPU
return "STARTF ";
default:
return String.Format("F1{0}", Conversion.ToOctal((int)ef1));
return String.Format("F1 {0}", Conversion.ToOctal((int)ef1));
}
}
@ -400,7 +400,7 @@ namespace Contralto.CPU
return "IDISP ";
default:
return String.Format("F2{0}", Conversion.ToOctal((int)ef2));
return String.Format("F2 {0}", Conversion.ToOctal((int)ef2));
}
}
}

View File

@ -93,10 +93,9 @@ namespace Contralto.CPU
get { return _uCodeRam; }
}
public static MicrocodeBank Bank
{
// Just return the Bank for the Emulator task for now
get { return _microcodeBank[(int)TaskType.Emulator]; }
public static MicrocodeBank GetBank(TaskType task)
{
return _microcodeBank[(int)task];
}
public static void LoadControlRAMAddress(ushort address)

View File

@ -52,6 +52,18 @@
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="PcapDotNet.Base">
<HintPath>pcap\PcapDotNet.Base.dll</HintPath>
</Reference>
<Reference Include="PcapDotNet.Core">
<HintPath>pcap\PcapDotNet.Core.dll</HintPath>
</Reference>
<Reference Include="PcapDotNet.Core.Extensions">
<HintPath>pcap\PcapDotNet.Core.Extensions.dll</HintPath>
</Reference>
<Reference Include="PcapDotNet.Packets">
<HintPath>pcap\PcapDotNet.Packets.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
@ -108,6 +120,7 @@
<Compile Include="IO\DiskController.cs" />
<Compile Include="IO\DiabloPack.cs" />
<Compile Include="IO\EthernetController.cs" />
<Compile Include="IO\HostEthernet.cs" />
<Compile Include="IO\Keyboard.cs" />
<Compile Include="IO\Mouse.cs" />
<Compile Include="Logging\Log.cs" />

View File

@ -214,7 +214,7 @@
this._rom0SourceViewer.Size = new System.Drawing.Size(582, 554);
this._rom0SourceViewer.TabIndex = 1;
this._rom0SourceViewer.TabStop = false;
this._rom0SourceViewer.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.SourceViewCellClick);
this._rom0SourceViewer.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.Rom0SourceViewCellClick);
//
// Breakpoint
//
@ -314,6 +314,7 @@
this._rom1SourceViewer.Size = new System.Drawing.Size(582, 545);
this._rom1SourceViewer.TabIndex = 2;
this._rom1SourceViewer.TabStop = false;
this._rom1SourceViewer.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.Rom1SourceViewCellClick);
//
// dataGridViewCheckBoxColumn1
//
@ -413,6 +414,7 @@
this._ram0SourceViewer.Size = new System.Drawing.Size(582, 545);
this._ram0SourceViewer.TabIndex = 2;
this._ram0SourceViewer.TabStop = false;
this._ram0SourceViewer.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.Ram0SourceViewCellClick);
//
// dataGridViewCheckBoxColumn2
//

View File

@ -25,7 +25,7 @@ namespace Contralto
{
_system = system;
_controller = controller;
_microcodeBreakpointEnabled = new bool[1024];
_microcodeBreakpointEnabled = new bool[3,1024];
_novaBreakpointEnabled = new bool[65536];
_controller.StepCallback += OnExecutionStep;
@ -143,22 +143,24 @@ namespace Contralto
//
// Select active tab based on current UCode bank
switch (UCodeMemory.Bank)
MicrocodeBank bank = UCodeMemory.GetBank(_system.CPU.CurrentTask.TaskType);
switch (bank)
{
case MicrocodeBank.ROM0:
SourceTabs.TabIndex = 0;
SourceTabs.SelectedIndex = 0;
break;
case MicrocodeBank.ROM1:
SourceTabs.TabIndex = 1;
SourceTabs.SelectedIndex = 1;
break;
case MicrocodeBank.RAM0:
SourceTabs.TabIndex = 2;
SourceTabs.SelectedIndex = 2;
break;
}
RefreshMicrocodeDisassembly();
RefreshMicrocodeDisassembly(_system.CPU.CurrentTask.MPC);
// Highlight the nova memory location corresponding to the emulator PC.
// TODO: this should be configurable
@ -197,25 +199,23 @@ namespace Contralto
this.BringToFront();
}
private void RefreshMicrocodeDisassembly()
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, _system.CPU.CurrentTask.MPC);
HighlightMicrocodeSourceLine(_rom0SourceViewer, address);
break;
case 1:
SourceTabs.TabIndex = 1;
HighlightMicrocodeSourceLine(_rom1SourceViewer, _system.CPU.CurrentTask.MPC);
case 1:
HighlightMicrocodeSourceLine(_rom1SourceViewer, address);
break;
case 2:
SourceTabs.TabIndex = 2;
RefreshMicrocodeDisassembly(MicrocodeBank.RAM0);
HighlightMicrocodeSourceLine(_ram0SourceViewer, _system.CPU.CurrentTask.MPC);
case 2:
UpdateMicrocodeDisassembly(MicrocodeBank.RAM0);
HighlightMicrocodeSourceLine(_ram0SourceViewer, address);
break;
}
}
@ -272,21 +272,60 @@ namespace Contralto
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SourceViewCellClick(object sender, DataGridViewCellEventArgs e)
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)
{
// See if this is a source line, if so check/uncheck the box
// and set/unset a breakpoint for the line
if (_rom0SourceViewer.Rows[e.RowIndex].Tag != null)
{
bool value = (bool)_rom0SourceViewer.Rows[e.RowIndex].Cells[0].Value;
_rom0SourceViewer.Rows[e.RowIndex].Cells[0].Value = !value;
SetBreakpointFromCellClick(MicrocodeBank.ROM1, e.RowIndex);
}
}
ModifyMicrocodeBreakpoint((UInt16)_rom0SourceViewer.Rows[e.RowIndex].Tag, !value);
}
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);
}
}
@ -303,7 +342,7 @@ namespace Contralto
}
}
private void RefreshMicrocodeDisassembly(MicrocodeBank bank)
private void UpdateMicrocodeDisassembly(MicrocodeBank bank)
{
DataGridView view = null;
uint[] uCode = null;
@ -414,9 +453,9 @@ namespace Contralto
}
private void ModifyMicrocodeBreakpoint(UInt16 address, bool set)
private void ModifyMicrocodeBreakpoint(MicrocodeBank bank, UInt16 address, bool set)
{
_microcodeBreakpointEnabled[address] = set;
_microcodeBreakpointEnabled[(int)bank,address] = set;
}
private bool GetNovaBreakpoint(UInt16 address)
@ -629,7 +668,7 @@ namespace Contralto
private void OnTabChanged(object sender, EventArgs e)
{
RefreshMicrocodeDisassembly();
RefreshMicrocodeDisassembly(_system.CPU.CurrentTask.MPC);
}
private void Debugger_Load(object sender, EventArgs e)
@ -647,7 +686,7 @@ namespace Contralto
UInt16 address = Convert.ToUInt16(JumpToAddress.Text, 8);
// find the source address that matches this, if any.
HighlightMicrocodeSourceLine(_rom0SourceViewer, address);
RefreshMicrocodeDisassembly(address);
}
catch
{
@ -768,7 +807,7 @@ namespace Contralto
case ExecutionType.NextNovaInstruction:
// See if we need to stop here
if (_execAbort || // The Stop button was hit
_microcodeBreakpointEnabled[_system.CPU.CurrentTask.MPC] || // A microcode breakpoint 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
@ -914,7 +953,7 @@ namespace Contralto
// 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;
private bool[,] _microcodeBreakpointEnabled;
// Nova Debugger breakpoints; same as above
private bool[] _novaBreakpointEnabled;

View File

@ -2,6 +2,7 @@
using Contralto.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@ -21,6 +22,8 @@ namespace Contralto.IO
Reset();
_fifoWakeupEvent = new Event(_fifoTransmitDuration, null, FIFOCallback);
_hostEthernet = new HostEthernet(null);
}
public void Reset()
@ -208,7 +211,9 @@ namespace Contralto.IO
Log.Write(LogComponent.EthernetController, "Sending {0} words from fifo.", _fifo.Count);
// TODO: actually transmit data in FIFO rather than bit-bucketing it.
// Copy FIFO to host ethernet output buffer
_fifo.CopyTo(_outputData, _outputIndex);
_outputIndex += _fifo.Count;
_fifo.Clear();
if (!end)
@ -224,6 +229,10 @@ namespace Contralto.IO
// Wakeup at end of transmission. ("OUTGONE Post wakeup.")
_system.CPU.WakeupTask(TaskType.Ethernet);
// And actually tell the host ethernet interface to send the data.
_hostEthernet.Send(_outputData, _outputIndex);
_outputIndex = 0;
}
}
@ -231,6 +240,32 @@ namespace Contralto.IO
{
// TODO: pull next packet off host ethernet interface that's destined for the Alto, and start the
// process of putting into the FIFO and generating wakeups for the microcode.
_receiverWaiting = true;
}
/// <summary>
/// Invoked when the host ethernet interface receives a packet destined for us.
/// TODO: determine the best behavior here; we could queue up a number of packets and let
/// the emulated interface pull them off one by one, or we could only save one packet and discard
/// any that arrive while the emulated interface is processing the current one.
///
/// The latter is probably more faithful to the intent, but the former might be more useful on
/// busy Ethernets (though the bottom-level filter implemented by HostEthernet might take care
/// of that already.)
///
/// For now, we just accept one at a time.
/// </summary>
/// <param name="data"></param>
private void OnHostPacketReceived(MemoryStream data)
{
if (_incomingPacket == null)
{
_incomingPacket = data;
}
else
{
// Drop.
}
}
private Queue<ushort> _fifo;
@ -253,6 +288,16 @@ namespace Contralto.IO
private ulong _fifoTransmitDuration = 87075; // ~87000 nsec to transmit 16 words at 3mbit, assuming no collision
private Event _fifoWakeupEvent;
// The actual connection to a real Ethernet device on the host
HostEthernet _hostEthernet;
// Buffer to hold outgoing data to the host ethernet
ushort[] _outputData;
int _outputIndex;
// Incoming data
MemoryStream _incomingPacket;
private AltoSystem _system;
}
}

View File

@ -0,0 +1,183 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PcapDotNet.Base;
using PcapDotNet.Core;
using PcapDotNet.Core.Extensions;
using PcapDotNet.Packets;
using PcapDotNet.Packets.Ethernet;
using System.IO;
namespace Contralto.IO
{
public struct EthernetInterface
{
public EthernetInterface(string name, string description, MacAddress macAddress)
{
Name = name;
Description = description;
MacAddress = macAddress;
}
public static List<EthernetInterface> EnumerateDevices()
{
List<EthernetInterface> interfaces = new List<EthernetInterface>();
foreach (LivePacketDevice device in LivePacketDevice.AllLocalMachine)
{
interfaces.Add(new EthernetInterface(device.Name, device.Description, device.GetMacAddress()));
}
return interfaces;
}
public string Name;
public string Description;
public MacAddress MacAddress;
}
public delegate void ReceivePacketDelegate(MemoryStream data);
/// <summary>
/// Implements the logic for encapsulating a 3mbit ethernet packet into a 10mb packet and sending it over an actual
/// interface controlled by the host operating system.
///
/// This uses PCap.NET to do the dirty work.
/// </summary>
public class HostEthernet
{
public HostEthernet(EthernetInterface iface)
{
AttachInterface(iface);
}
public void RegisterReceiveCallback(ReceivePacketDelegate callback)
{
_callback = callback;
// Now that we have a callback we can start receiving stuff.
Open(false /* not promiscuous */, int.MaxValue);
BeginReceive();
}
/// <summary>
/// Sends an array of bytes over the ethernet as a 3mbit packet encapsulated in a 10mbit packet.
/// </summary>
/// <param name="packet"></param>
/// <param name="hostId"></param>
public void Send(ushort[] packet, int length)
{
// Sanity check.
if (length < 1)
{
throw new InvalidOperationException("Raw packet data must contain at least two bytes for addressing.");
}
//
// Do this annoying dance to stuff the ushorts into bytes because this is C#.
//
byte[] packetBytes = new byte[length * 2];
for (int i = 0; i < length; i++)
{
packetBytes[i * 2] = (byte)(packet[i] >> 8);
packetBytes[i * 2 + 1] = (byte)packet[i];
}
//
// Grab the source and destination host addresses from the packet we're sending
// and build 10mbit versions.
//
byte destinationHost = packetBytes[0];
byte sourceHost = packetBytes[1];
MacAddress destinationMac = new MacAddress((UInt48)(_10mbitMACPrefix | destinationHost));
MacAddress sourceMac = new MacAddress((UInt48)(_10mbitMACPrefix | sourceHost));
// Build the outgoing packet; place the source/dest addresses, type field and the raw data.
EthernetLayer ethernetLayer = new EthernetLayer
{
Source = sourceMac,
Destination = destinationMac,
EtherType = (EthernetType)_3mbitFrameType,
};
PayloadLayer payloadLayer = new PayloadLayer
{
Data = new Datagram(packetBytes),
};
PacketBuilder builder = new PacketBuilder(ethernetLayer, payloadLayer);
// Send it over the 'net!
_communicator.SendPacket(builder.Build(DateTime.Now));
}
private void ReceiveCallback(Packet p)
{
//
// Filter out packets intended for the emulator, forward them on, drop everything else.
//
if ((int)p.Ethernet.EtherType == _3mbitFrameType &&
(p.Ethernet.Destination.ToValue() & 0xffffffffff00) == _10mbitMACPrefix )
{
_callback(p.Ethernet.Payload.ToMemoryStream());
}
else
{
// Not for us, discard the packet.
}
}
private void AttachInterface(EthernetInterface iface)
{
_interface = null;
// Find the specified device by name
foreach (LivePacketDevice device in LivePacketDevice.AllLocalMachine)
{
if (device.Name == iface.Name && device.GetMacAddress() == iface.MacAddress)
{
_interface = device;
break;
}
}
if (_interface == null)
{
throw new InvalidOperationException("Requested interface not found.");
}
}
private void Open(bool promiscuous, int timeout)
{
_communicator = _interface.Open(0xffff, promiscuous ? PacketDeviceOpenAttributes.Promiscuous : PacketDeviceOpenAttributes.None, timeout);
}
/// <summary>
/// Begin receiving packets, forever.
/// </summary>
private void BeginReceive()
{
_communicator.ReceivePackets(-1, ReceiveCallback);
}
private LivePacketDevice _interface;
private PacketCommunicator _communicator;
private ReceivePacketDelegate _callback;
private const int _3mbitFrameType = 0xbeef; // easy to identify, ostensibly unused by anything of any import
/// <summary>
/// On output, these bytes are prepended to the Alto's 3mbit (1 byte) address to form a full
/// 6 byte Ethernet MAC.
/// On input, ethernet frames are checked for this prefix
/// </summary>
private UInt48 _10mbitMACPrefix = 0x0000aa010200; // 00-00-AA is the old Xerox vendor code, used just to be cute.
}
}

View File

@ -257,10 +257,11 @@ namespace Contralto.Memory
case 3:
// debug
/*
if (_memoryAddress == 0xfc90 || _memoryAddress == 0xfc91 || _memoryAddress == 0x151) // 176220 -- status word for disk
{
Logging.Log.Write(Logging.LogComponent.Debug, "++> Task {0} wrote {1} to {3} (was {2}).", _task, Conversion.ToOctal(data), Conversion.ToOctal(DebugReadWord(_task, _memoryAddress)), Conversion.ToOctal(_memoryAddress));
}
}*/
_memoryData = data; // Only really necessary to show in debugger
// Start of doubleword write: