1
0
mirror of https://github.com/livingcomputermuseum/sImlac.git synced 2026-01-13 15:27:40 +00:00

Compare commits

...

44 Commits
v0.1 ... master

Author SHA1 Message Date
Josh Dersch
37c5437059 Small tweak to the last commit for invalid instruction handling. 2020-05-12 14:43:39 -07:00
Josh Dersch
f0bc983c8a Added "halt on invalid instructions" and "continue on invalid instructions" commands, which do what they say on the tin. 2020-05-12 14:30:06 -07:00
Josh Dersch
0af475cc27 Added new commands:
"draw invisible vectors" - displays non-drawing beam motion in red.
"hide invisible vectors" - returns to normal rendring mode
"step display" - runs until the next display rendering command completes, refreshes the display to allow easier debugging of display programs.

Console input buffer is now cleared after a break operation.
2020-05-12 14:00:35 -07:00
Josh Dersch
8b7280ebe7 Added DCAM disassembly. Fixed annoying space-eating behavior in the CLI's command completion. 2020-05-05 23:49:29 -07:00
Josh Dersch
4c65f894be Fixes for disassembly of uncached instructions; enhancements of display instruction disassembly. More refactoring of display processor code. 2020-05-05 15:10:22 -07:00
Josh Dersch
fcf0c96b6c Fixed issue with REPT key. Added "squiggle" mode. 2020-04-30 22:42:08 -07:00
Josh Dersch
6f30305c79 Fixed keyboard, Mazewar is happy at last, fixed issues with n-key rollover. Tweaked TTY speed up. 2020-04-30 21:29:53 -07:00
Josh Dersch
4130c969c2 Fixed TTY send behavior. 2020-04-29 13:09:53 -07:00
Josh Dersch
3c55ecfb5d Merge branch 'master' of https://github.com/livingcomputermuseum/sImlac 2020-04-29 11:31:54 -07:00
Josh Dersch
1dd50d74f4 Updates to readme.txt. Updated clock speed for PDS-4 emulation. Fixed issue on non-windows platforms for high-dpi calls. 2020-04-29 11:31:50 -07:00
Living Computers: Museum+Labs
3efc31f107
Merge pull request #5 from larsbrinkhoff/lars/appveyor
Continuous integration build on AppVeyor.
2020-04-28 23:50:14 -07:00
Lars Brinkhoff
5124c35296 Continuous integration build on AppVeyor. 2020-04-29 08:13:56 +02:00
Josh Dersch
6f7711fe27 Minor keyboard tweak (never return Invalid for unmapped keys, just ignore). Added high-dpi awareness. Looks nice on 4K displays. 2020-04-28 17:37:10 -07:00
Josh Dersch
8ced87eb1e Tweaks to keyboard (no longer loses keystrokes, auto-repeat is disabled unless REPT (Alt key) is held down. Fixed 40 cycle sync interrupt (must wait for display to halt as well.) SSV.22 behaves sanely with large numbers of characters displayed now. Slowed TTY interface down, MAZE now loads reliably from ITS. 2020-04-28 16:36:50 -07:00
Josh Dersch
3a7afbafc0 Tweaks for interrupt handling; added one instruction delay after ION before enabling interrupts, interrupts are now clocked on the leading edge of the processor Fetch cycle rather than on every clock cycle. 2020-04-28 11:07:45 -07:00
Josh Dersch
aee6c5dc63 Made reset a bit more thorough. 2020-04-27 00:46:35 -07:00
Josh Dersch
99d6173392 Fix bug with screen intensity (should reset to 1.0). Added disassembly comments for all known IOTs. 2020-04-26 23:06:36 -07:00
Josh Dersch
64d027dcab 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. 2020-04-26 01:56:10 -07:00
Josh Dersch
5b9eb37f9c Implemented decoding of PDS-4 display instructions, updated LVM decoding and execution (completely different from PDS-1). Execution of new PDS-4 display instructions not yet implemented; awaiting the discovery of code that uses them.
Tweaked color of point-plotted and vectors that resolve to a single point, brighter and slightly different shade of green for effect.
2020-04-23 17:32:44 -07:00
Josh Dersch
7dce231368 Initial swipe at refactoring display processor to allow separate PDS-1 and PDS-4 variants. 2020-04-23 11:16:24 -07:00
Josh Dersch
7b616105b2 More PDS-4 work; most of the PDS-4 main processor instructions are implemented though not thoroughly tested (no software that has been unearthed of uses most of them). The byte-wise instructions have not been tackled yet.
MIT DADR behavior is corrected, now acts as a toggle.  Single point plotting is now brighter than vector drawing for emphasis.  Minor correction to disassembly of HLT instructions.  Move config options to a new Configuration class, will someday be more sophisticated.
2020-04-21 10:58:10 -07:00
Josh Dersch
6ddbf3791d Quick tweaks to Telnet; added real raw mode, synchronization around buffer reads/writes 2020-04-17 12:14:47 -07:00
Josh Dersch
b0321a70e6 Preliminary mods for MIT and PDS-4 modes. 2020-04-16 00:56:05 -07:00
Living Computers: Museum+Labs
8b5fb8e997
Merge pull request #2 from larsbrinkhoff/lars/travis
Build in Travis CI.
2019-02-02 13:17:20 -08:00
Josh Dersch
d33cc3d166 Fix for boneheaded mistake in disassembler. 2018-10-12 16:40:05 -07:00
Josh Dersch
18bb3eb9bd Made memory size configurable. Defaults to 8KW. 2018-10-12 12:53:01 -07:00
Josh Dersch
d54292d346 Extended memory to 16KW. 2018-10-11 17:52:40 -07:00
Josh Dersch
98189686ce Added missing / fixed mismapped keyboard keys. Minor tweak to display. 2018-10-11 12:25:46 -07:00
Josh Dersch
ed54a38d3a Small tweaks, improvement to TTY flags. 2018-10-10 14:17:47 -07:00
Josh Dersch
a7f81a8b8f Display is properly centered in fullscreen mode. Added very primitive Telnet interface for TTY. 2018-10-10 09:32:04 -07:00
Josh Dersch
34d48856b6 Moved UI to main thread (so OS X is happier). 2018-10-08 18:59:47 -07:00
Josh Dersch
cc6cc29682 Fixing .gitignore to ignore VS files properly. 2018-10-07 13:39:23 -07:00
Living Computers: Museum+Labs
e306c11e4f
Delete .dtbcache 2018-10-07 13:33:57 -07:00
Living Computers: Museum+Labs
91682a2f6d
Delete storage.ide 2018-10-07 13:33:39 -07:00
Living Computers: Museum+Labs
47c6643cce
Delete storage.ide-shm 2018-10-07 13:33:32 -07:00
Living Computers: Museum+Labs
085886e30c
Delete storage.ide-wal 2018-10-07 13:33:25 -07:00
Living Computers: Museum+Labs
d1971d41de
Delete ProjectSettings.json 2018-10-07 13:33:15 -07:00
Living Computers: Museum+Labs
39032d79c4
Delete VSWorkspaceState.json 2018-10-07 13:33:08 -07:00
Living Computers: Museum+Labs
2fb927d857
Delete slnx.sqlite 2018-10-07 13:33:01 -07:00
Josh Dersch
9ed3e85ab6 Fix for debugger prompt when buffersize parameters are zero. 2018-10-07 13:31:18 -07:00
Lars Brinkhoff
2d2f4d9b6d Build in Travis CI. 2018-10-06 09:41:47 +02:00
Josh Dersch
0c26c2f080 Fix high res timer issue on non-win32 platforms. 2018-10-05 22:56:25 -07:00
Josh Dersch
024d95994b Changed SDL wrapper to SDL-CS for better cross-platform support. 2018-10-05 12:32:01 -07:00
Living Computers: Museum+Labs
8d866c46c4 Delete Program.cs 2017-05-30 14:42:36 -07:00
32 changed files with 4943 additions and 1655 deletions

4
.gitignore vendored
View File

@ -190,3 +190,7 @@ _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

2
.travis.yml Normal file
View File

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

3
.vs/ProjectSettings.json Normal file
View File

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

View File

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

BIN
.vs/slnx.sqlite Normal file

Binary file not shown.

3
README.md Normal file
View File

@ -0,0 +1,3 @@
![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)

8
appveyor.yml Normal file
View File

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

34
imlac/Configuration.cs Normal file
View File

@ -0,0 +1,34 @@
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, MethodInfo method, object instance)
public void AddSubNode(List<string> words, DebuggerCommand command)
{
// 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(instance, words[0], null, null, null);
subNode = new DebuggerCommand(command.Instance, words[0], command.Description, command.Usage, 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(method);
subNode.Methods.Add(command.Methods[0]);
// 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 = method.GetParameters().Length;
int argCount = command.Methods[0].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(method);
subNode.Methods.Add(command.Methods[0]);
// and return early.
return;
@ -119,7 +119,7 @@ namespace imlac.Debugger
// We have more words to go.
words.RemoveAt(0);
subNode.AddSubNode(words, method, instance);
subNode.AddSubNode(words, command);
}
public DebuggerCommand FindSubNodeByName(string name)
@ -159,14 +159,22 @@ namespace imlac.Debugger
// Get the command string from the prompt.
string command = _consolePrompt.Prompt().Trim();
if (command != String.Empty)
if (string.IsNullOrEmpty(command))
{
next = ExecuteLine(command, system);
// Repeat the last command
command = _lastCommand;
Console.WriteLine(">> {0}", command);
}
next = ExecuteLine(command, system);
_lastCommand = command;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(
"Error: {0}",
e.InnerException != null ? e.InnerException.Message : e.Message);
}
return next;
@ -650,9 +658,9 @@ namespace imlac.Debugger
{
string[] commandWords = c.Name.Split(' ');
// This is kind of ugly, we know that at this point every command built above have only
// This is kind of ugly, we know that at this point every command built above has only
// one method. When building the tree, overloaded commands may end up with more than one.
_commandRoot.AddSubNode(new List<string>(commandWords), c.Methods[0], c.Instance);
_commandRoot.AddSubNode(new List<string>(commandWords), c);
}
}
@ -683,5 +691,7 @@ namespace imlac.Debugger
private DebuggerPrompt _consolePrompt;
private DebuggerCommand _commandRoot;
private List<DebuggerCommand> _commandList;
private string _lastCommand;
}
}

View File

@ -197,8 +197,11 @@ namespace imlac.Debugger
clear = sb.ToString();
}
int column = ((_textPosition + _originColumn) % Console.BufferWidth);
int row = ((_textPosition + _originColumn) / Console.BufferWidth) + _originRow;
// 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;
// Move cursor to origin to draw string
Console.CursorLeft = _originColumn;
@ -294,6 +297,13 @@ 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;
}
@ -320,6 +330,7 @@ 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.
@ -328,6 +339,7 @@ namespace imlac.Debugger
if (c.Name.ToLower() == tokens[0].ToLower())
{
match = c;
exactMatch = true;
break;
}
}
@ -391,7 +403,7 @@ namespace imlac.Debugger
{
subMatch = FuzzyMatch(match, tokens, silent);
}
else // if (exactMatch)
else
{
if (!silent && match.SubCommands.Count > 1)
{
@ -419,11 +431,27 @@ 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,28 +24,57 @@ namespace imlac
{
static void Main(string[] args)
{
ImlacSystem system = new ImlacSystem();
ConsoleExecutor debuggerPrompt =
new ConsoleExecutor(system);
_startupArgs = args;
_console = new SDLConsole(0.5f);
_imlacSystem = new ImlacSystem();
_imlacSystem.AttachConsole(_console);
_imlacSystem.Reset();
_state = SystemExecutionState.Debugging;
Console.CancelKeyPress += new ConsoleCancelEventHandler(OnBreak);
PrintHerald();
if (args.Length > 0)
//
// 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)
{
//
// Assume arg 0 is a script file to be executed.
//
Console.WriteLine("Executing startup script '{0}'", args[0]);
Console.WriteLine("Executing startup script '{0}'", _startupArgs[0]);
try
{
_state = debuggerPrompt.ExecuteScript(system, args[0]);
_state = debuggerPrompt.ExecuteScript(_imlacSystem, _startupArgs[0]);
}
catch(Exception e)
catch (Exception e)
{
Console.WriteLine("Error parsing script: {0}", e.Message);
}
@ -59,19 +88,19 @@ namespace imlac
{
case SystemExecutionState.Halted:
case SystemExecutionState.Debugging:
_state = debuggerPrompt.Prompt(system);
_state = debuggerPrompt.Prompt(_imlacSystem);
break;
case SystemExecutionState.SingleStep:
system.SingleStep();
system.Display.RenderCurrent(false);
_imlacSystem.SingleStep();
_imlacSystem.Display.RenderCurrent(false);
_state = SystemExecutionState.Debugging;
break;
case SystemExecutionState.SingleFrame:
system.SingleStep();
_imlacSystem.SingleStep();
if (system.DisplayProcessor.FrameLatch)
if (_imlacSystem.DisplayProcessor.FrameLatch)
{
Console.WriteLine("Frame completed.");
_state = SystemExecutionState.Debugging;
@ -79,55 +108,85 @@ namespace imlac
break;
case SystemExecutionState.UntilDisplayStart:
system.SingleStep();
_imlacSystem.SingleStep();
if (system.DisplayProcessor.State == ProcessorState.Running)
if (_imlacSystem.DisplayProcessor.State == ProcessorState.Running)
{
Console.WriteLine("Display started.");
_state = SystemExecutionState.Debugging;
}
break;
case SystemExecutionState.Running:
system.SingleStep();
case SystemExecutionState.SingleDisplayOperation:
_imlacSystem.SingleStep();
if (system.Processor.State == ProcessorState.Halted)
if (_imlacSystem.DisplayProcessor.DisplayDrawLatch)
{
Console.WriteLine("Main processor halted at {0}", Helpers.ToOctal(system.Processor.PC));
_imlacSystem.Display.RenderCurrent(false);
Console.WriteLine("Display operation completed.");
_state = SystemExecutionState.Debugging;
}
else if (system.Processor.State == ProcessorState.BreakpointHalt)
break;
case SystemExecutionState.Running:
_imlacSystem.SingleStep();
if (_imlacSystem.Processor.State == ProcessorState.Halted)
{
Console.WriteLine("Main processor halted at {0}", Helpers.ToOctal(_imlacSystem.Processor.PC));
_state = SystemExecutionState.Debugging;
}
else if (_imlacSystem.Processor.State == ProcessorState.BreakpointHalt)
{
Console.WriteLine(
"Breakpoint hit: {0} at address {1}",
BreakpointManager.GetBreakpoint(system.Processor.BreakpointAddress),
Helpers.ToOctal(system.Processor.BreakpointAddress));
BreakpointManager.GetBreakpoint(_imlacSystem.Processor.BreakpointAddress),
Helpers.ToOctal(_imlacSystem.Processor.BreakpointAddress));
_state = SystemExecutionState.Debugging;
}
break;
}
}
catch(Exception e)
catch (Exception e)
{
Console.WriteLine("Internal error during execution: {0}", e.Message);
_state = SystemExecutionState.Debugging;
if (!(e is System.Threading.ThreadAbortException))
{
Console.WriteLine("Internal error during execution: {0}", e.Message);
_state = SystemExecutionState.Debugging;
}
}
}
// We are exiting, shut things down.
//
_imlacSystem.Shutdown();
}
static void OnBreak(object sender, ConsoleCancelEventArgs e)
private 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();
}
}
static void PrintHerald()
private static void PrintHerald()
{
Console.WriteLine("sImlac v0.1. (c) 2016, 2017 Living Computers: Museum+Labs");
Console.WriteLine("sImlac v0.3. (c) 2016-2020 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,5 +37,17 @@ 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,16 +155,24 @@ namespace imlac
~FrameTimer()
{
//
// Clean stuff up
//
DeleteTimerQueueTimer(_hTimerQueue, _hTimer, IntPtr.Zero);
DeleteTimerQueue(_hTimerQueue, IntPtr.Zero);
try
{
//
// 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();
//
// 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.
}
}
/// <summary>

View File

@ -28,7 +28,111 @@ 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
@ -40,7 +144,7 @@ namespace imlac
/// <summary>
/// Indicates whether a key is currently pressed.
/// </summary>
bool IsKeyPressed
bool NewKeyPressed
{
get;
}
@ -69,6 +173,16 @@ namespace imlac
get;
}
int MouseX
{
get;
}
int MouseY
{
get;
}
bool ThrottleFramerate
{
get;
@ -97,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);
@ -107,8 +221,14 @@ 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,38 +69,87 @@ namespace imlac.IO
public void Clock()
{
if (_interruptsEnabled)
if (_interruptsEnabled && _interruptEnableCount == 0)
{
// 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 &&
_system.DisplayProcessor.DisplayHalted)
{
_interruptStatus |= 0x0002;
}
// bit 11 - keyboard
if (_system.Keyboard.KeyReady)
{
_interruptStatus |= 0x0010;
}
// bit 12 - TTY rcv
if (_system.TTY.DataReady)
{
_interruptStatus |= 0x0008;
}
// bit 2 - ACI-1 (clock)
if (_system.Clock.TimerTriggered)
// 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;
}
}
else
{
_interruptStatus |= 0x2000;
// PDS-4:
// Bit 15: Display halt
if (_system.DisplayProcessor.DisplayHalted)
{
_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;
}
@ -109,7 +158,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);
@ -124,6 +173,10 @@ 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()
@ -135,6 +188,7 @@ namespace imlac.IO
{
//
// Dispatch the IOT instruction.
// TODO: handle PDS-4 IOTs.
//
switch (iotCode)
{
@ -146,12 +200,20 @@ 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 {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:
@ -161,10 +223,11 @@ 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)));
}
}
private int _interruptEnableCount;
private bool _interruptsEnabled;
private bool _interruptPending;
private int _interruptMask;
@ -174,10 +237,18 @@ 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,6 +31,7 @@ namespace imlac.IO
public enum ImlacKey
{
None = 0x0,
DataXmit = 0x2,
Down = 0x4,
Right = 0x5,
@ -39,6 +40,7 @@ namespace imlac.IO
Tab = 0x9,
CR = 0x0d,
FF = 0x0c,
LF = 0x0a,
PageXmit = 0xe,
Home = 0xf,
Brk = 0x19,
@ -117,26 +119,18 @@ namespace imlac.IO
public void Clock()
{
// If we do not already have a key latched and one has been pressed,
// we will latch it now.
// Based on the keycode & modifiers we will generate an Imlac keyboard code
if (_system.Display.IsKeyPressed)
// we will raise the Ready flag now.
if (!_keyReady)
{
_keyCode = GetScancodeForCurrentKey();
if (_keyCode != 0)
{
Trace.Log(LogType.Keyboard, "Key latched {0}", Helpers.ToOctal(_keyCode));
if (_system.Display.NewKeyPressed)
{
_keyReady = true;
}
}
else
{
_keyCode = 0;
}
}
public void Reset()
{
_keyCode = 0;
_keyReady = false;
}
@ -155,20 +149,18 @@ namespace imlac.IO
switch (iotCode)
{
case 0x11:
_system.Processor.AC |= _keyCode;
_system.Processor.AC |= GetScancodeForCurrentKey();
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 |= _keyCode;
_keyCode = 0;
_system.Processor.AC |= GetScancodeForCurrentKey();
_keyReady = false;
_system.Display.UnlatchKey();
Trace.Log(LogType.Keyboard, "Key OR'd into AC {0}, keyboard flag reset.", Helpers.ToOctal(_system.Processor.AC));
@ -196,7 +188,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.
@ -225,7 +217,6 @@ namespace imlac.IO
private readonly int[] _handledIOTs = { 0x11, 0x12, 0x13 };
private bool _keyReady;
private ushort _keyCode;
private ImlacSystem _system;
@ -247,6 +238,7 @@ 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);
@ -255,6 +247,7 @@ 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,6 +36,10 @@ namespace imlac.IO
_dataSendReady = true;
_dataReady = false;
_clocks = 0;
_dataBufferFull = false;
_dataSentLatch = false;
_rxData = 0;
_txData = 0;
if (_dataChannel != null)
{
@ -50,6 +54,11 @@ namespace imlac.IO
throw new ArgumentNullException("channel");
}
if (_dataChannel != null)
{
_dataChannel.Close();
}
_dataChannel = channel;
}
@ -61,27 +70,22 @@ namespace imlac.IO
{
_clocks = 0;
if (_dataChannel.DataAvailable)
if (_dataChannel.DataAvailable && !_dataReady)
{
_dataReady = true;
_data = _dataChannel.Read();
_rxData = _dataChannel.Read();
Trace.Log(LogType.TTY, "i");
}
else
// Are we waiting to send something?
if (_dataBufferFull && _dataChannel.OutputReady)
{
_dataReady = false;
_dataChannel.Write(_txData);
Trace.Log(LogType.TTY, "o {0}", Helpers.ToOctal(_txData));
_dataBufferFull = false;
_dataSentLatch = true;
_dataSendReady = true;
}
}
// Are we waiting to send something?
if (!_dataSendReady && _dataChannel.OutputReady)
{
_dataChannel.Write(_data);
Trace.Log(LogType.TTY, "o");
// Sent, reset flag.
_dataSendReady = true;
}
}
@ -95,6 +99,16 @@ namespace imlac.IO
get { return _dataSendReady; }
}
public bool DataSentLatch
{
get
{
bool latch = _dataSentLatch;
_dataSentLatch = false;
return latch;
}
}
public int[] GetHandledIOTs()
{
return _handledIOTs;
@ -105,8 +119,8 @@ namespace imlac.IO
switch (iotCode)
{
case 0x19: // RRB - TTY read
Trace.Log(LogType.TTY, "TTY read {0}", Helpers.ToOctal(_data));
_system.Processor.AC |= _data;
Trace.Log(LogType.TTY, "TTY read {0}", Helpers.ToOctal(_rxData));
_system.Processor.AC |= _rxData;
break;
case 0x1a: // RCF - Clear TTY status
@ -114,30 +128,35 @@ namespace imlac.IO
break;
case 0x1b: // RRC - Read and clear status
Trace.Log(LogType.TTY, "TTY read {0}, status cleared.", Helpers.ToOctal(_data));
Trace.Log(LogType.TTY, "TTY read {0}, status cleared.", Helpers.ToOctal(_rxData));
_dataReady = false;
_system.Processor.AC |= _data;
_system.Processor.AC |= _rxData;
break;
case 0x21: // TPR - transmit
if (_dataSendReady) // only if transmitter is ready
if (_dataSendReady)
{
_data = (byte)_system.Processor.AC;
_txData = (byte)_system.Processor.AC;
_dataSendReady = false;
_dataBufferFull = true;
}
break;
case 0x22: // TCF - clear output flag
_dataSendReady = true;
break;
case 0x23: // TPC - print, clear flag
_data = (byte)_system.Processor.AC;
_dataSendReady = false;
break;
case 0x23: // TPC - print, clear flag
if (_dataSendReady)
{
_txData = (byte)_system.Processor.AC;
_dataSendReady = false;
_dataBufferFull = true;
}
break;
default:
Trace.Log(LogType.TTY, "Stub: TTY xmit op", Helpers.ToOctal(_data));
Trace.Log(LogType.TTY, "Stub: TTY xmit op", Helpers.ToOctal(_rxData));
break;
}
}
@ -146,10 +165,13 @@ namespace imlac.IO
private bool _dataReady;
private bool _dataSendReady;
private byte _data;
private bool _dataBufferFull;
private bool _dataSentLatch;
private byte _rxData;
private byte _txData;
private int _clocks;
private readonly int _dataClocks = 100;
private readonly int _dataClocks = 90; // Appx. 50kbps
private ISerialDataChannel _dataChannel;

View File

@ -0,0 +1,221 @@
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,15 +15,39 @@
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)
{
_mem = new ushort[Size];
{
_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();
}
}
public ushort Fetch(ushort address)
@ -44,15 +68,18 @@ namespace imlac
public static ushort Size
{
get { return 8192; }
get { return _size; }
}
public static ushort SizeMask
{
get { return 0x1fff; }
get { return _sizeMask; }
}
private static ushort _size;
private static ushort _sizeMask;
private ushort[] _mem;
private ImlacSystem _system;
}
}
}

View File

@ -0,0 +1,816 @@
/*
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

View File

@ -1,14 +0,0 @@
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 © 2017")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

File diff suppressed because it is too large Load Diff

View File

@ -31,6 +31,7 @@ namespace imlac
Halted,
SingleStep,
SingleFrame,
SingleDisplayOperation,
UntilDisplayStart,
Running,
Quit
@ -39,19 +40,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);
_displayProcessor = new DisplayProcessor(this);
_interruptFacility = new InterruptFacility(this);
_processor = new Processor(this);
// Register IOT devices
_processor.RegisterDeviceIOTs(_displayProcessor);
AttachDisplayProcessor();
// Register IOT devices
_processor.RegisterDeviceIOTs(_paperTapeReader);
_processor.RegisterDeviceIOTs(_tty);
_processor.RegisterDeviceIOTs(_keyboard);
@ -70,6 +71,16 @@ namespace imlac
_processor.Reset();
}
public void Shutdown()
{
_display.Shutdown();
}
public void AttachConsole(IImlacConsole console)
{
_display = console;
}
public Memory Memory
{
get { return _memory; }
@ -80,7 +91,7 @@ namespace imlac
get { return _processor; }
}
public DisplayProcessor DisplayProcessor
public DisplayProcessorBase DisplayProcessor
{
get { return _displayProcessor; }
}
@ -124,8 +135,12 @@ namespace imlac
_keyboard.Clock();
_clock.Clock();
// interrupts last so that devices that raise interrupts get clocked first
_interruptFacility.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();
}
}
//
@ -161,11 +176,10 @@ namespace imlac
return SystemExecutionState.Running;
}
[DebuggerFunction("step", "Executes a single instruction cycle at the specified address", "<address>")]
private SystemExecutionState StepProcessor(ushort address)
[DebuggerFunction("set pc", "Sets the Processor's PC to the specified address", "<address>")]
private SystemExecutionState SetPC(ushort address)
{
Processor.PC = address;
Processor.State = ProcessorState.Running;
return SystemExecutionState.SingleStep;
}
@ -190,13 +204,29 @@ namespace imlac
return SystemExecutionState.UntilDisplayStart;
}
[DebuggerFunction("set bootstrap", "Loads the specified bootstrap into memory at 40", "<bootstrap>")]
private SystemExecutionState SetBootstrap(string bootstrap)
[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)
{
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)
{
@ -239,13 +269,36 @@ namespace imlac
return SystemExecutionState.Debugging;
}
[DebuggerFunction("display memory", "Displays memory contents", "<start> <length>")]
[DebuggerFunction("show 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)
{
@ -281,6 +334,20 @@ 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()
{
@ -448,23 +515,114 @@ 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}",
Console.WriteLine("PC={0} AC={1} MB={2} - {3}\n{4}",
Helpers.ToOctal(Processor.PC),
Helpers.ToOctal(Processor.AC),
Helpers.ToOctal(Memory.Fetch(Processor.PC)),
Processor.State);
Processor.State,
Processor.Disassemble(Processor.PC));
Console.WriteLine("DPC={0} DT={1} DPCE={2} X={3} Y={4}\nMode={5} HalfWord={6} - {7}",
Console.WriteLine("DPC={0} DT={1} DPCE={2} X={3} Y={4}\nMode={5} HalfWord={6}",
Helpers.ToOctal(DisplayProcessor.PC),
Helpers.ToOctal(DisplayProcessor.DT),
Helpers.ToOctal(DisplayProcessor.DPCEntry),
@ -546,28 +704,35 @@ namespace imlac
}
ushort endAddress = (ushort)Math.Min(Memory.Size, startAddress + length);
for (ushort address = startAddress; address <= endAddress; address++)
ushort address = startAddress;
while (address < endAddress)
{
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);
disassembly = DisplayProcessor.Disassemble(address, DisplayProcessorMode.Indeterminate, out size);
break;
case DisassemblyMode.DisplayProcessor:
disassembly = DisplayProcessor.Disassemble(address, DisplayProcessorMode.Processor);
disassembly = DisplayProcessor.Disassemble(address, DisplayProcessorMode.Processor, out size);
break;
case DisassemblyMode.DisplayIncrement:
disassembly = DisplayProcessor.Disassemble(address, DisplayProcessorMode.Increment);
disassembly = DisplayProcessor.Disassemble(address, DisplayProcessorMode.Increment, out size);
break;
case DisassemblyMode.DisplayCompact:
disassembly = DisplayProcessor.Disassemble(address, DisplayProcessorMode.CompactAddressing, out size);
break;
}
}
@ -575,9 +740,12 @@ 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;
}
}
@ -626,7 +794,7 @@ namespace imlac
}
private Processor _processor;
private DisplayProcessor _displayProcessor;
private DisplayProcessorBase _displayProcessor;
private IImlacConsole _display;
private Memory _memory;
private PaperTapeReader _paperTapeReader;

View File

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

81
imlac/app.manifest Normal file
View File

@ -0,0 +1,81 @@
<?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,10 +34,12 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
<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 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>
<Reference Include="System" />
<Reference Include="System.Core" />
@ -47,15 +49,19 @@
<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" />
@ -86,6 +92,7 @@
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="app.manifest" />
<None Include="boot\mtty">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
@ -98,6 +105,7 @@
<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,19 +1,20 @@
sImlac v0.1 README - (c) 2016, 2017 Living Computers: Museum+Labs
sImlac v0.3 README - (c) 2016-2020 Living Computers: Museum+Labs
-----------------------------------------------------------------
1. Overview
-----------
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
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
display.
sImlac currently emulates the current hardware:
- Standard Imlac Processor / Display Processor (with 1.8us cycle timings)
- 8KW of core memory
- 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
- Vector display (with long-persistence phosphor)
- PTR and TTY interfaces (using physical serial ports or files as inputs)
- PTR and TTY interfaces (using physical serial ports, telnet hosts, or files as inputs)
- Keyboard
- Interrupt facility
- Long Vector (LVH-1 option) instruction support
@ -23,12 +24,30 @@ 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.1, there are still likely to be bugs.
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.
Questions, comments, or bug reports can be directed at
joshd@livingcomputers.org.
2. Getting Started
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
------------------
Bootstrapping an Imlac is a pretty straightforward process:
@ -40,20 +59,18 @@ 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 using the "set bootstrap" command. Let's say we want
A loader can be loaded and run using the "boot" 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 command:
TTY) loader, so we issue the following commands:
> set bootstrap stty
Now we need to attach the image to the TTY port, this is done by:
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. By default, the PC is set
to 40(8), the start of the boot loader, so we can just do:
Once this is done, we can start the CPU running the bootstrap, which will
load the "war" program into memory and start it running:
> go
> boot stty
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.
@ -70,10 +87,10 @@ halt if bit 0 is not set.
Will usually set you right.
3. Usage
4. Usage
--------
3.1 Command line arguments
4.1 Command line arguments
--------------------------
sImlac accepts one optional command line argument, which specifies a file to
@ -85,7 +102,7 @@ the contents of "otherscript.txt" to be loaded and executed as a script.)
Whitespace is ignored.
3.2 The sImlac console/debugger
4.2 The sImlac console/debugger
-------------------------------
After startup, you will be at the sImlac debugger prompt (a '>' character).
@ -106,7 +123,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.
3.3 The sImlac display
4.3 The sImlac display
----------------------
sImlac creates a window that simulates the Imlac's vector display and allows
@ -114,12 +131,15 @@ 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.
3.4 Commands
4.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.
set bootstrap <boot>: Loads the specified bootstrap into memory at 40(8).
boot <boot> : Loads the specified bootstrap into memory at 40(8) and
executes it.
Options are PTR (paper tape), TTY (standard TTY), and
STTY (alternate TTY).
@ -132,9 +152,17 @@ 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.
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.
go <addr> : Starts the system running at the current PC, or <addr> if
specified.
@ -148,6 +176,16 @@ 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
@ -237,7 +275,7 @@ show breakpoints : Lists the defined breakpoints.
show commands : Displays a synopsis of available console commands.
3.5 Data Switch Mappings
4.5 Data Switch Mappings
------------------------
Some Imlac software (mostly games) use the Data Switches on the Imlac's front
@ -309,3 +347,19 @@ 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

4
imlac/packages.config Normal file
View File

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