diff --git a/Contralto/CPU/Shifter.cs b/Contralto/CPU/Shifter.cs
index 9178e6c..122f98b 100644
--- a/Contralto/CPU/Shifter.cs
+++ b/Contralto/CPU/Shifter.cs
@@ -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:
diff --git a/Contralto/CPU/Tasks/EmulatorTask.cs b/Contralto/CPU/Tasks/EmulatorTask.cs
index 44c33e6..e61cfcd 100644
--- a/Contralto/CPU/Tasks/EmulatorTask.cs
+++ b/Contralto/CPU/Tasks/EmulatorTask.cs
@@ -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;
diff --git a/Contralto/CPU/Tasks/Task.cs b/Contralto/CPU/Tasks/Task.cs
index 3fa1ada..02a2d92 100644
--- a/Contralto/CPU/Tasks/Task.cs
+++ b/Contralto/CPU/Tasks/Task.cs
@@ -27,6 +27,11 @@ namespace Contralto.CPU
get { return (int)_taskType; }
}
+ public TaskType TaskType
+ {
+ get { return _taskType; }
+ }
+
public bool Wakeup
{
get { return _wakeup; }
diff --git a/Contralto/CPU/UCodeDisassembler.cs b/Contralto/CPU/UCodeDisassembler.cs
index eafafa4..037cb82 100644
--- a/Contralto/CPU/UCodeDisassembler.cs
+++ b/Contralto/CPU/UCodeDisassembler.cs
@@ -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));
}
}
}
diff --git a/Contralto/CPU/UCodeMemory.cs b/Contralto/CPU/UCodeMemory.cs
index c8ff2ac..744f9ef 100644
--- a/Contralto/CPU/UCodeMemory.cs
+++ b/Contralto/CPU/UCodeMemory.cs
@@ -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)
diff --git a/Contralto/Contralto.csproj b/Contralto/Contralto.csproj
index 24dac31..17e1b44 100644
--- a/Contralto/Contralto.csproj
+++ b/Contralto/Contralto.csproj
@@ -52,6 +52,18 @@
true
+
+ pcap\PcapDotNet.Base.dll
+
+
+ pcap\PcapDotNet.Core.dll
+
+
+ pcap\PcapDotNet.Core.Extensions.dll
+
+
+ pcap\PcapDotNet.Packets.dll
+
@@ -108,6 +120,7 @@
+
diff --git a/Contralto/Debugger.Designer.cs b/Contralto/Debugger.Designer.cs
index b18af4a..10c0996 100644
--- a/Contralto/Debugger.Designer.cs
+++ b/Contralto/Debugger.Designer.cs
@@ -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
//
diff --git a/Contralto/Debugger.cs b/Contralto/Debugger.cs
index 046d996..9d7d765 100644
--- a/Contralto/Debugger.cs
+++ b/Contralto/Debugger.cs
@@ -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
///
///
///
- 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;
diff --git a/Contralto/IO/EthernetController.cs b/Contralto/IO/EthernetController.cs
index 7e670c8..9f51610 100644
--- a/Contralto/IO/EthernetController.cs
+++ b/Contralto/IO/EthernetController.cs
@@ -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;
+ }
+
+ ///
+ /// 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.
+ ///
+ ///
+ private void OnHostPacketReceived(MemoryStream data)
+ {
+ if (_incomingPacket == null)
+ {
+ _incomingPacket = data;
+ }
+ else
+ {
+ // Drop.
+ }
}
private Queue _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;
}
}
diff --git a/Contralto/IO/HostEthernet.cs b/Contralto/IO/HostEthernet.cs
new file mode 100644
index 0000000..d139fe7
--- /dev/null
+++ b/Contralto/IO/HostEthernet.cs
@@ -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 EnumerateDevices()
+ {
+ List interfaces = new List();
+
+ 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);
+
+ ///
+ /// 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.
+ ///
+ 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();
+ }
+
+
+ ///
+ /// Sends an array of bytes over the ethernet as a 3mbit packet encapsulated in a 10mbit packet.
+ ///
+ ///
+ ///
+ 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);
+ }
+
+ ///
+ /// Begin receiving packets, forever.
+ ///
+ 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
+
+ ///
+ /// 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
+ ///
+ private UInt48 _10mbitMACPrefix = 0x0000aa010200; // 00-00-AA is the old Xerox vendor code, used just to be cute.
+ }
+}
diff --git a/Contralto/Memory/MemoryBus.cs b/Contralto/Memory/MemoryBus.cs
index 39e1c6f..f1d4c5a 100644
--- a/Contralto/Memory/MemoryBus.cs
+++ b/Contralto/Memory/MemoryBus.cs
@@ -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: