mirror of
https://github.com/livingcomputermuseum/ContrAlto.git
synced 2026-04-28 04:46:08 +00:00
- Fixed two memory state machine issues:
1) The Orbit microcode occasionally expects to be able to do a double-word read in cycles 6 and 7 instead of the usual 5 and 6. 2) The double-word logic incorrectly handles several species of overlapped read/writes, which Spruce's layout microcode uses. As a result, Spruce now works, as do the Smalltalk-80 images. Several tweaks to microcode disassembly (still rough). Completed Orbit / Dover ROS / Dover Print Engine emulation. Still some rough edges; it works and can create PDF output. Tweaked Disk Controller's Restore operation, it is now more accurate though wasn't causing any noticable issues.
This commit is contained in:
@@ -242,8 +242,6 @@ namespace Contralto
|
||||
get { return _scheduler; }
|
||||
}
|
||||
|
||||
public int _novaInst;
|
||||
|
||||
private AltoCPU _cpu;
|
||||
private MemoryBus _memBus;
|
||||
private Memory.Memory _mem;
|
||||
|
||||
@@ -58,6 +58,15 @@
|
||||
<setting name="AudioDACCapturePath" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="EnablePrinting" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
<setting name="PrintOutputPath" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="ReversePageOrder" serializeAs="String">
|
||||
<value>True</value>
|
||||
</setting>
|
||||
</Contralto.Properties.Settings>
|
||||
</userSettings>
|
||||
</configuration>
|
||||
|
||||
@@ -160,7 +160,7 @@ namespace Contralto.CPU
|
||||
|
||||
case InstructionCompletion.MemoryWait:
|
||||
// We were waiting for memory on this cycle, we do nothing
|
||||
// (no task switch even if one is pending) in this case.
|
||||
// (no task switch even if one is pending) in this case.
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -187,13 +187,13 @@ namespace Contralto.CPU
|
||||
UCodeMemory.LoadBanksFromRMR(_rmr);
|
||||
|
||||
// Reset RMR after reset.
|
||||
_rmr = 0x0;
|
||||
_rmr = 0xffff;
|
||||
|
||||
// Start in Emulator
|
||||
_currentTask = _tasks[0];
|
||||
|
||||
//
|
||||
// TODO:
|
||||
// TODO:
|
||||
// This is a hack of sorts, it ensures that the sector task initializes
|
||||
// itself as soon as the Emulator task yields after the reset. (CopyDisk is broken otherwise due to the
|
||||
// sector task stomping over the KBLK CopyDisk sets up after the reset. This is a race of sorts.)
|
||||
@@ -201,6 +201,7 @@ namespace Contralto.CPU
|
||||
// in play that are not understood.
|
||||
//
|
||||
WakeupTask(CPU.TaskType.DiskSector);
|
||||
BlockTask(CPU.TaskType.DiskWord);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -250,7 +251,20 @@ namespace Contralto.CPU
|
||||
public Task NextTask
|
||||
{
|
||||
get { return _nextTask; }
|
||||
}
|
||||
}
|
||||
|
||||
public bool InternalBreak
|
||||
{
|
||||
get
|
||||
{
|
||||
return _internalBreak;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_internalBreak = value;
|
||||
}
|
||||
}
|
||||
|
||||
private void TaskSwitch()
|
||||
{
|
||||
@@ -295,6 +309,8 @@ namespace Contralto.CPU
|
||||
private Task[] _tasks = new Task[16];
|
||||
|
||||
// The system this CPU belongs to
|
||||
private AltoSystem _system;
|
||||
private AltoSystem _system;
|
||||
|
||||
private bool _internalBreak;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ namespace Contralto.CPU
|
||||
// bus specifies the initial mode of task 15, the highest priority task(recall that task i starts at location i; the
|
||||
// reset mode register determines only which microinstruction bank will be used at the outset). A task will
|
||||
// commence in ROM0 if its associated bit in the reset mode register contains the value 1; otherwise it will
|
||||
// start in RAM0.Upon initial power - up of the Alto, and after each reset operation, the reset mode register
|
||||
// start in RAM0. Upon initial power-up of the Alto, and after each reset operation, the reset mode register
|
||||
// is automatically set to all ones, corresponding to starting all tasks in ROM0."
|
||||
//
|
||||
_cpu._rmr = _busData;
|
||||
@@ -125,11 +125,7 @@ namespace Contralto.CPU
|
||||
// Dispatch function to Ethernet I/O based on contents of AC0.
|
||||
if ((_busData & 0x8000) != 0)
|
||||
{
|
||||
//
|
||||
// BOOT (soft-reset) operation.
|
||||
// Reset the CPU using the current RMR (start tasks in RAM or ROM as specified.)
|
||||
_cpu.SoftReset();
|
||||
|
||||
|
||||
// Since this is a soft reset, we don't want MPC to be taken from the NEXT
|
||||
// field at the end of the cycle, setting this flag causes the main Task
|
||||
// implementation to skip updating _mpc at the end of this instruction.
|
||||
@@ -150,7 +146,7 @@ namespace Contralto.CPU
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// Orbit
|
||||
// Orbit
|
||||
_cpu._system.OrbitController.STARTF(_busData);
|
||||
break;
|
||||
|
||||
|
||||
@@ -33,24 +33,10 @@ namespace Contralto.CPU
|
||||
_wakeup = false;
|
||||
}
|
||||
|
||||
public override void OnTaskSwitch()
|
||||
{
|
||||
// We put ourselves back to sleep immediately once we've started running.
|
||||
//_wakeup = false;
|
||||
}
|
||||
|
||||
protected override void ExecuteBlock()
|
||||
{
|
||||
//_wakeup = false;
|
||||
_cpu._system.OrbitController.Stop();
|
||||
}
|
||||
|
||||
protected override InstructionCompletion ExecuteInstruction(MicroInstruction instruction)
|
||||
{
|
||||
// TODO: get rid of polling.
|
||||
//_wakeup = _cpu._system.OrbitController.Wakeup;
|
||||
return base.ExecuteInstruction(instruction);
|
||||
}
|
||||
}
|
||||
|
||||
protected override ushort GetBusSource(MicroInstruction instruction)
|
||||
{
|
||||
@@ -80,7 +66,7 @@ namespace Contralto.CPU
|
||||
// "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 0x0; // Technically this is an "undefined value," we're defining it as -1.
|
||||
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));
|
||||
|
||||
@@ -142,13 +142,14 @@ namespace Contralto.CPU
|
||||
bool swMode = false;
|
||||
ushort aluData;
|
||||
ushort nextModifier;
|
||||
bool softReset = _softReset;
|
||||
_loadR = false;
|
||||
_loadS = false;
|
||||
_rSelect = 0;
|
||||
_busData = 0;
|
||||
_softReset = false;
|
||||
|
||||
Shifter.Reset();
|
||||
Shifter.Reset();
|
||||
|
||||
//
|
||||
// Wait for memory state machine if a memory operation is requested by this instruction and
|
||||
@@ -197,10 +198,10 @@ namespace Contralto.CPU
|
||||
break;
|
||||
|
||||
case BusSource.ReadMD:
|
||||
_busData = _cpu._system.MemoryBus.ReadMD();
|
||||
_busData = _cpu._system.MemoryBus.ReadMD();
|
||||
break;
|
||||
|
||||
case BusSource.ReadMouse:
|
||||
case BusSource.ReadMouse:
|
||||
// "BUS[12-15]<-MOUSE; BUS[0-13]<- -1"
|
||||
// (Note -- BUS[0-13] appears to be a typo, and should likely be BUS[0-11]).
|
||||
_busData = (ushort)(_cpu._system.Mouse.PollMouseBits() | 0xfff0);
|
||||
@@ -533,10 +534,14 @@ namespace Contralto.CPU
|
||||
// Select next address, using the address modifier from the last instruction.
|
||||
// (Unless a soft reset occurred during this instruction)
|
||||
//
|
||||
if (!_softReset)
|
||||
if (!softReset)
|
||||
{
|
||||
_mpc = (ushort)(instruction.NEXT | nextModifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
_cpu.SoftReset();
|
||||
}
|
||||
|
||||
_firstInstructionAfterSwitch = false;
|
||||
return completion;
|
||||
|
||||
@@ -250,10 +250,10 @@ namespace Contralto.CPU
|
||||
//
|
||||
|
||||
// Load T
|
||||
bool loadTFromALU = false;
|
||||
if (instruction.LoadT)
|
||||
{
|
||||
// Does this operation change the source for T?
|
||||
bool loadTFromALU = false;
|
||||
// Does this operation change the source for T?
|
||||
switch (instruction.ALUF)
|
||||
{
|
||||
case AluFunction.Bus:
|
||||
@@ -275,11 +275,18 @@ namespace Contralto.CPU
|
||||
{
|
||||
if (string.IsNullOrEmpty(load))
|
||||
{
|
||||
// T not loaded at all, L loaded from ALU
|
||||
load = String.Format("L← {0}", operation);
|
||||
}
|
||||
else if (loadTFromALU)
|
||||
{
|
||||
// T loaded from ALU, L loaded from ALU
|
||||
load = String.Format("L← {0}", load);
|
||||
}
|
||||
else
|
||||
{
|
||||
load = String.Format("L← {0}", load);
|
||||
// T loaded from bus source, L loaded from ALU
|
||||
load = String.Format("L← {0}, {1}", operation, load);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,8 +294,8 @@ namespace Contralto.CPU
|
||||
if (loadR)
|
||||
{
|
||||
load = String.Format("$R{0}← {1}",
|
||||
Conversion.ToOctal((int)rSelect),
|
||||
load != String.Empty ? load : operation);
|
||||
Conversion.ToOctal((int)rSelect),
|
||||
!string.IsNullOrEmpty(load) ? load : operation);
|
||||
}
|
||||
|
||||
// Do writeback to selected S register from M
|
||||
@@ -307,7 +314,18 @@ namespace Contralto.CPU
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(load))
|
||||
// Test for a NOP-like instruction.
|
||||
if (!instruction.LoadL &&
|
||||
!instruction.LoadT &&
|
||||
!loadR &&
|
||||
!loadS &&
|
||||
instruction.F1 == SpecialFunction1.None &&
|
||||
instruction.F2 == SpecialFunction2.None &&
|
||||
instruction.ALUF == AluFunction.Bus)
|
||||
{
|
||||
disassembly.AppendFormat("NOP :{0}", Conversion.ToOctal(instruction.NEXT));
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(load))
|
||||
{
|
||||
disassembly.AppendFormat("{0}{1}{2} :{3}",
|
||||
f1,
|
||||
@@ -333,7 +351,8 @@ namespace Contralto.CPU
|
||||
switch(task)
|
||||
{
|
||||
case TaskType.Emulator:
|
||||
return DisassembleEmulatorBusSource(instruction, out loadS);
|
||||
case TaskType.Orbit:
|
||||
return DisassembleEmulatorBusSource(instruction, out loadS);
|
||||
|
||||
default:
|
||||
loadS = false;
|
||||
@@ -346,7 +365,10 @@ namespace Contralto.CPU
|
||||
switch (task)
|
||||
{
|
||||
case TaskType.Emulator:
|
||||
return DisassembleEmulatorSpecialFunction1(instruction);
|
||||
return DisassembleEmulatorSpecialFunction1(instruction);
|
||||
|
||||
case TaskType.Orbit:
|
||||
return DisassembleOrbitSpecialFunction1(instruction);
|
||||
|
||||
default:
|
||||
return String.Format("F1 {0}", Conversion.ToOctal((int)instruction.F1));
|
||||
@@ -358,7 +380,10 @@ namespace Contralto.CPU
|
||||
switch (task)
|
||||
{
|
||||
case TaskType.Emulator:
|
||||
return DisassembleEmulatorSpecialFunction2(instruction);
|
||||
return DisassembleEmulatorSpecialFunction2(instruction);
|
||||
|
||||
case TaskType.Orbit:
|
||||
return DisassembleOrbitSpecialFunction2(instruction);
|
||||
|
||||
default:
|
||||
return String.Format("F2 {0}", Conversion.ToOctal((int)instruction.F2));
|
||||
@@ -373,7 +398,15 @@ namespace Contralto.CPU
|
||||
{
|
||||
case EmulatorBusSource.ReadSLocation:
|
||||
loadS = false;
|
||||
return String.Format("$S{0}", Conversion.ToOctal((int)instruction.RSELECT));
|
||||
|
||||
if (instruction.RSELECT == 0)
|
||||
{
|
||||
return "M";
|
||||
}
|
||||
else
|
||||
{
|
||||
return String.Format("$S{0}", Conversion.ToOctal((int)instruction.RSELECT));
|
||||
}
|
||||
|
||||
case EmulatorBusSource.LoadSLocation:
|
||||
loadS = true;
|
||||
@@ -415,7 +448,7 @@ namespace Contralto.CPU
|
||||
return "STARTF ";
|
||||
|
||||
default:
|
||||
return String.Format("F1 {0}", Conversion.ToOctal((int)ef1));
|
||||
return String.Format("Emulator F1 {0}", Conversion.ToOctal((int)ef1));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -448,7 +481,62 @@ namespace Contralto.CPU
|
||||
return "IDISP ";
|
||||
|
||||
default:
|
||||
return String.Format("F2 {0}", Conversion.ToOctal((int)ef2));
|
||||
return String.Format("Emulator F2 {0}", Conversion.ToOctal((int)ef2));
|
||||
}
|
||||
}
|
||||
|
||||
private static string DisassembleOrbitSpecialFunction1(MicroInstruction instruction)
|
||||
{
|
||||
OrbitF1 of1 = (OrbitF1)instruction.F1;
|
||||
|
||||
switch(of1)
|
||||
{
|
||||
case OrbitF1.OrbitBlock:
|
||||
return "OrbitBlock ";
|
||||
|
||||
case OrbitF1.OrbitDeltaWC:
|
||||
return "←OrbitDeltaWC ";
|
||||
|
||||
case OrbitF1.OrbitDBCWidthRead:
|
||||
return "←OrbitDBCWidthRead ";
|
||||
|
||||
case OrbitF1.OrbitStatus:
|
||||
return "←OrbitStatus ";
|
||||
|
||||
default:
|
||||
return String.Format("Orbit F1 {0}", Conversion.ToOctal((int)of1));
|
||||
}
|
||||
}
|
||||
|
||||
private static string DisassembleOrbitSpecialFunction2(MicroInstruction instruction)
|
||||
{
|
||||
OrbitF2 of2 = (OrbitF2)instruction.F2;
|
||||
|
||||
switch (of2)
|
||||
{
|
||||
case OrbitF2.OrbitDBCWidthSet:
|
||||
return "OrbitDBCWidthSet← ";
|
||||
|
||||
case OrbitF2.OrbitXY:
|
||||
return "OrbitXY← ";
|
||||
|
||||
case OrbitF2.OrbitHeight:
|
||||
return "OrbitHeight← ";
|
||||
|
||||
case OrbitF2.OrbitFontData:
|
||||
return "OrbitFontData← ";
|
||||
|
||||
case OrbitF2.OrbitInk:
|
||||
return "OrbitInk← ";
|
||||
|
||||
case OrbitF2.OrbitControl:
|
||||
return "OrbitControl← ";
|
||||
|
||||
case OrbitF2.OrbitROSCommand:
|
||||
return "OrbitROSCommand← ";
|
||||
|
||||
default:
|
||||
return String.Format("Orbit F2 {0}", Conversion.ToOctal((int)of2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,6 +204,21 @@ namespace Contralto
|
||||
/// </summary>
|
||||
public static string AudioDACCapturePath;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to enable printing via the Orbit / DoverROS interface.
|
||||
/// </summary>
|
||||
public static bool EnablePrinting;
|
||||
|
||||
/// <summary>
|
||||
/// Path for print output.
|
||||
/// </summary>
|
||||
public static string PrintOutputPath;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to reverse the page order when printing.
|
||||
/// </summary>
|
||||
public static bool ReversePageOrder;
|
||||
|
||||
/// <summary>
|
||||
/// The components to enable debug logging for.
|
||||
/// </summary>
|
||||
@@ -291,6 +306,9 @@ namespace Contralto
|
||||
EnableAudioDAC = Properties.Settings.Default.EnableAudioDAC;
|
||||
EnableAudioDACCapture = Properties.Settings.Default.EnableAudioDACCapture;
|
||||
AudioDACCapturePath = Properties.Settings.Default.AudioDACCapturePath;
|
||||
EnablePrinting = Properties.Settings.Default.EnablePrinting;
|
||||
PrintOutputPath = Properties.Settings.Default.PrintOutputPath;
|
||||
ReversePageOrder = Properties.Settings.Default.ReversePageOrder;
|
||||
}
|
||||
|
||||
private static void WriteConfigurationWindows()
|
||||
@@ -308,8 +326,10 @@ namespace Contralto
|
||||
Properties.Settings.Default.ThrottleSpeed = ThrottleSpeed;
|
||||
Properties.Settings.Default.EnableAudioDAC = EnableAudioDAC;
|
||||
Properties.Settings.Default.EnableAudioDACCapture = EnableAudioDACCapture;
|
||||
Properties.Settings.Default.AudioDACCapturePath = AudioDACCapturePath;
|
||||
Properties.Settings.Default.AudioDACCapturePath = Properties.Settings.Default.AudioDACCapturePath;
|
||||
Properties.Settings.Default.AudioDACCapturePath = AudioDACCapturePath;
|
||||
Properties.Settings.Default.EnablePrinting = EnablePrinting;
|
||||
Properties.Settings.Default.PrintOutputPath = PrintOutputPath;
|
||||
Properties.Settings.Default.ReversePageOrder = ReversePageOrder;
|
||||
Properties.Settings.Default.Save();
|
||||
}
|
||||
|
||||
|
||||
@@ -18,3 +18,8 @@ InterlaceDisplay = False
|
||||
BootAddress = 0
|
||||
BootFile = 0
|
||||
AlternateBootType = Ethernet
|
||||
|
||||
# Printing options
|
||||
EnablePrinting = true
|
||||
PrintOutputPath = .
|
||||
ReversePageOrder = true
|
||||
|
||||
@@ -97,6 +97,9 @@
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="itextsharp, Version=5.5.11.0, Culture=neutral, PublicKeyToken=8354ae6d2174ddca, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\iTextSharp.5.5.11\lib\itextsharp.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NAudio, Version=1.8.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NAudio.1.8.0\lib\net35\NAudio.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
@@ -127,6 +130,9 @@
|
||||
<Compile Include="IO\AudioDAC.cs" />
|
||||
<Compile Include="IO\OrbitController.cs" />
|
||||
<Compile Include="IO\OrganKeyboard.cs" />
|
||||
<Compile Include="IO\Printing\IPageSink.cs" />
|
||||
<Compile Include="IO\Printing\NullPageSink.cs" />
|
||||
<Compile Include="IO\Printing\PdfPageSink.cs" />
|
||||
<Compile Include="IO\UDPEncapsulation.cs" />
|
||||
<Compile Include="IO\IPacketEncapsulation.cs" />
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
|
||||
@@ -115,8 +115,8 @@ namespace Contralto.IO
|
||||
if ((_kDataWrite & 0x1) != 0)
|
||||
{
|
||||
// Restore operation to cyl. 0:
|
||||
InitSeek(0);
|
||||
}
|
||||
_restore = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ namespace Contralto.IO
|
||||
|
||||
if (_sendAdr & (_kDataWrite & 0x2) != 0)
|
||||
{
|
||||
_seeking = false;
|
||||
_seeking = false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -244,6 +244,7 @@ namespace Contralto.IO
|
||||
_kDataWriteLatch = false;
|
||||
_sendAdr = false;
|
||||
_seeking = false;
|
||||
_restore = false;
|
||||
|
||||
_wdInhib = true;
|
||||
_xferOff = true;
|
||||
@@ -311,7 +312,7 @@ namespace Contralto.IO
|
||||
_kDataRead = 0;
|
||||
|
||||
// Load new sector in
|
||||
SelectedDrive.Sector = _sector;
|
||||
SelectedDrive.Sector = _sector;
|
||||
|
||||
// Only wake up if not actively seeking.
|
||||
if ((_kStat & STROBE) == 0)
|
||||
@@ -417,7 +418,7 @@ namespace Contralto.IO
|
||||
public void Strobe()
|
||||
{
|
||||
//
|
||||
// "Initiates a disk seek operation. The KDATA register must have been loaded previously,
|
||||
// "Initiates a disk seek [or restore] operation. The KDATA register must have been loaded previously,
|
||||
// and the SENDADR bit of the KCOMM register previously set to 1."
|
||||
//
|
||||
|
||||
@@ -431,7 +432,14 @@ namespace Contralto.IO
|
||||
|
||||
Log.Write(LogComponent.DiskController, "STROBE: Seek initialized.");
|
||||
|
||||
InitSeek((_kDataWrite & 0x0ff8) >> 3);
|
||||
if (_restore)
|
||||
{
|
||||
InitSeek(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
InitSeek((_kDataWrite & 0x0ff8) >> 3);
|
||||
}
|
||||
}
|
||||
|
||||
private void InitSeek(int destCylinder)
|
||||
@@ -636,7 +644,8 @@ namespace Contralto.IO
|
||||
{
|
||||
// clear Seek bit
|
||||
_kStat &= (ushort)~STROBE;
|
||||
_seeking = false;
|
||||
_seeking = false;
|
||||
_restore = false;
|
||||
|
||||
Log.Write(LogComponent.DiskController, "Seek to {0} completed.", SelectedDrive.Cylinder);
|
||||
}
|
||||
@@ -704,6 +713,7 @@ namespace Contralto.IO
|
||||
private ulong _seekDuration;
|
||||
private Event _seekEvent;
|
||||
private bool _seeking;
|
||||
private bool _restore;
|
||||
|
||||
// Selected disk
|
||||
private int _disk;
|
||||
|
||||
@@ -1,11 +1,24 @@
|
||||
using Contralto.Logging;
|
||||
/*
|
||||
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.Printing;
|
||||
using Contralto.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Contralto.IO
|
||||
{
|
||||
@@ -22,18 +35,28 @@ namespace Contralto.IO
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encapsulates the logic for both the ROS and the printer portions of the
|
||||
/// print pipeline.
|
||||
/// Encapsulates the logic for both the ROS (Raster Output Scanner) hardware and
|
||||
/// the Dover print engine.
|
||||
///
|
||||
/// These two should probably be separated out in the event that we want to support
|
||||
/// other kinds of printers.
|
||||
///
|
||||
/// The Dover print engine support is currently very hand-wavy as the documentation
|
||||
/// isn't extremely specific on some details and we're emulating a mechanical construct
|
||||
/// moving papers past sensors, etc. It mostly works but it has some rough edges.
|
||||
/// In particular the "Cold Start" mechanic is not well understood. Many parameters
|
||||
/// are currently ignored (some for good reason; we don't need to emulate the actual
|
||||
/// laser scanning and polygon rotation in order to create a bitmap, for example) but
|
||||
/// some of them might be put to good use.
|
||||
///
|
||||
/// None of the diagnostic switches or options are implemented.
|
||||
/// </summary>
|
||||
public class DoverROS
|
||||
{
|
||||
public DoverROS(AltoSystem system)
|
||||
{
|
||||
_system = system;
|
||||
|
||||
_cs5Event = new Event(0, null, ColdStartCallback);
|
||||
_innerLoopEvent = new Event(0, null, InnerLoopCallback);
|
||||
_pageBuffer = new Bitmap(4096, 4096, PixelFormat.Format1bppIndexed);
|
||||
_system = system;
|
||||
_printEngineEvent = new Event(0, null, PrintEngineCallback);
|
||||
|
||||
Reset();
|
||||
}
|
||||
@@ -54,9 +77,8 @@ namespace Contralto.IO
|
||||
|
||||
_packetsOK = true;
|
||||
|
||||
_state = PrintState.ColdStart;
|
||||
_innerLoopState = InnerLoopState.Idle;
|
||||
_coldStartState = ColdStartState.SendVideoLow;
|
||||
_state = PrintState.Idle;
|
||||
_runoutCount = 0;
|
||||
}
|
||||
|
||||
public void RecvCommand(ushort commandWord)
|
||||
@@ -83,27 +105,40 @@ namespace Contralto.IO
|
||||
_extendVideo = (argument & 0x20) != 0;
|
||||
_motorScale = (argument & 0x1c0) >> 6;
|
||||
_bitScale = (argument & 0xe00) >> 9;
|
||||
|
||||
Log.Write(LogComponent.DoverROS, "TestMode {0} CommandBeamOn {1} CommandLocal {2} TestPageSync {3} ExtendVideo {4} MotorScale {5} BitScale {6}",
|
||||
_testMode,
|
||||
_commandBeamOn,
|
||||
_commandLocal,
|
||||
_testPageSync,
|
||||
_extendVideo,
|
||||
_motorScale,
|
||||
_bitScale);
|
||||
break;
|
||||
|
||||
case AdapterCommand.SetBitClockRegister:
|
||||
_bitClock = argument;
|
||||
Log.Write(LogComponent.DoverROS, "BitClock set to {0}", argument);
|
||||
break;
|
||||
|
||||
case AdapterCommand.SetMotorSpeedRegister:
|
||||
_motorSpeed = argument;
|
||||
Log.Write(LogComponent.DoverROS, "MotorSpeed set to {0}", argument);
|
||||
break;
|
||||
|
||||
case AdapterCommand.SetLineSyncDelayRegister:
|
||||
_lineSyncDelay = argument;
|
||||
Log.Write(LogComponent.DoverROS, "LineSyncDelay set to {0}", argument);
|
||||
break;
|
||||
|
||||
case AdapterCommand.SetPageSyncDelayRegister:
|
||||
_pageSyncDelay = argument;
|
||||
Log.Write(LogComponent.DoverROS, "PageSyncDelay set to {0}", argument);
|
||||
break;
|
||||
|
||||
case AdapterCommand.ExternalCommand1:
|
||||
|
||||
bool lastPrintRequest = (_externalCommand1 & 0x1) != 0;
|
||||
bool lastPrintRequest = (_externalCommand1 & 0x1) == 0;
|
||||
|
||||
_externalCommand1 = argument;
|
||||
|
||||
@@ -113,7 +148,7 @@ namespace Contralto.IO
|
||||
// sheets.
|
||||
//
|
||||
Log.Write(LogComponent.DoverROS, "ExternalCommand1 written {0}", argument);
|
||||
if (lastPrintRequest && (_externalCommand1 & 0x1) == 0)
|
||||
if (lastPrintRequest && (_externalCommand1 & 0x1) != 0)
|
||||
{
|
||||
PrintRequest();
|
||||
}
|
||||
@@ -121,6 +156,7 @@ namespace Contralto.IO
|
||||
|
||||
case AdapterCommand.ExternalCommand2:
|
||||
_videoGate = argument;
|
||||
Log.Write(LogComponent.DoverROS, "VideoGate set to {0}", argument);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -164,8 +200,6 @@ namespace Contralto.IO
|
||||
value |= (_local ? 0x2000 : 0);
|
||||
value |= (_beamEnable ? 0x1000 : 0);
|
||||
value |= (_statusBeamOn ? 0x0800 : 0);
|
||||
|
||||
//Log.Write(LogComponent.DoverROS, "ROS word 0 bits 0-15: {0}", Conversion.ToOctal(value));
|
||||
break;
|
||||
|
||||
case 1:
|
||||
@@ -255,6 +289,24 @@ namespace Contralto.IO
|
||||
//
|
||||
value |= (_lineCount << 12);
|
||||
value |= _videoGate;
|
||||
|
||||
//
|
||||
// LineCount is not well documented anywhere in the hardware documentation
|
||||
// available. It is related to the motion of the laser as it sweeps across
|
||||
// the page. The source code for Spruce (SprucePrinDover.bcpl)
|
||||
// has this to say:
|
||||
// "A check, designed chiefly for Dover, verifies that the laser is on and the polygon is
|
||||
// scanning (SOS/EOS are seeing things), by making sure that the low four bits of the line
|
||||
// count (indicated in status word) are changing. The code reads the line count, waits
|
||||
// approximately the time needed for four scan lines (generous margin), then reads the
|
||||
// count again, reporting a problem if the values are equal."
|
||||
//
|
||||
|
||||
//
|
||||
// Indeed, if this value does not increment, Spruce fails with a
|
||||
// "Laser appears to be off" error.
|
||||
// We fudge this here.
|
||||
_lineCount++;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
@@ -270,9 +322,7 @@ namespace Contralto.IO
|
||||
// 8 - LS4 (adequate paper in tray)
|
||||
// 11 - LaserOn
|
||||
// 13 - ReadyTemp
|
||||
value |= 0x214;
|
||||
|
||||
Log.Write(LogComponent.DoverROS, "Dover bits 0-15: {0}", Conversion.ToOctal(value));
|
||||
value |= 0x0094;
|
||||
break;
|
||||
|
||||
case 9:
|
||||
@@ -284,8 +334,7 @@ namespace Contralto.IO
|
||||
// These are:
|
||||
// 5 - ACMonitor
|
||||
// 13 - LS24 & LS31
|
||||
|
||||
value |= 0x2004;
|
||||
value |= 0x0404;
|
||||
|
||||
Log.Write(LogComponent.DoverROS, "Dover bits 16-31: {0}", Conversion.ToOctal(value));
|
||||
break;
|
||||
@@ -302,6 +351,7 @@ namespace Contralto.IO
|
||||
|
||||
default:
|
||||
Log.Write(LogComponent.DoverROS, "Unhandled ROS status word {0}", wordNumber);
|
||||
value = 0xffff;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -310,301 +360,388 @@ namespace Contralto.IO
|
||||
|
||||
private void PrintRequest()
|
||||
{
|
||||
switch(_state)
|
||||
switch (_state)
|
||||
{
|
||||
case PrintState.ColdStart:
|
||||
|
||||
case PrintState.Idle:
|
||||
if (!_printMode)
|
||||
{
|
||||
_state = PrintState.ColdStart;
|
||||
_printMode = true;
|
||||
_sendVideo = false;
|
||||
_sendVideo = true;
|
||||
_keepGoing = false;
|
||||
_runoutCount = 0;
|
||||
|
||||
// Queue a 250ms event to fire CS-5(0).
|
||||
// and 990ms item to cancel printing if a second
|
||||
// print-request isn't received.
|
||||
_innerLoopState = InnerLoopState.CS5;
|
||||
_innerLoopEvent.TimestampNsec = (ulong)(120 * Conversion.MsecToNsec);
|
||||
_system.Scheduler.Schedule(_innerLoopEvent);
|
||||
ClearPageRaster();
|
||||
|
||||
Log.Write(LogComponent.DoverROS, "Cold Start initialized at {0}ms -- CS-5(0) in 250ms.", _system.Scheduler.CurrentTimeNsec * Conversion.NsecToMsec);
|
||||
//
|
||||
// Calculate a few things based on ROS parameters
|
||||
//
|
||||
// PageSyncDelay is (4096-n/i) where n is the number of
|
||||
// scan-lines to pass up after receiving PageSync from the engine
|
||||
// before starting SendVideo. i is 1 for Dover II, and 4 for older
|
||||
// ones.
|
||||
// We assume a Dover I here.
|
||||
_sendVideoStartScanline = 4 * (4096 - _pageSyncDelay);
|
||||
|
||||
//
|
||||
// VideoGate is (4096-n/4) where n is the number of scan-lines to
|
||||
// pass up after SendVideo starts before stopping SendVideo.
|
||||
_sendVideoEndScanline = 4 * (4096 - _videoGate) + _sendVideoStartScanline;
|
||||
|
||||
Log.Write(LogComponent.DoverROS, "SendVideo start {0}, end {1}",
|
||||
_sendVideoStartScanline,
|
||||
_sendVideoEndScanline);
|
||||
|
||||
//
|
||||
// Start printing engine running.
|
||||
//
|
||||
_printEngineEvent.TimestampNsec = _printEngineTimestepInterval;
|
||||
_system.Scheduler.Schedule(_printEngineEvent);
|
||||
_printEngineTimestep = -375; // Start 250ms before the first CS-5
|
||||
|
||||
//
|
||||
// Start output
|
||||
//
|
||||
InitializePrintOutput();
|
||||
|
||||
Log.Write(LogComponent.DoverROS, "Cold Start initialized. Engine started.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Write(LogComponent.DoverROS, "PrintRequest received in cold start at {0}ms.", _system.Scheduler.CurrentTimeNsec * Conversion.NsecToMsec);
|
||||
_keepGoing = true;
|
||||
Log.Write(LogComponent.DoverROS, "Unexpected PrintRequest with PrintMode active during Idle.");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PrintState.ColdStart:
|
||||
Log.Write(LogComponent.DoverROS, "PrintRequest received in cold start.");
|
||||
_keepGoing = true;
|
||||
break;
|
||||
|
||||
case PrintState.InnerLoop:
|
||||
if (!_printMode)
|
||||
{
|
||||
// PrintRequest too late.
|
||||
Log.Write(LogComponent.DoverROS, "PrintRequest too late. Ignoring.");
|
||||
Log.Write(LogComponent.DoverROS, "PrintRequest too late during Inner Loop. Ignoring.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_innerLoopState != InnerLoopState.Idle)
|
||||
{
|
||||
Log.Write(LogComponent.DoverROS, "PrintRequest received in inner loop.");
|
||||
_keepGoing = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// Currently idle: Kick off the first round of the inner loop.
|
||||
// Queue a PageSyncDelay (250ms) event to pulse SendVideo
|
||||
// after the pulse, gather video raster from Orbit
|
||||
//
|
||||
Log.Write(LogComponent.DoverROS, "PrintRequest received, starting inner loop.");
|
||||
_innerLoopState = InnerLoopState.CS5;
|
||||
_innerLoopEvent.TimestampNsec = 120 * Conversion.MsecToNsec;
|
||||
_system.Scheduler.Schedule(_innerLoopEvent);
|
||||
}
|
||||
Log.Write(LogComponent.DoverROS, "PrintRequest during inner loop. Continuing.");
|
||||
_keepGoing = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case PrintState.Runout:
|
||||
Log.Write(LogComponent.DoverROS, "Runout.");
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void InnerLoopCallback(ulong timestampNsec, ulong delta, object context)
|
||||
{
|
||||
switch(_innerLoopState)
|
||||
{
|
||||
case InnerLoopState.CS5:
|
||||
_countH = false;
|
||||
_sendVideo = false;
|
||||
|
||||
// Keep SendVideo low for 125ms
|
||||
_innerLoopState = InnerLoopState.SendVideo;
|
||||
_innerLoopEvent.TimestampNsec = 125 * Conversion.MsecToNsec;
|
||||
_system.Scheduler.Schedule(_innerLoopEvent);
|
||||
|
||||
Log.Write(LogComponent.DoverROS, "Inner loop: CS5");
|
||||
break;
|
||||
|
||||
case InnerLoopState.SendVideo:
|
||||
_sendVideo = true;
|
||||
|
||||
_innerLoopState = InnerLoopState.ReadBands;
|
||||
_readBands = 0;
|
||||
|
||||
// time for one band of 16 scanlines to be read (approx.)
|
||||
_innerLoopEvent.TimestampNsec = (ulong)(0.2 * Conversion.MsecToNsec);
|
||||
_system.Scheduler.Schedule(_innerLoopEvent);
|
||||
|
||||
Log.Write(LogComponent.DoverROS, "Inner loop: SendVideo");
|
||||
break;
|
||||
|
||||
case InnerLoopState.ReadBands:
|
||||
// Assume 3000 scanlines for an 8.5" sheet of paper at 350dpi.
|
||||
// If the Orbit is allowing us to read the output buffer then
|
||||
// we will do so, otherwise we emit nothing.
|
||||
if (_readBands > 3000)
|
||||
if (!_printMode)
|
||||
{
|
||||
if (_state == PrintState.ColdStart)
|
||||
{
|
||||
_innerLoopState = InnerLoopState.ColdStartEnd;
|
||||
}
|
||||
else
|
||||
{
|
||||
_innerLoopState = InnerLoopState.CountH;
|
||||
}
|
||||
// PrintRequest too late.
|
||||
Log.Write(LogComponent.DoverROS, "PrintRequest too late during Runout. Ignoring.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_system.OrbitController.SLOTTAKE)
|
||||
Log.Write(LogComponent.DoverROS, "PrintRequest during Runout. Moving to Inner Loop.");
|
||||
_state = PrintState.InnerLoop;
|
||||
_keepGoing = true;
|
||||
_runoutCount = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializePrintOutput()
|
||||
{
|
||||
//
|
||||
// Select the appropriate output based on the configuration.
|
||||
// For now it's either PDF or nothing.
|
||||
//
|
||||
if (Configuration.EnablePrinting)
|
||||
{
|
||||
_pageOutput = new PdfPageSink();
|
||||
}
|
||||
else
|
||||
{
|
||||
_pageOutput = new NullPageSink();
|
||||
}
|
||||
|
||||
_pageOutput.StartDoc();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is invoked for every scanline that passes under the virtual print path.
|
||||
/// At each scanline flags are updated and raster data is pulled from the Orbit and
|
||||
/// copied to the page as necessary.
|
||||
/// </summary>
|
||||
/// <param name="timestampNsec"></param>
|
||||
/// <param name="delta"></param>
|
||||
/// <param name="context"></param>
|
||||
private void PrintEngineCallback(ulong timestampNsec, ulong delta, object context)
|
||||
{
|
||||
Log.Write(LogComponent.DoverROS, "Scanline {0} (sendvideo {1})", _printEngineTimestep, _sendVideo);
|
||||
switch (_state)
|
||||
{
|
||||
case PrintState.ColdStart:
|
||||
{
|
||||
// Go through the motions but don't rasterize anything
|
||||
//
|
||||
// The Cold-start loop starts 250ms before the first CS-5 signal.
|
||||
if (_printEngineTimestep >= 0 && _printEngineTimestep < _sendVideoStartScanline)
|
||||
{
|
||||
// Read in 16 scanlines of data -- this is 256x16 words
|
||||
for (int y = 0; y < 16; y++)
|
||||
_sendVideo = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_sendVideo = true;
|
||||
}
|
||||
|
||||
|
||||
if (_printEngineTimestep >= 0 &&
|
||||
_printEngineTimestep < _sendVideoEndScanline)
|
||||
{
|
||||
// Pull rasters one scanline at a time out of Orbit,
|
||||
// since we're in cold-start, these are discarded.
|
||||
if (_system.OrbitController.SLOTTAKE)
|
||||
{
|
||||
// Read in one scanline of data -- this is 256 words
|
||||
for (int x = 0; x < 256 - _system.OrbitController.FA; x++)
|
||||
{
|
||||
ushort word = _system.OrbitController.GetOutputDataROS();
|
||||
|
||||
_pageData[(_readBands + y) * 512 + x * 2] = (byte)(word & 0xff);
|
||||
_pageData[(_readBands + y) * 512 + x * 2 + 1] = (byte)(word >> 8);
|
||||
|
||||
|
||||
// The assumption is that during this phase the Alto will just
|
||||
// be sending zeroes, log if this assumption does not hold...
|
||||
//
|
||||
if (word != 0)
|
||||
{
|
||||
Log.Write(LogComponent.DoverROS, "Read non-zero orbit data during cold-start {0}", Conversion.ToOctal(word));
|
||||
}
|
||||
}
|
||||
|
||||
Log.Write(LogComponent.DoverROS, "Read cold-start band {0}", _readBands);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Nothing right now
|
||||
Log.Write(LogComponent.DoverROS, "No bands available from Orbit.");
|
||||
}
|
||||
|
||||
Log.Write(LogComponent.DoverROS, "Read bands {0}", _readBands);
|
||||
_readBands++;
|
||||
}
|
||||
|
||||
if (_printEngineTimestep >= 3136)
|
||||
{
|
||||
// After appx. 896ms (3136 scanlines) the first Count-H is raised.
|
||||
_countH = true;
|
||||
}
|
||||
|
||||
_printEngineTimestep++;
|
||||
|
||||
if (_printEngineTimestep >= 3500)
|
||||
{
|
||||
// End of cold-start "page."
|
||||
_readBands = 0;
|
||||
_printEngineTimestep = 0;
|
||||
_countH = false;
|
||||
ClearPageRaster();
|
||||
|
||||
if (_keepGoing)
|
||||
{
|
||||
//
|
||||
// We got a PrintRequest during ColdStart, so we'll continue to the next page.
|
||||
//
|
||||
Log.Write(LogComponent.DoverROS, "End of Cold Start, switching to Inner Loop.");
|
||||
_state = PrintState.InnerLoop;
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// No PrintRequest, we will switch to Runout and begin shutting down.
|
||||
//
|
||||
Log.Write(LogComponent.DoverROS, "End of Cold Start, switching to Runout.");
|
||||
_state = PrintState.Runout;
|
||||
}
|
||||
|
||||
_keepGoing = false;
|
||||
}
|
||||
|
||||
_printEngineEvent.TimestampNsec = _printEngineTimestepInterval;
|
||||
_system.Scheduler.Schedule(_printEngineEvent);
|
||||
}
|
||||
break;
|
||||
|
||||
case PrintState.InnerLoop:
|
||||
{
|
||||
//
|
||||
// Inner loop starts with CS-5 (timestep 0).
|
||||
// After which there is a delay (PageSyncDelay) before
|
||||
// SendVideo goes high and we can start reading raster data.
|
||||
if (_printEngineTimestep < _sendVideoStartScanline)
|
||||
{
|
||||
_sendVideo = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Nothing right now
|
||||
Log.Write(LogComponent.DoverROS, "No bands available from Orbit.");
|
||||
_sendVideo = true;
|
||||
}
|
||||
|
||||
_readBands += 16;
|
||||
|
||||
if (_readBands > 2500 && !_countH)
|
||||
if (_printEngineTimestep >= 0 &&
|
||||
_printEngineTimestep < _sendVideoEndScanline)
|
||||
{
|
||||
// Video gate : pull rasters one scanline at a time out of Orbit.
|
||||
if (_system.OrbitController.SLOTTAKE)
|
||||
{
|
||||
// Read in one scanline's worth of data
|
||||
int scanlineWordCount = 256 - _system.OrbitController.FA;
|
||||
for (int x = 0; x < scanlineWordCount; x++)
|
||||
{
|
||||
ushort word = _system.OrbitController.GetOutputDataROS();
|
||||
|
||||
int pageDataIndex = _readBands * scanlineWordCount * 2 + x * 2;
|
||||
_pageData[pageDataIndex] = (byte)(~word >> 8);
|
||||
_pageData[pageDataIndex + 1] = (byte)(~word & 0xff);
|
||||
}
|
||||
|
||||
Log.Write(LogComponent.DoverROS, "Read band {0}", _readBands);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Nothing right now
|
||||
Log.Write(LogComponent.DoverROS, "No bands available from Orbit.");
|
||||
}
|
||||
|
||||
_readBands++;
|
||||
}
|
||||
|
||||
if (_printEngineTimestep >= 3136)
|
||||
{
|
||||
// After appx. 896ms (3136 scanlines) Count-H is raised.
|
||||
_countH = true;
|
||||
Log.Write(LogComponent.DoverROS, "Enabling CountH");
|
||||
}
|
||||
|
||||
_printEngineTimestep++;
|
||||
|
||||
if (_printEngineTimestep >= 3500)
|
||||
{
|
||||
//
|
||||
// Send page rasters to the print output.
|
||||
//
|
||||
_pageOutput.AddPage(_pageData, _readBands, (256 - _system.OrbitController.FA) * 16);
|
||||
|
||||
ClearPageRaster();
|
||||
|
||||
// End of page.
|
||||
_countH = false;
|
||||
_readBands = 0;
|
||||
_printEngineTimestep = 0;
|
||||
|
||||
if (_keepGoing)
|
||||
{
|
||||
//
|
||||
// A PrintRequest was recieved during the Inner Loop, so we'll keep going to the next page.
|
||||
Log.Write(LogComponent.DoverROS, "End of Page, continuing in Inner Loop.");
|
||||
_state = PrintState.InnerLoop;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Write(LogComponent.DoverROS, "End of Page, switching to Runout.");
|
||||
_state = PrintState.Runout;
|
||||
}
|
||||
|
||||
_keepGoing = false;
|
||||
}
|
||||
|
||||
_printEngineEvent.TimestampNsec = _printEngineTimestepInterval;
|
||||
_system.Scheduler.Schedule(_printEngineEvent);
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case PrintState.Runout:
|
||||
{
|
||||
//
|
||||
// SendVideo still gets toggled during Runout. CountH is
|
||||
// toggled by paper moving past a sensor, which does not happen
|
||||
// during runout.
|
||||
//
|
||||
if (_printEngineTimestep < _sendVideoStartScanline)
|
||||
{
|
||||
_sendVideo = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_sendVideo = true;
|
||||
}
|
||||
|
||||
_printEngineTimestep++;
|
||||
|
||||
if (_printEngineTimestep >= 3500)
|
||||
{
|
||||
|
||||
// End of runout cycle.
|
||||
_countH = false;
|
||||
_printEngineTimestep = 0;
|
||||
|
||||
if (_keepGoing)
|
||||
{
|
||||
Log.Write(LogComponent.DoverROS, "End of Runout cycle {0}, continuing in Inner Loop.", _runoutCount);
|
||||
_state = PrintState.InnerLoop;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Write(LogComponent.DoverROS, "End of Runout cycle {0}.", _runoutCount);
|
||||
_state = PrintState.Runout;
|
||||
}
|
||||
|
||||
_keepGoing = false;
|
||||
|
||||
_runoutCount++;
|
||||
}
|
||||
|
||||
if (_runoutCount > 7)
|
||||
{
|
||||
// Just shut off.
|
||||
_state = PrintState.Idle;
|
||||
_printMode = false;
|
||||
_countH = false;
|
||||
_sendVideo = false;
|
||||
|
||||
//
|
||||
// Finish the output.
|
||||
//
|
||||
_pageOutput.EndDoc();
|
||||
|
||||
Log.Write(LogComponent.DoverROS, "Runout: shutting down, switching to Idle state. Output completed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// Go around for another Runout cycle.
|
||||
_printEngineEvent.TimestampNsec = _printEngineTimestepInterval;
|
||||
_system.Scheduler.Schedule(_printEngineEvent);
|
||||
}
|
||||
}
|
||||
|
||||
// time for one band of 16 scanlines to be read (approx.)
|
||||
_innerLoopEvent.TimestampNsec = (ulong)(0.2 * Conversion.MsecToNsec);
|
||||
_system.Scheduler.Schedule(_innerLoopEvent);
|
||||
break;
|
||||
|
||||
case InnerLoopState.CountH:
|
||||
_countH = false;
|
||||
|
||||
if (_keepGoing)
|
||||
{
|
||||
// PrintRequest during ColdStart -- move to Inner loop
|
||||
_keepGoing = false;
|
||||
_state = PrintState.InnerLoop;
|
||||
_innerLoopState = InnerLoopState.CS5;
|
||||
Log.Write(LogComponent.DoverROS, "PrintRequest during ColdStart -- moving to inner loop.");
|
||||
}
|
||||
else
|
||||
{
|
||||
_innerLoopState = InnerLoopState.Idle;
|
||||
Log.Write(LogComponent.DoverROS, "No PrintRequest during ColdStart -- idling.");
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
|
||||
case InnerLoopState.ColdStartEnd:
|
||||
if (_keepGoing)
|
||||
{
|
||||
//
|
||||
// Got a PrintRequest during cold start, start the inner loop.
|
||||
//
|
||||
_keepGoing = false;
|
||||
_state = PrintState.InnerLoop;
|
||||
_innerLoopState = InnerLoopState.CS5;
|
||||
Log.Write(LogComponent.DoverROS, "Inner loop: CountH -- continuing");
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// No Print Request; idle and shut down.
|
||||
//
|
||||
_innerLoopState = InnerLoopState.Idle;
|
||||
Log.Write(LogComponent.DoverROS, "Inner loop: CountH -- idling");
|
||||
}
|
||||
|
||||
// Debug: Put picture in image
|
||||
/*
|
||||
BitmapData data = _pageBuffer.LockBits(_pageRect, ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
|
||||
|
||||
IntPtr ptr = data.Scan0;
|
||||
System.Runtime.InteropServices.Marshal.Copy(_pageData, 0, ptr, _pageData.Length);
|
||||
|
||||
_pageBuffer.UnlockBits(data);
|
||||
|
||||
EncoderParameters p = new EncoderParameters(1);
|
||||
p.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
|
||||
|
||||
_pageBuffer.Save(String.Format("c:\\temp\\raster{0}.png", _rasterNum++), GetEncoderForFormat(ImageFormat.Png), p);
|
||||
*/
|
||||
|
||||
_innerLoopEvent.TimestampNsec = (ulong)(150 * Conversion.MsecToNsec);
|
||||
_system.Scheduler.Schedule(_innerLoopEvent);
|
||||
|
||||
_innerLoopEvent.TimestampNsec = (ulong)(150 * Conversion.MsecToNsec);
|
||||
_system.Scheduler.Schedule(_innerLoopEvent);
|
||||
break;
|
||||
|
||||
case InnerLoopState.Idle:
|
||||
_countH = false;
|
||||
_sendVideo = false;
|
||||
_printMode = false;
|
||||
_state = PrintState.ColdStart;
|
||||
Log.Write(LogComponent.DoverROS, "Inner loop: Idle");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void ColdStartCallback(ulong timestampNsec, ulong delta, object context)
|
||||
private void ClearPageRaster()
|
||||
{
|
||||
switch (_coldStartState)
|
||||
//
|
||||
// reset to white
|
||||
//
|
||||
for(int i=0;i<_pageData.Length;i++)
|
||||
{
|
||||
case ColdStartState.SendVideoLow:
|
||||
_sendVideo = false;
|
||||
|
||||
// Keep SendVideo low for 125ms
|
||||
_coldStartState = ColdStartState.SendVideoHigh;
|
||||
_cs5Event.TimestampNsec = 125 * Conversion.MsecToNsec;
|
||||
_system.Scheduler.Schedule(_cs5Event);
|
||||
|
||||
Log.Write(LogComponent.DoverROS, "Cold start: toggle SendVideo low");
|
||||
break;
|
||||
|
||||
case ColdStartState.SendVideoHigh:
|
||||
_sendVideo = true;
|
||||
|
||||
_coldStartState = ColdStartState.Timeout;
|
||||
_cs5Event.TimestampNsec = 800 * Conversion.MsecToNsec;
|
||||
_system.Scheduler.Schedule(_cs5Event);
|
||||
|
||||
Log.Write(LogComponent.DoverROS, "Cold start: toggle SendVideo high");
|
||||
break;
|
||||
|
||||
case ColdStartState.Timeout:
|
||||
if (_state == PrintState.ColdStart)
|
||||
{
|
||||
// Never moved from ColdStart, Alto never sent another PageRequest.
|
||||
Log.Write(LogComponent.DoverROS, "Cold start timeout. Aborting.");
|
||||
_sendVideo = false;
|
||||
_printMode = false;
|
||||
}
|
||||
break;
|
||||
_pageData[i] = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
private ImageCodecInfo GetEncoderForFormat(ImageFormat format)
|
||||
{
|
||||
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
|
||||
|
||||
foreach (ImageCodecInfo codec in codecs)
|
||||
{
|
||||
if (codec.FormatID == format.Guid)
|
||||
{
|
||||
return codec;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private enum PrintState
|
||||
{
|
||||
ColdStart = 0,
|
||||
Idle = 0,
|
||||
ColdStart,
|
||||
InnerLoop,
|
||||
Runout
|
||||
}
|
||||
|
||||
private enum ColdStartState
|
||||
{
|
||||
SendVideoLow,
|
||||
SendVideoHigh,
|
||||
Timeout
|
||||
}
|
||||
|
||||
private enum InnerLoopState
|
||||
{
|
||||
Idle = 0,
|
||||
CS5,
|
||||
SendVideo,
|
||||
ReadBands,
|
||||
CountH,
|
||||
ColdStartEnd,
|
||||
}
|
||||
|
||||
private PrintState _state;
|
||||
private InnerLoopState _innerLoopState;
|
||||
private ColdStartState _coldStartState;
|
||||
|
||||
private bool _keepGoing;
|
||||
private int _readBands;
|
||||
@@ -653,8 +790,8 @@ namespace Contralto.IO
|
||||
private bool _switch4;
|
||||
|
||||
// ID and serial number
|
||||
private ushort _id;
|
||||
private ushort _serialNumber;
|
||||
private ushort _id = 0;
|
||||
private ushort _serialNumber = 0;
|
||||
|
||||
//
|
||||
// Dover specific status that we care to report.
|
||||
@@ -671,15 +808,35 @@ namespace Contralto.IO
|
||||
//
|
||||
private bool _countH;
|
||||
|
||||
/// <summary>
|
||||
/// Number of passes through the Runout state.
|
||||
/// </summary>
|
||||
private int _runoutCount;
|
||||
|
||||
|
||||
// Events to drive the print state machine
|
||||
//
|
||||
private Event _cs5Event;
|
||||
private Event _innerLoopEvent;
|
||||
//
|
||||
private Event _printEngineEvent;
|
||||
|
||||
private byte[] _pageData = new byte[4096 * 512];
|
||||
private Rectangle _pageRect = new Rectangle(0, 0, 4096, 4096);
|
||||
private Bitmap _pageBuffer;
|
||||
private int _printEngineTimestep;
|
||||
private int _sendVideoStartScanline;
|
||||
private int _sendVideoEndScanline;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The timeslice for a single engine step (one scanline).
|
||||
/// There are (in theory) 2975 scanlines per 8.5" page (at 350dpi) which moves
|
||||
/// through the engine in 850ms.
|
||||
///
|
||||
/// The cycle time for an entire page is 1000ms, which we pretend
|
||||
/// is 3500 scanline times.
|
||||
///
|
||||
/// </summary>
|
||||
private ulong _printEngineTimestepInterval = (ulong)((1000.0 / 3500.0) * Conversion.MsecToNsec);
|
||||
|
||||
private byte[] _pageData = new byte[3500 * 512];
|
||||
private int _rasterNum;
|
||||
|
||||
private IPageSink _pageOutput;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
using Contralto.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
/*
|
||||
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;
|
||||
|
||||
namespace Contralto.IO
|
||||
{
|
||||
@@ -17,12 +29,10 @@ namespace Contralto.IO
|
||||
public OrbitController(AltoSystem system)
|
||||
{
|
||||
_system = system;
|
||||
|
||||
_ros = new DoverROS(system);
|
||||
|
||||
_refreshEvent = new Event(_refreshInterval, null, RefreshCallback);
|
||||
|
||||
Reset();
|
||||
Reset();
|
||||
}
|
||||
|
||||
public bool RefreshTimerExpired
|
||||
@@ -48,10 +58,7 @@ namespace Contralto.IO
|
||||
public void Reset()
|
||||
{
|
||||
//
|
||||
// A Reset performs at least the following functions:
|
||||
// FA <- 0, SLOTTAKE <- 0, band buffer A is assigned
|
||||
// to the image buffer, the status and control dialogs
|
||||
// with the adapter are reset.
|
||||
// Reset to power-up (or reboot) defaults.
|
||||
//
|
||||
_fa = 0;
|
||||
_slottake = false;
|
||||
@@ -70,10 +77,46 @@ namespace Contralto.IO
|
||||
_goAway = false;
|
||||
_behind = false;
|
||||
|
||||
_stableROS = true;
|
||||
_badROS = false;
|
||||
|
||||
Log.Write(LogComponent.Orbit, "Orbit system reset.");
|
||||
UpdateWakeup();
|
||||
}
|
||||
|
||||
private void OrbitReset()
|
||||
{
|
||||
//
|
||||
// A Reset performs at least the following functions:
|
||||
// FA <- 0, SLOTTAKE <- 0, band buffer A is assigned
|
||||
// to the image buffer, the status and control dialogs
|
||||
// with the adapter are reset.
|
||||
//
|
||||
_fa = 0;
|
||||
_slottake = false;
|
||||
|
||||
_image = _a;
|
||||
_output = _b;
|
||||
_incon = false;
|
||||
|
||||
_outputY = 0;
|
||||
_outputX = 0;
|
||||
|
||||
// Cleared "by reseting [sic] Orbit"
|
||||
_iacs = false;
|
||||
|
||||
// Cleared by a buffer switch or an Orbit reset
|
||||
_goAway = false;
|
||||
|
||||
_refresh = false;
|
||||
|
||||
_behind = false;
|
||||
|
||||
_stableROS = true;
|
||||
_badROS = false;
|
||||
|
||||
Log.Write(LogComponent.Orbit, "Orbit reset.");
|
||||
Log.Write(LogComponent.Orbit, "Orbit reset.");
|
||||
UpdateWakeup();
|
||||
}
|
||||
|
||||
public bool Wakeup
|
||||
@@ -86,17 +129,36 @@ namespace Contralto.IO
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateWakeup()
|
||||
{
|
||||
if (_system.CPU == null)
|
||||
return;
|
||||
|
||||
if (Wakeup)
|
||||
{
|
||||
_system.CPU.WakeupTask(CPU.TaskType.Orbit);
|
||||
Log.Write(LogComponent.Orbit, "Orbit wakeup.");
|
||||
}
|
||||
else
|
||||
{
|
||||
_system.CPU.BlockTask(CPU.TaskType.Orbit);
|
||||
Log.Write(LogComponent.Orbit, "Orbit block.");
|
||||
}
|
||||
}
|
||||
|
||||
public void STARTF(ushort value)
|
||||
{
|
||||
// Kick off the refresh timer if it's not already pending
|
||||
|
||||
// Wake up task, etc.
|
||||
_run = true;
|
||||
_system.CPU.WakeupTask(CPU.TaskType.Orbit);
|
||||
|
||||
// "IACS is cleared by StartIO(4)"
|
||||
_iacs = false;
|
||||
|
||||
// per microcode, GOAWAY is cleared
|
||||
_goAway = false;
|
||||
|
||||
if (!_refreshRunning)
|
||||
{
|
||||
_refreshRunning = true;
|
||||
@@ -105,15 +167,16 @@ namespace Contralto.IO
|
||||
}
|
||||
|
||||
Log.Write(LogComponent.Orbit, "Orbit started.");
|
||||
|
||||
UpdateWakeup();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_run = false;
|
||||
|
||||
_system.CPU.BlockTask(CPU.TaskType.Orbit);
|
||||
|
||||
//Log.Write(LogComponent.Orbit, "Orbit blocked.");
|
||||
Log.Write(LogComponent.Orbit, "Orbit stopped.");
|
||||
UpdateWakeup();
|
||||
}
|
||||
|
||||
public void Control(ushort value)
|
||||
@@ -133,7 +196,7 @@ namespace Contralto.IO
|
||||
//
|
||||
if ((value & 0x1) != 0)
|
||||
{
|
||||
Reset();
|
||||
OrbitReset();
|
||||
}
|
||||
|
||||
//
|
||||
@@ -156,11 +219,9 @@ namespace Contralto.IO
|
||||
{
|
||||
_fa = auxControl;
|
||||
|
||||
_outputY = _fa;
|
||||
|
||||
// "The setting of FA provided by the Alto is examined only when
|
||||
// Orbit reads out the last 16 bits of a scanline..."
|
||||
// (So we *should* leave _outputY alone here).
|
||||
// (So we should leave _outputY alone here).
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -173,7 +234,6 @@ namespace Contralto.IO
|
||||
if ((value & 0x8) != 0)
|
||||
{
|
||||
_goAway = true;
|
||||
_system.CPU.BlockTask(CPU.TaskType.Orbit);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -195,11 +255,13 @@ namespace Contralto.IO
|
||||
if ((value & 0xc0) == 0xc0)
|
||||
{
|
||||
_slottake = true;
|
||||
}
|
||||
}
|
||||
|
||||
Log.Write(LogComponent.Orbit,
|
||||
"Set Control: aux {0}, reset {1} refresh {2} which {3} goaway {4} behind {5} slottake {6}",
|
||||
auxControl, (value & 0x1) != 0, (value & 0x2) != 0, (value & 0x4) != 0, _goAway, _behind, _slottake);
|
||||
|
||||
UpdateWakeup();
|
||||
}
|
||||
|
||||
public void SetHeight(ushort value)
|
||||
@@ -258,10 +320,10 @@ namespace Contralto.IO
|
||||
_cx = _x;
|
||||
_cy = _y;
|
||||
|
||||
_scanlines = (_x + (_width + 1) < 16) ? (_width + 1): 16 - _x;
|
||||
|
||||
Log.Write(LogComponent.Orbit,
|
||||
"Set DBCWidth: DeltaBC {0}, Width {1}", _deltaBC, _width);
|
||||
|
||||
UpdateWakeup();
|
||||
}
|
||||
|
||||
public void WriteFontData(ushort value)
|
||||
@@ -277,10 +339,13 @@ namespace Contralto.IO
|
||||
if (!_iacs)
|
||||
{
|
||||
Log.Write(LogType.Error,
|
||||
LogComponent.Orbit, "Unxpected OrbitFontData while IACS false.");
|
||||
LogComponent.Orbit, "Unexpected OrbitFontData while IACS false.");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Write(LogComponent.Orbit,
|
||||
"Font Data: {0}", Conversion.ToOctal(value));
|
||||
|
||||
//
|
||||
// We insert the word one bit at a time; this is more costly
|
||||
// computationally but it's a ton simpler than dealing with word
|
||||
@@ -290,15 +355,16 @@ namespace Contralto.IO
|
||||
if (_firstWord)
|
||||
{
|
||||
startBit = 15 - _deltaBC;
|
||||
_firstWord = false;
|
||||
}
|
||||
|
||||
|
||||
for(int i = startBit; i >=0 ;i--)
|
||||
{
|
||||
int bitValue = (value & (0x1 << i)) == 0 ? 0 : 1;
|
||||
|
||||
if (bitValue != 0 && _cy < 4096) // clip to end of band
|
||||
{
|
||||
SetImageRasterBit(_cx, _cy, bitValue);
|
||||
SetImageRasterBit(_cx, _cy);
|
||||
}
|
||||
|
||||
_cy++;
|
||||
@@ -316,15 +382,24 @@ namespace Contralto.IO
|
||||
{
|
||||
_iacs = false;
|
||||
|
||||
Log.Write(LogComponent.DoverROS, "Image band completed.");
|
||||
//
|
||||
// A height of 1024 is used by the refresh microcode to refresh the Orbit memory.
|
||||
// We log this separately to make debugging more clear.
|
||||
//
|
||||
if (_height == 1024)
|
||||
{
|
||||
Log.Write(LogComponent.Orbit, "Image band completed (refresh).");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Write(LogComponent.Orbit, "Image band completed.");
|
||||
}
|
||||
|
||||
UpdateWakeup();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_firstWord = false;
|
||||
Log.Write(LogComponent.Orbit,
|
||||
"Font Data: {0}", Conversion.ToOctal(value));
|
||||
}
|
||||
|
||||
public void WriteInkData(ushort value)
|
||||
@@ -387,7 +462,7 @@ namespace Contralto.IO
|
||||
public ushort GetDBCWidth()
|
||||
{
|
||||
Log.Write(LogComponent.Orbit,
|
||||
"Delta DBCWidth {0},{1}", _deltaBCEnd % 16, _width);
|
||||
"Delta DBCWidth {0},{1}", _deltaBCEnd % 16, _width);
|
||||
|
||||
return (ushort)((_deltaBCEnd << 12) | (_width & 0xfff));
|
||||
}
|
||||
@@ -417,7 +492,7 @@ namespace Contralto.IO
|
||||
}
|
||||
|
||||
Log.Write(LogComponent.Orbit,
|
||||
"OrbitStatus {0}", Conversion.ToOctal(result));
|
||||
"OrbitStatus, address {0}, {1}", _statusAddress, Conversion.ToOctal(result));
|
||||
|
||||
return (ushort)result;
|
||||
}
|
||||
@@ -425,7 +500,7 @@ namespace Contralto.IO
|
||||
private ushort GetOutputData()
|
||||
{
|
||||
//
|
||||
// Basically, the function OrbitOutputData reads 16 bits from the output buffer
|
||||
// "Basically, the function OrbitOutputData reads 16 bits from the output buffer
|
||||
// into the Alto.
|
||||
//
|
||||
// The programmer (or emulator author) needs to be aware of two tricky details
|
||||
@@ -437,7 +512,7 @@ namespace Contralto.IO
|
||||
// The usual convention is to read out this one last word. This leaves the first
|
||||
// word of the new buffer in ROB.
|
||||
//
|
||||
// 2. The output band buffer will not be refreshed ... (not a concern here)
|
||||
// 2. The output band buffer will not be refreshed ... [not a concern here]"
|
||||
//
|
||||
|
||||
// Return the last value from ROB
|
||||
@@ -456,7 +531,7 @@ namespace Contralto.IO
|
||||
|
||||
if (_outputY == 256)
|
||||
{
|
||||
Log.Write(LogComponent.DoverROS,
|
||||
Log.Write(LogComponent.Orbit,
|
||||
"OutputData - scanline {0} completed", _outputX);
|
||||
|
||||
_outputY = _fa;
|
||||
@@ -466,7 +541,7 @@ namespace Contralto.IO
|
||||
{
|
||||
// Done reading output band -- swap buffers!
|
||||
|
||||
Log.Write(LogComponent.DoverROS,
|
||||
Log.Write(LogComponent.Orbit,
|
||||
"OutputData - buffer read completed.");
|
||||
|
||||
SwapBuffers();
|
||||
@@ -494,23 +569,22 @@ namespace Contralto.IO
|
||||
// the Alto is now behind the consumer...
|
||||
//
|
||||
if (!_goAway)
|
||||
{
|
||||
Console.WriteLine("BEHIND");
|
||||
{
|
||||
_behind = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// "The buffer switch will turn off GOAWAY and consequently
|
||||
// allow wakeups again."
|
||||
_goAway = false;
|
||||
_system.CPU.WakeupTask(CPU.TaskType.Orbit);
|
||||
_goAway = false;
|
||||
UpdateWakeup();
|
||||
}
|
||||
|
||||
// Flip buffer bit
|
||||
_incon = !_incon;
|
||||
}
|
||||
|
||||
private void SetImageRasterBit(int x, int y, int bitValue)
|
||||
private void SetImageRasterBit(int x, int y)
|
||||
{
|
||||
// Pick out the word we're interested in
|
||||
int wordAddress = y >> 4;
|
||||
@@ -526,21 +600,22 @@ namespace Contralto.IO
|
||||
{
|
||||
_refresh = true;
|
||||
|
||||
//Log.Write(LogComponent.Orbit, "Refresh signal raised.");
|
||||
Log.Write(LogComponent.Orbit, "Refresh signal raised.");
|
||||
|
||||
if (_run)
|
||||
{
|
||||
_system.CPU.WakeupTask(CPU.TaskType.Orbit);
|
||||
|
||||
// do it again
|
||||
_refreshEvent.TimestampNsec = _refreshInterval;
|
||||
_system.Scheduler.Schedule(_refreshEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Log.Write(LogComponent.Orbit, "RUN deasserted, Refresh aborted.");
|
||||
Log.Write(LogComponent.Orbit, "RUN deasserted, Refresh aborted.");
|
||||
_refreshRunning = false;
|
||||
_refresh = false;
|
||||
}
|
||||
|
||||
UpdateWakeup();
|
||||
}
|
||||
|
||||
// Run status
|
||||
@@ -553,9 +628,7 @@ namespace Contralto.IO
|
||||
private int _width; // raster width of character
|
||||
private int _bitCount; // count of processed bits; used to calculate DeltaWC.
|
||||
private int _deltaBC; // bit of first word to start with
|
||||
private int _deltaBCEnd; // number of bits left in last word rasterized
|
||||
|
||||
private int _scanlines;
|
||||
private int _deltaBCEnd; // number of bits left in last word rasterized
|
||||
|
||||
// FA -- "First Address" of each scanline -- a value between
|
||||
// 0 and 255 indicating the starting word to be read by the
|
||||
@@ -630,7 +703,7 @@ namespace Contralto.IO
|
||||
private readonly ulong _refreshInterval = 2 * Conversion.MsecToNsec; // 2ms in nsec
|
||||
|
||||
//
|
||||
// The ROS we talk to to get raster onto a "page"
|
||||
// The ROS we talk to to get raster onto a page.
|
||||
//
|
||||
private DoverROS _ros;
|
||||
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
using Contralto.CPU;
|
||||
/*
|
||||
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.CPU;
|
||||
using Contralto.Logging;
|
||||
using Contralto.Memory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Contralto.IO
|
||||
{
|
||||
|
||||
43
Contralto/IO/Printing/IPageSink.cs
Normal file
43
Contralto/IO/Printing/IPageSink.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
namespace Contralto.IO.Printing
|
||||
{
|
||||
/// <summary>
|
||||
/// IPageSink defines the interface between a ROS device and physical output
|
||||
/// on the emulated host (be it real paper on a printer or PDF, Postscript, etc).
|
||||
/// </summary>
|
||||
public interface IPageSink
|
||||
{
|
||||
/// <summary>
|
||||
/// Starts a new document to be printed.
|
||||
/// </summary>
|
||||
void StartDoc();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new page to the output. This is provided as a byte array containing 'height' scanlines of the specified width.
|
||||
/// </summary>
|
||||
/// <param name="raster"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
void AddPage(byte[] raster, int width, int height);
|
||||
|
||||
/// <summary>
|
||||
/// Ends the document being printed.
|
||||
/// </summary>
|
||||
void EndDoc();
|
||||
}
|
||||
}
|
||||
44
Contralto/IO/Printing/NullPageSink.cs
Normal file
44
Contralto/IO/Printing/NullPageSink.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
namespace Contralto.IO.Printing
|
||||
{
|
||||
/// <summary>
|
||||
/// The NullPageSink: All pages go to the bit-bucket, for the true paperless office.
|
||||
/// </summary>
|
||||
public class NullPageSink : IPageSink
|
||||
{
|
||||
public NullPageSink()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void StartDoc()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void AddPage(byte[] rasters, int width, int height)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void EndDoc()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
127
Contralto/IO/Printing/PdfPageSink.cs
Normal file
127
Contralto/IO/Printing/PdfPageSink.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
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.IO;
|
||||
|
||||
using iTextSharp.text;
|
||||
using iTextSharp.text.pdf;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using Contralto.Logging;
|
||||
|
||||
namespace Contralto.IO.Printing
|
||||
{
|
||||
/// <summary>
|
||||
/// PdfPageSink takes output from the ROS and turns it into
|
||||
/// PDF documents in the PrintOutputPath folder.
|
||||
///
|
||||
/// This uses the iTextSharp PDF creation libraries to do the hard work.
|
||||
/// </summary>
|
||||
public class PdfPageSink : IPageSink
|
||||
{
|
||||
public PdfPageSink()
|
||||
{
|
||||
_pageImages = new List<Image>();
|
||||
}
|
||||
|
||||
public void StartDoc()
|
||||
{
|
||||
_pageImages.Clear();
|
||||
|
||||
try
|
||||
{
|
||||
// Start a new document.
|
||||
// All output to a Dover printer is letter-sized.
|
||||
_pdf = new Document(PageSize.LETTER);
|
||||
|
||||
string path = Path.Combine(
|
||||
Configuration.PrintOutputPath,
|
||||
String.Format("AltoDocument-{0}.pdf", DateTime.Now.ToString("yyyyMMdd-hhmmss")));
|
||||
|
||||
PdfWriter writer = PdfWriter.GetInstance(
|
||||
_pdf,
|
||||
new FileStream(path, FileMode.Create));
|
||||
|
||||
_pdf.Open();
|
||||
|
||||
// Let the Orbit deal with the margins.
|
||||
_pdf.SetMargins(0, 0, 0, 0);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
//
|
||||
// Most likely we couldn't create the output file; log the failure.
|
||||
// All output will be relegated to the bit bucket.
|
||||
//
|
||||
_pdf = null;
|
||||
|
||||
Log.Write(LogType.Error, LogComponent.DoverROS, "Failed to create output PDF. Error {0}", e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddPage(byte[] rasters, int width, int height)
|
||||
{
|
||||
if (_pdf != null)
|
||||
{
|
||||
Image pageImage = iTextSharp.text.Image.GetInstance(height, width, 1 /* greyscale */, 1 /* 1bpp */, rasters);
|
||||
pageImage.SetDpi(375, 375);
|
||||
pageImage.SetAbsolutePosition(0, 0);
|
||||
pageImage.RotationDegrees = 90;
|
||||
pageImage.ScaleToFit(_pdf.PageSize);
|
||||
|
||||
_pageImages.Add(pageImage);
|
||||
}
|
||||
}
|
||||
|
||||
public void EndDoc()
|
||||
{
|
||||
if (_pdf != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Grab the configuration here so that if some joker changes the configuration
|
||||
// while we're printing we don't do something weird.
|
||||
bool reversePageOrder = Configuration.ReversePageOrder;
|
||||
|
||||
// Actually write out the pages now, in the proper order.
|
||||
for (int i = 0; i < _pageImages.Count; i++)
|
||||
{
|
||||
_pdf.Add(_pageImages[reversePageOrder ? (_pageImages.Count - 1) - i : i]);
|
||||
_pdf.NewPage();
|
||||
}
|
||||
|
||||
_pdf.Close();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Something bad happened during creation, log an error.
|
||||
Log.Write(LogComponent.DoverROS, "Failed to create output PDF. Error {0}", e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of page images in this document.
|
||||
/// Since Alto software typically prints the document in reverse-page-order due to the way the
|
||||
/// Dover produces output, we need be able to produce the PDF in reverse order.
|
||||
/// This uses extra memory, (about 1.7mb per page printed.)
|
||||
/// </summary>
|
||||
private List<Image> _pageImages;
|
||||
|
||||
private Document _pdf;
|
||||
}
|
||||
}
|
||||
@@ -78,9 +78,8 @@ namespace Contralto.Memory
|
||||
_memoryCycle = 0;
|
||||
_memoryAddress = 0;
|
||||
_memoryDataLow = 0;
|
||||
_memoryDataHigh = 0;
|
||||
_firstWordStored = false;
|
||||
_firstWordRead = false;
|
||||
_memoryDataHigh = 0;
|
||||
_firstWord = false;
|
||||
_memoryOperationActive = false;
|
||||
_extendedMemoryReference = false;
|
||||
}
|
||||
@@ -186,7 +185,7 @@ namespace Contralto.Memory
|
||||
|
||||
case 6:
|
||||
// End of memory operation
|
||||
_memoryOperationActive = false;
|
||||
_memoryOperationActive = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -245,14 +244,18 @@ namespace Contralto.Memory
|
||||
}
|
||||
else
|
||||
{
|
||||
_memoryOperationActive = true;
|
||||
_firstWordStored = false;
|
||||
_firstWordRead = false;
|
||||
_memoryOperationActive = true;
|
||||
_firstWord = false;
|
||||
_memoryAddress = address;
|
||||
_extendedMemoryReference = extendedMemoryReference;
|
||||
_task = task;
|
||||
|
||||
//
|
||||
// Memory cycle 1 is the instruction in which a MAR<- is executed,
|
||||
// per the convention in the Alto HW ref.
|
||||
//
|
||||
_memoryCycle = 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -285,12 +288,12 @@ namespace Contralto.Memory
|
||||
throw new InvalidOperationException("Invalid ReadMD request during cycle 3 or 4 of memory operation.");
|
||||
|
||||
case 5:
|
||||
// Single word read
|
||||
// Single word read
|
||||
return _memoryDataLow;
|
||||
|
||||
case 6:
|
||||
// Double word read, return other half of double word.
|
||||
return _memoryDataHigh;
|
||||
return _memoryDataHigh;
|
||||
|
||||
default:
|
||||
// Invalid state.
|
||||
@@ -324,12 +327,15 @@ namespace Contralto.Memory
|
||||
// If this is memory cycle 5 of a double-word *store* (started in cycle 3) then the second word can be *read* here.
|
||||
// (An example of this is provided in the hardware ref, section 2, pg 8. and is done by the Ethernet microcode on
|
||||
// the Alto II, see the code block starting at address 0224 -- EPLOC (600) is loaded with the interface status,
|
||||
// EBLOC (601) is read and OR'd with NWW in the same memory op.)
|
||||
_firstWordRead = true;
|
||||
return _firstWordStored ? _memoryDataHigh : _memoryDataLow;
|
||||
// EBLOC (601) is read and OR'd with NWW in the same memory op.)
|
||||
ushort memData = _firstWord ? _memoryDataHigh : _memoryDataLow;
|
||||
|
||||
_firstWord = !_firstWord;
|
||||
|
||||
return memData;
|
||||
|
||||
// ***
|
||||
// NB: Handler for double-word read (cycle 6) is in the "else" clause below; this is kind of a hack.
|
||||
// NB: Handler for double-word read (cycles 6 and later) is in the "else" clause below; this is kind of a hack.
|
||||
// ***
|
||||
|
||||
default:
|
||||
@@ -341,12 +347,19 @@ namespace Contralto.Memory
|
||||
{
|
||||
//
|
||||
// Memory state machine not running, just return last latched contents.
|
||||
// ("Because the Alto II latches memory contents, it is possible to execute _MD anytime after
|
||||
// ("Because the Alto II latches memory contents, it is possible to execute <-MD anytime after
|
||||
// cycle 5 of a reference and obtain the results of the read operation")
|
||||
// We will return the last half of the doubleword to complete a double-word read.
|
||||
// (If the first half hasn't yet been read, we return the first half instead...)
|
||||
//
|
||||
return _firstWordRead ? _memoryDataHigh : _memoryDataLow;
|
||||
// We will return the proper half of the word as in cycle 5.
|
||||
//
|
||||
// Note that the Orbit character transfer microcode will occasionally do a double-word read in memory
|
||||
// cycles 6 and 7, not 5 and 6, and still expect the correct 32-bit double-word to be read...
|
||||
//
|
||||
ushort memData;
|
||||
|
||||
memData = _firstWord ? _memoryDataHigh : _memoryDataLow;
|
||||
|
||||
_firstWord = !_firstWord;
|
||||
return memData;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,11 +405,11 @@ namespace Contralto.Memory
|
||||
_memoryDataWrite = data;
|
||||
// Start of doubleword write:
|
||||
WriteToBus(_memoryAddress, data, _task, _extendedMemoryReference);
|
||||
_firstWordStored = true;
|
||||
_firstWord = !_firstWord;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
if (!_firstWordStored)
|
||||
if (!_firstWord)
|
||||
{
|
||||
throw new InvalidOperationException("Unexpected microcode behavior -- LoadMD on cycle 6, no LoadMD on cycle 5.");
|
||||
}
|
||||
@@ -405,6 +418,8 @@ namespace Contralto.Memory
|
||||
ushort actualAddress = (ushort)(_memoryAddress | 1);
|
||||
|
||||
WriteToBus(actualAddress, data, _task, _extendedMemoryReference);
|
||||
|
||||
_firstWord = !_firstWord;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -424,13 +439,14 @@ namespace Contralto.Memory
|
||||
_memoryDataWrite = data;
|
||||
// Start of doubleword write:
|
||||
WriteToBus(_memoryAddress, data, _task, _extendedMemoryReference);
|
||||
_firstWordStored = true;
|
||||
_firstWord = !_firstWord;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
_memoryDataWrite = data;
|
||||
ushort actualAddress = _firstWordStored ? (ushort)(_memoryAddress ^ 1) : _memoryAddress;
|
||||
ushort actualAddress = _firstWord ? (ushort)(_memoryAddress ^ 1) : _memoryAddress;
|
||||
WriteToBus(actualAddress, data, _task, _extendedMemoryReference);
|
||||
_firstWord = !_firstWord;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
@@ -533,10 +549,9 @@ namespace Contralto.Memory
|
||||
// Write data (used for debugger UI only)
|
||||
private ushort _memoryDataWrite;
|
||||
|
||||
// Indicates that the first word of a double-word store has taken place (started on cycle 3)
|
||||
private bool _firstWordStored;
|
||||
|
||||
// Indicates tghat the first word of a double-word read has taken place (started on cycle 5)
|
||||
private bool _firstWordRead;
|
||||
// Indicates which word of a double-word read or write was last read (or written).
|
||||
// true if the first word was processed; false otherwise.
|
||||
private bool _firstWord;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,8 @@
|
||||
along with ContrAlto. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using Contralto.IO;
|
||||
using Contralto.SdlUI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Contralto
|
||||
@@ -121,16 +119,15 @@ namespace Contralto
|
||||
// Commit current configuration to disk
|
||||
//
|
||||
Configuration.WriteConfiguration();
|
||||
|
||||
}
|
||||
|
||||
private static void PrintHerald()
|
||||
{
|
||||
Console.WriteLine("ContrAlto v1.2 (c) 2015-2017 Living Computers: Museum+Labs.");
|
||||
Console.WriteLine("ContrAlto v{0} (c) 2015-2017 Living Computers: Museum+Labs.", typeof(Program).Assembly.GetName().Version);
|
||||
Console.WriteLine("Bug reports to joshd@livingcomputers.org");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
private static AltoSystem _system;
|
||||
private static AltoSystem _system;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.2.0")]
|
||||
[assembly: AssemblyFileVersion("1.2.0.0")]
|
||||
[assembly: AssemblyVersion("1.2.1")]
|
||||
[assembly: AssemblyFileVersion("1.2.1.0")]
|
||||
|
||||
36
Contralto/Properties/Settings.Designer.cs
generated
36
Contralto/Properties/Settings.Designer.cs
generated
@@ -202,5 +202,41 @@ namespace Contralto.Properties {
|
||||
this["AudioDACCapturePath"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
public bool EnablePrinting {
|
||||
get {
|
||||
return ((bool)(this["EnablePrinting"]));
|
||||
}
|
||||
set {
|
||||
this["EnablePrinting"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("")]
|
||||
public string PrintOutputPath {
|
||||
get {
|
||||
return ((string)(this["PrintOutputPath"]));
|
||||
}
|
||||
set {
|
||||
this["PrintOutputPath"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("True")]
|
||||
public bool ReversePageOrder {
|
||||
get {
|
||||
return ((bool)(this["ReversePageOrder"]));
|
||||
}
|
||||
set {
|
||||
this["ReversePageOrder"] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,5 +47,14 @@
|
||||
<Setting Name="AudioDACCapturePath" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="EnablePrinting" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="PrintOutputPath" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="ReversePageOrder" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">True</Value>
|
||||
</Setting>
|
||||
</Settings>
|
||||
</SettingsFile>
|
||||
8
Contralto/UI/AboutBox.Designer.cs
generated
8
Contralto/UI/AboutBox.Designer.cs
generated
@@ -52,11 +52,11 @@
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Location = new System.Drawing.Point(20, 59);
|
||||
this.label2.Location = new System.Drawing.Point(6, 58);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(46, 13);
|
||||
this.label2.Size = new System.Drawing.Size(76, 13);
|
||||
this.label2.TabIndex = 1;
|
||||
this.label2.Text = "(c) 2016";
|
||||
this.label2.Text = "(c) 2016, 2017";
|
||||
//
|
||||
// label3
|
||||
//
|
||||
@@ -109,7 +109,7 @@
|
||||
// websiteLink
|
||||
//
|
||||
this.websiteLink.AutoSize = true;
|
||||
this.websiteLink.Location = new System.Drawing.Point(65, 59);
|
||||
this.websiteLink.Location = new System.Drawing.Point(80, 59);
|
||||
this.websiteLink.Name = "websiteLink";
|
||||
this.websiteLink.Size = new System.Drawing.Size(163, 13);
|
||||
this.websiteLink.TabIndex = 7;
|
||||
|
||||
74
Contralto/UI/Debugger.Designer.cs
generated
74
Contralto/UI/Debugger.Designer.cs
generated
@@ -109,9 +109,6 @@
|
||||
this.StopButton = new System.Windows.Forms.Button();
|
||||
this.groupBox2 = new System.Windows.Forms.GroupBox();
|
||||
this._taskData = new System.Windows.Forms.DataGridView();
|
||||
this.TaskName = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
this.TaskState = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
this.TaskPC = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
this.groupBox3 = new System.Windows.Forms.GroupBox();
|
||||
this._otherRegs = new System.Windows.Forms.DataGridView();
|
||||
this.Reg = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
@@ -136,6 +133,10 @@
|
||||
this.dataGridViewTextBoxColumn9 = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
this.dataGridViewTextBoxColumn10 = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
this.dataGridViewTextBoxColumn11 = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
this.TaskName = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
this.TaskState = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
this.Bank = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
this.TaskPC = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
this.Microcode.SuspendLayout();
|
||||
this.SourceTabs.SuspendLayout();
|
||||
this.Rom0Page.SuspendLayout();
|
||||
@@ -855,6 +856,7 @@
|
||||
this._taskData.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
|
||||
this.TaskName,
|
||||
this.TaskState,
|
||||
this.Bank,
|
||||
this.TaskPC});
|
||||
dataGridViewCellStyle29.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
|
||||
dataGridViewCellStyle29.BackColor = System.Drawing.SystemColors.Window;
|
||||
@@ -881,31 +883,6 @@
|
||||
this._taskData.TabIndex = 0;
|
||||
this._taskData.TabStop = false;
|
||||
//
|
||||
// TaskName
|
||||
//
|
||||
this.TaskName.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCellsExceptHeader;
|
||||
this.TaskName.HeaderText = "T";
|
||||
this.TaskName.MinimumWidth = 16;
|
||||
this.TaskName.Name = "TaskName";
|
||||
this.TaskName.ReadOnly = true;
|
||||
this.TaskName.Width = 16;
|
||||
//
|
||||
// TaskState
|
||||
//
|
||||
this.TaskState.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.DisplayedCellsExceptHeader;
|
||||
this.TaskState.HeaderText = "S";
|
||||
this.TaskState.MinimumWidth = 16;
|
||||
this.TaskState.Name = "TaskState";
|
||||
this.TaskState.ReadOnly = true;
|
||||
this.TaskState.Width = 16;
|
||||
//
|
||||
// TaskPC
|
||||
//
|
||||
this.TaskPC.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
|
||||
this.TaskPC.HeaderText = "uPC";
|
||||
this.TaskPC.Name = "TaskPC";
|
||||
this.TaskPC.ReadOnly = true;
|
||||
//
|
||||
// groupBox3
|
||||
//
|
||||
this.groupBox3.Controls.Add(this._otherRegs);
|
||||
@@ -1234,6 +1211,40 @@
|
||||
this.dataGridViewTextBoxColumn11.Resizable = System.Windows.Forms.DataGridViewTriState.False;
|
||||
this.dataGridViewTextBoxColumn11.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
|
||||
//
|
||||
// TaskName
|
||||
//
|
||||
this.TaskName.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCellsExceptHeader;
|
||||
this.TaskName.HeaderText = "T";
|
||||
this.TaskName.MinimumWidth = 16;
|
||||
this.TaskName.Name = "TaskName";
|
||||
this.TaskName.ReadOnly = true;
|
||||
this.TaskName.Width = 16;
|
||||
//
|
||||
// TaskState
|
||||
//
|
||||
this.TaskState.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.DisplayedCellsExceptHeader;
|
||||
this.TaskState.HeaderText = "S";
|
||||
this.TaskState.MinimumWidth = 16;
|
||||
this.TaskState.Name = "TaskState";
|
||||
this.TaskState.ReadOnly = true;
|
||||
this.TaskState.Width = 16;
|
||||
//
|
||||
// Bank
|
||||
//
|
||||
this.Bank.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCellsExceptHeader;
|
||||
this.Bank.HeaderText = "B";
|
||||
this.Bank.MinimumWidth = 16;
|
||||
this.Bank.Name = "Bank";
|
||||
this.Bank.ReadOnly = true;
|
||||
this.Bank.Width = 16;
|
||||
//
|
||||
// TaskPC
|
||||
//
|
||||
this.TaskPC.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
|
||||
this.TaskPC.HeaderText = "uPC";
|
||||
this.TaskPC.Name = "TaskPC";
|
||||
this.TaskPC.ReadOnly = true;
|
||||
//
|
||||
// Debugger
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
@@ -1306,9 +1317,6 @@
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn S;
|
||||
private System.Windows.Forms.GroupBox groupBox2;
|
||||
private System.Windows.Forms.DataGridView _taskData;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn TaskName;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn TaskState;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn TaskPC;
|
||||
private System.Windows.Forms.GroupBox groupBox3;
|
||||
private System.Windows.Forms.DataGridView _otherRegs;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn Reg;
|
||||
@@ -1366,5 +1374,9 @@
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn Address;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn Data;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn Disassembly;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn TaskName;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn TaskState;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn Bank;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn TaskPC;
|
||||
}
|
||||
}
|
||||
@@ -116,7 +116,8 @@ namespace Contralto
|
||||
{
|
||||
_taskData.Rows[i].Cells[0].Value = GetTextForTask((TaskType)i);
|
||||
_taskData.Rows[i].Cells[1].Value = GetTextForTaskState(_system.CPU.Tasks[i]);
|
||||
_taskData.Rows[i].Cells[2].Value =
|
||||
_taskData.Rows[i].Cells[2].Value = GetTextForTaskBank(_system.CPU.Tasks[i]);
|
||||
_taskData.Rows[i].Cells[3].Value =
|
||||
_system.CPU.Tasks[i] != null ? Conversion.ToOctal(_system.CPU.Tasks[i].MPC, 4) : String.Empty;
|
||||
}
|
||||
|
||||
@@ -532,7 +533,7 @@ namespace Contralto
|
||||
case "Disassembly":
|
||||
// TODO: should provide means to disassemble as specific task, not just Emulator.
|
||||
MicroInstruction instruction = new MicroInstruction(UCodeMemory.UCodeRAM[address]);
|
||||
e.Value = UCodeDisassembler.DisassembleInstruction(instruction, TaskType.Emulator);
|
||||
e.Value = UCodeDisassembler.DisassembleInstruction(instruction, _system.CPU.CurrentTask.TaskType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -578,6 +579,18 @@ namespace Contralto
|
||||
}
|
||||
}
|
||||
|
||||
private string GetTextForTaskBank(AltoCPU.Task task)
|
||||
{
|
||||
if (task == null)
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
return UCodeMemory.GetBank(task.TaskType).ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private string GetTextForTask(TaskType task)
|
||||
{
|
||||
string[] taskText =
|
||||
@@ -931,38 +944,13 @@ namespace Contralto
|
||||
case ExecutionType.Normal:
|
||||
case ExecutionType.NextTask:
|
||||
case ExecutionType.NextNovaInstruction:
|
||||
|
||||
// For debugging floating point microcode:
|
||||
#if FLOAT_DEBUG
|
||||
if (_system.CPU.CurrentTask.MPC == 0x10) // MPC is 20(octal) meaning a new Nova instruction.
|
||||
{
|
||||
if (_lastFPInstruction == 0)
|
||||
{
|
||||
// check for new floating instruction
|
||||
FloatDebugPre(_system.MemoryBus.DebugReadWord(TaskType.Emulator, _system.CPU.R[6]));
|
||||
}
|
||||
else
|
||||
{
|
||||
// last instruction was a floating point instruction, check the result
|
||||
FloatDebugPost();
|
||||
|
||||
// And see if this new one is also a floating point instruction...
|
||||
FloatDebugPre(_system.MemoryBus.DebugReadWord(TaskType.Emulator, _system.CPU.R[6]));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (_system.CPU.CurrentTask.MPC == 0x10) // MPC is 20(octal) meaning a new Nova instruction.
|
||||
{
|
||||
// count nova instructions (for profiling)
|
||||
_system._novaInst++;
|
||||
}
|
||||
|
||||
// See if we need to stop here
|
||||
if (_execAbort || // The Stop button was hit
|
||||
_system.CPU.InternalBreak || // Something internal has requested a debugger break
|
||||
_microcodeBreakpointEnabled[
|
||||
(int)UCodeMemory.GetBank(
|
||||
_system.CPU.CurrentTask.TaskType),
|
||||
_system.CPU.CurrentTask.MPC] || // A microcode breakpoint was hit
|
||||
_system.CPU.CurrentTask.MPC] || // A microcode breakpoint was hit
|
||||
(_execType == ExecutionType.NextTask &&
|
||||
_system.CPU.NextTask != null &&
|
||||
_system.CPU.NextTask != _system.CPU.CurrentTask) || // The next task was switched to
|
||||
@@ -970,15 +958,17 @@ namespace Contralto
|
||||
(_novaBreakpointEnabled[_system.CPU.R[6]] || // A breakpoint is set here
|
||||
_execType == ExecutionType.NextNovaInstruction))) // or we're running only a single Nova instruction.
|
||||
{
|
||||
if (!_execAbort)
|
||||
{
|
||||
SetExecutionState(ExecutionState.BreakpointStop);
|
||||
}
|
||||
|
||||
// Stop here as we've hit a breakpoint or have been stopped
|
||||
// Update UI to indicate where we stopped.
|
||||
this.BeginInvoke(new StepDelegate(RefreshUI));
|
||||
this.BeginInvoke(new StepDelegate(Invalidate));
|
||||
|
||||
if (!_execAbort)
|
||||
{
|
||||
SetExecutionState(ExecutionState.BreakpointStop);
|
||||
}
|
||||
_system.CPU.InternalBreak = false;
|
||||
|
||||
_execAbort = false;
|
||||
return true;
|
||||
@@ -990,379 +980,7 @@ namespace Contralto
|
||||
return false;
|
||||
}
|
||||
|
||||
#if FLOAT_DEBUG
|
||||
// vars for float debug
|
||||
ushort _lastFPInstruction;
|
||||
ushort _ac0;
|
||||
ushort _ac1;
|
||||
ushort _ac2;
|
||||
ushort _ac3;
|
||||
ushort _fpRegAddr;
|
||||
ushort _fpRegCount;
|
||||
|
||||
int _fpInstructionCount;
|
||||
|
||||
/// <summary>
|
||||
/// Temporary, for debugging floating point ucode issues
|
||||
/// </summary>
|
||||
/// <param name="instruction"></param>
|
||||
private void FloatDebugPre(ushort instruction)
|
||||
{
|
||||
_lastFPInstruction = 0;
|
||||
// Float instructions are from 70001-70022 octal
|
||||
if (instruction >= 0x7000 && instruction <= 0x7012)
|
||||
{
|
||||
_fpInstructionCount++;
|
||||
|
||||
// Save instruction
|
||||
_lastFPInstruction = instruction;
|
||||
|
||||
// Save ACs
|
||||
_ac0 = _system.CPU.R[3];
|
||||
_ac1 = _system.CPU.R[2];
|
||||
_ac2 = _system.CPU.R[1];
|
||||
_ac3 = _system.CPU.R[0];
|
||||
|
||||
Console.Write("{0}:FP: ", _fpInstructionCount);
|
||||
switch (instruction)
|
||||
{
|
||||
case 0x7000:
|
||||
Console.WriteLine("FPSetup");
|
||||
|
||||
_fpRegAddr = _system.CPU.R[3];
|
||||
_fpRegCount = _system.MemoryBus.DebugReadWord(TaskType.Emulator, _fpRegAddr);
|
||||
|
||||
Console.WriteLine(" FP register address {0}, count {1}", Conversion.ToOctal(_fpRegAddr), _fpRegCount);
|
||||
|
||||
break;
|
||||
|
||||
case 0x7001:
|
||||
Console.WriteLine("FML {0},{1} ({2},{3})", _ac0, _ac1, GetFloat(_ac0), GetFloat(_ac1));
|
||||
break;
|
||||
|
||||
case 0x7002:
|
||||
Console.WriteLine("FDV {0},{1} ({2},{3})", _ac0, _ac1, GetFloat(_ac0), GetFloat(_ac1));
|
||||
break;
|
||||
|
||||
case 0x7003:
|
||||
Console.WriteLine("FAD {0},{1} ({2},{3})", _ac0, _ac1, GetFloat(_ac0), GetFloat(_ac1));
|
||||
break;
|
||||
|
||||
case 0x7004:
|
||||
Console.WriteLine("FSB {0},{1} ({2},{3})", _ac0, _ac1, GetFloat(_ac0), GetFloat(_ac1));
|
||||
break;
|
||||
|
||||
case 0x7005:
|
||||
Console.WriteLine("FLD {0},{1} (src {2})", _ac0, _ac1, GetFloat(_ac1));
|
||||
break;
|
||||
|
||||
case 0x7006:
|
||||
Console.WriteLine("FLDV {0},{1} (src {2})", _ac0, _ac1, GetFloatFromInternalFormat(_ac1));
|
||||
break;
|
||||
|
||||
case 0x7007:
|
||||
Console.WriteLine("FSTV {0},{1} (src {2})", _ac0, _ac1, GetFloat(_ac0));
|
||||
break;
|
||||
|
||||
case 0x7008:
|
||||
Console.WriteLine("FLDI {0},{1}", _ac0, Conversion.ToOctal(_ac1));
|
||||
break;
|
||||
|
||||
case 0x7009:
|
||||
Console.WriteLine("FTR {0} ({1})", _ac0, GetFloat(_ac0));
|
||||
break;
|
||||
|
||||
case 0x700a:
|
||||
Console.WriteLine("FNEG {0} ({1})", _ac0, GetFloat(_ac0));
|
||||
break;
|
||||
|
||||
case 0x700b:
|
||||
Console.WriteLine("FSN {0} ({1})", _ac0, GetFloat(_ac0));
|
||||
break;
|
||||
|
||||
case 0x700c:
|
||||
Console.WriteLine("FCM {0} ({1})", _ac0, GetFloat(_ac0));
|
||||
break;
|
||||
|
||||
case 0x700d:
|
||||
Console.WriteLine("FST");
|
||||
break;
|
||||
|
||||
case 0x700e:
|
||||
Console.WriteLine("FLDDP");
|
||||
break;
|
||||
|
||||
case 0x700f:
|
||||
Console.WriteLine("FSTDP");
|
||||
break;
|
||||
|
||||
case 0x7010:
|
||||
Console.WriteLine("DPAD");
|
||||
break;
|
||||
|
||||
case 0x7011:
|
||||
Console.WriteLine("DPSB");
|
||||
break;
|
||||
|
||||
case 0x7012:
|
||||
Console.WriteLine("FEXP {0},{1} ({2})", _ac0, _ac1, GetFloat(_ac0));
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
Console.WriteLine(" AC0={0} AC1={1} AC2={2} AC3={3}",
|
||||
Conversion.ToOctal(_ac0),
|
||||
Conversion.ToOctal(_ac1),
|
||||
Conversion.ToOctal(_ac2),
|
||||
Conversion.ToOctal(_ac3)); */
|
||||
}
|
||||
}
|
||||
|
||||
private void FloatDebugPost()
|
||||
{
|
||||
|
||||
Console.Write("{0}:Post: ", _fpInstructionCount);
|
||||
switch (_lastFPInstruction)
|
||||
{
|
||||
case 0x7000:
|
||||
Console.WriteLine("FPSetup done.");
|
||||
break;
|
||||
|
||||
case 0x7001:
|
||||
Console.WriteLine("Result {0}", GetFloat(_ac0));
|
||||
break;
|
||||
|
||||
case 0x7002:
|
||||
Console.WriteLine("Result {0}", GetFloat(_ac0));
|
||||
break;
|
||||
|
||||
case 0x7003:
|
||||
Console.WriteLine("Result {0}", GetFloat(_ac0));
|
||||
break;
|
||||
|
||||
case 0x7004:
|
||||
Console.WriteLine("Result {0}", GetFloat(_ac0));
|
||||
break;
|
||||
|
||||
case 0x7005:
|
||||
Console.WriteLine("Loaded {0}", GetFloat(_ac0));
|
||||
break;
|
||||
|
||||
case 0x7006:
|
||||
Console.WriteLine("Loaded {0}", GetFloat(_ac0));
|
||||
break;
|
||||
|
||||
case 0x7007:
|
||||
Console.WriteLine("FSTV done.");
|
||||
break;
|
||||
|
||||
case 0x7008:
|
||||
Console.WriteLine("Loaded {0}", GetFloat(_ac0));
|
||||
break;
|
||||
|
||||
case 0x7009:
|
||||
Console.WriteLine("Result {0}", GetFloat(_ac0));
|
||||
break;
|
||||
|
||||
case 0x700a:
|
||||
Console.WriteLine("Result {0}", GetFloat(_ac0));
|
||||
break;
|
||||
|
||||
case 0x700b:
|
||||
Console.WriteLine("Result {0}", _ac3);
|
||||
break;
|
||||
|
||||
case 0x700c:
|
||||
Console.WriteLine("Result {0}", _ac3);
|
||||
break;
|
||||
|
||||
case 0x700d:
|
||||
Console.WriteLine("FST done.");
|
||||
break;
|
||||
|
||||
case 0x700e:
|
||||
Console.WriteLine("FLDDP done.");
|
||||
break;
|
||||
|
||||
case 0x700f:
|
||||
Console.WriteLine("FSTDP done.");
|
||||
break;
|
||||
|
||||
case 0x7010:
|
||||
Console.WriteLine("DPAD done.");
|
||||
break;
|
||||
|
||||
case 0x7011:
|
||||
Console.WriteLine("DPSB done.");
|
||||
break;
|
||||
|
||||
case 0x7012:
|
||||
Console.WriteLine("Result {0}", GetFloat(_ac0));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException("Unexpected op for post.");
|
||||
}
|
||||
|
||||
_lastFPInstruction = 0;
|
||||
}
|
||||
|
||||
private double GetFloat(ushort arg)
|
||||
{
|
||||
// If arg is less than the number of registers, it's assumed
|
||||
// to be a register; otherwise an address
|
||||
if (arg < _fpRegCount)
|
||||
{
|
||||
return GetFloatFromUcode(arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetFloatFromPackedFormat(arg);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a float from memory in "packed" format
|
||||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <returns></returns>
|
||||
private double GetFloatFromPackedFormat(ushort addr)
|
||||
{
|
||||
//
|
||||
// Packed format is only two words:
|
||||
// structure FP: [
|
||||
// sign bit 1 //1 if negative.
|
||||
// expon bit 8 //excess 128 format (complemented if number <0)
|
||||
// mantissa1 bit 7 //High order 7 bits of mantissa
|
||||
// mantissa2 bit 16 //Low order 16 bits of mantissa
|
||||
// ]
|
||||
//
|
||||
uint packedWord =
|
||||
(uint)(_system.MemoryBus.DebugReadWord(TaskType.Emulator, addr) << 16) |
|
||||
(uint)(_system.MemoryBus.DebugReadWord(TaskType.Emulator, (ushort)(addr + 1)));
|
||||
|
||||
double sign = (packedWord & 0x80000000) != 0 ? -1.0 : 1.0;
|
||||
uint exponent = (packedWord & 0x7f800000) >> 23;
|
||||
uint mantissa = (packedWord & 0x007fffff);
|
||||
|
||||
double val = 0.0;
|
||||
for (int i = 0; i < 23; i++)
|
||||
{
|
||||
double bit = (mantissa & (0x00400000 >> i)) != 0 ? 1.0 : 0.0;
|
||||
|
||||
val += (bit * (1.0 / Math.Pow(2.0, (double)i)));
|
||||
}
|
||||
|
||||
double adjustedExponent = exponent - 128.0;
|
||||
|
||||
val = sign * (val) * Math.Pow(2.0, adjustedExponent);
|
||||
|
||||
Console.WriteLine("packed: {0}", val);
|
||||
|
||||
return val;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the float value for register, from alto code
|
||||
/// </summary>
|
||||
/// <param name="reg"></param>
|
||||
/// <returns></returns>
|
||||
private double GetFloatFromInternalFormat(ushort addr)
|
||||
{
|
||||
|
||||
// Internal format is 4 words long:
|
||||
// Word 0: sign
|
||||
// Word 1: exponent
|
||||
// Word 2-3: mantissa
|
||||
//
|
||||
|
||||
double sign = (_system.MemoryBus.DebugReadWord(TaskType.Emulator, addr)) != 0 ? -1.0 : 1.0;
|
||||
int exponent = (int)(short)(_system.MemoryBus.DebugReadWord(TaskType.Emulator, (ushort)(addr + 1)));
|
||||
uint mantissa =
|
||||
(uint)(_system.MemoryBus.DebugReadWord(TaskType.Emulator, (ushort)(addr + 2)) << 16) |
|
||||
(uint)(_system.MemoryBus.DebugReadWord(TaskType.Emulator, (ushort)(addr + 3)));
|
||||
|
||||
double valMantissa = 0.0;
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
double bit = (mantissa & (0x80000000 >> i)) != 0 ? 1.0 : 0.0;
|
||||
|
||||
valMantissa += (bit * (1.0 / Math.Pow(2.0, (double)i)));
|
||||
}
|
||||
|
||||
double val = sign * (valMantissa) * Math.Pow(2.0, exponent - 1);
|
||||
|
||||
//if (double.IsInfinity(val) || double.IsNaN(val))
|
||||
{
|
||||
Console.WriteLine(" Vec: sign {0} exp {1} mantissa {2:x} ({3}) value {4}",
|
||||
sign,
|
||||
exponent,
|
||||
mantissa,
|
||||
valMantissa,
|
||||
val);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the float value for register, from ucode store
|
||||
/// </summary>
|
||||
/// <param name="reg"></param>
|
||||
/// <returns></returns>
|
||||
private double GetFloatFromUcode(ushort reg)
|
||||
{
|
||||
|
||||
// Internal format is 4 words long:
|
||||
// Word 0: sign
|
||||
// Word 1: exponent
|
||||
// Word 2-3: mantissa
|
||||
//
|
||||
// In current ucode, each word is staggered across the FP buffer space rather than being in linear order to prevent having to do multiplies.
|
||||
// For FP register N of M total registers starting at offset O:
|
||||
// Word 0 is at O + N + 1
|
||||
// Word 1 is at O + N + M + 1
|
||||
// Word 2 is at O + N + 2*M + 1
|
||||
// Word 3 is at O + N + 3*M + 1
|
||||
ushort oreg = reg;
|
||||
|
||||
|
||||
reg += _fpRegAddr;
|
||||
reg++;
|
||||
|
||||
//Console.WriteLine("reg base {0}, num {1} addr {2}", _fpRegAddr, oreg, Conversion.ToOctal(reg));
|
||||
|
||||
// reg is now an address; read things in...
|
||||
double sign = (_system.MemoryBus.DebugReadWord(TaskType.Emulator, reg)) != 0 ? -1.0 : 1.0;
|
||||
int exponent = (int)(short)(_system.MemoryBus.DebugReadWord(TaskType.Emulator, (ushort)(reg + _fpRegCount)));
|
||||
uint mantissa =
|
||||
(uint)(_system.MemoryBus.DebugReadWord(TaskType.Emulator, (ushort)(reg + 2 * _fpRegCount)) << 16) |
|
||||
(uint)(_system.MemoryBus.DebugReadWord(TaskType.Emulator, (ushort)(reg + 3 * _fpRegCount)));
|
||||
|
||||
double valMantissa = 0.0;
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
double bit = (mantissa & (0x80000000 >> i)) != 0 ? 1.0 : 0.0;
|
||||
|
||||
valMantissa += (bit * (1.0 / Math.Pow(2.0, (double)i)));
|
||||
}
|
||||
|
||||
double val = sign * (valMantissa) * Math.Pow(2.0, exponent - 1);
|
||||
|
||||
//if (double.IsInfinity(val) || double.IsNaN(val))
|
||||
{
|
||||
Console.WriteLine(" UCode: sign {0} exp {1} mantissa {2:x} ({3}) value {4}",
|
||||
sign,
|
||||
exponent,
|
||||
mantissa,
|
||||
valMantissa,
|
||||
val);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
#endif
|
||||
private void SetExecutionState(ExecutionState state)
|
||||
{
|
||||
_execState = state;
|
||||
|
||||
@@ -192,6 +192,9 @@
|
||||
<metadata name="TaskState.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
<metadata name="Bank.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
<metadata name="TaskPC.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
|
||||
258
Contralto/UI/SystemOptions.Designer.cs
generated
258
Contralto/UI/SystemOptions.Designer.cs
generated
@@ -28,7 +28,7 @@
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.tabControl1 = new System.Windows.Forms.TabControl();
|
||||
this.OptionsTabs = new System.Windows.Forms.TabControl();
|
||||
this.tabPage1 = new System.Windows.Forms.TabPage();
|
||||
this.AltoI1KROMRadioButton = new System.Windows.Forms.RadioButton();
|
||||
this.AltoII3KRAMRadioButton = new System.Windows.Forms.RadioButton();
|
||||
@@ -48,16 +48,23 @@
|
||||
this.tabPage3 = new System.Windows.Forms.TabPage();
|
||||
this.ThrottleSpeedCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.InterlaceDisplayCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.tabPage4 = new System.Windows.Forms.TabPage();
|
||||
this.DACOptionsGroupBox = new System.Windows.Forms.GroupBox();
|
||||
this.BrowseButton = new System.Windows.Forms.Button();
|
||||
this.DACOutputCapturePathTextBox = new System.Windows.Forms.TextBox();
|
||||
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.tabPage4 = new System.Windows.Forms.TabPage();
|
||||
this.EnableDACCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.DACOptionsGroupBox = new System.Windows.Forms.GroupBox();
|
||||
this.EnableDACCaptureCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.DACOutputCapturePathTextBox = new System.Windows.Forms.TextBox();
|
||||
this.BrowseButton = new System.Windows.Forms.Button();
|
||||
this.tabControl1.SuspendLayout();
|
||||
this.PrintingTab = new System.Windows.Forms.TabPage();
|
||||
this.PrintingOptionsGroupBox = new System.Windows.Forms.GroupBox();
|
||||
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.OptionsTabs.SuspendLayout();
|
||||
this.tabPage1.SuspendLayout();
|
||||
this.tabPage2.SuspendLayout();
|
||||
this.groupBox1.SuspendLayout();
|
||||
@@ -65,19 +72,22 @@
|
||||
this.tabPage3.SuspendLayout();
|
||||
this.tabPage4.SuspendLayout();
|
||||
this.DACOptionsGroupBox.SuspendLayout();
|
||||
this.PrintingTab.SuspendLayout();
|
||||
this.PrintingOptionsGroupBox.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// tabControl1
|
||||
// OptionsTabs
|
||||
//
|
||||
this.tabControl1.Controls.Add(this.tabPage1);
|
||||
this.tabControl1.Controls.Add(this.tabPage2);
|
||||
this.tabControl1.Controls.Add(this.tabPage3);
|
||||
this.tabControl1.Controls.Add(this.tabPage4);
|
||||
this.tabControl1.Location = new System.Drawing.Point(3, 5);
|
||||
this.tabControl1.Name = "tabControl1";
|
||||
this.tabControl1.SelectedIndex = 0;
|
||||
this.tabControl1.Size = new System.Drawing.Size(368, 227);
|
||||
this.tabControl1.TabIndex = 0;
|
||||
this.OptionsTabs.Controls.Add(this.tabPage1);
|
||||
this.OptionsTabs.Controls.Add(this.tabPage2);
|
||||
this.OptionsTabs.Controls.Add(this.tabPage3);
|
||||
this.OptionsTabs.Controls.Add(this.tabPage4);
|
||||
this.OptionsTabs.Controls.Add(this.PrintingTab);
|
||||
this.OptionsTabs.Location = new System.Drawing.Point(3, 5);
|
||||
this.OptionsTabs.Name = "OptionsTabs";
|
||||
this.OptionsTabs.SelectedIndex = 0;
|
||||
this.OptionsTabs.Size = new System.Drawing.Size(368, 227);
|
||||
this.OptionsTabs.TabIndex = 0;
|
||||
//
|
||||
// tabPage1
|
||||
//
|
||||
@@ -101,7 +111,6 @@
|
||||
this.AltoI1KROMRadioButton.Name = "AltoI1KROMRadioButton";
|
||||
this.AltoI1KROMRadioButton.Size = new System.Drawing.Size(214, 17);
|
||||
this.AltoI1KROMRadioButton.TabIndex = 4;
|
||||
this.AltoI1KROMRadioButton.TabStop = true;
|
||||
this.AltoI1KROMRadioButton.Text = "Alto I, 1K Control ROM, 1K Control RAM";
|
||||
this.AltoI1KROMRadioButton.UseVisualStyleBackColor = true;
|
||||
//
|
||||
@@ -112,7 +121,6 @@
|
||||
this.AltoII3KRAMRadioButton.Name = "AltoII3KRAMRadioButton";
|
||||
this.AltoII3KRAMRadioButton.Size = new System.Drawing.Size(236, 17);
|
||||
this.AltoII3KRAMRadioButton.TabIndex = 3;
|
||||
this.AltoII3KRAMRadioButton.TabStop = true;
|
||||
this.AltoII3KRAMRadioButton.Text = "Alto II XM, 1K Control ROM, 3K Control RAM";
|
||||
this.AltoII3KRAMRadioButton.UseVisualStyleBackColor = true;
|
||||
this.AltoII3KRAMRadioButton.CheckedChanged += new System.EventHandler(this.OnSystemTypeCheckChanged);
|
||||
@@ -124,7 +132,6 @@
|
||||
this.AltoII2KROMRadioButton.Name = "AltoII2KROMRadioButton";
|
||||
this.AltoII2KROMRadioButton.Size = new System.Drawing.Size(236, 17);
|
||||
this.AltoII2KROMRadioButton.TabIndex = 2;
|
||||
this.AltoII2KROMRadioButton.TabStop = true;
|
||||
this.AltoII2KROMRadioButton.Text = "Alto II XM, 2K Control ROM, 1K Control RAM";
|
||||
this.AltoII2KROMRadioButton.UseVisualStyleBackColor = true;
|
||||
this.AltoII2KROMRadioButton.CheckedChanged += new System.EventHandler(this.OnSystemTypeCheckChanged);
|
||||
@@ -141,6 +148,7 @@
|
||||
// AltoII1KROMRadioButton
|
||||
//
|
||||
this.AltoII1KROMRadioButton.AutoSize = true;
|
||||
this.AltoII1KROMRadioButton.Checked = true;
|
||||
this.AltoII1KROMRadioButton.Location = new System.Drawing.Point(14, 53);
|
||||
this.AltoII1KROMRadioButton.Name = "AltoII1KROMRadioButton";
|
||||
this.AltoII1KROMRadioButton.Size = new System.Drawing.Size(236, 17);
|
||||
@@ -287,6 +295,79 @@
|
||||
this.InterlaceDisplayCheckBox.Text = "Interlaced Display (headache mode)";
|
||||
this.InterlaceDisplayCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// tabPage4
|
||||
//
|
||||
this.tabPage4.Controls.Add(this.DACOptionsGroupBox);
|
||||
this.tabPage4.Controls.Add(this.EnableDACCheckBox);
|
||||
this.tabPage4.Location = new System.Drawing.Point(4, 22);
|
||||
this.tabPage4.Name = "tabPage4";
|
||||
this.tabPage4.Padding = new System.Windows.Forms.Padding(3);
|
||||
this.tabPage4.Size = new System.Drawing.Size(360, 201);
|
||||
this.tabPage4.TabIndex = 3;
|
||||
this.tabPage4.Text = "DAC";
|
||||
this.tabPage4.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// DACOptionsGroupBox
|
||||
//
|
||||
this.DACOptionsGroupBox.Controls.Add(this.BrowseButton);
|
||||
this.DACOptionsGroupBox.Controls.Add(this.DACOutputCapturePathTextBox);
|
||||
this.DACOptionsGroupBox.Controls.Add(this.label2);
|
||||
this.DACOptionsGroupBox.Controls.Add(this.EnableDACCaptureCheckBox);
|
||||
this.DACOptionsGroupBox.Location = new System.Drawing.Point(15, 52);
|
||||
this.DACOptionsGroupBox.Name = "DACOptionsGroupBox";
|
||||
this.DACOptionsGroupBox.Size = new System.Drawing.Size(335, 139);
|
||||
this.DACOptionsGroupBox.TabIndex = 1;
|
||||
this.DACOptionsGroupBox.TabStop = false;
|
||||
this.DACOptionsGroupBox.Text = "DAC options";
|
||||
//
|
||||
// BrowseButton
|
||||
//
|
||||
this.BrowseButton.Location = new System.Drawing.Point(251, 53);
|
||||
this.BrowseButton.Name = "BrowseButton";
|
||||
this.BrowseButton.Size = new System.Drawing.Size(75, 23);
|
||||
this.BrowseButton.TabIndex = 3;
|
||||
this.BrowseButton.Text = "Browse...";
|
||||
this.BrowseButton.UseVisualStyleBackColor = true;
|
||||
this.BrowseButton.Click += new System.EventHandler(this.BrowseButton_Click);
|
||||
//
|
||||
// DACOutputCapturePathTextBox
|
||||
//
|
||||
this.DACOutputCapturePathTextBox.Location = new System.Drawing.Point(127, 55);
|
||||
this.DACOutputCapturePathTextBox.Name = "DACOutputCapturePathTextBox";
|
||||
this.DACOutputCapturePathTextBox.Size = new System.Drawing.Size(110, 20);
|
||||
this.DACOutputCapturePathTextBox.TabIndex = 2;
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Location = new System.Drawing.Point(16, 58);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(105, 13);
|
||||
this.label2.TabIndex = 1;
|
||||
this.label2.Text = "Output capture path:";
|
||||
//
|
||||
// EnableDACCaptureCheckBox
|
||||
//
|
||||
this.EnableDACCaptureCheckBox.AutoSize = true;
|
||||
this.EnableDACCaptureCheckBox.Location = new System.Drawing.Point(18, 28);
|
||||
this.EnableDACCaptureCheckBox.Name = "EnableDACCaptureCheckBox";
|
||||
this.EnableDACCaptureCheckBox.Size = new System.Drawing.Size(156, 17);
|
||||
this.EnableDACCaptureCheckBox.TabIndex = 0;
|
||||
this.EnableDACCaptureCheckBox.Text = "Enable DAC output capture";
|
||||
this.EnableDACCaptureCheckBox.UseVisualStyleBackColor = true;
|
||||
this.EnableDACCaptureCheckBox.CheckedChanged += new System.EventHandler(this.EnableDACCaptureCheckBox_CheckedChanged);
|
||||
//
|
||||
// EnableDACCheckBox
|
||||
//
|
||||
this.EnableDACCheckBox.AutoSize = true;
|
||||
this.EnableDACCheckBox.Location = new System.Drawing.Point(19, 22);
|
||||
this.EnableDACCheckBox.Name = "EnableDACCheckBox";
|
||||
this.EnableDACCheckBox.Size = new System.Drawing.Size(275, 17);
|
||||
this.EnableDACCheckBox.TabIndex = 0;
|
||||
this.EnableDACCheckBox.Text = "Enable Audio DAC (Used by Smalltalk Music System)";
|
||||
this.EnableDACCheckBox.UseVisualStyleBackColor = true;
|
||||
this.EnableDACCheckBox.CheckedChanged += new System.EventHandler(this.OnEnableDACCheckboxChanged);
|
||||
//
|
||||
// DialogOKButton
|
||||
//
|
||||
this.DialogOKButton.Location = new System.Drawing.Point(211, 239);
|
||||
@@ -307,78 +388,76 @@
|
||||
this.DialogCancelButton.UseVisualStyleBackColor = true;
|
||||
this.DialogCancelButton.Click += new System.EventHandler(this.CancelButton_Click);
|
||||
//
|
||||
// tabPage4
|
||||
// PrintingTab
|
||||
//
|
||||
this.tabPage4.Controls.Add(this.DACOptionsGroupBox);
|
||||
this.tabPage4.Controls.Add(this.EnableDACCheckBox);
|
||||
this.tabPage4.Location = new System.Drawing.Point(4, 22);
|
||||
this.tabPage4.Name = "tabPage4";
|
||||
this.tabPage4.Padding = new System.Windows.Forms.Padding(3);
|
||||
this.tabPage4.Size = new System.Drawing.Size(360, 201);
|
||||
this.tabPage4.TabIndex = 3;
|
||||
this.tabPage4.Text = "DAC";
|
||||
this.tabPage4.UseVisualStyleBackColor = true;
|
||||
this.PrintingTab.Controls.Add(this.PrintingOptionsGroupBox);
|
||||
this.PrintingTab.Controls.Add(this.EnablePrintingCheckBox);
|
||||
this.PrintingTab.Location = new System.Drawing.Point(4, 22);
|
||||
this.PrintingTab.Name = "PrintingTab";
|
||||
this.PrintingTab.Padding = new System.Windows.Forms.Padding(3);
|
||||
this.PrintingTab.Size = new System.Drawing.Size(360, 201);
|
||||
this.PrintingTab.TabIndex = 4;
|
||||
this.PrintingTab.Text = "Printing";
|
||||
this.PrintingTab.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// EnableDACCheckBox
|
||||
// PrintingOptionsGroupBox
|
||||
//
|
||||
this.EnableDACCheckBox.AutoSize = true;
|
||||
this.EnableDACCheckBox.Location = new System.Drawing.Point(19, 22);
|
||||
this.EnableDACCheckBox.Name = "EnableDACCheckBox";
|
||||
this.EnableDACCheckBox.Size = new System.Drawing.Size(275, 17);
|
||||
this.EnableDACCheckBox.TabIndex = 0;
|
||||
this.EnableDACCheckBox.Text = "Enable Audio DAC (Used by Smalltalk Music System)";
|
||||
this.EnableDACCheckBox.UseVisualStyleBackColor = true;
|
||||
this.EnableDACCheckBox.CheckedChanged += new System.EventHandler(this.OnEnableDACCheckboxChanged);
|
||||
this.PrintingOptionsGroupBox.Controls.Add(this.ReversePageOrderCheckBox);
|
||||
this.PrintingOptionsGroupBox.Controls.Add(this.button1);
|
||||
this.PrintingOptionsGroupBox.Controls.Add(this.PrintOutputPathTextBox);
|
||||
this.PrintingOptionsGroupBox.Controls.Add(this.label5);
|
||||
this.PrintingOptionsGroupBox.Location = new System.Drawing.Point(14, 52);
|
||||
this.PrintingOptionsGroupBox.Name = "PrintingOptionsGroupBox";
|
||||
this.PrintingOptionsGroupBox.Size = new System.Drawing.Size(335, 139);
|
||||
this.PrintingOptionsGroupBox.TabIndex = 3;
|
||||
this.PrintingOptionsGroupBox.TabStop = false;
|
||||
this.PrintingOptionsGroupBox.Text = "Printing options";
|
||||
//
|
||||
// DACOptionsGroupBox
|
||||
// button1
|
||||
//
|
||||
this.DACOptionsGroupBox.Controls.Add(this.BrowseButton);
|
||||
this.DACOptionsGroupBox.Controls.Add(this.DACOutputCapturePathTextBox);
|
||||
this.DACOptionsGroupBox.Controls.Add(this.label2);
|
||||
this.DACOptionsGroupBox.Controls.Add(this.EnableDACCaptureCheckBox);
|
||||
this.DACOptionsGroupBox.Location = new System.Drawing.Point(15, 52);
|
||||
this.DACOptionsGroupBox.Name = "DACOptionsGroupBox";
|
||||
this.DACOptionsGroupBox.Size = new System.Drawing.Size(335, 139);
|
||||
this.DACOptionsGroupBox.TabIndex = 1;
|
||||
this.DACOptionsGroupBox.TabStop = false;
|
||||
this.DACOptionsGroupBox.Text = "DAC options";
|
||||
this.button1.Location = new System.Drawing.Point(254, 24);
|
||||
this.button1.Name = "button1";
|
||||
this.button1.Size = new System.Drawing.Size(75, 23);
|
||||
this.button1.TabIndex = 3;
|
||||
this.button1.Text = "Browse...";
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// EnableDACCaptureCheckBox
|
||||
// PrintOutputPathTextBox
|
||||
//
|
||||
this.EnableDACCaptureCheckBox.AutoSize = true;
|
||||
this.EnableDACCaptureCheckBox.Location = new System.Drawing.Point(18, 28);
|
||||
this.EnableDACCaptureCheckBox.Name = "EnableDACCaptureCheckBox";
|
||||
this.EnableDACCaptureCheckBox.Size = new System.Drawing.Size(156, 17);
|
||||
this.EnableDACCaptureCheckBox.TabIndex = 0;
|
||||
this.EnableDACCaptureCheckBox.Text = "Enable DAC output capture";
|
||||
this.EnableDACCaptureCheckBox.UseVisualStyleBackColor = true;
|
||||
this.EnableDACCaptureCheckBox.CheckedChanged += new System.EventHandler(this.EnableDACCaptureCheckBox_CheckedChanged);
|
||||
this.PrintOutputPathTextBox.Location = new System.Drawing.Point(113, 25);
|
||||
this.PrintOutputPathTextBox.Name = "PrintOutputPathTextBox";
|
||||
this.PrintOutputPathTextBox.Size = new System.Drawing.Size(125, 20);
|
||||
this.PrintOutputPathTextBox.TabIndex = 2;
|
||||
//
|
||||
// label2
|
||||
// label5
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Location = new System.Drawing.Point(16, 58);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(105, 13);
|
||||
this.label2.TabIndex = 1;
|
||||
this.label2.Text = "Output capture path:";
|
||||
this.label5.AutoSize = true;
|
||||
this.label5.Location = new System.Drawing.Point(19, 28);
|
||||
this.label5.Name = "label5";
|
||||
this.label5.Size = new System.Drawing.Size(88, 13);
|
||||
this.label5.TabIndex = 1;
|
||||
this.label5.Text = "PDF output path:";
|
||||
//
|
||||
// DACOutputCapturePathTextBox
|
||||
// EnablePrintingCheckBox
|
||||
//
|
||||
this.DACOutputCapturePathTextBox.Location = new System.Drawing.Point(127, 55);
|
||||
this.DACOutputCapturePathTextBox.Name = "DACOutputCapturePathTextBox";
|
||||
this.DACOutputCapturePathTextBox.Size = new System.Drawing.Size(110, 20);
|
||||
this.DACOutputCapturePathTextBox.TabIndex = 2;
|
||||
this.EnablePrintingCheckBox.AutoSize = true;
|
||||
this.EnablePrintingCheckBox.Location = new System.Drawing.Point(19, 22);
|
||||
this.EnablePrintingCheckBox.Name = "EnablePrintingCheckBox";
|
||||
this.EnablePrintingCheckBox.Size = new System.Drawing.Size(211, 17);
|
||||
this.EnablePrintingCheckBox.TabIndex = 2;
|
||||
this.EnablePrintingCheckBox.Text = "Enable Printing (via Orbit / Dover ROS)";
|
||||
this.EnablePrintingCheckBox.UseVisualStyleBackColor = true;
|
||||
this.EnablePrintingCheckBox.CheckedChanged += new System.EventHandler(this.EnablePrintingCheckBox_CheckedChanged);
|
||||
//
|
||||
// BrowseButton
|
||||
// ReversePageOrderCheckBox
|
||||
//
|
||||
this.BrowseButton.Location = new System.Drawing.Point(251, 53);
|
||||
this.BrowseButton.Name = "BrowseButton";
|
||||
this.BrowseButton.Size = new System.Drawing.Size(75, 23);
|
||||
this.BrowseButton.TabIndex = 3;
|
||||
this.BrowseButton.Text = "Browse...";
|
||||
this.BrowseButton.UseVisualStyleBackColor = true;
|
||||
this.BrowseButton.Click += new System.EventHandler(this.BrowseButton_Click);
|
||||
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;
|
||||
//
|
||||
// SystemOptions
|
||||
//
|
||||
@@ -387,12 +466,12 @@
|
||||
this.ClientSize = new System.Drawing.Size(371, 271);
|
||||
this.Controls.Add(this.DialogCancelButton);
|
||||
this.Controls.Add(this.DialogOKButton);
|
||||
this.Controls.Add(this.tabControl1);
|
||||
this.Controls.Add(this.OptionsTabs);
|
||||
this.Name = "SystemOptions";
|
||||
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "System Options";
|
||||
this.tabControl1.ResumeLayout(false);
|
||||
this.OptionsTabs.ResumeLayout(false);
|
||||
this.tabPage1.ResumeLayout(false);
|
||||
this.tabPage1.PerformLayout();
|
||||
this.tabPage2.ResumeLayout(false);
|
||||
@@ -407,13 +486,17 @@
|
||||
this.tabPage4.PerformLayout();
|
||||
this.DACOptionsGroupBox.ResumeLayout(false);
|
||||
this.DACOptionsGroupBox.PerformLayout();
|
||||
this.PrintingTab.ResumeLayout(false);
|
||||
this.PrintingTab.PerformLayout();
|
||||
this.PrintingOptionsGroupBox.ResumeLayout(false);
|
||||
this.PrintingOptionsGroupBox.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.TabControl tabControl1;
|
||||
private System.Windows.Forms.TabControl OptionsTabs;
|
||||
private System.Windows.Forms.TabPage tabPage1;
|
||||
private System.Windows.Forms.RadioButton AltoII3KRAMRadioButton;
|
||||
private System.Windows.Forms.RadioButton AltoII2KROMRadioButton;
|
||||
@@ -442,5 +525,12 @@
|
||||
private System.Windows.Forms.Button BrowseButton;
|
||||
private System.Windows.Forms.TextBox DACOutputCapturePathTextBox;
|
||||
private System.Windows.Forms.Label label2;
|
||||
private System.Windows.Forms.TabPage PrintingTab;
|
||||
private System.Windows.Forms.GroupBox PrintingOptionsGroupBox;
|
||||
private System.Windows.Forms.Button button1;
|
||||
private System.Windows.Forms.TextBox PrintOutputPathTextBox;
|
||||
private System.Windows.Forms.Label label5;
|
||||
private System.Windows.Forms.CheckBox EnablePrintingCheckBox;
|
||||
private System.Windows.Forms.CheckBox ReversePageOrderCheckBox;
|
||||
}
|
||||
}
|
||||
@@ -113,6 +113,15 @@ namespace Contralto.UI
|
||||
|
||||
DACOutputCapturePathTextBox.Text = Configuration.AudioDACCapturePath;
|
||||
EnableDACCaptureCheckBox.Checked = Configuration.EnableAudioDACCapture;
|
||||
|
||||
//
|
||||
// Printing Tab
|
||||
//
|
||||
PrintOutputPathTextBox.Text = Configuration.PrintOutputPath;
|
||||
EnablePrintingCheckBox.Checked = Configuration.EnablePrinting;
|
||||
PrintingOptionsGroupBox.Enabled = Configuration.EnablePrinting;
|
||||
ReversePageOrderCheckBox.Checked = Configuration.ReversePageOrder;
|
||||
|
||||
}
|
||||
|
||||
private void PopulateNetworkAdapterList(PacketInterfaceType encapType)
|
||||
@@ -273,10 +282,21 @@ namespace Contralto.UI
|
||||
Configuration.EnableAudioDACCapture = EnableDACCaptureCheckBox.Checked;
|
||||
Configuration.AudioDACCapturePath = DACOutputCapturePathTextBox.Text;
|
||||
|
||||
// Validate that the output folder exists, if not, warn the user
|
||||
// Printing
|
||||
Configuration.EnablePrinting = EnablePrintingCheckBox.Checked;
|
||||
Configuration.PrintOutputPath = PrintOutputPathTextBox.Text;
|
||||
Configuration.ReversePageOrder = ReversePageOrderCheckBox.Checked;
|
||||
|
||||
// Validate that the DAC output folder exists, if not, warn the user
|
||||
if (Configuration.EnableAudioDACCapture && !Directory.Exists(Configuration.AudioDACCapturePath))
|
||||
{
|
||||
MessageBox.Show("Warning: the specified DAC output capture path does not exist or is inaccessible.");
|
||||
MessageBox.Show("Warning: The specified DAC output capture path does not exist or is inaccessible.");
|
||||
}
|
||||
|
||||
// Validate that the Print output folder exists, if not, warn the user
|
||||
if (Configuration.EnablePrinting && !Directory.Exists(Configuration.PrintOutputPath))
|
||||
{
|
||||
MessageBox.Show("Warning: The specified Print output path does not exist or is inaccessible.");
|
||||
}
|
||||
|
||||
this.Close();
|
||||
@@ -306,11 +326,15 @@ namespace Contralto.UI
|
||||
{
|
||||
DACOutputCapturePathTextBox.Text = folderDialog.SelectedPath;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EnableDACCaptureCheckBox.Checked = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnableDACCheckboxChanged(object sender, EventArgs e)
|
||||
{
|
||||
DACOptionsGroupBox.Enabled = Configuration.EnableAudioDAC;
|
||||
DACOptionsGroupBox.Enabled = EnableDACCheckBox.Checked;
|
||||
}
|
||||
|
||||
private void EnableDACCaptureCheckBox_CheckedChanged(object sender, EventArgs e)
|
||||
@@ -318,10 +342,38 @@ namespace Contralto.UI
|
||||
if (EnableDACCaptureCheckBox.Checked == true && String.IsNullOrWhiteSpace(DACOutputCapturePathTextBox.Text))
|
||||
{
|
||||
//
|
||||
// When enabled and no output folder is set, force the user to choose an output folder.
|
||||
// When DAC enabled and no output folder is set, force the user to choose an output folder.
|
||||
//
|
||||
BrowseForDACOutputFolder();
|
||||
}
|
||||
}
|
||||
|
||||
private void BrowseForPrintOutputFolder()
|
||||
{
|
||||
FolderBrowserDialog folderDialog = new FolderBrowserDialog();
|
||||
folderDialog.Description = "Choose a folder to store PDFs of print output.";
|
||||
folderDialog.ShowNewFolderButton = true;
|
||||
|
||||
if (folderDialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
PrintOutputPathTextBox.Text = folderDialog.SelectedPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
EnablePrintingCheckBox.Checked = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void EnablePrintingCheckBox_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
PrintingOptionsGroupBox.Enabled = EnablePrintingCheckBox.Checked;
|
||||
if (EnablePrintingCheckBox.Checked == true && String.IsNullOrWhiteSpace(PrintOutputPathTextBox.Text))
|
||||
{
|
||||
//
|
||||
// When Printing enabled and no output folder is set, force the user to choose an output folder.
|
||||
//
|
||||
BrowseForPrintOutputFolder();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="iTextSharp" version="5.5.11" targetFramework="net452" />
|
||||
<package id="NAudio" version="1.8.0" targetFramework="net452" />
|
||||
<package id="SDL2-CS.dll" version="2.0.0.0" targetFramework="net452" />
|
||||
<package id="Sharp_Pcap" version="4.2.0" targetFramework="net452" />
|
||||
|
||||
@@ -20,13 +20,14 @@ ContrAlto currently emulates the following Alto hardware:
|
||||
- Ethernet (encapsulated in UDP datagrams on the host machine)
|
||||
- Standard Keyboard/Mouse/Video
|
||||
- Audio DAC (used with the Smalltalk Music System)
|
||||
- The Orbit raster hardware, Dover Raster Output Scanner and Dover print
|
||||
engine, which provides 384dpi print output (currently PDF only)
|
||||
|
||||
1.2 What's Not
|
||||
--------------
|
||||
|
||||
At this time, ContrAlto does not support more exotic hardware such as Trident
|
||||
disks, printers or audio using the utility port, the Orbit printer interface, or the
|
||||
keyset input device.
|
||||
disks, printers or audio using the utility port, or the keyset input device.
|
||||
|
||||
The Audio DAC is technically emulated, but output is not connected to an audio
|
||||
device on non-Windows platforms at this time.
|
||||
@@ -263,6 +264,11 @@ An example configuration file looks something like:
|
||||
BootFile = 0
|
||||
AlternateBootType = Ethernet
|
||||
|
||||
# Printing options
|
||||
EnablePrinting = true
|
||||
PrintOutputPath = .
|
||||
ReversePageOrder = true
|
||||
|
||||
|
||||
The following parameters are configurable:
|
||||
|
||||
@@ -294,6 +300,19 @@ BootAddress: The address to use with a Keyboard Disk Boot (See section 5.0)
|
||||
BootFile: The file number to use with a Keyboard Net Boot (again, Section 5.0)
|
||||
AlternateBootType: The type of boot to default to (Section 5.0)
|
||||
|
||||
EnablePrinting: Enables or disables printing via the emulated Orbit / Dover interface.
|
||||
|
||||
PrintOutputPath: Specifies the folder that output PDFs are written to. When the Alto
|
||||
prints a new document, a new PDF file will be created in this
|
||||
directory containing the printer's output.
|
||||
|
||||
ReversePageOrder: Controls the order in which pages are written to the PDF -- due to
|
||||
the way the original Dover printer worked, most Alto software printed
|
||||
documents in reverse order (i.e. the last page printed first) so
|
||||
that the pages didn't have to be reshuffled when picked up from the
|
||||
tray. By default, leaving this box checked is probably what you want,
|
||||
but if your documents come out backwards, uncheck it.
|
||||
|
||||
|
||||
4.1 Ethernet Encapsulation Setup
|
||||
================================
|
||||
@@ -452,10 +471,17 @@ On Unix and OS X, display and keyboard/mouse input is provided through SDL, see:
|
||||
https://www.libsdl.org/ and is accessed using the SDL2# wrapper, see:
|
||||
https://github.com/flibitijibibo/SDL2-CS.
|
||||
|
||||
PDF generation is provided by the iTextSharp library, see: https://github.com/itext.
|
||||
|
||||
10.0 Change History
|
||||
===================
|
||||
|
||||
V1.2.1
|
||||
------
|
||||
- Completed implementation of Orbit, Dover ROS and Dover print engine.
|
||||
- Bugfixes to memory state machine around overlapped double-word reads/writes.
|
||||
Smalltalk-80 now runs, as does Spruce.
|
||||
|
||||
V1.2
|
||||
----
|
||||
- First release supporting Unix / OS X
|
||||
|
||||
@@ -21,13 +21,14 @@ ContrAlto currently emulates the following Alto hardware:
|
||||
on the host machine)
|
||||
- Standard Keyboard/Mouse/Video
|
||||
- Audio DAC (used with the Smalltalk Music System)
|
||||
- The Orbit raster hardware, Dover Raster Output Scanner and Dover print
|
||||
engine, which provides 384dpi print output (currently PDF only)
|
||||
|
||||
1.2 What's Not
|
||||
--------------
|
||||
|
||||
At this time, ContrAlto does not support more exotic hardware such as Trident
|
||||
disks, printers or audio using the utility port, the Orbit printer interface, or the
|
||||
keyset input device.
|
||||
disks, printers or audio using the utility port or the keyset input device.
|
||||
|
||||
|
||||
2.0 Requirements
|
||||
@@ -297,11 +298,12 @@ The "Interlaced Display" checkbox attempts to simulate the Alto's original
|
||||
interlaced display. Depending on your monitor and the speed of your computer
|
||||
this may or may not work well.
|
||||
|
||||
|
||||
4.4 DAC
|
||||
-------
|
||||
|
||||
The DAC tab provides options controlling the Audio DAC used by the Smalltalk
|
||||
Music System. The "Enable Audio DAC" does what you'd expect -- it enables
|
||||
Music System. "Enable Audio DAC" does what you'd expect -- it enables
|
||||
or disables audio output (and audio capture). If this option is enabled,
|
||||
the "DAC Options" fields become available.
|
||||
|
||||
@@ -313,7 +315,26 @@ directory. If the Alto is restarted or if ContrAlto exits, this file will
|
||||
be closed. WAV files created by ContrAlto are 16-bit, mono at 13Khz.
|
||||
|
||||
|
||||
4.5 Alternate ("keyboard") Boots
|
||||
4.5 Printing
|
||||
------------
|
||||
|
||||
The Printing tab provides options for the Orbit / Dover print system emulation.
|
||||
The "Enable Printing" checkbox enables or disables print output. If this option
|
||||
is enabled, the "Printing Options" fields become available.
|
||||
|
||||
The "PDF output path" specifies the folder that output PDFs are written to.
|
||||
When the Alto prints a new document, a new PDF file will be created in this
|
||||
directory containing the printer's output.
|
||||
|
||||
The "Reverse Output Page Order" checkbox controls the order in which pages are
|
||||
written to the PDF -- due to the way the original Dover printer worked, most
|
||||
Alto software printed pages in reverse order (i.e. the last page printed
|
||||
first) so that the pages didn't have to be reshuffled when picked up from the
|
||||
tray. By default, leaving this box checked is probably what you want, but
|
||||
if your documents come out backwards, uncheck it.
|
||||
|
||||
|
||||
4.6 Alternate ("keyboard") Boots
|
||||
--------------------------------
|
||||
|
||||
The Alto allowed the specification of alternate boot addresses by holding down
|
||||
@@ -493,10 +514,18 @@ On Unix and OS X, display and keyboard/mouse input is provided through SDL, see:
|
||||
https://www.libsdl.org/ and is accessed using the SDL2# wrapper, see:
|
||||
https://github.com/flibitijibibo/SDL2-CS.
|
||||
|
||||
PDF generation is provided by the iTextSharp library, see: https://github.com/itext.
|
||||
|
||||
|
||||
10.0 Change History
|
||||
===================
|
||||
|
||||
V1.2.1
|
||||
------
|
||||
- Completed implementation of Orbit, Dover ROS and Dover print engine.
|
||||
- Bugfixes to memory state machine around overlapped double-word reads/writes.
|
||||
Smalltalk-80 now runs, as does Spruce.
|
||||
|
||||
V1.2
|
||||
----
|
||||
- First release officially supporting Unix / OS X
|
||||
|
||||
Reference in New Issue
Block a user