mirror of
https://github.com/livingcomputermuseum/ContrAlto.git
synced 2026-02-01 22:33:05 +00:00
Initial implementation of Trident controller and drives (supporting T-80 and T-300 packs). TFU works and can certify, erase, exercise and manipulate files on Trident packs. TriEx doesn't quite work properly yet. Still some issues to iron out.
Added file-backed disk image implementation for use with Trident disk images, did some basic refactoring of disk load/unload logic, added support for creating new (empty) disk images for both Trident and Diablo disks. Added UI for loading/unloading/creating up to 8 trident packs; added blank Diablo pack creation UI. (Both Windows and *nix interfaces.) Added configuration support for same (both Windows and *nix.) Small correction to Print output path browsing logic. Fixed Windows installer, now places the right ROMs for Alto I configurations in the right place. Fixed issue when starting up with corrupted configuration. Corrupted configuration is ignored and ContrAlto will run with default config.
This commit is contained in:
@@ -46,6 +46,7 @@ namespace Contralto
|
||||
_orbitController = new OrbitController(this);
|
||||
_audioDAC = new AudioDAC(this);
|
||||
_organKeyboard = new OrganKeyboard(this);
|
||||
_tridentController = new TridentController(this);
|
||||
|
||||
_cpu = new AltoCPU(this);
|
||||
|
||||
@@ -73,7 +74,8 @@ namespace Contralto
|
||||
_mouse.Reset();
|
||||
_cpu.Reset();
|
||||
_ethernetController.Reset();
|
||||
_orbitController.Reset();
|
||||
_orbitController.Reset();
|
||||
_tridentController.Reset();
|
||||
|
||||
UCodeMemory.Reset();
|
||||
}
|
||||
@@ -104,6 +106,17 @@ namespace Contralto
|
||||
// Allow the DAC to flush its output
|
||||
//
|
||||
_audioDAC.Shutdown();
|
||||
|
||||
//
|
||||
// Save disk contents
|
||||
//
|
||||
_diskController.CommitDisk(0);
|
||||
_diskController.CommitDisk(1);
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
_tridentController.CommitDisk(i);
|
||||
}
|
||||
}
|
||||
|
||||
public void SingleStep()
|
||||
@@ -116,73 +129,131 @@ namespace Contralto
|
||||
_scheduler.Clock();
|
||||
}
|
||||
|
||||
public void LoadDrive(int drive, string path)
|
||||
public void LoadDiabloDrive(int drive, string path, bool newImage)
|
||||
{
|
||||
if (drive < 0 || drive > 1)
|
||||
{
|
||||
throw new InvalidOperationException("drive must be 0 or 1.");
|
||||
}
|
||||
|
||||
DiabloDiskType type;
|
||||
//
|
||||
// Commit the current disk first
|
||||
//
|
||||
_diskController.CommitDisk(drive);
|
||||
|
||||
DiskGeometry geometry;
|
||||
|
||||
//
|
||||
// We select the disk type based on the file extension. Very elegant.
|
||||
//
|
||||
switch(Path.GetExtension(path).ToLowerInvariant())
|
||||
{
|
||||
case ".dsk":
|
||||
geometry = DiskGeometry.Diablo31;
|
||||
break;
|
||||
|
||||
case ".dsk44":
|
||||
type = DiabloDiskType.Diablo44;
|
||||
geometry = DiskGeometry.Diablo44;
|
||||
break;
|
||||
|
||||
default:
|
||||
type = DiabloDiskType.Diablo31;
|
||||
geometry = DiskGeometry.Diablo31;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DiabloPack newPack = new DiabloPack(type);
|
||||
|
||||
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
newPack.Load(fs, path, false);
|
||||
fs.Close();
|
||||
IDiskPack newPack;
|
||||
|
||||
if (newImage)
|
||||
{
|
||||
newPack = InMemoryDiskPack.CreateEmpty(geometry, path);
|
||||
}
|
||||
else
|
||||
{
|
||||
newPack = InMemoryDiskPack.Load(geometry, path);
|
||||
}
|
||||
|
||||
_diskController.Drives[drive].LoadPack(newPack);
|
||||
}
|
||||
|
||||
public void UnloadDrive(int drive)
|
||||
public void UnloadDiabloDrive(int drive)
|
||||
{
|
||||
if (drive < 0 || drive > 1)
|
||||
{
|
||||
throw new InvalidOperationException("drive must be 0 or 1.");
|
||||
}
|
||||
|
||||
//
|
||||
// Commit the current disk first
|
||||
//
|
||||
_diskController.CommitDisk(drive);
|
||||
|
||||
_diskController.Drives[drive].UnloadPack();
|
||||
}
|
||||
|
||||
//
|
||||
// Disk handling
|
||||
//
|
||||
public void CommitDiskPack(int driveId)
|
||||
public void LoadTridentDrive(int drive, string path, bool newImage)
|
||||
{
|
||||
DiabloDrive drive = _diskController.Drives[driveId];
|
||||
if (drive.IsLoaded)
|
||||
if (drive < 0 || drive > 8)
|
||||
{
|
||||
using (FileStream fs = new FileStream(drive.Pack.PackName, FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
try
|
||||
{
|
||||
drive.Pack.Save(fs);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// TODO: this does not really belong here.
|
||||
System.Windows.Forms.MessageBox.Show(String.Format("Unable to save disk {0}'s contents. Error {0}. Any changes have been lost.", e.Message), "Disk save error");
|
||||
}
|
||||
}
|
||||
throw new InvalidOperationException("drive must be between 0 and 7.");
|
||||
}
|
||||
|
||||
//
|
||||
// Commit the current disk first
|
||||
//
|
||||
_tridentController.CommitDisk(drive);
|
||||
|
||||
DiskGeometry geometry;
|
||||
|
||||
//
|
||||
// We select the disk type based on the file extension. Very elegant.
|
||||
//
|
||||
switch (Path.GetExtension(path).ToLowerInvariant())
|
||||
{
|
||||
case ".t80":
|
||||
geometry = DiskGeometry.TridentT80;
|
||||
break;
|
||||
|
||||
case ".t300":
|
||||
geometry = DiskGeometry.TridentT300;
|
||||
break;
|
||||
|
||||
default:
|
||||
geometry = DiskGeometry.TridentT80;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
IDiskPack newPack;
|
||||
|
||||
if (newImage)
|
||||
{
|
||||
newPack = FileBackedDiskPack.CreateEmpty(geometry, path);
|
||||
}
|
||||
else
|
||||
{
|
||||
newPack = FileBackedDiskPack.Load(geometry, path);
|
||||
}
|
||||
|
||||
_tridentController.Drives[drive].LoadPack(newPack);
|
||||
}
|
||||
|
||||
public void UnloadTridentDrive(int drive)
|
||||
{
|
||||
if (drive < 0 || drive > 7)
|
||||
{
|
||||
throw new InvalidOperationException("drive must be between 0 and 7.");
|
||||
}
|
||||
|
||||
//
|
||||
// Commit the current disk first
|
||||
//
|
||||
_tridentController.CommitDisk(drive);
|
||||
|
||||
_tridentController.Drives[drive].UnloadPack();
|
||||
}
|
||||
|
||||
|
||||
public void PressBootKeys(AlternateBootType bootType)
|
||||
{
|
||||
switch(bootType)
|
||||
@@ -237,6 +308,11 @@ namespace Contralto
|
||||
get { return _orbitController; }
|
||||
}
|
||||
|
||||
public TridentController TridentController
|
||||
{
|
||||
get { return _tridentController; }
|
||||
}
|
||||
|
||||
public Scheduler Scheduler
|
||||
{
|
||||
get { return _scheduler; }
|
||||
@@ -253,6 +329,7 @@ namespace Contralto
|
||||
private OrbitController _orbitController;
|
||||
private AudioDAC _audioDAC;
|
||||
private OrganKeyboard _organKeyboard;
|
||||
private TridentController _tridentController;
|
||||
|
||||
private Scheduler _scheduler;
|
||||
}
|
||||
|
||||
@@ -67,6 +67,22 @@
|
||||
<setting name="ReversePageOrder" serializeAs="String">
|
||||
<value>True</value>
|
||||
</setting>
|
||||
<setting name="TridentImages" serializeAs="Xml">
|
||||
<value>
|
||||
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<string />
|
||||
<string />
|
||||
<string />
|
||||
<string />
|
||||
<string />
|
||||
<string />
|
||||
<string />
|
||||
<string />
|
||||
<string />
|
||||
</ArrayOfString>
|
||||
</value>
|
||||
</setting>
|
||||
</Contralto.Properties.Settings>
|
||||
</userSettings>
|
||||
</configuration>
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace Contralto.CPU
|
||||
Invalid = -1,
|
||||
Emulator = 0,
|
||||
Orbit = 1,
|
||||
TridentOutput = 3,
|
||||
DiskSector = 4,
|
||||
Ethernet = 7,
|
||||
MemoryRefresh = 8,
|
||||
@@ -35,6 +36,7 @@ namespace Contralto.CPU
|
||||
DisplayVertical = 12,
|
||||
Parity = 13,
|
||||
DiskWord = 14,
|
||||
TridentInput = 15,
|
||||
}
|
||||
|
||||
public partial class AltoCPU : IClockable
|
||||
@@ -54,6 +56,8 @@ namespace Contralto.CPU
|
||||
_tasks[(int)TaskType.Ethernet] = new EthernetTask(this);
|
||||
_tasks[(int)TaskType.Parity] = new ParityTask(this);
|
||||
_tasks[(int)TaskType.Orbit] = new OrbitTask(this);
|
||||
_tasks[(int)TaskType.TridentInput] = new TridentTask(this, true);
|
||||
_tasks[(int)TaskType.TridentOutput] = new TridentTask(this, false);
|
||||
|
||||
Reset();
|
||||
}
|
||||
@@ -182,7 +186,7 @@ namespace Contralto.CPU
|
||||
_tasks[i].SoftReset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Log.Write(LogComponent.CPU, "Silent Boot; microcode banks initialized to {0}", Conversion.ToOctal(_rmr));
|
||||
UCodeMemory.LoadBanksFromRMR(_rmr);
|
||||
|
||||
@@ -202,6 +206,9 @@ namespace Contralto.CPU
|
||||
//
|
||||
WakeupTask(CPU.TaskType.DiskSector);
|
||||
BlockTask(CPU.TaskType.DiskWord);
|
||||
|
||||
BlockTask(CPU.TaskType.TridentInput);
|
||||
BlockTask(CPU.TaskType.TridentOutput);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -217,6 +217,21 @@ namespace Contralto.CPU
|
||||
OrbitROSCommand = 14,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trident disk controller, from the microcode.
|
||||
/// </summary>
|
||||
enum TridentF2
|
||||
{
|
||||
ReadKDTA = 6,
|
||||
STATUS = 8,
|
||||
KTAG = 10,
|
||||
WriteKDTA = 11,
|
||||
WAIT = 12, // These two are identical in function
|
||||
WAIT2 = 13,
|
||||
RESET = 14,
|
||||
EMPTY = 15,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MicroInstruction encapsulates the decoding of a microinstruction word.
|
||||
/// It also caches precomputed metadata related to the microinstruction that
|
||||
|
||||
@@ -33,6 +33,9 @@ namespace Contralto.CPU
|
||||
|
||||
// The Wakeup signal is always true for the Emulator task.
|
||||
_wakeup = true;
|
||||
|
||||
// The Emulator is a RAM-related task.
|
||||
_ramTask = true;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
@@ -138,20 +141,26 @@ namespace Contralto.CPU
|
||||
//
|
||||
switch(_busData)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
// Ethernet
|
||||
_cpu._system.EthernetController.STARTF(_busData);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// Orbit
|
||||
case 0x04:
|
||||
// Orbit
|
||||
_cpu._system.OrbitController.STARTF(_busData);
|
||||
break;
|
||||
|
||||
case 0x10:
|
||||
case 0x20:
|
||||
// Trident start
|
||||
_cpu._system.TridentController.STARTF(_busData);
|
||||
break;
|
||||
|
||||
default:
|
||||
Log.Write(Logging.LogType.Warning, Logging.LogComponent.EmulatorTask, "STARTF for non-Ethernet device (code {0})",
|
||||
Log.Write(Logging.LogType.Warning, Logging.LogComponent.EmulatorTask, "STARTF for unknown device (code {0})",
|
||||
Conversion.ToOctal(_busData));
|
||||
break;
|
||||
}
|
||||
@@ -163,6 +172,8 @@ namespace Contralto.CPU
|
||||
break;
|
||||
|
||||
case EmulatorF1.RDRAM:
|
||||
// TODO: move RDRAM, WRTRAM and S-register BS stuff into the main Task implementation,
|
||||
// guarded by a check of _ramTask.
|
||||
_rdRam = true;
|
||||
break;
|
||||
|
||||
|
||||
@@ -31,6 +31,9 @@ namespace Contralto.CPU
|
||||
{
|
||||
_taskType = TaskType.Orbit;
|
||||
_wakeup = false;
|
||||
|
||||
// The Orbit task is RAM-related.
|
||||
_ramTask = true;
|
||||
}
|
||||
|
||||
protected override void ExecuteBlock()
|
||||
|
||||
@@ -139,13 +139,13 @@ namespace Contralto.CPU
|
||||
protected virtual InstructionCompletion ExecuteInstruction(MicroInstruction instruction)
|
||||
{
|
||||
InstructionCompletion completion = InstructionCompletion.Normal;
|
||||
bool swMode = false;
|
||||
bool swMode = false;
|
||||
ushort aluData;
|
||||
ushort nextModifier;
|
||||
bool softReset = _softReset;
|
||||
_loadR = false;
|
||||
_loadS = false;
|
||||
_rSelect = 0;
|
||||
_rSelect = 0;
|
||||
_busData = 0;
|
||||
_softReset = false;
|
||||
|
||||
@@ -159,7 +159,7 @@ namespace Contralto.CPU
|
||||
{
|
||||
if (!_cpu._system.MemoryBus.Ready(instruction.MemoryOperation))
|
||||
{
|
||||
// Suspend operation for this cycle.
|
||||
// Suspend operation for this cycle.
|
||||
return InstructionCompletion.MemoryWait;
|
||||
}
|
||||
}
|
||||
@@ -248,6 +248,12 @@ namespace Contralto.CPU
|
||||
_busData &= instruction.ConstantValue;
|
||||
}
|
||||
|
||||
//
|
||||
// Let F2s that need to modify bus data before the ALU runs do their thing.
|
||||
// (This is used by the Trident KDTA special functions)
|
||||
//
|
||||
ExecuteSpecialFunction2PostBusSource(instruction);
|
||||
|
||||
//
|
||||
// If there was a RDRAM operation last cycle, we AND in the uCode RAM data here.
|
||||
//
|
||||
@@ -487,7 +493,7 @@ namespace Contralto.CPU
|
||||
// address register... which is loaded from the ALU output whenver T is loaded
|
||||
// from its source."
|
||||
//
|
||||
UCodeMemory.LoadControlRAMAddress(aluData);
|
||||
UCodeMemory.LoadControlRAMAddress(aluData);
|
||||
}
|
||||
|
||||
// Load L (and M) from ALU outputs.
|
||||
@@ -496,8 +502,7 @@ namespace Contralto.CPU
|
||||
_cpu._l = aluData;
|
||||
|
||||
// Only RAM-related tasks can modify M.
|
||||
if (_taskType == TaskType.Emulator ||
|
||||
_taskType == TaskType.Orbit)
|
||||
if (_ramTask)
|
||||
{
|
||||
_cpu._m = aluData;
|
||||
}
|
||||
@@ -587,6 +592,16 @@ namespace Contralto.CPU
|
||||
// Nothing by default.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes after bus sources are selected but before the ALU. Used to allow Task-specific F2s that need to
|
||||
/// modify bus data to do so.
|
||||
/// </summary>
|
||||
/// <param name="f2"></param>
|
||||
protected virtual void ExecuteSpecialFunction2PostBusSource(MicroInstruction instruction)
|
||||
{
|
||||
// Nothing by default.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes after the ALU has run but before the shifter runs, provides task-specific implementations
|
||||
/// the opportunity to handle task-specific F2s.
|
||||
@@ -629,6 +644,14 @@ namespace Contralto.CPU
|
||||
/// </summary>
|
||||
protected SystemType _systemType;
|
||||
|
||||
//
|
||||
// Task metadata:
|
||||
//
|
||||
|
||||
// Whether this task is RAM-enabled or not
|
||||
// (can access S registers, etc.)
|
||||
protected bool _ramTask;
|
||||
|
||||
//
|
||||
// Per uInstruction Task Data:
|
||||
// Modified by both the base Task implementation and any subclasses
|
||||
|
||||
157
Contralto/CPU/Tasks/TridentTask.cs
Normal file
157
Contralto/CPU/Tasks/TridentTask.cs
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
This file is part of ContrAlto.
|
||||
|
||||
ContrAlto is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ContrAlto is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with ContrAlto. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using Contralto.IO;
|
||||
using System;
|
||||
|
||||
namespace Contralto.CPU
|
||||
{
|
||||
public partial class AltoCPU
|
||||
{
|
||||
/// <summary>
|
||||
/// TridentTask implements the microcode special functions for the Trident
|
||||
/// disk controller (for both the read and write tasks).
|
||||
/// </summary>
|
||||
private sealed class TridentTask : Task
|
||||
{
|
||||
public TridentTask(AltoCPU cpu, bool input) : base(cpu)
|
||||
{
|
||||
_taskType = input ? TaskType.TridentInput : TaskType.TridentOutput;
|
||||
_wakeup = false;
|
||||
|
||||
_tridentController = cpu._system.TridentController;
|
||||
|
||||
// Both Trident tasks are RAM-related
|
||||
_ramTask = true;
|
||||
}
|
||||
|
||||
public override void SoftReset()
|
||||
{
|
||||
//
|
||||
// Stop the controller.
|
||||
//
|
||||
_tridentController.Stop();
|
||||
|
||||
base.SoftReset();
|
||||
}
|
||||
|
||||
protected override ushort GetBusSource(MicroInstruction instruction)
|
||||
{
|
||||
//
|
||||
// The Trident tasks are wired to be RAM-enabled tasks so they can use
|
||||
// S registers.
|
||||
// This code is stolen from the Emulator task; we should REALLY refactor this
|
||||
// since both this and the Orbit Task need it.
|
||||
//
|
||||
EmulatorBusSource ebs = (EmulatorBusSource)instruction.BS;
|
||||
|
||||
switch (ebs)
|
||||
{
|
||||
case EmulatorBusSource.ReadSLocation:
|
||||
if (instruction.RSELECT != 0)
|
||||
{
|
||||
return _cpu._s[_rb][instruction.RSELECT];
|
||||
}
|
||||
else
|
||||
{
|
||||
// "...when reading data from the S registers onto the processor bus,
|
||||
// the RSELECT value 0 causes the current value of the M register to
|
||||
// appear on the bus..."
|
||||
return _cpu._m;
|
||||
}
|
||||
|
||||
case EmulatorBusSource.LoadSLocation:
|
||||
// "When an S register is being loaded from M, the processor bus receives an
|
||||
// undefined value rather than being set to zero."
|
||||
_loadS = true;
|
||||
return 0xffff; // Technically this is an "undefined value," we're defining it as -1.
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException(String.Format("Unhandled bus source {0}", instruction.BS));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ExecuteSpecialFunction2PostBusSource(MicroInstruction instruction)
|
||||
{
|
||||
TridentF2 tf2 = (TridentF2)instruction.F2;
|
||||
|
||||
switch (tf2)
|
||||
{
|
||||
case TridentF2.ReadKDTA:
|
||||
//
|
||||
// <-KDTA is actually MD<- (SF 6), repurposed to gate disk data onto the bus
|
||||
// iff BS is None. Otherwise it behaves like a normal MD<-. We'll let
|
||||
// the normal Task implementation handle the actual MD<- operation.
|
||||
//
|
||||
if (instruction.BS == BusSource.None)
|
||||
{
|
||||
// _busData at this point should be 0xffff. We could technically
|
||||
// just directly assign the bits...
|
||||
_busData &= _tridentController.KDTA;
|
||||
}
|
||||
break;
|
||||
|
||||
case TridentF2.STATUS:
|
||||
_busData &= _tridentController.STATUS;
|
||||
break;
|
||||
|
||||
case TridentF2.EMPTY:
|
||||
_tridentController.WaitForEmpty();
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ExecuteSpecialFunction2(MicroInstruction instruction)
|
||||
{
|
||||
TridentF2 tf2 = (TridentF2)instruction.F2;
|
||||
|
||||
switch (tf2)
|
||||
{
|
||||
case TridentF2.KTAG:
|
||||
_tridentController.TagInstruction(_busData);
|
||||
break;
|
||||
|
||||
case TridentF2.WriteKDTA:
|
||||
_tridentController.KDTA = _busData;
|
||||
break;
|
||||
|
||||
case TridentF2.WAIT:
|
||||
case TridentF2.WAIT2:
|
||||
// Identical to BLOCK
|
||||
this.BlockTask();
|
||||
break;
|
||||
|
||||
case TridentF2.RESET:
|
||||
_tridentController.ControllerReset();
|
||||
break;
|
||||
|
||||
case TridentF2.STATUS:
|
||||
case TridentF2.EMPTY:
|
||||
// Handled in PostBusSource override.
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException(String.Format("Unhandled trident special function 2 {0}", tf2));
|
||||
}
|
||||
}
|
||||
|
||||
private TridentController _tridentController;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -229,7 +229,13 @@ namespace Contralto.CPU
|
||||
break;
|
||||
|
||||
case SpecialFunction2.StoreMD:
|
||||
if (instruction.F1 != SpecialFunction1.LoadMAR)
|
||||
// Special case for Trident
|
||||
if ((task == TaskType.TridentInput || task == TaskType.TridentOutput) &&
|
||||
instruction.BS == BusSource.None)
|
||||
{
|
||||
f2 = "MD← KDTA ";
|
||||
}
|
||||
else if (instruction.F1 != SpecialFunction1.LoadMAR)
|
||||
{
|
||||
f2 = "MD← ";
|
||||
}
|
||||
@@ -352,6 +358,8 @@ namespace Contralto.CPU
|
||||
{
|
||||
case TaskType.Emulator:
|
||||
case TaskType.Orbit:
|
||||
case TaskType.TridentInput:
|
||||
case TaskType.TridentOutput:
|
||||
return DisassembleEmulatorBusSource(instruction, out loadS);
|
||||
|
||||
default:
|
||||
@@ -368,7 +376,7 @@ namespace Contralto.CPU
|
||||
return DisassembleEmulatorSpecialFunction1(instruction);
|
||||
|
||||
case TaskType.Orbit:
|
||||
return DisassembleOrbitSpecialFunction1(instruction);
|
||||
return DisassembleOrbitSpecialFunction1(instruction);
|
||||
|
||||
default:
|
||||
return String.Format("F1 {0}", Conversion.ToOctal((int)instruction.F1));
|
||||
@@ -383,7 +391,11 @@ namespace Contralto.CPU
|
||||
return DisassembleEmulatorSpecialFunction2(instruction);
|
||||
|
||||
case TaskType.Orbit:
|
||||
return DisassembleOrbitSpecialFunction2(instruction);
|
||||
return DisassembleOrbitSpecialFunction2(instruction);
|
||||
|
||||
case TaskType.TridentInput:
|
||||
case TaskType.TridentOutput:
|
||||
return DisassembleTridentSpecialFunction2(instruction);
|
||||
|
||||
default:
|
||||
return String.Format("F2 {0}", Conversion.ToOctal((int)instruction.F2));
|
||||
@@ -539,5 +551,38 @@ namespace Contralto.CPU
|
||||
return String.Format("Orbit F2 {0}", Conversion.ToOctal((int)of2));
|
||||
}
|
||||
}
|
||||
|
||||
private static string DisassembleTridentSpecialFunction2(MicroInstruction instruction)
|
||||
{
|
||||
TridentF2 tf2 = (TridentF2)instruction.F2;
|
||||
|
||||
switch (tf2)
|
||||
{
|
||||
case TridentF2.EMPTY:
|
||||
return "EMPTY ";
|
||||
|
||||
case TridentF2.KTAG:
|
||||
return "KTAG← ";
|
||||
|
||||
case TridentF2.ReadKDTA:
|
||||
return "←KDTA ";
|
||||
|
||||
case TridentF2.RESET:
|
||||
return "RESET ";
|
||||
|
||||
case TridentF2.STATUS:
|
||||
return "STATUS ";
|
||||
|
||||
case TridentF2.WAIT:
|
||||
case TridentF2.WAIT2:
|
||||
return "WAIT ";
|
||||
|
||||
case TridentF2.WriteKDTA:
|
||||
return "KDTA← ";
|
||||
|
||||
default:
|
||||
return String.Format("Trident F2 {0}", Conversion.ToOctal((int)tf2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
using Contralto.Logging;
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
@@ -114,7 +115,14 @@ namespace Contralto
|
||||
// See if PCap is available.
|
||||
TestPCap();
|
||||
|
||||
ReadConfiguration();
|
||||
try
|
||||
{
|
||||
ReadConfiguration();
|
||||
}
|
||||
catch
|
||||
{
|
||||
Console.WriteLine("Warning: unable to load configuration. Assuming default settings.");
|
||||
}
|
||||
|
||||
// Special case: On first startup, AlternateBoot will come back as "None" which
|
||||
// is an invalid UI setting; default to Ethernet in this case.
|
||||
@@ -122,6 +130,15 @@ namespace Contralto
|
||||
{
|
||||
AlternateBootType = AlternateBootType.Ethernet;
|
||||
}
|
||||
|
||||
//
|
||||
// If configuration specifies fewer than 8 Trident disks, we need to pad the list out to 8 with empty entries.
|
||||
//
|
||||
int start = TridentImages.Count;
|
||||
for (int i = start; i < 8; i++)
|
||||
{
|
||||
TridentImages.Add(String.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -144,6 +161,11 @@ namespace Contralto
|
||||
/// </summary>
|
||||
public static string Drive1Image;
|
||||
|
||||
/// <summary>
|
||||
/// The currently loaded images for the Trident controller.
|
||||
/// </summary>
|
||||
public static StringCollection TridentImages;
|
||||
|
||||
/// <summary>
|
||||
/// The Ethernet host address for this Alto
|
||||
/// </summary>
|
||||
@@ -294,6 +316,7 @@ namespace Contralto
|
||||
AudioDACCapturePath = Properties.Settings.Default.AudioDACCapturePath;
|
||||
Drive0Image = Properties.Settings.Default.Drive0Image;
|
||||
Drive1Image = Properties.Settings.Default.Drive1Image;
|
||||
TridentImages = Properties.Settings.Default.TridentImages;
|
||||
SystemType = (SystemType)Properties.Settings.Default.SystemType;
|
||||
HostAddress = Properties.Settings.Default.HostAddress;
|
||||
HostPacketInterfaceName = Properties.Settings.Default.HostPacketInterfaceName;
|
||||
@@ -308,13 +331,14 @@ namespace Contralto
|
||||
AudioDACCapturePath = Properties.Settings.Default.AudioDACCapturePath;
|
||||
EnablePrinting = Properties.Settings.Default.EnablePrinting;
|
||||
PrintOutputPath = Properties.Settings.Default.PrintOutputPath;
|
||||
ReversePageOrder = Properties.Settings.Default.ReversePageOrder;
|
||||
ReversePageOrder = Properties.Settings.Default.ReversePageOrder;
|
||||
}
|
||||
|
||||
private static void WriteConfigurationWindows()
|
||||
{
|
||||
Properties.Settings.Default.Drive0Image = Drive0Image;
|
||||
Properties.Settings.Default.Drive1Image = Drive1Image;
|
||||
Properties.Settings.Default.TridentImages = TridentImages;
|
||||
Properties.Settings.Default.SystemType = (int)SystemType;
|
||||
Properties.Settings.Default.HostAddress = HostAddress;
|
||||
Properties.Settings.Default.HostPacketInterfaceName = HostPacketInterfaceName;
|
||||
@@ -478,6 +502,21 @@ namespace Contralto
|
||||
field.SetValue(null, Enum.Parse(typeof(LogComponent), value, true));
|
||||
}
|
||||
break;
|
||||
|
||||
case "StringCollection":
|
||||
{
|
||||
// value is expected to be a comma-delimited set.
|
||||
StringCollection sc = new StringCollection();
|
||||
string[] strings = value.Split(',');
|
||||
|
||||
foreach(string s in strings)
|
||||
{
|
||||
sc.Add(s);
|
||||
}
|
||||
|
||||
field.SetValue(null, sc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch
|
||||
|
||||
@@ -23,3 +23,15 @@ AlternateBootType = Ethernet
|
||||
EnablePrinting = true
|
||||
PrintOutputPath = .
|
||||
ReversePageOrder = true
|
||||
|
||||
# Disk options
|
||||
#
|
||||
|
||||
# Diablo images: These specify a single image file for drive 0 and drive 1
|
||||
#
|
||||
# Drive0Image =
|
||||
# Drive1Image =
|
||||
|
||||
# Trident images: This specifies up to eight images for Trident drives 0 through 8
|
||||
# in a comma-delimited list. Empty entries are allowed, as below:
|
||||
# TridentImages = image0.dsk80, , image2.dsk300, image3.disk80
|
||||
|
||||
@@ -125,7 +125,9 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="CPU\Tasks\OrbitTask.cs" />
|
||||
<Compile Include="CPU\Tasks\TridentTask.cs" />
|
||||
<Compile Include="HighResTimer.cs" />
|
||||
<Compile Include="IO\TridentDrive.cs" />
|
||||
<Compile Include="IO\DoverROS.cs" />
|
||||
<Compile Include="IO\AudioDAC.cs" />
|
||||
<Compile Include="IO\OrbitController.cs" />
|
||||
@@ -133,6 +135,7 @@
|
||||
<Compile Include="IO\Printing\IPageSink.cs" />
|
||||
<Compile Include="IO\Printing\NullPageSink.cs" />
|
||||
<Compile Include="IO\Printing\PdfPageSink.cs" />
|
||||
<Compile Include="IO\TridentController.cs" />
|
||||
<Compile Include="IO\UDPEncapsulation.cs" />
|
||||
<Compile Include="IO\IPacketEncapsulation.cs" />
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
@@ -199,7 +202,7 @@
|
||||
<Compile Include="IClockable.cs" />
|
||||
<Compile Include="IO\DiabloDrive.cs" />
|
||||
<Compile Include="IO\DiskController.cs" />
|
||||
<Compile Include="IO\DiabloPack.cs" />
|
||||
<Compile Include="IO\DiskPack.cs" />
|
||||
<Compile Include="IO\EthernetController.cs" />
|
||||
<Compile Include="IO\HostEthernetEncapsulation.cs" />
|
||||
<Compile Include="IO\Keyboard.cs" />
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace Contralto.IO
|
||||
public void Reset()
|
||||
{
|
||||
_sector = 0;
|
||||
_cylinder = 0;
|
||||
_cylinder = 0;
|
||||
_head = 0;
|
||||
|
||||
_sectorModified = false;
|
||||
@@ -91,14 +91,24 @@ namespace Contralto.IO
|
||||
// Total "words" (really timeslices) per sector.
|
||||
public static readonly int SectorWordCount = 269 + HeaderOffset + 34;
|
||||
|
||||
public void LoadPack(DiabloPack pack)
|
||||
public void LoadPack(IDiskPack pack)
|
||||
{
|
||||
if (_pack != null)
|
||||
{
|
||||
_pack.Dispose();
|
||||
}
|
||||
|
||||
_pack = pack;
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void UnloadPack()
|
||||
{
|
||||
if (_pack != null)
|
||||
{
|
||||
_pack.Dispose();
|
||||
}
|
||||
|
||||
_pack = null;
|
||||
Reset();
|
||||
}
|
||||
@@ -108,7 +118,7 @@ namespace Contralto.IO
|
||||
get { return _pack != null; }
|
||||
}
|
||||
|
||||
public DiabloPack Pack
|
||||
public IDiskPack Pack
|
||||
{
|
||||
get { return _pack; }
|
||||
}
|
||||
@@ -218,13 +228,13 @@ namespace Contralto.IO
|
||||
// Note that this data is packed in in REVERSE ORDER because that's
|
||||
// how it gets written out and it's how the Alto expects it to be read back in.
|
||||
//
|
||||
DiabloDiskSector sector = _pack.GetSector(_cylinder, _head, _sector);
|
||||
DiskSector sector = _pack.GetSector(_cylinder, _head, _sector);
|
||||
|
||||
// Header (2 words data, 1 word cksum)
|
||||
for (int i = HeaderOffset + 1, j = 1; i < HeaderOffset + 3; i++, j--)
|
||||
{
|
||||
// actual data to be loaded from disk / cksum calculated
|
||||
_sectorData[i] = new DataCell(sector.Header[j], CellType.Data);
|
||||
_sectorData[i] = new DataCell(sector.ReadHeader(j), CellType.Data);
|
||||
}
|
||||
|
||||
ushort checksum = CalculateChecksum(_sectorData, HeaderOffset + 1, 2);
|
||||
@@ -235,7 +245,7 @@ namespace Contralto.IO
|
||||
for (int i = LabelOffset + 1, j = 7; i < LabelOffset + 9; i++, j--)
|
||||
{
|
||||
// actual data to be loaded from disk / cksum calculated
|
||||
_sectorData[i] = new DataCell(sector.Label[j], CellType.Data);
|
||||
_sectorData[i] = new DataCell(sector.ReadLabel(j), CellType.Data);
|
||||
}
|
||||
|
||||
checksum = CalculateChecksum(_sectorData, LabelOffset + 1, 8);
|
||||
@@ -246,7 +256,7 @@ namespace Contralto.IO
|
||||
for (int i = DataOffset + 1, j = 255; i < DataOffset + 257; i++, j--)
|
||||
{
|
||||
// actual data to be loaded from disk / cksum calculated
|
||||
_sectorData[i] = new DataCell(sector.Data[j], CellType.Data);
|
||||
_sectorData[i] = new DataCell(sector.ReadData(j), CellType.Data);
|
||||
}
|
||||
|
||||
checksum = CalculateChecksum(_sectorData, DataOffset + 1, 256);
|
||||
@@ -257,7 +267,7 @@ namespace Contralto.IO
|
||||
|
||||
/// <summary>
|
||||
/// Commits modified sector data back to the emulated disk.
|
||||
/// Intended to be called at the end of the sector / beginning of the next.
|
||||
/// Intended to be called at the end of the sector / beginning of the next.
|
||||
/// </summary>
|
||||
private void CommitSector()
|
||||
{
|
||||
@@ -266,33 +276,38 @@ namespace Contralto.IO
|
||||
return;
|
||||
}
|
||||
|
||||
DiabloDiskSector sector = _pack.GetSector(_cylinder, _head, _sector);
|
||||
DiskSector sector = _pack.GetSector(_cylinder, _head, _sector);
|
||||
|
||||
// Header (2 words data, 1 word cksum)
|
||||
for (int i = HeaderOffset + 1, j = 1; i < HeaderOffset + 3; i++, j--)
|
||||
{
|
||||
// actual data to be loaded from disk / cksum calculated
|
||||
sector.Header[j] = _sectorData[i].Data;
|
||||
sector.WriteHeader(j, _sectorData[i].Data);
|
||||
}
|
||||
|
||||
// Label (8 words data, 1 word cksum)
|
||||
for (int i = LabelOffset + 1, j = 7; i < LabelOffset + 9; i++, j--)
|
||||
{
|
||||
// actual data to be loaded from disk / cksum calculated
|
||||
sector.Label[j] = _sectorData[i].Data;
|
||||
sector.WriteLabel(j, _sectorData[i].Data);
|
||||
}
|
||||
|
||||
// sector data (256 words data, 1 word cksum)
|
||||
for (int i = DataOffset + 1, j = 255; i < DataOffset + 257; i++, j--)
|
||||
{
|
||||
// actual data to be loaded from disk / cksum calculated
|
||||
sector.Data[j] = _sectorData[i].Data;
|
||||
sector.WriteData(j, _sectorData[i].Data);
|
||||
}
|
||||
|
||||
//
|
||||
// Commit it back to the pack.
|
||||
//
|
||||
_pack.CommitSector(sector);
|
||||
}
|
||||
|
||||
private void InitSector()
|
||||
{
|
||||
// Fill in sector with default data (basically, fill in non-data areas).
|
||||
// Fill in sector with default data (basically, fill in non-data areas).
|
||||
|
||||
//
|
||||
// header delay, 22 words
|
||||
@@ -362,6 +377,6 @@ namespace Contralto.IO
|
||||
|
||||
|
||||
// The pack loaded into the drive
|
||||
DiabloPack _pack;
|
||||
IDiskPack _pack;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,230 +0,0 @@
|
||||
/*
|
||||
This file is part of ContrAlto.
|
||||
|
||||
ContrAlto is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ContrAlto is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with ContrAlto. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Contralto.IO
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// DiskGeometry encapsulates the geometry of a disk.
|
||||
/// </summary>
|
||||
public struct DiskGeometry
|
||||
{
|
||||
public DiskGeometry(int cylinders, int tracks, int sectors)
|
||||
{
|
||||
Cylinders = cylinders;
|
||||
Tracks = tracks;
|
||||
Sectors = sectors;
|
||||
}
|
||||
|
||||
public int Cylinders;
|
||||
public int Tracks;
|
||||
public int Sectors;
|
||||
}
|
||||
|
||||
public enum DiabloDiskType
|
||||
{
|
||||
Diablo31,
|
||||
Diablo44
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DiabloDiskSector encapsulates the records contained in a single Alto disk sector
|
||||
/// on a Diablo disk. This includes the header, label, and data records.
|
||||
/// </summary>
|
||||
public class DiabloDiskSector
|
||||
{
|
||||
public DiabloDiskSector(byte[] header, byte[] label, byte[] data)
|
||||
{
|
||||
if (header.Length != 4 ||
|
||||
label.Length != 16 ||
|
||||
data.Length != 512)
|
||||
{
|
||||
throw new InvalidOperationException("Invalid sector header/label/data length.");
|
||||
}
|
||||
|
||||
Header = GetUShortArray(header);
|
||||
Label = GetUShortArray(label);
|
||||
Data = GetUShortArray(data);
|
||||
}
|
||||
|
||||
private ushort[] GetUShortArray(byte[] data)
|
||||
{
|
||||
if ((data.Length % 2) != 0)
|
||||
{
|
||||
throw new InvalidOperationException("Array length must be even.");
|
||||
}
|
||||
|
||||
ushort[] array = new ushort[data.Length / 2];
|
||||
|
||||
int offset = 0;
|
||||
for(int i=0;i<array.Length;i++)
|
||||
{
|
||||
array[i] = (ushort)((data[offset]) | (data[offset + 1] << 8));
|
||||
offset += 2;
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
public ushort[] Header;
|
||||
public ushort[] Label;
|
||||
public ushort[] Data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encapsulates disk image data for all disk packs used with the
|
||||
/// standard Alto Disk Controller (i.e. the 31 and 44, which differ
|
||||
/// only in the number of cylinders)
|
||||
/// </summary>
|
||||
public class DiabloPack
|
||||
{
|
||||
public DiabloPack(DiabloDiskType type)
|
||||
{
|
||||
_diskType = type;
|
||||
_packName = null;
|
||||
_geometry = new DiskGeometry(type == DiabloDiskType.Diablo31 ? 203 : 406, 2, 12);
|
||||
_sectors = new DiabloDiskSector[_geometry.Cylinders, _geometry.Tracks, _geometry.Sectors];
|
||||
}
|
||||
|
||||
public DiskGeometry Geometry
|
||||
{
|
||||
get { return _geometry; }
|
||||
}
|
||||
|
||||
public string PackName
|
||||
{
|
||||
get { return _packName; }
|
||||
}
|
||||
|
||||
public void Load(Stream imageStream, string path, bool reverseByteOrder)
|
||||
{
|
||||
_packName = path;
|
||||
for(int cylinder = 0; cylinder < _geometry.Cylinders; cylinder++)
|
||||
{
|
||||
for(int track = 0; track < _geometry.Tracks; track++)
|
||||
{
|
||||
for(int sector = 0; sector < _geometry.Sectors; sector++)
|
||||
{
|
||||
byte[] header = new byte[4]; // 2 words
|
||||
byte[] label = new byte[16]; // 8 words
|
||||
byte[] data = new byte[512]; // 256 words
|
||||
|
||||
//
|
||||
// Bitsavers images have an extra word in the header for some reason.
|
||||
// ignore it.
|
||||
// TODO: should support different formats ("correct" raw, Alto CopyDisk format, etc.)
|
||||
//
|
||||
imageStream.Seek(2, SeekOrigin.Current);
|
||||
|
||||
|
||||
if (imageStream.Read(header, 0, header.Length) != header.Length)
|
||||
{
|
||||
throw new InvalidOperationException("Short read while reading sector header.");
|
||||
}
|
||||
|
||||
if (imageStream.Read(label, 0, label.Length) != label.Length)
|
||||
{
|
||||
throw new InvalidOperationException("Short read while reading sector label.");
|
||||
}
|
||||
|
||||
if (imageStream.Read(data, 0, data.Length) != data.Length)
|
||||
{
|
||||
throw new InvalidOperationException("Short read while reading sector data.");
|
||||
}
|
||||
|
||||
if (reverseByteOrder)
|
||||
{
|
||||
SwapBytes(header);
|
||||
SwapBytes(label);
|
||||
SwapBytes(data);
|
||||
}
|
||||
|
||||
_sectors[cylinder, track, sector] = new DiabloDiskSector(header, label, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (imageStream.Position != imageStream.Length)
|
||||
{
|
||||
throw new InvalidOperationException("Extra data at end of image file.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Save(Stream imageStream)
|
||||
{
|
||||
for (int cylinder = 0; cylinder < _geometry.Cylinders; cylinder++)
|
||||
{
|
||||
for (int track = 0; track < _geometry.Tracks; track++)
|
||||
{
|
||||
for (int sector = 0; sector < _geometry.Sectors; sector++)
|
||||
{
|
||||
byte[] header = new byte[4]; // 2 words
|
||||
byte[] label = new byte[16]; // 8 words
|
||||
byte[] data = new byte[512]; // 256 words
|
||||
|
||||
//
|
||||
// Bitsavers images have an extra word in the header for some reason.
|
||||
// We will follow this 'standard' when writing out.
|
||||
// TODO: should support different formats ("correct" raw, Alto CopyDisk format, etc.)
|
||||
//
|
||||
byte[] dummy = new byte[2];
|
||||
imageStream.Write(dummy, 0, 2);
|
||||
|
||||
DiabloDiskSector s = _sectors[cylinder, track, sector];
|
||||
|
||||
WriteWordBuffer(imageStream, s.Header);
|
||||
WriteWordBuffer(imageStream, s.Label);
|
||||
WriteWordBuffer(imageStream, s.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DiabloDiskSector GetSector(int cylinder, int track, int sector)
|
||||
{
|
||||
return _sectors[cylinder, track, sector];
|
||||
}
|
||||
|
||||
private void WriteWordBuffer(Stream imageStream, ushort[] buffer)
|
||||
{
|
||||
// TODO: this is beyond inefficient
|
||||
for(int i=0;i<buffer.Length;i++)
|
||||
{
|
||||
imageStream.WriteByte((byte)buffer[i]);
|
||||
imageStream.WriteByte((byte)(buffer[i] >> 8));
|
||||
}
|
||||
}
|
||||
|
||||
private void SwapBytes(byte[] data)
|
||||
{
|
||||
for(int i=0;i<data.Length;i+=2)
|
||||
{
|
||||
byte t = data[i];
|
||||
data[i] = data[i + 1];
|
||||
data[i + 1] = t;
|
||||
}
|
||||
}
|
||||
|
||||
private string _packName; // The file from whence the data came
|
||||
private DiabloDiskSector[,,] _sectors;
|
||||
private DiabloDiskType _diskType;
|
||||
private DiskGeometry _geometry;
|
||||
}
|
||||
}
|
||||
@@ -230,7 +230,24 @@ namespace Contralto.IO
|
||||
public DiskActivityType LastDiskActivity
|
||||
{
|
||||
get { return _lastDiskActivity; }
|
||||
}
|
||||
}
|
||||
|
||||
public void CommitDisk(int driveId)
|
||||
{
|
||||
DiabloDrive drive = _drives[driveId];
|
||||
if (drive.IsLoaded)
|
||||
{
|
||||
try
|
||||
{
|
||||
drive.Pack.Save();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// TODO: this does not really belong here.
|
||||
System.Windows.Forms.MessageBox.Show(String.Format("Unable to save Diablo disk {0}'s contents. Error {0}. Any changes have been lost.", e.Message), "Disk save error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
@@ -322,7 +339,7 @@ namespace Contralto.IO
|
||||
Log.Write(LogType.Verbose, LogComponent.DiskController, "KDATA is {0}", Conversion.ToOctal(_kDataWrite));
|
||||
_system.CPU.WakeupTask(CPU.TaskType.DiskSector);
|
||||
|
||||
// Reset SECLATE
|
||||
// Reset SECLATE
|
||||
_seclate = false;
|
||||
_seclateEnable = true;
|
||||
_kStat &= (ushort)~SECLATE;
|
||||
|
||||
615
Contralto/IO/DiskPack.cs
Normal file
615
Contralto/IO/DiskPack.cs
Normal file
@@ -0,0 +1,615 @@
|
||||
/*
|
||||
This file is part of ContrAlto.
|
||||
|
||||
ContrAlto is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ContrAlto is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with ContrAlto. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Contralto.IO
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// DiskGeometry encapsulates the geometry of a disk.
|
||||
/// </summary>
|
||||
public struct DiskGeometry
|
||||
{
|
||||
public DiskGeometry(int cylinders, int heads, int sectors, SectorGeometry sectorGeometry)
|
||||
{
|
||||
Cylinders = cylinders;
|
||||
Heads = heads;
|
||||
Sectors = sectors;
|
||||
SectorGeometry = sectorGeometry;
|
||||
}
|
||||
|
||||
public int Cylinders;
|
||||
public int Heads;
|
||||
public int Sectors;
|
||||
public SectorGeometry SectorGeometry;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the total size (in bytes) of a disk with this geometry.
|
||||
/// This includes the extra word-per-sector of the Bitsavers images.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int GetDiskSizeBytes()
|
||||
{
|
||||
return SectorGeometry.GetSectorSizeBytes() * (Sectors * Heads * Cylinders);
|
||||
}
|
||||
|
||||
//
|
||||
// Standard Diablo geometries
|
||||
//
|
||||
public static readonly DiskGeometry Diablo31 = new DiskGeometry(203, 2, 12, SectorGeometry.Diablo);
|
||||
public static readonly DiskGeometry Diablo44 = new DiskGeometry(406, 2, 12, SectorGeometry.Diablo);
|
||||
|
||||
//
|
||||
// Standard Trident geometries
|
||||
//
|
||||
public static readonly DiskGeometry TridentT80 = new DiskGeometry(815, 5, 9, SectorGeometry.Trident);
|
||||
public static readonly DiskGeometry TridentT300 = new DiskGeometry(815, 19, 9, SectorGeometry.Trident);
|
||||
public static readonly DiskGeometry Shugart4004 = new DiskGeometry(202, 4, 8, SectorGeometry.Trident);
|
||||
public static readonly DiskGeometry Shugart4008 = new DiskGeometry(202, 8, 8, SectorGeometry.Trident);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes the geometry of an Alto disk sector in terms of the
|
||||
/// size of the header, label, and data blocks.
|
||||
/// </summary>
|
||||
public struct SectorGeometry
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the layout of a sector on an Alto pack. Sizes are in bytes.
|
||||
/// </summary>
|
||||
public SectorGeometry(int headerSizeBytes, int labelSizeBytes, int dataSizeBytes)
|
||||
{
|
||||
HeaderSizeBytes = headerSizeBytes;
|
||||
LabelSizeBytes = labelSizeBytes;
|
||||
DataSizeBytes = dataSizeBytes;
|
||||
|
||||
HeaderSize = HeaderSizeBytes / 2;
|
||||
LabelSize = LabelSizeBytes / 2;
|
||||
DataSize = DataSizeBytes / 2;
|
||||
}
|
||||
|
||||
public int HeaderSize;
|
||||
public int LabelSize;
|
||||
public int DataSize;
|
||||
|
||||
public int HeaderSizeBytes;
|
||||
public int LabelSizeBytes;
|
||||
public int DataSizeBytes;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the total size (in bytes) of a sector with this geometry.
|
||||
/// This includes the extra word-per-sector of the Bitsavers images.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int GetSectorSizeBytes()
|
||||
{
|
||||
return DataSizeBytes +
|
||||
LabelSizeBytes +
|
||||
HeaderSizeBytes +
|
||||
2; // Extra dummy word
|
||||
}
|
||||
|
||||
public static readonly SectorGeometry Diablo = new SectorGeometry(4, 16, 512);
|
||||
public static readonly SectorGeometry Trident = new SectorGeometry(4, 20, 2048);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DiskSector encapsulates the records contained in a single Alto disk sector
|
||||
/// on a disk. This includes the header, label, and data records.
|
||||
/// </summary>
|
||||
public class DiskSector
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a new DiskSector populated from the specified stream.
|
||||
/// </summary>
|
||||
/// <param name="geometry"></param>
|
||||
/// <param name="inputStream"></param>
|
||||
/// <param name="cylinder"></param>
|
||||
/// <param name="head"></param>
|
||||
/// <param name="sector"></param>
|
||||
public DiskSector(SectorGeometry geometry, Stream inputStream, int cylinder, int head, int sector)
|
||||
{
|
||||
//
|
||||
// Read in the sector from the input stream.
|
||||
//
|
||||
byte[] header = new byte[geometry.HeaderSizeBytes];
|
||||
byte[] label = new byte[geometry.LabelSizeBytes];
|
||||
byte[] data = new byte[geometry.DataSizeBytes];
|
||||
|
||||
//
|
||||
// Bitsavers images have an extra word in the header for some reason.
|
||||
// ignore it.
|
||||
// TODO: should support different formats ("correct" raw, Alto CopyDisk format, etc.)
|
||||
//
|
||||
inputStream.Seek(2, SeekOrigin.Current);
|
||||
|
||||
if (inputStream.Read(header, 0, header.Length) != header.Length)
|
||||
{
|
||||
throw new InvalidOperationException("Short read while reading sector header.");
|
||||
}
|
||||
|
||||
if (inputStream.Read(label, 0, label.Length) != label.Length)
|
||||
{
|
||||
throw new InvalidOperationException("Short read while reading sector label.");
|
||||
}
|
||||
|
||||
if (inputStream.Read(data, 0, data.Length) != data.Length)
|
||||
{
|
||||
throw new InvalidOperationException("Short read while reading sector data.");
|
||||
}
|
||||
|
||||
_header = GetUShortArray(header);
|
||||
_label = GetUShortArray(label);
|
||||
_data = GetUShortArray(data);
|
||||
|
||||
_cylinder = cylinder;
|
||||
_head = head;
|
||||
_sector = sector;
|
||||
|
||||
_modified = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new, empty sector.
|
||||
/// </summary>
|
||||
/// <param name="geometry"></param>
|
||||
/// <param name="cylinder"></param>
|
||||
/// <param name="head"></param>
|
||||
/// <param name="sector"></param>
|
||||
public DiskSector(SectorGeometry geometry, int cylinder, int head, int sector)
|
||||
{
|
||||
_header = new ushort[geometry.HeaderSize];
|
||||
_label = new ushort[geometry.LabelSize];
|
||||
_data = new ushort[geometry.DataSize];
|
||||
|
||||
_cylinder = cylinder;
|
||||
_head = head;
|
||||
_sector = sector;
|
||||
|
||||
_modified = true;
|
||||
}
|
||||
|
||||
public int Cylinder
|
||||
{
|
||||
get { return _cylinder; }
|
||||
}
|
||||
|
||||
public int Head
|
||||
{
|
||||
get { return _head; }
|
||||
}
|
||||
|
||||
public int Sector
|
||||
{
|
||||
get { return _sector; }
|
||||
}
|
||||
|
||||
public bool Modified
|
||||
{
|
||||
get { return _modified; }
|
||||
}
|
||||
|
||||
public ushort ReadHeader(int offset)
|
||||
{
|
||||
return _header[offset];
|
||||
}
|
||||
|
||||
public void WriteHeader(int offset, ushort value)
|
||||
{
|
||||
_header[offset] = value;
|
||||
_modified = true;
|
||||
}
|
||||
|
||||
public ushort ReadLabel(int offset)
|
||||
{
|
||||
return _label[offset];
|
||||
}
|
||||
|
||||
public void WriteLabel(int offset, ushort value)
|
||||
{
|
||||
_label[offset] = value;
|
||||
_modified = true;
|
||||
}
|
||||
|
||||
public ushort ReadData(int offset)
|
||||
{
|
||||
return _data[offset];
|
||||
}
|
||||
|
||||
public void WriteData(int offset, ushort value)
|
||||
{
|
||||
_data[offset] = value;
|
||||
_modified = true;
|
||||
}
|
||||
|
||||
public void WriteToStream(Stream s)
|
||||
{
|
||||
//
|
||||
// Bitsavers images have an extra word in the header for some reason.
|
||||
// We will follow this standard when writing out.
|
||||
// TODO: should support different formats ("correct" raw, Alto CopyDisk format, etc.)
|
||||
//
|
||||
byte[] dummy = new byte[2];
|
||||
s.Write(dummy, 0, 2);
|
||||
|
||||
WriteWordBuffer(s, _header);
|
||||
WriteWordBuffer(s, _label);
|
||||
WriteWordBuffer(s, _data);
|
||||
|
||||
_modified = false;
|
||||
}
|
||||
|
||||
private ushort[] GetUShortArray(byte[] data)
|
||||
{
|
||||
if ((data.Length % 2) != 0)
|
||||
{
|
||||
throw new InvalidOperationException("Array length must be even.");
|
||||
}
|
||||
|
||||
ushort[] array = new ushort[data.Length / 2];
|
||||
|
||||
int offset = 0;
|
||||
for(int i=0;i<array.Length;i++)
|
||||
{
|
||||
array[i] = (ushort)((data[offset]) | (data[offset + 1] << 8));
|
||||
offset += 2;
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
private void WriteWordBuffer(Stream imageStream, ushort[] buffer)
|
||||
{
|
||||
// TODO: this is beyond inefficient
|
||||
for (int i = 0; i < buffer.Length; i++)
|
||||
{
|
||||
imageStream.WriteByte((byte)buffer[i]);
|
||||
imageStream.WriteByte((byte)(buffer[i] >> 8));
|
||||
}
|
||||
}
|
||||
|
||||
private ushort[] _header;
|
||||
private ushort[] _label;
|
||||
private ushort[] _data;
|
||||
|
||||
private int _cylinder;
|
||||
private int _head;
|
||||
private int _sector;
|
||||
|
||||
private bool _modified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The IDiskPack interface defines a generic mechanism for creating, loading, storing,
|
||||
/// and accessing the sectors of a disk pack.
|
||||
/// </summary>
|
||||
public interface IDiskPack : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The geometry of this pack.
|
||||
/// </summary>
|
||||
DiskGeometry Geometry { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The filename of this pack.
|
||||
/// </summary>
|
||||
string PackName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Commits the current in-memory image back to the file it came from.
|
||||
/// </summary>
|
||||
void Save();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the specified sector from storage.
|
||||
/// </summary>
|
||||
/// <param name="cylinder"></param>
|
||||
/// <param name="head"></param>
|
||||
/// <param name="sector"></param>
|
||||
/// <returns></returns>
|
||||
DiskSector GetSector(int cylinder, int head, int sector);
|
||||
|
||||
/// <summary>
|
||||
/// Commits this sector back to storage.
|
||||
/// </summary>
|
||||
/// <param name="sector"></param>
|
||||
void CommitSector(DiskSector sector);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// In-memory implementation of IDiskPack. Useful for small disks (e.g. Diablo).
|
||||
/// Changes to the in-memory copy are only committed back to the disk image
|
||||
/// when Save is invoked.
|
||||
/// </summary>
|
||||
public class InMemoryDiskPack : IDiskPack
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new, empty disk pack with the specified geometry.
|
||||
/// </summary>
|
||||
/// <param name="geometry"></param>
|
||||
/// <param name="path"></param>
|
||||
public static InMemoryDiskPack CreateEmpty(DiskGeometry geometry, string path)
|
||||
{
|
||||
return new InMemoryDiskPack(geometry, path, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads an existing disk pack image.
|
||||
/// </summary>
|
||||
/// <param name="geometry"></param>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
public static InMemoryDiskPack Load(DiskGeometry geometry, string path)
|
||||
{
|
||||
return new InMemoryDiskPack(geometry, path, true);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
//
|
||||
// Nothing to do here.
|
||||
//
|
||||
}
|
||||
|
||||
private InMemoryDiskPack(DiskGeometry geometry, string path, bool load)
|
||||
{
|
||||
_packName = path;
|
||||
_geometry = geometry;
|
||||
_sectors = new DiskSector[_geometry.Cylinders, _geometry.Heads, _geometry.Sectors];
|
||||
|
||||
if (load)
|
||||
{
|
||||
//
|
||||
// Attempt to load in the specified image file.
|
||||
//
|
||||
using (FileStream imageStream = new FileStream(_packName, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
try
|
||||
{
|
||||
for (int cylinder = 0; cylinder < _geometry.Cylinders; cylinder++)
|
||||
{
|
||||
for (int head = 0; head < _geometry.Heads; head++)
|
||||
{
|
||||
for (int sector = 0; sector < _geometry.Sectors; sector++)
|
||||
{
|
||||
_sectors[cylinder, head, sector] = new DiskSector(_geometry.SectorGeometry, imageStream, cylinder, head, sector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (imageStream.Position != imageStream.Length)
|
||||
{
|
||||
throw new InvalidOperationException("Extra data at end of image file.");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
imageStream.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// Just initialize a new, empty disk.
|
||||
//
|
||||
for (int cylinder = 0; cylinder < _geometry.Cylinders; cylinder++)
|
||||
{
|
||||
for (int head = 0; head < _geometry.Heads; head++)
|
||||
{
|
||||
for (int sector = 0; sector < _geometry.Sectors; sector++)
|
||||
{
|
||||
_sectors[cylinder, head, sector] = new DiskSector(_geometry.SectorGeometry, cylinder, head, sector);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DiskGeometry Geometry
|
||||
{
|
||||
get { return _geometry; }
|
||||
}
|
||||
|
||||
public string PackName
|
||||
{
|
||||
get { return _packName; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Commits the current in-memory image back to the file from which it was loaded.
|
||||
/// </summary>
|
||||
public void Save()
|
||||
{
|
||||
using (FileStream imageStream = new FileStream(_packName, FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
try
|
||||
{
|
||||
for (int cylinder = 0; cylinder < _geometry.Cylinders; cylinder++)
|
||||
{
|
||||
for (int head = 0; head < _geometry.Heads; head++)
|
||||
{
|
||||
for (int sector = 0; sector < _geometry.Sectors; sector++)
|
||||
{
|
||||
_sectors[cylinder, head, sector].WriteToStream(imageStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
imageStream.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DiskSector GetSector(int cylinder, int head, int sector)
|
||||
{
|
||||
//
|
||||
// Return the in-memory sector reference.
|
||||
//
|
||||
return _sectors[cylinder, head, sector];
|
||||
}
|
||||
|
||||
public void CommitSector(DiskSector sector)
|
||||
{
|
||||
//
|
||||
// Update the in-memory sector reference to point to this (possibly new) sector object.
|
||||
//
|
||||
if (sector.Modified)
|
||||
{
|
||||
_sectors[sector.Cylinder, sector.Head, sector.Sector] = sector;
|
||||
}
|
||||
}
|
||||
|
||||
private string _packName; // The file from whence the data came
|
||||
private DiskSector[,,] _sectors; // All of the sectors on disk
|
||||
private DiskGeometry _geometry; // The geometry of this disk.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// FileBackedDiskPack provides an implementation of IDiskPack where sectors are read into memory
|
||||
/// only when requested, and changes are flushed to disk when use of the sector is complete.
|
||||
/// </summary>
|
||||
public class FileBackedDiskPack : IDiskPack
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new, empty disk pack with the specified geometry.
|
||||
/// </summary>
|
||||
/// <param name="geometry"></param>
|
||||
/// <param name="path"></param>
|
||||
public static FileBackedDiskPack CreateEmpty(DiskGeometry geometry, string path)
|
||||
{
|
||||
return new FileBackedDiskPack(geometry, path, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads an existing image.
|
||||
/// </summary>
|
||||
/// <param name="geometry"></param>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
public static FileBackedDiskPack Load(DiskGeometry geometry, string path)
|
||||
{
|
||||
return new FileBackedDiskPack(geometry, path, true);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_diskStream != null)
|
||||
{
|
||||
_diskStream.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private FileBackedDiskPack(DiskGeometry geometry, string path, bool load)
|
||||
{
|
||||
_packName = path;
|
||||
_geometry = geometry;
|
||||
|
||||
if (load)
|
||||
{
|
||||
//
|
||||
// Attempt to open an existing stream for read/write access.
|
||||
//
|
||||
_diskStream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite);
|
||||
|
||||
//
|
||||
// Quick sanity check that the disk image is the right size.
|
||||
//
|
||||
if (_diskStream.Length != geometry.GetDiskSizeBytes())
|
||||
{
|
||||
_diskStream.Close();
|
||||
_diskStream = null;
|
||||
throw new InvalidOperationException("Image size is invalid.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// Attempt to initialize a new stream with read/write access.
|
||||
//
|
||||
_diskStream = new FileStream(path, FileMode.Create, FileAccess.ReadWrite);
|
||||
|
||||
//
|
||||
// And set the size of the stream.
|
||||
//
|
||||
_diskStream.SetLength(geometry.GetDiskSizeBytes());
|
||||
}
|
||||
}
|
||||
|
||||
public DiskGeometry Geometry
|
||||
{
|
||||
get { return _geometry; }
|
||||
}
|
||||
|
||||
public string PackName
|
||||
{
|
||||
get { return _packName; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Commits pending changes back to disc.
|
||||
/// </summary>
|
||||
/// <param name="imageStream"></param>
|
||||
public void Save()
|
||||
{
|
||||
// Nothing here, we expect CommitSector
|
||||
// to be called by whoever has a sector checked out before shutdown.
|
||||
}
|
||||
|
||||
public DiskSector GetSector(int cylinder, int head, int sector)
|
||||
{
|
||||
//
|
||||
// Retrieve the appropriate sector from disk.
|
||||
// Seek to the appropriate position and read.
|
||||
//
|
||||
_diskStream.Position = GetOffsetForSector(cylinder, head, sector);
|
||||
|
||||
return new DiskSector(_geometry.SectorGeometry, _diskStream, cylinder, head, sector);
|
||||
}
|
||||
|
||||
public void CommitSector(DiskSector sector)
|
||||
{
|
||||
if (sector.Modified)
|
||||
{
|
||||
//
|
||||
// Commit this data back to disk.
|
||||
// Seek to the appropriate position and flush.
|
||||
//
|
||||
_diskStream.Position = GetOffsetForSector(sector.Cylinder, sector.Head, sector.Sector);
|
||||
sector.WriteToStream(_diskStream);
|
||||
}
|
||||
}
|
||||
|
||||
private long GetOffsetForSector(int cylinder, int head, int sector)
|
||||
{
|
||||
int sectorNumber = (cylinder * _geometry.Heads * _geometry.Sectors) +
|
||||
(head * _geometry.Sectors) +
|
||||
sector;
|
||||
|
||||
return sectorNumber * _geometry.SectorGeometry.GetSectorSizeBytes();
|
||||
}
|
||||
|
||||
private string _packName; // The file from whence the data came
|
||||
private FileStream _diskStream; // The disk image stream containing this disk's contents.
|
||||
private DiskGeometry _geometry; // The geometry of this disk.
|
||||
}
|
||||
}
|
||||
1123
Contralto/IO/TridentController.cs
Normal file
1123
Contralto/IO/TridentController.cs
Normal file
File diff suppressed because it is too large
Load Diff
284
Contralto/IO/TridentDrive.cs
Normal file
284
Contralto/IO/TridentDrive.cs
Normal file
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
This file is part of ContrAlto.
|
||||
|
||||
ContrAlto is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ContrAlto is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with ContrAlto. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using Contralto.Logging;
|
||||
using System;
|
||||
|
||||
namespace Contralto.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// Encapsulates logic that belongs to a Trident drive, including loading/saving packs,
|
||||
/// seeking, and parceling out sector data.
|
||||
/// </summary>
|
||||
public class TridentDrive
|
||||
{
|
||||
public TridentDrive(AltoSystem system)
|
||||
{
|
||||
_system = system;
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_sector = 0;
|
||||
_cylinder = 0;
|
||||
_head = 0;
|
||||
|
||||
UpdateTrackCache();
|
||||
}
|
||||
|
||||
public void NewPack(string path, DiskGeometry geometry)
|
||||
{
|
||||
if (_pack != null)
|
||||
{
|
||||
UpdateTrackCache();
|
||||
_pack.Dispose();
|
||||
}
|
||||
|
||||
_pack = FileBackedDiskPack.CreateEmpty(geometry, path);
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void LoadPack(IDiskPack pack)
|
||||
{
|
||||
if (_pack != null)
|
||||
{
|
||||
UpdateTrackCache();
|
||||
_pack.Dispose();
|
||||
}
|
||||
|
||||
_pack = pack;
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void UnloadPack()
|
||||
{
|
||||
if (_pack != null)
|
||||
{
|
||||
UpdateTrackCache();
|
||||
_pack.Dispose();
|
||||
}
|
||||
|
||||
_pack = null;
|
||||
Reset();
|
||||
}
|
||||
|
||||
public bool IsLoaded
|
||||
{
|
||||
get { return _pack != null; }
|
||||
}
|
||||
|
||||
public IDiskPack Pack
|
||||
{
|
||||
get { return _pack; }
|
||||
}
|
||||
|
||||
public int Sector
|
||||
{
|
||||
get { return _sector; }
|
||||
set { _sector = value; }
|
||||
}
|
||||
|
||||
public int Head
|
||||
{
|
||||
get { return _head; }
|
||||
set
|
||||
{
|
||||
if (_head != value)
|
||||
{
|
||||
_head = value;
|
||||
UpdateTrackCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Cylinder
|
||||
{
|
||||
get { return _cylinder; }
|
||||
set
|
||||
{
|
||||
if (_cylinder != value)
|
||||
{
|
||||
_cylinder = value;
|
||||
UpdateTrackCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ReadOnly
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public ushort ReadHeader(int wordOffset)
|
||||
{
|
||||
if (wordOffset >= SectorGeometry.Trident.HeaderSize)
|
||||
{
|
||||
//
|
||||
// We just ignore this; the microcode may read extra words
|
||||
// and the controller was expected to ignore them.
|
||||
//
|
||||
Log.Write(LogType.Warning, LogComponent.TridentDisk, "Extra header word read, offset {0}", wordOffset);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CurrentSector.ReadHeader(wordOffset);
|
||||
}
|
||||
}
|
||||
|
||||
public ushort ReadLabel(int wordOffset)
|
||||
{
|
||||
if (wordOffset >= SectorGeometry.Trident.LabelSize)
|
||||
{
|
||||
//
|
||||
// We just ignore this; the microcode may read extra words
|
||||
// and the controller was expected to ignore them.
|
||||
//
|
||||
Log.Write(LogType.Warning, LogComponent.TridentDisk, "Extra label word read, offset {0}", wordOffset);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CurrentSector.ReadLabel(wordOffset);
|
||||
}
|
||||
}
|
||||
|
||||
public ushort ReadData(int wordOffset)
|
||||
{
|
||||
if (wordOffset >= SectorGeometry.Trident.DataSize)
|
||||
{
|
||||
//
|
||||
// We just ignore this; the microcode may read extra words
|
||||
// and the controller was expected to ignore them.
|
||||
//
|
||||
Log.Write(LogType.Warning, LogComponent.TridentDisk, "Extra data word read, offset {0 }", wordOffset);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CurrentSector.ReadData(wordOffset);
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteHeader(int wordOffset, ushort word)
|
||||
{
|
||||
if (wordOffset >= SectorGeometry.Trident.HeaderSize)
|
||||
{
|
||||
//
|
||||
// We just ignore this; the microcode may send extra words
|
||||
// and the controller was expected to ignore them.
|
||||
//
|
||||
Log.Write(LogType.Warning, LogComponent.TridentDisk, "Extra header word ({0}) written, offset {1}", Conversion.ToOctal(word), wordOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentSector.WriteHeader(wordOffset, word);
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteLabel(int wordOffset, ushort word)
|
||||
{
|
||||
if (wordOffset >= SectorGeometry.Trident.LabelSize)
|
||||
{
|
||||
//
|
||||
// We just ignore this; the microcode may send extra words
|
||||
// and the controller was expected to ignore them.
|
||||
//
|
||||
Log.Write(LogType.Warning, LogComponent.TridentDisk, "Extra label word ({0}) written, offset {1}", Conversion.ToOctal(word), wordOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentSector.WriteLabel(wordOffset, word);
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteData(int wordOffset, ushort word)
|
||||
{
|
||||
if (wordOffset >= SectorGeometry.Trident.DataSize)
|
||||
{
|
||||
//
|
||||
// We just ignore this; the microcode may send extra words
|
||||
// and the controller was expected to ignore them.
|
||||
//
|
||||
Log.Write(LogType.Warning, LogComponent.TridentDisk, "Extra data word ({0}) written, offset {1}", Conversion.ToOctal(word), wordOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentSector.WriteData(wordOffset, word);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Sector management. We load in an entire track's worth of sectors at a time.
|
||||
// When the head or cylinder changes, UpdateTrackCache must be called.
|
||||
//
|
||||
private void UpdateTrackCache()
|
||||
{
|
||||
if (_pack != null)
|
||||
{
|
||||
if (_trackCache == null)
|
||||
{
|
||||
//
|
||||
// First time through, initialize the cache.
|
||||
//
|
||||
_trackCache = new DiskSector[_pack.Geometry.Sectors];
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// Commit the sectors back to disk before loading in the new ones.
|
||||
//
|
||||
for (int i = 0; i < _trackCache.Length; i++)
|
||||
{
|
||||
_pack.CommitSector(_trackCache[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Load the new sectors for this track.
|
||||
//
|
||||
for (int i = 0; i < _trackCache.Length; i++)
|
||||
{
|
||||
_trackCache[i] = _pack.GetSector(_cylinder, _head, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DiskSector CurrentSector
|
||||
{
|
||||
get { return _trackCache[_sector]; }
|
||||
}
|
||||
|
||||
private AltoSystem _system;
|
||||
|
||||
//
|
||||
// Current disk position
|
||||
//
|
||||
private int _cylinder;
|
||||
private int _head;
|
||||
private int _sector;
|
||||
|
||||
// The pack loaded into the drive
|
||||
IDiskPack _pack;
|
||||
|
||||
//
|
||||
// The track cache
|
||||
//
|
||||
private DiskSector[] _trackCache;
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,9 @@ namespace Contralto.Logging
|
||||
Organ = 0x20000,
|
||||
Orbit = 0x40000,
|
||||
DoverROS = 0x80000,
|
||||
TridentTask = 0x100000,
|
||||
TridentController = 0x200000,
|
||||
TridentDisk = 0x400000,
|
||||
|
||||
Debug = 0x40000000,
|
||||
All = 0x7fffffff
|
||||
@@ -77,7 +80,7 @@ namespace Contralto.Logging
|
||||
{
|
||||
_components = Configuration.LogComponents;
|
||||
_type = Configuration.LogTypes;
|
||||
|
||||
_logIndex = 0;
|
||||
}
|
||||
|
||||
public static LogComponent LogComponents
|
||||
@@ -107,7 +110,8 @@ namespace Contralto.Logging
|
||||
//
|
||||
// My log has something to tell you...
|
||||
// TODO: color based on type, etc.
|
||||
Console.WriteLine(component.ToString() + ": " + message, args);
|
||||
Console.WriteLine(_logIndex.ToString() + ": " + component.ToString() + ": " + message, args);
|
||||
_logIndex++;
|
||||
}
|
||||
}
|
||||
#else
|
||||
@@ -125,5 +129,6 @@ namespace Contralto.Logging
|
||||
|
||||
private static LogComponent _components;
|
||||
private static LogType _type;
|
||||
private static long _logIndex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,16 +42,17 @@ namespace Contralto
|
||||
|
||||
_system = new AltoSystem();
|
||||
|
||||
// Load disks specified by configuration
|
||||
if (!String.IsNullOrEmpty(Configuration.Drive0Image))
|
||||
{
|
||||
try
|
||||
{
|
||||
_system.LoadDrive(0, Configuration.Drive0Image);
|
||||
_system.LoadDiabloDrive(0, Configuration.Drive0Image, false);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Console.WriteLine("Could not load image '{0}' for drive 0. Error '{1}'.", Configuration.Drive0Image, e.Message);
|
||||
_system.UnloadDrive(0);
|
||||
Console.WriteLine("Could not load image '{0}' for Diablo drive 0. Error '{1}'.", Configuration.Drive0Image, e.Message);
|
||||
_system.UnloadDiabloDrive(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,12 +60,32 @@ namespace Contralto
|
||||
{
|
||||
try
|
||||
{
|
||||
_system.LoadDrive(1, Configuration.Drive1Image);
|
||||
_system.LoadDiabloDrive(1, Configuration.Drive1Image, false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine("Could not load image '{0}' for drive 1. Error '{1}'.", Configuration.Drive1Image, e.Message);
|
||||
_system.UnloadDrive(1);
|
||||
Console.WriteLine("Could not load image '{0}' for Diablo drive 1. Error '{1}'.", Configuration.Drive1Image, e.Message);
|
||||
_system.UnloadDiabloDrive(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (Configuration.TridentImages != null)
|
||||
{
|
||||
for (int i = 0; i < Math.Min(8, Configuration.TridentImages.Count); i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!String.IsNullOrWhiteSpace(Configuration.TridentImages[i]))
|
||||
{
|
||||
_system.LoadTridentDrive(i, Configuration.TridentImages[i], false);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine("Could not load image '{0}' for Trident drive {1}. Error '{2}'.", Configuration.TridentImages[i], i, e.Message);
|
||||
_system.UnloadTridentDrive(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,13 +127,7 @@ namespace Contralto
|
||||
private static void OnProcessExit(object sender, EventArgs e)
|
||||
{
|
||||
Console.WriteLine("Exiting...");
|
||||
|
||||
//
|
||||
// Save disk contents
|
||||
//
|
||||
_system.CommitDiskPack(0);
|
||||
_system.CommitDiskPack(1);
|
||||
|
||||
|
||||
_system.Shutdown();
|
||||
|
||||
//
|
||||
|
||||
23
Contralto/Properties/Settings.Designer.cs
generated
23
Contralto/Properties/Settings.Designer.cs
generated
@@ -238,5 +238,28 @@ namespace Contralto.Properties {
|
||||
this["ReversePageOrder"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute(@"<?xml version=""1.0"" encoding=""utf-16""?>
|
||||
<ArrayOfString xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
|
||||
<string />
|
||||
<string />
|
||||
<string />
|
||||
<string />
|
||||
<string />
|
||||
<string />
|
||||
<string />
|
||||
<string />
|
||||
<string />
|
||||
</ArrayOfString>")]
|
||||
public global::System.Collections.Specialized.StringCollection TridentImages {
|
||||
get {
|
||||
return ((global::System.Collections.Specialized.StringCollection)(this["TridentImages"]));
|
||||
}
|
||||
set {
|
||||
this["TridentImages"] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,5 +56,19 @@
|
||||
<Setting Name="ReversePageOrder" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">True</Value>
|
||||
</Setting>
|
||||
<Setting Name="TridentImages" Type="System.Collections.Specialized.StringCollection" Scope="User">
|
||||
<Value Profile="(Default)"><?xml version="1.0" encoding="utf-16"?>
|
||||
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<string />
|
||||
<string />
|
||||
<string />
|
||||
<string />
|
||||
<string />
|
||||
<string />
|
||||
<string />
|
||||
<string />
|
||||
<string />
|
||||
</ArrayOfString></Value>
|
||||
</Setting>
|
||||
</Settings>
|
||||
</SettingsFile>
|
||||
@@ -15,6 +15,7 @@
|
||||
along with ContrAlto. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
@@ -120,6 +121,13 @@ namespace Contralto
|
||||
/// <returns></returns>
|
||||
public Event Schedule(Event e)
|
||||
{
|
||||
#if DEBUG
|
||||
if (_schedule.Contains(e))
|
||||
{
|
||||
throw new InvalidOperationException("Can't do that, time will bend.");
|
||||
}
|
||||
#endif
|
||||
|
||||
e.TimestampNsec += _currentTimeNsec;
|
||||
_schedule.Push(e);
|
||||
|
||||
@@ -163,6 +171,11 @@ namespace Contralto
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(Event e)
|
||||
{
|
||||
return _queue.Contains(e);
|
||||
}
|
||||
|
||||
public void Push(Event e)
|
||||
{
|
||||
// Degenerate case: list is empty or new entry is earlier than the head of the list.
|
||||
|
||||
@@ -187,11 +187,8 @@ namespace Contralto.SdlUI
|
||||
throw new InvalidOperationException("Drive specification out of range.");
|
||||
}
|
||||
|
||||
// Save current drive contents.
|
||||
_system.CommitDiskPack(drive);
|
||||
|
||||
// Load the new pack.
|
||||
_system.LoadDrive(drive, path);
|
||||
_system.LoadDiabloDrive(drive, path, false);
|
||||
Console.WriteLine("Drive {0} loaded.", drive);
|
||||
|
||||
return CommandResult.Normal;
|
||||
@@ -205,11 +202,8 @@ namespace Contralto.SdlUI
|
||||
throw new InvalidOperationException("Drive specification out of range.");
|
||||
}
|
||||
|
||||
// Save current drive contents.
|
||||
_system.CommitDiskPack(drive);
|
||||
|
||||
// Unload the current pack.
|
||||
_system.UnloadDrive(drive);
|
||||
_system.UnloadDiabloDrive(drive);
|
||||
Console.WriteLine("Drive {0} unloaded.", drive);
|
||||
|
||||
return CommandResult.Normal;
|
||||
@@ -236,7 +230,60 @@ namespace Contralto.SdlUI
|
||||
}
|
||||
|
||||
return CommandResult.Normal;
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerFunction("load trident", "Loads the specified trident drive with the requested disk image.", "<drive> <path>")]
|
||||
private CommandResult LoadTrident(ushort drive, string path)
|
||||
{
|
||||
if (drive > 7)
|
||||
{
|
||||
throw new InvalidOperationException("Drive specification out of range.");
|
||||
}
|
||||
|
||||
// Load the new pack.
|
||||
_system.LoadTridentDrive(drive, path, false);
|
||||
Console.WriteLine("Trident {0} loaded.", drive);
|
||||
|
||||
return CommandResult.Normal;
|
||||
}
|
||||
|
||||
[DebuggerFunction("unload trident", "Unloads the specified trident drive.", "<drive>")]
|
||||
private CommandResult UnloadTrident(ushort drive)
|
||||
{
|
||||
if (drive > 7)
|
||||
{
|
||||
throw new InvalidOperationException("Drive specification out of range.");
|
||||
}
|
||||
|
||||
// Unload the current pack.
|
||||
_system.UnloadTridentDrive(drive);
|
||||
Console.WriteLine("Trident {0} unloaded.", drive);
|
||||
|
||||
return CommandResult.Normal;
|
||||
}
|
||||
|
||||
[DebuggerFunction("show trident", "Displays the contents of the specified trident drive.", "<drive>")]
|
||||
private CommandResult ShowTrident(ushort drive)
|
||||
{
|
||||
if (drive > 7)
|
||||
{
|
||||
throw new InvalidOperationException("Drive specification out of range.");
|
||||
}
|
||||
|
||||
// Save current drive contents.
|
||||
if (_system.TridentController.Drives[drive].IsLoaded)
|
||||
{
|
||||
Console.WriteLine("Trident {0} contains image {1}",
|
||||
drive,
|
||||
_system.TridentController.Drives[drive].Pack.PackName);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Trident {0} is not loaded.", drive);
|
||||
}
|
||||
|
||||
return CommandResult.Normal;
|
||||
}
|
||||
|
||||
[DebuggerFunction("show system type", "Displays the Alto system type.")]
|
||||
private CommandResult ShowSystemType()
|
||||
|
||||
81
Contralto/UI/AltoWindow.Designer.cs
generated
81
Contralto/UI/AltoWindow.Designer.cs
generated
@@ -41,13 +41,14 @@
|
||||
this.unloadToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.Drive0ImageName = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.drive1ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.loadToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.unloadToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.loadToolStripMenuItem2 = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.unloadToolStripMenuItem2 = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.Drive1ImageName = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.AlternateBootToolStripMenuItem = 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.fullScreenToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.StatusLine = new System.Windows.Forms.StatusStrip();
|
||||
@@ -56,7 +57,9 @@
|
||||
this.CaptureStatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
|
||||
this.SystemStatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
|
||||
this.DisplayBox = new System.Windows.Forms.PictureBox();
|
||||
this.fullScreenToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.newToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.newToolStripMenuItem2 = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.TridentToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this._mainMenu.SuspendLayout();
|
||||
this.StatusLine.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.DisplayBox)).BeginInit();
|
||||
@@ -106,6 +109,7 @@
|
||||
this.SystemResetMenuItem,
|
||||
this.drive0ToolStripMenuItem,
|
||||
this.drive1ToolStripMenuItem,
|
||||
this.TridentToolStripMenuItem,
|
||||
this.AlternateBootToolStripMenuItem,
|
||||
this.SystemEthernetBootMenu,
|
||||
this.optionsToolStripMenuItem,
|
||||
@@ -139,6 +143,7 @@
|
||||
this.drive0ToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.loadToolStripMenuItem1,
|
||||
this.unloadToolStripMenuItem1,
|
||||
this.newToolStripMenuItem1,
|
||||
this.Drive0ImageName});
|
||||
this.drive0ToolStripMenuItem.Name = "drive0ToolStripMenuItem";
|
||||
this.drive0ToolStripMenuItem.Size = new System.Drawing.Size(223, 22);
|
||||
@@ -157,7 +162,7 @@
|
||||
//
|
||||
this.unloadToolStripMenuItem1.Name = "unloadToolStripMenuItem1";
|
||||
this.unloadToolStripMenuItem1.Size = new System.Drawing.Size(172, 22);
|
||||
this.unloadToolStripMenuItem1.Text = "Unload...";
|
||||
this.unloadToolStripMenuItem1.Text = "Unload";
|
||||
this.unloadToolStripMenuItem1.Click += new System.EventHandler(this.OnSystemDrive0UnloadClick);
|
||||
//
|
||||
// Drive0ImageName
|
||||
@@ -170,32 +175,33 @@
|
||||
// drive1ToolStripMenuItem
|
||||
//
|
||||
this.drive1ToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.loadToolStripMenuItem,
|
||||
this.unloadToolStripMenuItem,
|
||||
this.loadToolStripMenuItem2,
|
||||
this.unloadToolStripMenuItem2,
|
||||
this.newToolStripMenuItem2,
|
||||
this.Drive1ImageName});
|
||||
this.drive1ToolStripMenuItem.Name = "drive1ToolStripMenuItem";
|
||||
this.drive1ToolStripMenuItem.Size = new System.Drawing.Size(223, 22);
|
||||
this.drive1ToolStripMenuItem.Text = "Drive 1";
|
||||
//
|
||||
// loadToolStripMenuItem
|
||||
// loadToolStripMenuItem2
|
||||
//
|
||||
this.loadToolStripMenuItem.Name = "loadToolStripMenuItem";
|
||||
this.loadToolStripMenuItem.Size = new System.Drawing.Size(142, 22);
|
||||
this.loadToolStripMenuItem.Text = "Load...";
|
||||
this.loadToolStripMenuItem.Click += new System.EventHandler(this.OnSystemDrive1LoadClick);
|
||||
this.loadToolStripMenuItem2.Name = "loadToolStripMenuItem2";
|
||||
this.loadToolStripMenuItem2.Size = new System.Drawing.Size(152, 22);
|
||||
this.loadToolStripMenuItem2.Text = "Load...";
|
||||
this.loadToolStripMenuItem2.Click += new System.EventHandler(this.OnSystemDrive1LoadClick);
|
||||
//
|
||||
// unloadToolStripMenuItem
|
||||
// unloadToolStripMenuItem2
|
||||
//
|
||||
this.unloadToolStripMenuItem.Name = "unloadToolStripMenuItem";
|
||||
this.unloadToolStripMenuItem.Size = new System.Drawing.Size(142, 22);
|
||||
this.unloadToolStripMenuItem.Text = "Unload...";
|
||||
this.unloadToolStripMenuItem.Click += new System.EventHandler(this.OnSystemDrive1UnloadClick);
|
||||
this.unloadToolStripMenuItem2.Name = "unloadToolStripMenuItem2";
|
||||
this.unloadToolStripMenuItem2.Size = new System.Drawing.Size(152, 22);
|
||||
this.unloadToolStripMenuItem2.Text = "Unload";
|
||||
this.unloadToolStripMenuItem2.Click += new System.EventHandler(this.OnSystemDrive1UnloadClick);
|
||||
//
|
||||
// Drive1ImageName
|
||||
//
|
||||
this.Drive1ImageName.Enabled = false;
|
||||
this.Drive1ImageName.Name = "Drive1ImageName";
|
||||
this.Drive1ImageName.Size = new System.Drawing.Size(142, 22);
|
||||
this.Drive1ImageName.Size = new System.Drawing.Size(152, 22);
|
||||
this.Drive1ImageName.Text = "Image Name";
|
||||
//
|
||||
// AlternateBootToolStripMenuItem
|
||||
@@ -228,6 +234,15 @@
|
||||
this.SystemShowDebuggerMenuItem.Text = "Show Debugger";
|
||||
this.SystemShowDebuggerMenuItem.Click += new System.EventHandler(this.OnDebuggerShowClick);
|
||||
//
|
||||
// fullScreenToolStripMenuItem
|
||||
//
|
||||
this.fullScreenToolStripMenuItem.Name = "fullScreenToolStripMenuItem";
|
||||
this.fullScreenToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Alt)
|
||||
| System.Windows.Forms.Keys.F)));
|
||||
this.fullScreenToolStripMenuItem.Size = new System.Drawing.Size(223, 22);
|
||||
this.fullScreenToolStripMenuItem.Text = "Full Screen";
|
||||
this.fullScreenToolStripMenuItem.Click += new System.EventHandler(this.OnFullScreenMenuClick);
|
||||
//
|
||||
// helpToolStripMenuItem
|
||||
//
|
||||
this.helpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
@@ -307,14 +322,25 @@
|
||||
this.DisplayBox.MouseMove += new System.Windows.Forms.MouseEventHandler(this.OnDisplayMouseMove);
|
||||
this.DisplayBox.MouseUp += new System.Windows.Forms.MouseEventHandler(this.OnDisplayMouseUp);
|
||||
//
|
||||
// fullScreenToolStripMenuItem
|
||||
// newToolStripMenuItem1
|
||||
//
|
||||
this.fullScreenToolStripMenuItem.Name = "fullScreenToolStripMenuItem";
|
||||
this.fullScreenToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Alt)
|
||||
| System.Windows.Forms.Keys.F)));
|
||||
this.fullScreenToolStripMenuItem.Size = new System.Drawing.Size(223, 22);
|
||||
this.fullScreenToolStripMenuItem.Text = "Full Screen";
|
||||
this.fullScreenToolStripMenuItem.Click += new System.EventHandler(this.OnFullScreenMenuClick);
|
||||
this.newToolStripMenuItem1.Name = "newToolStripMenuItem1";
|
||||
this.newToolStripMenuItem1.Size = new System.Drawing.Size(172, 22);
|
||||
this.newToolStripMenuItem1.Text = "New...";
|
||||
this.newToolStripMenuItem1.Click += new System.EventHandler(this.OnSystemDrive0NewClick);
|
||||
//
|
||||
// newToolStripMenuItem2
|
||||
//
|
||||
this.newToolStripMenuItem2.Name = "newToolStripMenuItem2";
|
||||
this.newToolStripMenuItem2.Size = new System.Drawing.Size(152, 22);
|
||||
this.newToolStripMenuItem2.Text = "New...";
|
||||
this.newToolStripMenuItem2.Click += new System.EventHandler(this.OnSystemDrive1NewClick);
|
||||
//
|
||||
// TridentToolStripMenuItem
|
||||
//
|
||||
this.TridentToolStripMenuItem.Name = "TridentToolStripMenuItem";
|
||||
this.TridentToolStripMenuItem.Size = new System.Drawing.Size(223, 22);
|
||||
this.TridentToolStripMenuItem.Text = "Trident Drives";
|
||||
//
|
||||
// AltoWindow
|
||||
//
|
||||
@@ -363,8 +389,8 @@
|
||||
private System.Windows.Forms.ToolStripMenuItem loadToolStripMenuItem1;
|
||||
private System.Windows.Forms.ToolStripMenuItem unloadToolStripMenuItem1;
|
||||
private System.Windows.Forms.ToolStripMenuItem drive1ToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem loadToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem unloadToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem loadToolStripMenuItem2;
|
||||
private System.Windows.Forms.ToolStripMenuItem unloadToolStripMenuItem2;
|
||||
private System.Windows.Forms.ToolStripMenuItem optionsToolStripMenuItem;
|
||||
private System.Windows.Forms.StatusStrip StatusLine;
|
||||
private System.Windows.Forms.ToolStripStatusLabel CaptureStatusLabel;
|
||||
@@ -378,5 +404,8 @@
|
||||
private System.Windows.Forms.ToolStripMenuItem saveScreenshotToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripStatusLabel FPSLabel;
|
||||
private System.Windows.Forms.ToolStripMenuItem fullScreenToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem newToolStripMenuItem1;
|
||||
private System.Windows.Forms.ToolStripMenuItem newToolStripMenuItem2;
|
||||
private System.Windows.Forms.ToolStripMenuItem TridentToolStripMenuItem;
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,8 @@ namespace Contralto
|
||||
|
||||
ReleaseMouse();
|
||||
|
||||
CreateTridentMenu();
|
||||
|
||||
SystemStatusLabel.Text = _systemStoppedText;
|
||||
DiskStatusLabel.Text = String.Empty;
|
||||
|
||||
@@ -95,8 +97,16 @@ namespace Contralto
|
||||
_controller.ErrorCallback += OnExecutionError;
|
||||
|
||||
// Update disk image UI info
|
||||
// Diablo disks:
|
||||
Drive0ImageName.Text = _system.DiskController.Drives[0].IsLoaded ? Path.GetFileName(_system.DiskController.Drives[0].Pack.PackName) : _noImageLoadedText;
|
||||
Drive1ImageName.Text = _system.DiskController.Drives[1].IsLoaded ? Path.GetFileName(_system.DiskController.Drives[1].Pack.PackName) : _noImageLoadedText;
|
||||
|
||||
// Trident disks
|
||||
for (int i = 0; i < _tridentImageNames.Count; i++)
|
||||
{
|
||||
TridentDrive drive = _system.TridentController.Drives[i];
|
||||
_tridentImageNames[i].Text = drive.IsLoaded ? Path.GetFileName(drive.Pack.PackName) : _noImageLoadedText;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -129,7 +139,7 @@ namespace Contralto
|
||||
|
||||
private void OnSystemDrive0LoadClick(object sender, EventArgs e)
|
||||
{
|
||||
string path = ShowImageLoadDialog(0);
|
||||
string path = ShowImageLoadDialog(0, false);
|
||||
|
||||
if (String.IsNullOrEmpty(path))
|
||||
{
|
||||
@@ -138,9 +148,7 @@ namespace Contralto
|
||||
|
||||
try
|
||||
{
|
||||
// Commit loaded pack back to disk
|
||||
_system.CommitDiskPack(0);
|
||||
_system.LoadDrive(0, path);
|
||||
_system.LoadDiabloDrive(0, path, false);
|
||||
Drive0ImageName.Text = System.IO.Path.GetFileName(path);
|
||||
Configuration.Drive0Image = path;
|
||||
}
|
||||
@@ -154,15 +162,14 @@ namespace Contralto
|
||||
|
||||
private void OnSystemDrive0UnloadClick(object sender, EventArgs e)
|
||||
{
|
||||
_system.CommitDiskPack(0);
|
||||
_system.UnloadDrive(0);
|
||||
_system.UnloadDiabloDrive(0);
|
||||
Drive0ImageName.Text = _noImageLoadedText;
|
||||
Configuration.Drive0Image = String.Empty;
|
||||
}
|
||||
|
||||
private void OnSystemDrive1LoadClick(object sender, EventArgs e)
|
||||
private void OnSystemDrive0NewClick(object sender, EventArgs e)
|
||||
{
|
||||
string path = ShowImageLoadDialog(1);
|
||||
string path = ShowImageNewDialog(0, false);
|
||||
|
||||
if (String.IsNullOrEmpty(path))
|
||||
{
|
||||
@@ -171,9 +178,30 @@ namespace Contralto
|
||||
|
||||
try
|
||||
{
|
||||
// Commit loaded pack back to disk
|
||||
_system.CommitDiskPack(1);
|
||||
_system.LoadDrive(1, path);
|
||||
_system.LoadDiabloDrive(0, path, true);
|
||||
Drive0ImageName.Text = System.IO.Path.GetFileName(path);
|
||||
Configuration.Drive0Image = path;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(
|
||||
String.Format("An error occurred while creating new disk image: {0}", ex.Message),
|
||||
"Image creation error", MessageBoxButtons.OK);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSystemDrive1LoadClick(object sender, EventArgs e)
|
||||
{
|
||||
string path = ShowImageLoadDialog(1, false);
|
||||
|
||||
if (String.IsNullOrEmpty(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_system.LoadDiabloDrive(1, path, false);
|
||||
Drive1ImageName.Text = System.IO.Path.GetFileName(path);
|
||||
Configuration.Drive1Image = path;
|
||||
}
|
||||
@@ -186,13 +214,92 @@ namespace Contralto
|
||||
}
|
||||
|
||||
private void OnSystemDrive1UnloadClick(object sender, EventArgs e)
|
||||
{
|
||||
_system.CommitDiskPack(1);
|
||||
_system.UnloadDrive(1);
|
||||
{
|
||||
_system.UnloadDiabloDrive(1);
|
||||
Drive1ImageName.Text = _noImageLoadedText;
|
||||
Configuration.Drive1Image = String.Empty;
|
||||
}
|
||||
|
||||
|
||||
private void OnSystemDrive1NewClick(object sender, EventArgs e)
|
||||
{
|
||||
string path = ShowImageNewDialog(1, false);
|
||||
|
||||
if (String.IsNullOrEmpty(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_system.LoadDiabloDrive(1, path, true);
|
||||
Drive0ImageName.Text = System.IO.Path.GetFileName(path);
|
||||
Configuration.Drive0Image = path;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(
|
||||
String.Format("An error occurred while creating new disk image: {0}", ex.Message),
|
||||
"Image creation error", MessageBoxButtons.OK);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTridentLoadClick(object sender, EventArgs e)
|
||||
{
|
||||
int drive = (int)((ToolStripDropDownItem)sender).Tag;
|
||||
string path = ShowImageLoadDialog(drive, true);
|
||||
|
||||
if (String.IsNullOrEmpty(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_system.LoadTridentDrive(drive, path, false);
|
||||
_tridentImageNames[drive].Text = System.IO.Path.GetFileName(path);
|
||||
Configuration.TridentImages[drive] = path;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(
|
||||
String.Format("An error occurred while loading Trident image: {0}", ex.Message),
|
||||
"Image load error", MessageBoxButtons.OK);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTridentUnloadClick(object sender, EventArgs e)
|
||||
{
|
||||
int drive = (int)((ToolStripDropDownItem)sender).Tag;
|
||||
_system.UnloadTridentDrive(drive);
|
||||
_tridentImageNames[drive].Text = _noImageLoadedText;
|
||||
Configuration.TridentImages[drive] = String.Empty;
|
||||
}
|
||||
|
||||
private void OnTridentNewClick(object sender, EventArgs e)
|
||||
{
|
||||
int drive = (int)((ToolStripDropDownItem)sender).Tag;
|
||||
string path = ShowImageNewDialog(drive, true);
|
||||
|
||||
if (String.IsNullOrEmpty(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_system.LoadTridentDrive(drive, path, true);
|
||||
_tridentImageNames[drive].Text = System.IO.Path.GetFileName(path);
|
||||
Configuration.TridentImages[drive] = path;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(
|
||||
String.Format("An error occurred while creating new Trident image: {0}", ex.Message),
|
||||
"Image creation error", MessageBoxButtons.OK);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAlternateBootOptionsClicked(object sender, EventArgs e)
|
||||
{
|
||||
AlternateBootOptions bootWindow = new AlternateBootOptions();
|
||||
@@ -311,16 +418,40 @@ namespace Contralto
|
||||
DialogResult = DialogResult.OK;
|
||||
}
|
||||
|
||||
private string ShowImageLoadDialog(int drive)
|
||||
private string ShowImageLoadDialog(int drive, bool trident)
|
||||
{
|
||||
OpenFileDialog fileDialog = new OpenFileDialog();
|
||||
|
||||
fileDialog.DefaultExt = "dsk";
|
||||
fileDialog.Filter = "Alto Disk Images (*.dsk, *.dsk44)|*.dsk;*.dsk44|Diablo 31 Disk Images (*.dsk)|*.dsk|Diablo 44 Disk Images (*.dsk44)|*.dsk44|All Files (*.*)|*.*";
|
||||
fileDialog.DefaultExt = trident ? "dsk80" : "dsk";
|
||||
fileDialog.Filter = trident ? _tridentFilter : _diabloFilter;
|
||||
fileDialog.Multiselect = false;
|
||||
fileDialog.CheckFileExists = true;
|
||||
fileDialog.CheckPathExists = true;
|
||||
fileDialog.Title = String.Format("Select image to load into drive {0}", drive);
|
||||
fileDialog.Title = String.Format("Select image to load into {0} drive {1}", trident ? "Trident" : "Diablo", drive);
|
||||
|
||||
DialogResult res = fileDialog.ShowDialog();
|
||||
|
||||
if (res == DialogResult.OK)
|
||||
{
|
||||
return fileDialog.FileName;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private string ShowImageNewDialog(int drive, bool trident)
|
||||
{
|
||||
SaveFileDialog fileDialog = new SaveFileDialog();
|
||||
|
||||
fileDialog.DefaultExt = trident ? "dsk80" : "dsk";
|
||||
fileDialog.Filter = trident ? _tridentFilter : _diabloFilter;
|
||||
fileDialog.CheckFileExists = false;
|
||||
fileDialog.CheckPathExists = true;
|
||||
fileDialog.OverwritePrompt = true;
|
||||
fileDialog.ValidateNames = true;
|
||||
fileDialog.Title = String.Format("Select path for new {0} image for drive {1}", trident ? "Trident" : "Diablo", drive);
|
||||
|
||||
DialogResult res = fileDialog.ShowDialog();
|
||||
|
||||
@@ -965,6 +1096,47 @@ namespace Contralto
|
||||
return null;
|
||||
}
|
||||
|
||||
private void CreateTridentMenu()
|
||||
{
|
||||
//
|
||||
// Add eight sub-menus, one per drive.
|
||||
//
|
||||
_tridentImageNames = new List<ToolStripMenuItem>(8);
|
||||
|
||||
for (int i=0;i<8;i++)
|
||||
{
|
||||
// Parent menu item
|
||||
ToolStripMenuItem tridentMenu = new ToolStripMenuItem(String.Format("Drive {0}", i));
|
||||
|
||||
// Children:
|
||||
// - Load
|
||||
// - Unload
|
||||
// - New
|
||||
// - Pack Name (disabled)
|
||||
//
|
||||
ToolStripMenuItem loadMenu = new ToolStripMenuItem("Load...", null, OnTridentLoadClick);
|
||||
loadMenu.Tag = i;
|
||||
|
||||
ToolStripMenuItem unloadMenu = new ToolStripMenuItem("Unload", null, OnTridentUnloadClick);
|
||||
unloadMenu.Tag = i;
|
||||
|
||||
ToolStripMenuItem newMenu = new ToolStripMenuItem("New...", null, OnTridentNewClick);
|
||||
newMenu.Tag = i;
|
||||
|
||||
ToolStripMenuItem imageMenu = new ToolStripMenuItem(_noImageLoadedText);
|
||||
imageMenu.Tag = i;
|
||||
imageMenu.Enabled = false;
|
||||
_tridentImageNames.Add(imageMenu);
|
||||
|
||||
tridentMenu.DropDownItems.Add(loadMenu);
|
||||
tridentMenu.DropDownItems.Add(unloadMenu);
|
||||
tridentMenu.DropDownItems.Add(newMenu);
|
||||
tridentMenu.DropDownItems.Add(imageMenu);
|
||||
|
||||
TridentToolStripMenuItem.DropDownItems.Add(tridentMenu);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Display related data.
|
||||
// Note: display is actually 606 pixels wide, but that's not an even multiple of 8, so we round up.
|
||||
@@ -1015,10 +1187,17 @@ namespace Contralto
|
||||
private Image _diskWriteImage;
|
||||
private Image _diskSeekImage;
|
||||
|
||||
// Trident menu items for disk names
|
||||
private List<ToolStripMenuItem> _tridentImageNames;
|
||||
|
||||
// strings. TODO: move to resource
|
||||
private const string _noImageLoadedText = "<no image loaded>";
|
||||
private const string _systemStoppedText = "Alto Stopped.";
|
||||
private const string _systemRunningText = "Alto Running.";
|
||||
private const string _systemErrorText = "Alto Stopped due to error. See Debugger.";
|
||||
private const string _systemErrorText = "Alto Stopped due to error. See Debugger.";
|
||||
private const string _diabloFilter = "Alto Diablo Disk Images (*.dsk, *.dsk44)|*.dsk;*.dsk44|Diablo 31 Disk Images (*.dsk)|*.dsk|Diablo 44 Disk Images (*.dsk44)|*.dsk44|All Files (*.*)|*.*";
|
||||
private const string _tridentFilter = "Alto Trident Disk Images (*.dsk80, *.dsk300)|*.dsk80;*.dsk300|Trident T80 Disk Images (*.dsk80)|*.dsk80|Trident T300 Disk Images (*.dsk300)|*.dsk300|All Files (*.*)|*.*";
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -598,7 +598,7 @@ namespace Contralto
|
||||
"EM", // 0 - emulator
|
||||
"OR", // 1 - orbit
|
||||
String.Empty,
|
||||
String.Empty,
|
||||
"TO", // 3 - trident output
|
||||
"KS", // 4 - disk sector
|
||||
String.Empty,
|
||||
String.Empty,
|
||||
@@ -610,7 +610,7 @@ namespace Contralto
|
||||
"DV", // 12 - display vertical
|
||||
"PA", // 13 - parity
|
||||
"KW", // 14 - disk word
|
||||
String.Empty,
|
||||
"TI", // 15 - trident input
|
||||
};
|
||||
|
||||
if (task == TaskType.Invalid)
|
||||
@@ -630,7 +630,7 @@ namespace Contralto
|
||||
Color.LightBlue, // 0 - emulator
|
||||
Color.LightGoldenrodYellow, // 1 - orbit
|
||||
Color.LightGray, // 2 - unused
|
||||
Color.LightGray, // 3 - unused
|
||||
Color.LightCoral, // 3 - trident output
|
||||
Color.LightGreen, // 4 - disk sector
|
||||
Color.LightGray, // 5 - unused
|
||||
Color.LightGray, // 6 - unused
|
||||
@@ -638,11 +638,11 @@ namespace Contralto
|
||||
Color.LightSeaGreen,// 8 - memory refresh
|
||||
Color.LightYellow, // 9 - display word
|
||||
Color.LightPink, // 10 - cursor
|
||||
Color.Chartreuse, // 11 - display horizontal
|
||||
Color.Chartreuse, // 11 - display horizontal
|
||||
Color.LightCoral, // 12 - display vertical
|
||||
Color.LightSteelBlue, // 13 - parity
|
||||
Color.Gray, // 14 - disk word
|
||||
Color.LightGray, // 15 - unused
|
||||
Color.LightSteelBlue, // 15 - trident output
|
||||
};
|
||||
|
||||
if (task == TaskType.Invalid)
|
||||
|
||||
63
Contralto/UI/SystemOptions.Designer.cs
generated
63
Contralto/UI/SystemOptions.Designer.cs
generated
@@ -55,15 +55,15 @@
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.EnableDACCaptureCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.EnableDACCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.DialogOKButton = new System.Windows.Forms.Button();
|
||||
this.DialogCancelButton = new System.Windows.Forms.Button();
|
||||
this.PrintingTab = new System.Windows.Forms.TabPage();
|
||||
this.PrintingOptionsGroupBox = new System.Windows.Forms.GroupBox();
|
||||
this.ReversePageOrderCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.button1 = new System.Windows.Forms.Button();
|
||||
this.PrintOutputPathTextBox = new System.Windows.Forms.TextBox();
|
||||
this.label5 = new System.Windows.Forms.Label();
|
||||
this.EnablePrintingCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.ReversePageOrderCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.DialogOKButton = new System.Windows.Forms.Button();
|
||||
this.DialogCancelButton = new System.Windows.Forms.Button();
|
||||
this.OptionsTabs.SuspendLayout();
|
||||
this.tabPage1.SuspendLayout();
|
||||
this.tabPage2.SuspendLayout();
|
||||
@@ -368,26 +368,6 @@
|
||||
this.EnableDACCheckBox.UseVisualStyleBackColor = true;
|
||||
this.EnableDACCheckBox.CheckedChanged += new System.EventHandler(this.OnEnableDACCheckboxChanged);
|
||||
//
|
||||
// DialogOKButton
|
||||
//
|
||||
this.DialogOKButton.Location = new System.Drawing.Point(211, 239);
|
||||
this.DialogOKButton.Name = "DialogOKButton";
|
||||
this.DialogOKButton.Size = new System.Drawing.Size(75, 23);
|
||||
this.DialogOKButton.TabIndex = 1;
|
||||
this.DialogOKButton.Text = "OK";
|
||||
this.DialogOKButton.UseVisualStyleBackColor = true;
|
||||
this.DialogOKButton.Click += new System.EventHandler(this.OKButton_Click);
|
||||
//
|
||||
// DialogCancelButton
|
||||
//
|
||||
this.DialogCancelButton.Location = new System.Drawing.Point(292, 239);
|
||||
this.DialogCancelButton.Name = "DialogCancelButton";
|
||||
this.DialogCancelButton.Size = new System.Drawing.Size(75, 23);
|
||||
this.DialogCancelButton.TabIndex = 2;
|
||||
this.DialogCancelButton.Text = "Cancel";
|
||||
this.DialogCancelButton.UseVisualStyleBackColor = true;
|
||||
this.DialogCancelButton.Click += new System.EventHandler(this.CancelButton_Click);
|
||||
//
|
||||
// PrintingTab
|
||||
//
|
||||
this.PrintingTab.Controls.Add(this.PrintingOptionsGroupBox);
|
||||
@@ -413,6 +393,16 @@
|
||||
this.PrintingOptionsGroupBox.TabStop = false;
|
||||
this.PrintingOptionsGroupBox.Text = "Printing options";
|
||||
//
|
||||
// ReversePageOrderCheckBox
|
||||
//
|
||||
this.ReversePageOrderCheckBox.AutoSize = true;
|
||||
this.ReversePageOrderCheckBox.Location = new System.Drawing.Point(22, 51);
|
||||
this.ReversePageOrderCheckBox.Name = "ReversePageOrderCheckBox";
|
||||
this.ReversePageOrderCheckBox.Size = new System.Drawing.Size(158, 17);
|
||||
this.ReversePageOrderCheckBox.TabIndex = 4;
|
||||
this.ReversePageOrderCheckBox.Text = "Reverse Output Page Order";
|
||||
this.ReversePageOrderCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// button1
|
||||
//
|
||||
this.button1.Location = new System.Drawing.Point(254, 24);
|
||||
@@ -421,6 +411,7 @@
|
||||
this.button1.TabIndex = 3;
|
||||
this.button1.Text = "Browse...";
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
this.button1.Click += new System.EventHandler(this.OnPrintOutputBrowseButtonClicked);
|
||||
//
|
||||
// PrintOutputPathTextBox
|
||||
//
|
||||
@@ -449,15 +440,25 @@
|
||||
this.EnablePrintingCheckBox.UseVisualStyleBackColor = true;
|
||||
this.EnablePrintingCheckBox.CheckedChanged += new System.EventHandler(this.EnablePrintingCheckBox_CheckedChanged);
|
||||
//
|
||||
// ReversePageOrderCheckBox
|
||||
// DialogOKButton
|
||||
//
|
||||
this.ReversePageOrderCheckBox.AutoSize = true;
|
||||
this.ReversePageOrderCheckBox.Location = new System.Drawing.Point(22, 51);
|
||||
this.ReversePageOrderCheckBox.Name = "ReversePageOrderCheckBox";
|
||||
this.ReversePageOrderCheckBox.Size = new System.Drawing.Size(158, 17);
|
||||
this.ReversePageOrderCheckBox.TabIndex = 4;
|
||||
this.ReversePageOrderCheckBox.Text = "Reverse Output Page Order";
|
||||
this.ReversePageOrderCheckBox.UseVisualStyleBackColor = true;
|
||||
this.DialogOKButton.Location = new System.Drawing.Point(211, 239);
|
||||
this.DialogOKButton.Name = "DialogOKButton";
|
||||
this.DialogOKButton.Size = new System.Drawing.Size(75, 23);
|
||||
this.DialogOKButton.TabIndex = 1;
|
||||
this.DialogOKButton.Text = "OK";
|
||||
this.DialogOKButton.UseVisualStyleBackColor = true;
|
||||
this.DialogOKButton.Click += new System.EventHandler(this.OKButton_Click);
|
||||
//
|
||||
// DialogCancelButton
|
||||
//
|
||||
this.DialogCancelButton.Location = new System.Drawing.Point(292, 239);
|
||||
this.DialogCancelButton.Name = "DialogCancelButton";
|
||||
this.DialogCancelButton.Size = new System.Drawing.Size(75, 23);
|
||||
this.DialogCancelButton.TabIndex = 2;
|
||||
this.DialogCancelButton.Text = "Cancel";
|
||||
this.DialogCancelButton.UseVisualStyleBackColor = true;
|
||||
this.DialogCancelButton.Click += new System.EventHandler(this.CancelButton_Click);
|
||||
//
|
||||
// SystemOptions
|
||||
//
|
||||
|
||||
@@ -360,10 +360,15 @@ namespace Contralto.UI
|
||||
}
|
||||
else
|
||||
{
|
||||
EnablePrintingCheckBox.Checked = false;
|
||||
EnablePrintingCheckBox.Checked = string.IsNullOrEmpty(PrintOutputPathTextBox.Text) ? false : true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPrintOutputBrowseButtonClicked(object sender, EventArgs e)
|
||||
{
|
||||
BrowseForPrintOutputFolder();
|
||||
}
|
||||
|
||||
private void EnablePrintingCheckBox_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
PrintingOptionsGroupBox.Enabled = EnablePrintingCheckBox.Checked;
|
||||
@@ -374,6 +379,6 @@ namespace Contralto.UI
|
||||
//
|
||||
BrowseForPrintOutputFolder();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,10 +194,10 @@
|
||||
<File Id="u36_23" Name="36_23.BIN" Source="$(var.Contralto.TargetDir)\ROM\AltoI\36_23.BIN"/>
|
||||
<File Id="u37_23" Name="37_23.BIN" Source="$(var.Contralto.TargetDir)\ROM\AltoI\37_23.BIN"/>
|
||||
|
||||
<File Id="C0_23" Name="C1_23.BIN" Source="$(var.Contralto.TargetDir)\ROM\AltoII\C0"/>
|
||||
<File Id="C1_23" Name="C2_23.BIN" Source="$(var.Contralto.TargetDir)\ROM\AltoII\C1"/>
|
||||
<File Id="C2_23" Name="C3_23.BIN" Source="$(var.Contralto.TargetDir)\ROM\AltoII\C2"/>
|
||||
<File Id="C3_23" Name="C4_23.BIN" Source="$(var.Contralto.TargetDir)\ROM\AltoII\C3"/>
|
||||
<File Id="C0_23" Name="C0_23.BIN" Source="$(var.Contralto.TargetDir)\ROM\AltoI\C0_23.BIN"/>
|
||||
<File Id="C1_23" Name="C1_23.BIN" Source="$(var.Contralto.TargetDir)\ROM\AltoI\C1_23.BIN"/>
|
||||
<File Id="C2_23" Name="C2_23.BIN" Source="$(var.Contralto.TargetDir)\ROM\AltoI\C2_23.BIN"/>
|
||||
<File Id="C3_23" Name="C3_23.BIN" Source="$(var.Contralto.TargetDir)\ROM\AltoI\C3_23.BIN"/>
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user