mirror of
https://github.com/livingcomputermuseum/sImlac.git
synced 2026-04-20 01:23:23 +00:00
Implemented most of the PDS-4 display hardware enhancements (including BLINK, which is very important.) Implemented SBL and SBR, multiple indirects, and fixed decdoding of ACT 2 instructions. Rewired interrupt status bits (arbitrarily rearranged on the PDS-4). SSV4 now mostly seems to work, keyboard still non-functional.
This commit is contained in:
@@ -27,7 +27,9 @@ namespace imlac
|
||||
{
|
||||
Indeterminate,
|
||||
Processor,
|
||||
Increment
|
||||
Increment,
|
||||
MediumVector,
|
||||
CompactAddressing,
|
||||
}
|
||||
|
||||
public enum ImmediateHalf
|
||||
@@ -53,6 +55,7 @@ namespace imlac
|
||||
public virtual void Reset()
|
||||
{
|
||||
State = ProcessorState.Halted;
|
||||
_halted = false;
|
||||
_mode = DisplayProcessorMode.Processor;
|
||||
_pc = 0;
|
||||
_block = 0;
|
||||
@@ -63,7 +66,7 @@ namespace imlac
|
||||
|
||||
_dadr = false;
|
||||
|
||||
_system.Display.MoveAbsolute(X, Y, DrawingMode.Off);
|
||||
_system.Display.MoveAbsolute(0, 0, DrawingMode.Off);
|
||||
|
||||
_clocks = 0;
|
||||
_frameLatch = false;
|
||||
@@ -105,6 +108,11 @@ namespace imlac
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsHalted
|
||||
{
|
||||
get { return _halted; }
|
||||
}
|
||||
|
||||
public DisplayProcessorMode Mode
|
||||
{
|
||||
get { return _mode; }
|
||||
@@ -137,7 +145,7 @@ namespace imlac
|
||||
set { _frameLatch = value; }
|
||||
}
|
||||
|
||||
public uint X
|
||||
public int X
|
||||
{
|
||||
get { return _x; }
|
||||
set
|
||||
@@ -146,7 +154,7 @@ namespace imlac
|
||||
}
|
||||
}
|
||||
|
||||
public uint Y
|
||||
public int Y
|
||||
{
|
||||
get { return _y; }
|
||||
set
|
||||
@@ -183,6 +191,20 @@ namespace imlac
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void StartProcessor()
|
||||
{
|
||||
State = ProcessorState.Running;
|
||||
// MIT DADR bit gets reset when display is started.
|
||||
_dadr = false;
|
||||
_halted = false;
|
||||
}
|
||||
|
||||
public virtual void HaltProcessor()
|
||||
{
|
||||
State = ProcessorState.Halted;
|
||||
_halted = true;
|
||||
}
|
||||
|
||||
public abstract void InitializeCache();
|
||||
|
||||
public abstract void InvalidateCache(ushort address);
|
||||
@@ -195,13 +217,8 @@ namespace imlac
|
||||
|
||||
public abstract void ExecuteIOT(int iotCode);
|
||||
|
||||
protected void ReturnFromDisplaySubroutine()
|
||||
{
|
||||
Pop();
|
||||
}
|
||||
|
||||
protected uint _x;
|
||||
protected uint _y;
|
||||
protected int _x;
|
||||
protected int _y;
|
||||
protected float _scale;
|
||||
protected ushort _pc;
|
||||
protected ushort _block;
|
||||
@@ -219,6 +236,9 @@ namespace imlac
|
||||
protected bool _frameLatch;
|
||||
|
||||
protected ProcessorState _state;
|
||||
protected bool _halted; // The Halted flag is independent of the current state.
|
||||
// (i.e. it is set when the processor gets halted, but can later be cleared
|
||||
// whiile the display remains halted.)
|
||||
protected DisplayProcessorMode _mode;
|
||||
protected ImlacSystem _system;
|
||||
protected Memory _mem;
|
||||
|
||||
@@ -173,6 +173,16 @@ namespace imlac
|
||||
get;
|
||||
}
|
||||
|
||||
int MouseX
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
int MouseY
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
bool ThrottleFramerate
|
||||
{
|
||||
get;
|
||||
@@ -201,9 +211,9 @@ namespace imlac
|
||||
|
||||
void ClearDisplay();
|
||||
|
||||
void MoveAbsolute(uint x, uint y, DrawingMode mode);
|
||||
void MoveAbsolute(int x, int y, DrawingMode mode);
|
||||
|
||||
void DrawPoint(uint x, uint y);
|
||||
void DrawPoint(int x, int y);
|
||||
|
||||
void RenderCurrent(bool completeFrame);
|
||||
|
||||
@@ -211,6 +221,10 @@ namespace imlac
|
||||
|
||||
void SetScale(float scale);
|
||||
|
||||
void SetIntensity(int intensity);
|
||||
|
||||
void SetBlink(bool on);
|
||||
|
||||
void MapDataSwitch(uint switchNumber, VKeys key);
|
||||
|
||||
VKeys GetDataSwitchMapping(uint switchNumber);
|
||||
|
||||
@@ -74,39 +74,81 @@ namespace imlac.IO
|
||||
// Collect up devices that want to interrupt us.
|
||||
_interruptStatus = 0;
|
||||
|
||||
// bit 14: 40 cycle sync
|
||||
if (_system.DisplayProcessor.FrameLatch)
|
||||
//
|
||||
// PDS-1 and PDS-4 status bits are arranged almost entirely differently.
|
||||
//
|
||||
if (Configuration.CPUType == ImlacCPUType.PDS1)
|
||||
{
|
||||
_interruptStatus |= 0x0002;
|
||||
}
|
||||
// PDS-1:
|
||||
|
||||
// bit 12 - TTY rcv
|
||||
if (_system.TTY.DataReady)
|
||||
{
|
||||
_interruptStatus |= 0x0008;
|
||||
}
|
||||
// bit 14: 40 cycle sync
|
||||
if (_system.DisplayProcessor.FrameLatch)
|
||||
{
|
||||
_interruptStatus |= 0x0002;
|
||||
}
|
||||
|
||||
// bit 11 - keyboard
|
||||
if (_system.Keyboard.KeyReady)
|
||||
{
|
||||
_interruptStatus |= 0x0010;
|
||||
}
|
||||
// bit 12 - TTY rcv
|
||||
if (_system.TTY.DataReady)
|
||||
{
|
||||
_interruptStatus |= 0x0008;
|
||||
}
|
||||
|
||||
// bit 10 - TTY send
|
||||
if (_system.TTY.DataSentLatch)
|
||||
{
|
||||
_interruptStatus |= 0x0020;
|
||||
}
|
||||
// bit 11 - keyboard
|
||||
if (_system.Keyboard.KeyReady)
|
||||
{
|
||||
_interruptStatus |= 0x0010;
|
||||
}
|
||||
|
||||
// bit 2 - ACI-1 (clock)
|
||||
if (_system.Clock.TimerTriggered)
|
||||
// bit 10 - TTY send
|
||||
if (_system.TTY.DataSentLatch)
|
||||
{
|
||||
_interruptStatus |= 0x0020;
|
||||
}
|
||||
|
||||
// bit 2 - ACI-1 (clock)
|
||||
if (_system.Clock.TimerTriggered)
|
||||
{
|
||||
_interruptStatus |= 0x2000;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_interruptStatus |= 0x2000;
|
||||
// PDS-4:
|
||||
|
||||
// Bit 15: Display halt
|
||||
if (_system.DisplayProcessor.IsHalted)
|
||||
{
|
||||
_interruptStatus |= 0x0001;
|
||||
}
|
||||
|
||||
// Bit 14: 40 cycle sync
|
||||
if (_system.DisplayProcessor.FrameLatch)
|
||||
{
|
||||
_interruptStatus |= 0x0002;
|
||||
}
|
||||
|
||||
// Bit 2: TTY Send
|
||||
if (_system.TTY.DataSentLatch)
|
||||
{
|
||||
_interruptStatus |= 0x2000;
|
||||
}
|
||||
|
||||
// Bit 1: TTY Receive
|
||||
if (_system.TTY.DataReady)
|
||||
{
|
||||
_interruptStatus |= 0x4000;
|
||||
}
|
||||
|
||||
// Bit 0: TTY Keyboard
|
||||
if (_system.Keyboard.KeyReady)
|
||||
{
|
||||
_interruptStatus |= 0x8000;
|
||||
}
|
||||
}
|
||||
|
||||
// mask it with our interrupt mask and if non-zero then we have a pending interrupt,
|
||||
// which we will execute at the start of the next CPU instruction.
|
||||
if ((_interruptMask & _interruptStatus) != 0)
|
||||
if (_interruptsEnabled && (_interruptMask & _interruptStatus) != 0)
|
||||
{
|
||||
_interruptPending = true;
|
||||
}
|
||||
@@ -115,7 +157,7 @@ namespace imlac.IO
|
||||
// If we have an interrupt pending and the processor is starting the next instruction
|
||||
// we will interrupt now, otherwise we wait until the processor is ready.
|
||||
//
|
||||
if (_interruptPending && _system.Processor.InstructionState == ExecState.Fetch)
|
||||
if (_interruptPending && _system.Processor.CanBeInterrupted())
|
||||
{
|
||||
// save the current PC at 0
|
||||
_system.Memory.Store(0x0000, _system.Processor.PC);
|
||||
@@ -158,7 +200,13 @@ namespace imlac.IO
|
||||
|
||||
case 0x41:
|
||||
_system.Processor.AC |= (ushort)(_interruptStatus);
|
||||
Trace.Log(LogType.Interrupt, "Interrupt status {0} copied to AC", Helpers.ToOctal((ushort)_interruptStatus));
|
||||
Trace.Log(LogType.Interrupt, "Interrupt status word 1 {0} copied to AC", Helpers.ToOctal((ushort)_interruptStatus));
|
||||
break;
|
||||
|
||||
case 0x42:
|
||||
// TODO: implement second status register when we have devices that use it
|
||||
// implemented.
|
||||
Trace.Log(LogType.Interrupt, "Interrupt status word 2 STUB.");
|
||||
break;
|
||||
|
||||
case 0x61:
|
||||
@@ -168,7 +216,7 @@ namespace imlac.IO
|
||||
|
||||
|
||||
default:
|
||||
throw new NotImplementedException(String.Format("Unimplemented Interrupt IOT instruction {0:x4}", iotCode));
|
||||
throw new NotImplementedException(String.Format("Unimplemented Interrupt IOT instruction {0}", Helpers.ToOctal((ushort)iotCode)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,8 +234,8 @@ namespace imlac.IO
|
||||
0x71, // IOF (disable interrupts)
|
||||
0x72, // ION (enabled masked interrupts)
|
||||
|
||||
// PDS-4 2nd level interrupt facility
|
||||
0x42, // read interrupt status bits word two
|
||||
// 2nd level interrupt facility
|
||||
0x42, // Read Interrupt Status Bits Word Two
|
||||
0x89, // Read 2nd Level Interrupt status
|
||||
0x91, // Disable 2nd Level Interrupts
|
||||
0x92, // Enable 2nd Level Interrupts
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace imlac.IO
|
||||
if (_dataBufferFull && _dataChannel.OutputReady)
|
||||
{
|
||||
_dataChannel.Write(_txData);
|
||||
Trace.Log(LogType.TTY, "o");
|
||||
Trace.Log(LogType.TTY, "o {0}", Helpers.ToOctal(_txData));
|
||||
_dataBufferFull = false;
|
||||
_dataSentLatch = true;
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ namespace imlac
|
||||
break;
|
||||
|
||||
case 0x0a: // Halt display processor
|
||||
State = ProcessorState.Halted;
|
||||
HaltProcessor();
|
||||
break;
|
||||
|
||||
case 0x39: // Clear display 40Hz sync latch
|
||||
@@ -128,10 +128,7 @@ namespace imlac
|
||||
|
||||
case 0xc4: // clear halt state
|
||||
// TODO: what does this actually do?
|
||||
//State = ProcessorState.Running;
|
||||
|
||||
// MIT DADR bit gets reset when display is started.
|
||||
_dadr = false;
|
||||
_halted = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -187,7 +184,7 @@ namespace imlac
|
||||
{
|
||||
// DHLT -- halt the display processor. other micro-ops in this
|
||||
// instruction are still run.
|
||||
State = ProcessorState.Halted;
|
||||
HaltProcessor();
|
||||
}
|
||||
|
||||
if ((instruction.Data & 0x400) != 0)
|
||||
@@ -292,7 +289,7 @@ namespace imlac
|
||||
break;
|
||||
|
||||
case DisplayOpcode.DLXA:
|
||||
X = (uint)(instruction.Data << 1);
|
||||
X = instruction.Data << 1;
|
||||
|
||||
DrawingMode mode;
|
||||
if (_sgrModeOn && _sgrBeamOn)
|
||||
@@ -319,7 +316,7 @@ namespace imlac
|
||||
break;
|
||||
|
||||
case DisplayOpcode.DLYA:
|
||||
Y = (uint)(instruction.Data << 1);
|
||||
Y = instruction.Data << 1;
|
||||
|
||||
if (_sgrModeOn && _sgrBeamOn)
|
||||
{
|
||||
@@ -371,7 +368,10 @@ namespace imlac
|
||||
private void ExecuteIncrement()
|
||||
{
|
||||
int halfWord = _immediateHalf == ImmediateHalf.First ? (_immediateWord & 0xff00) >> 8 : (_immediateWord & 0xff);
|
||||
|
||||
|
||||
int newX = (int)X;
|
||||
int newY = (int)Y;
|
||||
|
||||
// translate the half word to vector movements or escapes
|
||||
if ((halfWord & 0x80) == 0)
|
||||
{
|
||||
@@ -398,29 +398,29 @@ namespace imlac
|
||||
|
||||
if ((halfWord & 0x10) != 0)
|
||||
{
|
||||
X += 0x20;
|
||||
newX += 0x20;
|
||||
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "Increment X MSB, X is now {0}", X);
|
||||
}
|
||||
|
||||
if ((halfWord & 0x08) != 0)
|
||||
{
|
||||
X = X & (0xffe0);
|
||||
newX = newX & (0xffe0);
|
||||
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "Reset X LSB, X is now {0}", X);
|
||||
}
|
||||
|
||||
if ((halfWord & 0x02) != 0)
|
||||
{
|
||||
Y += 0x20;
|
||||
newY += 0x20;
|
||||
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "Increment Y MSB, Y is now {0}", Y);
|
||||
}
|
||||
|
||||
if ((halfWord & 0x01) != 0)
|
||||
{
|
||||
Y = Y & (0xffe0);
|
||||
newY = newY & (0xffe0);
|
||||
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "Reset Y LSB, Y is now {0}", Y);
|
||||
}
|
||||
|
||||
_system.Display.MoveAbsolute(X, Y, DrawingMode.Off);
|
||||
_system.Display.MoveAbsolute(newX, newY, DrawingMode.Off);
|
||||
|
||||
}
|
||||
else
|
||||
@@ -431,15 +431,28 @@ namespace imlac
|
||||
int ySign = (int)(((halfWord & 0x04) == 0) ? 1 : -1);
|
||||
int yMag = (int)((halfWord & 0x03) * _scale);
|
||||
|
||||
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "Inc mode ({0}:{1}), x={2} y={3} dx={4} dy={5} beamon {6}", Helpers.ToOctal((ushort)_pc), Helpers.ToOctal((ushort)halfWord), X, Y, xSign * xMag, ySign * yMag, (halfWord & 0x40) != 0);
|
||||
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor,
|
||||
"Inc mode ({0}:{1}), x={2} y={3} dx={4} dy={5} beamon {6}",
|
||||
Helpers.ToOctal((ushort)_pc),
|
||||
Helpers.ToOctal((ushort)halfWord),
|
||||
newX,
|
||||
newY,
|
||||
xSign * xMag,
|
||||
ySign * yMag,
|
||||
(halfWord & 0x40) != 0);
|
||||
|
||||
X = (uint)(X + xSign * xMag * 2);
|
||||
Y = (uint)(Y + ySign * yMag * 2);
|
||||
_system.Display.MoveAbsolute(X, Y, (halfWord & 0x40) == 0 ? DrawingMode.Off : DrawingMode.Normal);
|
||||
newX = (int)(newX + xSign * xMag * 2);
|
||||
newY = (int)(newY + ySign * yMag * 2);
|
||||
|
||||
_system.Display.MoveAbsolute(newX, newY, (halfWord & 0x40) == 0 ? DrawingMode.Off : DrawingMode.Dotted);
|
||||
|
||||
MoveToNextHalfWord();
|
||||
}
|
||||
|
||||
// Assign back to X, Y registers; clipping into range.
|
||||
X = newX;
|
||||
Y = newY;
|
||||
|
||||
// If the next instruction has a breakpoint set we'll halt at this point, before executing it.
|
||||
if (_immediateHalf == ImmediateHalf.First && BreakpointManager.TestBreakpoint(BreakpointType.Display, _pc))
|
||||
{
|
||||
@@ -510,16 +523,25 @@ namespace imlac
|
||||
// * 2 for translation to 11-bit space
|
||||
// The docs don't call this out, but the scale setting used in increment mode appears to apply
|
||||
// to the LVH vectors as well. (Maze appears to rely on this.)
|
||||
X = (uint)(X + (dx * dxSign) * 2 * _scale);
|
||||
Y = (uint)(Y + (dy * dySign) * 2 * _scale);
|
||||
int newX = (int)(X + (dx * dxSign) * 2 * _scale);
|
||||
int newY = (int)(Y + (dy * dySign) * 2 * _scale);
|
||||
|
||||
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "LongVector, move complete - x={0} y={1}", X, Y, dx * dxSign, dy * dySign, beamOn, dotted);
|
||||
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "LongVector, move complete - x={0} y={1}", newX, newY, dx * dxSign, dy * dySign, beamOn, dotted);
|
||||
|
||||
_system.Display.MoveAbsolute(X, Y, beamOn ? (dotted ? DrawingMode.Dotted : DrawingMode.Normal) : DrawingMode.Off);
|
||||
_system.Display.MoveAbsolute(newX, newY, beamOn ? (dotted ? DrawingMode.Dotted : DrawingMode.Normal) : DrawingMode.Off);
|
||||
|
||||
// Assign back to X, Y registers; clipping into range.
|
||||
X = newX;
|
||||
Y = newY;
|
||||
|
||||
_pc++;
|
||||
}
|
||||
|
||||
private void ReturnFromDisplaySubroutine()
|
||||
{
|
||||
Pop();
|
||||
}
|
||||
|
||||
private PDS1DisplayInstruction GetCachedInstruction(ushort address, DisplayProcessorMode mode)
|
||||
{
|
||||
if (_instructionCache[address & Memory.SizeMask] == null)
|
||||
@@ -610,9 +632,9 @@ namespace imlac
|
||||
_opcode = DisplayOpcode.DEIM;
|
||||
_data = (ushort)(_word & 0xff);
|
||||
|
||||
if ((_word & 0x0800) != 0)
|
||||
if ((_word & 0xff00) == 0x3800)
|
||||
{
|
||||
Console.Write("PPM-1 not implemented (instr {0})", Helpers.ToOctal(_word));
|
||||
Console.WriteLine("PPM-1 not implemented (instr {0})", Helpers.ToOctal(_word));
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -41,6 +41,12 @@ namespace imlac
|
||||
_fxyOn = false;
|
||||
_fxyBeamOn = false;
|
||||
_fxyDRJMOn = false;
|
||||
|
||||
_camEnabled = false;
|
||||
_caBase = 0;
|
||||
_camHalf = ImmediateHalf.First;
|
||||
_camWord = 0;
|
||||
_camStackDepth = 0;
|
||||
}
|
||||
|
||||
public override void InitializeCache()
|
||||
@@ -95,6 +101,10 @@ namespace imlac
|
||||
case DisplayProcessorMode.Increment:
|
||||
ExecuteIncrement();
|
||||
break;
|
||||
|
||||
case DisplayProcessorMode.CompactAddressing:
|
||||
ExecuteCompactAddressing();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +113,21 @@ namespace imlac
|
||||
return _handledIOTs;
|
||||
}
|
||||
|
||||
public override void StartProcessor()
|
||||
{
|
||||
base.StartProcessor();
|
||||
|
||||
// Beam intensity is set to maximum after display is enabled.
|
||||
_system.Display.SetIntensity(16);
|
||||
_system.Display.SetBlink(false);
|
||||
}
|
||||
|
||||
public override void HaltProcessor()
|
||||
{
|
||||
base.HaltProcessor();
|
||||
_camEnabled = false;
|
||||
}
|
||||
|
||||
public override void ExecuteIOT(int iotCode)
|
||||
{
|
||||
//
|
||||
@@ -120,20 +145,16 @@ namespace imlac
|
||||
|
||||
if (iotCode == 0x03)
|
||||
{
|
||||
State = ProcessorState.Running;
|
||||
// MIT DADR bit gets reset when display is started.
|
||||
_dadr = false;
|
||||
StartProcessor();
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x02: // Turn DP On.
|
||||
State = ProcessorState.Running;
|
||||
// MIT DADR bit gets reset when display is started.
|
||||
_dadr = false;
|
||||
StartProcessor();
|
||||
break;
|
||||
|
||||
case 0x0a: // Halt display processor
|
||||
State = ProcessorState.Halted;
|
||||
HaltProcessor();
|
||||
break;
|
||||
|
||||
case 0x39: // Clear display 40Hz sync latch
|
||||
@@ -141,11 +162,7 @@ namespace imlac
|
||||
break;
|
||||
|
||||
case 0xc4: // clear halt state
|
||||
// TODO: what does this actually do?
|
||||
//State = ProcessorState.Running;
|
||||
|
||||
// MIT DADR bit gets reset when display is started.
|
||||
_dadr = false;
|
||||
_halted = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -201,7 +218,7 @@ namespace imlac
|
||||
{
|
||||
// DHLT -- halt the display processor. other micro-ops in this
|
||||
// instruction are still run.
|
||||
State = ProcessorState.Halted;
|
||||
HaltProcessor();
|
||||
}
|
||||
|
||||
// Used to modify DSTS or DSTB operation
|
||||
@@ -272,8 +289,6 @@ namespace imlac
|
||||
case 0x1:
|
||||
// Set scale based on C and Bit 5.
|
||||
_scale = c + (bit5 ? 4 : 0);
|
||||
|
||||
Console.WriteLine("scale {0}", _scale);
|
||||
|
||||
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "Scale set to {0}", _scale);
|
||||
break;
|
||||
@@ -361,6 +376,48 @@ namespace imlac
|
||||
_pc++;
|
||||
break;
|
||||
|
||||
case DisplayOpcode.DVIC:
|
||||
_system.Display.SetIntensity(instruction.Data);
|
||||
_pc++;
|
||||
break;
|
||||
|
||||
case DisplayOpcode.DCAM:
|
||||
// Enter Compact Addressing Mode, this is supposedly illegal if
|
||||
// we're already in that mode, so we'll throw here to help with debugging
|
||||
if (_camEnabled)
|
||||
{
|
||||
throw new InvalidOperationException("DCAM while in Compact Addressing Mode.");
|
||||
}
|
||||
|
||||
_camEnabled = true;
|
||||
|
||||
// subroutine table address is the next word. Low 8-bits should be zero, we'll
|
||||
// sanity check it.
|
||||
_caBase = _mem.Fetch(++_pc);
|
||||
|
||||
if ((_caBase & 0xff) != 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
String.Format("CAM subroutine base address {0} not on a 256-word boundary!",
|
||||
Helpers.ToOctal(_caBase)));
|
||||
}
|
||||
|
||||
// start things off by fetching the first subroutine word. This is not strictly
|
||||
// accurate with respect to timing. (Ok, it's not accurate at all.)
|
||||
// TODO: refactor Immediate halfword routines here (share w/short vectors?)
|
||||
_camWord = _mem.Fetch(++_pc);
|
||||
_camHalf = ImmediateHalf.First;
|
||||
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "Enter Compact Addressing mode, base address {0}",
|
||||
Helpers.ToOctal(_caBase));
|
||||
|
||||
_mode = DisplayProcessorMode.CompactAddressing;
|
||||
break;
|
||||
|
||||
case DisplayOpcode.DBLI:
|
||||
_system.Display.SetBlink((instruction.Data) != 0);
|
||||
_pc++;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException(String.Format("Unimplemented Display Processor Opcode {0}, ({1}), operands {1}",
|
||||
instruction.Opcode,
|
||||
@@ -442,8 +499,8 @@ namespace imlac
|
||||
|
||||
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "Inc mode ({0}:{1}), x={2} y={3} dx={4} dy={5} beamon {6}", Helpers.ToOctal((ushort)_pc), Helpers.ToOctal((ushort)halfWord), X, Y, xSign * xMag, ySign * yMag, (halfWord & 0x40) != 0);
|
||||
|
||||
X = (uint)(X + xSign * xMag);
|
||||
Y = (uint)(Y + ySign * yMag);
|
||||
X = X + xSign * xMag;
|
||||
Y = Y + ySign * yMag;
|
||||
_system.Display.MoveAbsolute(X, Y, (halfWord & 0x40) == 0 ? DrawingMode.Off : DrawingMode.Normal);
|
||||
|
||||
MoveToNextHalfWord();
|
||||
@@ -465,7 +522,7 @@ namespace imlac
|
||||
_immediateHalf = ImmediateHalf.First;
|
||||
|
||||
// Update the instruction cache with the type of instruction (to aid in debugging).
|
||||
PDS4DisplayInstruction instruction = GetCachedInstruction(_pc, DisplayProcessorMode.Increment);
|
||||
PDS4DisplayInstruction instruction = GetCachedInstruction(_pc, DisplayProcessorMode.Increment);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -473,6 +530,63 @@ namespace imlac
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecuteCompactAddressing()
|
||||
{
|
||||
int halfWord = _camHalf == ImmediateHalf.First ? (_camWord & 0xff00) >> 8 : (_camWord & 0xff);
|
||||
|
||||
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor,
|
||||
"CAM Halfword {2} is {0} (fullword {1})", Helpers.ToOctal((ushort)halfWord), Helpers.ToOctal(_camWord), _camHalf);
|
||||
|
||||
// Is this an exit?
|
||||
if (halfWord == 0xff)
|
||||
{
|
||||
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "CAM: Exit from CAM.");
|
||||
_camEnabled = false;
|
||||
_pc++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do a subroutine jump to the routine at base address + routine halfword
|
||||
ushort subroutineAddress = (ushort)(_caBase | halfWord);
|
||||
|
||||
_camStackDepth = _dtStack.Count;
|
||||
|
||||
// Store the current PC on the MDS stack
|
||||
_pc--;
|
||||
Push();
|
||||
|
||||
// TODO: does the MIT DADR mod factor in here? I assume not because
|
||||
// the standard DSTB mechanism doesn't apply either
|
||||
|
||||
// Jump to the subroutine at table address. On return to this stack depth
|
||||
// we will return to CAM to pick up the next halfword.
|
||||
_pc = _mem.Fetch(subroutineAddress);
|
||||
|
||||
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "CAM: Jump to subroutine at {0}",
|
||||
Helpers.ToOctal(_pc));
|
||||
}
|
||||
|
||||
// Return to Processor mode for the duration of the subroutine.
|
||||
_mode = DisplayProcessorMode.Processor;
|
||||
}
|
||||
|
||||
private void MoveToNextCAMHalfWord()
|
||||
{
|
||||
if (_camHalf == ImmediateHalf.Second)
|
||||
{
|
||||
_pc++;
|
||||
_camWord = _mem.Fetch(_pc);
|
||||
_camHalf = ImmediateHalf.First;
|
||||
|
||||
// Update the instruction cache with the type of instruction (to aid in debugging).
|
||||
PDS4DisplayInstruction instruction = GetCachedInstruction(_pc, DisplayProcessorMode.CompactAddressing);
|
||||
}
|
||||
else
|
||||
{
|
||||
_camHalf = ImmediateHalf.Second;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawLongVector(ushort word0)
|
||||
{
|
||||
//
|
||||
@@ -529,8 +643,8 @@ namespace imlac
|
||||
|
||||
// The docs don't call this out, but the scale setting used in increment mode appears to apply
|
||||
// to the LVH vectors as well. (Maze appears to rely on this.)
|
||||
X = (uint)(X + (dx * dxSign) * _scale);
|
||||
Y = (uint)(Y + (dy * dySign) * _scale);
|
||||
X = (int)(X + (dx * dxSign) * _scale);
|
||||
Y = (int)(Y + (dy * dySign) * _scale);
|
||||
|
||||
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "LongVector, move complete - x={0} y={1}", X, Y, dx * dxSign, dy * dySign, beamOn, dotted);
|
||||
|
||||
@@ -545,6 +659,21 @@ namespace imlac
|
||||
}
|
||||
}
|
||||
|
||||
private void ReturnFromDisplaySubroutine()
|
||||
{
|
||||
Pop();
|
||||
|
||||
// If CAM is enabled, we return to CAM mode to pick up the next word.
|
||||
if (_camEnabled && _camStackDepth == _dtStack.Count)
|
||||
{
|
||||
MoveToNextCAMHalfWord();
|
||||
_mode = DisplayProcessorMode.CompactAddressing;
|
||||
|
||||
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "CAM: Return from subroutine. PC {0} halfword {1}",
|
||||
Helpers.ToOctal(_pc), _camHalf);
|
||||
}
|
||||
}
|
||||
|
||||
private PDS4DisplayInstruction GetCachedInstruction(ushort address, DisplayProcessorMode mode)
|
||||
{
|
||||
if (_instructionCache[address & Memory.SizeMask] == null)
|
||||
@@ -554,12 +683,23 @@ namespace imlac
|
||||
|
||||
return _instructionCache[address & Memory.SizeMask];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// SGR-1 mode switches
|
||||
private bool _fxyOn;
|
||||
private bool _fxyDRJMOn;
|
||||
private bool _fxyBeamOn;
|
||||
|
||||
// CAM data -- enabled bit and base address.
|
||||
private bool _camEnabled;
|
||||
private ushort _caBase;
|
||||
private int _camStackDepth;
|
||||
|
||||
protected ushort _camWord;
|
||||
protected ImmediateHalf _camHalf;
|
||||
|
||||
/// <summary>
|
||||
/// TODO: All available PDS-4 docs insist that the X/Y AC LSB is four bits wide.
|
||||
/// This was 5 bits on the PDS-1...
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace imlac
|
||||
{
|
||||
Fetch,
|
||||
Defer,
|
||||
ExtraDefer,
|
||||
Execute
|
||||
}
|
||||
|
||||
@@ -59,6 +60,7 @@ namespace imlac
|
||||
_currentIndirectAddress = 0x0000;
|
||||
_instructionState = ExecState.Fetch;
|
||||
_state = ProcessorState.Halted;
|
||||
_byteAccess = ByteAccess.Normal;
|
||||
}
|
||||
|
||||
public void RegisterDeviceIOTs(IIOTDevice device)
|
||||
@@ -124,6 +126,13 @@ namespace imlac
|
||||
get { return _breakpointAddress; }
|
||||
}
|
||||
|
||||
public bool CanBeInterrupted()
|
||||
{
|
||||
// We can be interrupted while in the Fetch state, iff we are not executing
|
||||
// an instruction modified by SBR or SBL (byte-wise two-instruction ops are atomic).
|
||||
return _instructionState == ExecState.Fetch && _byteAccess == ByteAccess.Normal;
|
||||
}
|
||||
|
||||
public string Disassemble(ushort address)
|
||||
{
|
||||
string disassembly;
|
||||
@@ -195,21 +204,49 @@ namespace imlac
|
||||
case ExecState.Defer:
|
||||
//
|
||||
// Get the indirect address for this instruction.
|
||||
//
|
||||
// On the PDS-4, indirection can continue indefinitely.
|
||||
//
|
||||
ushort effectiveAddress = GetEffectiveAddress(_currentInstruction.Data);
|
||||
|
||||
_currentIndirectAddress = _mem.Fetch(effectiveAddress);
|
||||
|
||||
//
|
||||
// If this is an auto-increment indirect index register (octal locations 10-17 on each 2k page)
|
||||
// then we will increment the contents (and the indirect address).
|
||||
//
|
||||
if ((effectiveAddress & 0x7ff) >= 0x08 && (effectiveAddress & 0x7ff) < 0x10)
|
||||
if (Configuration.CPUType == ImlacCPUType.PDS1 ||
|
||||
(_pc >= 0x20 && _pc < 0x40) || _pc == 0x3ff7 || _pc == 0x3fe6) // latter is a hack to fix broken bootstrap on pds-4
|
||||
{
|
||||
_currentIndirectAddress++;
|
||||
_mem.Store(effectiveAddress, (ushort)(_currentIndirectAddress));
|
||||
// done, only one level of indirection on the PDS-1.
|
||||
_instructionState = ExecState.Execute;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((_currentIndirectAddress & 0x8000) != 0)
|
||||
{
|
||||
// Indirect bit is set -- continue for another level of indirection.
|
||||
_instructionState = ExecState.ExtraDefer;
|
||||
}
|
||||
else
|
||||
{
|
||||
_instructionState = ExecState.Execute;
|
||||
}
|
||||
}
|
||||
|
||||
AutoIncrement(effectiveAddress);
|
||||
break;
|
||||
|
||||
case ExecState.ExtraDefer:
|
||||
|
||||
// Continue indirection using the current indirect address:
|
||||
_currentIndirectAddress = _mem.Fetch(_currentIndirectAddress);
|
||||
|
||||
_instructionState = ExecState.Execute;
|
||||
if ((_currentIndirectAddress & 0x8000) == 0)
|
||||
{
|
||||
// Indirect bit is unset; exit from this state and finally
|
||||
// go execute the instruction.
|
||||
_instructionState = ExecState.Execute;
|
||||
}
|
||||
|
||||
// TODO: Do auto-index locations apply for multiple indirects?
|
||||
AutoIncrement(_currentIndirectAddress);
|
||||
|
||||
break;
|
||||
|
||||
case ExecState.Execute:
|
||||
@@ -223,6 +260,26 @@ namespace imlac
|
||||
}
|
||||
}
|
||||
|
||||
private void AutoIncrement(ushort effectiveAddress)
|
||||
{
|
||||
//
|
||||
// If this is an auto-increment indirect index register (octal locations 10-17 on each 2k page)
|
||||
// then we will increment the contents (and the indirect address).
|
||||
// On the PDS-4 there are auto-decrement registers at octal 20-27).
|
||||
//
|
||||
if ((effectiveAddress & 0x7ff) >= 0x08 && (effectiveAddress & 0x7ff) < 0x10)
|
||||
{
|
||||
_currentIndirectAddress++;
|
||||
_mem.Store(effectiveAddress, (ushort)(_currentIndirectAddress));
|
||||
}
|
||||
else if (Configuration.CPUType == ImlacCPUType.PDS4 &&
|
||||
(effectiveAddress & 0x7ff) >= 0x10 && (effectiveAddress & 0x7ff) < 0x18)
|
||||
{
|
||||
_currentIndirectAddress--;
|
||||
_mem.Store(effectiveAddress, (ushort)(_currentIndirectAddress));
|
||||
}
|
||||
}
|
||||
|
||||
private Instruction GetCachedInstruction(ushort address)
|
||||
{
|
||||
// TODO: factor this masking logic out.
|
||||
@@ -239,7 +296,7 @@ namespace imlac
|
||||
ushort q;
|
||||
uint res;
|
||||
|
||||
if (Trace.TraceOn) Trace.Log(LogType.Processor, "{0} - {1}", _pc, _currentInstruction.Disassemble(_pc));
|
||||
// if (Trace.TraceOn) Trace.Log(LogType.Processor, "{0} - {1}", _pc, _currentInstruction.Disassemble(_pc));
|
||||
|
||||
switch (_currentInstruction.Opcode)
|
||||
{
|
||||
@@ -267,8 +324,14 @@ namespace imlac
|
||||
_pc++;
|
||||
break;
|
||||
|
||||
case Opcode.DACS:
|
||||
_sp[_currentInstruction.Data] = _ac;
|
||||
_pc++;
|
||||
break;
|
||||
|
||||
case Opcode.DCM: // PDS-4 only
|
||||
// Decrement memory by 1
|
||||
_byteAccess = ByteAccess.Normal;
|
||||
q = DoFetchForCurrentInstruction();
|
||||
DoStoreForCurrentInstruction((ushort)(q - 1));
|
||||
_pc++;
|
||||
@@ -295,6 +358,7 @@ namespace imlac
|
||||
break;
|
||||
|
||||
case Opcode.ISZ:
|
||||
_byteAccess = ByteAccess.Normal;
|
||||
q = DoFetchForCurrentInstruction();
|
||||
q++;
|
||||
DoStoreForCurrentInstruction(q);
|
||||
@@ -308,6 +372,7 @@ namespace imlac
|
||||
break;
|
||||
|
||||
case Opcode.JMP:
|
||||
_byteAccess = ByteAccess.Normal;
|
||||
if (_currentInstruction.IsIndirect)
|
||||
{
|
||||
_pc = _currentIndirectAddress;
|
||||
@@ -319,6 +384,7 @@ namespace imlac
|
||||
break;
|
||||
|
||||
case Opcode.JMS:
|
||||
_byteAccess = ByteAccess.Normal;
|
||||
// Store next PC at location specified by instruction (Q),
|
||||
// continue execution at Q+1
|
||||
DoStoreForCurrentInstruction((ushort)(_pc + 1));
|
||||
@@ -338,9 +404,15 @@ namespace imlac
|
||||
_pc++;
|
||||
break;
|
||||
|
||||
case Opcode.LACS:
|
||||
_ac = _sp[_currentInstruction.Data];
|
||||
_pc++;
|
||||
break;
|
||||
|
||||
case Opcode.LAMP: // PDS-4 only
|
||||
// Flashes a keyboard indicator lamp for 150ms.
|
||||
// At this time, thou cannot GET ye LAMP.
|
||||
Console.WriteLine("*LAMP*");
|
||||
_pc++;
|
||||
break;
|
||||
|
||||
@@ -350,6 +422,7 @@ namespace imlac
|
||||
break;
|
||||
|
||||
case Opcode.LIAC:
|
||||
// TODO: Are byte accesses allowed here? Manual doesn't say.
|
||||
_ac = DoFetchForAddress(_ac);
|
||||
_pc++;
|
||||
break;
|
||||
@@ -462,7 +535,7 @@ namespace imlac
|
||||
|
||||
if (_currentInstruction.DisplayOn)
|
||||
{
|
||||
_system.DisplayProcessor.State = ProcessorState.Running;
|
||||
_system.DisplayProcessor.StartProcessor();
|
||||
}
|
||||
|
||||
_pc++;
|
||||
@@ -478,7 +551,7 @@ namespace imlac
|
||||
|
||||
if (_currentInstruction.DisplayOn)
|
||||
{
|
||||
_system.DisplayProcessor.State = ProcessorState.Running;
|
||||
_system.DisplayProcessor.StartProcessor();
|
||||
}
|
||||
|
||||
_pc++;
|
||||
@@ -528,7 +601,7 @@ namespace imlac
|
||||
|
||||
if (_currentInstruction.DisplayOn)
|
||||
{
|
||||
_system.DisplayProcessor.State = ProcessorState.Running;
|
||||
_system.DisplayProcessor.StartProcessor();
|
||||
}
|
||||
|
||||
_pc++;
|
||||
@@ -555,12 +628,22 @@ namespace imlac
|
||||
|
||||
if (_currentInstruction.DisplayOn)
|
||||
{
|
||||
_system.DisplayProcessor.State = ProcessorState.Running;
|
||||
_system.DisplayProcessor.StartProcessor();
|
||||
}
|
||||
|
||||
_pc++;
|
||||
break;
|
||||
|
||||
case Opcode.SBL: // PDS-4 only
|
||||
_byteAccess = ByteAccess.Left;
|
||||
_pc++;
|
||||
break;
|
||||
|
||||
case Opcode.SBR: // PDS-4 only
|
||||
_byteAccess = ByteAccess.Right;
|
||||
_pc++;
|
||||
break;
|
||||
|
||||
case Opcode.SKP:
|
||||
bool skip = false;
|
||||
|
||||
@@ -685,12 +768,22 @@ namespace imlac
|
||||
throw new NotImplementedException(String.Format("Unimplemented Opcode {0}", _currentInstruction.Opcode));
|
||||
}
|
||||
|
||||
//
|
||||
// If this isn't an SBL/SBR instruction, reset the byte access mode
|
||||
// now that we're done with the modified instruction.
|
||||
//
|
||||
if (_currentInstruction.Opcode != Opcode.SBL &&
|
||||
_currentInstruction.Opcode != Opcode.SBR)
|
||||
{
|
||||
_byteAccess = ByteAccess.Normal;
|
||||
}
|
||||
|
||||
// If the next instruction has a breakpoint set we'll halt at this point, before executing it.
|
||||
if (BreakpointManager.TestBreakpoint(BreakpointType.Execution, _pc))
|
||||
{
|
||||
_state = ProcessorState.BreakpointHalt;
|
||||
_breakpointAddress = _pc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DoIOT()
|
||||
@@ -717,14 +810,27 @@ namespace imlac
|
||||
}
|
||||
else
|
||||
{
|
||||
Trace.Log(LogType.Processor, "Unimplemented IOT device {0}, IOT opcode {1}", Helpers.ToOctal((ushort)_currentInstruction.IOTDevice), Helpers.ToOctal(_currentInstruction.Data));
|
||||
Trace.Log(LogType.Processor, "Unimplemented IOT device {0}, IOT opcode {1}", Helpers.ToOctal((ushort)_currentInstruction.IOTDevice), Helpers.ToOctal(_currentInstruction.Data));
|
||||
}
|
||||
}
|
||||
|
||||
private ushort DoFetchForCurrentInstruction()
|
||||
{
|
||||
ushort effectiveAddress = _currentInstruction.IsIndirect ? _currentIndirectAddress : GetEffectiveAddress(_currentInstruction.Data);
|
||||
return DoFetchForAddress(effectiveAddress);
|
||||
ushort value = DoFetchForAddress(effectiveAddress);
|
||||
|
||||
switch (_byteAccess)
|
||||
{
|
||||
case ByteAccess.Left:
|
||||
value = (ushort)(value >> 8);
|
||||
break;
|
||||
|
||||
case ByteAccess.Right:
|
||||
value = (ushort)(value & 0xff);
|
||||
break;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private ushort DoFetchForAddress(ushort effectiveAddress)
|
||||
@@ -755,7 +861,26 @@ namespace imlac
|
||||
_breakpointAddress = effectiveAddress;
|
||||
}
|
||||
|
||||
_mem.Store(effectiveAddress, word);
|
||||
switch (_byteAccess)
|
||||
{
|
||||
case ByteAccess.Normal:
|
||||
_mem.Store(effectiveAddress, word);
|
||||
break;
|
||||
|
||||
case ByteAccess.Left:
|
||||
{
|
||||
ushort value = _mem.Fetch(effectiveAddress);
|
||||
_mem.Store(effectiveAddress, (ushort)((value & 0xff) | (word << 8)));
|
||||
}
|
||||
break;
|
||||
|
||||
case ByteAccess.Right:
|
||||
{
|
||||
ushort value = _mem.Fetch(effectiveAddress);
|
||||
_mem.Store(effectiveAddress, (ushort)((value & 0xff00) | (word & 0xff)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void Push(ushort pointer, ushort value)
|
||||
@@ -790,7 +915,10 @@ namespace imlac
|
||||
//
|
||||
private ushort[] _sp = new ushort[2];
|
||||
|
||||
|
||||
//
|
||||
// PDS-4 byte access mode set by SBL, SBR
|
||||
//
|
||||
private ByteAccess _byteAccess;
|
||||
|
||||
//
|
||||
// Debug information -- the PC at which the last breakpoint occurred.
|
||||
@@ -808,6 +936,13 @@ namespace imlac
|
||||
//
|
||||
private IIOTDevice[] _iotDispatch;
|
||||
|
||||
private enum ByteAccess
|
||||
{
|
||||
Normal = 0,
|
||||
Left,
|
||||
Right
|
||||
}
|
||||
|
||||
private enum Opcode
|
||||
{
|
||||
// PDS-1 Opcodes:
|
||||
@@ -1467,10 +1602,10 @@ namespace imlac
|
||||
|
||||
private bool DecodeAct2Class(ushort word)
|
||||
{
|
||||
// Act 2 Class instructions have 0 001 000 011 in bits 0-9
|
||||
// Act 2 Class instructions have 1 000 011 000 in bits 0-9
|
||||
// and are only valid on PDS-4 processors.
|
||||
if (Configuration.CPUType == ImlacCPUType.PDS4 &&
|
||||
(word & 0xffc0) == 0x10c0)
|
||||
(word & 0xffc0) == 0x8600)
|
||||
{
|
||||
_operateOrIOT = false; // All require two cycles
|
||||
|
||||
|
||||
@@ -71,6 +71,8 @@ namespace imlac
|
||||
_displayList = new List<Vector>(_displayListSize);
|
||||
_displayListIndex = 0;
|
||||
|
||||
_blink = false;
|
||||
|
||||
//
|
||||
// Prepopulate the display list with Vectors. Only those used in the current frame are
|
||||
// actually rendered, we prepopulate the list to prevent having to cons up new ones
|
||||
@@ -78,7 +80,7 @@ namespace imlac
|
||||
//
|
||||
for (int i = 0; i < _displayListSize; i++)
|
||||
{
|
||||
_displayList.Add(new Vector(DrawingMode.Off, 0, 0, 0, 0));
|
||||
_displayList.Add(new Vector(DrawingMode.Off, 1.0f, false, 0, 0, 0, 0));
|
||||
}
|
||||
|
||||
BuildKeyMappings();
|
||||
@@ -117,6 +119,16 @@ namespace imlac
|
||||
get { return (ushort)_dataSwitches; }
|
||||
}
|
||||
|
||||
public int MouseX
|
||||
{
|
||||
get { return _mouseX; }
|
||||
}
|
||||
|
||||
public int MouseY
|
||||
{
|
||||
get { return _mouseY; }
|
||||
}
|
||||
|
||||
public bool ThrottleFramerate
|
||||
{
|
||||
get { return _throttleFramerate; }
|
||||
@@ -189,6 +201,16 @@ namespace imlac
|
||||
UpdateDisplayScale();
|
||||
}
|
||||
|
||||
public void SetIntensity(int intensity)
|
||||
{
|
||||
_intensity = ((float)intensity / 16.0f);
|
||||
}
|
||||
|
||||
public void SetBlink(bool on)
|
||||
{
|
||||
_blink = on;
|
||||
}
|
||||
|
||||
public void MapDataSwitch(uint switchNumber, VKeys key)
|
||||
{
|
||||
_dataSwitchMappings[switchNumber] = key;
|
||||
@@ -306,8 +328,8 @@ namespace imlac
|
||||
}
|
||||
}
|
||||
|
||||
public void MoveAbsolute(uint x, uint y, DrawingMode mode)
|
||||
{
|
||||
public void MoveAbsolute(int x, int y, DrawingMode mode)
|
||||
{
|
||||
//
|
||||
// Take coordinates as an 11-bit quantity (0-2048) even though we may not be displaying the full resolution.
|
||||
//
|
||||
@@ -320,7 +342,7 @@ namespace imlac
|
||||
_y = y;
|
||||
}
|
||||
|
||||
public void DrawPoint(uint x, uint y)
|
||||
public void DrawPoint(int x, int y)
|
||||
{
|
||||
_x = x;
|
||||
_y = y;
|
||||
@@ -381,6 +403,11 @@ namespace imlac
|
||||
case SDL.SDL_EventType.SDL_KEYUP:
|
||||
SdlKeyUp(e.key.keysym.sym);
|
||||
break;
|
||||
|
||||
|
||||
case SDL.SDL_EventType.SDL_MOUSEMOTION:
|
||||
SdlMouseMove(e.motion.x, e.motion.y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,6 +490,13 @@ namespace imlac
|
||||
}
|
||||
}
|
||||
|
||||
private void SdlMouseMove(int x, int y)
|
||||
{
|
||||
_mouseX = x;
|
||||
_mouseY = y;
|
||||
}
|
||||
|
||||
|
||||
void FullScreenToggle()
|
||||
{
|
||||
_fullScreen = !_fullScreen;
|
||||
@@ -493,6 +527,12 @@ namespace imlac
|
||||
_lock.EnterReadLock();
|
||||
_frame++;
|
||||
|
||||
// Blinking items are on for 16 frames, off for 16.
|
||||
if ((_frame % 16) == 0)
|
||||
{
|
||||
_frameBlink = !_frameBlink;
|
||||
}
|
||||
|
||||
//
|
||||
// If we're drawing a complete frame (not running in debug mode)
|
||||
// fade out the last frame by drawing an alpha-blended black rectangle over the display.
|
||||
@@ -513,7 +553,7 @@ namespace imlac
|
||||
// And draw in this frame's vectors
|
||||
for (int i = 0; i < _displayListIndex; i++)
|
||||
{
|
||||
_displayList[i].Draw(_sdlRenderer);
|
||||
_displayList[i].Draw(_sdlRenderer, _frameBlink);
|
||||
}
|
||||
|
||||
SDL.SDL_RenderPresent(_sdlRenderer);
|
||||
@@ -531,22 +571,22 @@ namespace imlac
|
||||
_syncEvent.Set();
|
||||
}
|
||||
|
||||
private void AddNewVector(DrawingMode mode, uint startX, uint startY, uint endX, uint endY)
|
||||
private void AddNewVector(DrawingMode mode, int startX, int startY, int endX, int endY)
|
||||
{
|
||||
//
|
||||
// Scale the vector to the current scaling factor.
|
||||
// The Imlac specifies 11 bits of resolution (2048 points in X and Y)
|
||||
// which corresponds to a _scaleFactor of 1.0.
|
||||
//
|
||||
startX = (uint)(startX * _scaleFactor + _xOffset);
|
||||
startY = (uint)(startY *_scaleFactor - _yOffset);
|
||||
endX = (uint)(endX * _scaleFactor + _xOffset);
|
||||
endY = (uint)(endY * _scaleFactor - _yOffset);
|
||||
startX = (int)(startX * _scaleFactor + _xOffset);
|
||||
startY = (int)(startY *_scaleFactor - _yOffset);
|
||||
endX = (int)(endX * _scaleFactor + _xOffset);
|
||||
endY = (int)(endY * _scaleFactor - _yOffset);
|
||||
|
||||
_lock.EnterWriteLock();
|
||||
|
||||
Vector newVector = _displayList[_displayListIndex];
|
||||
newVector.Modify(mode, (short)startX, (short)(_yResolution - startY), (short)endX, (short)(_yResolution - endY));
|
||||
newVector.Modify(mode, _intensity, _blink, (short)startX, (short)(_yResolution - startY), (short)endX, (short)(_yResolution - endY));
|
||||
_displayListIndex++;
|
||||
|
||||
_lock.ExitWriteLock();
|
||||
@@ -559,9 +599,12 @@ namespace imlac
|
||||
|
||||
private class Vector
|
||||
{
|
||||
public Vector(DrawingMode mode, uint startX, uint startY, uint endX, uint endY)
|
||||
public Vector(DrawingMode mode, float intensity, bool blink, uint startX, uint startY, uint endX, uint endY)
|
||||
{
|
||||
_mode = mode;
|
||||
_intensity = intensity;
|
||||
_blink = blink;
|
||||
|
||||
_x1 = (int)startX;
|
||||
_y1 = (int)startY;
|
||||
_x2 = (int)endX;
|
||||
@@ -570,26 +613,42 @@ namespace imlac
|
||||
UpdateColor();
|
||||
}
|
||||
|
||||
public void Modify(DrawingMode mode, short startX, short startY, short endX, short endY)
|
||||
public void Modify(DrawingMode mode, float intensity, bool blink, short startX, short startY, short endX, short endY)
|
||||
{
|
||||
if (_mode != mode)
|
||||
{
|
||||
_mode = mode;
|
||||
}
|
||||
|
||||
UpdateColor();
|
||||
if (blink)
|
||||
{
|
||||
Console.WriteLine("blink {0},{1}-{2},{3}", startX, startY, endX, endY);
|
||||
}
|
||||
|
||||
_intensity = intensity;
|
||||
_blink = blink;
|
||||
_x1 = (int)startX;
|
||||
_y1 = (int)startY;
|
||||
_x2 = (int)endX;
|
||||
_y2 = (int)endY;
|
||||
UpdateColor();
|
||||
}
|
||||
|
||||
public void Draw(IntPtr sdlRenderer)
|
||||
public void Draw(IntPtr sdlRenderer, bool blinkOn)
|
||||
{
|
||||
// TODO: handle dotted lines, line thickness options
|
||||
SDL.SDL_SetRenderDrawColor(sdlRenderer, _color.R, _color.G, _color.B, _color.A);
|
||||
SDL.SDL_RenderDrawLine(sdlRenderer, _x1, _y1, _x2, _y2);
|
||||
|
||||
if (!_blink || blinkOn)
|
||||
{
|
||||
SDL.SDL_SetRenderDrawColor(
|
||||
sdlRenderer,
|
||||
(byte)(_color.R * _intensity),
|
||||
(byte)(_color.G * _intensity),
|
||||
(byte)(_color.B * _intensity),
|
||||
_color.A);
|
||||
|
||||
SDL.SDL_RenderDrawLine(sdlRenderer, _x1, _y1, _x2, _y2);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateColor()
|
||||
@@ -625,12 +684,14 @@ namespace imlac
|
||||
|
||||
private DrawingMode _mode;
|
||||
private Color _color;
|
||||
private float _intensity;
|
||||
private bool _blink;
|
||||
private int _x1;
|
||||
private int _y1;
|
||||
private int _x2;
|
||||
private int _y2;
|
||||
|
||||
private static Color NormalColor = Color.FromArgb(200, Color.ForestGreen);
|
||||
private static Color NormalColor = Color.FromArgb(255, Color.GreenYellow);
|
||||
private static Color PointColor = Color.FromArgb(255, Color.GreenYellow);
|
||||
private static Color SGRColor = Color.FromArgb(127, Color.DarkGreen);
|
||||
private static Color DebugColor = Color.FromArgb(255, Color.OrangeRed);
|
||||
@@ -886,7 +947,15 @@ namespace imlac
|
||||
};
|
||||
|
||||
private int _dataSwitches;
|
||||
private DataSwitchMappingMode _dataSwitchMappingMode;
|
||||
private DataSwitchMappingMode _dataSwitchMappingMode;
|
||||
|
||||
//
|
||||
// Mouse Data
|
||||
//
|
||||
private int _mouseX;
|
||||
private int _mouseY;
|
||||
private int _mouseButtons;
|
||||
|
||||
|
||||
//
|
||||
// SDL
|
||||
@@ -909,12 +978,15 @@ namespace imlac
|
||||
|
||||
private AutoResetEvent _syncEvent;
|
||||
|
||||
private uint _x;
|
||||
private uint _y;
|
||||
private int _x;
|
||||
private int _y;
|
||||
|
||||
private int _xResolution;
|
||||
private int _yResolution;
|
||||
private float _scaleFactor;
|
||||
private float _intensity;
|
||||
private bool _blink; // The Blink attribute is applied to vectors while this is set.
|
||||
private bool _frameBlink; // Blink'ed vectors are blanked while this is set (toggled every 16 frames).
|
||||
private int _xOffset;
|
||||
private int _yOffset;
|
||||
private bool _renderCompleteFrame;
|
||||
|
||||
Reference in New Issue
Block a user