1
0
mirror of https://github.com/livingcomputermuseum/sImlac.git synced 2026-01-16 08:25:24 +00:00

Compare commits

..

No commits in common. "master" and "v0.1" have entirely different histories.
master ... v0.1

32 changed files with 1644 additions and 4932 deletions

4
.gitignore vendored
View File

@ -190,7 +190,3 @@ _Pvt_Extensions/
ModelManifest.xml
/sImlac.VC.VC.opendb
/sImlac.VC.db
/.vs/imlac/v15/Server/sqlite3/db.lock
/.vs/imlac/v15/Server/sqlite3/storage.ide
/.vs/imlac/v15/Server/sqlite3/storage.ide-shm
/.vs/imlac/v15/Server/sqlite3/storage.ide-wal

View File

@ -1,2 +0,0 @@
language: csharp
solution: imlac.sln

View File

@ -1,3 +0,0 @@
{
"CurrentProjectSetting": null
}

View File

@ -1,8 +0,0 @@
{
"ExpandedNodes": [
"",
"\\imlac"
],
"SelectedNode": "\\imlac.sln",
"PreviewInSolutionExplorer": false
}

Binary file not shown.

View File

@ -1,3 +0,0 @@
![Build Status](https://api.travis-ci.org/livingcomputermuseum/sImlac.svg)
[![AppVeyor](https://ci.appveyor.com/api/projects/status/butfmm8cd5vge16l?svg=true)](https://ci.appveyor.com/project/larsbrinkhoff/sImlac/history)

View File

@ -1,8 +0,0 @@
platform:
- x86
install:
- nuget restore
build:
project: imlac.sln
artifacts:
- path: imlac/bin

View File

@ -1,34 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using static imlac.ImlacSystem;
namespace imlac
{
public enum ImlacCPUType
{
PDS1,
PDS4,
}
public static class Configuration
{
static Configuration()
{
MITMode = false;
CPUType = ImlacCPUType.PDS1;
SquiggleMode = false;
ShowInvisibleVectors = false;
HaltOnInvalidOpcodes = true;
}
//
// Static System configuration parameters
//
public static bool MITMode;
public static ImlacCPUType CPUType;
public static bool SquiggleMode;
public static bool ShowInvisibleVectors;
public static bool HaltOnInvalidOpcodes;
}
}

View File

@ -63,7 +63,7 @@ namespace imlac.Debugger
}
}
public void AddSubNode(List<string> words, DebuggerCommand command)
public void AddSubNode(List<string> words, MethodInfo method, object instance)
{
// We should never hit this case.
if (words.Count == 0)
@ -77,13 +77,13 @@ namespace imlac.Debugger
if (subNode == null)
{
// No, it has not -- create one and add it now.
subNode = new DebuggerCommand(command.Instance, words[0], command.Description, command.Usage, null);
subNode = new DebuggerCommand(instance, words[0], null, null, null);
this.SubCommands.Add(subNode);
if (words.Count == 1)
{
// This is the last stop -- set the method and be done with it now.
subNode.Methods.Add(command.Methods[0]);
subNode.Methods.Add(method);
// early return.
return;
@ -98,7 +98,7 @@ namespace imlac.Debugger
// If we're on the last word at this point then this is an overloaded command.
// Check that we don't have any other commands with this number of arguments.
//
int argCount = command.Methods[0].GetParameters().Length;
int argCount = method.GetParameters().Length;
foreach (MethodInfo info in subNode.Methods)
{
if (info.GetParameters().Length == argCount)
@ -110,7 +110,7 @@ namespace imlac.Debugger
//
// We're ok. Add it to the method list.
//
subNode.Methods.Add(command.Methods[0]);
subNode.Methods.Add(method);
// and return early.
return;
@ -119,7 +119,7 @@ namespace imlac.Debugger
// We have more words to go.
words.RemoveAt(0);
subNode.AddSubNode(words, command);
subNode.AddSubNode(words, method, instance);
}
public DebuggerCommand FindSubNodeByName(string name)
@ -159,22 +159,14 @@ namespace imlac.Debugger
// Get the command string from the prompt.
string command = _consolePrompt.Prompt().Trim();
if (string.IsNullOrEmpty(command))
if (command != String.Empty)
{
// Repeat the last command
command = _lastCommand;
Console.WriteLine(">> {0}", command);
next = ExecuteLine(command, system);
}
next = ExecuteLine(command, system);
_lastCommand = command;
}
catch (Exception e)
{
Console.WriteLine(
"Error: {0}",
e.InnerException != null ? e.InnerException.Message : e.Message);
Console.WriteLine(e.Message);
}
return next;
@ -658,9 +650,9 @@ namespace imlac.Debugger
{
string[] commandWords = c.Name.Split(' ');
// This is kind of ugly, we know that at this point every command built above has only
// This is kind of ugly, we know that at this point every command built above have only
// one method. When building the tree, overloaded commands may end up with more than one.
_commandRoot.AddSubNode(new List<string>(commandWords), c);
_commandRoot.AddSubNode(new List<string>(commandWords), c.Methods[0], c.Instance);
}
}
@ -691,7 +683,5 @@ namespace imlac.Debugger
private DebuggerPrompt _consolePrompt;
private DebuggerCommand _commandRoot;
private List<DebuggerCommand> _commandList;
private string _lastCommand;
}
}

View File

@ -197,11 +197,8 @@ namespace imlac.Debugger
clear = sb.ToString();
}
// Default to 80 columns if BufferWidth happens to be zero.
int bufferWidth = Console.BufferWidth > 0 ? Console.BufferWidth : 80;
int column = ((_textPosition + _originColumn) % bufferWidth);
int row = ((_textPosition + _originColumn) / bufferWidth) + _originRow;
int column = ((_textPosition + _originColumn) % Console.BufferWidth);
int row = ((_textPosition + _originColumn) / Console.BufferWidth) + _originRow;
// Move cursor to origin to draw string
Console.CursorLeft = _originColumn;
@ -297,13 +294,6 @@ namespace imlac.Debugger
{
changed = _input.Trim().ToLower() != matchString.Trim().ToLower();
// Add a space if the output is different than the input (in which case a completion
// actually took place.
if (changed)
{
matchString += " ";
}
_input = matchString;
TextPosition = _input.Length;
}
@ -330,7 +320,6 @@ namespace imlac.Debugger
}
DebuggerCommand match = null;
bool exactMatch = false;
// Search for exact matches. If we find one it's guaranteed to be unique
// so we can follow that node.
@ -339,7 +328,6 @@ namespace imlac.Debugger
if (c.Name.ToLower() == tokens[0].ToLower())
{
match = c;
exactMatch = true;
break;
}
}
@ -403,7 +391,7 @@ namespace imlac.Debugger
{
subMatch = FuzzyMatch(match, tokens, silent);
}
else
else // if (exactMatch)
{
if (!silent && match.SubCommands.Count > 1)
{
@ -431,27 +419,11 @@ namespace imlac.Debugger
return sb.ToString();
}
else if (!silent &&
match.SubCommands.Count == 0 &&
exactMatch)
{
// No more completions; this was an exact match, so
// instead print the help for this command if any
// is available.
Console.WriteLine();
Console.WriteLine(match.Description);
if (!String.IsNullOrWhiteSpace(match.Usage))
{
Console.WriteLine("Parameters: {0}", match.Usage);
}
}
}
if (subMatch == String.Empty)
{
return String.Format("{0}", match.Name);
return String.Format("{0} ", match.Name);
}
else
{

File diff suppressed because it is too large Load Diff

View File

@ -24,57 +24,28 @@ namespace imlac
{
static void Main(string[] args)
{
_startupArgs = args;
ImlacSystem system = new ImlacSystem();
ConsoleExecutor debuggerPrompt =
new ConsoleExecutor(system);
_console = new SDLConsole(0.5f);
_imlacSystem = new ImlacSystem();
_imlacSystem.AttachConsole(_console);
_imlacSystem.Reset();
_state = SystemExecutionState.Debugging;
Console.CancelKeyPress += new ConsoleCancelEventHandler(OnBreak);
PrintHerald();
//
// Start the debugger / execution thread
//
_debuggerThread = new System.Threading.Thread(new System.Threading.ThreadStart(DebuggerThread));
_debuggerThread.Start();
//
// Show the display; this will return when the display window is closed.
//
_console.Show();
//
// Kill the system if it's still running.
//
_debuggerThread.Abort();
}
private static void DebuggerThread()
{
//
// Wait for the display to be ready.
//
_console.WaitForSync();
ConsoleExecutor debuggerPrompt = new ConsoleExecutor(_imlacSystem);
if (_startupArgs.Length > 0)
if (args.Length > 0)
{
//
// Assume arg 0 is a script file to be executed.
//
Console.WriteLine("Executing startup script '{0}'", _startupArgs[0]);
Console.WriteLine("Executing startup script '{0}'", args[0]);
try
{
_state = debuggerPrompt.ExecuteScript(_imlacSystem, _startupArgs[0]);
_state = debuggerPrompt.ExecuteScript(system, args[0]);
}
catch (Exception e)
catch(Exception e)
{
Console.WriteLine("Error parsing script: {0}", e.Message);
}
@ -88,19 +59,19 @@ namespace imlac
{
case SystemExecutionState.Halted:
case SystemExecutionState.Debugging:
_state = debuggerPrompt.Prompt(_imlacSystem);
_state = debuggerPrompt.Prompt(system);
break;
case SystemExecutionState.SingleStep:
_imlacSystem.SingleStep();
_imlacSystem.Display.RenderCurrent(false);
system.SingleStep();
system.Display.RenderCurrent(false);
_state = SystemExecutionState.Debugging;
break;
case SystemExecutionState.SingleFrame:
_imlacSystem.SingleStep();
system.SingleStep();
if (_imlacSystem.DisplayProcessor.FrameLatch)
if (system.DisplayProcessor.FrameLatch)
{
Console.WriteLine("Frame completed.");
_state = SystemExecutionState.Debugging;
@ -108,85 +79,55 @@ namespace imlac
break;
case SystemExecutionState.UntilDisplayStart:
_imlacSystem.SingleStep();
system.SingleStep();
if (_imlacSystem.DisplayProcessor.State == ProcessorState.Running)
if (system.DisplayProcessor.State == ProcessorState.Running)
{
Console.WriteLine("Display started.");
_state = SystemExecutionState.Debugging;
}
break;
case SystemExecutionState.SingleDisplayOperation:
_imlacSystem.SingleStep();
if (_imlacSystem.DisplayProcessor.DisplayDrawLatch)
{
_imlacSystem.Display.RenderCurrent(false);
Console.WriteLine("Display operation completed.");
_state = SystemExecutionState.Debugging;
}
break;
case SystemExecutionState.Running:
_imlacSystem.SingleStep();
system.SingleStep();
if (_imlacSystem.Processor.State == ProcessorState.Halted)
if (system.Processor.State == ProcessorState.Halted)
{
Console.WriteLine("Main processor halted at {0}", Helpers.ToOctal(_imlacSystem.Processor.PC));
Console.WriteLine("Main processor halted at {0}", Helpers.ToOctal(system.Processor.PC));
_state = SystemExecutionState.Debugging;
}
else if (_imlacSystem.Processor.State == ProcessorState.BreakpointHalt)
else if (system.Processor.State == ProcessorState.BreakpointHalt)
{
Console.WriteLine(
"Breakpoint hit: {0} at address {1}",
BreakpointManager.GetBreakpoint(_imlacSystem.Processor.BreakpointAddress),
Helpers.ToOctal(_imlacSystem.Processor.BreakpointAddress));
BreakpointManager.GetBreakpoint(system.Processor.BreakpointAddress),
Helpers.ToOctal(system.Processor.BreakpointAddress));
_state = SystemExecutionState.Debugging;
}
break;
}
}
catch (Exception e)
catch(Exception e)
{
if (!(e is System.Threading.ThreadAbortException))
{
Console.WriteLine("Internal error during execution: {0}", e.Message);
_state = SystemExecutionState.Debugging;
}
Console.WriteLine("Internal error during execution: {0}", e.Message);
_state = SystemExecutionState.Debugging;
}
}
// We are exiting, shut things down.
//
_imlacSystem.Shutdown();
}
private static void OnBreak(object sender, ConsoleCancelEventArgs e)
static void OnBreak(object sender, ConsoleCancelEventArgs e)
{
Console.WriteLine("User break.");
_state = SystemExecutionState.Debugging;
e.Cancel = true;
// Flush console input.
while(Console.KeyAvailable)
{
Console.ReadKey();
}
}
private static void PrintHerald()
static void PrintHerald()
{
Console.WriteLine("sImlac v0.3. (c) 2016-2020 Living Computers: Museum+Labs");
Console.WriteLine("sImlac v0.1. (c) 2016, 2017 Living Computers: Museum+Labs");
Console.WriteLine();
}
private static string[] _startupArgs;
private static SDLConsole _console;
private static ImlacSystem _imlacSystem;
private static SystemExecutionState _state;
private static System.Threading.Thread _debuggerThread;
}
}

View File

@ -37,17 +37,5 @@ namespace imlac
ushort value = Convert.ToUInt16(octal, 8);
return value;
}
public static void SignalError(LogType logtype, string format, params object[] args)
{
if (Configuration.HaltOnInvalidOpcodes)
{
throw new NotImplementedException(String.Format(format, args));
}
else
{
if (Trace.TraceOn) Trace.Log(logtype, format, args);
}
}
}
}

View File

@ -155,24 +155,16 @@ namespace imlac
~FrameTimer()
{
try
{
//
// Clean stuff up
//
DeleteTimerQueueTimer(_hTimerQueue, _hTimer, IntPtr.Zero);
DeleteTimerQueue(_hTimerQueue, IntPtr.Zero);
//
// Clean stuff up
//
DeleteTimerQueueTimer(_hTimerQueue, _hTimer, IntPtr.Zero);
DeleteTimerQueue(_hTimerQueue, IntPtr.Zero);
//
// Fire off a final event to release any call that's waiting...
//
_event.Set();
}
catch
{
// Nothing. If the above fails it's due to the Win32 APIs not being present.
// Just fail quietly.
}
//
// Fire off a final event to release any call that's waiting...
//
_event.Set();
}
/// <summary>

View File

@ -28,111 +28,7 @@ namespace imlac
SGR1, // reduced intensity
Point, // increased intensity
Debug, // For debugging purposes
}
public enum VKeys
{
// Keys that map to Imlac keyboard keys
Shift = 0x10,
Ctrl = 0x11,
Alt = 0x12,
End = 0x23,
DownArrow = 0x28,
RightArrow = 0x27,
UpArrow = 0x26,
LeftArrow = 0x25,
Tab = 0x9,
Return = 0xd,
PageUp = 0x21,
PageDown = 0x22,
Home = 0x24,
Pause = 0x91,
Escape = 0x1b,
Space = 0x20,
Comma = 0xbc,
Plus = 0xbb,
Period = 0xbe,
QuestionMark = 0xbf,
Zero = 0x30,
One = 0x31,
Two = 0x32,
Three = 0x33,
Four = 0x34,
Five = 0x35,
Six = 0x36,
Seven = 0x37,
Eight = 0x38,
Nine = 0x39,
Minus = 0xbd,
Semicolon = 0xba,
Keypad0 = 0x60,
Keypad2 = 0x62,
Keypad4 = 0x64,
Keypad5 = 0x65,
Keypad6 = 0x66,
DoubleQuote = 0xde,
A = 0x41,
B = 0x42,
C = 0x43,
D = 0x44,
E = 0x45,
F = 0x46,
G = 0x47,
H = 0x48,
I = 0x49,
J = 0x4a,
K = 0x4b,
L = 0x4c,
M = 0x4d,
N = 0x4e,
O = 0x4f,
P = 0x50,
Q = 0x51,
R = 0x52,
S = 0x53,
T = 0x54,
U = 0x55,
V = 0x56,
W = 0x57,
X = 0x58,
Y = 0x59,
Z = 0x5a,
Delete = 0x8,
// Additional keys, not used by the Imlac but available
// for data switch mapping.
F1 = 0x70,
F2 = 0x71,
F3 = 0x72,
F4 = 0x73,
F5 = 0x74,
F6 = 0x75,
F7 = 0x76,
F8 = 0x77,
F9 = 0x78,
F10 = 0x79,
F11 = 0x7a,
F12 = 0x7b,
Keypad1 = 0x61,
Keypad3 = 0x63,
Keypad7 = 0x67,
Keypad8 = 0x68,
Keypad9 = 0x69,
// hack to toggle fullscreen.
Insert = 0x2d,
// Special values for data switch mappings
None0 = 0, // No key mapped to data switch, DS for this bit is set to 0
None1 = 1, // Ditto, but for the value 1
}
}
/// <summary>
/// IImlacConsole provides the interface for the components making up a standard
@ -144,7 +40,7 @@ namespace imlac
/// <summary>
/// Indicates whether a key is currently pressed.
/// </summary>
bool NewKeyPressed
bool IsKeyPressed
{
get;
}
@ -173,16 +69,6 @@ namespace imlac
get;
}
int MouseX
{
get;
}
int MouseY
{
get;
}
bool ThrottleFramerate
{
get;
@ -211,9 +97,9 @@ namespace imlac
void ClearDisplay();
void MoveAbsolute(int x, int y, DrawingMode mode);
void MoveAbsolute(uint x, uint y, DrawingMode mode);
void DrawPoint(int x, int y);
void DrawPoint(uint x, uint y);
void RenderCurrent(bool completeFrame);
@ -221,14 +107,8 @@ namespace imlac
void SetScale(float scale);
void SetIntensity(int intensity);
void SetBlink(bool on);
void MapDataSwitch(uint switchNumber, VKeys key);
VKeys GetDataSwitchMapping(uint switchNumber);
void Shutdown();
}
}

View File

@ -69,87 +69,38 @@ namespace imlac.IO
public void Clock()
{
if (_interruptsEnabled && _interruptEnableCount == 0)
if (_interruptsEnabled)
{
// Collect up devices that want to interrupt us.
_interruptStatus = 0;
//
// PDS-1 and PDS-4 status bits are arranged almost entirely differently.
//
if (Configuration.CPUType == ImlacCPUType.PDS1)
// bit 14: 40 cycle sync
if (_system.DisplayProcessor.FrameLatch)
{
// PDS-1:
// bit 14: 40 cycle sync
if (_system.DisplayProcessor.FrameLatch &&
_system.DisplayProcessor.DisplayHalted)
{
_interruptStatus |= 0x0002;
}
// bit 12 - TTY rcv
if (_system.TTY.DataReady)
{
_interruptStatus |= 0x0008;
}
// bit 11 - keyboard
if (_system.Keyboard.KeyReady)
{
_interruptStatus |= 0x0010;
}
// bit 10 - TTY send
if (_system.TTY.DataSentLatch)
{
_interruptStatus |= 0x0020;
}
// bit 2 - ACI-1 (clock)
if (_system.Clock.TimerTriggered)
{
_interruptStatus |= 0x2000;
}
_interruptStatus |= 0x0002;
}
else
// bit 12 - TTY rcv
if (_system.TTY.DataReady)
{
// PDS-4:
_interruptStatus |= 0x0008;
}
// Bit 15: Display halt
if (_system.DisplayProcessor.DisplayHalted)
{
_interruptStatus |= 0x0001;
}
// bit 11 - keyboard
if (_system.Keyboard.KeyReady)
{
_interruptStatus |= 0x0010;
}
// 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;
}
// bit 2 - ACI-1 (clock)
if (_system.Clock.TimerTriggered)
{
_interruptStatus |= 0x2000;
}
// 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 (_interruptsEnabled && (_interruptMask & _interruptStatus) != 0)
if ((_interruptMask & _interruptStatus) != 0)
{
_interruptPending = true;
}
@ -158,7 +109,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.CanBeInterrupted())
if (_interruptPending && _system.Processor.InstructionState == ExecState.Fetch)
{
// save the current PC at 0
_system.Memory.Store(0x0000, _system.Processor.PC);
@ -173,10 +124,6 @@ namespace imlac.IO
Trace.Log(LogType.Interrupt, "Interrupt triggered (for device(s) {0})", Helpers.ToOctal((ushort)(_interruptMask & _interruptStatus)));
}
}
else if (_interruptsEnabled)
{
_interruptEnableCount--;
}
}
public int[] GetHandledIOTs()
@ -188,7 +135,6 @@ namespace imlac.IO
{
//
// Dispatch the IOT instruction.
// TODO: handle PDS-4 IOTs.
//
switch (iotCode)
{
@ -200,20 +146,12 @@ namespace imlac.IO
case 0x72:
_interruptsEnabled = true;
// Interrupts are enabled the second instruction after the ION.
_interruptEnableCount = 1;
Trace.Log(LogType.Interrupt, "Interrupts enabled");
break;
case 0x41:
_system.Processor.AC |= (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.");
Trace.Log(LogType.Interrupt, "Interrupt status {0} copied to AC", Helpers.ToOctal((ushort)_interruptStatus));
break;
case 0x61:
@ -223,11 +161,10 @@ namespace imlac.IO
default:
throw new NotImplementedException(String.Format("Unimplemented Interrupt IOT instruction {0}", Helpers.ToOctal((ushort)iotCode)));
throw new NotImplementedException(String.Format("Unimplemented Interrupt IOT instruction {0:x4}", iotCode));
}
}
private int _interruptEnableCount;
private bool _interruptsEnabled;
private bool _interruptPending;
private int _interruptMask;
@ -237,18 +174,10 @@ namespace imlac.IO
private readonly int[] _handledIOTs =
{
0x41, // read interrupt status bits
0x41, // read interrupt status bits
0x61, // arm/disarm devices (set interrupt mask)
0x71, // IOF (disable interrupts)
0x72, // ION (enabled masked interrupts)
// 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
0x93, // Disable, then Enable
};
}
}

View File

@ -31,7 +31,6 @@ namespace imlac.IO
public enum ImlacKey
{
None = 0x0,
DataXmit = 0x2,
Down = 0x4,
Right = 0x5,
@ -40,7 +39,6 @@ namespace imlac.IO
Tab = 0x9,
CR = 0x0d,
FF = 0x0c,
LF = 0x0a,
PageXmit = 0xe,
Home = 0xf,
Brk = 0x19,
@ -119,18 +117,26 @@ namespace imlac.IO
public void Clock()
{
// If we do not already have a key latched and one has been pressed,
// we will raise the Ready flag now.
if (!_keyReady)
// we will latch it now.
// Based on the keycode & modifiers we will generate an Imlac keyboard code
if (_system.Display.IsKeyPressed)
{
if (_system.Display.NewKeyPressed)
{
_keyCode = GetScancodeForCurrentKey();
if (_keyCode != 0)
{
Trace.Log(LogType.Keyboard, "Key latched {0}", Helpers.ToOctal(_keyCode));
_keyReady = true;
}
}
else
{
_keyCode = 0;
}
}
public void Reset()
{
_keyCode = 0;
_keyReady = false;
}
@ -149,18 +155,20 @@ namespace imlac.IO
switch (iotCode)
{
case 0x11:
_system.Processor.AC |= GetScancodeForCurrentKey();
_system.Processor.AC |= _keyCode;
Trace.Log(LogType.Keyboard, "Key OR'd into AC {0}", Helpers.ToOctal(_system.Processor.AC));
break;
case 0x12:
_keyCode = 0;
_keyReady = false;
_system.Display.UnlatchKey();
Trace.Log(LogType.Keyboard, "Keyboard flag reset.");
break;
case 0x13:
_system.Processor.AC |= GetScancodeForCurrentKey();
_system.Processor.AC |= _keyCode;
_keyCode = 0;
_keyReady = false;
_system.Display.UnlatchKey();
Trace.Log(LogType.Keyboard, "Key OR'd into AC {0}, keyboard flag reset.", Helpers.ToOctal(_system.Processor.AC));
@ -188,7 +196,7 @@ namespace imlac.IO
// bit 8 is always set
scanCode = (ushort)(scanCode | 0x80);
//
// The Repeat, Control, and Shift keys correspond to bits 5, 6, and 7 of the
// scancode returned.
@ -217,6 +225,7 @@ namespace imlac.IO
private readonly int[] _handledIOTs = { 0x11, 0x12, 0x13 };
private bool _keyReady;
private ushort _keyCode;
private ImlacSystem _system;
@ -238,7 +247,6 @@ namespace imlac.IO
{
_keyMappings = new Dictionary<ImlacKey, ImlacKeyMapping>();
AddMapping(ImlacKey.None, 0x0, 0x0);
AddMapping(ImlacKey.DataXmit, 0x2, 0x0);
AddMapping(ImlacKey.Down, 0x4, 0x0);
AddMapping(ImlacKey.Right, 0x5, 0x0);
@ -247,7 +255,6 @@ namespace imlac.IO
AddMapping(ImlacKey.Tab, 0x9, 0x0);
AddMapping(ImlacKey.CR, 0x0d, 0x0);
AddMapping(ImlacKey.FF, 0x0c, 0x0);
AddMapping(ImlacKey.LF, 0x0a, 0x0);
AddMapping(ImlacKey.PageXmit, 0xe, 0x0);
AddMapping(ImlacKey.Home, 0xf, 0x0);
AddMapping(ImlacKey.Brk, 0x19, 0x0);

View File

@ -36,10 +36,6 @@ namespace imlac.IO
_dataSendReady = true;
_dataReady = false;
_clocks = 0;
_dataBufferFull = false;
_dataSentLatch = false;
_rxData = 0;
_txData = 0;
if (_dataChannel != null)
{
@ -54,11 +50,6 @@ namespace imlac.IO
throw new ArgumentNullException("channel");
}
if (_dataChannel != null)
{
_dataChannel.Close();
}
_dataChannel = channel;
}
@ -70,22 +61,27 @@ namespace imlac.IO
{
_clocks = 0;
if (_dataChannel.DataAvailable && !_dataReady)
if (_dataChannel.DataAvailable)
{
_dataReady = true;
_rxData = _dataChannel.Read();
_data = _dataChannel.Read();
Trace.Log(LogType.TTY, "i");
}
// Are we waiting to send something?
if (_dataBufferFull && _dataChannel.OutputReady)
else
{
_dataChannel.Write(_txData);
Trace.Log(LogType.TTY, "o {0}", Helpers.ToOctal(_txData));
_dataBufferFull = false;
_dataSentLatch = true;
_dataSendReady = true;
_dataReady = false;
}
}
// Are we waiting to send something?
if (!_dataSendReady && _dataChannel.OutputReady)
{
_dataChannel.Write(_data);
Trace.Log(LogType.TTY, "o");
// Sent, reset flag.
_dataSendReady = true;
}
}
@ -99,16 +95,6 @@ namespace imlac.IO
get { return _dataSendReady; }
}
public bool DataSentLatch
{
get
{
bool latch = _dataSentLatch;
_dataSentLatch = false;
return latch;
}
}
public int[] GetHandledIOTs()
{
return _handledIOTs;
@ -119,8 +105,8 @@ namespace imlac.IO
switch (iotCode)
{
case 0x19: // RRB - TTY read
Trace.Log(LogType.TTY, "TTY read {0}", Helpers.ToOctal(_rxData));
_system.Processor.AC |= _rxData;
Trace.Log(LogType.TTY, "TTY read {0}", Helpers.ToOctal(_data));
_system.Processor.AC |= _data;
break;
case 0x1a: // RCF - Clear TTY status
@ -128,35 +114,30 @@ namespace imlac.IO
break;
case 0x1b: // RRC - Read and clear status
Trace.Log(LogType.TTY, "TTY read {0}, status cleared.", Helpers.ToOctal(_rxData));
Trace.Log(LogType.TTY, "TTY read {0}, status cleared.", Helpers.ToOctal(_data));
_dataReady = false;
_system.Processor.AC |= _rxData;
_system.Processor.AC |= _data;
break;
case 0x21: // TPR - transmit
if (_dataSendReady)
if (_dataSendReady) // only if transmitter is ready
{
_txData = (byte)_system.Processor.AC;
_data = (byte)_system.Processor.AC;
_dataSendReady = false;
_dataBufferFull = true;
}
break;
case 0x22: // TCF - clear output flag
_dataSendReady = false;
_dataSendReady = true;
break;
case 0x23: // TPC - print, clear flag
if (_dataSendReady)
{
_txData = (byte)_system.Processor.AC;
_dataSendReady = false;
_dataBufferFull = true;
}
_data = (byte)_system.Processor.AC;
_dataSendReady = false;
break;
default:
Trace.Log(LogType.TTY, "Stub: TTY xmit op", Helpers.ToOctal(_rxData));
Trace.Log(LogType.TTY, "Stub: TTY xmit op", Helpers.ToOctal(_data));
break;
}
}
@ -165,13 +146,10 @@ namespace imlac.IO
private bool _dataReady;
private bool _dataSendReady;
private bool _dataBufferFull;
private bool _dataSentLatch;
private byte _rxData;
private byte _txData;
private byte _data;
private int _clocks;
private readonly int _dataClocks = 90; // Appx. 50kbps
private readonly int _dataClocks = 100;
private ISerialDataChannel _dataChannel;

View File

@ -1,221 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace imlac.IO.TTYChannels
{
public class TelnetDataChannel : ISerialDataChannel
{
public TelnetDataChannel(string server, int port, bool raw)
{
//
// Try to open the channel.
//
try
{
_telnetStream = new TelnetStream(server, port, raw);
}
catch(Exception e)
{
Console.WriteLine("Failed to connect to {0}:{1}, error: {2}",
server, port, e.Message);
_telnetStream = null;
}
}
public void Reset()
{
// TODO: how to handle reset?
}
public void Close()
{
_telnetStream.Close();
_telnetStream = null;
}
public byte Read()
{
byte b = (byte)_telnetStream.ReadByte();
Trace.Log(LogType.Telnet, "r:{0:x2}({1}) ", b, (char)b);
return b;
}
public void Write(byte value)
{
Trace.Log(LogType.Telnet, "w:{0:x2}({1})", value, (char)value);
_telnetStream.WriteByte(value);
}
public bool DataAvailable
{
get
{
return _telnetStream != null ? _telnetStream.DataAvailable : false;
}
}
public bool OutputReady
{
get
{
// Always return true, we can always send data
return _telnetStream != null ? true : false;
}
}
private TelnetStream _telnetStream;
}
public class TelnetStream
{
public TelnetStream(string host, int port, bool raw)
{
_tcpClient = new TcpClient(host, port);
_tcpStream = _tcpClient.GetStream();
_raw = raw;
_abort = false;
_asyncBuffer = new byte[2048];
_inputBuffer = new Queue<byte>();
_bufferLock = new ReaderWriterLockSlim();
_streamLock = new ReaderWriterLockSlim();
//
// Kick off reading from the stream, asynchronously.
_tcpStream.BeginRead(
_asyncBuffer,
0,
_asyncBuffer.Length,
new AsyncCallback(AsyncReadCallback),
null);
}
public bool DataAvailable
{
get
{
bool avail = false;
_bufferLock.EnterReadLock();
avail = _inputBuffer.Count > 0;
_bufferLock.ExitReadLock();
return avail;
}
}
public void Close()
{
_streamLock.EnterWriteLock();
_abort = true;
_tcpStream.Close();
_tcpClient.Close();
_streamLock.ExitWriteLock();
}
public byte ReadByte()
{
byte b = 0;
_bufferLock.EnterUpgradeableReadLock();
if (_inputBuffer.Count > 0)
{
_bufferLock.EnterWriteLock();
b = _inputBuffer.Dequeue();
_bufferLock.ExitWriteLock();
}
_bufferLock.ExitUpgradeableReadLock();
return b;
}
public void WriteByte(byte b)
{
_tcpStream.WriteByte(b);
}
private void AsyncReadCallback(IAsyncResult ar)
{
_streamLock.EnterReadLock();
if (_abort)
{
return;
}
//
// Process incoming data
// TODO: The telnet processing is terrible.
//
int bytesRead = _tcpStream.EndRead(ar);
for (int i = 0; i < bytesRead; )
{
byte b = _asyncBuffer[i++];
if (!_raw && b == IAC)
{
// For now we just eat all option requests.
b = _asyncBuffer[i++];
switch(b)
{
case WILL:
case WONT:
case DO:
case DONT:
i++;
break;
default:
break;
}
}
else
{
_bufferLock.EnterWriteLock();
_inputBuffer.Enqueue(b);
_bufferLock.ExitWriteLock();
}
}
//
// And start the next read
//
_tcpStream.BeginRead(
_asyncBuffer,
0,
_asyncBuffer.Length,
new AsyncCallback(AsyncReadCallback),
null);
_streamLock.ExitReadLock();
}
private const byte IAC = 255;
private const byte WILL = 251;
private const byte WONT = 252;
private const byte DO = 253;
private const byte DONT = 254;
private byte[] _asyncBuffer;
private Queue<byte> _inputBuffer;
private TcpClient _tcpClient;
private NetworkStream _tcpStream;
private bool _raw;
private bool _abort;
private ReaderWriterLockSlim _bufferLock;
private ReaderWriterLockSlim _streamLock;
}
}

View File

@ -15,39 +15,15 @@
along with sImlac. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
namespace imlac
{
// TODO: make memory size configurable.
public class Memory
{
public Memory(ImlacSystem system)
{
_system = system;
SetMemorySize(0x2000);
}
public void SetMemorySize(ushort size)
{
if (size != 0x1000 && size != 0x2000 && size != 0x4000)
{
throw new InvalidOperationException("Size must be 4k, 8k, or 16k.");
}
_size = size;
_sizeMask = (ushort)(size - 1);
_mem = new ushort[Size];
if (_system.Processor != null)
{
_system.Processor.InitializeCache();
}
if (_system.DisplayProcessor != null)
{
_system.DisplayProcessor.InitializeCache();
}
_system = system;
}
public ushort Fetch(ushort address)
@ -68,18 +44,15 @@ namespace imlac
public static ushort Size
{
get { return _size; }
get { return 8192; }
}
public static ushort SizeMask
{
get { return _sizeMask; }
get { return 0x1fff; }
}
private static ushort _size;
private static ushort _sizeMask;
private ushort[] _mem;
private ImlacSystem _system;
}
}
}

View File

@ -1,816 +0,0 @@
/*
This file is part of sImlac.
sImlac 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.
sImlac 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 sImlac. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using imlac.IO;
using imlac.Debugger;
namespace imlac
{
/// <summary>
/// DisplayProcessor implements the Display processor found in an Imlac PDS-1 with long vector hardware.
/// </summary>
public class PDS1DisplayProcessor : DisplayProcessorBase
{
public PDS1DisplayProcessor(ImlacSystem system) : base(system)
{
}
public override void Reset()
{
base.Reset();
_sgrModeOn = false;
_sgrBeamOn = false;
_sgrDJRMOn = false;
}
public override void InitializeCache()
{
_instructionCache = new PDS1DisplayInstruction[Memory.Size];
}
public override void InvalidateCache(ushort address)
{
_instructionCache[address & Memory.SizeMask] = null;
}
public override string Disassemble(ushort address, DisplayProcessorMode mode, out int length)
{
//
// Return a precached instruction if we have it due to previous execution
// otherwise disassemble it now in the requested mode; this disassembly
// does not get added to the cache.
//
if (_instructionCache[address & Memory.SizeMask] != null)
{
return _instructionCache[address & Memory.SizeMask].Disassemble(mode, _mem, out length);
}
else
{
return new PDS1DisplayInstruction(_mem.Fetch(address), address, mode).Disassemble(mode, _mem, out length);
}
}
public override void Clock()
{
_clocks++;
if (_clocks > _frameClocks40Hz)
{
_clocks = 0;
_frameLatch = true;
_system.Display.FrameDone();
}
if (_state == ProcessorState.Halted)
{
return;
}
switch (_mode)
{
case DisplayProcessorMode.Processor:
ExecuteProcessor();
break;
case DisplayProcessorMode.Increment:
ExecuteIncrement();
break;
}
}
public override int[] GetHandledIOTs()
{
return _handledIOTs;
}
public override void ExecuteIOT(int iotCode)
{
//
// Dispatch the IOT instruction.
//
switch (iotCode)
{
case 0x03: // DLA:
PC = _system.Processor.AC;
// this is for debugging only, we keep track of the load address
// to make it easy to see where the main Display List starts
_dpcEntry = PC;
break;
case 0x0a: // Halt display processor
HaltProcessor();
break;
case 0x39: // Clear display 40Hz sync latch
_frameLatch = false;
break;
case 0xc4: // clear halt state
_halted = false;
break;
default:
Helpers.SignalError(LogType.DisplayProcessor, "Unhandled IOT {0}", Helpers.ToOctal((ushort)iotCode));
break;
}
}
private void ExecuteProcessor()
{
PDS1DisplayInstruction instruction = GetCachedInstruction(_pc, DisplayProcessorMode.Processor);
instruction.UsageMode = DisplayProcessorMode.Processor;
switch (instruction.Opcode)
{
case DisplayOpcode.DEIM:
_mode = DisplayProcessorMode.Increment;
_immediateWord = instruction.Data;
_immediateHalf = ImmediateHalf.Second;
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "Enter increment mode");
break;
case DisplayOpcode.DJMP:
if (!_dadr)
{
// DADR off, use only 12 bits
_pc = (ushort)((instruction.Data & 0xfff) | _block);
}
else
{
_pc = (ushort)(instruction.Data | _block);
}
break;
case DisplayOpcode.DJMS:
Push();
if (!_dadr)
{
// DADR off, use only 12 bits
_pc = (ushort)((instruction.Data & 0xfff) | _block);
}
else
{
_pc = (ushort)(instruction.Data | _block);
}
break;
case DisplayOpcode.DOPR:
// Each of bits 4-11 can be combined in any fashion
// to do a number of operations simultaneously; we walk the bits
// and perform the operations as set.
if ((instruction.Data & 0x800) == 0)
{
// DHLT -- halt the display processor. other micro-ops in this
// instruction are still run.
HaltProcessor();
}
if ((instruction.Data & 0x400) != 0)
{
// HV Sync; this is currently a no-op, not much to do in emulation.
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "HV Sync");
MoveAbsolute(X, Y, DrawingMode.Off);
}
if ((instruction.Data & 0x200) != 0)
{
// DIXM -- increment X DAC MSB
X += 0x20;
MoveAbsolute(X, Y, DrawingMode.Off);
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "DIXM, X is now {0}", X);
}
if ((instruction.Data & 0x100) != 0)
{
// DIYM -- increment Y DAC MSB
Y += 0x20;
MoveAbsolute(X, Y, DrawingMode.Off);
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "DIYM, Y is now {0}", Y);
}
if ((instruction.Data & 0x80) != 0)
{
// DDXM - decrement X DAC MSB
X -= 0x20;
MoveAbsolute(X, Y, DrawingMode.Off);
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "DDXM, X is now {0}", X);
}
if ((instruction.Data & 0x40) != 0)
{
// DDYM - decrement y DAC MSB
Y -= 0x20;
MoveAbsolute(X, Y, DrawingMode.Off);
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "DDYM, Y is now {0}", Y);
}
if ((instruction.Data & 0x20) != 0)
{
// DRJM - return from display subroutine
ReturnFromDisplaySubroutine();
_pc--; // hack (we add +1 at the end...)
}
if ((instruction.Data & 0x10) != 0)
{
// DDSP -- intensify point on screen for 1.8us (one instruction)
// at the current position.
DrawPoint(X, Y);
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "DDSP at {0},{1}", X, Y);
}
// F/C ops:
int f = (instruction.Data & 0xc) >> 2;
int c = instruction.Data & 0x3;
switch (f)
{
case 0x0:
// if bit 15 is set, the MIT mods flip the DADR bit.
if (Configuration.MITMode && (c == 1))
{
_dadr = !_dadr;
}
break;
case 0x1:
// Set scale based on C
switch (c)
{
case 0:
_scale = 1.0f;
break;
default:
_scale = c;
break;
}
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "Scale set to {0}", _scale);
break;
case 0x2:
if (!Configuration.MITMode)
{
_block = (ushort)(c << 12);
}
break;
case 0x3:
// TODO: light pen sensitize
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "Light pen, stub!");
break;
}
_pc++;
break;
case DisplayOpcode.DLXA:
X = instruction.Data << 1;
DrawingMode mode;
if (_sgrModeOn && _sgrBeamOn)
{
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "SGR-1 X set to {0}", X);
mode = DrawingMode.SGR1;
}
else
{
mode = DrawingMode.Off;
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "X set to {0}", X);
}
MoveAbsolute(X, Y, mode);
if (_sgrDJRMOn)
{
ReturnFromDisplaySubroutine();
}
else
{
_pc++;
}
break;
case DisplayOpcode.DLYA:
Y = instruction.Data << 1;
if (_sgrModeOn && _sgrBeamOn)
{
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "SGR-1 Y set to {0}", Y);
mode = DrawingMode.SGR1;
}
else
{
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "Y set to {0}", Y);
mode = DrawingMode.Off;
}
MoveAbsolute(X, Y, mode);
if (_sgrDJRMOn)
{
ReturnFromDisplaySubroutine();
}
else
{
_pc++;
}
break;
case DisplayOpcode.DLVH:
DrawLongVector(instruction.Data);
break;
case DisplayOpcode.SGR1:
_sgrModeOn = (instruction.Data & 0x1) != 0;
_sgrDJRMOn = (instruction.Data & 0x2) != 0;
_sgrBeamOn = (instruction.Data & 0x4) != 0;
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "SGR-1 instruction: Enter {0} BeamOn {1} DRJM {2}", _sgrModeOn, _sgrBeamOn, _sgrDJRMOn);
_pc++;
break;
case DisplayOpcode.Invalid:
Helpers.SignalError(
LogType.DisplayProcessor,
"Execution of invalid display processor instruction {0}",
Helpers.ToOctal(instruction.Data));
_pc++;
break;
default:
Helpers.SignalError(
LogType.DisplayProcessor,
"Unimplemented Display Processor Opcode {0}, operands {1}",
Helpers.ToOctal((ushort)instruction.Opcode),
Helpers.ToOctal(instruction.Data));
break;
}
// If the next instruction has a breakpoint set we'll halt at this point, before executing it.
if (BreakpointManager.TestBreakpoint(BreakpointType.Display, _pc))
{
_state = ProcessorState.BreakpointHalt;
}
}
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)
{
if ((halfWord & 0x40) != 0)
{
// Escape code
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "Increment mode escape on halfword {0}", _immediateHalf);
_mode = DisplayProcessorMode.Processor;
_pc++; // move to next word
// Moved this into this check (not sure it makes sense to do a DJMS when not escaped from Increment mode)
if ((halfWord & 0x20) != 0)
{
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "Increment mode return from subroutine.");
ReturnFromDisplaySubroutine();
}
}
else
{
// Stay in increment mode.
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "Increment instruction, non-drawing.");
MoveToNextHalfWord();
}
if ((halfWord & 0x10) != 0)
{
newX += 0x20;
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "Increment X MSB, X is now {0}", X);
}
if ((halfWord & 0x08) != 0)
{
newX = newX & (0xffe0);
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "Reset X LSB, X is now {0}", X);
}
if ((halfWord & 0x02) != 0)
{
newY += 0x20;
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "Increment Y MSB, Y is now {0}", Y);
}
if ((halfWord & 0x01) != 0)
{
newY = newY & (0xffe0);
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "Reset Y LSB, Y is now {0}", Y);
}
MoveAbsolute(newX, newY, DrawingMode.Off);
}
else
{
int xSign = ((halfWord & 0x20) == 0) ? 1 : -1;
int xMag = (int)(((halfWord & 0x18) >> 3) * _scale);
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),
newX,
newY,
xSign * xMag,
ySign * yMag,
(halfWord & 0x40) != 0);
newX = (int)(newX + xSign * xMag * 2);
newY = (int)(newY + ySign * yMag * 2);
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))
{
_state = ProcessorState.BreakpointHalt;
}
}
private void MoveToNextHalfWord()
{
if (_immediateHalf == ImmediateHalf.Second)
{
_pc++;
_immediateWord = _mem.Fetch(_pc);
_immediateHalf = ImmediateHalf.First;
// Update the instruction cache with the type of instruction (to aid in debugging).
PDS1DisplayInstruction instruction = GetCachedInstruction(_pc, DisplayProcessorMode.Increment);
}
else
{
_immediateHalf = ImmediateHalf.Second;
}
}
private void DrawLongVector(ushort word0)
{
//
// A Long Vector instruction is 3 words long:
// Word 0: upper 4 bits indicate the opcode (4), lower 12 specify N-M
// Word 1: upper 3 bits specify beam options (dotted, solid, etc) and the lower 12 specify the larger increment "M"
// Word 2: upper 3 bits specify signs, lower 12 specify the smaller increment "N"
// M is the larger absolute value between dX and dY
// N is the smaller.
//
// Unsure at the moment what the N-M bits are for (I'm guessing they're there to help the processor figure things out).
// Also unsure what bits are used in the 12 bits for N and M (the DACs are only 11-bits, but normally only 10 can be specified)...
//
ushort word1 = _mem.Fetch(++_pc);
ushort word2 = _mem.Fetch(++_pc);
uint M = (uint)(word1 & 0x3ff);
uint N = (uint)(word2 & 0x3ff);
bool beamOn = (word1 & 0x2000) != 0;
bool dotted = (word1 & 0x4000) != 0;
int dySign = (word2 & 0x2000) != 0 ? -1 : 1;
int dxSign = (word2 & 0x4000) != 0 ? -1 : 1;
bool dyGreater = (word2 & 0x1000) != 0;
uint dx = 0;
uint dy = 0;
if (dyGreater)
{
dy = M;
dx = N;
}
else
{
dx = M;
dy = N;
}
if (Trace.TraceOn) Trace.Log(LogType.DisplayProcessor, "LongVector x={0} y={1} dx={2} dy={3} beamOn {4} dotted {5}", X, Y, dx * dxSign, dy * dySign, beamOn, dotted);
// * 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.)
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}", newX, newY, dx * dxSign, dy * dySign, beamOn, dotted);
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)
{
_instructionCache[address & Memory.SizeMask] = new PDS1DisplayInstruction(_mem.Fetch(address), address, mode);
}
return _instructionCache[address & Memory.SizeMask];
}
// SGR-1 mode switches
private bool _sgrModeOn;
private bool _sgrDJRMOn;
private bool _sgrBeamOn;
protected const int _frameClocks40Hz = 13889; // cycles per 1/40th of a second (rounded up) for a 1.8uS clock speed.
private PDS1DisplayInstruction[] _instructionCache;
private readonly int[] _handledIOTs = { 0x3, 0xa, 0x39, 0xc4 };
/// <summary>
/// PDS-1 Display instruction decoder and disassembler.
/// </summary>
private class PDS1DisplayInstruction : DisplayInstructionBase
{
public PDS1DisplayInstruction(ushort word, ushort address, DisplayProcessorMode mode) : base (word, address, mode)
{
}
public override string Disassemble(DisplayProcessorMode mode, Memory mem, out int length)
{
if (mode == DisplayProcessorMode.Indeterminate)
{
mode = _usageMode;
}
switch (mode)
{
case DisplayProcessorMode.Increment:
length = 1;
return DisassembleIncrement();
case DisplayProcessorMode.Processor:
return DisassembleProcessor(mem, out length);
case DisplayProcessorMode.Indeterminate:
length = 1;
return "Indeterminate";
default:
throw new InvalidOperationException(String.Format("{0} is not a supported disassembly mode for this processor.", mode));
}
}
protected override void Decode()
{
if (_usageMode == DisplayProcessorMode.Processor)
{
DecodeProcessor();
}
else
{
DecodeImmediate();
}
}
private void DecodeProcessor()
{
int op = (_word & 0x7000) >> 12;
switch (op)
{
case 0x00:
// opr code
_opcode = DisplayOpcode.DOPR;
_data = (ushort)(_word & 0xfff);
break;
case 0x01:
_opcode = DisplayOpcode.DLXA;
_data = (ushort)(_word & 0x3ff);
break;
case 0x02:
_opcode = DisplayOpcode.DLYA;
_data = (ushort)(_word & 0x3ff);
break;
case 0x03:
_opcode = DisplayOpcode.DEIM;
_data = (ushort)(_word & 0xff);
if ((_word & 0xff00) == 0x3800)
{
Console.WriteLine("PPM-1 not implemented (instr {0})", Helpers.ToOctal(_word));
}
break;
case 0x04:
_opcode = DisplayOpcode.DLVH;
_data = (ushort)(_word & 0xfff);
break;
case 0x05:
_opcode = DisplayOpcode.DJMS;
_data = (ushort)(_word & 0xfff);
if (Configuration.MITMode && (_word & 0x8000) != 0)
{
// MIT's mod takes the MSB of the address from the MSB of the instruction word
_data |= 0x1000;
}
break;
case 0x06:
_opcode = DisplayOpcode.DJMP;
_data = (ushort)(_word & 0xfff);
if (Configuration.MITMode && (_word & 0x8000) != 0)
{
// MIT's mod takes the MSB of the address from the MSB of the instruction word
_data |= 0x1000;
}
break;
case 0x07:
DecodeExtendedInstruction(_word);
break;
default:
Helpers.SignalError(
LogType.DisplayProcessor,
"Unhandled Display Processor Mode instruction {0}",
Helpers.ToOctal(_word));
_opcode = DisplayOpcode.Invalid;
_data = _word;
break;
}
}
void DecodeExtendedInstruction(ushort word)
{
int op = (word & 0x1f8) >> 3;
switch (op)
{
case 0x36:
case 0x37:
_opcode = DisplayOpcode.ASG1;
break;
case 0x3a:
case 0x3b:
_opcode = DisplayOpcode.VIC1;
break;
case 0x3c:
case 0x3d:
_opcode = DisplayOpcode.MCI1;
break;
case 0x3e:
_opcode = DisplayOpcode.STI1;
break;
case 0x3f:
_opcode = DisplayOpcode.SGR1;
break;
default:
Helpers.SignalError(
LogType.DisplayProcessor,
"Unhandled extended Display Processor Mode instruction {0}",
Helpers.ToOctal(word));
_opcode = DisplayOpcode.Invalid;
_data = word;
break;
}
_data = (ushort)(word & 0x7);
}
private void DecodeImmediate()
{
// TODO: eventually actually precache movement calculations.
}
protected override string DisassembleExtended(Memory mem, out int length)
{
string ret = String.Empty;
switch (_opcode)
{
case DisplayOpcode.DLVH:
length = 3;
ret = DisassembleLongVector(mem);
break;
default:
length = 1;
// Handle as yet not-special-cased opcodes
ret = String.Format("{0} {1}", _opcode, Helpers.ToOctal(_data));
break;
}
return ret;
}
private string DisassembleLongVector(Memory mem)
{
//
// A Long Vector instruction is 3 words long:
// Word 0: upper 4 bits indicate the opcode (4), lower 12 specify N-M
// Word 1: upper 3 bits specify beam options (dotted, solid, etc) and the lower 12 specify the larger increment "M"
// Word 2: upper 3 bits specify signs, lower 12 specify the smaller increment "N"
// M is the larger absolute value between dX and dY
// N is the smaller.
//
// TODO: Would make sense to precache this during decoding, would require
// modifications to cache invalidation logic.
ushort word1 = mem.Fetch((ushort)(_address + 1));
ushort word2 = mem.Fetch((ushort)(_address + 2));
uint M = (uint)(word1 & 0x3ff);
uint N = (uint)(word2 & 0x3ff);
bool beamOn = (word1 & 0x2000) != 0;
bool dotted = (word1 & 0x4000) != 0;
int dySign = (word2 & 0x2000) != 0 ? -1 : 1;
int dxSign = (word2 & 0x4000) != 0 ? -1 : 1;
bool dyGreater = (word2 & 0x1000) != 0;
uint dx = 0;
uint dy = 0;
if (dyGreater)
{
dy = M;
dx = N;
}
else
{
dx = M;
dy = N;
}
return String.Format("DLVH ({0},{1}) {2} {3}",
dx * dxSign, dy * dySign,
beamOn ? "ON" : "OFF",
dotted ? "DOTTED" : String.Empty);
}
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

14
imlac/Program.cs Normal file
View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace imlac
{
class Program
{
static void Main(string[] args)
{
}
}
}

View File

@ -10,7 +10,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Living Computers: Museum+Labs")]
[assembly: AssemblyProduct("sImlac")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

File diff suppressed because it is too large Load Diff

View File

@ -31,7 +31,6 @@ namespace imlac
Halted,
SingleStep,
SingleFrame,
SingleDisplayOperation,
UntilDisplayStart,
Running,
Quit
@ -40,19 +39,19 @@ namespace imlac
public class ImlacSystem
{
public ImlacSystem()
{
{
_display = new SDLConsole(0.5f);
_memory = new Memory(this);
_paperTapeReader = new PaperTapeReader(this);
_tty = new TTY(this);
_keyboard = new Keyboard(this);
_clock = new AddressableClock(this);
_interruptFacility = new InterruptFacility(this);
_interruptFacility = new InterruptFacility(this);
_displayProcessor = new DisplayProcessor(this);
_processor = new Processor(this);
AttachDisplayProcessor();
// Register IOT devices
// Register IOT devices
_processor.RegisterDeviceIOTs(_displayProcessor);
_processor.RegisterDeviceIOTs(_paperTapeReader);
_processor.RegisterDeviceIOTs(_tty);
_processor.RegisterDeviceIOTs(_keyboard);
@ -71,16 +70,6 @@ namespace imlac
_processor.Reset();
}
public void Shutdown()
{
_display.Shutdown();
}
public void AttachConsole(IImlacConsole console)
{
_display = console;
}
public Memory Memory
{
get { return _memory; }
@ -91,7 +80,7 @@ namespace imlac
get { return _processor; }
}
public DisplayProcessorBase DisplayProcessor
public DisplayProcessor DisplayProcessor
{
get { return _displayProcessor; }
}
@ -135,12 +124,8 @@ namespace imlac
_keyboard.Clock();
_clock.Clock();
// interrupts last so that devices that raise interrupts get clocked first.
// We clock interrupts on the leading edge of the processor's Fetch state.
if (_processor.InstructionState == ExecState.Fetch)
{
_interruptFacility.Clock();
}
// interrupts last so that devices that raise interrupts get clocked first
_interruptFacility.Clock();
}
//
@ -176,10 +161,11 @@ namespace imlac
return SystemExecutionState.Running;
}
[DebuggerFunction("set pc", "Sets the Processor's PC to the specified address", "<address>")]
private SystemExecutionState SetPC(ushort address)
[DebuggerFunction("step", "Executes a single instruction cycle at the specified address", "<address>")]
private SystemExecutionState StepProcessor(ushort address)
{
Processor.PC = address;
Processor.State = ProcessorState.Running;
return SystemExecutionState.SingleStep;
}
@ -204,29 +190,13 @@ namespace imlac
return SystemExecutionState.UntilDisplayStart;
}
[DebuggerFunction("step display", "Runs until the end of the next display drawing or positioning ooperation")]
private SystemExecutionState RunDisplayOp()
{
Processor.State = ProcessorState.Running;
return SystemExecutionState.SingleDisplayOperation;
}
[DebuggerFunction("load bootstrap", "Loads the specified bootstrap into memory at 40", "<bootstrap>")]
private SystemExecutionState LoadBootstrap(string bootstrap)
[DebuggerFunction("set bootstrap", "Loads the specified bootstrap into memory at 40", "<bootstrap>")]
private SystemExecutionState SetBootstrap(string bootstrap)
{
LoadMemory(Paths.BuildBootPath(bootstrap), 0x20, 0x20);
return SystemExecutionState.Debugging;
}
[DebuggerFunction("boot", "Loads the specified bootstrap into memory at 40 and executes it", "<bootstrap>")]
private SystemExecutionState Boot(string bootstrap)
{
LoadMemory(Paths.BuildBootPath(bootstrap), 0x20, 0x20);
Processor.PC = 0x20;
Processor.State = ProcessorState.Running;
return SystemExecutionState.Running;
}
[DebuggerFunction("save memory", "Saves the specified range of memory to a file", "<file> <start> <length>")]
private SystemExecutionState SaveMemoryContents(string file, ushort start, ushort length)
{
@ -269,36 +239,13 @@ namespace imlac
return SystemExecutionState.Debugging;
}
[DebuggerFunction("show memory", "Displays memory contents", "<start> <length>")]
[DebuggerFunction("display memory", "Displays memory contents", "<start> <length>")]
private SystemExecutionState DisplayMemory(ushort start, ushort length)
{
DumpMemory(start, length);
return SystemExecutionState.Debugging;
}
[DebuggerFunction("set memory size 4kw", "Sets memory size to 4KW")]
private SystemExecutionState SetMemorySize4kw()
{
_memory.SetMemorySize(0x1000);
return SystemExecutionState.Debugging;
}
[DebuggerFunction("set memory size 8kw", "Sets memory size to 8KW")]
private SystemExecutionState SetMemorySize8kw()
{
_memory.SetMemorySize(0x2000);
return SystemExecutionState.Debugging;
}
[DebuggerFunction("set memory size 16kw", "Sets memory size to 16KW")]
private SystemExecutionState SetMemorySize16kw()
{
_memory.SetMemorySize(0x4000);
return SystemExecutionState.Debugging;
}
[DebuggerFunction("set logging", "Sets the logging configuration", "<loglevel>")]
private SystemExecutionState SetLogging(LogType value)
{
@ -334,20 +281,6 @@ namespace imlac
return SystemExecutionState.Debugging;
}
[DebuggerFunction("attach tty telnet", "Attaches the Imlac's TTY to a raw telnet port", "<host> <port>")]
private SystemExecutionState AttachTTY(string host, ushort port)
{
TTY.SetChannel(new TelnetDataChannel(host, port, false));
return SystemExecutionState.Debugging;
}
[DebuggerFunction("attach tty raw", "Attaches the Imlac's TTY to a raw port", "<host> <port>")]
private SystemExecutionState AttachTTYRaw(string host, ushort port)
{
TTY.SetChannel(new TelnetDataChannel(host, port, true));
return SystemExecutionState.Debugging;
}
[DebuggerFunction("detach tty", "Detaches the Imlac's TTY from host inputs")]
private SystemExecutionState DetachTTY()
{
@ -515,114 +448,23 @@ namespace imlac
return SystemExecutionState.Debugging;
}
[DebuggerFunction("enable mit mode", "Turns MIT modifications on.")]
private SystemExecutionState EnableMITMode()
{
Configuration.MITMode = true;
return SystemExecutionState.Debugging;
}
[DebuggerFunction("disable mit mode", "Turns MIT modifications off.")]
private SystemExecutionState DisableMITMode()
{
Configuration.MITMode = false;
return SystemExecutionState.Debugging;
}
[DebuggerFunction("enable squiggle mode", "Turns vector perturbations on.")]
private SystemExecutionState EnableSquiggleMode()
{
Configuration.SquiggleMode = true;
return SystemExecutionState.Debugging;
}
[DebuggerFunction("disable squiggle mode", "Turns vector perturbations off.")]
private SystemExecutionState DisableSquiggleMode()
{
Configuration.SquiggleMode = false;
return SystemExecutionState.Debugging;
}
[DebuggerFunction("draw invisible vectors", "Displays non-drawing beam motion in red.")]
private SystemExecutionState ShowInvisibleVectors()
{
Configuration.ShowInvisibleVectors = true;
return SystemExecutionState.Debugging;
}
[DebuggerFunction("hide invisible vectors", "Hides non-drawing beam motion (the default).")]
private SystemExecutionState HideInvisibleVectors()
{
Configuration.ShowInvisibleVectors = false;
return SystemExecutionState.Debugging;
}
[DebuggerFunction("set cpu type", "Selects CPU type: PDS1 or PDS4", "<cpu>")]
private SystemExecutionState SetCPUType(ImlacCPUType type)
{
if (Configuration.CPUType != type)
{
Configuration.CPUType = type;
AttachDisplayProcessor();
_displayProcessor.Reset();
}
return SystemExecutionState.Debugging;
}
[DebuggerFunction("halt on invalid instructions", "Halts emulation if an invalid instruction is encountered.")]
private SystemExecutionState HaltOnInvalidInstructions()
{
Configuration.HaltOnInvalidOpcodes = true;
return SystemExecutionState.Debugging;
}
[DebuggerFunction("continue on invalid instructions", "Continues emulation if an invalid instruction is encountered.")]
private SystemExecutionState ContinueOnInvalidInstructions()
{
Configuration.HaltOnInvalidOpcodes = false;
return SystemExecutionState.Debugging;
}
private void AttachDisplayProcessor()
{
// Unregister current display processor
if (_displayProcessor != null)
{
_processor.UnregisterDeviceIOTs(_displayProcessor);
}
if (Configuration.CPUType == ImlacCPUType.PDS1)
{
_displayProcessor = new PDS1DisplayProcessor(this);
}
else
{
_displayProcessor = new PDS4DisplayProcessor(this);
}
_processor.RegisterDeviceIOTs(_displayProcessor);
}
public enum DisassemblyMode
{
Processor,
DisplayProcessor,
DisplayIncrement,
DisplayCompact,
DisplayAuto
}
public void PrintProcessorStatus()
{
Console.WriteLine("PC={0} AC={1} MB={2} - {3}\n{4}",
Console.WriteLine("PC={0} AC={1} MB={2} - {3}",
Helpers.ToOctal(Processor.PC),
Helpers.ToOctal(Processor.AC),
Helpers.ToOctal(Memory.Fetch(Processor.PC)),
Processor.State,
Processor.Disassemble(Processor.PC));
Processor.State);
Console.WriteLine("DPC={0} DT={1} DPCE={2} X={3} Y={4}\nMode={5} HalfWord={6}",
Console.WriteLine("DPC={0} DT={1} DPCE={2} X={3} Y={4}\nMode={5} HalfWord={6} - {7}",
Helpers.ToOctal(DisplayProcessor.PC),
Helpers.ToOctal(DisplayProcessor.DT),
Helpers.ToOctal(DisplayProcessor.DPCEntry),
@ -704,35 +546,28 @@ namespace imlac
}
ushort endAddress = (ushort)Math.Min(Memory.Size, startAddress + length);
ushort address = startAddress;
while (address < endAddress)
for (ushort address = startAddress; address <= endAddress; address++)
{
string disassembly = string.Empty;
int size = 0;
try
{
switch (mode)
{
case DisassemblyMode.Processor:
size = 1;
disassembly = Processor.Disassemble(address);
break;
case DisassemblyMode.DisplayAuto:
disassembly = DisplayProcessor.Disassemble(address, DisplayProcessorMode.Indeterminate, out size);
disassembly = DisplayProcessor.Disassemble(address, DisplayProcessorMode.Indeterminate);
break;
case DisassemblyMode.DisplayProcessor:
disassembly = DisplayProcessor.Disassemble(address, DisplayProcessorMode.Processor, out size);
disassembly = DisplayProcessor.Disassemble(address, DisplayProcessorMode.Processor);
break;
case DisassemblyMode.DisplayIncrement:
disassembly = DisplayProcessor.Disassemble(address, DisplayProcessorMode.Increment, out size);
break;
case DisassemblyMode.DisplayCompact:
disassembly = DisplayProcessor.Disassemble(address, DisplayProcessorMode.CompactAddressing, out size);
disassembly = DisplayProcessor.Disassemble(address, DisplayProcessorMode.Increment);
break;
}
}
@ -740,12 +575,9 @@ namespace imlac
{
// this can happen if the data is not a valid instruction
disassembly = "<invalid instruction>";
size = 1;
}
Console.WriteLine("{0}\\{1} {2}", Helpers.ToOctal(address), Helpers.ToOctal(Memory.Fetch(address)), disassembly);
address += (ushort)size;
}
}
@ -794,7 +626,7 @@ namespace imlac
}
private Processor _processor;
private DisplayProcessorBase _displayProcessor;
private DisplayProcessor _displayProcessor;
private IImlacConsole _display;
private Memory _memory;
private PaperTapeReader _paperTapeReader;

View File

@ -36,7 +36,6 @@ namespace imlac
Interrupt = 0x10,
TTY = 0x20,
PTR = 0x40,
Telnet = 0x80,
All = 0x7fffffff
}
@ -98,10 +97,6 @@ namespace imlac
case LogType.Interrupt:
color = ConsoleColor.Blue;
break;
case LogType.Telnet:
color = ConsoleColor.Cyan;
break;
default:
// No change

View File

@ -1,81 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!-- UAC Manifest Options
If you want to change the Windows User Account Control level replace the
requestedExecutionLevel node with one of the following.
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
Specifying requestedExecutionLevel element will disable file and registry virtualization.
Remove this element if your application requires this virtualization for backwards
compatibility.
-->
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
<dpiAware>true</dpiAware>
</windowsSettings>
</application>
</compatibility>
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. -->
<!--
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
</application>
-->
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
<!--
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
-->
</assembly>

View File

@ -34,12 +34,10 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
<Reference Include="SDL2-CS, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\SDL2-CS.dll.2.0.0.0\lib\net20\SDL2-CS.dll</HintPath>
<Reference Include="SdlDotNet, Version=6.1.0.0, Culture=neutral, PublicKeyToken=26ad4f7e10c61408, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\Windows\assembly\GAC_MSIL\SdlDotNet\6.1.0.0__26ad4f7e10c61408\SdlDotNet.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
@ -49,19 +47,15 @@
<Reference Include="System.Data" />
</ItemGroup>
<ItemGroup>
<Compile Include="Configuration.cs" />
<Compile Include="Debugger\BreakpointManager.cs" />
<Compile Include="Debugger\Console.cs" />
<Compile Include="Debugger\DebuggerAttributes.cs" />
<Compile Include="Debugger\DebuggerPrompt.cs" />
<Compile Include="PDS4DisplayProcessor.cs" />
<Compile Include="PDS1DisplayProcessor.cs" />
<Compile Include="HighResTimer.cs" />
<Compile Include="IO\TTYChannels\NullDataChannel.cs" />
<Compile Include="IO\TTYChannels\SerialDataChannel.cs" />
<Compile Include="IO\TTYChannels\StreamDataChannel.cs" />
<Compile Include="IO\TTYChannels\ISerialDataChannel.cs" />
<Compile Include="IO\TTYChannels\TelnetDataChannel.cs" />
<Compile Include="SDLConsole.cs" />
<Compile Include="DisplayProcessor.cs" />
<Compile Include="Helpers.cs" />
@ -92,7 +86,6 @@
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="app.manifest" />
<None Include="boot\mtty">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
@ -105,7 +98,6 @@
<None Include="boot\tty">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

View File

@ -1,20 +1,19 @@
sImlac v0.3 README - (c) 2016-2020 Living Computers: Museum+Labs
sImlac v0.1 README - (c) 2016, 2017 Living Computers: Museum+Labs
-----------------------------------------------------------------
1. Overview
-----------
sImlac is an attempt to emulate/simulate the oft neglected Imlac PDS-1 and
PDS-4 computers/terminals. The Imlac combined a 16-bit CPU (very PDP-8 like)
with a simple (but fairly flexible) Display Processor which drove a vector
sImlac is an attempt to emulate/simulate the oft neglected Imlac PDS-1
computer/terminal. The Imlac combined a 16-bit CPU (very PDP-8 like) with
a simple (but fairly flexible) Display Processor which drove a vector
display.
sImlac currently emulates the current hardware:
- Standard Imlac PDS-1D Processor / Display Processor (with 1.8us cycle timings)
- Standard Imlac PDS-4 Processor / Display Processor (with 990ns cycle timings)
- Up to 16KW of core memory
- Standard Imlac Processor / Display Processor (with 1.8us cycle timings)
- 8KW of core memory
- Vector display (with long-persistence phosphor)
- PTR and TTY interfaces (using physical serial ports, telnet hosts, or files as inputs)
- PTR and TTY interfaces (using physical serial ports or files as inputs)
- Keyboard
- Interrupt facility
- Long Vector (LVH-1 option) instruction support
@ -24,30 +23,12 @@ This is enough to have fun with the small amount of archived software that's out
there. Support for additional hardware is planned, but is mostly dependent on
finding software that requires it.
Since this is v0.3, there are still likely to be bugs. In particular, the PDS-4 support
introduced in this release is not well tested due to lack of applicable software.
Since this is v0.1, there are still likely to be bugs.
Questions, comments, or bug reports can be directed at
joshd@livingcomputers.org.
2. System Requirements
----------------------
sImlac is a .NET application and should run on will run on any Windows PC running
Windows Vista or later, with version 4.5.3 or later of the .NET Framework installed.
.NET should be present by default on Windows Vista and later; if it is not installed
on your computer it can be obtained at https://www.microsoft.com/net.
As sImlac is a .NET application it will also run under Mono
(http://www.mono-project.com/) on Unix and OS X.
sImlac uses SDL 2.0 for display and input. On Windows the appropriate SDL.dll is
included in the distribution package. On Linux, use your system's package manager
to install SDL 2.0; on OS X the easiest way to get SDL 2.0 is to use Homebrew
(https://brew.sh/), via the "brew install sdl2" command.
3. Getting Started
2. Getting Started
------------------
Bootstrapping an Imlac is a pretty straightforward process:
@ -59,18 +40,20 @@ sImlac comes with three boot loaders, and the correct one must be chosen for
the image you are loading (see software.txt for a listing that describes the
available software and the loader to use).
A loader can be loaded and run using the "boot" command. Let's say we want
A loader can be loaded using the "set bootstrap" command. Let's say we want
to play a game of "Space War!." The image named "war" uses the STTY (special
TTY) loader, so we issue the following commands:
TTY) loader, so we issue the following command:
We need to attach the image to the TTY port, this is done by:
> set bootstrap stty
Now we need to attach the image to the TTY port, this is done by:
> attach tty file <path-to-images>\war
Once this is done, we can start the CPU running the bootstrap, which will
load the "war" program into memory and start it running:
Once this is done, we can start the CPU running. By default, the PC is set
to 40(8), the start of the boot loader, so we can just do:
> boot stty
> go
And the loader will run. This will take a few seconds to complete, after which
the title screen for "Space War!" will appear in the display window.
@ -87,10 +70,10 @@ halt if bit 0 is not set.
Will usually set you right.
4. Usage
3. Usage
--------
4.1 Command line arguments
3.1 Command line arguments
--------------------------
sImlac accepts one optional command line argument, which specifies a file to
@ -102,7 +85,7 @@ the contents of "otherscript.txt" to be loaded and executed as a script.)
Whitespace is ignored.
4.2 The sImlac console/debugger
3.2 The sImlac console/debugger
-------------------------------
After startup, you will be at the sImlac debugger prompt (a '>' character).
@ -123,7 +106,7 @@ While the simulated Imlac is running (via the 'go' or other commands) the
console is inactive; press Ctrl-C to stop the Imlac and return to the command
prompt.
4.3 The sImlac display
3.3 The sImlac display
----------------------
sImlac creates a window that simulates the Imlac's vector display and allows
@ -131,15 +114,12 @@ keyboard input to the Imlac. When Imlac programs are running, their output
will be displayed here. By default, the display is shown in a window, pressing
the "Insert" key will toggle between window and fullscreen modes.
4.4 Commands
3.4 Commands
------------
reset : Resets the Imlac, but does not clear its memory.
Any files attached to the paper tape reader or TTY
interface are reset to the beginning of the file.
boot <boot> : Loads the specified bootstrap into memory at 40(8) and
executes it.
set bootstrap <boot>: Loads the specified bootstrap into memory at 40(8).
Options are PTR (paper tape), TTY (standard TTY), and
STTY (alternate TTY).
@ -152,17 +132,9 @@ attach tty file [file] : Specifies a TTY or STTY-format file to attach to the TT
bootstraps.
attach tty port [port] [rate] [parity] [data bits] [stop bits] :
Specifies a physical port on the host machine to connect
to the emulated TTY port. Allows the emulated Imlac to
talk to real serial devices.
attach tty telnet [host] [port] :
Specifies a telnet host to connect to the emulated TTY
port. Allows the emulated Imlac to talk to another
computer over the Internet.
attach tty raw [host] [port] :
As above, but using a raw connection rather than Telnet.
Specifies a physical port on the host machine to connect
to the emulated TTY port. Allows the emulated Imlac to
talk to real serial devices.
go <addr> : Starts the system running at the current PC, or <addr> if
specified.
@ -176,16 +148,6 @@ step frame end : Runs the current 40hz frame to its completion. If the
step frame start : Runs until the start of the next frame.
set cpu type [type] : Sets the type of Imlac system sImlac will emulate.
Valid options are "PDS1" and "PDS4". Switching the cpu
type in the middle of a running program will produce
interesting results.
enable mit mode : Turns on MIT's DADR addressing modifications. This is
necessary to run some Imlac software from MIT.
disable mit mode : The opposite of the above.
edit memory [addr] : Begins a very simple memory editor starting at address
'addr'. You will be shown the current address\contents,
and prompted for new contents. Press ENTER to keep the
@ -275,7 +237,7 @@ show breakpoints : Lists the defined breakpoints.
show commands : Displays a synopsis of available console commands.
4.5 Data Switch Mappings
3.5 Data Switch Mappings
------------------------
Some Imlac software (mostly games) use the Data Switches on the Imlac's front
@ -347,19 +309,3 @@ Thanks go out to:
on it!
- Bitsavers.org for making documentation for the Imlac available.
7. Revision History
-------------------
v0.3 - Added PDS-4 support.
- Many bugfixes to PDS-1 emulation
- Added telnet/raw ports
- Added high-dpi awareness
- Enabled MIT display addressing modifications
- Fixed 40 cycle / display halt interrupt
v0.2 - Updated to use SDL-CS for better cross-platform support.
v0.1 - Initial Release

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="SDL2-CS.dll" version="2.0.0.0" targetFramework="net40" />
</packages>