diff --git a/Contralto/AltoSystem.cs b/Contralto/AltoSystem.cs
index 605b056..4781963 100644
--- a/Contralto/AltoSystem.cs
+++ b/Contralto/AltoSystem.cs
@@ -242,8 +242,6 @@ namespace Contralto
get { return _scheduler; }
}
- public int _novaInst;
-
private AltoCPU _cpu;
private MemoryBus _memBus;
private Memory.Memory _mem;
diff --git a/Contralto/App.config b/Contralto/App.config
index 7212975..9fb50f4 100644
--- a/Contralto/App.config
+++ b/Contralto/App.config
@@ -58,6 +58,15 @@
+
+ False
+
+
+
+
+
+ True
+
diff --git a/Contralto/CPU/CPU.cs b/Contralto/CPU/CPU.cs
index b68c7e4..f6e0116 100644
--- a/Contralto/CPU/CPU.cs
+++ b/Contralto/CPU/CPU.cs
@@ -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);
}
///
@@ -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;
}
}
diff --git a/Contralto/CPU/Tasks/EmulatorTask.cs b/Contralto/CPU/Tasks/EmulatorTask.cs
index 7c6953e..bb2f4f4 100644
--- a/Contralto/CPU/Tasks/EmulatorTask.cs
+++ b/Contralto/CPU/Tasks/EmulatorTask.cs
@@ -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;
diff --git a/Contralto/CPU/Tasks/OrbitTask.cs b/Contralto/CPU/Tasks/OrbitTask.cs
index 83dbc15..e192a14 100644
--- a/Contralto/CPU/Tasks/OrbitTask.cs
+++ b/Contralto/CPU/Tasks/OrbitTask.cs
@@ -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));
diff --git a/Contralto/CPU/Tasks/Task.cs b/Contralto/CPU/Tasks/Task.cs
index 0c8612d..aa9ce98 100644
--- a/Contralto/CPU/Tasks/Task.cs
+++ b/Contralto/CPU/Tasks/Task.cs
@@ -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;
diff --git a/Contralto/CPU/UCodeDisassembler.cs b/Contralto/CPU/UCodeDisassembler.cs
index 73855fb..a460f54 100644
--- a/Contralto/CPU/UCodeDisassembler.cs
+++ b/Contralto/CPU/UCodeDisassembler.cs
@@ -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));
}
}
}
diff --git a/Contralto/Configuration.cs b/Contralto/Configuration.cs
index be6f8f0..bd6be45 100644
--- a/Contralto/Configuration.cs
+++ b/Contralto/Configuration.cs
@@ -204,6 +204,21 @@ namespace Contralto
///
public static string AudioDACCapturePath;
+ ///
+ /// Whether to enable printing via the Orbit / DoverROS interface.
+ ///
+ public static bool EnablePrinting;
+
+ ///
+ /// Path for print output.
+ ///
+ public static string PrintOutputPath;
+
+ ///
+ /// Whether to reverse the page order when printing.
+ ///
+ public static bool ReversePageOrder;
+
///
/// The components to enable debug logging for.
///
@@ -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();
}
diff --git a/Contralto/Contralto.cfg b/Contralto/Contralto.cfg
index c8f6bcb..e0e177a 100644
--- a/Contralto/Contralto.cfg
+++ b/Contralto/Contralto.cfg
@@ -18,3 +18,8 @@ InterlaceDisplay = False
BootAddress = 0
BootFile = 0
AlternateBootType = Ethernet
+
+# Printing options
+EnablePrinting = true
+PrintOutputPath = .
+ReversePageOrder = true
diff --git a/Contralto/Contralto.csproj b/Contralto/Contralto.csproj
index df2439c..3798567 100644
--- a/Contralto/Contralto.csproj
+++ b/Contralto/Contralto.csproj
@@ -97,6 +97,9 @@
app.manifest
+
+ ..\packages\iTextSharp.5.5.11\lib\itextsharp.dll
+
..\packages\NAudio.1.8.0\lib\net35\NAudio.dll
True
@@ -127,6 +130,9 @@
+
+
+
diff --git a/Contralto/IO/DiskController.cs b/Contralto/IO/DiskController.cs
index 2e6dbd6..0e04068 100644
--- a/Contralto/IO/DiskController.cs
+++ b/Contralto/IO/DiskController.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;
diff --git a/Contralto/IO/DoverROS.cs b/Contralto/IO/DoverROS.cs
index 075d580..ffaa637 100644
--- a/Contralto/IO/DoverROS.cs
+++ b/Contralto/IO/DoverROS.cs
@@ -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 .
+*/
+
+
+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
}
///
- /// 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.
///
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();
+ }
+
+ ///
+ /// 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.
+ ///
+ ///
+ ///
+ ///
+ 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;
+ ///
+ /// Number of passes through the Runout state.
+ ///
+ 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;
+
+
+ ///
+ /// 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.
+ ///
+ ///
+ private ulong _printEngineTimestepInterval = (ulong)((1000.0 / 3500.0) * Conversion.MsecToNsec);
+
+ private byte[] _pageData = new byte[3500 * 512];
private int _rasterNum;
+
+ private IPageSink _pageOutput;
}
}
diff --git a/Contralto/IO/OrbitController.cs b/Contralto/IO/OrbitController.cs
index bb43560..bde5f97 100644
--- a/Contralto/IO/OrbitController.cs
+++ b/Contralto/IO/OrbitController.cs
@@ -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 .
+*/
+
+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;
diff --git a/Contralto/IO/OrganKeyboard.cs b/Contralto/IO/OrganKeyboard.cs
index f9e72ac..9e4479b 100644
--- a/Contralto/IO/OrganKeyboard.cs
+++ b/Contralto/IO/OrganKeyboard.cs
@@ -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 .
+*/
+
+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
{
diff --git a/Contralto/IO/Printing/IPageSink.cs b/Contralto/IO/Printing/IPageSink.cs
new file mode 100644
index 0000000..5ecefba
--- /dev/null
+++ b/Contralto/IO/Printing/IPageSink.cs
@@ -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 .
+*/
+namespace Contralto.IO.Printing
+{
+ ///
+ /// 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).
+ ///
+ public interface IPageSink
+ {
+ ///
+ /// Starts a new document to be printed.
+ ///
+ void StartDoc();
+
+ ///
+ /// Adds a new page to the output. This is provided as a byte array containing 'height' scanlines of the specified width.
+ ///
+ ///
+ ///
+ ///
+ void AddPage(byte[] raster, int width, int height);
+
+ ///
+ /// Ends the document being printed.
+ ///
+ void EndDoc();
+ }
+}
diff --git a/Contralto/IO/Printing/NullPageSink.cs b/Contralto/IO/Printing/NullPageSink.cs
new file mode 100644
index 0000000..c6b995a
--- /dev/null
+++ b/Contralto/IO/Printing/NullPageSink.cs
@@ -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 .
+*/
+namespace Contralto.IO.Printing
+{
+ ///
+ /// The NullPageSink: All pages go to the bit-bucket, for the true paperless office.
+ ///
+ public class NullPageSink : IPageSink
+ {
+ public NullPageSink()
+ {
+
+ }
+
+ public void StartDoc()
+ {
+
+ }
+
+ public void AddPage(byte[] rasters, int width, int height)
+ {
+
+ }
+
+ public void EndDoc()
+ {
+
+ }
+ }
+}
diff --git a/Contralto/IO/Printing/PdfPageSink.cs b/Contralto/IO/Printing/PdfPageSink.cs
new file mode 100644
index 0000000..14741f7
--- /dev/null
+++ b/Contralto/IO/Printing/PdfPageSink.cs
@@ -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 .
+*/
+
+using System.IO;
+
+using iTextSharp.text;
+using iTextSharp.text.pdf;
+using System.Collections.Generic;
+using System;
+using Contralto.Logging;
+
+namespace Contralto.IO.Printing
+{
+ ///
+ /// 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.
+ ///
+ public class PdfPageSink : IPageSink
+ {
+ public PdfPageSink()
+ {
+ _pageImages = new List();
+ }
+
+ 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);
+ }
+ }
+ }
+
+ ///
+ /// 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.)
+ ///
+ private List _pageImages;
+
+ private Document _pdf;
+ }
+}
diff --git a/Contralto/Memory/MemoryBus.cs b/Contralto/Memory/MemoryBus.cs
index 43ed2e0..fe02287 100644
--- a/Contralto/Memory/MemoryBus.cs
+++ b/Contralto/Memory/MemoryBus.cs
@@ -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;
}
}
diff --git a/Contralto/Program.cs b/Contralto/Program.cs
index d878630..ca7d4d7 100644
--- a/Contralto/Program.cs
+++ b/Contralto/Program.cs
@@ -15,10 +15,8 @@
along with ContrAlto. If not, see .
*/
-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;
}
}
diff --git a/Contralto/Properties/AssemblyInfo.cs b/Contralto/Properties/AssemblyInfo.cs
index b342b51..bcb06e9 100644
--- a/Contralto/Properties/AssemblyInfo.cs
+++ b/Contralto/Properties/AssemblyInfo.cs
@@ -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")]
diff --git a/Contralto/Properties/Settings.Designer.cs b/Contralto/Properties/Settings.Designer.cs
index 9cedbe1..f0c06fd 100644
--- a/Contralto/Properties/Settings.Designer.cs
+++ b/Contralto/Properties/Settings.Designer.cs
@@ -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;
+ }
+ }
}
}
diff --git a/Contralto/Properties/Settings.settings b/Contralto/Properties/Settings.settings
index f8f12bb..6ce2097 100644
--- a/Contralto/Properties/Settings.settings
+++ b/Contralto/Properties/Settings.settings
@@ -47,5 +47,14 @@
+
+ False
+
+
+
+
+
+ True
+
\ No newline at end of file
diff --git a/Contralto/UI/AboutBox.Designer.cs b/Contralto/UI/AboutBox.Designer.cs
index 5cd6ec4..d207941 100644
--- a/Contralto/UI/AboutBox.Designer.cs
+++ b/Contralto/UI/AboutBox.Designer.cs
@@ -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;
diff --git a/Contralto/UI/Debugger.Designer.cs b/Contralto/UI/Debugger.Designer.cs
index a9fbe28..610a4e4 100644
--- a/Contralto/UI/Debugger.Designer.cs
+++ b/Contralto/UI/Debugger.Designer.cs
@@ -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;
}
}
\ No newline at end of file
diff --git a/Contralto/UI/Debugger.cs b/Contralto/UI/Debugger.cs
index 0f04f9b..68baf69 100644
--- a/Contralto/UI/Debugger.cs
+++ b/Contralto/UI/Debugger.cs
@@ -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;
-
- ///
- /// Temporary, for debugging floating point ucode issues
- ///
- ///
- 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);
- }
- }
-
- ///
- /// Gets a float from memory in "packed" format
- ///
- ///
- ///
- 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;
-
- }
-
- ///
- /// Gets the float value for register, from alto code
- ///
- ///
- ///
- 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;
- }
-
- ///
- /// Gets the float value for register, from ucode store
- ///
- ///
- ///
- 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;
diff --git a/Contralto/UI/Debugger.resx b/Contralto/UI/Debugger.resx
index a03a29e..c2f72dc 100644
--- a/Contralto/UI/Debugger.resx
+++ b/Contralto/UI/Debugger.resx
@@ -192,6 +192,9 @@
True
+
+ True
+
True
diff --git a/Contralto/UI/SystemOptions.Designer.cs b/Contralto/UI/SystemOptions.Designer.cs
index 5ffb22d..014156c 100644
--- a/Contralto/UI/SystemOptions.Designer.cs
+++ b/Contralto/UI/SystemOptions.Designer.cs
@@ -28,7 +28,7 @@
///
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;
}
}
\ No newline at end of file
diff --git a/Contralto/UI/SystemOptions.cs b/Contralto/UI/SystemOptions.cs
index 7d4af17..23a599b 100644
--- a/Contralto/UI/SystemOptions.cs
+++ b/Contralto/UI/SystemOptions.cs
@@ -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();
+ }
+ }
}
}
diff --git a/Contralto/packages.config b/Contralto/packages.config
index 400781d..22bb297 100644
--- a/Contralto/packages.config
+++ b/Contralto/packages.config
@@ -1,5 +1,6 @@
+
diff --git a/Contralto/readme-mono.txt b/Contralto/readme-mono.txt
index 41571fa..b523b34 100644
--- a/Contralto/readme-mono.txt
+++ b/Contralto/readme-mono.txt
@@ -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
diff --git a/Contralto/readme.txt b/Contralto/readme.txt
index b8763d9..41e43ea 100644
--- a/Contralto/readme.txt
+++ b/Contralto/readme.txt
@@ -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
diff --git a/ContraltoSetup/Product.wxs b/ContraltoSetup/Product.wxs
index 6349803..db96d77 100644
--- a/ContraltoSetup/Product.wxs
+++ b/ContraltoSetup/Product.wxs
@@ -112,6 +112,9 @@
+
+
+