mirror of
https://github.com/livingcomputermuseum/ContrAlto.git
synced 2026-01-17 08:34:15 +00:00
Added configuration UI. Implemented ethernet encapsulation over UDP. A few minor tweaks.
This commit is contained in:
parent
209dea8052
commit
c73fb66dee
@ -47,7 +47,7 @@ namespace Contralto
|
||||
t.AutoReset = true;
|
||||
t.Interval = 1000;
|
||||
t.Elapsed += T_Elapsed;
|
||||
t.Start();
|
||||
//t.Start();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
@ -80,6 +80,11 @@ namespace Contralto
|
||||
_displayController.AttachDisplay(d);
|
||||
}
|
||||
|
||||
public void DetachDisplay()
|
||||
{
|
||||
_displayController.DetachDisplay();
|
||||
}
|
||||
|
||||
public void SingleStep()
|
||||
{
|
||||
// Run every device that needs attention for a single clock cycle.
|
||||
@ -128,7 +133,7 @@ namespace Contralto
|
||||
//
|
||||
if (Configuration.EthernetBootEnabled)
|
||||
{
|
||||
_keyboard.PressBootKeys(Configuration.EthernetBootFile, true);
|
||||
_keyboard.PressBootKeys(Configuration.BootAddress, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,20 +177,10 @@ namespace Contralto
|
||||
{
|
||||
get { return _scheduler; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Time (in msec) for one system clock
|
||||
/// </summary>
|
||||
///
|
||||
public static double ClockInterval
|
||||
{
|
||||
get { return 0.00017; } // appx 170nsec, TODO: more accurate value?
|
||||
}
|
||||
|
||||
|
||||
private void T_Elapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
//System.Console.WriteLine("{0} CPU clocks/sec %{1}. {2} fields/sec", _clocks, ((double)_clocks / 5882353.0) * 100.0, _displayController.Fields);
|
||||
System.Console.WriteLine("{0} CPU clocks/sec %{1}. {2} fields/sec", _clocks, ((double)_clocks / 5882353.0) * 100.0, _displayController.Fields);
|
||||
_clocks = 0;
|
||||
_displayController.Fields = 0;
|
||||
}
|
||||
|
||||
@ -172,7 +172,7 @@ namespace Contralto.CPU
|
||||
//
|
||||
WakeupTask(CPU.TaskType.DiskSector);
|
||||
|
||||
Logging.Log.Write(Logging.LogComponent.CPU, "Silent Boot; microcode banks initialized to {0}", Conversion.ToOctal(_rmr));
|
||||
Log.Write(Logging.LogComponent.CPU, "Silent Boot; microcode banks initialized to {0}", Conversion.ToOctal(_rmr));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -232,13 +232,15 @@ namespace Contralto.CPU
|
||||
if (_tasks[i] != null && _tasks[i].Wakeup)
|
||||
{
|
||||
_nextTask = _tasks[i];
|
||||
_nextTask.FirstInstructionAfterSwitch = true;
|
||||
|
||||
/*
|
||||
if (_nextTask != _currentTask && _currentTask != null)
|
||||
{
|
||||
Log.Write(LogComponent.TaskSwitch, "TASK: Next task will be {0} (pri {1}); current task {2} (pri {3})",
|
||||
(TaskType)_nextTask.Priority, _nextTask.Priority,
|
||||
(TaskType)_currentTask.Priority, _currentTask.Priority);
|
||||
}
|
||||
} */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,6 +42,17 @@ namespace Contralto.CPU
|
||||
get { return _mpc; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether a task switch just happened. TASK instructions behave differently on the
|
||||
/// first instruction after a switch. This is not documented, but observed on the real hardware.
|
||||
/// (See the implementation of the Task SF for more details.)
|
||||
/// </summary>
|
||||
public bool FirstInstructionAfterSwitch
|
||||
{
|
||||
get { return _firstInstructionAfterSwitch; }
|
||||
set { _firstInstructionAfterSwitch = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the current uInstruction asserts BLOCK.
|
||||
/// Used by hardware for various tasks.
|
||||
@ -59,6 +70,7 @@ namespace Contralto.CPU
|
||||
_mpc = (ushort)_taskType;
|
||||
_rdRam = false;
|
||||
_rb = 0;
|
||||
_firstInstructionAfterSwitch = false;
|
||||
}
|
||||
|
||||
public virtual void SoftReset()
|
||||
@ -273,7 +285,16 @@ namespace Contralto.CPU
|
||||
break;
|
||||
|
||||
case SpecialFunction1.Task:
|
||||
nextTask = true; // Yield to other more important tasks
|
||||
//
|
||||
// If the first uOp executed after a task switch contains a TASK F1, it does not take effect.
|
||||
// This is observed on the real hardware, and does not appear to be documented.
|
||||
// It also doensn't appear to affect the execution of the standard Alto uCode in any significant
|
||||
// way, but is included here for correctness.
|
||||
//
|
||||
//if (!_firstInstructionAfterSwitch)
|
||||
{
|
||||
nextTask = true; // Yield to other more important tasks
|
||||
}
|
||||
break;
|
||||
|
||||
case SpecialFunction1.Block:
|
||||
@ -430,7 +451,7 @@ namespace Contralto.CPU
|
||||
if (swMode)
|
||||
{
|
||||
UCodeMemory.SwitchMode(instruction.NEXT, _taskType);
|
||||
Logging.Log.Write(Logging.LogComponent.Microcode, "SWMODE: uPC {0}, next uPC {1}", Conversion.ToOctal(_mpc), Conversion.ToOctal(instruction.NEXT | nextModifier));
|
||||
Logging.Log.Write(Logging.LogComponent.Microcode, "SWMODE: uPC {0}, next uPC {1}", Conversion.ToOctal(_mpc), Conversion.ToOctal(instruction.NEXT));
|
||||
}
|
||||
|
||||
//
|
||||
@ -450,6 +471,7 @@ namespace Contralto.CPU
|
||||
_mpc = (ushort)(instruction.NEXT | nextModifier);
|
||||
}
|
||||
|
||||
_firstInstructionAfterSwitch = false;
|
||||
return nextTask;
|
||||
}
|
||||
|
||||
@ -523,6 +545,7 @@ namespace Contralto.CPU
|
||||
protected ushort _rb; // S register bank select
|
||||
protected TaskType _taskType;
|
||||
protected bool _wakeup;
|
||||
protected bool _firstInstructionAfterSwitch;
|
||||
|
||||
protected bool _block;
|
||||
|
||||
|
||||
@ -40,17 +40,14 @@ namespace Contralto.CPU
|
||||
|
||||
private static void Init()
|
||||
{
|
||||
//
|
||||
// TODO: this is currently configured for a 2K ROM machine
|
||||
// (1K RAM, 2K ROM). This should be configurable.
|
||||
//
|
||||
// 1 bank of microcode RAM
|
||||
_uCodeRam = new UInt32[1024];
|
||||
//
|
||||
// Max 3 banks of microcode RAM
|
||||
_uCodeRam = new UInt32[1024 * 3];
|
||||
LoadMicrocode(_uCodeRoms);
|
||||
|
||||
//
|
||||
// Cache 3k of instructions: 2K ROM, 1K RAM.
|
||||
_decodeCache = new MicroInstruction[1024 * 3];
|
||||
// Cache 5k of instructions: max 2K ROM, 3K RAM.
|
||||
_decodeCache = new MicroInstruction[1024 * 5];
|
||||
|
||||
// Precache ROM
|
||||
CacheMicrocodeROM();
|
||||
@ -103,12 +100,22 @@ namespace Contralto.CPU
|
||||
_ramBank = (address & 0x3000) >> 12;
|
||||
_ramSelect = (address & 0x0800) == 0;
|
||||
_lowHalfsel = (address & 0x0400) == 0;
|
||||
_ramAddr = (address & 0x3ff);
|
||||
|
||||
if (_ramBank != 0)
|
||||
_ramAddr = (address & 0x3ff);
|
||||
|
||||
// Clip RAM bank into range, it's always 0 unless we have a 3K uCode RAM system
|
||||
switch (Configuration.SystemType)
|
||||
{
|
||||
//throw new NotImplementedException(String.Format("Unexpected RAM BANK select of {0}", _ramBank));
|
||||
_ramBank = 0;
|
||||
case SystemType.OneKRom:
|
||||
case SystemType.TwoKRom:
|
||||
_ramBank = 0;
|
||||
break;
|
||||
case SystemType.ThreeKRam:
|
||||
if (_ramBank > 3)
|
||||
{
|
||||
// TODO: clip or not.
|
||||
throw new InvalidOperationException(String.Format("Unexpected RAM bank value of {0}.", _ramBank));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,26 +128,35 @@ namespace Contralto.CPU
|
||||
public static void SwitchMode(ushort nextAddress, TaskType task)
|
||||
{
|
||||
Logging.Log.Write(Logging.LogComponent.Microcode, "SWMODE: Current Bank {0}", _microcodeBank[(int)task]);
|
||||
|
||||
// 2K ROM
|
||||
switch(_microcodeBank[(int)task])
|
||||
|
||||
switch (Configuration.SystemType)
|
||||
{
|
||||
case MicrocodeBank.ROM0:
|
||||
_microcodeBank[(int)task] = (nextAddress & 0x100) == 0 ? MicrocodeBank.RAM0 : MicrocodeBank.ROM1;
|
||||
case SystemType.OneKRom:
|
||||
_microcodeBank[(int)task] = _microcodeBank[(int)task] == MicrocodeBank.ROM0 ? MicrocodeBank.RAM0 : MicrocodeBank.ROM0;
|
||||
break;
|
||||
|
||||
case MicrocodeBank.ROM1:
|
||||
_microcodeBank[(int)task] = (nextAddress & 0x100) == 0 ? MicrocodeBank.ROM0 : MicrocodeBank.RAM0;
|
||||
case SystemType.TwoKRom:
|
||||
switch (_microcodeBank[(int)task])
|
||||
{
|
||||
case MicrocodeBank.ROM0:
|
||||
_microcodeBank[(int)task] = (nextAddress & 0x100) == 0 ? MicrocodeBank.RAM0 : MicrocodeBank.ROM1;
|
||||
break;
|
||||
|
||||
case MicrocodeBank.ROM1:
|
||||
_microcodeBank[(int)task] = (nextAddress & 0x100) == 0 ? MicrocodeBank.ROM0 : MicrocodeBank.RAM0;
|
||||
break;
|
||||
|
||||
case MicrocodeBank.RAM0:
|
||||
_microcodeBank[(int)task] = (nextAddress & 0x100) == 0 ? MicrocodeBank.ROM0 : MicrocodeBank.ROM1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case MicrocodeBank.RAM0:
|
||||
_microcodeBank[(int)task] = (nextAddress & 0x100) == 0 ? MicrocodeBank.ROM0 : MicrocodeBank.ROM1;
|
||||
case SystemType.ThreeKRam:
|
||||
throw new NotImplementedException("3K uCode RAM not yet implemented.");
|
||||
break;
|
||||
}
|
||||
|
||||
// for 1K ROM -- todo: make configurable
|
||||
//_microcodeBank[(int)task] = _microcodeBank[(int)task] == MicrocodeBank.ROM0 ? MicrocodeBank.RAM0 : MicrocodeBank.ROM0;
|
||||
|
||||
Logging.Log.Write(Logging.LogComponent.Microcode, "SWMODE: New Bank {0} for Task {1}", _microcodeBank[(int)task], task);
|
||||
}
|
||||
|
||||
|
||||
@ -1,11 +1,48 @@
|
||||
using System;
|
||||
using Contralto.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Contralto
|
||||
{
|
||||
/// <summary>
|
||||
/// The configuration of an Alto II to emulate
|
||||
/// </summary>
|
||||
public enum SystemType
|
||||
{
|
||||
/// <summary>
|
||||
/// System with the standard 1K ROM, 1K RAM
|
||||
/// </summary>
|
||||
OneKRom,
|
||||
|
||||
/// <summary>
|
||||
/// System with 2K ROM, 1K RAM
|
||||
/// </summary>
|
||||
TwoKRom,
|
||||
|
||||
/// <summary>
|
||||
/// System with 3K RAM
|
||||
/// </summary>
|
||||
ThreeKRam,
|
||||
}
|
||||
|
||||
public enum PacketInterfaceType
|
||||
{
|
||||
/// <summary>
|
||||
/// Encapsulate frames inside raw ethernet frames on the host interface.
|
||||
/// Requires PCAP.
|
||||
/// </summary>
|
||||
EthernetEncapsulation,
|
||||
|
||||
/// <summary>
|
||||
/// Encapsulate frames inside UDP datagrams on the host interface.
|
||||
/// </summary>
|
||||
UDPEncapsulation,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encapsulates user-configurable settings. To be enhanced.
|
||||
/// </summary>
|
||||
@ -13,22 +50,218 @@ namespace Contralto
|
||||
{
|
||||
static Configuration()
|
||||
{
|
||||
// Initialize things to defaults.
|
||||
// TODO: Load from config file.
|
||||
// Initialize things to defaults.
|
||||
HostAddress = 0x22;
|
||||
|
||||
EthernetBootEnabled = false;
|
||||
EthernetBootFile = 0;
|
||||
BootAddress = 0;
|
||||
|
||||
SystemType = SystemType.TwoKRom;
|
||||
|
||||
InterlaceDisplay = false;
|
||||
|
||||
ThrottleSpeed = true;
|
||||
|
||||
ReadConfiguration();
|
||||
}
|
||||
|
||||
public static string Drive0Image;
|
||||
public static string Drive1Image;
|
||||
public static byte HostAddress;
|
||||
public static string HostEthernetInterfaceName;
|
||||
public static bool HostEthernetAvailable;
|
||||
/// <summary>
|
||||
/// The type of Alto II to emulate
|
||||
/// </summary>
|
||||
public static SystemType SystemType;
|
||||
|
||||
/// <summary>
|
||||
/// The currently loaded image for Drive 0
|
||||
/// </summary>
|
||||
public static string Drive0Image;
|
||||
|
||||
/// <summary>
|
||||
/// The currently loaded image for Drive 1
|
||||
/// </summary>
|
||||
public static string Drive1Image;
|
||||
|
||||
/// <summary>
|
||||
/// The Ethernet host address for this Alto
|
||||
/// </summary>
|
||||
public static byte HostAddress;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the Ethernet adaptor on the emulator host to use for Ethernet emulation
|
||||
/// </summary>
|
||||
public static string HostPacketInterfaceName;
|
||||
|
||||
/// <summary>
|
||||
/// Whether any packet interfaces are available on the host
|
||||
/// </summary>
|
||||
public static bool HostRawEthernetInterfacesAvailable;
|
||||
|
||||
/// <summary>
|
||||
/// The type of interface to use to host networking.
|
||||
/// </summary>
|
||||
public static PacketInterfaceType HostPacketInterfaceType;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to enable Ethernet boot at reset
|
||||
/// </summary>
|
||||
public static bool EthernetBootEnabled;
|
||||
public static ushort EthernetBootFile;
|
||||
|
||||
/// <summary>
|
||||
/// The address/file to boot at reset
|
||||
/// </summary>
|
||||
public static ushort BootAddress;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to render the display "interlaced" or not.
|
||||
/// </summary>
|
||||
public static bool InterlaceDisplay;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to cap execution speed at native execution speed or not.
|
||||
/// </summary>
|
||||
public static bool ThrottleSpeed;
|
||||
|
||||
/// <summary>
|
||||
/// Reads the current configuration file from disk.
|
||||
///
|
||||
/// TODO: use reflection to do this.
|
||||
/// </summary>
|
||||
public static void ReadConfiguration()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (StreamReader configStream = new StreamReader("contralto.cfg"))
|
||||
{
|
||||
//
|
||||
// Config file consists of text lines containing name / value pairs:
|
||||
// <Name> <Value>
|
||||
// Whitespace is ignored
|
||||
//
|
||||
int lineNumber = 0;
|
||||
while (!configStream.EndOfStream)
|
||||
{
|
||||
lineNumber++;
|
||||
string line = configStream.ReadLine().ToLowerInvariant().Trim();
|
||||
|
||||
if (string.IsNullOrEmpty(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find whitespace separating tokens
|
||||
int ws = line.IndexOfAny(new char[] { ' ', '\t' });
|
||||
|
||||
if (ws < 1)
|
||||
{
|
||||
Log.Write(LogType.Warning, LogComponent.Configuration, "Syntax error on line {0}. Ignoring.", lineNumber);
|
||||
continue;
|
||||
}
|
||||
|
||||
string parameter = line.Substring(0, ws);
|
||||
string value = line.Substring(ws + 1, line.Length - ws - 1);
|
||||
|
||||
try
|
||||
{
|
||||
switch (parameter)
|
||||
{
|
||||
case "drive0image":
|
||||
Drive0Image = value;
|
||||
break;
|
||||
|
||||
case "drive1image":
|
||||
Drive1Image = value;
|
||||
break;
|
||||
|
||||
case "systemtype":
|
||||
SystemType = (SystemType)Enum.Parse(typeof(SystemType), value, true);
|
||||
break;
|
||||
|
||||
case "hostaddress":
|
||||
HostAddress = Convert.ToByte(value, 8);
|
||||
break;
|
||||
|
||||
case "hostpacketinterfacename":
|
||||
HostPacketInterfaceName = value;
|
||||
break;
|
||||
|
||||
case "hostpacketinterfacetype":
|
||||
HostPacketInterfaceType = (PacketInterfaceType)Enum.Parse(typeof(PacketInterfaceType), value, true);
|
||||
break;
|
||||
|
||||
case "ethernetbootenabled":
|
||||
EthernetBootEnabled = bool.Parse(value);
|
||||
break;
|
||||
|
||||
case "bootaddress":
|
||||
BootAddress = Convert.ToUInt16(value, 8);
|
||||
break;
|
||||
|
||||
case "interlacedisplay":
|
||||
InterlaceDisplay = bool.Parse(value);
|
||||
break;
|
||||
|
||||
case "throttlespeed":
|
||||
ThrottleSpeed = bool.Parse(value);
|
||||
break;
|
||||
|
||||
default:
|
||||
Log.Write(LogType.Warning, LogComponent.Configuration, "Invalid parameter on line {0}. Ignoring.", lineNumber);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
Log.Write(LogType.Warning, LogComponent.Configuration, "Invalid value on line {0}. Ignoring.", lineNumber);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Log.Write(LogType.Warning, LogComponent.Configuration, "Configuration file 'contralto.cfg' could not be read; assuming default settings.");
|
||||
WriteConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Commits the current configuration to disk.
|
||||
/// </summary>
|
||||
public static void WriteConfiguration()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (StreamWriter configStream = new StreamWriter("contralto.cfg"))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Drive0Image))
|
||||
{
|
||||
configStream.WriteLine("Drive0Image {0}", Drive0Image);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(Drive1Image))
|
||||
{
|
||||
configStream.WriteLine("Drive1Image {0}", Drive1Image);
|
||||
}
|
||||
|
||||
configStream.WriteLine("SystemType {0}", SystemType);
|
||||
configStream.WriteLine("HostAddress {0}", Conversion.ToOctal(HostAddress));
|
||||
|
||||
if (!string.IsNullOrEmpty(HostPacketInterfaceName))
|
||||
{
|
||||
configStream.WriteLine("HostPacketInterfaceName {0}", HostPacketInterfaceName);
|
||||
}
|
||||
|
||||
configStream.WriteLine("HostPacketInterfaceType {0}", HostPacketInterfaceType);
|
||||
configStream.WriteLine("EthernetBootEnabled {0}", EthernetBootEnabled);
|
||||
configStream.WriteLine("BootAddress {0}", Conversion.ToOctal(BootAddress));
|
||||
configStream.WriteLine("InterlaceDisplay {0}", InterlaceDisplay);
|
||||
configStream.WriteLine("ThrottleSpeed {0}", ThrottleSpeed);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Log.Write(LogType.Warning, LogComponent.Configuration, "Configuration file 'contralto.cfg' could not be opened for writing.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -99,6 +99,14 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="HighResTimer.cs" />
|
||||
<Compile Include="IO\UDPEncapsulation.cs" />
|
||||
<Compile Include="IO\IPacketEncapsulation.cs" />
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="UI\AboutBox.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
@ -140,18 +148,18 @@
|
||||
<Compile Include="Display\FakeDisplayController.cs" />
|
||||
<Compile Include="Display\DisplayController.cs" />
|
||||
<Compile Include="Display\IAltoDisplay.cs" />
|
||||
<Compile Include="UI\EthernetBootWindow.cs">
|
||||
<Compile Include="UI\AlternateBootWindow.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="UI\EthernetBootWindow.Designer.cs">
|
||||
<DependentUpon>EthernetBootWindow.cs</DependentUpon>
|
||||
<Compile Include="UI\AlternateBootWindow.Designer.cs">
|
||||
<DependentUpon>AlternateBootWindow.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="IClockable.cs" />
|
||||
<Compile Include="IO\DiabloDrive.cs" />
|
||||
<Compile Include="IO\DiskController.cs" />
|
||||
<Compile Include="IO\DiabloPack.cs" />
|
||||
<Compile Include="IO\EthernetController.cs" />
|
||||
<Compile Include="IO\HostEthernet.cs" />
|
||||
<Compile Include="IO\HostEthernetEncapsulation.cs" />
|
||||
<Compile Include="IO\Keyboard.cs" />
|
||||
<Compile Include="IO\Mouse.cs" />
|
||||
<Compile Include="Logging\Log.cs" />
|
||||
@ -163,6 +171,12 @@
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Scheduler.cs" />
|
||||
<Compile Include="ExecutionController.cs" />
|
||||
<Compile Include="UI\SystemOptions.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="UI\SystemOptions.Designer.cs">
|
||||
<DependentUpon>SystemOptions.cs</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Disassembly\altocode24.mu" />
|
||||
@ -334,6 +348,10 @@
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="UI\AboutBox.resx">
|
||||
<DependentUpon>AboutBox.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
@ -343,8 +361,11 @@
|
||||
<EmbeddedResource Include="UI\Debugger.resx">
|
||||
<DependentUpon>Debugger.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="UI\EthernetBootWindow.resx">
|
||||
<DependentUpon>EthernetBootWindow.cs</DependentUpon>
|
||||
<EmbeddedResource Include="UI\AlternateBootWindow.resx">
|
||||
<DependentUpon>AlternateBootWindow.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="UI\SystemOptions.resx">
|
||||
<DependentUpon>SystemOptions.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@ -1102,7 +1102,7 @@ EM1036> AC1<- L, :LOADX;
|
||||
|
||||
;SIO - 61004 - Put AC0 on the bus, issue STARTF to get device attention,
|
||||
;Read Host address from Ethernet interface into AC0.
|
||||
EM104> SIO: L<- AC0, STARTF;
|
||||
EM0104> SIO: L<- AC0, STARTF;
|
||||
EM1037> T<- 77777; ***X21 sets AC0[0] to 0
|
||||
EM1040> L<- RSNF AND T;
|
||||
EM1041> LTOAC0: AC0<- L, TASK, :TOSTART;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -21,6 +21,11 @@ namespace Contralto.Display
|
||||
_display = display;
|
||||
}
|
||||
|
||||
public void DetachDisplay()
|
||||
{
|
||||
_display = null;
|
||||
}
|
||||
|
||||
public int Fields
|
||||
{
|
||||
get { return _fields; }
|
||||
@ -160,6 +165,11 @@ namespace Contralto.Display
|
||||
|
||||
private void WordCallback(ulong timeNsec, ulong skewNsec, object context)
|
||||
{
|
||||
if (_display == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Dequeue a word (if available) and draw it to the screen.
|
||||
ushort displayWord = (ushort)(_whiteOnBlack ? 0 : 0xffff);
|
||||
if (_dataBuffer.Count > 0)
|
||||
|
||||
191
Contralto/HighResTimer.cs
Normal file
191
Contralto/HighResTimer.cs
Normal file
@ -0,0 +1,191 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Contralto
|
||||
{
|
||||
/// <summary>
|
||||
/// HighResTimer gives us access to NT's very-high-resolution PerformanceCounters.
|
||||
/// This gives us the precision we need to sync emulation to any speed we desire.
|
||||
/// </summary>
|
||||
public sealed class HighResTimer
|
||||
{
|
||||
[DllImport("Kernel32.dll")]
|
||||
private static extern bool QueryPerformanceCounter(
|
||||
out long lpPerformanceCount);
|
||||
|
||||
[DllImport("Kernel32.dll")]
|
||||
private static extern bool QueryPerformanceFrequency(
|
||||
out long lpFrequency);
|
||||
|
||||
public HighResTimer()
|
||||
{
|
||||
// What's the frequency, Kenneth?
|
||||
if (QueryPerformanceFrequency(out _frequency) == false)
|
||||
{
|
||||
// high-performance counter not supported
|
||||
throw new Win32Exception();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current time in seconds.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public double GetCurrentTime()
|
||||
{
|
||||
long currentTime;
|
||||
QueryPerformanceCounter(out currentTime);
|
||||
|
||||
return (double)(currentTime) / (double)_frequency;
|
||||
}
|
||||
|
||||
private long _frequency;
|
||||
}
|
||||
|
||||
public sealed class FrameTimer
|
||||
{
|
||||
[DllImport("winmm.dll", EntryPoint = "timeGetDevCaps", SetLastError = true)]
|
||||
static extern UInt32 TimeGetDevCaps(ref TimeCaps timeCaps, UInt32 sizeTimeCaps);
|
||||
|
||||
[DllImport("winmm.dll", EntryPoint = "timeBeginPeriod")]
|
||||
static extern UInt32 TimeBeginPeriod(UInt32 uPeriod);
|
||||
|
||||
[DllImport("winmm.dll", EntryPoint = "timeEndPeriod")]
|
||||
public static extern uint TimeEndPeriod(uint uMilliseconds);
|
||||
|
||||
[DllImport("kernel32.dll", EntryPoint = "CreateTimerQueue")]
|
||||
public static extern IntPtr CreateTimerQueue();
|
||||
|
||||
[DllImport("kernel32.dll", EntryPoint = "DeleteTimerQueueEx")]
|
||||
public static extern bool DeleteTimerQueue(IntPtr hTimerQueue, IntPtr hCompletionEvent);
|
||||
|
||||
[DllImport("kernel32.dll", EntryPoint = "CreateTimerQueueTimer")]
|
||||
public static extern bool CreateTimerQueueTimer(out IntPtr phNewTimer, IntPtr hTimerQueue, IntPtr Callback, IntPtr Parameter, UInt32 DueTime, UInt32 Period, ulong Flags);
|
||||
|
||||
[DllImport("kernel32.dll", EntryPoint = "ChangeTimerQueueTimer")]
|
||||
public static extern bool ChangeTimerQueueTimer(IntPtr hTimerQueue, IntPtr hTimer, UInt32 DueTime, UInt32 Period);
|
||||
|
||||
|
||||
[DllImport("kernel32.dll", EntryPoint = "DeleteTimerQueueTimer")]
|
||||
public static extern bool DeleteTimerQueueTimer(IntPtr hTimerQueue, IntPtr hTimer, IntPtr hCompletionEvent);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct TimeCaps
|
||||
{
|
||||
public UInt32 wPeriodMin;
|
||||
public UInt32 wPeriodMax;
|
||||
};
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
public delegate void UnmanagedTimerCallback(IntPtr param, bool timerOrWait);
|
||||
|
||||
/// <summary>
|
||||
/// FrameTimer provides a simple method to synchronize execution to a given framerate.
|
||||
/// Calling WaitForFrame() blocks until the start of the next frame.
|
||||
///
|
||||
/// NOTE: This code uses the Win32 TimerQueue APIs instead of the System.Threading.Timer
|
||||
/// APIs because the .NET APIs do not allow execution of the callback on the timer's thread --
|
||||
/// it queues up a new worker thread. This lowers the accuracy of the timer, and since we
|
||||
/// need all the precision we can get they're not suitable here.
|
||||
/// </summary>
|
||||
/// <param name="framesPerSecond">The frame rate to sync to.</param>
|
||||
public FrameTimer(double framesPerSecond)
|
||||
{
|
||||
//
|
||||
// Set the timer to the minimum value (1ms). This should be supported on any modern x86 system.
|
||||
// If not, too bad...
|
||||
//
|
||||
UInt32 res = TimeBeginPeriod(1);
|
||||
|
||||
if (res != 0)
|
||||
{
|
||||
throw new InvalidOperationException("Unable to set timer period.");
|
||||
}
|
||||
|
||||
//
|
||||
// Create a new timer queue
|
||||
//
|
||||
_hTimerQueue = CreateTimerQueue();
|
||||
|
||||
if (_hTimerQueue == IntPtr.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("Unable to create timer queue.");
|
||||
}
|
||||
|
||||
//
|
||||
// Since we only have a resolution of 1ms, we have to do some hackery to get a slightly more accurate framerate.
|
||||
// (60 fields/sec requires 16 2/3s ms frame delay.)
|
||||
// We alternate between two rates at varying intervals and this gets us fairly close to the desired frame rate.
|
||||
//
|
||||
_callback = new UnmanagedTimerCallback(TimerCallbackFn);
|
||||
|
||||
_highPeriod = (uint)Math.Ceiling(1000.0 * (1.0 / framesPerSecond));
|
||||
_lowPeriod = (uint)Math.Floor(1000.0 * (1.0 / framesPerSecond));
|
||||
_periodTenths = _periodSwitch = (uint)((1000.0 * (1.0 / framesPerSecond) - Math.Floor(1000.0 * (1.0 / framesPerSecond))) * 10.0);
|
||||
|
||||
if (!CreateTimerQueueTimer(out _hTimer, _hTimerQueue, Marshal.GetFunctionPointerForDelegate(_callback), IntPtr.Zero, _lowPeriod, _lowPeriod, 0x00000020 /* execute in timer thread */))
|
||||
{
|
||||
throw new InvalidOperationException("Unable to create timer queue timer.");
|
||||
}
|
||||
|
||||
_event = new AutoResetEvent(false);
|
||||
|
||||
_lowTimer = 0;
|
||||
}
|
||||
|
||||
~FrameTimer()
|
||||
{
|
||||
//
|
||||
// Clean stuff up
|
||||
//
|
||||
DeleteTimerQueueTimer(_hTimerQueue, _hTimer, IntPtr.Zero);
|
||||
DeleteTimerQueue(_hTimerQueue, IntPtr.Zero);
|
||||
|
||||
//
|
||||
// Fire off a final event to release any call that's waiting...
|
||||
//
|
||||
_event.Set();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits for the timer to fire.
|
||||
/// </summary>
|
||||
public void WaitForFrame()
|
||||
{
|
||||
_event.WaitOne();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback from timer queue. Work done here is executed on the timer's thread, so must be quick.
|
||||
/// </summary>
|
||||
/// <param name="lpParameter"></param>
|
||||
/// <param name="TimerOrWaitFired"></param>
|
||||
private void TimerCallbackFn(IntPtr lpParameter, bool TimerOrWaitFired)
|
||||
{
|
||||
_event.Set();
|
||||
_lowTimer++;
|
||||
|
||||
if (_lowTimer >= _periodSwitch)
|
||||
{
|
||||
_lowTimer = 0;
|
||||
_period = !_period;
|
||||
ChangeTimerQueueTimer(_hTimerQueue, _hTimer, _period ? _lowPeriod : _highPeriod, _period ? _lowPeriod : _highPeriod);
|
||||
|
||||
_periodSwitch = !_period ? _periodTenths : 10 - _periodTenths;
|
||||
}
|
||||
}
|
||||
|
||||
private IntPtr _hTimerQueue;
|
||||
private IntPtr _hTimer;
|
||||
private AutoResetEvent _event;
|
||||
private UnmanagedTimerCallback _callback;
|
||||
private uint _lowPeriod;
|
||||
private uint _highPeriod;
|
||||
private uint _periodSwitch;
|
||||
private uint _periodTenths;
|
||||
private int _lowTimer;
|
||||
private bool _period;
|
||||
}
|
||||
}
|
||||
@ -25,10 +25,21 @@ namespace Contralto.IO
|
||||
|
||||
// Attach real Ethernet device if user has specified one, otherwise leave unattached; output data
|
||||
// will go into a bit-bucket.
|
||||
if (!String.IsNullOrEmpty(Configuration.HostEthernetInterfaceName))
|
||||
switch(Configuration.HostPacketInterfaceType)
|
||||
{
|
||||
_hostEthernet = new HostEthernet(Configuration.HostEthernetInterfaceName);
|
||||
_hostEthernet.RegisterReceiveCallback(OnHostPacketReceived);
|
||||
case PacketInterfaceType.UDPEncapsulation:
|
||||
_hostInterface = new UDPEncapsulation(Configuration.HostPacketInterfaceName);
|
||||
_hostInterface.RegisterReceiveCallback(OnHostPacketReceived);
|
||||
break;
|
||||
|
||||
case PacketInterfaceType.EthernetEncapsulation:
|
||||
_hostInterface = new HostEthernetEncapsulation(Configuration.HostPacketInterfaceName);
|
||||
_hostInterface.RegisterReceiveCallback(OnHostPacketReceived);
|
||||
break;
|
||||
|
||||
default:
|
||||
_hostInterface = null;
|
||||
break;
|
||||
}
|
||||
|
||||
// More words than the Alto will ever send.
|
||||
@ -285,9 +296,9 @@ namespace Contralto.IO
|
||||
// And actually tell the host ethernet interface to send the data.
|
||||
// NOTE: We do not append a checksum to the outgoing 3mbit packet. See comments on the
|
||||
// receiving end for an explanation.
|
||||
if (_hostEthernet != null)
|
||||
if (_hostInterface != null)
|
||||
{
|
||||
_hostEthernet.Send(_outputData, _outputIndex);
|
||||
_hostInterface.Send(_outputData, _outputIndex);
|
||||
}
|
||||
|
||||
_outputIndex = 0;
|
||||
@ -526,8 +537,8 @@ namespace Contralto.IO
|
||||
|
||||
private const int _maxQueuedPackets = 32;
|
||||
|
||||
// The actual connection to a real Ethernet device on the host
|
||||
HostEthernet _hostEthernet;
|
||||
// The actual connection to a real network device of some sort on the host
|
||||
IPacketEncapsulation _hostInterface;
|
||||
|
||||
// Buffer to hold outgoing data to the host ethernet
|
||||
ushort[] _outputData;
|
||||
|
||||
@ -18,11 +18,10 @@ namespace Contralto.IO
|
||||
|
||||
public struct EthernetInterface
|
||||
{
|
||||
public EthernetInterface(string name, string description, MacAddress macAddress)
|
||||
public EthernetInterface(string name, string description)
|
||||
{
|
||||
Name = name;
|
||||
Description = description;
|
||||
MacAddress = macAddress;
|
||||
Description = description;
|
||||
}
|
||||
|
||||
public static List<EthernetInterface> EnumerateDevices()
|
||||
@ -31,33 +30,35 @@ namespace Contralto.IO
|
||||
|
||||
foreach (LivePacketDevice device in LivePacketDevice.AllLocalMachine)
|
||||
{
|
||||
interfaces.Add(new EthernetInterface(device.Name, device.Description, device.GetMacAddress()));
|
||||
interfaces.Add(new EthernetInterface(device.Name, device.Description));
|
||||
}
|
||||
|
||||
return interfaces;
|
||||
}
|
||||
|
||||
public string Name;
|
||||
public string Description;
|
||||
public MacAddress MacAddress;
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return Description;
|
||||
}
|
||||
|
||||
public delegate void ReceivePacketDelegate(MemoryStream data);
|
||||
public string Name;
|
||||
public string Description;
|
||||
}
|
||||
|
||||
/// <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.
|
||||
/// ethernet interface controlled by the host operating system.
|
||||
///
|
||||
/// This uses PCap.NET to do the dirty work.
|
||||
/// </summary>
|
||||
public class HostEthernet
|
||||
public class HostEthernetEncapsulation : IPacketEncapsulation
|
||||
{
|
||||
public HostEthernet(EthernetInterface iface)
|
||||
public HostEthernetEncapsulation(EthernetInterface iface)
|
||||
{
|
||||
AttachInterface(iface);
|
||||
}
|
||||
|
||||
public HostEthernet(string name)
|
||||
public HostEthernetEncapsulation(string name)
|
||||
{
|
||||
// Find the specified device by name
|
||||
List<EthernetInterface> interfaces = EthernetInterface.EnumerateDevices();
|
||||
@ -71,7 +72,7 @@ namespace Contralto.IO
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Specified ethernet interface does not exist.");
|
||||
throw new InvalidOperationException("Specified ethernet interface does not exist or is not compatible with WinPCAP.");
|
||||
}
|
||||
|
||||
public void RegisterReceiveCallback(ReceivePacketDelegate callback)
|
||||
@ -127,7 +128,7 @@ namespace Contralto.IO
|
||||
byte destinationHost = packetBytes[3];
|
||||
byte sourceHost = packetBytes[2];
|
||||
|
||||
Log.Write(LogComponent.HostEthernet, "Sending packet; source {0} destination {1}, length {2} words.",
|
||||
Log.Write(LogComponent.HostNetworkInterface, "Sending packet; source {0} destination {1}, length {2} words.",
|
||||
Conversion.ToOctal(sourceHost),
|
||||
Conversion.ToOctal(destinationHost),
|
||||
length);
|
||||
@ -153,7 +154,7 @@ namespace Contralto.IO
|
||||
// Send it over the 'net!
|
||||
_communicator.SendPacket(builder.Build(DateTime.Now));
|
||||
|
||||
Log.Write(LogComponent.HostEthernet, "Encapsulated 3mbit packet sent.");
|
||||
Log.Write(LogComponent.HostNetworkInterface, "Encapsulated 3mbit packet sent.");
|
||||
}
|
||||
|
||||
private void ReceiveCallback(Packet p)
|
||||
@ -166,7 +167,7 @@ namespace Contralto.IO
|
||||
p.Ethernet.Destination.ToValue() == _10mbitBroadcast) && // broadcast
|
||||
(p.Ethernet.Source.ToValue() != (UInt48)(_10mbitMACPrefix | Configuration.HostAddress))) // and not sent by this emulator
|
||||
{
|
||||
Log.Write(LogComponent.HostEthernet, "Received encapsulated 3mbit packet.");
|
||||
Log.Write(LogComponent.HostNetworkInterface, "Received encapsulated 3mbit packet.");
|
||||
_callback(p.Ethernet.Payload.ToMemoryStream());
|
||||
}
|
||||
else
|
||||
@ -182,7 +183,7 @@ namespace Contralto.IO
|
||||
// Find the specified device by name
|
||||
foreach (LivePacketDevice device in LivePacketDevice.AllLocalMachine)
|
||||
{
|
||||
if (device.Name == iface.Name && device.GetMacAddress() == iface.MacAddress)
|
||||
if (device.Description == iface.Name)
|
||||
{
|
||||
_interface = device;
|
||||
break;
|
||||
@ -194,7 +195,7 @@ namespace Contralto.IO
|
||||
throw new InvalidOperationException("Requested interface not found.");
|
||||
}
|
||||
|
||||
Log.Write(LogComponent.HostEthernet, "Attached to host interface {0}", iface.Name);
|
||||
Log.Write(LogComponent.HostNetworkInterface, "Attached to host interface {0}", iface.Name);
|
||||
}
|
||||
|
||||
private void Open(bool promiscuous, int timeout)
|
||||
@ -204,7 +205,7 @@ namespace Contralto.IO
|
||||
// Set this to 1 so we'll get packets as soon as they arrive, no buffering.
|
||||
_communicator.SetKernelMinimumBytesToCopy(1);
|
||||
|
||||
Log.Write(LogComponent.HostEthernet, "Host interface opened and receiving packets.");
|
||||
Log.Write(LogComponent.HostNetworkInterface, "Host interface opened and receiving packets.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -222,7 +223,7 @@ namespace Contralto.IO
|
||||
// Just call ReceivePackets, that's it. This will never return.
|
||||
// (probably need to make this more elegant so we can tear down the thread
|
||||
// properly.)
|
||||
Log.Write(LogComponent.HostEthernet, "Receiver thread started.");
|
||||
Log.Write(LogComponent.HostNetworkInterface, "Receiver thread started.");
|
||||
_communicator.ReceivePackets(-1, ReceiveCallback);
|
||||
}
|
||||
|
||||
23
Contralto/IO/IPacketEncapsulation.cs
Normal file
23
Contralto/IO/IPacketEncapsulation.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using System.IO;
|
||||
|
||||
|
||||
namespace Contralto.IO
|
||||
{
|
||||
public delegate void ReceivePacketDelegate(MemoryStream data);
|
||||
|
||||
public interface IPacketEncapsulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Registers a callback delegate to handle packets that are received.
|
||||
/// </summary>
|
||||
/// <param name="callback"></param>
|
||||
void RegisterReceiveCallback(ReceivePacketDelegate callback);
|
||||
|
||||
/// <summary>
|
||||
/// Sends the specified word array
|
||||
/// </summary>
|
||||
/// <param name="packet"></param>
|
||||
/// <param name="length"></param>
|
||||
void Send(ushort[] packet, int length);
|
||||
}
|
||||
}
|
||||
208
Contralto/IO/UDPEncapsulation.cs
Normal file
208
Contralto/IO/UDPEncapsulation.cs
Normal file
@ -0,0 +1,208 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
using Contralto.Logging;
|
||||
using System.Threading;
|
||||
using System.Net.NetworkInformation;
|
||||
|
||||
namespace Contralto.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements the logic for encapsulating a 3mbit ethernet packet into/out of UDP datagrams.
|
||||
/// Sent packets are broadcast to the subnet.
|
||||
/// </summary>
|
||||
public class UDPEncapsulation : IPacketEncapsulation
|
||||
{
|
||||
public UDPEncapsulation(string interfaceName)
|
||||
{
|
||||
// Try to set up UDP client.
|
||||
try
|
||||
{
|
||||
_udpClient = new UdpClient(_udpPort, AddressFamily.InterNetwork);
|
||||
_udpClient.EnableBroadcast = true;
|
||||
|
||||
//
|
||||
// Grab the broadcast address for the interface so that we know what broadcast address to use
|
||||
// for our UDP datagrams.
|
||||
//
|
||||
NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
|
||||
|
||||
IPInterfaceProperties props = null;
|
||||
foreach(NetworkInterface nic in nics)
|
||||
{
|
||||
if (nic.Description.ToLowerInvariant() == interfaceName.ToLowerInvariant())
|
||||
{
|
||||
props = nic.GetIPProperties();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (props == null)
|
||||
{
|
||||
throw new InvalidOperationException(String.Format("No interface matching description '{0}' was found.", interfaceName));
|
||||
}
|
||||
|
||||
foreach(UnicastIPAddressInformation unicast in props.UnicastAddresses)
|
||||
{
|
||||
// Find the first InterNetwork address for this interface and
|
||||
// go with it.
|
||||
if (unicast.Address.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
_broadcastEndpoint = new IPEndPoint(GetBroadcastAddress(unicast.Address, unicast.IPv4Mask), _udpPort);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_broadcastEndpoint == null)
|
||||
{
|
||||
throw new InvalidOperationException(String.Format("No IPV4 network information was found for interface '{0}'.", interfaceName));
|
||||
}
|
||||
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Log.Write(LogType.Error, LogComponent.EthernetPacket,
|
||||
"Error configuring UDP socket {0} for use with ContrAlto on interface {1}. Ensure that the selected network interface is valid, configured properly, and that nothing else is using this port.",
|
||||
_udpPort,
|
||||
interfaceName);
|
||||
|
||||
Log.Write(LogType.Error, LogComponent.EthernetPacket,
|
||||
"Error was '{0}'.",
|
||||
e.Message);
|
||||
|
||||
_udpClient = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterReceiveCallback(ReceivePacketDelegate callback)
|
||||
{
|
||||
// UDP connection could not be configured, can't receive anything.
|
||||
if (_udpClient == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up input
|
||||
_callback = callback;
|
||||
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)
|
||||
{
|
||||
// UDP could not be configured, drop the packet.
|
||||
if (_udpClient == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Sanity check.
|
||||
if (length < 1)
|
||||
{
|
||||
throw new InvalidOperationException("Raw packet data must contain at least two bytes for addressing.");
|
||||
}
|
||||
|
||||
//
|
||||
// Outgoing packet contains 1 extra word (2 bytes) containing
|
||||
// the prepended packet length (one word)
|
||||
byte[] packetBytes = new byte[length * 2 + 2];
|
||||
|
||||
//
|
||||
// First two bytes include the length of the 3mbit packet; since 10mbit packets have a minimum length of 46
|
||||
// bytes, and 3mbit packets have no minimum length this is necessary so the receiver can pull out the
|
||||
// correct amount of data.
|
||||
//
|
||||
packetBytes[0] = (byte)(length);
|
||||
packetBytes[1] = (byte)((length) >> 8);
|
||||
|
||||
//
|
||||
// Do this annoying dance to stuff the ushorts into bytes because this is C#.
|
||||
//
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
packetBytes[i * 2 + 2] = (byte)(packet[i]);
|
||||
packetBytes[i * 2 + 3] = (byte)(packet[i] >> 8);
|
||||
}
|
||||
|
||||
//
|
||||
// Grab the source and destination host addresses from the packet we're sending
|
||||
// and build 10mbit versions.
|
||||
//
|
||||
byte destinationHost = packetBytes[3];
|
||||
byte sourceHost = packetBytes[2];
|
||||
|
||||
Log.Write(LogComponent.HostNetworkInterface, "Sending packet; source {0} destination {1}, length {2} words.",
|
||||
Conversion.ToOctal(sourceHost),
|
||||
Conversion.ToOctal(destinationHost),
|
||||
length);
|
||||
|
||||
_udpClient.Send(packetBytes, packetBytes.Length, _broadcastEndpoint);
|
||||
|
||||
Log.Write(LogComponent.HostNetworkInterface, "Encapsulated 3mbit packet sent via UDP.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Begin receiving packets, forever.
|
||||
/// </summary>
|
||||
private void BeginReceive()
|
||||
{
|
||||
// Kick off receive thread.
|
||||
_receiveThread = new Thread(ReceiveThread);
|
||||
_receiveThread.Start();
|
||||
}
|
||||
|
||||
private void ReceiveThread()
|
||||
{
|
||||
// Just call ReceivePackets, that's it. This will never return.
|
||||
// (probably need to make this more elegant so we can tear down the thread
|
||||
// properly.)
|
||||
Log.Write(LogComponent.HostNetworkInterface, "UDP Receiver thread started.");
|
||||
|
||||
IPEndPoint groupEndPoint = new IPEndPoint(IPAddress.Any, _udpPort);
|
||||
//return;
|
||||
|
||||
while (true)
|
||||
{
|
||||
byte[] data = _udpClient.Receive(ref groupEndPoint);
|
||||
|
||||
// TODO: sanitize data before handing it off.
|
||||
|
||||
Log.Write(LogComponent.HostNetworkInterface, "Received UDP-encapsulated 3mbit packet.");
|
||||
_callback(new System.IO.MemoryStream(data));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private IPAddress GetBroadcastAddress(IPAddress address, IPAddress subnetMask)
|
||||
{
|
||||
byte[] ipAdressBytes = address.GetAddressBytes();
|
||||
byte[] subnetMaskBytes = subnetMask.GetAddressBytes();
|
||||
|
||||
byte[] broadcastAddress = new byte[ipAdressBytes.Length];
|
||||
for (int i = 0; i < broadcastAddress.Length; i++)
|
||||
{
|
||||
broadcastAddress[i] = (byte)(ipAdressBytes[i] | (subnetMaskBytes[i] ^ 255));
|
||||
}
|
||||
|
||||
return new IPAddress(broadcastAddress);
|
||||
}
|
||||
|
||||
// Callback delegate for received data.
|
||||
private ReceivePacketDelegate _callback;
|
||||
|
||||
// Thread used for receive
|
||||
private Thread _receiveThread;
|
||||
|
||||
// UDP port (TODO: make configurable?)
|
||||
private const int _udpPort = 42424;
|
||||
private UdpClient _udpClient;
|
||||
private IPEndPoint _broadcastEndpoint;
|
||||
|
||||
}
|
||||
}
|
||||
@ -25,8 +25,9 @@ namespace Contralto.Logging
|
||||
EthernetController = 0x400,
|
||||
EthernetTask = 0x800,
|
||||
TaskSwitch = 0x1000,
|
||||
HostEthernet = 0x2000,
|
||||
HostNetworkInterface = 0x2000,
|
||||
EthernetPacket = 0x4000,
|
||||
Configuration = 0x8000,
|
||||
|
||||
Debug = 0x40000000,
|
||||
All = 0x7fffffff
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using Contralto.CPU;
|
||||
using Contralto.IO;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Contralto
|
||||
@ -12,11 +13,9 @@ namespace Contralto
|
||||
{
|
||||
// Handle command-line args
|
||||
PrintHerald();
|
||||
// See if WinPCap is installed and working
|
||||
TestPCap();
|
||||
|
||||
ParseCommandLine(args);
|
||||
|
||||
// See if WinPCap is installed and working
|
||||
TestPCap();
|
||||
|
||||
AltoSystem system = new AltoSystem();
|
||||
|
||||
@ -44,99 +43,7 @@ namespace Contralto
|
||||
Console.WriteLine("ContrAlto v0.1 (c) 2015, 2016 Living Computer Museum.");
|
||||
Console.WriteLine("Bug reports to joshd@livingcomputermuseum.org");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
private static void ParseCommandLine(string[] args)
|
||||
{
|
||||
// At the moment, options start with a "-" and are one of:
|
||||
// "-hostaddress <address>" : specifies ethernet host address in octal (1-377)
|
||||
// "-hostinterface <name>" : specifies the name of the host ethernet interface to use
|
||||
// "-listinterfaces" : lists ethernet interfaces known by pcap
|
||||
// "-drive0 <image>" : attaches disk image to drive 0
|
||||
// "-drive1 <image>" : attaches disk image to drive 1
|
||||
|
||||
int index = 0;
|
||||
|
||||
// TODO: this parsing needs to be made not terrible.
|
||||
while(index < args.Length)
|
||||
{
|
||||
switch (args[index++].ToLower())
|
||||
{
|
||||
case "-hostaddress":
|
||||
if (index < args.Length)
|
||||
{
|
||||
Configuration.HostAddress = Convert.ToByte(args[index++], 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintUsage();
|
||||
}
|
||||
break;
|
||||
|
||||
case "-hostinterface":
|
||||
if (index < args.Length)
|
||||
{
|
||||
if (!Configuration.HostEthernetAvailable)
|
||||
{
|
||||
Console.WriteLine("Ethernet functionality is disabled, host interface cannot be specified.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Configuration.HostEthernetInterfaceName = args[index++];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintUsage();
|
||||
}
|
||||
break;
|
||||
|
||||
case "-listinterfaces":
|
||||
if (!Configuration.HostEthernetAvailable)
|
||||
{
|
||||
Console.WriteLine("Ethernet functionality is disabled, interfaces cannot be enumerated.");
|
||||
}
|
||||
else
|
||||
{
|
||||
List<EthernetInterface> interfaces = EthernetInterface.EnumerateDevices();
|
||||
|
||||
foreach (EthernetInterface i in interfaces)
|
||||
{
|
||||
Console.WriteLine("Name: '{0}'\n Description: '{1}'\n MAC '{2}'", i.Name, i.Description, i.MacAddress);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "-drive0":
|
||||
if (index < args.Length)
|
||||
{
|
||||
Configuration.Drive0Image = args[index++];
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintUsage();
|
||||
}
|
||||
break;
|
||||
|
||||
case "-drive1":
|
||||
if (index < args.Length)
|
||||
{
|
||||
Configuration.Drive1Image = args[index++];
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintUsage();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void PrintUsage()
|
||||
{
|
||||
// TODO: make more useful.
|
||||
Console.WriteLine("Something is wrong, try again.");
|
||||
}
|
||||
}
|
||||
|
||||
private static void TestPCap()
|
||||
{
|
||||
@ -145,7 +52,7 @@ namespace Contralto
|
||||
try
|
||||
{
|
||||
List<EthernetInterface> interfaces = EthernetInterface.EnumerateDevices();
|
||||
Configuration.HostEthernetAvailable = true;
|
||||
Configuration.HostRawEthernetInterfacesAvailable = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
@ -153,7 +60,7 @@ namespace Contralto
|
||||
Console.WriteLine(" Ethernet functionality is disabled.");
|
||||
Console.WriteLine(" Please install WinPCAP from: http://www.winpcap.org/");
|
||||
|
||||
Configuration.HostEthernetAvailable = false;
|
||||
Configuration.HostRawEthernetInterfacesAvailable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
63
Contralto/Properties/Resources.Designer.cs
generated
Normal file
63
Contralto/Properties/Resources.Designer.cs
generated
Normal file
@ -0,0 +1,63 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Contralto.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Contralto.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
namespace Contralto
|
||||
{
|
||||
partial class EthernetBootWindow
|
||||
partial class AlternateBootOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
@ -99,16 +99,16 @@
|
||||
this.CancelButton.UseVisualStyleBackColor = true;
|
||||
this.CancelButton.Click += new System.EventHandler(this.CancelButton_Click);
|
||||
//
|
||||
// EthernetBootWindow
|
||||
// AlternateBootOptions
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(413, 130);
|
||||
this.ClientSize = new System.Drawing.Size(403, 128);
|
||||
this.Controls.Add(this.CancelButton);
|
||||
this.Controls.Add(this.OKButton);
|
||||
this.Controls.Add(this.BootFileGroup);
|
||||
this.Controls.Add(this.EthernetBootEnabled);
|
||||
this.Name = "EthernetBootWindow";
|
||||
this.Name = "AlternateBootOptions";
|
||||
this.ShowIcon = false;
|
||||
this.ShowInTaskbar = false;
|
||||
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
|
||||
@ -10,9 +10,9 @@ using System.Windows.Forms;
|
||||
|
||||
namespace Contralto
|
||||
{
|
||||
public partial class EthernetBootWindow : Form
|
||||
public partial class AlternateBootOptions : Form
|
||||
{
|
||||
public EthernetBootWindow()
|
||||
public AlternateBootOptions()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
@ -20,7 +20,7 @@ namespace Contralto
|
||||
|
||||
BootFileGroup.Enabled = EthernetBootEnabled.Checked = Configuration.EthernetBootEnabled;
|
||||
|
||||
SelectBootFile(Configuration.EthernetBootFile);
|
||||
SelectBootFile(Configuration.BootAddress);
|
||||
|
||||
}
|
||||
|
||||
@ -79,7 +79,7 @@ namespace Contralto
|
||||
_selectedBoot = ((BootFileEntry)BootFileComboBox.SelectedItem).FileNumber;
|
||||
}
|
||||
|
||||
Configuration.EthernetBootFile = _selectedBoot;
|
||||
Configuration.BootAddress = _selectedBoot;
|
||||
Configuration.EthernetBootEnabled = _bootEnabled;
|
||||
|
||||
this.Close();
|
||||
120
Contralto/UI/AlternateBootWindow.resx
Normal file
120
Contralto/UI/AlternateBootWindow.resx
Normal file
@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
37
Contralto/UI/AltoWindow.Designer.cs
generated
37
Contralto/UI/AltoWindow.Designer.cs
generated
@ -44,6 +44,8 @@
|
||||
this.loadToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.unloadToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.Drive1ImageName = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.enableAlternateBootToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.SystemEthernetBootMenu = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.optionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.SystemShowDebuggerMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
@ -52,7 +54,6 @@
|
||||
this.CaptureStatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
|
||||
this.SystemStatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
|
||||
this.DiskStatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
|
||||
this.SystemEthernetBootMenu = new System.Windows.Forms.ToolStripMenuItem();
|
||||
((System.ComponentModel.ISupportInitialize)(this.DisplayBox)).BeginInit();
|
||||
this.menuStrip1.SuspendLayout();
|
||||
this.StatusLine.SuspendLayout();
|
||||
@ -93,7 +94,7 @@
|
||||
// exitToolStripMenuItem
|
||||
//
|
||||
this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
|
||||
this.exitToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||
this.exitToolStripMenuItem.Size = new System.Drawing.Size(92, 22);
|
||||
this.exitToolStripMenuItem.Text = "Exit";
|
||||
this.exitToolStripMenuItem.Click += new System.EventHandler(this.OnFileExitClick);
|
||||
//
|
||||
@ -104,6 +105,7 @@
|
||||
this.SystemResetMenuItem,
|
||||
this.drive0ToolStripMenuItem,
|
||||
this.drive1ToolStripMenuItem,
|
||||
this.enableAlternateBootToolStripMenuItem,
|
||||
this.SystemEthernetBootMenu,
|
||||
this.optionsToolStripMenuItem,
|
||||
this.SystemShowDebuggerMenuItem});
|
||||
@ -176,14 +178,14 @@
|
||||
// loadToolStripMenuItem
|
||||
//
|
||||
this.loadToolStripMenuItem.Name = "loadToolStripMenuItem";
|
||||
this.loadToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||
this.loadToolStripMenuItem.Size = new System.Drawing.Size(134, 22);
|
||||
this.loadToolStripMenuItem.Text = "Load...";
|
||||
this.loadToolStripMenuItem.Click += new System.EventHandler(this.OnSystemDrive1LoadClick);
|
||||
//
|
||||
// unloadToolStripMenuItem
|
||||
//
|
||||
this.unloadToolStripMenuItem.Name = "unloadToolStripMenuItem";
|
||||
this.unloadToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||
this.unloadToolStripMenuItem.Size = new System.Drawing.Size(134, 22);
|
||||
this.unloadToolStripMenuItem.Text = "Unload...";
|
||||
this.unloadToolStripMenuItem.Click += new System.EventHandler(this.OnSystemDrive1UnloadClick);
|
||||
//
|
||||
@ -191,15 +193,28 @@
|
||||
//
|
||||
this.Drive1ImageName.Enabled = false;
|
||||
this.Drive1ImageName.Name = "Drive1ImageName";
|
||||
this.Drive1ImageName.Size = new System.Drawing.Size(152, 22);
|
||||
this.Drive1ImageName.Size = new System.Drawing.Size(134, 22);
|
||||
this.Drive1ImageName.Text = "Image Name";
|
||||
//
|
||||
// enableAlternateBootToolStripMenuItem
|
||||
//
|
||||
this.enableAlternateBootToolStripMenuItem.Name = "enableAlternateBootToolStripMenuItem";
|
||||
this.enableAlternateBootToolStripMenuItem.Size = new System.Drawing.Size(210, 22);
|
||||
this.enableAlternateBootToolStripMenuItem.Text = "Enable Alternate Boot";
|
||||
//
|
||||
// SystemEthernetBootMenu
|
||||
//
|
||||
this.SystemEthernetBootMenu.Name = "SystemEthernetBootMenu";
|
||||
this.SystemEthernetBootMenu.Size = new System.Drawing.Size(210, 22);
|
||||
this.SystemEthernetBootMenu.Text = "Alternate Boot Options...";
|
||||
this.SystemEthernetBootMenu.Click += new System.EventHandler(this.OnAlternateBootOptionsClicked);
|
||||
//
|
||||
// optionsToolStripMenuItem
|
||||
//
|
||||
this.optionsToolStripMenuItem.Enabled = false;
|
||||
this.optionsToolStripMenuItem.Name = "optionsToolStripMenuItem";
|
||||
this.optionsToolStripMenuItem.Size = new System.Drawing.Size(210, 22);
|
||||
this.optionsToolStripMenuItem.Text = "Options...";
|
||||
this.optionsToolStripMenuItem.Click += new System.EventHandler(this.OnSystemOptionsClick);
|
||||
//
|
||||
// SystemShowDebuggerMenuItem
|
||||
//
|
||||
@ -221,7 +236,7 @@
|
||||
// aboutToolStripMenuItem
|
||||
//
|
||||
this.aboutToolStripMenuItem.Name = "aboutToolStripMenuItem";
|
||||
this.aboutToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||
this.aboutToolStripMenuItem.Size = new System.Drawing.Size(103, 22);
|
||||
this.aboutToolStripMenuItem.Text = "About";
|
||||
this.aboutToolStripMenuItem.Click += new System.EventHandler(this.OnHelpAboutClick);
|
||||
//
|
||||
@ -256,13 +271,6 @@
|
||||
this.DiskStatusLabel.Size = new System.Drawing.Size(82, 17);
|
||||
this.DiskStatusLabel.Text = "DiskStatusLabel";
|
||||
//
|
||||
// SystemEthernetBootMenu
|
||||
//
|
||||
this.SystemEthernetBootMenu.Name = "SystemEthernetBootMenu";
|
||||
this.SystemEthernetBootMenu.Size = new System.Drawing.Size(210, 22);
|
||||
this.SystemEthernetBootMenu.Text = "Ethernet Boot...";
|
||||
this.SystemEthernetBootMenu.Click += new System.EventHandler(this.OnEthernetBootClicked);
|
||||
//
|
||||
// AltoWindow
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
@ -320,5 +328,6 @@
|
||||
private System.Windows.Forms.ToolStripStatusLabel SystemStatusLabel;
|
||||
private System.Windows.Forms.ToolStripStatusLabel DiskStatusLabel;
|
||||
private System.Windows.Forms.ToolStripMenuItem SystemEthernetBootMenu;
|
||||
private System.Windows.Forms.ToolStripMenuItem enableAlternateBootToolStripMenuItem;
|
||||
}
|
||||
}
|
||||
@ -1,17 +1,12 @@
|
||||
using Contralto.CPU;
|
||||
using Contralto.Display;
|
||||
using Contralto.IO;
|
||||
using Contralto.UI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Contralto
|
||||
@ -29,10 +24,17 @@ namespace Contralto
|
||||
_displayBuffer = new Bitmap(608, 808, PixelFormat.Format1bppIndexed);
|
||||
DisplayBox.Image = _displayBuffer;
|
||||
|
||||
_lastBuffer = _currentBuffer = _displayData0;
|
||||
_frame = 0;
|
||||
|
||||
_frameTimer = new FrameTimer(60.0);
|
||||
|
||||
ReleaseMouse();
|
||||
|
||||
SystemStatusLabel.Text = _systemStoppedText;
|
||||
DiskStatusLabel.Text = String.Empty;
|
||||
|
||||
this.DoubleBuffered = true;
|
||||
}
|
||||
|
||||
public void AttachSystem(AltoSystem system)
|
||||
@ -135,9 +137,9 @@ namespace Contralto
|
||||
Drive1ImageName.Text = _noImageLoadedText;
|
||||
}
|
||||
|
||||
private void OnEthernetBootClicked(object sender, EventArgs e)
|
||||
private void OnAlternateBootOptionsClicked(object sender, EventArgs e)
|
||||
{
|
||||
EthernetBootWindow bootWindow = new EthernetBootWindow();
|
||||
AlternateBootOptions bootWindow = new AlternateBootOptions();
|
||||
bootWindow.ShowDialog();
|
||||
|
||||
//
|
||||
@ -146,6 +148,12 @@ namespace Contralto
|
||||
_system.PressBootKeys();
|
||||
}
|
||||
|
||||
private void OnSystemOptionsClick(object sender, EventArgs e)
|
||||
{
|
||||
SystemOptions optionsWindow = new SystemOptions();
|
||||
optionsWindow.ShowDialog();
|
||||
}
|
||||
|
||||
private void OnHelpAboutClick(object sender, EventArgs e)
|
||||
{
|
||||
AboutBox about = new AboutBox();
|
||||
@ -185,14 +193,21 @@ namespace Contralto
|
||||
private void OnFileExitClick(object sender, EventArgs e)
|
||||
{
|
||||
_controller.StopExecution();
|
||||
this.Close();
|
||||
this.Close();
|
||||
}
|
||||
|
||||
private void OnAltoWindowClosed(object sender, FormClosedEventArgs e)
|
||||
{
|
||||
// Halt the system and detach our display
|
||||
_controller.StopExecution();
|
||||
_system.DetachDisplay();
|
||||
|
||||
// Commit loaded packs back to disk
|
||||
CommitDiskPack(0);
|
||||
CommitDiskPack(1);
|
||||
CommitDiskPack(1);
|
||||
|
||||
this.Dispose();
|
||||
Application.Exit();
|
||||
}
|
||||
|
||||
private string ShowImageLoadDialog(int drive)
|
||||
@ -259,7 +274,28 @@ namespace Contralto
|
||||
|
||||
public void Render()
|
||||
{
|
||||
BeginInvoke(new DisplayDelegate(RefreshDisplayBox));
|
||||
_frame++;
|
||||
|
||||
// Wait for the next frame
|
||||
_frameTimer.WaitForFrame();
|
||||
|
||||
if (Configuration.InterlaceDisplay)
|
||||
{
|
||||
// Flip the back-buffer
|
||||
if ((_frame % 2) == 0)
|
||||
{
|
||||
_currentBuffer = _displayData0;
|
||||
_lastBuffer = _displayData1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentBuffer = _displayData1;
|
||||
_lastBuffer = _displayData0;
|
||||
}
|
||||
}
|
||||
|
||||
// Asynchronously render this frame.
|
||||
BeginInvoke(new DisplayDelegate(RefreshDisplayBox));
|
||||
}
|
||||
|
||||
private void RefreshDisplayBox()
|
||||
@ -268,13 +304,16 @@ namespace Contralto
|
||||
BitmapData data = _displayBuffer.LockBits(_displayRect, ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
|
||||
|
||||
IntPtr ptr = data.Scan0;
|
||||
System.Runtime.InteropServices.Marshal.Copy(_displayData, 0, ptr, _displayData.Length - 4);
|
||||
System.Runtime.InteropServices.Marshal.Copy(_lastBuffer, 0, ptr, _lastBuffer.Length - 4);
|
||||
|
||||
_displayBuffer.UnlockBits(data);
|
||||
DisplayBox.Refresh();
|
||||
DisplayBox.Refresh();
|
||||
|
||||
// If you want interlacing to be more visible:
|
||||
//Array.Clear(_displayData, 0, _displayData.Length);
|
||||
// Clear the buffer if we're displaying in fakey-"interlaced" mode.
|
||||
if (Configuration.InterlaceDisplay)
|
||||
{
|
||||
Array.Clear(_lastBuffer, 0, _lastBuffer.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -292,29 +331,29 @@ namespace Contralto
|
||||
// Low resolution; double up pixels.
|
||||
int address = scanline * 76 + wordOffset * 4;
|
||||
|
||||
if (address > _displayData.Length)
|
||||
if (address > _currentBuffer.Length)
|
||||
{
|
||||
throw new InvalidOperationException("Display word address is out of bounds.");
|
||||
}
|
||||
|
||||
UInt32 stretched = StretchWord(word);
|
||||
|
||||
_displayData[address] = (byte)(stretched >> 24);
|
||||
_displayData[address + 1] = (byte)(stretched >> 16);
|
||||
_displayData[address + 2] = (byte)(stretched >> 8);
|
||||
_displayData[address + 3] = (byte)(stretched);
|
||||
_currentBuffer[address] = (byte)(stretched >> 24);
|
||||
_currentBuffer[address + 1] = (byte)(stretched >> 16);
|
||||
_currentBuffer[address + 2] = (byte)(stretched >> 8);
|
||||
_currentBuffer[address + 3] = (byte)(stretched);
|
||||
}
|
||||
else
|
||||
{
|
||||
int address = scanline * 76 + wordOffset * 2;
|
||||
|
||||
if (address > _displayData.Length)
|
||||
if (address > _currentBuffer.Length)
|
||||
{
|
||||
throw new InvalidOperationException("Display word address is out of bounds.");
|
||||
}
|
||||
|
||||
_displayData[address] = (byte)(word >> 8);
|
||||
_displayData[address + 1] = (byte)(word);
|
||||
_currentBuffer[address] = (byte)(word >> 8);
|
||||
_currentBuffer[address + 1] = (byte)(word);
|
||||
}
|
||||
}
|
||||
|
||||
@ -333,19 +372,19 @@ namespace Contralto
|
||||
// Grab the 32 bits straddling the cursor from the display buffer
|
||||
// so we can merge the 16 cursor bits in.
|
||||
//
|
||||
UInt32 displayWord = (UInt32)((_displayData[address] << 24) |
|
||||
(_displayData[address + 1] << 16) |
|
||||
(_displayData[address + 2] << 8) |
|
||||
_displayData[address + 3]);
|
||||
UInt32 displayWord = (UInt32)((_currentBuffer[address] << 24) |
|
||||
(_currentBuffer[address + 1] << 16) |
|
||||
(_currentBuffer[address + 2] << 8) |
|
||||
_currentBuffer[address + 3]);
|
||||
|
||||
UInt32 longcursorWord = (UInt32)(cursorWord << 16);
|
||||
|
||||
displayWord ^= (longcursorWord >> (xOffset % 8));
|
||||
|
||||
_displayData[address] = (byte)(displayWord >> 24);
|
||||
_displayData[address + 1] = (byte)(displayWord >> 16);
|
||||
_displayData[address + 2] = (byte)(displayWord >> 8);
|
||||
_displayData[address + 3] = (byte)(displayWord);
|
||||
_currentBuffer[address] = (byte)(displayWord >> 24);
|
||||
_currentBuffer[address + 1] = (byte)(displayWord >> 16);
|
||||
_currentBuffer[address + 2] = (byte)(displayWord >> 8);
|
||||
_currentBuffer[address + 3] = (byte)(displayWord);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -696,11 +735,19 @@ namespace Contralto
|
||||
|
||||
// Display related data.
|
||||
// Note: display is actually 606 pixels wide, but that's not an even multiple of 8, so we round up.
|
||||
private byte[] _displayData = new byte[808 * 76 + 4]; // + 4 to make cursor display logic simpler.
|
||||
// Two backbuffers and references to the current / last buffer for rendering
|
||||
private byte[] _displayData0 = new byte[808 * 76 + 4]; // + 4 to make cursor display logic simpler.
|
||||
private byte[] _displayData1 = new byte[808 * 76 + 4]; // + 4 to make cursor display logic simpler.
|
||||
private byte[] _currentBuffer;
|
||||
private byte[] _lastBuffer;
|
||||
private int _frame;
|
||||
private Bitmap _displayBuffer;
|
||||
private Rectangle _displayRect = new Rectangle(0, 0, 608, 808);
|
||||
private delegate void DisplayDelegate();
|
||||
|
||||
// Speed throttling
|
||||
FrameTimer _frameTimer;
|
||||
|
||||
// Input related data
|
||||
|
||||
// Keyboard mapping from windows vkeys to Alto keys
|
||||
@ -725,6 +772,5 @@ namespace Contralto
|
||||
private const string _systemRunningText = "Alto Running.";
|
||||
private const string _systemErrorText = "Alto Stopped due to error. See Debugger.";
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
324
Contralto/UI/SystemOptions.Designer.cs
generated
Normal file
324
Contralto/UI/SystemOptions.Designer.cs
generated
Normal file
@ -0,0 +1,324 @@
|
||||
namespace Contralto.UI
|
||||
{
|
||||
partial class SystemOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.tabControl1 = new System.Windows.Forms.TabControl();
|
||||
this.tabPage1 = new System.Windows.Forms.TabPage();
|
||||
this.AltoII3KRAMRadioButton = new System.Windows.Forms.RadioButton();
|
||||
this.AltoII2KROMRadioButton = new System.Windows.Forms.RadioButton();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.AltoII1KROMRadioButton = new System.Windows.Forms.RadioButton();
|
||||
this.tabPage2 = new System.Windows.Forms.TabPage();
|
||||
this.groupBox1 = new System.Windows.Forms.GroupBox();
|
||||
this.RawEthernetRadioButton = new System.Windows.Forms.RadioButton();
|
||||
this.UDPRadioButton = new System.Windows.Forms.RadioButton();
|
||||
this.HostInterfaceGroupBox = new System.Windows.Forms.GroupBox();
|
||||
this.EthernetInterfaceListBox = new System.Windows.Forms.ListBox();
|
||||
this.label4 = new System.Windows.Forms.Label();
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.AltoEthernetAddressTextBox = new System.Windows.Forms.TextBox();
|
||||
this.tabPage3 = new System.Windows.Forms.TabPage();
|
||||
this.ThrottleSpeedCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.InterlaceDisplayCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.OKButton = new System.Windows.Forms.Button();
|
||||
this.CancelButton = new System.Windows.Forms.Button();
|
||||
this.tabControl1.SuspendLayout();
|
||||
this.tabPage1.SuspendLayout();
|
||||
this.tabPage2.SuspendLayout();
|
||||
this.groupBox1.SuspendLayout();
|
||||
this.HostInterfaceGroupBox.SuspendLayout();
|
||||
this.tabPage3.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// tabControl1
|
||||
//
|
||||
this.tabControl1.Controls.Add(this.tabPage1);
|
||||
this.tabControl1.Controls.Add(this.tabPage2);
|
||||
this.tabControl1.Controls.Add(this.tabPage3);
|
||||
this.tabControl1.Location = new System.Drawing.Point(3, 5);
|
||||
this.tabControl1.Name = "tabControl1";
|
||||
this.tabControl1.SelectedIndex = 0;
|
||||
this.tabControl1.Size = new System.Drawing.Size(368, 227);
|
||||
this.tabControl1.TabIndex = 0;
|
||||
//
|
||||
// tabPage1
|
||||
//
|
||||
this.tabPage1.Controls.Add(this.AltoII3KRAMRadioButton);
|
||||
this.tabPage1.Controls.Add(this.AltoII2KROMRadioButton);
|
||||
this.tabPage1.Controls.Add(this.label1);
|
||||
this.tabPage1.Controls.Add(this.AltoII1KROMRadioButton);
|
||||
this.tabPage1.Location = new System.Drawing.Point(4, 22);
|
||||
this.tabPage1.Name = "tabPage1";
|
||||
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
|
||||
this.tabPage1.Size = new System.Drawing.Size(360, 201);
|
||||
this.tabPage1.TabIndex = 0;
|
||||
this.tabPage1.Text = "CPU";
|
||||
this.tabPage1.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// AltoII3KRAMRadioButton
|
||||
//
|
||||
this.AltoII3KRAMRadioButton.AutoSize = true;
|
||||
this.AltoII3KRAMRadioButton.Location = new System.Drawing.Point(14, 75);
|
||||
this.AltoII3KRAMRadioButton.Name = "AltoII3KRAMRadioButton";
|
||||
this.AltoII3KRAMRadioButton.Size = new System.Drawing.Size(217, 17);
|
||||
this.AltoII3KRAMRadioButton.TabIndex = 3;
|
||||
this.AltoII3KRAMRadioButton.TabStop = true;
|
||||
this.AltoII3KRAMRadioButton.Text = "Alto II, 1K Control ROM, 3K Control RAM";
|
||||
this.AltoII3KRAMRadioButton.UseVisualStyleBackColor = true;
|
||||
this.AltoII3KRAMRadioButton.CheckedChanged += new System.EventHandler(this.OnSystemTypeCheckChanged);
|
||||
//
|
||||
// AltoII2KROMRadioButton
|
||||
//
|
||||
this.AltoII2KROMRadioButton.AutoSize = true;
|
||||
this.AltoII2KROMRadioButton.Location = new System.Drawing.Point(14, 52);
|
||||
this.AltoII2KROMRadioButton.Name = "AltoII2KROMRadioButton";
|
||||
this.AltoII2KROMRadioButton.Size = new System.Drawing.Size(217, 17);
|
||||
this.AltoII2KROMRadioButton.TabIndex = 2;
|
||||
this.AltoII2KROMRadioButton.TabStop = true;
|
||||
this.AltoII2KROMRadioButton.Text = "Alto II, 2K Control ROM, 1K Control RAM";
|
||||
this.AltoII2KROMRadioButton.UseVisualStyleBackColor = true;
|
||||
this.AltoII2KROMRadioButton.CheckedChanged += new System.EventHandler(this.OnSystemTypeCheckChanged);
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Location = new System.Drawing.Point(11, 13);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(108, 13);
|
||||
this.label1.TabIndex = 1;
|
||||
this.label1.Text = "System configuration:";
|
||||
//
|
||||
// AltoII1KROMRadioButton
|
||||
//
|
||||
this.AltoII1KROMRadioButton.AutoSize = true;
|
||||
this.AltoII1KROMRadioButton.Location = new System.Drawing.Point(14, 29);
|
||||
this.AltoII1KROMRadioButton.Name = "AltoII1KROMRadioButton";
|
||||
this.AltoII1KROMRadioButton.Size = new System.Drawing.Size(217, 17);
|
||||
this.AltoII1KROMRadioButton.TabIndex = 0;
|
||||
this.AltoII1KROMRadioButton.TabStop = true;
|
||||
this.AltoII1KROMRadioButton.Text = "Alto II, 1K Control ROM, 1K Control RAM";
|
||||
this.AltoII1KROMRadioButton.UseVisualStyleBackColor = true;
|
||||
this.AltoII1KROMRadioButton.CheckedChanged += new System.EventHandler(this.OnSystemTypeCheckChanged);
|
||||
//
|
||||
// tabPage2
|
||||
//
|
||||
this.tabPage2.Controls.Add(this.groupBox1);
|
||||
this.tabPage2.Controls.Add(this.HostInterfaceGroupBox);
|
||||
this.tabPage2.Controls.Add(this.label3);
|
||||
this.tabPage2.Controls.Add(this.AltoEthernetAddressTextBox);
|
||||
this.tabPage2.Location = new System.Drawing.Point(4, 22);
|
||||
this.tabPage2.Name = "tabPage2";
|
||||
this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
|
||||
this.tabPage2.Size = new System.Drawing.Size(360, 201);
|
||||
this.tabPage2.TabIndex = 1;
|
||||
this.tabPage2.Text = "Ethernet";
|
||||
this.tabPage2.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// groupBox1
|
||||
//
|
||||
this.groupBox1.Controls.Add(this.RawEthernetRadioButton);
|
||||
this.groupBox1.Controls.Add(this.UDPRadioButton);
|
||||
this.groupBox1.Location = new System.Drawing.Point(10, 30);
|
||||
this.groupBox1.Name = "groupBox1";
|
||||
this.groupBox1.Size = new System.Drawing.Size(335, 39);
|
||||
this.groupBox1.TabIndex = 3;
|
||||
this.groupBox1.TabStop = false;
|
||||
this.groupBox1.Text = "Ethernet Encapsulation";
|
||||
//
|
||||
// RawEthernetRadioButton
|
||||
//
|
||||
this.RawEthernetRadioButton.AutoSize = true;
|
||||
this.RawEthernetRadioButton.Location = new System.Drawing.Point(77, 16);
|
||||
this.RawEthernetRadioButton.Name = "RawEthernetRadioButton";
|
||||
this.RawEthernetRadioButton.Size = new System.Drawing.Size(186, 17);
|
||||
this.RawEthernetRadioButton.TabIndex = 1;
|
||||
this.RawEthernetRadioButton.TabStop = true;
|
||||
this.RawEthernetRadioButton.Text = "Raw Ethernet (requires WinPCAP)";
|
||||
this.RawEthernetRadioButton.UseVisualStyleBackColor = true;
|
||||
this.RawEthernetRadioButton.CheckedChanged += new System.EventHandler(this.OnEthernetTypeCheckedChanged);
|
||||
//
|
||||
// UDPRadioButton
|
||||
//
|
||||
this.UDPRadioButton.AutoSize = true;
|
||||
this.UDPRadioButton.Location = new System.Drawing.Point(9, 16);
|
||||
this.UDPRadioButton.Name = "UDPRadioButton";
|
||||
this.UDPRadioButton.Size = new System.Drawing.Size(48, 17);
|
||||
this.UDPRadioButton.TabIndex = 0;
|
||||
this.UDPRadioButton.TabStop = true;
|
||||
this.UDPRadioButton.Text = "UDP";
|
||||
this.UDPRadioButton.UseVisualStyleBackColor = true;
|
||||
this.UDPRadioButton.CheckedChanged += new System.EventHandler(this.OnEthernetTypeCheckedChanged);
|
||||
//
|
||||
// HostInterfaceGroupBox
|
||||
//
|
||||
this.HostInterfaceGroupBox.Controls.Add(this.EthernetInterfaceListBox);
|
||||
this.HostInterfaceGroupBox.Controls.Add(this.label4);
|
||||
this.HostInterfaceGroupBox.Location = new System.Drawing.Point(10, 75);
|
||||
this.HostInterfaceGroupBox.Name = "HostInterfaceGroupBox";
|
||||
this.HostInterfaceGroupBox.Size = new System.Drawing.Size(335, 120);
|
||||
this.HostInterfaceGroupBox.TabIndex = 2;
|
||||
this.HostInterfaceGroupBox.TabStop = false;
|
||||
this.HostInterfaceGroupBox.Text = "Host Interface";
|
||||
//
|
||||
// EthernetInterfaceListBox
|
||||
//
|
||||
this.EthernetInterfaceListBox.FormattingEnabled = true;
|
||||
this.EthernetInterfaceListBox.Location = new System.Drawing.Point(9, 41);
|
||||
this.EthernetInterfaceListBox.Name = "EthernetInterfaceListBox";
|
||||
this.EthernetInterfaceListBox.Size = new System.Drawing.Size(326, 69);
|
||||
this.EthernetInterfaceListBox.TabIndex = 1;
|
||||
//
|
||||
// label4
|
||||
//
|
||||
this.label4.AutoSize = true;
|
||||
this.label4.Location = new System.Drawing.Point(9, 20);
|
||||
this.label4.Name = "label4";
|
||||
this.label4.Size = new System.Drawing.Size(245, 13);
|
||||
this.label4.TabIndex = 0;
|
||||
this.label4.Text = "Select the Network interface to use with ContrAlto:";
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.AutoSize = true;
|
||||
this.label3.Location = new System.Drawing.Point(7, 13);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(101, 13);
|
||||
this.label3.TabIndex = 1;
|
||||
this.label3.Text = "Alto Address (octal):";
|
||||
//
|
||||
// AltoEthernetAddressTextBox
|
||||
//
|
||||
this.AltoEthernetAddressTextBox.Location = new System.Drawing.Point(114, 10);
|
||||
this.AltoEthernetAddressTextBox.Name = "AltoEthernetAddressTextBox";
|
||||
this.AltoEthernetAddressTextBox.Size = new System.Drawing.Size(49, 20);
|
||||
this.AltoEthernetAddressTextBox.TabIndex = 0;
|
||||
//
|
||||
// tabPage3
|
||||
//
|
||||
this.tabPage3.Controls.Add(this.ThrottleSpeedCheckBox);
|
||||
this.tabPage3.Controls.Add(this.InterlaceDisplayCheckBox);
|
||||
this.tabPage3.Location = new System.Drawing.Point(4, 22);
|
||||
this.tabPage3.Name = "tabPage3";
|
||||
this.tabPage3.Padding = new System.Windows.Forms.Padding(3);
|
||||
this.tabPage3.Size = new System.Drawing.Size(360, 201);
|
||||
this.tabPage3.TabIndex = 2;
|
||||
this.tabPage3.Text = "Display";
|
||||
this.tabPage3.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// ThrottleSpeedCheckBox
|
||||
//
|
||||
this.ThrottleSpeedCheckBox.AutoSize = true;
|
||||
this.ThrottleSpeedCheckBox.Location = new System.Drawing.Point(19, 22);
|
||||
this.ThrottleSpeedCheckBox.Name = "ThrottleSpeedCheckBox";
|
||||
this.ThrottleSpeedCheckBox.Size = new System.Drawing.Size(188, 17);
|
||||
this.ThrottleSpeedCheckBox.TabIndex = 1;
|
||||
this.ThrottleSpeedCheckBox.Text = "Throttle Framerate at 60 fields/sec";
|
||||
this.ThrottleSpeedCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// InterlaceDisplayCheckBox
|
||||
//
|
||||
this.InterlaceDisplayCheckBox.AutoSize = true;
|
||||
this.InterlaceDisplayCheckBox.Location = new System.Drawing.Point(19, 45);
|
||||
this.InterlaceDisplayCheckBox.Name = "InterlaceDisplayCheckBox";
|
||||
this.InterlaceDisplayCheckBox.Size = new System.Drawing.Size(196, 17);
|
||||
this.InterlaceDisplayCheckBox.TabIndex = 0;
|
||||
this.InterlaceDisplayCheckBox.Text = "Interlaced Display (headache mode)";
|
||||
this.InterlaceDisplayCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// OKButton
|
||||
//
|
||||
this.OKButton.Location = new System.Drawing.Point(211, 239);
|
||||
this.OKButton.Name = "OKButton";
|
||||
this.OKButton.Size = new System.Drawing.Size(75, 23);
|
||||
this.OKButton.TabIndex = 1;
|
||||
this.OKButton.Text = "OK";
|
||||
this.OKButton.UseVisualStyleBackColor = true;
|
||||
this.OKButton.Click += new System.EventHandler(this.OKButton_Click);
|
||||
//
|
||||
// CancelButton
|
||||
//
|
||||
this.CancelButton.Location = new System.Drawing.Point(292, 239);
|
||||
this.CancelButton.Name = "CancelButton";
|
||||
this.CancelButton.Size = new System.Drawing.Size(75, 23);
|
||||
this.CancelButton.TabIndex = 2;
|
||||
this.CancelButton.Text = "Cancel";
|
||||
this.CancelButton.UseVisualStyleBackColor = true;
|
||||
this.CancelButton.Click += new System.EventHandler(this.CancelButton_Click);
|
||||
//
|
||||
// SystemOptions
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(371, 271);
|
||||
this.Controls.Add(this.CancelButton);
|
||||
this.Controls.Add(this.OKButton);
|
||||
this.Controls.Add(this.tabControl1);
|
||||
this.Name = "SystemOptions";
|
||||
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "System Options";
|
||||
this.tabControl1.ResumeLayout(false);
|
||||
this.tabPage1.ResumeLayout(false);
|
||||
this.tabPage1.PerformLayout();
|
||||
this.tabPage2.ResumeLayout(false);
|
||||
this.tabPage2.PerformLayout();
|
||||
this.groupBox1.ResumeLayout(false);
|
||||
this.groupBox1.PerformLayout();
|
||||
this.HostInterfaceGroupBox.ResumeLayout(false);
|
||||
this.HostInterfaceGroupBox.PerformLayout();
|
||||
this.tabPage3.ResumeLayout(false);
|
||||
this.tabPage3.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.TabControl tabControl1;
|
||||
private System.Windows.Forms.TabPage tabPage1;
|
||||
private System.Windows.Forms.RadioButton AltoII3KRAMRadioButton;
|
||||
private System.Windows.Forms.RadioButton AltoII2KROMRadioButton;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.RadioButton AltoII1KROMRadioButton;
|
||||
private System.Windows.Forms.TabPage tabPage2;
|
||||
private System.Windows.Forms.GroupBox HostInterfaceGroupBox;
|
||||
private System.Windows.Forms.ListBox EthernetInterfaceListBox;
|
||||
private System.Windows.Forms.Label label4;
|
||||
private System.Windows.Forms.Label label3;
|
||||
private System.Windows.Forms.TextBox AltoEthernetAddressTextBox;
|
||||
private System.Windows.Forms.Button OKButton;
|
||||
private System.Windows.Forms.Button CancelButton;
|
||||
private System.Windows.Forms.TabPage tabPage3;
|
||||
private System.Windows.Forms.CheckBox ThrottleSpeedCheckBox;
|
||||
private System.Windows.Forms.CheckBox InterlaceDisplayCheckBox;
|
||||
private System.Windows.Forms.GroupBox groupBox1;
|
||||
private System.Windows.Forms.RadioButton RawEthernetRadioButton;
|
||||
private System.Windows.Forms.RadioButton UDPRadioButton;
|
||||
}
|
||||
}
|
||||
218
Contralto/UI/SystemOptions.cs
Normal file
218
Contralto/UI/SystemOptions.cs
Normal file
@ -0,0 +1,218 @@
|
||||
using Contralto.IO;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Contralto.UI
|
||||
{
|
||||
public partial class SystemOptions : Form
|
||||
{
|
||||
public SystemOptions()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
PopulateUI();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Populates the UI fields with data from the current configuration.
|
||||
/// </summary>
|
||||
private void PopulateUI()
|
||||
{
|
||||
_selectedSystemType = Configuration.SystemType;
|
||||
_selectedInterfaceType = Configuration.HostPacketInterfaceType;
|
||||
|
||||
switch(Configuration.SystemType)
|
||||
{
|
||||
case SystemType.OneKRom:
|
||||
AltoII1KROMRadioButton.Checked = true;
|
||||
break;
|
||||
|
||||
case SystemType.TwoKRom:
|
||||
AltoII2KROMRadioButton.Checked = true;
|
||||
break;
|
||||
|
||||
case SystemType.ThreeKRam:
|
||||
AltoII3KRAMRadioButton.Checked = true;
|
||||
break;
|
||||
}
|
||||
|
||||
InterlaceDisplayCheckBox.Checked = Configuration.InterlaceDisplay;
|
||||
ThrottleSpeedCheckBox.Checked = Configuration.ThrottleSpeed;
|
||||
|
||||
AltoEthernetAddressTextBox.Text = Conversion.ToOctal(Configuration.HostAddress);
|
||||
|
||||
if (!Configuration.HostRawEthernetInterfacesAvailable)
|
||||
{
|
||||
// If PCAP isn't installed, the RAW Ethernet option is not available.
|
||||
RawEthernetRadioButton.Enabled = false;
|
||||
UDPRadioButton.Checked = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Configuration.HostPacketInterfaceType == PacketInterfaceType.UDPEncapsulation)
|
||||
{
|
||||
UDPRadioButton.Checked = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
RawEthernetRadioButton.Checked = true;
|
||||
}
|
||||
}
|
||||
|
||||
PopulateNetworkAdapterList(UDPRadioButton.Checked);
|
||||
}
|
||||
|
||||
private void PopulateNetworkAdapterList(bool udpEncapsulation)
|
||||
{
|
||||
//
|
||||
// Populate the list with the interfaces available on the machine, for the
|
||||
// type of encapsulation being used.
|
||||
//
|
||||
EthernetInterfaceListBox.Items.Clear();
|
||||
|
||||
NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces();
|
||||
|
||||
// Add the "Use no interface" option
|
||||
EthernetInterfaceListBox.Items.Add(
|
||||
new EthernetInterface("None", "No network adapter"));
|
||||
|
||||
foreach (NetworkInterface iface in interfaces)
|
||||
{
|
||||
// For UDP we show all interfaces that support IPV4, for Raw Ethernet we show only Ethernet interfaces.
|
||||
if (udpEncapsulation)
|
||||
{
|
||||
if (iface.Supports(NetworkInterfaceComponent.IPv4))
|
||||
{
|
||||
EthernetInterfaceListBox.Items.Add(new EthernetInterface(iface.Name, iface.Description));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (iface.NetworkInterfaceType == NetworkInterfaceType.Ethernet)
|
||||
{
|
||||
EthernetInterfaceListBox.Items.Add(new EthernetInterface(iface.Name, iface.Description));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Select the one that is already selected (if any)
|
||||
//
|
||||
EthernetInterfaceListBox.SelectedIndex = 0;
|
||||
|
||||
if (!string.IsNullOrEmpty(Configuration.HostPacketInterfaceName))
|
||||
{
|
||||
for (int i = 0; i < EthernetInterfaceListBox.Items.Count; i++)
|
||||
{
|
||||
EthernetInterface iface = (EthernetInterface)EthernetInterfaceListBox.Items[i];
|
||||
|
||||
if (iface.Description.ToLowerInvariant() == Configuration.HostPacketInterfaceName.ToLowerInvariant())
|
||||
{
|
||||
EthernetInterfaceListBox.SelectedIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSystemTypeCheckChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (AltoII1KROMRadioButton.Checked)
|
||||
{
|
||||
_selectedSystemType = SystemType.OneKRom;
|
||||
}
|
||||
else if (AltoII2KROMRadioButton.Checked)
|
||||
{
|
||||
_selectedSystemType = SystemType.TwoKRom;
|
||||
}
|
||||
else if (AltoII3KRAMRadioButton.Checked)
|
||||
{
|
||||
_selectedSystemType = SystemType.ThreeKRam;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEthernetTypeCheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (UDPRadioButton.Checked)
|
||||
{
|
||||
_selectedInterfaceType = PacketInterfaceType.UDPEncapsulation;
|
||||
}
|
||||
else
|
||||
{
|
||||
_selectedInterfaceType = PacketInterfaceType.EthernetEncapsulation;
|
||||
}
|
||||
|
||||
PopulateNetworkAdapterList(UDPRadioButton.Checked);
|
||||
}
|
||||
|
||||
private void OKButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
int testValue = Convert.ToByte(AltoEthernetAddressTextBox.Text, 8);
|
||||
|
||||
if (testValue < 1 || testValue > 254)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Invalid host address.");
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
MessageBox.Show("The Alto Ethernet address must be an octal value between 1 and 376.");
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Commit changes back to Configuration.
|
||||
//
|
||||
|
||||
EthernetInterface iface = (EthernetInterface)EthernetInterfaceListBox.SelectedItem;
|
||||
|
||||
//
|
||||
// First warn the user of changes that require a restart.
|
||||
//
|
||||
if (Configuration.HostPacketInterfaceName.ToLowerInvariant() != iface.Description.ToLowerInvariant() ||
|
||||
Configuration.HostPacketInterfaceType != _selectedInterfaceType ||
|
||||
Configuration.SystemType != _selectedSystemType)
|
||||
{
|
||||
MessageBox.Show("Changes to CPU or Ethernet configuration will not take effect until ContrAlto is restarted.");
|
||||
}
|
||||
|
||||
//System
|
||||
Configuration.SystemType = _selectedSystemType;
|
||||
|
||||
// Ethernet
|
||||
Configuration.HostAddress = Convert.ToByte(AltoEthernetAddressTextBox.Text, 8);
|
||||
Configuration.HostPacketInterfaceName = iface.Description;
|
||||
Configuration.HostPacketInterfaceType = _selectedInterfaceType;
|
||||
|
||||
// Display
|
||||
Configuration.InterlaceDisplay = InterlaceDisplayCheckBox.Checked;
|
||||
Configuration.ThrottleSpeed = ThrottleSpeedCheckBox.Checked;
|
||||
|
||||
Configuration.WriteConfiguration();
|
||||
|
||||
this.Close();
|
||||
|
||||
}
|
||||
|
||||
private void CancelButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
|
||||
private PacketInterfaceType _selectedInterfaceType;
|
||||
private SystemType _selectedSystemType;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
120
Contralto/UI/SystemOptions.resx
Normal file
120
Contralto/UI/SystemOptions.resx
Normal file
@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
Loading…
x
Reference in New Issue
Block a user