1
0
mirror of https://github.com/livingcomputermuseum/Darkstar.git synced 2026-03-30 19:58:59 +00:00

Initial commit to public repository.

This commit is contained in:
Josh Dersch
2019-01-15 12:55:18 -08:00
parent dbeb0ed280
commit aedcf2dd71
114 changed files with 31547 additions and 3 deletions

63
.gitattributes vendored Normal file
View File

@@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

261
.gitignore vendored Normal file
View File

@@ -0,0 +1,261 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
project.fragment.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
#*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc

73
D.sln Normal file
View File

@@ -0,0 +1,73 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26403.7
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Darkstar", "D\Darkstar.csproj", "{0590465E-1D91-4591-946E-EE3F7D82834B}"
EndProject
Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "DarkstarSetup", "DSetup\DarkstarSetup.wixproj", "{BCB1273C-6A17-4AFE-88AD-470A3594A009}"
EndProject
Global
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|ia64 = Debug|ia64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|ia64 = Release|ia64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0590465E-1D91-4591-946E-EE3F7D82834B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0590465E-1D91-4591-946E-EE3F7D82834B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0590465E-1D91-4591-946E-EE3F7D82834B}.Debug|ia64.ActiveCfg = Debug|Any CPU
{0590465E-1D91-4591-946E-EE3F7D82834B}.Debug|ia64.Build.0 = Debug|Any CPU
{0590465E-1D91-4591-946E-EE3F7D82834B}.Debug|x64.ActiveCfg = Debug|x64
{0590465E-1D91-4591-946E-EE3F7D82834B}.Debug|x64.Build.0 = Debug|x64
{0590465E-1D91-4591-946E-EE3F7D82834B}.Debug|x86.ActiveCfg = Debug|Any CPU
{0590465E-1D91-4591-946E-EE3F7D82834B}.Debug|x86.Build.0 = Debug|Any CPU
{0590465E-1D91-4591-946E-EE3F7D82834B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0590465E-1D91-4591-946E-EE3F7D82834B}.Release|Any CPU.Build.0 = Release|Any CPU
{0590465E-1D91-4591-946E-EE3F7D82834B}.Release|ia64.ActiveCfg = Release|Any CPU
{0590465E-1D91-4591-946E-EE3F7D82834B}.Release|ia64.Build.0 = Release|Any CPU
{0590465E-1D91-4591-946E-EE3F7D82834B}.Release|x64.ActiveCfg = Release|x64
{0590465E-1D91-4591-946E-EE3F7D82834B}.Release|x64.Build.0 = Release|x64
{0590465E-1D91-4591-946E-EE3F7D82834B}.Release|x86.ActiveCfg = Release|Any CPU
{0590465E-1D91-4591-946E-EE3F7D82834B}.Release|x86.Build.0 = Release|Any CPU
{BCB1273C-6A17-4AFE-88AD-470A3594A009}.Debug|Any CPU.ActiveCfg = Debug|x86
{BCB1273C-6A17-4AFE-88AD-470A3594A009}.Debug|ia64.ActiveCfg = Debug|x86
{BCB1273C-6A17-4AFE-88AD-470A3594A009}.Debug|x64.ActiveCfg = Debug|x86
{BCB1273C-6A17-4AFE-88AD-470A3594A009}.Debug|x86.ActiveCfg = Debug|x86
{BCB1273C-6A17-4AFE-88AD-470A3594A009}.Debug|x86.Build.0 = Debug|x86
{BCB1273C-6A17-4AFE-88AD-470A3594A009}.Release|Any CPU.ActiveCfg = Release|x86
{BCB1273C-6A17-4AFE-88AD-470A3594A009}.Release|ia64.ActiveCfg = Release|x86
{BCB1273C-6A17-4AFE-88AD-470A3594A009}.Release|x64.ActiveCfg = Release|x86
{BCB1273C-6A17-4AFE-88AD-470A3594A009}.Release|x86.ActiveCfg = Release|x86
{BCB1273C-6A17-4AFE-88AD-470A3594A009}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DA616BB8-1CC8-43E2-A841-6DB3E9DA4AA8}
EndGlobalSection
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
EndGlobal

48
D/App.config Normal file
View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="D.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
<userSettings>
<D.Properties.Settings>
<setting name="HardDriveImage" serializeAs="String">
<value />
</setting>
<setting name="FloppyDriveImage" serializeAs="String">
<value />
</setting>
<setting name="ThrottleSpeed" serializeAs="String">
<value>True</value>
</setting>
<setting name="DisplayScale" serializeAs="String">
<value>1</value>
</setting>
<setting name="HostAddress" serializeAs="String">
<value>2852201285</value>
</setting>
<setting name="HostPacketInterfaceName" serializeAs="String">
<value />
</setting>
<setting name="MemorySize" serializeAs="String">
<value>1024</value>
</setting>
<setting name="SlowPhosphor" serializeAs="String">
<value>True</value>
</setting>
<setting name="TODDateTime" serializeAs="String">
<value>1979-12-10</value>
</setting>
<setting name="TODSetMode" serializeAs="String">
<value>0</value>
</setting>
<setting name="TODDate" serializeAs="String">
<value>1955-11-05</value>
</setting>
</D.Properties.Settings>
</userSettings>
</configuration>

807
D/CP/AM2901.cs Normal file
View File

@@ -0,0 +1,807 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
namespace D.CP
{
/// <summary>
/// This implements an abstraction of the AMD 2901 as seen by the Central Processor --
/// that is: as a 16-bit ALU + register file, rather than four 4-bit ALUs hooked together.
/// This only implements the signals and operations that the CP actually cares about.
/// </summary>
public class AM2901
{
static AM2901()
{
BuildTables();
}
public AM2901()
{
}
public ushort[] R
{
get { return _r; }
}
public ushort Q
{
get { return _q; }
}
/// <summary>
/// Executes the ALU operation specified by the given microinstruction.
/// </summary>
/// <param name="i">The microinstruction</param>
/// <param name="d">The ALU D input</param>
/// <param name="carryIn">The ALU Carry in</param>
/// <param name="loadMAR">Whether this operation is taking place during an MAR<- operation,
/// in which case the top half of the ALU needs to be treated specially.</param>
public ushort Execute(Microinstruction i, ushort d, bool carryIn, bool loadMAR)
{
//
// Save R[a] for the A-bypass case.
// (If the ALU op ends up modifying R[a] in A-Bypass mode (because a == b)
// it will happen much later than A-bypass and we want
// Y to get the original value of R[a], not the later value.)
//
// Select source data
int r, s;
switch(i.aS)
{
case AluSourcePair.AQ:
r = _r[i.rA];
s = _q;
break;
case AluSourcePair.AB:
r = _r[i.rA];
s = _r[i.rB];
break;
case AluSourcePair.ZQ:
r = 0;
s = _q;
break;
case AluSourcePair.ZB:
r = 0;
s = _r[i.rB];
break;
case AluSourcePair.ZA:
r = 0;
s = _r[i.rA];
break;
case AluSourcePair.DA:
r = d;
s = _r[i.rA];
break;
case AluSourcePair.DQ:
r = d;
s = _q;
break;
case AluSourcePair.D0:
r = d;
s = 0;
break;
default:
throw new InvalidOperationException(
String.Format("Unhandled source pair {0}", i.aS));
}
//
// Do ALU op
//
int f;
int cIn = (carryIn ? 1 : 0);
switch (i.aF)
{
case AluFunction.RplusS:
{
f = r + s + cIn;
CarryOut = (f > 0xffff);
NibCarry = (r & 0xf) + (s & 0xf) + cIn > 0xf;
PgCarry = (r & 0xff) + (s & 0xff) + cIn > 0xff;
int cn = (r & 0xfff) + (s & 0xfff) + cIn > 0xfff ? 1 : 0;
Overflow = _overflowTable[r >> 12, s >> 12, cn];
}
break;
case AluFunction.SminusR:
{
f = s + (~r & 0xffff) + cIn;
CarryOut = (f > 0xffff);
NibCarry = ((~r & 0xf) + (s & 0xf) + cIn > 0xf);
PgCarry = ((~r & 0xff) + (s & 0xff) + cIn > 0xff);
int cn = (~r & 0xfff) + (s & 0xfff) + cIn > 0xfff ? 1 : 0;
Overflow = _overflowTable[(~r & 0xffff) >> 12, s >> 12, cn];
}
break;
case AluFunction.RminusS:
{
f = r + (~s & 0xffff) + cIn;
CarryOut = (f > 0xffff);
NibCarry = ((r & 0xf) + (~s & 0xf) + cIn > 0xf);
PgCarry = ((r & 0xff) + (~s & 0xff) + cIn > 0xff);
int cn = (r & 0xfff) + (~s & 0xfff) + cIn > 0xfff ? 1 : 0;
Overflow = _overflowTable[r >> 12, (~s & 0xffff) >> 12, cn];
}
break;
case AluFunction.RorS:
f = r | s;
// A few microinstructions do an MAR<- with RorS and expect PgCarry to be set appropriately.
NibCarry = _carryTableOr[r & 0xf, s & 0xf, cIn];
PgCarry = _carryTableOr[(r >> 4) & 0xf, (s >> 4) & 0xf, NibCarry ? 1 : 0];
CarryOut = false;
Overflow = false;
break;
case AluFunction.RandS:
f = r & s;
NibCarry = false;
PgCarry = false;
CarryOut = false;
Overflow = false;
break;
case AluFunction.notRandS:
f = (~r) & s;
NibCarry = false;
PgCarry = false;
CarryOut = false;
Overflow = false;
break;
case AluFunction.RxorS:
f = r ^ s;
NibCarry = false;
PgCarry = false;
CarryOut = false;
Overflow = false;
break;
case AluFunction.notRxorS:
f = (~r) ^ s;
NibCarry = false;
PgCarry = false;
CarryOut = false;
Overflow = false;
break;
default:
throw new InvalidOperationException(
String.Format("Unhandled function {0}", i.aF));
}
// Clip F to 16 bits
f = f & 0xffff;
if (loadMAR)
{
//
// If the ALU is being run during a MAR<- operation, the top 8 bits of the ALU are
// computed using an operator specified by aF | 3, with the source set to 0,B.
// The CarryOut and Overflow flags are clear (since they are not affected by the
// OR/notXOR operation), and the carry from the least-significant byte of the ALU does not
// carry over to the most-significant byte.
//
// See page 25 of the microcode ref for details.
//
// We implement this here by overwriting the upper byte of F with the upper bits of rB
// (or its complement). Interlisp microcode expects Overflow and Carry to be set appropriately.
//
switch ((AluFunction)((int)i.aF | 0x3))
{
case AluFunction.RorS:
{
f = (f & 0xff) | (_r[i.rB] & 0xff00);
bool midCarry = _carryTableOr[(r >> 8) & 0xf, (s >> 8) & 0xf, PgCarry ? 1 : 0];
Overflow = CarryOut = _carryTableOr[(r >> 12) & 0xf, (s >> 12) & 0xf, midCarry ? 1 : 0];
}
break;
case AluFunction.notRxorS:
{
f = (f & 0xff) | ((~_r[i.rB]) & 0xff00);
bool midCarry = _carryTableNotXor[(r >> 8) & 0xf, (s >> 8) & 0xf, PgCarry ? 1 : 0];
CarryOut = _carryTableNotXor[(r >> 12) & 0xf, (s >> 12) & 0xf, midCarry ? 1 : 0];
Overflow = _overflowNotXor[(r >> 12) & 0xf, (s >> 12) & 0xf, midCarry ? 1 : 0];
}
break;
}
}
Zero = (f == 0);
Neg = ((f & 0x8000) != 0);
//
// Write outputs, do shifts and cycles as appropriate before writing back.
// (Shifts and cycles do not affect the Y output, only the register being written back to.)
//
switch (i.AluDestination)
{
case 0:
_q = (ushort)f;
Y = (ushort)f;
break;
case 1:
Y = (ushort)f;
break;
case 2:
Y = _r[i.rA];
_r[i.rB] = (ushort)f;
break;
case 3:
_r[i.rB] = (ushort)f;
Y = (ushort)f;
break;
case 4:
Y = (ushort)f;
if (i.Cycle)
{
// double-word right shift
// MSB of Q gets inverted LSB of F.
_q = (ushort)((_q >> 1) | ((~f & 0x1) << 15));
// MSB of F gets Carry in.
f = (ushort)((f >> 1) | (carryIn ? 0x8000 : 0x0));
}
else
{
// double-word arithmetic right shift.
// MSB of Q gets inverted LSB of F.
_q = (ushort)((_q >> 1) | ((~f & 0x1) << 15));
// MSB of F gets Carry out.
f = (ushort)((f >> 1) | (CarryOut ? 0x8000 : 0x0));
}
_r[i.rB] = (ushort)f;
break;
case 5:
Y = (ushort)f;
if (i.Cycle)
{
// F: single-word right rotate:
f = (ushort)((f >> 1) | ((f & 0x1) << 15));
}
else
{
// F: single-word right shift w/carryIn to MSB:
f = (ushort)((f >> 1) | (carryIn ? 0x8000 : 0x0));
}
_r[i.rB] = (ushort)f;
break;
case 6:
Y = (ushort)f;
// double-word left shift (apparently identical for cycle and shift)
// LSB of F gets MSB of Q, not inverted.
f = (ushort)((f << 1) | ((_q & 0x8000) >> 15));
// LSB of Q gets Cin, inverted
_q = (ushort)((_q << 1) | (1 - cIn));
_r[i.rB] = (ushort)f;
break;
case 7:
Y = (ushort)f;
if (i.Cycle)
{
// F: single-word left rotate:
f = (ushort)((f << 1) | ((f & 0x8000) >> 15));
}
else
{
// F: single-word left shift w/carryIn to LSB:
f = (ushort)((f << 1) | cIn);
}
_r[i.rB] = (ushort)f;
break;
default:
throw new InvalidOperationException(
String.Format("Unhandled destination {0}", i.aF));
}
return Y;
}
/// <summary>
/// Executes the ALU operation specified by the given microinstruction with all condition flags
/// calculated, even for logical operations. This is significantly slower than Execute().
/// </summary>
/// <param name="i">The microinstruction</param>
/// <param name="d">The ALU D input</param>
/// <param name="carryIn">The ALU Carry in</param>
/// <param name="loadMAR">Whether this operation is taking place during an MAR<- operation,
/// in which case the top half of the ALU needs to be treated specially.</param>
public ushort ExecuteAccurate(Microinstruction i, ushort d, bool carryIn, bool loadMAR)
{
// Select source data
int r, s;
switch (i.aS)
{
case AluSourcePair.AQ:
r = _r[i.rA];
s = _q;
break;
case AluSourcePair.AB:
r = _r[i.rA];
s = _r[i.rB];
break;
case AluSourcePair.ZQ:
r = 0;
s = _q;
break;
case AluSourcePair.ZB:
r = 0;
s = _r[i.rB];
break;
case AluSourcePair.ZA:
r = 0;
s = _r[i.rA];
break;
case AluSourcePair.DA:
r = d;
s = _r[i.rA];
break;
case AluSourcePair.DQ:
r = d;
s = _q;
break;
case AluSourcePair.D0:
r = d;
s = 0;
break;
default:
throw new InvalidOperationException(
String.Format("Unhandled source pair {0}", i.aS));
}
//
// Do ALU op
//
int f;
int cIn = (carryIn ? 1 : 0);
switch (i.aF)
{
case AluFunction.RplusS:
{
f = r + s + cIn;
NibCarry = _carryTableArithmetic[r & 0xf, s & 0xf, cIn];
PgCarry = _carryTableArithmetic[(r >> 4) & 0xf, (s >> 4) & 0xf, NibCarry ? 1 : 0];
bool midCarry = _carryTableArithmetic[(r >> 8) & 0xf, (s >> 8) & 0xf, PgCarry ? 1 : 0];
CarryOut = _carryTableArithmetic[(r >> 12) & 0xf, (s >> 12) & 0xf, midCarry ? 1 : 0];
Overflow = _overflowTable[r >> 12, s >> 12, midCarry ? 1 : 0];
}
break;
case AluFunction.SminusR:
{
f = s + (~r & 0xffff) + cIn;
NibCarry = _carryTableArithmetic[~r & 0xf, s & 0xf, cIn];
PgCarry = _carryTableArithmetic[(~r >> 4) & 0xf, (s >> 4) & 0xf, NibCarry ? 1 : 0];
bool midCarry = _carryTableArithmetic[(~r >> 8) & 0xf, (s >> 8) & 0xf, PgCarry ? 1 : 0];
CarryOut = _carryTableArithmetic[(~r >> 12) & 0xf, (s >> 12) & 0xf, midCarry ? 1 : 0];
Overflow = _overflowTable[(~r & 0xffff) >> 12, s >> 12, midCarry ? 1 : 0];
}
break;
case AluFunction.RminusS:
{
f = r + (~s & 0xffff) + cIn;
NibCarry = _carryTableArithmetic[r & 0xf, ~s & 0xf, cIn];
PgCarry = _carryTableArithmetic[(r >> 4) & 0xf, (~s >> 4) & 0xf, NibCarry ? 1 : 0];
bool midCarry = _carryTableArithmetic[(r >> 8) & 0xf, (~s >> 8) & 0xf, PgCarry ? 1 : 0];
CarryOut = _carryTableArithmetic[(r >> 12) & 0xf, (~s >> 12) & 0xf, midCarry ? 1 : 0];
Overflow = _overflowTable[r >> 12, (~s & 0xffff) >> 12, midCarry ? 1 : 0];
}
break;
case AluFunction.RorS:
{
f = r | s;
NibCarry = _carryTableOr[r & 0xf, s & 0xf, cIn];
PgCarry = _carryTableOr[(r >> 4) & 0xf, (s >> 4) & 0xf, NibCarry ? 1 : 0];
bool midCarry = _carryTableOr[(r >> 8) & 0xf, (s >> 8) & 0xf, PgCarry ? 1 : 0];
Overflow = CarryOut = _carryTableOr[(r >> 12) & 0xf, (s >> 12) & 0xf, midCarry ? 1 : 0];
}
break;
case AluFunction.RandS:
{
f = r & s;
NibCarry = _carryTableAnd[r & 0xf, s & 0xf, cIn];
PgCarry = _carryTableAnd[(r >> 4) & 0xf, (s >> 4) & 0xf, NibCarry ? 1 : 0];
bool midCarry = _carryTableAnd[(r >> 8) & 0xf, (s >> 8) & 0xf, PgCarry ? 1 : 0];
Overflow = CarryOut = _carryTableAnd[(r >> 12) & 0xf, (s >> 12) & 0xf, midCarry ? 1 : 0];
}
break;
case AluFunction.notRandS:
{
f = (~r) & s;
NibCarry = _carryTableAnd[~r & 0xf, s & 0xf, cIn];
PgCarry = _carryTableAnd[(~r >> 4) & 0xf, (s >> 4) & 0xf, NibCarry ? 1 : 0];
bool midCarry = _carryTableAnd[(~r >> 8) & 0xf, (s >> 8) & 0xf, PgCarry ? 1 : 0];
Overflow = CarryOut = _carryTableAnd[(~r >> 12) & 0xf, (s >> 12) & 0xf, midCarry ? 1 : 0];
}
break;
case AluFunction.RxorS:
{
f = r ^ s;
NibCarry = _carryTableNotXor[~r & 0xf, s & 0xf, cIn];
PgCarry = _carryTableNotXor[(~r >> 4) & 0xf, (s >> 4) & 0xf, NibCarry ? 1 : 0];
bool midCarry = _carryTableNotXor[(~r >> 8) & 0xf, (s >> 8) & 0xf, PgCarry ? 1 : 0];
CarryOut = _carryTableNotXor[(~r >> 12) & 0xf, (s >> 12) & 0xf, midCarry ? 1 : 0];
Overflow = _overflowNotXor[(~r >> 12) & 0xf, (s >> 12) & 0xf, midCarry ? 1 : 0];
}
break;
case AluFunction.notRxorS:
{
f = (~r) ^ s;
NibCarry = _carryTableNotXor[r & 0xf, s & 0xf, cIn];
PgCarry = _carryTableNotXor[(r >> 4) & 0xf, (s >> 4) & 0xf, NibCarry ? 1 : 0];
bool midCarry = _carryTableNotXor[(r >> 8) & 0xf, (s >> 8) & 0xf, PgCarry ? 1 : 0];
CarryOut = _carryTableNotXor[(r >> 12) & 0xf, (s >> 12) & 0xf, midCarry ? 1 : 0];
Overflow = _overflowNotXor[(r >> 12) & 0xf, (s >> 12) & 0xf, midCarry ? 1 : 0];
}
break;
default:
throw new InvalidOperationException(
String.Format("Unhandled function {0}", i.aF));
}
// Clip F to 16 bits
f = f & 0xffff;
if (loadMAR)
{
//
// If the ALU is being run during a MAR<- operation, the top 8 bits of the ALU are
// computed using an operator specified by aF | 3, with the source set to 0,B.
// The CarryOut and Overflow flags are clear (since they are not affected by the
// OR/notXOR operation), and the carry from the least-significant byte of the ALU does not
// carry over to the most-significant byte.
//
// See page 25 of the microcode ref for details.
//
// We implement this here by simply overwriting the upper byte of F with the upper bits of rB
// (or its complement), and clearing CarryOut and Overflow.
//
switch ((AluFunction)((int)i.aF | 0x3))
{
case AluFunction.RorS:
{
f = (f & 0xff) | (_r[i.rB] & 0xff00);
bool midCarry = _carryTableOr[(r >> 8) & 0xf, (s >> 8) & 0xf, PgCarry ? 1 : 0];
Overflow = CarryOut = _carryTableOr[(r >> 12) & 0xf, (s >> 12) & 0xf, midCarry ? 1 : 0];
}
break;
case AluFunction.notRxorS:
{
f = (f & 0xff) | ((~_r[i.rB]) & 0xff00);
bool midCarry = _carryTableNotXor[(r >> 8) & 0xf, (s >> 8) & 0xf, PgCarry ? 1 : 0];
CarryOut = _carryTableNotXor[(r >> 12) & 0xf, (s >> 12) & 0xf, midCarry ? 1 : 0];
Overflow = _overflowNotXor[(r >> 12) & 0xf, (s >> 12) & 0xf, midCarry ? 1 : 0];
}
break;
}
}
Zero = (f == 0);
Neg = ((f & 0x8000) != 0);
//
// Write outputs, do shifts and cycles as appropriate before writing back.
// (Shifts and cycles do not affect the Y output, only the register being written back to.)
//
switch (i.AluDestination)
{
case 0:
_q = (ushort)f;
Y = (ushort)f;
break;
case 1:
Y = (ushort)f;
break;
case 2:
Y = _r[i.rA];
_r[i.rB] = (ushort)f;
break;
case 3:
_r[i.rB] = (ushort)f;
Y = (ushort)f;
break;
case 4:
Y = (ushort)f;
if (i.Cycle)
{
// double-word right shift
// MSB of Q gets inverted LSB of F.
_q = (ushort)((_q >> 1) | ((~f & 0x1) << 15));
// MSB of F gets Carry in.
f = (ushort)((f >> 1) | (carryIn ? 0x8000 : 0x0));
}
else
{
// double-word arithmetic right shift.
// MSB of Q gets inverted LSB of F.
_q = (ushort)((_q >> 1) | ((~f & 0x1) << 15));
// MSB of F gets Carry out.
f = (ushort)((f >> 1) | (CarryOut ? 0x8000 : 0x0));
}
_r[i.rB] = (ushort)f;
break;
case 5:
Y = (ushort)f;
if (i.Cycle)
{
// F: single-word right rotate:
f = (ushort)((f >> 1) | ((f & 0x1) << 15));
}
else
{
// F: single-word right shift w/carryIn to MSB:
f = (ushort)((f >> 1) | (carryIn ? 0x8000 : 0x0));
}
_r[i.rB] = (ushort)f;
break;
case 6:
Y = (ushort)f;
// double-word left shift (apparently identical for cycle and shift)
// LSB of F gets MSB of Q, not inverted.
f = (ushort)((f << 1) | ((_q & 0x8000) >> 15));
// LSB of Q gets Cin, inverted
_q = (ushort)((_q << 1) | (1 - cIn));
_r[i.rB] = (ushort)f;
break;
case 7:
Y = (ushort)f;
if (i.Cycle)
{
// F: single-word left rotate:
f = (ushort)((f << 1) | ((f & 0x8000) >> 15));
}
else
{
// F: single-word left shift w/carryIn to LSB:
f = (ushort)((f << 1) | cIn);
}
_r[i.rB] = (ushort)f;
break;
default:
throw new InvalidOperationException(
String.Format("Unhandled destination {0}", i.aF));
}
return Y;
}
/// <summary>
/// TODO: replace with a nice table lookup.
/// </summary>
/// <param name="r"></param>
/// <param name="s"></param>
/// <param name="cIn"></param>
/// <returns></returns>
private static bool CalcOverflow(int r, int s, int cIn)
{
int p0 = (r | s) & 0x1;
int p1 = ((r | s) & 0x2) >> 1;
int p2 = ((r | s) & 0x4) >> 2;
int p3 = ((r | s) & 0x8) >> 3;
int g0 = (r & s & 0x1);
int g1 = (r & s & 0x2) >> 1;
int g2 = (r & s & 0x4) >> 2;
int g3 = (r & s & 0x8) >> 3;
int c4 = g3 | (p3 & g2) | (p3 & p2 & g1) | (p3 & p2 & p1 & g0) | (p3 & p2 & p1 & p0 & cIn);
int c3 = g2 | (p2 & g1) | (p2 & p1 & g0) | (p2 & p1 & p0 & cIn);
return (c3 ^ c4) != 0;
}
private static bool CalcCarryArithmetic(int r, int s, int cIn)
{
int p0 = (r | s) & 0x1;
int p1 = ((r | s) & 0x2) >> 1;
int p2 = ((r | s) & 0x4) >> 2;
int p3 = ((r | s) & 0x8) >> 3;
int g0 = (r & s & 0x1);
int g1 = (r & s & 0x2) >> 1;
int g2 = (r & s & 0x4) >> 2;
int g3 = (r & s & 0x8) >> 3;
int c4 = g3 | (p3 & g2) | (p3 & p2 & g1) | (p3 & p2 & p1 & g0) | (p3 & p2 & p1 & p0 & cIn);
return c4 != 0;
}
private static bool CalcCarryOr(int r, int s, int cIn)
{
int p0 = (r | s) & 0x1;
int p1 = ((r | s) & 0x2) >> 1;
int p2 = ((r | s) & 0x4) >> 2;
int p3 = ((r | s) & 0x8) >> 3;
int c4 = (~(p3 & p2 & p1 & p0) & 0x1) | cIn;
return c4 != 0;
}
private static bool CalcCarryAnd(int r, int s, int cIn)
{
int g0 = (r & s & 0x1);
int g1 = (r & s & 0x2) >> 1;
int g2 = (r & s & 0x4) >> 2;
int g3 = (r & s & 0x8) >> 3;
int c4 = g3 | g2 | g1 | g0 | cIn;
return c4 != 0;
}
private static bool CalcCarryNotXor(int r, int s, int cIn)
{
int p0 = (r | s) & 0x1;
int p1 = ((r | s) & 0x2) >> 1;
int p2 = ((r | s) & 0x4) >> 2;
int p3 = ((r | s) & 0x8) >> 3;
int g0 = (r & s & 0x1);
int g1 = (r & s & 0x2) >> 1;
int g2 = (r & s & 0x4) >> 2;
int g3 = (r & s & 0x8) >> 3;
int c4 = ~(g3 | (p3 & g2) | (p3 & p2 & g1) | (p3 & p2 & p1 & p0 & (g0 | ~cIn))) & 0x1;
return c4 != 0;
}
private static bool CalcOverflowNotXor(int r, int s, int cIn)
{
int p0 = (r | s) & 0x1;
int p1 = ((r | s) & 0x2) >> 1;
int p2 = ((r | s) & 0x4) >> 2;
int p3 = ((r | s) & 0x8) >> 3;
int g0 = (r & s & 0x1);
int g1 = (r & s & 0x2) >> 1;
int g2 = (r & s & 0x4) >> 2;
int g3 = (r & s & 0x8) >> 3;
int ovr = ((~p2 | (~g2 & ~p1) | (~g2 & ~g1 & ~p0) | (~g2 & ~g1 & ~g0 & cIn)) ^
(~p3 | (~g3 & ~p2) | (~g3 & ~g2 & ~p1) | (~g3 & ~g2 & ~g1 & ~p0) | (~g3 & ~g2 & ~g1 & ~g0 & cIn))) & 0x1;
return ovr != 0;
}
private static void BuildTables()
{
for (int r = 0; r < 16; r++)
{
for (int s = 0; s < 16; s++)
{
for (int c = 0; c < 2; c++)
{
_overflowTable[r, s, c] = CalcOverflow(r, s, c);
_carryTableArithmetic[r, s, c] = CalcCarryArithmetic(r, s, c);
_carryTableOr[r, s, c] = CalcCarryOr(r, s, c);
_carryTableAnd[r, s, c] = CalcCarryAnd(r, s, c);
_carryTableNotXor[r, s, c] = CalcCarryNotXor(r, s, c);
_overflowNotXor[r, s, c] = CalcOverflowNotXor(r, s, c);
}
}
}
}
//
// Overflow lookup table for most-significant nibble.
//
private static bool[,,] _overflowTable = new bool[16, 16, 2];
private static bool[,,] _carryTableArithmetic = new bool[16, 16, 2];
private static bool[,,] _carryTableOr = new bool[16, 16, 2];
private static bool[,,] _carryTableAnd = new bool[16, 16, 2];
private static bool[,,] _carryTableNotXor = new bool[16, 16, 2];
private static bool[,,] _overflowNotXor = new bool[16, 16, 2];
//
// Registers
//
private ushort[] _r = new ushort[16];
private ushort _q;
//
// Flags
//
public bool Zero;
public bool Neg;
public bool NibCarry;
public bool PgCarry;
public bool CarryOut;
public bool Overflow;
//
// Output
//
public ushort Y;
}
}

1634
D/CP/CentralProcessor.cs Normal file

File diff suppressed because it is too large Load Diff

712
D/CP/CentralProcessorIO.cs Normal file
View File

@@ -0,0 +1,712 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using D.IOP;
using D.Logging;
using System;
namespace D.CP
{
//
// From SysDefs.asm:
// Bits 0:5 - (IOPWait', SwTAddr', IOPattn, CPDmaMode, CPDmaIn)
// (in the usual Xerox reverse order)
//
[Flags]
public enum CPControlFlags
{
IOPWait_ = 0x80,
SwTAddr_ = 0x40,
IOPattn = 0x20,
CPDmaMode = 0x10,
CPDmaIn = 0x08,
}
//
// From IOP schematics, p 15-17:
//
public enum CPStatusFlags
{
CPAttn = 0x80,
EmuWake = 0x40,
IOPAttn_ = 0x20,
CPDmaMode_ = 0x10,
CPDmaIn_ = 0x08,
CPInIntReq_ = 0x04,
CPOutIntReq_ = 0x2,
CPDmaComplete_ = 0x1,
}
[Flags]
public enum IOPCtlFlags
{
EmuWake = 0x8,
CPAttn = 0x4,
WakeMode0 = 0x2,
WakeMode1 = 0x1,
}
[Flags]
public enum IOPStatusFlags
{
IOPAttn = 0x20,
EmuWake_ = 0x10,
CPAttn_ = 0x8,
WakeMode0_ = 0x4,
WakeMode1_ = 0x2,
IOPReq = 0x1,
}
/// <summary>
/// The Dandelion Central Processor, I/O implmentation.
/// This partial class implements the CP<->IOP communication channel,
/// and IOP microcode loading logic.
/// </summary>
public partial class CentralProcessor : IIOPDevice, IDMAInterface
{
//
// IOP port info
//
public int[] ReadPorts
{
get { return _readPorts; }
}
public int[] WritePorts
{
get { return _writePorts; }
}
public void WritePort(int port, byte value)
{
switch ((PortWriteRegister)port)
{
case PortWriteRegister.CPDataOut:
WriteCPInBuffer(value);
break;
case PortWriteRegister.CPControl:
WriteCPCtl(value);
break;
case PortWriteRegister.CPClrDmaComplete:
_cpDmaComplete_ = false;
if (Log.Enabled) Log.Write(LogComponent.IOPDMA, "CP DMA complete flag cleared.");
break;
case PortWriteRegister.CPCSa:
case PortWriteRegister.CPCSb:
case PortWriteRegister.CPCSc:
case PortWriteRegister.CPCSd:
case PortWriteRegister.CPCSe:
case PortWriteRegister.CPCSf: // CS microcode word, MSB ($F8) to LSB ($FD)
WriteIOPMicrocodeWord(port - 0xf8, (byte)~value);
if (port == 0xfd)
{
if (Log.Enabled) Log.Write(LogComponent.CPMicrocodeLoad, "CS word {0:x3} completed: {1:x12} {2}", _tpc[6], _microcode[_tpc[6]], new Microinstruction(_microcode[_tpc[6]]).Disassemble(-1));
}
break;
case PortWriteRegister.TPCHigh: // TPC high : TPCAddr[0:2],,TPCData[0:4]'
_tpcAddr = value >> 5;
_tpcTemp = ((~value & 0x1f) << 7);
if (Log.Enabled) Log.Write(LogComponent.CPTPCLoad, "TPC high written: TPC[{0}] ({1}) is now {2:x3}", _tpcAddr, (TaskType)_tpcAddr, _tpcTemp);
break;
case PortWriteRegister.TPCLow: // TPC low : don't care,,TPCData[5:11]'
_tpc[_tpcAddr] = _tpcTemp | (~value & 0x7f);
if (Log.Enabled) Log.Write(LogComponent.CPTPCLoad, "TPC low written: TPC[{0}] ({1}) is now {2:x3}", _tpcAddr, (TaskType)_tpcAddr, _tpc[_tpcAddr]);
break;
default:
throw new InvalidOperationException(String.Format("Unexpected write to port {0:x2}", port));
}
}
public byte ReadPort(int port)
{
byte value = 0;
switch ((PortReadRegister)port)
{
case PortReadRegister.CPDataIn:
value = ReadCPOutBuffer();
break;
case PortReadRegister.CPStatus:
value = ReadCPStatus();
break;
case PortReadRegister.CPCS0:
case PortReadRegister.CPCS1:
case PortReadRegister.CPCS2:
case PortReadRegister.CPCS3:
case PortReadRegister.CPCS4:
case PortReadRegister.CPCS5: // CS microcode word, MSB ($F8) to LSB ($FD)
value = ReadIOPMicrocodeWord(port - 0xf8);
break;
case PortReadRegister.CPCS6: // TPC high : TC[0:3],,TPCData[0:3]'
value = (byte)~((~_tc[_tpcAddr] << 4) | ((_tpc[_tpcAddr] & 0xf00) >> 8));
break;
case PortReadRegister.CPCS7: // TPC low : TPCData[4:11]'
value = (byte)(~_tpc[_tpcAddr]);
break;
default:
throw new InvalidOperationException(String.Format("Unexpected read from port {0:x2}", port));
}
if (Log.Enabled) Log.Write(LogComponent.CPControl, "CP port {0:x2}({1}) read {2:x2}", port, (PortReadRegister)port, value);
return value;
}
//
// IDMAInterface implementation
//
/// <summary>
/// DMA Request: device request to obtain a DMA cycle from the DMA controller
/// </summary>
public bool DRQ
{
get
{
if (_cpDmaMode) // DMA is enabled
{
if (_cpDmaIn)
{
return _outLatched;
}
else
{
return !_inLatched;
}
}
else
{
return false;
}
}
}
/// <summary>
/// Writes a single byte to the device from the DMA controller
/// </summary>
/// <param name="value"></param>
public void DMAWrite(byte value)
{
WriteCPInBuffer(value);
}
/// <summary>
/// Reads a single byte from the device to the DMA controller
/// </summary>
/// <returns></returns>
public byte DMARead()
{
return ReadCPOutBuffer();
}
public void DMAComplete()
{
_cpDmaComplete_ = true;
if (Log.Enabled) Log.Write(LogComponent.IOPDMA, "CP DMA complete, flag set.");
}
private byte ReadCPOutBuffer()
{
if (!_outLatched)
{
if (Log.Enabled) Log.Write(LogType.Warning, LogComponent.CPControl, "CP data out not latched on IOP read.");
}
//
// Clear the output data latched flag.
//
_outLatched = false;
//
// Clear the CP->IOP interrupt flag (active low): the IOP has read the available data.
//
_cpInIntReq_ = true;
UpdateIOPTaskWakeup();
//
// Return the buffer data
//
return _cpInData;
}
private void WriteCPInBuffer(byte value)
{
if (Log.Enabled) Log.Write(LogComponent.CPControl, "CP data out write ({0:x2})", value);
if (_inLatched)
{
if (Log.Enabled) Log.Write(LogType.Warning, LogComponent.CPControl, "CP data out already latched on IOP write", value);
}
//
// Clear the IOP->CP interrupt (active low): The CP has yet to read the data provided by the IOP.
//
_cpOutIntReq_ = true;
_cpOutData = value;
//
// Let the CP know there's data available.
//
_inLatched = true;
UpdateIOPTaskWakeup();
}
/// <summary>
/// Writes one byte of the microcode word currently pointed to by
/// TPC[6].
/// </summary>
/// <param name="b"></param>
/// <param name="value"></param>
private void WriteIOPMicrocodeWord(int b, byte value)
{
//
// In true Xerox fashion, the 48 bits of the microcode word provided by the IOP
// are not in order from MSB to LSB or anything simple like that. Though most of them are.
// From SysDefs.asm:
//
// "; Write (all CSi are complemented values):
// CSa equ CSBase + 0; CS Byte a: rA[0:3],,rB[0:3]
// CSb equ CSBase + 1; CS Byte b: aS[0:2],,aF[0:2],,aD[0:1]
// CSc equ CSBase + 2; CS Byte c: EP,,CIN,,EnSU,,mem,,fS[0:3]
// CSd equ CSBase + 3; CS Byte d: fY[0:3], INIA[0:3]
// CSe equ CSBase + 4; CS Byte e: fX[0:3], INIA[4:7]
// CSf equ CSBase + 5; CS Byte f: fZ[0:3], INIA[8:11]"
//
// "value" is expected to already be complemented on call to WriteIOPMicrocodeWord.
//
// TPC register 6 is always used for IOP microcode writes.
ulong word = _microcode[_tpc[6]];
switch (b)
{
case 0:
word = (word & 0x00ffffffffff) | ((ulong)value << 40);
break;
case 1:
word = (word & 0xff00ffffffff) | ((ulong)value << 32);
break;
case 2:
word = (word & 0xffff00ffffff) | ((ulong)value << 24);
break;
case 3:
{
// FY[0:3], INIA[0:3]
ulong fy = ((ulong)value & 0xf0) >> 4;
ulong inia = ((ulong)value & 0xf);
word = (word & 0xfffffff0f0ff) | (fy << 16) | (inia << 8);
}
break;
case 4:
{
// FX[0:3], INIA[4:7]
ulong fx = ((ulong)value & 0xf0) >> 4;
ulong inia = ((ulong)value & 0xf);
word = (word & 0xffffff0fff0f) | (fx << 20) | (inia << 4);
}
break;
case 5:
{
// FZ[0:3], INIA[8:11]
ulong fz = ((ulong)value & 0xf0) >> 4;
ulong inia = ((ulong)value & 0xf);
word = (word & 0xffffffff0ff0) | (fz << 12) | inia;
}
break;
default:
throw new InvalidOperationException("Invalid byte number for microcode word.");
}
_microcode[_tpc[6]] = word;
_microcodeCache[_tpc[6]] = new Microinstruction(word);
}
/// <summary>
/// Reads one byte of the microcode word currently pointed to by
/// TPC[6].
/// </summary>
/// <param name="b"></param>
/// <param name="value"></param>
private byte ReadIOPMicrocodeWord(int b)
{
byte value = 0;
// TPC register 6 is always used for IOP microcode reads.
ulong word = _microcode[_tpc[6]];
switch (b)
{
case 0:
value = (byte)(_microcode[_tpc[6]] >> 40);
break;
case 1:
value = (byte)(_microcode[_tpc[6]] >> 32);
break;
case 2:
value = (byte)(_microcode[_tpc[6]] >> 24);
break;
case 3:
{
// FY[0:3], INIA[0:3]
value = (byte)(((_microcode[_tpc[6]] >> 12) & 0xf0) | ((_microcode[_tpc[6]] >> 8) & 0xf));
}
break;
case 4:
{
// FX[0:3], INIA[4:7]
value = (byte)(((_microcode[_tpc[6]] >> 16) & 0xf0) | ((_microcode[_tpc[6]] >> 4) & 0xf));
}
break;
case 5:
{
// FZ[0:3], INIA[8:11]
value = (byte)(((_microcode[_tpc[6]] >> 8) & 0xf0) | (_microcode[_tpc[6]] & 0xf));
}
break;
default:
throw new InvalidOperationException("Invalid byte number for microcode word.");
}
return value;
}
private void WriteCPCtl(byte value)
{
if (Log.Enabled) Log.Write(LogComponent.CPControl, "CP control write {0} ({1:x2})", (CPControlFlags)value, value);
bool oldIopWait = _iopWait_;
_iopWait_ = (value & (int)CPControlFlags.IOPWait_) == 0; // inverted sense
_swTAddr = (value & (int)CPControlFlags.SwTAddr_) == 0; // ditto
_iopAttn = (value & (int)CPControlFlags.IOPattn) != 0;
_cpDmaMode = (value & (int)CPControlFlags.CPDmaMode) != 0;
_cpDmaIn = (value & (int)CPControlFlags.CPDmaIn) != 0;
if (oldIopWait != _iopWait_)
{
//
// Raising IOPWait causes a Kernel wakeup to be requested so that the Kernel task
// will run when the CP is allowed to run again.
//
for(int i=0;i<7;i++)
{
SleepTask((TaskType)i);
}
WakeTask(TaskType.Kernel);
_currentTask = TaskType.Kernel;
//
// Reset any error status
//
_emulatorErrorTrap = false;
_emulatorErrorTrapClickCount = 0;
}
}
private byte ReadCPStatus()
{
return (byte)
((_cpDmaComplete_ ? CPStatusFlags.CPDmaComplete_ : 0) |
(!_cpOutIntReq_ ? CPStatusFlags.CPOutIntReq_ : 0) |
(!_cpInIntReq_ ? CPStatusFlags.CPInIntReq_ : 0) |
(!_cpDmaIn ? CPStatusFlags.CPDmaIn_ : 0) |
(!_cpDmaMode ? CPStatusFlags.CPDmaMode_ : 0) |
(_emuWake ? CPStatusFlags.EmuWake : 0) |
(!_cpAttn ? CPStatusFlags.CPAttn : 0));
}
private void WriteIOPCtl(byte value)
{
if (Log.Enabled) Log.Write(LogComponent.CPControl, "IOPCtl<- {0} ({1:x2})", (IOPCtlFlags)value, value);
_wakeMode1 = (value & (int)IOPCtlFlags.WakeMode1) != 0;
_wakeMode0 = (value & (int)IOPCtlFlags.WakeMode0) != 0;
_cpAttn = (value & (int)IOPCtlFlags.CPAttn) != 0;
_emuWake = (value & (int)IOPCtlFlags.EmuWake) != 0;
_wakeMode = (IOPTaskWakeMode)((_wakeMode0 ? 0x2 : 0x0) | (_wakeMode1 ? 0x1 : 0x0));
if (Log.Enabled) Log.Write(LogComponent.CPControl, "IOP Wake mode is {0}", _wakeMode);
// See if the wake status of the IOP task needs to change.
UpdateIOPTaskWakeup();
}
private byte ReadIOPStatus()
{
return (byte)
((_iopReq ? IOPStatusFlags.IOPReq : 0) |
(!_wakeMode1 ? IOPStatusFlags.WakeMode1_ : 0) |
(!_wakeMode0 ? IOPStatusFlags.WakeMode0_ : 0) |
(!_cpAttn ? IOPStatusFlags.CPAttn_ : 0) |
(!_emuWake ? IOPStatusFlags.EmuWake_ : 0) |
(_iopAttn ? IOPStatusFlags.IOPAttn : 0));
}
private byte ReadIOPData()
{
if (!_inLatched)
{
if (Log.Enabled) Log.Write(LogType.Warning, LogComponent.CPControl, "CP data not latched on <-IOPData.");
}
//
// Clear the IOP request flag
//
_inLatched = false;
//
// Raise the IOP->CP flag (active low): the CP has read the available data.
//
_cpOutIntReq_ = false;
UpdateIOPTaskWakeup();
return _cpOutData;
}
private void WriteIOPData(byte value)
{
if (_outLatched)
{
if (Log.Enabled) Log.Write(LogType.Warning, LogComponent.CPControl, "CP data already latched on IOPOData<-");
}
//
// Latch the output data
//
_outLatched = true;
//
// Raise the CP->IOP interrupt flag (active low): there is data available for the IOP to read.
//
_cpInIntReq_ = false;
//
// Fill the CP->IOP buffer
//
_cpInData = value;
UpdateIOPTaskWakeup();
}
private void UpdateIOPTaskWakeup()
{
//
// Wake or sleep the IOP task as appropriate based on the current wake mode
// and the status of the I/O channel.
//
switch (_wakeMode)
{
case IOPTaskWakeMode.Always:
//
// Like the label says, we wake up the IOP task unconditionally.
//
_iopReq = true;
WakeTask(TaskType.IOP);
break;
case IOPTaskWakeMode.Input:
//
// If there's input waiting, wake the IOP task.
//
if (_inLatched)
{
_iopReq = true;
WakeTask(TaskType.IOP);
}
else
{
_iopReq = false;
SleepTask(TaskType.IOP);
}
break;
case IOPTaskWakeMode.Output:
//
// If the output buffer is currently empty, wake the IOP task.
//
if (!_outLatched)
{
_iopReq = true;
WakeTask(TaskType.IOP);
}
else
{
_iopReq = false;
SleepTask(TaskType.IOP);
}
break;
case IOPTaskWakeMode.Disabled:
_iopReq = false;
SleepTask(TaskType.IOP);
break;
}
}
private enum PortReadRegister
{
CPDataIn = 0xeb,
CPStatus = 0xec,
CPCS0 = 0xf8,
CPCS1 = 0xf9,
CPCS2 = 0xfa,
CPCS3 = 0xfb,
CPCS4 = 0xfc,
CPCS5 = 0xfd,
CPCS6 = 0xfe,
CPCS7 = 0xff,
}
private enum PortWriteRegister
{
CPDataOut = 0xeb,
CPControl = 0xec,
CPClrDmaComplete = 0xee,
CPCSa = 0xf8,
CPCSb = 0xf9,
CPCSc = 0xfa,
CPCSd = 0xfb,
CPCSe = 0xfc,
CPCSf = 0xfd,
TPCHigh = 0xfe,
TPCLow = 0xff,
}
// From the IOP schematic:
// - 00 = Disabled(no wakeups)
// - 01 = Input(wakeup when Input from IOP is available)
// - 10 = Output(wakeup when IOP is ready for data from CP)
// - 11 = Always wake up
private enum IOPTaskWakeMode
{
Disabled = 0,
Input,
Output,
Always,
}
//
// IOP port data
//
private readonly int[] _readPorts = new int[]
{
(int)PortReadRegister.CPDataIn, // CP data in
(int)PortReadRegister.CPStatus, // CP port status
(int)PortReadRegister.CPCS0, // control store word, MSB
(int)PortReadRegister.CPCS1,
(int)PortReadRegister.CPCS2,
(int)PortReadRegister.CPCS3,
(int)PortReadRegister.CPCS4,
(int)PortReadRegister.CPCS5, // control store word, LSB
(int)PortReadRegister.CPCS6, // TPC high
(int)PortReadRegister.CPCS7, // low
};
private readonly int[] _writePorts = new int[]
{
(int)PortWriteRegister.CPDataOut,
(int)PortWriteRegister.CPControl,
(int)PortWriteRegister.CPClrDmaComplete,
(int)PortWriteRegister.CPCSa, // control store word, MSB
(int)PortWriteRegister.CPCSb,
(int)PortWriteRegister.CPCSc,
(int)PortWriteRegister.CPCSd,
(int)PortWriteRegister.CPCSe,
(int)PortWriteRegister.CPCSf, // control store word, LSB
(int)PortWriteRegister.TPCHigh,
(int)PortWriteRegister.TPCLow,
};
//
// Control data, IOP
//
private bool _cpDmaComplete_;
private bool _iopWait_; // Waiting for IOP to wake us
private bool _swTAddr;
private bool _iopAttn;
private bool _cpDmaMode;
private bool _cpDmaIn;
//
// Control data, CP
//
private bool _wakeMode1;
private bool _wakeMode0;
private bool _cpAttn;
private bool _emuWake;
private IOPTaskWakeMode _wakeMode;
//
// Status data
//
private bool _cpOutIntReq_;
private bool _cpInIntReq_;
private bool _outLatched; // Data from CP->IOP latched
private bool _inLatched; // Data from IOP->CP latched
private bool _iopReq;
//
// CP<->IOP data buffers
//
private byte _cpOutData; // OUT from IOP (CP reads)
private byte _cpInData; // IN from CP (IOP reads)
// Used as TPC address when IOP is writing control store or modifying TPC values.
private int _tpcAddr;
// Temporary used when loading TPC values; stores high bits of new TPC address.
private int _tpcTemp;
}
}

641
D/CP/MacroInstruction.cs Normal file
View File

@@ -0,0 +1,641 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace D.CP
{
public enum MacroType
{
Mesa,
Lisp,
}
public enum MacroOperand
{
None,
Byte,
SignedByte,
Pair,
TwoByte,
Word,
ThreeByte,
}
/// <summary>
/// Provides facilities for interpreting Mesa bytecodes
/// </summary>
public struct MacroInstruction
{
private MacroInstruction(byte opcode, string mnemonic, MacroOperand operand)
{
_mnemonic = mnemonic;
_operand = operand;
}
public string Mnemonic
{
get { return _mnemonic; }
}
public MacroOperand Operand
{
get { return _operand; }
}
public static MacroInstruction GetInstruction(MacroType type, byte opcode)
{
MacroInstruction inst = Invalid;
switch(type)
{
case MacroType.Mesa:
inst = _mesaInstructionTable[opcode];
break;
case MacroType.Lisp:
inst = _lispInstructionTable[opcode];
break;
}
return inst;
}
private string _mnemonic;
private MacroOperand _operand;
private static MacroInstruction[] _mesaInstructionTable =
{
Invalid,
new MacroInstruction(0x01, "LL0", MacroOperand.None),
new MacroInstruction(0x02, "LL1", MacroOperand.None),
new MacroInstruction(0x03, "LL2", MacroOperand.None),
new MacroInstruction(0x04, "LL3", MacroOperand.None),
new MacroInstruction(0x05, "LL4", MacroOperand.None),
new MacroInstruction(0x06, "LL5", MacroOperand.None),
new MacroInstruction(0x07, "LL6", MacroOperand.None),
new MacroInstruction(0x08, "LL7", MacroOperand.None),
new MacroInstruction(0x09, "LL8", MacroOperand.None),
new MacroInstruction(0x0a, "LL9", MacroOperand.None),
new MacroInstruction(0x0b, "LL10", MacroOperand.None),
new MacroInstruction(0x0c, "LL11", MacroOperand.None),
new MacroInstruction(0x0d, "LLB", MacroOperand.Byte),
new MacroInstruction(0x0e, "LLD0", MacroOperand.None),
new MacroInstruction(0x0f, "LLD1", MacroOperand.None),
new MacroInstruction(0x10, "LLD2", MacroOperand.None),
new MacroInstruction(0x11, "LLD3", MacroOperand.None),
new MacroInstruction(0x12, "LLD4", MacroOperand.None),
new MacroInstruction(0x13, "LLD5", MacroOperand.None),
new MacroInstruction(0x14, "LLD6", MacroOperand.None),
new MacroInstruction(0x15, "LLD7", MacroOperand.None),
new MacroInstruction(0x16, "LLD8", MacroOperand.None),
new MacroInstruction(0x17, "LLD10", MacroOperand.None),
new MacroInstruction(0x18, "LLDB", MacroOperand.Byte),
new MacroInstruction(0x19, "SL0", MacroOperand.None),
new MacroInstruction(0x1a, "SL1", MacroOperand.None),
new MacroInstruction(0x1b, "SL2", MacroOperand.None),
new MacroInstruction(0x1c, "SL3", MacroOperand.None),
new MacroInstruction(0x1d, "SL4", MacroOperand.None),
new MacroInstruction(0x1e, "SL5", MacroOperand.None),
new MacroInstruction(0x1f, "SL6", MacroOperand.None),
new MacroInstruction(0x20, "SL7", MacroOperand.None),
new MacroInstruction(0x21, "SL8", MacroOperand.None),
new MacroInstruction(0x22, "SL9", MacroOperand.None),
new MacroInstruction(0x23, "SL10", MacroOperand.None),
new MacroInstruction(0x24, "SLB", MacroOperand.Byte),
new MacroInstruction(0x25, "SLD0", MacroOperand.None),
new MacroInstruction(0x26, "SLD1", MacroOperand.None),
new MacroInstruction(0x27, "SLD2", MacroOperand.None),
new MacroInstruction(0x28, "SLD3", MacroOperand.None),
new MacroInstruction(0x29, "SLD4", MacroOperand.None),
new MacroInstruction(0x2a, "SLD5", MacroOperand.None),
new MacroInstruction(0x2b, "SLD6", MacroOperand.None),
new MacroInstruction(0x2c, "SLD8", MacroOperand.None),
new MacroInstruction(0x2d, "PL0", MacroOperand.None),
new MacroInstruction(0x2e, "PL1", MacroOperand.None),
new MacroInstruction(0x2f, "PL2", MacroOperand.None),
new MacroInstruction(0x30, "PL3", MacroOperand.None),
new MacroInstruction(0x31, "PLB", MacroOperand.Byte),
new MacroInstruction(0x32, "PLD0", MacroOperand.None),
new MacroInstruction(0x33, "PLDB", MacroOperand.Byte),
new MacroInstruction(0x34, "LG0", MacroOperand.None),
new MacroInstruction(0x35, "LG1", MacroOperand.None),
new MacroInstruction(0x36, "LG2", MacroOperand.None),
new MacroInstruction(0x37, "LGB", MacroOperand.Byte),
new MacroInstruction(0x38, "LGD0", MacroOperand.None),
new MacroInstruction(0x39, "LGD2", MacroOperand.None),
new MacroInstruction(0x3a, "LGDB", MacroOperand.Byte),
new MacroInstruction(0x3b, "SGB", MacroOperand.Byte),
new MacroInstruction(0x3c, "BNDCK", MacroOperand.None),
new MacroInstruction(0x3d, "BRK", MacroOperand.None),
Invalid,
new MacroInstruction(0x3f, "STC", MacroOperand.None),
new MacroInstruction(0x40, "R0", MacroOperand.None),
new MacroInstruction(0x41, "R1", MacroOperand.None),
new MacroInstruction(0x42, "RB", MacroOperand.Byte),
new MacroInstruction(0x43, "RL0", MacroOperand.None),
new MacroInstruction(0x44, "RLB", MacroOperand.Byte),
new MacroInstruction(0x45, "RD0", MacroOperand.None),
new MacroInstruction(0x46, "RDB", MacroOperand.None),
new MacroInstruction(0x47, "RDL0", MacroOperand.None),
new MacroInstruction(0x48, "RDLB", MacroOperand.Byte),
new MacroInstruction(0x49, "W0", MacroOperand.None),
new MacroInstruction(0x4a, "WB", MacroOperand.Byte),
new MacroInstruction(0x4b, "PSB", MacroOperand.Byte),
new MacroInstruction(0x4c, "WLB", MacroOperand.Byte),
new MacroInstruction(0x4d, "PSLB", MacroOperand.Byte),
new MacroInstruction(0x4e, "WDB", MacroOperand.Byte),
new MacroInstruction(0x4f, "PSD0", MacroOperand.None),
new MacroInstruction(0x50, "PSDB", MacroOperand.Byte),
new MacroInstruction(0x51, "WDLB", MacroOperand.Byte),
new MacroInstruction(0x52, "PSDLB", MacroOperand.Byte),
new MacroInstruction(0x53, "RLI00", MacroOperand.None),
new MacroInstruction(0x54, "RLI01", MacroOperand.None),
new MacroInstruction(0x55, "RLI02", MacroOperand.None),
new MacroInstruction(0x56, "RLI03", MacroOperand.None),
new MacroInstruction(0x57, "RLIP", MacroOperand.Pair),
new MacroInstruction(0x58, "RLILP", MacroOperand.Pair),
new MacroInstruction(0x59, "RLDI00", MacroOperand.None),
new MacroInstruction(0x5a, "RLDIP", MacroOperand.Pair),
new MacroInstruction(0x5b, "RLDILP", MacroOperand.Pair),
new MacroInstruction(0x5c, "RGIP", MacroOperand.Pair),
new MacroInstruction(0x5d, "RGILP", MacroOperand.Pair),
new MacroInstruction(0x5e, "WLIP", MacroOperand.Pair),
new MacroInstruction(0x5f, "WLILP", MacroOperand.Pair),
new MacroInstruction(0x60, "WLDILP", MacroOperand.Pair),
new MacroInstruction(0x61, "RS", MacroOperand.None),
new MacroInstruction(0x62, "RLS", MacroOperand.None),
new MacroInstruction(0x63, "WS", MacroOperand.None),
new MacroInstruction(0x64, "WLS", MacroOperand.None),
new MacroInstruction(0x65, "R0F", MacroOperand.Byte),
new MacroInstruction(0x66, "RF", MacroOperand.TwoByte),
new MacroInstruction(0x67, "RL0F", MacroOperand.Byte),
new MacroInstruction(0x68, "RLF", MacroOperand.TwoByte),
new MacroInstruction(0x69, "RLFS", MacroOperand.None),
new MacroInstruction(0x6a, "RLIPF", MacroOperand.Word),
new MacroInstruction(0x6b, "RLILPF", MacroOperand.Word),
new MacroInstruction(0x6c, "WOF", MacroOperand.None),
new MacroInstruction(0x6d, "WF", MacroOperand.TwoByte),
new MacroInstruction(0x6e, "PSF", MacroOperand.TwoByte),
new MacroInstruction(0x6f, "PS0F", MacroOperand.None),
new MacroInstruction(0x70, "WS0F", MacroOperand.Byte),
new MacroInstruction(0x71, "WL0F", MacroOperand.Byte),
new MacroInstruction(0x72, "WLF", MacroOperand.TwoByte),
new MacroInstruction(0x73, "PSLF", MacroOperand.TwoByte),
new MacroInstruction(0x74, "WLFS", MacroOperand.Byte),
new MacroInstruction(0x75, "SLDB", MacroOperand.Byte),
new MacroInstruction(0x76, "SGDB", MacroOperand.Byte),
new MacroInstruction(0x77, "LLKB", MacroOperand.Byte),
new MacroInstruction(0x78, "RKIB", MacroOperand.Byte),
new MacroInstruction(0x79, "RKDIB", MacroOperand.Byte),
new MacroInstruction(0x7a, "LKB", MacroOperand.Byte),
new MacroInstruction(0x7b, "SHIFT", MacroOperand.None),
new MacroInstruction(0x7c, "SHIFTSB", MacroOperand.SignedByte),
new MacroInstruction(0x7d, "MBP", MacroOperand.Pair),
new MacroInstruction(0x7e, "RBP", MacroOperand.Pair),
new MacroInstruction(0x7f, "WBP", MacroOperand.Pair),
new MacroInstruction(0x80, "CATCH", MacroOperand.Byte),
new MacroInstruction(0x81, "J2", MacroOperand.None),
new MacroInstruction(0x82, "J3", MacroOperand.None),
new MacroInstruction(0x83, "J4", MacroOperand.None),
new MacroInstruction(0x84, "J5", MacroOperand.None),
new MacroInstruction(0x85, "J6", MacroOperand.None),
new MacroInstruction(0x86, "J7", MacroOperand.None),
new MacroInstruction(0x87, "J8", MacroOperand.None),
new MacroInstruction(0x88, "JB", MacroOperand.Byte),
new MacroInstruction(0x89, "JW", MacroOperand.Word),
new MacroInstruction(0x8a, "JEP", MacroOperand.Pair),
new MacroInstruction(0x8b, "JEB", MacroOperand.Byte),
new MacroInstruction(0x8c, "JEBB", MacroOperand.TwoByte),
new MacroInstruction(0x8d, "JNEP", MacroOperand.Pair),
new MacroInstruction(0x8e, "JNEB", MacroOperand.Byte),
new MacroInstruction(0x8f, "JNEBB", MacroOperand.TwoByte),
new MacroInstruction(0x90, "JLB", MacroOperand.Byte),
new MacroInstruction(0x91, "JGB", MacroOperand.Byte),
new MacroInstruction(0x92, "JGEB", MacroOperand.Byte),
new MacroInstruction(0x93, "JLEB", MacroOperand.Byte),
new MacroInstruction(0x94, "JULB", MacroOperand.Byte),
new MacroInstruction(0x95, "JUGB", MacroOperand.Byte),
new MacroInstruction(0x96, "JUGEB", MacroOperand.Byte),
new MacroInstruction(0x97, "JULEB", MacroOperand.Byte),
new MacroInstruction(0x98, "JZ3", MacroOperand.None),
new MacroInstruction(0x99, "JZ4", MacroOperand.None),
new MacroInstruction(0x9a, "JZB", MacroOperand.Byte),
new MacroInstruction(0x9b, "JNZ3", MacroOperand.None),
new MacroInstruction(0x9c, "JNZ4", MacroOperand.None),
new MacroInstruction(0x9d, "JNZB", MacroOperand.Byte),
new MacroInstruction(0x9e, "JDEB", MacroOperand.Byte),
new MacroInstruction(0x9f, "JDNEB", MacroOperand.Byte),
new MacroInstruction(0xa0, "JIB", MacroOperand.Byte),
new MacroInstruction(0xa1, "JIW", MacroOperand.Word),
new MacroInstruction(0xa2, "REC", MacroOperand.None),
new MacroInstruction(0xa3, "REC2", MacroOperand.None),
new MacroInstruction(0xa4, "DIS", MacroOperand.None),
new MacroInstruction(0xa5, "DIS2", MacroOperand.None),
new MacroInstruction(0xa6, "EXCH", MacroOperand.None),
new MacroInstruction(0xa7, "DEXCH", MacroOperand.None),
new MacroInstruction(0xa8, "DUP", MacroOperand.None),
new MacroInstruction(0xa9, "DDUP", MacroOperand.None),
new MacroInstruction(0xaa, "EXDIS", MacroOperand.None),
new MacroInstruction(0xab, "NEG", MacroOperand.None),
new MacroInstruction(0xac, "INC", MacroOperand.None),
new MacroInstruction(0xad, "DEC", MacroOperand.None),
new MacroInstruction(0xae, "DINC", MacroOperand.None),
new MacroInstruction(0xaf, "DBL", MacroOperand.None),
new MacroInstruction(0xb0, "DDBL", MacroOperand.None),
new MacroInstruction(0xb1, "TRPL", MacroOperand.None),
new MacroInstruction(0xb2, "AND", MacroOperand.None),
new MacroInstruction(0xb3, "IOR", MacroOperand.None),
new MacroInstruction(0xb4, "ADDSB", MacroOperand.SignedByte),
new MacroInstruction(0xb5, "ADD", MacroOperand.None),
new MacroInstruction(0xb6, "SUB", MacroOperand.None),
new MacroInstruction(0xb7, "DADD", MacroOperand.None),
new MacroInstruction(0xb8, "DSUB", MacroOperand.None),
new MacroInstruction(0xb9, "ADC", MacroOperand.None),
new MacroInstruction(0xba, "ACD", MacroOperand.None),
new MacroInstruction(0xbb, "AL0IB", MacroOperand.Byte),
new MacroInstruction(0xbc, "MUL", MacroOperand.None),
new MacroInstruction(0xbd, "DCMP", MacroOperand.None),
new MacroInstruction(0xbe, "UDCMP", MacroOperand.None),
Invalid,
new MacroInstruction(0xc0, "LI0", MacroOperand.None),
new MacroInstruction(0xc1, "LI1", MacroOperand.None),
new MacroInstruction(0xc2, "LI2", MacroOperand.None),
new MacroInstruction(0xc3, "LI3", MacroOperand.None),
new MacroInstruction(0xc4, "LI4", MacroOperand.None),
new MacroInstruction(0xc5, "LI5", MacroOperand.None),
new MacroInstruction(0xc6, "LI6", MacroOperand.None),
new MacroInstruction(0xc7, "LI7", MacroOperand.None),
new MacroInstruction(0xc8, "LI8", MacroOperand.None),
new MacroInstruction(0xc9, "LI9", MacroOperand.None),
new MacroInstruction(0xca, "LI10", MacroOperand.None),
new MacroInstruction(0xcb, "LIN1", MacroOperand.None),
new MacroInstruction(0xcc, "LINI", MacroOperand.None),
new MacroInstruction(0xcd, "LIB", MacroOperand.Byte),
new MacroInstruction(0xce, "LIW", MacroOperand.Word),
new MacroInstruction(0xcf, "LINB", MacroOperand.Byte),
new MacroInstruction(0xd0, "LIHB", MacroOperand.Byte),
new MacroInstruction(0xd1, "LID0", MacroOperand.None),
new MacroInstruction(0xd2, "LA0", MacroOperand.None),
new MacroInstruction(0xd3, "LA1", MacroOperand.None),
new MacroInstruction(0xd4, "LA2", MacroOperand.None),
new MacroInstruction(0xd5, "LA3", MacroOperand.None),
new MacroInstruction(0xd6, "LA6", MacroOperand.None),
new MacroInstruction(0xd7, "LA8", MacroOperand.None),
new MacroInstruction(0xd8, "LAB", MacroOperand.Byte),
new MacroInstruction(0xd9, "LAW", MacroOperand.Word),
new MacroInstruction(0xda, "GA0", MacroOperand.None),
new MacroInstruction(0xdb, "GA1", MacroOperand.None),
new MacroInstruction(0xdc, "GAB", MacroOperand.Byte),
new MacroInstruction(0xdd, "GAW", MacroOperand.Word),
Invalid,
new MacroInstruction(0xdf, "EFC0", MacroOperand.None),
new MacroInstruction(0xe0, "EFC1", MacroOperand.None),
new MacroInstruction(0xe1, "EFC2", MacroOperand.None),
new MacroInstruction(0xe2, "EFC3", MacroOperand.None),
new MacroInstruction(0xe3, "EFC4", MacroOperand.None),
new MacroInstruction(0xe4, "EFC5", MacroOperand.None),
new MacroInstruction(0xe5, "EFC6", MacroOperand.None),
new MacroInstruction(0xe6, "EFC7", MacroOperand.None),
new MacroInstruction(0xe7, "EFC8", MacroOperand.None),
new MacroInstruction(0xe8, "EFC9", MacroOperand.None),
new MacroInstruction(0xe9, "EFC10", MacroOperand.None),
new MacroInstruction(0xea, "EFC11", MacroOperand.None),
new MacroInstruction(0xeb, "EFC12", MacroOperand.None),
new MacroInstruction(0xec, "EFCB", MacroOperand.Byte),
new MacroInstruction(0xed, "LFC", MacroOperand.Word),
new MacroInstruction(0xee, "SFC", MacroOperand.None),
new MacroInstruction(0xef, "RET", MacroOperand.None),
new MacroInstruction(0xf0, "KFCB", MacroOperand.Byte),
new MacroInstruction(0xf1, "ME", MacroOperand.None),
new MacroInstruction(0xf2, "MX", MacroOperand.None),
new MacroInstruction(0xf3, "BLT", MacroOperand.None),
new MacroInstruction(0xf4, "BLTL", MacroOperand.None),
new MacroInstruction(0xf5, "BLTC", MacroOperand.None),
new MacroInstruction(0xf6, "BLTCL", MacroOperand.None),
new MacroInstruction(0xf7, "LP", MacroOperand.None),
new MacroInstruction(0xf8, "ESC", MacroOperand.Byte),
new MacroInstruction(0xf9, "ESCL", MacroOperand.Word),
new MacroInstruction(0xfa, "LGA0", MacroOperand.None),
new MacroInstruction(0xfb, "LGAB", MacroOperand.Byte),
new MacroInstruction(0xfc, "LGAW", MacroOperand.Word),
new MacroInstruction(0xfd, "DESC", MacroOperand.None),
Invalid,
Invalid,
};
private static MacroInstruction[] _lispInstructionTable =
{
Invalid,
new MacroInstruction(0x01, "CAR", MacroOperand.None),
new MacroInstruction(0x02, "CDR", MacroOperand.None),
new MacroInstruction(0x03, "LISTP", MacroOperand.None),
new MacroInstruction(0x04, "NTYPX", MacroOperand.None),
new MacroInstruction(0x05, "TYPEP", MacroOperand.Byte),
new MacroInstruction(0x06, "DTEST", MacroOperand.TwoByte),
new MacroInstruction(0x07, "UNWIND", MacroOperand.TwoByte),
new MacroInstruction(0x08, "FN0", MacroOperand.TwoByte),
new MacroInstruction(0x09, "FN1", MacroOperand.TwoByte),
new MacroInstruction(0x0a, "FN2", MacroOperand.TwoByte),
new MacroInstruction(0x0b, "FN3", MacroOperand.TwoByte),
new MacroInstruction(0x0c, "FN4", MacroOperand.TwoByte),
new MacroInstruction(0x0d, "FNX", MacroOperand.ThreeByte),
new MacroInstruction(0x0e, "APPLYFN", MacroOperand.None),
new MacroInstruction(0x0f, "CHECKAPPLY", MacroOperand.None),
new MacroInstruction(0x10, "RETURN", MacroOperand.None),
new MacroInstruction(0x11, "BIND", MacroOperand.TwoByte),
new MacroInstruction(0x12, "UNBIND", MacroOperand.None),
new MacroInstruction(0x13, "DUNBIND", MacroOperand.None),
new MacroInstruction(0x14, "RPLPTR.N", MacroOperand.Byte),
new MacroInstruction(0x15, "GCREF", MacroOperand.Byte),
new MacroInstruction(0x16, "ASSOC", MacroOperand.None),
new MacroInstruction(0x17, "GVAR<-", MacroOperand.TwoByte),
new MacroInstruction(0x18, "RPLACA", MacroOperand.None),
new MacroInstruction(0x19, "RPLACD", MacroOperand.None),
new MacroInstruction(0x1a, "CONS", MacroOperand.None),
new MacroInstruction(0x1b, "CMLASSOC", MacroOperand.None),
new MacroInstruction(0x1c, "FMEMB", MacroOperand.None),
new MacroInstruction(0x1d, "CMLMEMBER", MacroOperand.None),
new MacroInstruction(0x1e, "FINDKEY", MacroOperand.Byte),
new MacroInstruction(0x1f, "CREATECELL", MacroOperand.None),
new MacroInstruction(0x20, "BIN", MacroOperand.None),
new MacroInstruction(0x21, "BOUT", MacroOperand.None),
new MacroInstruction(0x22, "PROLOGOPDISP", MacroOperand.None),
new MacroInstruction(0x23, "RESTLIST", MacroOperand.Byte),
new MacroInstruction(0x24, "MISCN", MacroOperand.TwoByte),
new MacroInstruction(0x25, "ENDCOLLECT", MacroOperand.None),
new MacroInstruction(0x26, "RPLCONS", MacroOperand.None),
Invalid,
new MacroInstruction(0x28, "ELT", MacroOperand.None),
new MacroInstruction(0x29, "NTHCHC", MacroOperand.None),
new MacroInstruction(0x2a, "SETA", MacroOperand.None),
new MacroInstruction(0x2b, "RPLCHARCODE", MacroOperand.None),
new MacroInstruction(0x2c, "EVAL", MacroOperand.None),
new MacroInstruction(0x2d, "EVALV", MacroOperand.None),
new MacroInstruction(0x2e, "TYPECHECK.N", MacroOperand.Byte),
new MacroInstruction(0x2f, "STKSCAN", MacroOperand.None),
new MacroInstruction(0x30, "BUSBLT", MacroOperand.Byte),
new MacroInstruction(0x31, "MISC8", MacroOperand.Byte),
new MacroInstruction(0x32, "UBFLOAT3", MacroOperand.Byte),
new MacroInstruction(0x33, "TYPEMASK.N", MacroOperand.Byte),
new MacroInstruction(0x34, "PROLOGREADPTR", MacroOperand.None),
new MacroInstruction(0x35, "PROLOGREADTAG", MacroOperand.None),
new MacroInstruction(0x36, "PROLOGWRITETAGPTR", MacroOperand.None),
new MacroInstruction(0x37, "PROLOGWRITE0PTR", MacroOperand.None),
new MacroInstruction(0x38, "PSEUDOCOLOR", MacroOperand.None),
Invalid,
new MacroInstruction(0x3a, "EQL", MacroOperand.None),
new MacroInstruction(0x3b, "DRAWLINE", MacroOperand.None),
new MacroInstruction(0x3c, "STORE.N", MacroOperand.Byte),
new MacroInstruction(0x3d, "COPY.N", MacroOperand.Byte),
new MacroInstruction(0x3e, "RAID", MacroOperand.None),
new MacroInstruction(0x3f, "\\RETURN", MacroOperand.None),
new MacroInstruction(0x40, "IVAR0", MacroOperand.None),
new MacroInstruction(0x41, "IVAR1", MacroOperand.None),
new MacroInstruction(0x42, "IVAR2", MacroOperand.None),
new MacroInstruction(0x43, "IVAR3", MacroOperand.None),
new MacroInstruction(0x44, "IVAR4", MacroOperand.None),
new MacroInstruction(0x45, "IVAR5", MacroOperand.None),
new MacroInstruction(0x46, "IVAR6", MacroOperand.None),
new MacroInstruction(0x47, "IVARX", MacroOperand.Byte),
new MacroInstruction(0x48, "PVAR0", MacroOperand.None),
new MacroInstruction(0x49, "PVAR1", MacroOperand.None),
new MacroInstruction(0x4a, "PVAR2", MacroOperand.None),
new MacroInstruction(0x4b, "PVAR3", MacroOperand.None),
new MacroInstruction(0x4c, "PVAR4", MacroOperand.None),
new MacroInstruction(0x4d, "PVAR5", MacroOperand.None),
new MacroInstruction(0x4e, "PVAR6", MacroOperand.None),
new MacroInstruction(0x4f, "PVARX", MacroOperand.Byte),
new MacroInstruction(0x50, "FVAR0", MacroOperand.None),
new MacroInstruction(0x51, "FVAR1", MacroOperand.None),
new MacroInstruction(0x52, "FVAR2", MacroOperand.None),
new MacroInstruction(0x53, "FVAR3", MacroOperand.None),
new MacroInstruction(0x54, "FVAR4", MacroOperand.None),
new MacroInstruction(0x55, "FVAR5", MacroOperand.None),
new MacroInstruction(0x56, "FVAR6", MacroOperand.None),
new MacroInstruction(0x57, "FVARX", MacroOperand.Byte),
new MacroInstruction(0x58, "PVAR0<-", MacroOperand.None),
new MacroInstruction(0x59, "PVAR1<-", MacroOperand.None),
new MacroInstruction(0x5a, "PVAR2<-", MacroOperand.None),
new MacroInstruction(0x5b, "PVAR3<-", MacroOperand.None),
new MacroInstruction(0x5c, "PVAR4<-", MacroOperand.None),
new MacroInstruction(0x5d, "PVAR5<-", MacroOperand.None),
new MacroInstruction(0x5e, "PVAR6<-", MacroOperand.None),
new MacroInstruction(0x5f, "PVARX<-", MacroOperand.Byte),
new MacroInstruction(0x60, "GVAR", MacroOperand.TwoByte),
new MacroInstruction(0x61, "ARG0", MacroOperand.None),
new MacroInstruction(0x62, "IVARX<-", MacroOperand.Byte),
new MacroInstruction(0x63, "FVARX<-", MacroOperand.Byte),
new MacroInstruction(0x64, "COPY", MacroOperand.None),
new MacroInstruction(0x65, "MYARGCOUNT", MacroOperand.None),
new MacroInstruction(0x66, "MYALINK", MacroOperand.None),
new MacroInstruction(0x67, "ACONST", MacroOperand.TwoByte),
new MacroInstruction(0x68, "'NIL", MacroOperand.None),
new MacroInstruction(0x69, "'T", MacroOperand.None),
new MacroInstruction(0x6a, "'0", MacroOperand.None),
new MacroInstruction(0x6b, "'1", MacroOperand.None),
new MacroInstruction(0x6c, "SIC", MacroOperand.Byte),
new MacroInstruction(0x6d, "SNIC", MacroOperand.Byte),
new MacroInstruction(0x6e, "SICX", MacroOperand.TwoByte),
new MacroInstruction(0x6f, "GCONST", MacroOperand.ThreeByte),
new MacroInstruction(0x70, "ATOMNUMBER", MacroOperand.TwoByte),
new MacroInstruction(0x71, "READFLAGS", MacroOperand.None),
new MacroInstruction(0x72, "READRP", MacroOperand.None),
new MacroInstruction(0x73, "WRITEMAP", MacroOperand.None),
new MacroInstruction(0x74, "READPRINTERPORT", MacroOperand.None),
new MacroInstruction(0x75, "WRITEPRINTERPORT", MacroOperand.None),
new MacroInstruction(0x76, "PILOTBITBLT", MacroOperand.None),
new MacroInstruction(0x77, "RCLK", MacroOperand.None),
new MacroInstruction(0x78, "MISC1", MacroOperand.Byte),
new MacroInstruction(0x79, "MISC2", MacroOperand.Byte),
new MacroInstruction(0x7a, "RECLAIMCELL", MacroOperand.None),
new MacroInstruction(0x7b, "GCSCAN1", MacroOperand.None),
new MacroInstruction(0x7c, "GCSCAN2", MacroOperand.None),
new MacroInstruction(0x7d, "SUBRCALL", MacroOperand.TwoByte),
new MacroInstruction(0x7e, "CONTEXT", MacroOperand.None),
Invalid,
new MacroInstruction(0x80, "JUMP0", MacroOperand.None),
new MacroInstruction(0x81, "JUMP1", MacroOperand.None),
new MacroInstruction(0x82, "JUMP2", MacroOperand.None),
new MacroInstruction(0x83, "JUMP3", MacroOperand.None),
new MacroInstruction(0x84, "JUMP4", MacroOperand.None),
new MacroInstruction(0x85, "JUMP5", MacroOperand.None),
new MacroInstruction(0x86, "JUMP6", MacroOperand.None),
new MacroInstruction(0x87, "JUMP7", MacroOperand.None),
new MacroInstruction(0x88, "JUMP8", MacroOperand.None),
new MacroInstruction(0x89, "JUMP9", MacroOperand.None),
new MacroInstruction(0x8a, "JUMP10", MacroOperand.None),
new MacroInstruction(0x8b, "JUMP11", MacroOperand.None),
new MacroInstruction(0x8c, "JUMP12", MacroOperand.None),
new MacroInstruction(0x8d, "JUMP13", MacroOperand.None),
new MacroInstruction(0x8e, "JUMP14", MacroOperand.None),
new MacroInstruction(0x8f, "JUMP15", MacroOperand.None),
new MacroInstruction(0x90, "FJUMP0", MacroOperand.None),
new MacroInstruction(0x91, "FJUMP1", MacroOperand.None),
new MacroInstruction(0x92, "FJUMP2", MacroOperand.None),
new MacroInstruction(0x93, "FJUMP3", MacroOperand.None),
new MacroInstruction(0x94, "FJUMP4", MacroOperand.None),
new MacroInstruction(0x95, "FJUMP5", MacroOperand.None),
new MacroInstruction(0x96, "FJUMP6", MacroOperand.None),
new MacroInstruction(0x97, "FJUMP7", MacroOperand.None),
new MacroInstruction(0x98, "FJUMP8", MacroOperand.None),
new MacroInstruction(0x99, "FJUMP9", MacroOperand.None),
new MacroInstruction(0x9a, "FJUMP10", MacroOperand.None),
new MacroInstruction(0x9b, "FJUMP11", MacroOperand.None),
new MacroInstruction(0x9c, "FJUMP12", MacroOperand.None),
new MacroInstruction(0x9d, "FJUMP13", MacroOperand.None),
new MacroInstruction(0x9e, "FJUMP14", MacroOperand.None),
new MacroInstruction(0x9f, "FJUMP15", MacroOperand.None),
new MacroInstruction(0xa0, "TJUMP2", MacroOperand.None),
new MacroInstruction(0xa1, "TJUMP3", MacroOperand.None),
new MacroInstruction(0xa2, "TJUMP4", MacroOperand.None),
new MacroInstruction(0xa3, "TJUMP5", MacroOperand.None),
new MacroInstruction(0xa4, "TJUMP6", MacroOperand.None),
new MacroInstruction(0xa5, "TJUMP7", MacroOperand.None),
new MacroInstruction(0xa6, "TJUMP8", MacroOperand.None),
new MacroInstruction(0xa7, "TJUMP9", MacroOperand.None),
new MacroInstruction(0xa8, "TJUMPa", MacroOperand.None),
new MacroInstruction(0xa9, "TJUMPb", MacroOperand.None),
new MacroInstruction(0xaa, "TJUMPc", MacroOperand.None),
new MacroInstruction(0xab, "TJUMPe", MacroOperand.None),
new MacroInstruction(0xac, "TJUMPf", MacroOperand.None),
new MacroInstruction(0xad, "TJUMP10", MacroOperand.None),
new MacroInstruction(0xae, "TJUMP11", MacroOperand.None),
new MacroInstruction(0xaf, "TJUMP12", MacroOperand.None),
new MacroInstruction(0xb0, "JUMPX", MacroOperand.Byte),
new MacroInstruction(0xb1, "JUMPXX", MacroOperand.TwoByte),
new MacroInstruction(0xb2, "FJUMPX", MacroOperand.Byte),
new MacroInstruction(0xb3, "TJUMPX", MacroOperand.Byte),
new MacroInstruction(0xb4, "NFJUMPX", MacroOperand.Byte),
new MacroInstruction(0xb5, "NTJUMPX", MacroOperand.Byte),
new MacroInstruction(0xb6, "AREF1", MacroOperand.None),
new MacroInstruction(0xb7, "ASET1", MacroOperand.None),
new MacroInstruction(0xb8, "PVAR0<-^", MacroOperand.None),
new MacroInstruction(0xb9, "PVAR1<-^", MacroOperand.None),
new MacroInstruction(0xba, "PVAR2<-^", MacroOperand.None),
new MacroInstruction(0xbb, "PVAR3<-^", MacroOperand.None),
new MacroInstruction(0xbc, "PVAR4<-^", MacroOperand.None),
new MacroInstruction(0xbd, "PVAR5<-^", MacroOperand.None),
new MacroInstruction(0xbe, "PVAR6<-^", MacroOperand.None),
new MacroInstruction(0xbf, "POP", MacroOperand.None),
new MacroInstruction(0xc0, "POP.N", MacroOperand.Byte),
new MacroInstruction(0xc1, "ATOMCELL.N", MacroOperand.Byte),
new MacroInstruction(0xc2, "GETBASEBYTE", MacroOperand.None),
new MacroInstruction(0xc3, "INSTANCEP", MacroOperand.TwoByte),
new MacroInstruction(0xc4, "BLT", MacroOperand.None),
new MacroInstruction(0xc5, "MISC10", MacroOperand.TwoByte),
Invalid,
new MacroInstruction(0xc7, "PUTBASEBYTE", MacroOperand.None),
new MacroInstruction(0xc8, "GETBASE.N", MacroOperand.Byte),
new MacroInstruction(0xc9, "GETBASEPTR.N", MacroOperand.Byte),
new MacroInstruction(0xca, "GETBITS.N.FD", MacroOperand.TwoByte),
Invalid,
new MacroInstruction(0xcc, "CMLEQUAL", MacroOperand.None),
new MacroInstruction(0xcd, "PUTBASE.N", MacroOperand.Byte),
new MacroInstruction(0xce, "PUTBASEPTR.N", MacroOperand.Byte),
new MacroInstruction(0xcf, "PUTBITS.N.FD", MacroOperand.TwoByte),
new MacroInstruction(0xd0, "ADDBASE", MacroOperand.None),
new MacroInstruction(0xd1, "VAG2", MacroOperand.None),
new MacroInstruction(0xd2, "HILOC", MacroOperand.None),
new MacroInstruction(0xd3, "LOLOC", MacroOperand.None),
new MacroInstruction(0xd4, "PLUS2", MacroOperand.None),
new MacroInstruction(0xd5, "DIFFERENCE", MacroOperand.None),
new MacroInstruction(0xd6, "TIMES2", MacroOperand.None),
new MacroInstruction(0xd7, "QUOTIENT", MacroOperand.None),
new MacroInstruction(0xd8, "IPLUS2", MacroOperand.None),
new MacroInstruction(0xd9, "IDIFFERENCE", MacroOperand.None),
new MacroInstruction(0xda, "ITIMES2", MacroOperand.None),
new MacroInstruction(0xdb, "IQUOTIENT", MacroOperand.None),
new MacroInstruction(0xdc, "IREMAINDER", MacroOperand.None),
new MacroInstruction(0xdd, "IPLUS.N", MacroOperand.Byte),
new MacroInstruction(0xde, "IDIFFERENCE.N", MacroOperand.Byte),
Invalid,
new MacroInstruction(0xe0, "LLSH1", MacroOperand.None),
new MacroInstruction(0xe1, "LLSH8", MacroOperand.None),
new MacroInstruction(0xe2, "LRSH1", MacroOperand.None),
new MacroInstruction(0xe3, "LRSH8", MacroOperand.None),
new MacroInstruction(0xe4, "LOGOR2", MacroOperand.None),
new MacroInstruction(0xe5, "LOGAND2", MacroOperand.None),
new MacroInstruction(0xe6, "LOGXOR2", MacroOperand.None),
new MacroInstruction(0xe7, "LSH", MacroOperand.None),
new MacroInstruction(0xe8, "FPLUS2", MacroOperand.None),
new MacroInstruction(0xe9, "FDIFFERENCE", MacroOperand.None),
new MacroInstruction(0xea, "FTIMES2", MacroOperand.None),
new MacroInstruction(0xeb, "FQUOTIENT", MacroOperand.None),
new MacroInstruction(0xec, "UBFLOAT2", MacroOperand.Byte),
new MacroInstruction(0xed, "UBFLOAT1", MacroOperand.Byte),
new MacroInstruction(0xee, "AREF2", MacroOperand.None),
new MacroInstruction(0xef, "ASET2", MacroOperand.None),
new MacroInstruction(0xe0, "EQ", MacroOperand.None),
new MacroInstruction(0xe1, "IGREATERP", MacroOperand.None),
new MacroInstruction(0xe2, "FGREATERP", MacroOperand.None),
new MacroInstruction(0xe3, "GREATERP", MacroOperand.None),
new MacroInstruction(0xe4, "EQUAL", MacroOperand.None),
new MacroInstruction(0xe5, "MAKENUMBER", MacroOperand.None),
new MacroInstruction(0xe6, "BOXIPLUS", MacroOperand.None),
new MacroInstruction(0xe7, "BOXIDIFFERENCE", MacroOperand.None),
new MacroInstruction(0xe8, "FLOATBLT", MacroOperand.None),
new MacroInstruction(0xe9, "FFTSTEP", MacroOperand.None),
new MacroInstruction(0xea, "MISC3", MacroOperand.ThreeByte),
new MacroInstruction(0xeb, "MISC4", MacroOperand.ThreeByte),
Invalid,
new MacroInstruction(0xed, "SWAP", MacroOperand.None),
new MacroInstruction(0xee, "NOP", MacroOperand.None),
Invalid,
};
private static MacroInstruction Invalid = new MacroInstruction(0x00, "INVALID", MacroOperand.None);
}
}

1267
D/CP/Microinstruction.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,43 @@
#
# This file maps assembly source symbols to PROM/microcode addresses and source lines, forming a crude
# symbol table.
#
# Each source file is given a header a la:
# [FooSource.asm]
# (with brackets)
# Each line's syntax is:
# <symbol name 1>, .. , <symbol name N>: <address or value (hex)>,<line number(decimal) in current source file>
# where '*none*' is a special symbol name meaning no symbol mapping is present.
[BootKernel.mc,v]
*none*: 0x0fdf,50
KGo: 0x0fde,49
*none*: 0x0fdd,51
*none*: 0x0fdc,52
*none*: 0x0fdb,53
*none*: 0x0fda,54
*none*: 0x0fd9,55
*none*: 0x0fd8,56
KEntry: 0x0fe0,66
*none*: 0x0fe2,67
*none*: 0x0fe6,68
*none*: 0x0fe8,69
KRefresh: 0x0fe5,71
*none*: 0x0fe9,72
*none*: 0x0fea,73
KLoop: 0x0fe4,75
*none*: 0x0feb,76
*none*: 0x0ff0,77
KTable: 0x0ffc,88
*none*: 0x0fee,89
*none*: 0x0fef,90
*none*: 0x0fe7,81
*none*: 0x0fe1,80
*none*: 0x0ff1,82
*none*: 0x0fec,85
*none*: 0x0fed,86
KDisp: 0x0fe3,84
KRefCmd: 0x0ffd,92
KCmd2: 0x0ffe,93
KCmd3: 0x0fff,94

View File

@@ -0,0 +1,256 @@
#
# This file maps assembly source symbols to PROM/microcode addresses and source lines, forming a crude
# symbol table.
#
# Each source file is given a header a la:
# [FooSource.asm]
# (with brackets)
# Each line's syntax is:
# <symbol name 1>, .. , <symbol name N>: <address or value (hex)>,<line number(decimal) in current source file>
# where '*none*' is a special symbol name meaning no symbol mapping is present.
[MoonCPMemTest.mc,v]
*none*: 0x00ff,72
Start: 0x0182,71
*none*: 0x0183,73
*none*: 0x0184,75
*none*: 0x0185,76
*none*: 0x0186,77
*none*: 0x0187,79
*none*: 0x0188,80
*none*: 0x0189,81
*none*: 0x018a,83
*none*: 0x018b,84
*none*: 0x018c,85
walk: 0x018d,90
ChkBound: 0x018e,91
*none*: 0x018f,92
*none*: 0x0190,94
*none*: 0x0096,95
ChkYTop: 0x0191,97
YBotOK: 0x0097,96
*none*: 0x0192,99
YTopOK: 0x0068,101
ChkDone: 0x0193,102
StartM: 0x0194,105
*none*: 0x0195,106
*none*: 0x0196,107
*none*: 0x0197,109
*none*: 0x0198,110
*none*: 0x0199,111
*none*: 0x019a,113
*none*: 0x019b,114
*none*: 0x019c,115
*none*: 0x019d,117
RUNLIST: 0x0098,120
RUNLISTX: 0x0060,121
SETUP: 0x01ea,293
*none*: 0x01eb,294
*none*: 0x01ec,295
*none*: 0x01ed,297
*none*: 0x01ee,298
*none*: 0x01ef,299
*none*: 0x01f0,301
*none*: 0x01f1,302
*none*: 0x01f2,303
*none*: 0x01f3,305
*none*: 0x01f4,306
*none*: 0x01f5,307
*none*: 0x01f6,308
*none*: 0x01f7,311
*none*: 0x01f8,312
*none*: 0x01f9,313
*none*: 0x01fa,314
*none*: 0x01fb,315
*none*: 0x01fc,318
*none*: 0x01fd,319
*none*: 0x01fe,321
*none*: 0x01ff,322
*none*: 0x0200,323
*none*: 0x0201,325
RUNLISTRET: 0x01cf,241
*none*: 0x01d0,242
*none*: 0x0038,243
*none*: 0x00e0,123
RUNL: 0x019e,124
*none*: 0x019f,125
RunTest: 0x0050,127
*none*: 0x0051,128
*none*: 0x0052,129
*none*: 0x0053,130
RUNLIST1: 0x01a0,133
*none*: 0x0061,134
*none*: 0x00e1,136
*none*: 0x01a1,137
*none*: 0x0022,138
SSTORE: 0x0202,345
*none*: 0x0025,346
AdjustQ: 0x0227,480
*none*: 0x0228,481
*none*: 0x001c,483
*none*: 0x001d,484
*none*: 0x001e,485
*none*: 0x001f,486
*none*: 0x0105,347
*none*: 0x0203,349
*none*: 0x0046,350
*none*: 0x0106,351
*none*: 0x0204,353
*none*: 0x0205,354
STYPE: 0x0058,356
*none*: 0x0059,357
*none*: 0x005a,358
*none*: 0x005b,359
*none*: 0x005c,360
SSTORE3: 0x020e,384
*none*: 0x020f,385
*none*: 0x0210,386
*none*: 0x0211,387
SDOAGAIN: 0x0084,388
SLMATCH: 0x0085,391
*none*: 0x0212,389
SSTORE1: 0x0206,364
*none*: 0x0207,365
STYPE1: 0x0078,367
*none*: 0x0079,368
*none*: 0x007a,369
*none*: 0x007b,370
*none*: 0x007c,371
*none*: 0x0027,392
*none*: 0x0107,393
*none*: 0x0213,394
*none*: 0x0214,395
*none*: 0x0215,397
*none*: 0x0008,398
*none*: 0x00d4,401
SUMATCH: 0x00d5,403
*none*: 0x0108,402
*none*: 0x00e2,140
*none*: 0x01a2,141
Delay1: 0x01a3,143
*none*: 0x01a4,144
Delay2: 0x006b,146
*none*: 0x006a,145
*none*: 0x01a5,147
*none*: 0x01a6,148
*none*: 0x009c,149
RUNL1: 0x009d,151
*none*: 0x01a7,152
RunTest1: 0x0070,154
*none*: 0x0071,155
*none*: 0x0072,156
*none*: 0x0073,157
RUNLIST2: 0x01a8,160
*none*: 0x0023,161
*none*: 0x00e3,164
*none*: 0x01a9,165
*none*: 0x0024,166
CHECK: 0x0216,421
*none*: 0x0081,422
*none*: 0x0101,423
*none*: 0x0217,425
*none*: 0x0218,426
*none*: 0x0028,428
*none*: 0x0029,429
*none*: 0x002a,430
*none*: 0x002b,431
*none*: 0x002c,432
CHECK2: 0x021e,446
*none*: 0x021f,447
*none*: 0x0220,448
CHECKENTRY: 0x0221,450
*none*: 0x0222,451
*none*: 0x0086,452
DATAERR: 0x0087,471
*none*: 0x0223,454
CDOAGAIN: 0x00d6,455
CLMATCH: 0x00d7,458
*none*: 0x0224,456
CHECK1: 0x0219,434
*none*: 0x021a,435
CTYPE1: 0x00d8,437
*none*: 0x00d9,438
*none*: 0x00da,439
*none*: 0x00db,440
*none*: 0x00dc,441
*none*: 0x0225,459
*none*: 0x0226,462
*none*: 0x0042,463
CDOAGAIN1: 0x0088,466
CUMATCH: 0x0089,469
*none*: 0x0102,467
*none*: 0x00e4,168
*none*: 0x01aa,169
*none*: 0x01ab,170
RUNLIST4X: 0x00e6,184
*none*: 0x01b2,185
*none*: 0x01b3,186
*none*: 0x01b4,188
TYPE0: 0x006c,190
TYPE1: 0x006d,191
TYPE3: 0x01b5,193
DATAINC: 0x01b6,197
*none*: 0x01b7,198
*none*: 0x01b8,199
*none*: 0x01b9,201
*none*: 0x01ba,202
*none*: 0x01bb,203
*none*: 0x01bc,205
*none*: 0x01bd,206
*none*: 0x01be,207
*none*: 0x01bf,208
*none*: 0x01c0,209
CHECKPASS: 0x01c1,212
*none*: 0x01c2,213
PASSDONE1: 0x0099,216
*none*: 0x00e7,172
*none*: 0x01ac,173
*none*: 0x01ad,174
*none*: 0x01ae,177
*none*: 0x01af,178
*none*: 0x0026,179
MAP: 0x0229,498
*none*: 0x022a,499
*none*: 0x022b,500
MAPOFF: 0x00f0,502
MAPON: 0x00f1,505
MDOAGAIN: 0x00f2,517
*none*: 0x0233,518
*none*: 0x0234,520
*none*: 0x0235,521
*none*: 0x0236,522
*none*: 0x0237,524
*none*: 0x0238,525
*none*: 0x0239,526
MAP2: 0x022d,508
*none*: 0x022e,509
*none*: 0x022f,510
MAPENTRY: 0x0230,512
*none*: 0x0231,513
*none*: 0x008a,514
MDATAERR: 0x008b,543
*none*: 0x0232,516
MLMATCH: 0x00f3,529
*none*: 0x023a,530
*none*: 0x023b,532
*none*: 0x023c,533
MDOAGAIN1: 0x008c,536
MUMATCH: 0x008d,540
*none*: 0x023d,537
*none*: 0x021b,442
*none*: 0x021c,443
*none*: 0x021d,444
TERROR1: 0x00df,63
*none*: 0x0208,372
*none*: 0x0209,373
*none*: 0x020a,374
SSTORE2: 0x020b,380
*none*: 0x020c,381
*none*: 0x020d,382
[MoonCPMemDisplay.mc,v]
DisplayStart: 0x0030,81
[MoonCPMemKernel.mc,v]
KernelStart: 0x003c,40

View File

@@ -0,0 +1,263 @@
#
# This file maps assembly source symbols to PROM/microcode addresses and source lines, forming a crude
# symbol table.
#
# Each source file is given a header a la:
# [FooSource.asm]
# (with brackets)
# Each line's syntax is:
# <symbol name 1>, .. , <symbol name N>: <address or value (hex)>,<line number(decimal) in current source file>
# where '*none*' is a special symbol name meaning no symbol mapping is present.
[CoreInitial.mc,v]
*none*: 0x01f2,179
*none*: 0x01f1,178
*none*: 0x01f0,177
*none*: 0x01ef,176
*none*: 0x01f3,180
*none*: 0x01f4,181
largeVMOK: 0x01f5,187
*none*: 0x01f6,188
*none*: 0x01f7,189
*none*: 0x01f8,191
*none*: 0x010a,192
*none*: 0x01f9,193
*none*: 0x01ee,171
*none*: 0x01ec,169
*none*: 0x01ed,170
*none*: 0x01eb,166
*none*: 0x01ea,165
*none*: 0x01e9,164
mapBuild: 0x01e8,163
go: 0x01b1,51
*none*: 0x012f,52
*none*: 0x01b2,55
*none*: 0x01b3,66
*none*: 0x01b4,67
*none*: 0x01b5,68
*none*: 0x01b6,69
*none*: 0x01b7,70
*none*: 0x01b8,71
*none*: 0x01d8,140
mapInit: 0x01d7,139
*none*: 0x01d9,141
*none*: 0x01da,142
*none*: 0x01db,143
*none*: 0x01dc,144
*none*: 0x01dd,145
*none*: 0x01de,146
mark1: 0x01e0,151
*none*: 0x01e1,152
*none*: 0x011c,153
*none*: 0x01e2,154
*none*: 0x0108,155
*none*: 0x01e3,156
*none*: 0x01e4,157
*none*: 0x01e5,158
*none*: 0x01e6,159
*none*: 0x01e7,160
markPages: 0x0109,149
*none*: 0x01df,150
BLT: 0x0231,324
*none*: 0x0232,325
*none*: 0x0233,326
*none*: 0x0228,299
*none*: 0x0229,300
*none*: 0x0124,298
*none*: 0x0125,302
*none*: 0x022a,303
*none*: 0x022b,304
clearLoop: 0x0134,290
*none*: 0x0135,306
*none*: 0x01b9,77
*none*: 0x01ba,78
*none*: 0x01bb,79
*none*: 0x01bc,80
*none*: 0x01bd,81
clear: 0x0114,83
*none*: 0x01be,84
*none*: 0x01bf,85
*none*: 0x0115,87
*none*: 0x01c0,88
*none*: 0x01c1,89
*none*: 0x01c2,94
*none*: 0x01c3,95
*none*: 0x01c4,98
*none*: 0x01c5,99
*none*: 0x01c6,100
*none*: 0x01c7,101
*none*: 0x01c8,103
*none*: 0x01c9,104
*none*: 0x01ca,105
SetVMMSize: 0x01cb,107
*none*: 0x01cc,108
*none*: 0x01cd,109
*none*: 0x01ce,111
*none*: 0x01cf,112
enableIOP: 0x01d0,113
*none*: 0x01d1,115
*none*: 0x01d2,116
*none*: 0x01d3,117
*none*: 0x01d4,120
*none*: 0x01d5,121
*none*: 0x01d6,122
[InitDLion.mc,v]
OnceOnlyINit: 0x0101,66
*none*: 0x010b,67
*none*: 0x010e,78
*none*: 0x011a,79
*none*: 0x010f,89
*none*: 0x011e,90
*none*: 0x0128,92
*none*: 0x0129,93
*none*: 0x012b,94
*none*: 0x012c,96
*none*: 0x012d,97
*none*: 0x012e,98
*none*: 0x013a,100
*none*: 0x013b,101
*none*: 0x013c,102
*none*: 0x013d,104
*none*: 0x013e,105
*none*: 0x013f,106
*none*: 0x0140,108
*none*: 0x0141,109
*none*: 0x0142,110
*none*: 0x0143,112
*none*: 0x0144,113
*none*: 0x0145,114
*none*: 0x0146,116
*none*: 0x0147,117
*none*: 0x0148,118
*none*: 0x0149,120
*none*: 0x014a,121
*none*: 0x014b,122
IOPageLoop: 0x0111,126
*none*: 0x014c,127
*none*: 0x014d,128
EtherInit: 0x0113,131
*none*: 0x014e,132
*none*: 0x014f,133
*none*: 0x0150,135
*none*: 0x0151,136
*none*: 0x0152,137
*none*: 0x0153,139
*none*: 0x0154,140
*none*: 0x0155,141
MagTapeInit: 0x0156,144
*none*: 0x0157,145
*none*: 0x0158,146
DiskInit: 0x0159,149
*none*: 0x015a,150
SAx000: 0x0117,153
*none*: 0x015b,156
*none*: 0x015c,157
*none*: 0x015d,158
*none*: 0x015e,161
*none*: 0x015f,162
*none*: 0x0160,163
*none*: 0x0161,165
SetSA1Const: 0x0102,177
*none*: 0x0165,178
*none*: 0x0166,181
*none*: 0x0167,182
SetURegs: 0x0168,183
*none*: 0x0169,186
*none*: 0x016a,187
*none*: 0x016b,188
DisplayInit: 0x018d,273
*none*: 0x018e,274
*none*: 0x018f,275
*none*: 0x0190,277
*none*: 0x0191,278
*none*: 0x0192,279
*none*: 0x0193,281
*none*: 0x0194,282
*none*: 0x0195,283
*none*: 0x0196,285
*none*: 0x0197,286
*none*: 0x0198,287
*none*: 0x0199,289
*none*: 0x019a,290
*none*: 0x019b,291
*none*: 0x019c,293
*none*: 0x019d,294
*none*: 0x019e,295
*none*: 0x019f,297
*none*: 0x01a0,298
*none*: 0x01a1,299
*none*: 0x01a2,301
*none*: 0x01a3,302
*none*: 0x01a4,303
*none*: 0x01a5,305
*none*: 0x01a6,306
*none*: 0x01a7,307
*none*: 0x01a8,309
*none*: 0x01a9,310
LSepInit: 0x01aa,311
*none*: 0x01ab,313
*none*: 0x01ac,314
*none*: 0x01ad,315
*none*: 0x01ae,317
*none*: 0x01af,318
*none*: 0x01b0,319
[FloppyInitial.mc,v]
DoneOnceOnlyInit: 0x0244,50
*none*: 0x0245,51
*none*: 0x0246,52
clearBank0: 0x0126,54
*none*: 0x0247,55
*none*: 0x0248,56
GFT: 0x0127,60
*none*: 0x0249,61
*none*: 0x024a,62
*none*: 0x024b,64
*none*: 0x024c,65
*none*: 0x0112,66
*none*: 0x024d,68
*none*: 0x024e,69
*none*: 0x024f,70
Remap: 0x0138,73
*none*: 0x0250,74
*none*: 0x0116,75
DoneRemap: 0x0139,80
*none*: 0x0251,81
*none*: 0x0252,82
[IOP.mc,v]
IOPIdle: 0x0027,86
*none*: 0x005f,87
*none*: 0x002c,88
CReadMem: 0x0021,158
IOPSubc2: 0x0069,323
*none*: 0x006a,324
*none*: 0x006b,327
*none*: 0x006c,328
*none*: 0x006d,329
*none*: 0x006e,332
*none*: 0x006f,333
*none*: 0x0070,334
IOPSub2: 0x0022,339
*none*: 0x0071,340
*none*: 0x0072,341
*none*: 0x0073,344
*none*: 0x0013,345
*none*: 0x0011,160
RFirst: 0x0046,165
*none*: 0x0047,166
*none*: 0x0031,167
RLoByte: 0x0048,172
IOPSubc1: 0x0020,322
*none*: 0x0010,102
*none*: 0x002d,106
*none*: 0x002e,107
WLo3: 0x0019,119
WHiByte: 0x002f,112
*none*: 0x0030,113
*none*: 0x003c,114
WLo3Last: 0x003d,123
WLo: 0x0032,117
*none*: 0x0034,118

572
D/CP/Source/Main_map.txt Normal file
View File

@@ -0,0 +1,572 @@
#
# This file maps assembly source symbols to PROM/microcode addresses and source lines, forming a crude
# symbol table.
#
# Each source file is given a header a la:
# [FooSource.asm]
# (with brackets)
# Each line's syntax is:
# <symbol name 1>, .. , <symbol name N>: <address or value (hex)>,<line number(decimal) in current source file>
# where '*none*' is a special symbol name meaning no symbol mapping is present.
[IOPMain.mc,v]
IOPIdle: 0x0ecc,57
*none*: 0x014f,58
IOPNoop: 0x061c,289
*none*: 0x0c7f,61
*none*: 0x0404,62
*none*: 0x0c74,65
*none*: 0x0ecd,66
*none*: 0x0ece,67
*none*: 0x04a5,71
rTmpLRot8: 0x0226,287
*none*: 0x0c75,75
*none*: 0x0c76,79
*none*: 0x0ecf,80
CalculateLast: 0x0ed3,117
*none*: 0x0ed4,118
TestCarry: 0x0ed5,120
MapLast: 0x092e,134
*none*: 0x0ed7,135
*none*: 0x0ed8,136
*none*: 0x0ed9,141
*none*: 0x0eda,142
*none*: 0x0edb,143
*none*: 0x0edc,145
*none*: 0x0edd,146
*none*: 0x0ede,147
*none*: 0x0c70,178
MapVirt3: 0x0890,180
*none*: 0x0ee5,183
*none*: 0x0ee6,184
*none*: 0x0ee7,185
*none*: 0x0ee8,190
*none*: 0x0ee9,191
*none*: 0x061a,192
WriteBlock: 0x07ca,201
*none*: 0x0eea,202
*none*: 0x0942,203
WriteBlockLoop: 0x0eeb,206
*none*: 0x0eec,207
*none*: 0x0699,208
WritePageCross: 0x069a,232
[MultiBankStartMesa.mc,v]
*none*: 0x07fc,83
*none*: 0x07fd,84
SetMDS: 0x0aff,89
*none*: 0x0dea,90
*none*: 0x0deb,92
*none*: 0x0dec,98
*none*: 0x0ded,99
*none*: 0x0dee,101
*none*: 0x0def,102
*none*: 0x0df0,103
*none*: 0x0df1,118
*none*: 0x0df2,119
*none*: 0x0df3,120
*none*: 0x0df4,122
*none*: 0x0df5,123
*none*: 0x0df6,124
*none*: 0x0df7,127
*none*: 0x0df8,128
*none*: 0x0df9,129
*none*: 0x0dfa,131
*none*: 0x0904,132
*none*: 0x0dfb,133
KeyDone: 0x0905,149
*none*: 0x0dfc,150
*none*: 0x0dfd,154
*none*: 0x0dfe,155
*none*: 0x0dff,156
LastChance: 0x0e00,159
*none*: 0x0e01,160
*none*: 0x0e02,161
[Xfer.mc,v]
XFER: 0x0b22,484
XferIndirect: 0x042e,517
*none*: 0x0d8d,518
*none*: 0x018c,520
*none*: 0x0701,521
XReadx: 0x07e1,969
XReady: 0x09f1,971
XReadz: 0x0903,972
*none*: 0x04da,973
*none*: 0x038c,523
*none*: 0x0d8e,524
*none*: 0x0d8f,525
*none*: 0x0d90,527
*none*: 0x042c,488
*none*: 0x0d85,489
*none*: 0x08e6,491
ControlTrap: 0x08e7,743
*none*: 0x02d7,744
TH1: 0x0dc4,745
TH: 0x0dc9,756
*none*: 0x0dca,757
Tha: 0x0920,758
*none*: 0x0dcb,760
THb: 0x0788,761
THc: 0x0dcc,765
*none*: 0x0dcd,767
*none*: 0x0dce,768
*none*: 0x0dcf,769
THx: 0x0619,775
*none*: 0x0dd2,776
*none*: 0x0dd3,777
THd: 0x0330,779
*none*: 0x08a1,780
TrapGo: 0x007a,782
*none*: 0x07fa,784
*none*: 0x0dd4,785
*none*: 0x0dd5,786
*none*: 0x0dd6,788
KFCBa: 0x0d5e,276
*none*: 0x0d5f,277
*none*: 0x0d60,279
*none*: 0x0402,280
GLe: 0x0ddc,894
*none*: 0x04fc,896
*none*: 0x020c,897
*none*: 0x020d,898
*none*: 0x0ddd,899
*none*: 0x0dde,901
GLc: 0x0443,902
*none*: 0x04d2,903
XFMUD: 0x0765,493
StashPC0: 0x0940,842
*none*: 0x07c1,847
*none*: 0x0349,848
*none*: 0x0334,850
*none*: 0x0635,852
StShift: 0x0dd8,855
*none*: 0x0dd9,856
StashPCb: 0x04d0,859
*none*: 0x027a,860
*none*: 0x0767,495
*none*: 0x0d86,496
*none*: 0x020a,498
*none*: 0x0d87,499
*none*: 0x0d88,500
LGC: 0x0dae,656
LGCx: 0x0daf,657
*none*: 0x022a,658
SameG: 0x04fb,701
*none*: 0x033d,702
sgOdd: 0x048e,703
sgEven: 0x048f,704
sg: 0x0dbe,706
*none*: 0x0dbf,707
*none*: 0x07f8,709
sgY: 0x07f9,708
UnboundTrap: 0x09fd,748
*none*: 0x062b,749
*none*: 0x0dc5,750
*none*: 0x0dc6,752
*none*: 0x0dc7,753
*none*: 0x0dc8,754
XferProc3: 0x042f,545
*none*: 0x018d,547
XNPa: 0x07f0,549
*none*: 0x04fa,660
LGCb: 0x07f5,663
*none*: 0x0db0,664
*none*: 0x0db1,666
*none*: 0x04a6,667
*none*: 0x0db2,668
XCa: 0x08ec,671
*none*: 0x0db3,672
*none*: 0x0adf,674
xcOdd: 0x0ade,673
XMapG: 0x0db4,676
*none*: 0x0db5,677
*none*: 0x0db6,678
*none*: 0x0db7,680
LGCd: 0x07f7,682
*none*: 0x0db8,683
*none*: 0x0db9,685
*none*: 0x0dba,686
*none*: 0x0dbb,687
*none*: 0x026e,689
*none*: 0x0dbc,690
*none*: 0x0dbd,691
*none*: 0x09fe,693
XCe: 0x08ef,694
XCd: 0x08ee,698
*none*: 0x01bb,696
*none*: 0x01ba,695
XAlloc: 0x02ad,552
AllocSub: 0x0483,925
*none*: 0x0347,926
Alloc1: 0x0217,928
AllocMUD1: 0x07a5,952
*none*: 0x07a7,929
*none*: 0x0de1,930
*none*: 0x0de2,932
AV0: 0x04cc,933
*none*: 0x0368,934
*none*: 0x0218,936
*none*: 0x0655,953
*none*: 0x0657,937
*none*: 0x0de3,938
*none*: 0x0de4,940
*none*: 0x04d8,941
*none*: 0x003d,554
*none*: 0x0d92,556
*none*: 0x0d93,557
*none*: 0x0d94,558
*none*: 0x0d95,560
XPCalI: 0x032e,564
XPSD: 0x0d9a,579
*none*: 0x0d9b,581
*none*: 0x04b6,582
XferDone: 0x044e,617
XTail: 0x0da3,619
*none*: 0x0616,620
[CommonSubs.mc,v]
WMapFix: 0x07df,83
*none*: 0x0160,84
WMaps: 0x0354,86
WMapb: 0x02d4,91
*none*: 0x0acd,247
*none*: 0x0710,246
*none*: 0x0ace,248
*none*: 0x000a,249
[Write.mc,v]
W: 0x0211,83
*none*: 0x0463,84
WMUD: 0x0461,86
[LoadStore.mc,v]
SLa: 0x0669,296
*none*: 0x0504,185
LLn: 0x00c6,195
LLa: 0x00e1,196
LLb: 0x00e2,197
@@PLDB: 0x0533,270
*none*: 0x0240,271
PLDBa: 0x0359,272
PLDB2: 0x0b14,275
PLBx: 0x0125,254
PLa: 0x04e9,255
[Refill.mc,v]
OpTable: 0x0500,126
*none*: 0x0c78,127
NoRCross: 0x0248,130
RefillE: 0x0400,119
*none*: 0x0228,120
*none*: 0x016f,281
*none*: 0x0740,282
StackErr: 0x0492,299
DISPNIonly: 0x0c85,261
UpdatePC: 0x012f,156
*none*: 0x0c79,157
*none*: 0x0149,158
RReMap: 0x0c7a,160
*none*: 0x0471,161
JRedo: 0x09ff,189
*none*: 0x0889,190
*none*: 0x0476,191
ECross: 0x03c0,166
JCross: 0x03cf,187
[Misc.mc,v]
@@ESC: 0x05f8,70
*none*: 0x0b37,71
*none*: 0x08b7,79
@@WRMP: 0x0a67,349
*none*: 0x0b5d,350
*none*: 0x0b68,351
*none*: 0x0b6a,353
*none*: 0x0b6b,354
@@WRWDC: 0x0a63,333
WRx: 0x0b53,311
*none*: 0x08b1,73
*none*: 0x08b0,72
@@GMF: 0x08f9,261
*none*: 0x0161,262
SMFa: 0x02c0,229
*none*: 0x0b46,231
*none*: 0x0b47,232
*none*: 0x0b48,233
*none*: 0x0b49,235
*none*: 0x0b4a,236
*none*: 0x0b4b,237
*none*: 0x0b4c,238
*none*: 0x06c4,240
*none*: 0x0433,246
*none*: 0x0b4d,248
*none*: 0x0b4e,249
*none*: 0x0b4f,250
*none*: 0x0392,252
GMFa: 0x0719,264
SMFd: 0x0718,253
*none*: 0x0b50,254
SMd: 0x0b44,211
*none*: 0x03ae,218
ESC0n: 0x08f0,93
@@SM: 0x08f7,199
*none*: 0x0b3a,200
*none*: 0x0b3b,201
*none*: 0x0b3c,203
*none*: 0x0b3d,204
*none*: 0x0b3e,205
*none*: 0x0b41,207
*none*: 0x0b42,208
*none*: 0x0b43,209
SMc: 0x03a8,212
@@SMF: 0x08f8,227
*none*: 0x0b45,228
*none*: 0x08b8,80
@@INPUT: 0x0910,271
*none*: 0x0b51,272
*none*: 0x0a83,276
[Stack.mc,v]
@@DSHIFT: 0x0857,401
*none*: 0x0cc1,402
DSa: 0x079c,415
*none*: 0x02a2,417
*none*: 0x0cc3,418
DSc: 0x08a6,407
*none*: 0x047a,409
*none*: 0x049a,412
@@UDCMP: 0x05be,576
*none*: 0x0ce2,577
comp: 0x0ce3,578
*none*: 0x0ce4,580
CHighNE: 0x08ae,582
CompG: 0x07b9,585
[Jump.mc,v]
@@JGEB: 0x0591,292
*none*: 0x0adc,293
*none*: 0x0ae4,303
jNoOv: 0x03d6,305
jOv: 0x03d7,306
jc22: 0x000c,201
jT: 0x0716,422
jF: 0x0717,423
NoJUmp: 0x0033,474
jPop2Incr2: 0x089d,479
@@JB: 0x0588,137
*none*: 0x0180,138
JPosOdd: 0x0609,428
JPos: 0x0af6,434
jnPNoCross: 0x0119,439
JPtr1Pop0: 0x0872,459
Jgo: 0x0af9,469
*none*: 0x06ab,430
@@JIW: 0x05a1,361
*none*: 0x00a4,362
jiCom: 0x0aee,365
*none*: 0x0aef,367
*none*: 0x0714,368
*none*: 0x004d,369
*none*: 0x01e0,371
*none*: 0x0af0,372
*none*: 0x0af1,373
*none*: 0x0af2,376
*none*: 0x0af3,377
*none*: 0x0af4,378
jiRedo: 0x04fd,380
*none*: 0x0805,381
*none*: 0x0350,382
jibL: 0x0332,384
ji: 0x012e,388
jwPos: 0x04c9,148
*none*: 0x0ad3,151
jwOdd: 0x03d3,154
jwCross: 0x0ad4,157
JPtr0Pop2: 0x087f,467
jiwL: 0x0336,386
[DiskDlionA.mc,v]
GetCSB: 0x0ef8,84
*none*: 0x070f,85
*none*: 0x0ef9,86
*none*: 0x0efa,89
*none*: 0x0efb,91
*none*: 0x00ba,93
NewIOCB: 0x0efc,97
*none*: 0x0efd,98
GoodIOCB: 0x0946,100
*none*: 0x0947,101
StartIOCB: 0x0efe,105
*none*: 0x0eff,106
*none*: 0x0f00,108
GetCmd: 0x08d0,111
GetCmdC2: 0x0f01,112
GetCmdC3: 0x0346,113
FetArg: 0x074f,118
SameC2s: 0x0158,123
SendCtlWd: 0x0159,153
*none*: 0x0386,156
SameC3s: 0x0366,124
Inr: 0x0768,133
*none*: 0x0f02,135
*none*: 0x0f03,136
*none*: 0x0f04,139
*none*: 0x0f05,140
IncBr: 0x0f06,141
DoInc: 0x08d1,145
FinLdReg: 0x0f1d,247
LoadPr: 0x076d,162
LoadRCLpC2: 0x0f08,170
LoadRCLpC3: 0x06c2,172
LoadRCLp: 0x0f07,168
StartRALp: 0x06c3,176
LoadRALp: 0x0f09,179
LoadRALpC2: 0x0f0a,182
LoadRALpC3: 0x06ba,184
FinLd: 0x06bb,186
*none*: 0x0f0b,188
*none*: 0x0f0c,189
*none*: 0x0f0d,196
*none*: 0x0f0e,198
*none*: 0x0f0f,199
SaveHeadSect: 0x0948,206
IsHeaderRead: 0x0949,202
*none*: 0x0f10,210
*none*: 0x0f11,213
LdHeadSectC3: 0x03a6,214
SetUHeadSect: 0x0f12,217
FixupDone: 0x0f13,220
*none*: 0x0f14,221
*none*: 0x0f15,223
SavePgNum: 0x08d2,228
IsLabelRead: 0x08d3,225
FormHdNotOkMsk: 0x0f1b,243
*none*: 0x0f1c,244
*none*: 0x015a,255
*none*: 0x03c6,256
NewSector: 0x094a,258
*none*: 0x0f1e,259
*none*: 0x0f1f,260
*none*: 0x0f20,262
*none*: 0x0f21,263
*none*: 0x0f22,264
NewHeader: 0x08d4,267
*none*: 0x0f23,269
*none*: 0x0a40,270
HeaderRet: 0x0050,273
*none*: 0x0f24,275
*none*: 0x0f25,276
HeaderWrong: 0x094c,278
DoLabel: 0x094d,295
*none*: 0x0f2a,296
*none*: 0x0f2b,297
*none*: 0x0f2c,299
*none*: 0x0f2d,300
*none*: 0x0861,301
LabelRet: 0x0051,304
*none*: 0x0f2e,305
*none*: 0x0f2f,306
DoData: 0x094f,318
LabelQuit: 0x094e,315
*none*: 0x0f30,320
NoIncrDatPtr: 0x06f2,322
IncrDatPtr: 0x06f3,323
MapDataAddr: 0x0f31,326
*none*: 0x0f32,329
GetPhysDatAddr: 0x0406,331
*none*: 0x0f33,335
*none*: 0x0f34,336
*none*: 0x0602,337
*none*: 0x0f26,280
*none*: 0x0f27,281
TstSeenAll: 0x08d7,287
HeaderQuit: 0x08d6,284
*none*: 0x0f28,288
*none*: 0x0f29,289
NotFound: 0x08d5,292
[DiskDlionB.mc,v]
InitRegs: 0x076f,321
*none*: 0x0f7b,322
*none*: 0x0f7c,323
*none*: 0x0f7d,325
FinishIOCB: 0x076e,233
*none*: 0x0f61,235
*none*: 0x01fa,237
*none*: 0x0f62,240
*none*: 0x0f63,241
*none*: 0x021a,242
*none*: 0x0f64,245
*none*: 0x0f65,246
*none*: 0x0f66,247
*none*: 0x0f67,250
*none*: 0x0f68,251
*none*: 0x0f69,252
ComposeStat: 0x096b,263
*none*: 0x0f6c,264
*none*: 0x0f6d,268
*none*: 0x0f6e,271
*none*: 0x0f70,272
*none*: 0x0f71,273
*none*: 0x0f72,276
*none*: 0x0f73,277
MemError: 0x07ef,279
LastIOCB: 0x0973,286
ResetFirmwareBusy: 0x096d,291
GetIntrMask: 0x0f74,300
*none*: 0x0f75,303
*none*: 0x0f76,304
*none*: 0x0f78,305
*none*: 0x0f79,309
*none*: 0x0f7a,310
QuitNow: 0x0cde,313
TransferField: 0x0f48,87
*none*: 0x0f49,88
*none*: 0x0f4a,89
*none*: 0x0f4b,91
*none*: 0x0f4c,92
SetupWrt: 0x0c9f,162
*none*: 0x0f55,164
SyncLp2: 0x0f56,169
*none*: 0x0f57,170
*none*: 0x0936,168
FinSync: 0x0937,173
*none*: 0x0f58,175
*none*: 0x0f59,177
SetupHd: 0x0968,181
MakeSyncAdrMk: 0x0f5a,185
*none*: 0x0f5b,186
*none*: 0x0f5c,190
*none*: 0x0f5d,191
InitWrtC3: 0x046e,192
WrtVerLp: 0x0970,197
WrtVerLpC2: 0x0f5e,198
WrtVerLpC3: 0x01f2,199
FinWrtVer: 0x0971,202
*none*: 0x0f5f,203
FinWrite: 0x0cbf,207
FinVerify: 0x0cbe,205
*none*: 0x06e2,210
SndWaitC2: 0x0f60,216
SndWaitC3: 0x02d2,218
SndWait: 0x07cf,215
SndFreeze: 0x02d3,222
SetupRdVer: 0x0c9e,101
*none*: 0x0f4d,105
*none*: 0x0f4e,106
*none*: 0x0f4f,107
*none*: 0x0f50,113
*none*: 0x0f51,118
StartVer: 0x07af,121
StartRd: 0x07ad,126
StartRdC1: 0x08c1,130
ReadLpC2: 0x08dc,137
ReadLpC3: 0x046a,138
ReadLp: 0x0f52,136
FinRead: 0x08dd,147
FinReadC3: 0x011a,149
FinReadLp: 0x0f53,152
RdLastWd: 0x0966,153
*none*: 0x0f54,154
FreezeRead: 0x0967,157

View File

@@ -0,0 +1,89 @@
#
# This file maps assembly source symbols to PROM/microcode addresses and source lines, forming a crude
# symbol table.
#
# Each source file is given a header a la:
# [FooSource.asm]
# (with brackets)
# Each line's syntax is:
# <symbol name 1>, .. , <symbol name N>: <address or value (hex)>,<line number(decimal) in current source file>
# where '*none*' is a special symbol name meaning no symbol mapping is present.
[MoonCycle.mc,v]
START: 0x0090,155
*none*: 0x011f,156
*none*: 0x0091,157
*none*: 0x0092,159
*none*: 0x0093,160
*none*: 0x0094,161
*none*: 0x0095,163
*none*: 0x0096,164
*none*: 0x0097,165
*none*: 0x0098,167
*none*: 0x0099,168
*none*: 0x009a,169
*none*: 0x009b,171
*none*: 0x009c,172
START1: 0x0036,173
START2: 0x0037,174
START3: 0x009d,178
*none*: 0x009e,179
*none*: 0x0020,180
Delay: 0x0052,304
*none*: 0x00c9,305
*none*: 0x00ca,306
DelENRET: 0x0053,308
*none*: 0x00cb,309
*none*: 0x0017,310
Task1: 0x0055,40
Task2: 0x0067,62
Task3: 0x0071,84
Task4: 0x007b,106
Task5: 0x0086,128
*none*: 0x0010,182
*none*: 0x00a0,183
*none*: 0x0040,184
CkRZero: 0x00c7,291
*none*: 0x00c8,292
ErrorTask1: 0x002d,298
*none*: 0x002c,293
ErrorTask2: 0x003b,299
*none*: 0x003a,294
ErrorTask3: 0x002f,300
*none*: 0x002e,295
ErrorTask4: 0x0051,301
*none*: 0x0050,296
ErrorTask5: 0x0043,302
CkENRET: 0x0042,312
*none*: 0x00cc,313
*none*: 0x001e,314
*none*: 0x0060,187
*none*: 0x00a1,188
*none*: 0x0001,189
*none*: 0x005f,41
*none*: 0x0056,42
Task1A: 0x0018,44
Task1B: 0x0019,48
*none*: 0x0059,49
*none*: 0x005a,50
Task1C: 0x0008,52
*none*: 0x005b,53
*none*: 0x005c,54
Task1D: 0x0009,56
*none*: 0x005d,57
*none*: 0x005e,58
*none*: 0x0057,45
*none*: 0x0058,46
*none*: 0x0011,191
*none*: 0x00a2,192
*none*: 0x0021,193
*none*: 0x0061,196
*none*: 0x00a3,197
*none*: 0x0002,198
*none*: 0x0012,200
*none*: 0x00a4,201
*none*: 0x0022,202
*none*: 0x0062,205
*none*: 0x00a5,206
*none*: 0x0003,207

View File

@@ -0,0 +1,267 @@
#
# This file maps assembly source symbols to PROM/microcode addresses and source lines, forming a crude
# symbol table.
#
# Each source file is given a header a la:
# [FooSource.asm]
# (with brackets)
# Each line's syntax is:
# <symbol name 1>, .. , <symbol name N>: <address or value (hex)>,<line number(decimal) in current source file>
# where '*none*' is a special symbol name meaning no symbol mapping is present.
[MoonEthBTest.mc,v]
CkTest: 0x0074,699
*none*: 0x00bf,700
*none*: 0x0196,701
Go: 0x00a9,123
*none*: 0x009f,124
*none*: 0x00aa,125
*none*: 0x00ab,127
*none*: 0x00ac,128
*none*: 0x00ad,129
Go1: 0x0008,131
Go2: 0x0009,134
RunGood: 0x0fff,484
*none*: 0x00ae,135
*none*: 0x00af,136
*none*: 0x00b3,138
*none*: 0x00b4,139
*none*: 0x00b5,140
*none*: 0x00b6,144
*none*: 0x00b7,145
*none*: 0x00b8,146
*none*: 0x00b9,150
*none*: 0x00ba,151
*none*: 0x00bb,152
*none*: 0x00bc,154
*none*: 0x00bd,155
*none*: 0x00be,156
Pause: 0x00c8,159
*none*: 0x00c9,160
*none*: 0x00ca,161
Btest0: 0x00cb,164
*none*: 0x00cc,165
*none*: 0x00cd,166
*none*: 0x00ce,168
*none*: 0x00cf,169
*none*: 0x00d3,170
*none*: 0x00d4,172
*none*: 0x00d5,173
Badt0: 0x0014,640
t0Stop: 0x00e0,641
Btest1: 0x0015,176
*none*: 0x00d6,178
*none*: 0x00d7,179
*none*: 0x00d8,180
*none*: 0x00d9,182
*none*: 0x00da,183
*none*: 0x00db,184
Btest2: 0x000d,188
Badt1: 0x000c,643
t1Stop: 0x00e1,644
*none*: 0x00dc,189
*none*: 0x00dd,190
*none*: 0x00de,192
*none*: 0x00e3,193
*none*: 0x00e4,194
*none*: 0x00e5,196
*none*: 0x00e6,197
*none*: 0x00e7,198
*none*: 0x00e8,200
*none*: 0x00e9,201
*none*: 0x0019,202
Badt2: 0x0018,646
t2Stop: 0x00e2,647
*none*: 0x00ea,204
*none*: 0x00eb,205
*none*: 0x0020,206
FifoCk: 0x00ef,225
*none*: 0x00f3,226
*none*: 0x00f4,227
FifoCk1: 0x0025,229
*none*: 0x00f5,230
*none*: 0x00f7,231
*none*: 0x00f8,233
*none*: 0x00f9,234
*none*: 0x00fa,235
FifoRead: 0x0024,238
FifoRead1: 0x001a,240
*none*: 0x00fb,241
FifoErr: 0x0026,246
*none*: 0x0027,242
FifoDone: 0x001b,250
*none*: 0x003c,251
*none*: 0x0010,208
*none*: 0x00ec,209
*none*: 0x0001,210
*none*: 0x0011,212
*none*: 0x0012,216
*none*: 0x0033,247
*none*: 0x001e,248
FifoErr0: 0x0100,649
*none*: 0x0101,650
*none*: 0x00ed,213
*none*: 0x0002,214
*none*: 0x00ee,218
*none*: 0x0003,219
Btest4: 0x0013,258
*none*: 0x00fc,259
*none*: 0x00fd,260
*none*: 0x00a0,263
*none*: 0x00fe,264
*none*: 0x0104,265
*none*: 0x0105,268
*none*: 0x0106,269
*none*: 0x0107,270
*none*: 0x0108,272
*none*: 0x0109,273
*none*: 0x0040,274
MemStore: 0x0173,574
*none*: 0x0174,575
*none*: 0x0175,576
*none*: 0x0176,578
*none*: 0x0177,579
Pattern: 0x0070,581
*none*: 0x0071,582
Sun1: 0x017b,591
*none*: 0x017c,592
*none*: 0x017d,593
Block: 0x017e,595
*none*: 0x0072,583
*none*: 0x0073,584
ADD1: 0x0178,587
*none*: 0x017f,596
*none*: 0x0064,597
MemDone: 0x0065,600
*none*: 0x0183,601
*none*: 0x0184,602
*none*: 0x009b,603
*none*: 0x00c0,276
EndWB3: 0x010a,278
*none*: 0x010b,281
*none*: 0x010c,283
*none*: 0x010d,284
*none*: 0x0021,285
*none*: 0x00c1,287
*none*: 0x010e,288
*none*: 0x0022,289
*none*: 0x00c2,293
ClrFin: 0x00c3,302
*none*: 0x010f,294
*none*: 0x0110,295
ClrBuf: 0x0111,297
*none*: 0x0112,298
*none*: 0x0023,299
*none*: 0x0113,303
*none*: 0x0114,304
*none*: 0x0115,306
*none*: 0x0116,309
*none*: 0x0117,310
*none*: 0x0118,311
*none*: 0x0119,316
*none*: 0x0060,317
Delay: 0x0044,488
*none*: 0x015e,490
*none*: 0x003a,491
LLDone: 0x003b,495
TimeOut: 0x0045,525
*none*: 0x0075,703
*none*: 0x0197,704
DoTst: 0x001c,706
LoopBkS: 0x0198,713
*none*: 0x0199,714
*none*: 0x019a,715
*none*: 0x019b,718
*none*: 0x019c,719
*none*: 0x019d,720
*none*: 0x019e,722
*none*: 0x019f,723
*none*: 0x01a0,724
*none*: 0x01a1,726
*none*: 0x01a2,727
*none*: 0x01a3,728
*none*: 0x01a4,730
*none*: 0x01a5,731
*none*: 0x01a6,732
*none*: 0x01a7,734
*none*: 0x01a8,737
LData: 0x000e,738
LbpLoop: 0x0068,736
*none*: 0x0069,740
*none*: 0x01a9,741
*none*: 0x01aa,742
*none*: 0x01ab,744
*none*: 0x01ac,745
*none*: 0x01ad,746
Wait: 0x0078,761
*none*: 0x01b3,762
*none*: 0x006a,763
AttenUp: 0x006b,766
*none*: 0x007a,768
*none*: 0x01b4,769
*none*: 0x01b5,770
ToReTry: 0x006d,774
*none*: 0x006c,772
ReTryA: 0x01ae,752
T7Time: 0x007b,779
ReadSt: 0x01b6,780
*none*: 0x01b7,781
*none*: 0x01b8,783
*none*: 0x007c,784
T7Attn1: 0x006f,788
*none*: 0x006e,785
TrnBad: 0x01b9,793
LoopRec: 0x01bc,798
*none*: 0x01bd,799
*none*: 0x01be,800
*none*: 0x01bf,802
GetWd: 0x0082,805
RData: 0x00df,806
AddWd: 0x01c0,804
*none*: 0x0043,498
*none*: 0x015f,499
*none*: 0x0054,501
*none*: 0x0163,503
*none*: 0x0046,505
*none*: 0x0164,506
CkLoop: 0x0056,508
*none*: 0x0165,509
*none*: 0x0166,512
StatusBad: 0x0048,561
*none*: 0x0053,562
*none*: 0x0098,563
Badt44: 0x00d0,670
*none*: 0x0049,515
*none*: 0x0059,517
*none*: 0x0167,518
*none*: 0x004b,519
*none*: 0x0168,520
*none*: 0x003e,521
CkLoopL: 0x0030,320
*none*: 0x011a,321
*none*: 0x0080,322
MemCheck: 0x0185,606
*none*: 0x0186,607
*none*: 0x0187,608
MemCheck1: 0x0188,610
*none*: 0x0189,611
*none*: 0x018a,612
*none*: 0x018b,614
*none*: 0x018c,615
*none*: 0x018d,616
*none*: 0x018e,618
*none*: 0x018f,619
CheckErr: 0x005c,620
Check: 0x005d,626
*none*: 0x0192,627
*none*: 0x0193,628
*none*: 0x0066,629
*none*: 0x0067,632
*none*: 0x0194,633
*none*: 0x0195,634
*none*: 0x009d,635
WdsGood: 0x0050,326
*none*: 0x011b,327
*none*: 0x011c,328
Btest5: 0x00a1,335

View File

@@ -0,0 +1,17 @@
#
# This file maps assembly source symbols to PROM/microcode addresses and source lines, forming a crude
# symbol table.
#
# Each source file is given a header a la:
# [FooSource.asm]
# (with brackets)
# Each line's syntax is:
# <symbol name 1>, .. , <symbol name N>: <address or value (hex)>,<line number(decimal) in current source file>
# where '*none*' is a special symbol name meaning no symbol mapping is present.
[MoonPortIn.mc,v]
START1: 0x003f,49
*none*: 0x0004,50
Wait: 0x0002,52
*none*: 0x0005,53

View File

@@ -0,0 +1,27 @@
#
# This file maps assembly source symbols to PROM/microcode addresses and source lines, forming a crude
# symbol table.
#
# Each source file is given a header a la:
# [FooSource.asm]
# (with brackets)
# Each line's syntax is:
# <symbol name 1>, .. , <symbol name N>: <address or value (hex)>,<line number(decimal) in current source file>
# where '*none*' is a special symbol name meaning no symbol mapping is present.
[MoonPortOut.mc,v]
*none*: 0x003f,48
START: 0x0001,46
*none*: 0x0004,49
*none*: 0x0005,50
Wait: 0x0002,52
*none*: 0x0006,53
In: 0x0003,55
*none*: 0x0007,56
*none*: 0x0008,57
Bad: 0x0010,59
*none*: 0x0011,60
*none*: 0x0009,61
*none*: 0x000a,62
Wait1: 0x0ffe,64

View File

@@ -0,0 +1,42 @@
#
# This file maps assembly source symbols to PROM/microcode addresses and source lines, forming a crude
# symbol table.
#
# Each source file is given a header a la:
# [FooSource.asm]
# (with brackets)
# Each line's syntax is:
# <symbol name 1>, .. , <symbol name N>: <address or value (hex)>,<line number(decimal) in current source file>
# where '*none*' is a special symbol name meaning no symbol mapping is present.
[Protected.mc,v]
*none*: 0x001a,77
Trap: 0x0000,59
infinite: 0x0001,71
[IOPBoot.mc,v]
*none*: 0x0020,62
*none*: 0x005f,63
*none*: 0x0023,64
IOPIdle: 0x000d,70
IOPIdlex: 0x007f,72
*none*: 0x0026,75
CReadMem: 0x0009,84
ReadWordx: 0x0005,185
Noop1: 0x0014,208
*none*: 0x0070,187
*none*: 0x001b,145
*none*: 0x002e,149
*none*: 0x0032,150
*none*: 0x0013,188
*none*: 0x0019,87
*none*: 0x0006,90
Noop2: 0x000b,207
*none*: 0x0076,95
*none*: 0x0015,97
SendAddress: 0x0075,103
*none*: 0x0021,105
*none*: 0x0029,106
RLoByte: 0x0071,113
*none*: 0x0028,114

View File

@@ -0,0 +1,46 @@
#
# This file maps assembly source symbols to PROM/microcode addresses and source lines, forming a crude
# symbol table.
#
# Each source file is given a header a la:
# [FooSource.asm]
# (with brackets)
# Each line's syntax is:
# <symbol name 1>, .. , <symbol name N>: <address or value (hex)>,<line number(decimal) in current source file>
# where '*none*' is a special symbol name meaning no symbol mapping is present.
[Phase0.mc,v]
go: 0x0108,97
*none*: 0x011f,98
*none*: 0x0146,99
clear0: 0x0128,102
*none*: 0x0148,103
*none*: 0x014a,104
Trident/SA: 0x013d,115
*none*: 0x013c,108
*none*: 0x014e,109
*none*: 0x0168,110
Trident: 0x013f,117
enableIOP: 0x01ae,127
*none*: 0x01b0,128
*none*: 0x01c6,129
*none*: 0x0130,134
*none*: 0x01c7,135
*none*: 0x01c8,136
*none*: 0x016a,116
SA: 0x013e,118
pollDevices: 0x018a,120
*none*: 0x019a,121
*none*: 0x012b,123
*none*: 0x012a,124
[DiskBootDLion.mc,v]
VerifyLp: 0x0255,97
*none*: 0x0256,99
MoreVerify: 0x01d4,101
FinVerify: 0x01d5,105
*none*: 0x0160,109
FindSA1Sect0: 0x013a,110
FindSA1Sect0Lp: 0x01a8,94
Wait2: 0x013b,264

View File

@@ -0,0 +1,18 @@
#
# This file maps assembly source symbols to PROM/microcode addresses and source lines, forming a crude
# symbol table.
#
# Each source file is given a header a la:
# [FooSource.asm]
# (with brackets)
# Each line's syntax is:
# <symbol name 1>, .. , <symbol name N>: <address or value (hex)>,<line number(decimal) in current source file>
# where '*none*' is a special symbol name meaning no symbol mapping is present.
[MoonSunlightO2.mc,v]
Go: 0x00fe,46
*none*: 0x007f,47
*none*: 0x0100,48
*none*: 0x0101,49
*none*: 0x0016,51

View File

@@ -0,0 +1,164 @@
#
# This file maps assembly source symbols to PROM/microcode addresses and source lines, forming a crude
# symbol table.
#
# Each source file is given a header a la:
# [FooSource.asm]
# (with brackets)
# Each line's syntax is:
# <symbol name 1>, .. , <symbol name N>: <address or value (hex)>,<line number(decimal) in current source file>
# where '*none*' is a special symbol name meaning no symbol mapping is present.
[MoonSunlightO4.mc,v]
ibTests: 0x0066,61
*none*: 0x008f,62
*none*: 0x0067,63
*none*: 0x0068,64
*none*: 0x0069,66
*none*: 0x006a,67
*none*: 0x006b,68
IBPtr0Bad: 0x0018,257
IBPtr0Good: 0x0019,71
*none*: 0x006c,72
*none*: 0x006d,73
*none*: 0x006e,75
*none*: 0x0070,76
*none*: 0x0071,77
IBPtr1Bad: 0x000c,258
IBPtr1Good: 0x000d,80
*none*: 0x0072,81
*none*: 0x0073,82
*none*: 0x0074,84
*none*: 0x0075,85
*none*: 0x0076,86
*none*: 0x0077,88
*none*: 0x0078,89
*none*: 0x0079,90
IBLeftaBad: 0x0030,259
IBLeftaGood: 0x0031,92
IBLeftbBad: 0x0020,261
IBLeftbGood: 0x0021,93
*none*: 0x007a,94
*none*: 0x007b,96
*none*: 0x007c,97
IBRaBad: 0x0032,262
IBRaGood: 0x0033,98
IBRbBad: 0x0024,264
IBRbGood: 0x0025,101
*none*: 0x007d,102
*none*: 0x007e,103
IBHighBad: 0x0034,265
IBHighGood: 0x0035,105
*none*: 0x007f,106
*none*: 0x0080,107
IBLowBad: 0x0026,266
IBLowGood: 0x0027,110
*none*: 0x0081,111
*none*: 0x0082,113
*none*: 0x0083,114
*none*: 0x0002,115
*none*: 0x0084,117
*none*: 0x0085,118
*none*: 0x0006,119
*none*: 0x0086,121
*none*: 0x0087,122
*none*: 0x0088,123
*none*: 0x0089,125
IBPtr1xBad: 0x0036,267
IBPtr1xGood: 0x0037,128
*none*: 0x008a,129
*none*: 0x008b,130
*none*: 0x008c,132
*none*: 0x008d,133
NETrap: 0x0500,135
*none*: 0x008e,136
NETrapBad: 0x0028,268
NETrapGood: 0x0029,140
*none*: 0x0090,141
*none*: 0x0091,142
*none*: 0x0092,143
*none*: 0x0093,145
*none*: 0x0094,146
*none*: 0x0095,147
IBaBad: 0x0038,269
IBaGood: 0x0039,149
*none*: 0x0096,150
*none*: 0x0097,151
IBbBad: 0x002a,270
IBbGood: 0x002b,153
*none*: 0x0098,154
*none*: 0x0099,155
IBcBad: 0x003a,271
IBcGood: 0x003b,157
*none*: 0x009a,158
*none*: 0x009b,159
IBdBad: 0x0040,272
IBdGood: 0x0041,161
*none*: 0x009c,162
*none*: 0x009d,163
ETrap: 0x0400,165
*none*: 0x009e,166
ETrapBad: 0x003c,273
ETrapGood: 0x003d,170
*none*: 0x009f,171
*none*: 0x00a0,172
*none*: 0x00a1,173
NEMIntTrap: 0x0700,175
*none*: 0x0600,177
*none*: 0x00a2,178
EMTrapG: 0x0043,181
EMTrapBad: 0x0042,274
*none*: 0x00a3,182
*none*: 0x00a4,183
*none*: 0x00a5,185
*none*: 0x08af,231
IBDispaG: 0x00a6,188
*none*: 0x00a7,189
*none*: 0x00a8,191
*none*: 0x00a9,192
*none*: 0x00aa,193
*none*: 0x00ab,195
*none*: 0x00b0,196
*none*: 0x00b1,197
*none*: 0x00b2,199
*none*: 0x00b3,200
*none*: 0x00b4,201
IBDispbG: 0x085f,226
IBDispbG: 0x00b5,204
*none*: 0x00b6,205
*none*: 0x00b7,206
*none*: 0x00b8,208
*none*: 0x00b9,209
*none*: 0x08fa,247
IBDispcG: 0x00ba,212
*none*: 0x00bb,213
*none*: 0x00bc,215
*none*: 0x00bd,216
*none*: 0x00be,217
*none*: 0x08f5,242
MemTest: 0x00c7,288
*none*: 0x00c8,289
*none*: 0x00c9,291
*none*: 0x00ca,292
*none*: 0x00cb,293
*none*: 0x00cc,296
*none*: 0x00cd,297
*none*: 0x00ce,298
*none*: 0x00d0,300
*none*: 0x00d1,301
*none*: 0x00d2,302
*none*: 0x00d3,304
*none*: 0x00d4,305
*none*: 0x0045,306
SimMARBad: 0x0044,284
*none*: 0x00d5,309
*none*: 0x00d6,310
*none*: 0x0016,311
*none*: 0x00d7,313
*none*: 0x00d8,314
*none*: 0x00d9,315
*none*: 0x00da,317
*none*: 0x00db,318
MDRCanBad: 0x0052,285
*none*: 0x0053,319

View File

@@ -0,0 +1,183 @@
#
# This file maps assembly source symbols to PROM/microcode addresses and source lines, forming a crude
# symbol table.
#
# Each source file is given a header a la:
# [FooSource.asm]
# (with brackets)
# Each line's syntax is:
# <symbol name 1>, .. , <symbol name N>: <address or value (hex)>,<line number(decimal) in current source file>
# where '*none*' is a special symbol name meaning no symbol mapping is present.
[MoonSunlightO5.mc,v]
*none*: 0x002f,78
TrapTest: 0x0074,77
*none*: 0x0075,79
StkUF: 0x0076,82
*none*: 0x007f,83
WaitTrapc3: 0x0099,188
WaitTrapc1: 0x009a,189
*none*: 0x009b,190
FastTrapc3: 0x009c,191
*none*: 0x009d,192
*none*: 0x005f,45
*none*: 0x006a,46
TrapType: 0x0010,49
*none*: 0x0011,50
*none*: 0x0012,51
*none*: 0x0013,52
NotCSPar: 0x006b,54
*none*: 0x006c,55
*none*: 0x006d,58
*none*: 0x006e,59
*none*: 0x0024,60
StkSimUF: 0x0027,85
*none*: 0x0026,62
*none*: 0x0001,86
*none*: 0x001c,63
StkSim2UF: 0x001d,88
*none*: 0x0037,89
*none*: 0x0077,90
WaitTrapc2: 0x0098,187
*none*: 0x0028,64
Stk2UF: 0x0029,92
*none*: 0x0041,93
*none*: 0x0078,94
DidntTrap: 0x00cd,296
*none*: 0x0030,66
StkOF: 0x0031,96
*none*: 0x0039,97
*none*: 0x002a,67
StkOFp: 0x002b,99
*none*: 0x0043,100
*none*: 0x0079,101
*none*: 0x0032,68
StkEReg: 0x0033,104
*none*: 0x003b,105
StkEE: 0x0004,107
*none*: 0x0005,108
*none*: 0x0006,109
*none*: 0x0007,110
Memf: 0x007a,114
*none*: 0x007b,115
MWait: 0x0045,117
*none*: 0x007c,118
*none*: 0x007d,119
MemF2: 0x0044,122
*none*: 0x007e,123
*none*: 0x0080,125
*none*: 0x0081,126
*none*: 0x0082,127
*none*: 0x0083,129
*none*: 0x0084,130
*none*: 0x0085,131
*none*: 0x006f,70
*none*: 0x0072,71
*none*: 0x002c,72
MemEReg: 0x002d,134
*none*: 0x003d,135
MemEE: 0x0014,137
*none*: 0x0015,138
MemStatE: 0x0086,142
*none*: 0x0087,143
MStatBad0: 0x0009,289
*none*: 0x000b,144
*none*: 0x0088,145
IBTestc1: 0x0021,149
*none*: 0x0089,150
*none*: 0x008a,151
*none*: 0x008b,153
*none*: 0x008c,154
*none*: 0x008d,155
*none*: 0x0034,74
IBTestc2: 0x0035,158
*none*: 0x0051,159
*none*: 0x008e,160
*none*: 0x008f,162
*none*: 0x0090,163
IBEReg: 0x0047,166
*none*: 0x0053,167
IBEE: 0x0018,169
*none*: 0x0019,170
*none*: 0x001a,171
*none*: 0x001b,174
*none*: 0x0091,175
*none*: 0x0092,176
*none*: 0x0093,179
*none*: 0x0094,180
*none*: 0x0095,181
*none*: 0x0096,183
*none*: 0x0097,184
IBEMemW: 0x0054,302
CritTime: 0x0055,196
*none*: 0x009e,199
*none*: 0x009f,200
*none*: 0x00a0,201
*none*: 0x00a1,204
UZeroBrBad: 0x0048,306
UNeg: 0x0049,205
*none*: 0x00a2,206
*none*: 0x00a3,208
UNegBrBad: 0x0056,307
UStkp: 0x0057,209
*none*: 0x00a4,210
*none*: 0x00a5,213
*none*: 0x00a6,214
*none*: 0x00a7,215
*none*: 0x00a8,217
UstkpBad: 0x004a,308
URot: 0x004b,218
*none*: 0x00a9,219
*none*: 0x00aa,222
*none*: 0x00ab,223
*none*: 0x00ac,224
*none*: 0x00ad,226
*none*: 0x00ae,227
*none*: 0x0058,309
UPage: 0x0059,228
*none*: 0x00af,231
*none*: 0x00b0,232
*none*: 0x00b1,233
*none*: 0x00b2,235
MemNib: 0x00b3,236
UByPgCyBad: 0x004c,310
*none*: 0x004d,237
*none*: 0x00b4,240
*none*: 0x00b5,241
*none*: 0x00b6,242
*none*: 0x00b7,244
*none*: 0x00b8,245
*none*: 0x00b9,246
*none*: 0x00ba,248
*none*: 0x00bb,249
*none*: 0x00bc,250
MNibByBad: 0x005a,311
*none*: 0x005b,253
*none*: 0x00bd,254
*none*: 0x00be,255
*none*: 0x00bf,257
*none*: 0x00c0,258
*none*: 0x00c1,259
UFZeroT: 0x004e,261
*none*: 0x00c2,262
*none*: 0x00c3,263
*none*: 0x00c4,265
*none*: 0x00c5,266
*none*: 0x005c,267
rhPage: 0x004f,270
UFZeroBad: 0x005d,315
*none*: 0x00c6,271
*none*: 0x00c7,272
*none*: 0x00c8,274
*none*: 0x00c9,275
*none*: 0x0061,276
*none*: 0x00ca,279
RHByArBad: 0x0070,313
ByCarry: 0x0071,280
*none*: 0x00cb,281
*none*: 0x00cc,283
ByCyBrBad: 0x0062,314
*none*: 0x0063,284
NOERROR1: 0x00d3,318
NOERROR: 0x0fff,319

13
D/CP/Source/load_map.txt Normal file
View File

@@ -0,0 +1,13 @@
BootKernel,BootKernel_map.txt,fd8,fff,08db70caa4585b9a8c7f5ba9215eb34c
Phase0Protected,Phase0Protected_map.txt,000,0ff,c73c5996d9a9ac6ee37a2d92ae7f6c15
Phase0,Phase0_map.txt,100,fd7,d9d9f75e0f9539fa911488c3bd36e262
FloppyInitial,FloppyInitial_map.txt,000,fff,0bd42231451c66d8043867fd390580ac
Main,Main_map.txt,000,fd7,7f64459937a5a3104343bc7a0ad5dbd7
MoonPortIn,MoonPortIn_map.txt,000,fff,00960fada4edd268b6fc49c918d49582
MoonPortOut,MoonPortOut_map.txt,000,fff,de001dba5266a6fda17d35f88741fa1b
SunlightO2,SunlightO2_map.txt,000,fff,00a2160753bd3a6968dfffba0a8e566b
SunlightO4,SunlightO4_map.txt,000,fff,6c22068f622d7afb5d5ba9a74e2f8312
SunlightO5,SunlightO5_map.txt,000,fff,6a1971a8953f36e3777f1940e43f2d46
CPMemTest,CPMemTest_map.txt,000,fff,930c63cb19215d1140e742d86a3ca760
MoonCycle,MoonCycle_map.txt,000,fff,7936738bb3be4498cdf1f5169f35c1f7
MoonEthBTest,MoonEthBTest_map.txt,000,fff,990b5b14754ae7785838ace650384671

459
D/Configuration.cs Normal file
View File

@@ -0,0 +1,459 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using D.IOP;
using D.Logging;
using System;
using System.Collections.Specialized;
using System.IO;
using System.Reflection;
namespace D
{
public enum PlatformType
{
Windows,
Unix
}
/// <summary>
/// Encapsulates user-configurable settings. To be enhanced.
/// </summary>
public static class Configuration
{
static Configuration()
{
// Initialize things to defaults.
MemorySize = 768;
HostID = 0x0000aa012345;
ThrottleSpeed = true;
DisplayScale = 1;
SlowPhosphor = true;
HostPacketInterfaceName = String.Empty;
TODDateTime = new DateTime(1979, 12, 10);
TODDate = new DateTime(1955, 11, 5);
TODSetMode = TODPowerUpSetMode.HostTimeY2K;
switch (Environment.OSVersion.Platform)
{
case PlatformID.MacOSX:
case PlatformID.Unix:
Platform = PlatformType.Unix;
break;
default:
Platform = PlatformType.Windows;
break;
}
// See if PCap is available.
TestPCap();
try
{
ReadConfiguration();
}
catch
{
Log.Write(LogType.Error, LogComponent.Configuration,
"Unable to load configuration. Assuming default settings.");
}
//
// Sanity-check settings that need sanity-checking.
//
if (MemorySize > 1024 ||
MemorySize == 0 ||
(MemorySize % 128) != 0)
{
Log.Write(LogType.Error, LogComponent.Configuration,
"MemorySize configuration parameter is incorrect, defaulting to 768KW.");
MemorySize = 768;
}
if (DisplayScale < 1)
{
Log.Write(LogType.Error, LogComponent.Configuration,
"DisplayScale configuration parameter is incorrect, defaulting to 1.");
DisplayScale = 1;
}
}
/// <summary>
/// What kind of system we're running on. (Not technically configurable.)
/// </summary>
public static PlatformType Platform;
/// <summary>
/// System memory size, in KW.
/// </summary>
public static uint MemorySize;
/// <summary>
/// The currently loaded image for the hard disk.
/// </summary>
public static string HardDriveImage;
/// <summary>
/// The currently loaded image for the floppy drive.
/// </summary>
public static string FloppyDriveImage;
/// <summary>
/// The Ethernet host address for this Star.
/// </summary>
public static ulong HostID;
/// <summary>
/// The name of the Ethernet adaptor on the emulator host to use for Ethernet emulation
/// </summary>
public static string HostPacketInterfaceName;
/// <summary>
/// Whether any packet interfaces are available on the host
/// </summary>
public static bool HostRawEthernetInterfacesAvailable;
/// <summary>
/// Whether to cap execution speed at native execution speed or not.
/// </summary>
public static bool ThrottleSpeed;
/// <summary>
/// Scale factor to apply to the display.
/// </summary>
public static uint DisplayScale;
/// <summary>
/// Whether to apply a fake "slow phosphor" persistence to the emulated display.
/// </summary>
public static bool SlowPhosphor;
/// <summary>
/// How to set the TOD clock at power up/reset
/// </summary>
public static TODPowerUpSetMode TODSetMode;
/// <summary>
/// The specific date/time to set the TOD clock to if TODSetMode is "SpecificDateAndTime"
/// </summary>
public static DateTime TODDateTime;
/// <summary>
/// The specific date to set the TOD clock to if TODSetMode is "SpecificDate"
/// </summary>
public static DateTime TODDate;
/// <summary>
/// The components to enable debug logging for.
/// </summary>
public static LogComponent LogComponents;
/// <summary>
/// The types of logging to enable.
/// </summary>
public static LogType LogTypes;
/// <summary>
/// Reads the current configuration file from the appropriate place.
/// </summary>
public static void ReadConfiguration()
{
if (Configuration.Platform == PlatformType.Windows)
// && string.IsNullOrWhiteSpace(StartupOptions.ConfigurationFile))
{
//
// By default, on Windows we use the app Settings functionality
// to store settings in the registry on a per-user basis.
// If a configuration file is specified, we will use it instead.
//
ReadConfigurationWindows();
}
else
{
//
// On UNIX platforms we read in a configuration file.
// This is mostly because Mono's support for Properties.Settings
// is broken in inexplicable ways and I'm tired of fighting with it.
//
ReadConfigurationUnix();
}
}
/// <summary>
/// Commits the current configuration to the app's settings.
/// </summary>
public static void WriteConfiguration()
{
if (Configuration.Platform == PlatformType.Windows)
{
WriteConfigurationWindows();
}
else
{
//
// At the moment the configuration files are read-only
// on UNIX platforms.
//
}
}
private static void ReadConfigurationWindows()
{
MemorySize = Properties.Settings.Default.MemorySize;
HardDriveImage = Properties.Settings.Default.HardDriveImage;
FloppyDriveImage = Properties.Settings.Default.FloppyDriveImage;
HostID = Properties.Settings.Default.HostAddress;
HostPacketInterfaceName = Properties.Settings.Default.HostPacketInterfaceName;
ThrottleSpeed = Properties.Settings.Default.ThrottleSpeed;
DisplayScale = Properties.Settings.Default.DisplayScale;
SlowPhosphor = Properties.Settings.Default.SlowPhosphor;
TODSetMode = (TODPowerUpSetMode)Properties.Settings.Default.TODSetMode;
TODDateTime = Properties.Settings.Default.TODDateTime;
TODDate = Properties.Settings.Default.TODDate;
}
private static void WriteConfigurationWindows()
{
Properties.Settings.Default.MemorySize = MemorySize;
Properties.Settings.Default.HardDriveImage = HardDriveImage;
Properties.Settings.Default.FloppyDriveImage = FloppyDriveImage;
Properties.Settings.Default.HostAddress = HostID;
Properties.Settings.Default.HostPacketInterfaceName = HostPacketInterfaceName;
Properties.Settings.Default.ThrottleSpeed = ThrottleSpeed;
Properties.Settings.Default.DisplayScale = DisplayScale;
Properties.Settings.Default.SlowPhosphor = SlowPhosphor;
Properties.Settings.Default.TODSetMode = (int)TODSetMode;
Properties.Settings.Default.TODDateTime = TODDateTime;
Properties.Settings.Default.TODDate = TODDate;
Properties.Settings.Default.Save();
}
private static void ReadConfigurationUnix()
{
string configFilePath = null;
if (false) //!string.IsNullOrWhiteSpace(StartupOptions.ConfigurationFile))
{
// configFilePath = StartupOptions.ConfigurationFile;
}
else
{
// No config file specified, default.
configFilePath = "Darkstar.cfg";
}
//
// Check that the configuration file exists.
// If not, we will warn the user and use default settings.
//
if (!File.Exists(configFilePath))
{
Console.WriteLine("Configuration file {0} does not exist or cannot be accessed. Using default settings.", configFilePath);
return;
}
using (StreamReader configStream = new StreamReader(configFilePath))
{
//
// Config file consists of text lines containing name / value pairs:
// <Name>=<Value>
// Whitespace is ignored.
//
int lineNumber = 0;
while (!configStream.EndOfStream)
{
lineNumber++;
string line = configStream.ReadLine().Trim();
if (string.IsNullOrEmpty(line))
{
// Empty line, ignore.
continue;
}
if (line.StartsWith("#"))
{
// Comment to EOL, ignore.
continue;
}
// Find the '=' separating tokens and ensure there are just two.
string[] tokens = line.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
if (tokens.Length < 2)
{
Console.WriteLine(
"{0} line {1}: Invalid syntax.", configFilePath, lineNumber);
continue;
}
string parameter = tokens[0].Trim();
string value = tokens[1].Trim();
// Reflect over the public, static properties in this class and see if the parameter matches one of them
// If not, it's an error, if it is then we attempt to coerce the value to the correct type.
System.Reflection.FieldInfo[] info = typeof(Configuration).GetFields(BindingFlags.Public | BindingFlags.Static);
bool bMatch = false;
foreach (FieldInfo field in info)
{
// Case-insensitive compare.
if (field.Name.ToLowerInvariant() == parameter.ToLowerInvariant())
{
bMatch = true;
//
// Switch on the type of the field and attempt to convert the value to the appropriate type.
// At this time we support only strings and integers.
//
try
{
switch (field.FieldType.Name)
{
case "Int32":
{
int v = Convert.ToInt32(value, 10);
field.SetValue(null, v);
}
break;
case "UInt16":
{
UInt16 v = Convert.ToUInt16(value, 16);
field.SetValue(null, v);
}
break;
case "Byte":
{
byte v = Convert.ToByte(value, 16);
field.SetValue(null, v);
}
break;
case "String":
{
field.SetValue(null, value);
}
break;
case "Boolean":
{
bool v = bool.Parse(value);
field.SetValue(null, v);
}
break;
case "UInt64":
{
UInt64 v = Convert.ToUInt64(value, 16);
field.SetValue(null, v);
}
break;
case "TODPowerUpSetMode":
{
field.SetValue(null, Enum.Parse(typeof(TODPowerUpSetMode), value, true));
}
break;
case "DateTime":
{
field.SetValue(null, DateTime.Parse(value));
}
break;
case "LogType":
{
field.SetValue(null, Enum.Parse(typeof(LogType), value, true));
}
break;
case "LogComponent":
{
field.SetValue(null, Enum.Parse(typeof(LogComponent), value, true));
}
break;
case "StringCollection":
{
// value is expected to be a comma-delimited set.
StringCollection sc = new StringCollection();
string[] strings = value.Split(',');
foreach (string s in strings)
{
sc.Add(s);
}
field.SetValue(null, sc);
}
break;
}
}
catch
{
Console.WriteLine(
"{0} line {1}: Value '{2}' is invalid for parameter '{3}'.", configFilePath, lineNumber, value, parameter);
}
}
}
if (!bMatch)
{
Console.WriteLine(
"{0} line {1}: Unknown configuration parameter '{2}'.", configFilePath, lineNumber, parameter);
}
}
}
}
private static void TestPCap()
{
// Just try enumerating interfaces, if this fails for any reason we assume
// PCap is not properly installed.
try
{
SharpPcap.CaptureDeviceList devices = SharpPcap.CaptureDeviceList.Instance;
Configuration.HostRawEthernetInterfacesAvailable = true;
}
catch
{
Configuration.HostRawEthernetInterfacesAvailable = false;
}
}
}
}

49
D/Conversion.cs Normal file
View File

@@ -0,0 +1,49 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace D
{
public static class Conversion
{
/// <summary>
/// Conversion from millseconds to nanoseconds
/// </summary>
public static readonly ulong MsecToNsec = 1000000;
/// <summary>
/// Conversion from nanoseconds to milliseconds
/// </summary>
public static readonly double NsecToMsec = 0.000001;
/// <summary>
/// Conversion from microseconds to nanoseconds
/// </summary>
public static readonly ulong UsecToNsec = 1000;
}
}

BIN
D/Dandelion.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

34
D/Darkstar.cfg Normal file
View File

@@ -0,0 +1,34 @@
# darkstar.cfg:
#
# This file contains configuration parameters for Darkstar.
# All integers are specified in hexadecimal.
#
# System configuration
MemorySize = 0x400
HostID = 0x0000aa012345
# Disk options
# FloppyDriveImage =
# HardDriveImage =
# Ethernet configuration
# HostPacketInterfaceName =
# Display options
DisplayScale = 1
SlowPhosphor = true
# TOD (Time Of Day) Clock options
# TODSetMode specifies how to set the TOD clock at reset
# (see readme.txt). Possible values are:
# HostTimeY2K
# HostTime
# SpecificDateAndTime
# SpecificDate
# NoChange
#
TODSetMode = NoChange
# TODDate = 1990/11/09
# TODDateTime = 1990/11/09 12:00:00

306
D/Darkstar.csproj Normal file
View File

@@ -0,0 +1,306 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{0590465E-1D91-4591-946E-EE3F7D82834B}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>D</RootNamespace>
<AssemblyName>Darkstar</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="PacketDotNet, Version=0.13.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Sharp_Pcap.4.2.0\lib\PacketDotNet.dll</HintPath>
</Reference>
<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="SharpPcap, Version=4.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Sharp_Pcap.4.2.0\lib\SharpPcap.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Configuration.cs" />
<Compile Include="Conversion.cs" />
<Compile Include="CP\AM2901.cs" />
<Compile Include="CP\CentralProcessor.cs" />
<Compile Include="CP\CentralProcessorIO.cs" />
<Compile Include="CP\MacroInstruction.cs" />
<Compile Include="CP\Microinstruction.cs" />
<Compile Include="Debugger\BreakpointManager.cs" />
<Compile Include="Debugger\CPDebugger.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Debugger\CPDebugger.Designer.cs">
<DependentUpon>CPDebugger.cs</DependentUpon>
</Compile>
<Compile Include="Debugger\DebuggerMain.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Debugger\DebuggerMain.Designer.cs">
<DependentUpon>DebuggerMain.cs</DependentUpon>
</Compile>
<Compile Include="Debugger\IOPDebugger.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Debugger\IOPDebugger.Designer.cs">
<DependentUpon>IOPDebugger.cs</DependentUpon>
</Compile>
<Compile Include="Debugger\LoadMapDialog.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Debugger\LoadMapDialog.Designer.cs">
<DependentUpon>LoadMapDialog.cs</DependentUpon>
</Compile>
<Compile Include="Debugger\MicrocodeLoadMap.cs" />
<Compile Include="Debugger\SourceDisplay.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Debugger\MicrocodeDisplay.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Debugger\SourceMap.cs" />
<Compile Include="Display\DisplayController.cs" />
<Compile Include="Ethernet\HostEthernet.cs" />
<Compile Include="Ethernet\IPacketInterface.cs" />
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
<Compile Include="UI\AboutBox.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="UI\AboutBox.Designer.cs">
<DependentUpon>AboutBox.cs</DependentUpon>
</Compile>
<Compile Include="UI\ConfigurationDialog.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="UI\ConfigurationDialog.Designer.cs">
<DependentUpon>ConfigurationDialog.cs</DependentUpon>
</Compile>
<Compile Include="UI\DWindow-IO.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="UI\DWindow.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="UI\DWindow.Designer.cs">
<DependentUpon>DWindow.cs</DependentUpon>
</Compile>
<Compile Include="Ethernet\CRC32.cs" />
<Compile Include="Ethernet\EthernetController.cs" />
<Compile Include="HighResTimer.cs" />
<Compile Include="IOP\DMAController.cs" />
<Compile Include="IOP\FloppyController.cs" />
<Compile Include="IOP\Keyboard.cs" />
<Compile Include="IOP\Mouse.cs" />
<Compile Include="IOP\TODClock.cs" />
<Compile Include="IOP\Printer.cs" />
<Compile Include="IO\FloppyDisk.cs" />
<Compile Include="IO\FloppyDrive.cs" />
<Compile Include="IOP\i8085.cs" />
<Compile Include="IOP\I8085IOBus.cs" />
<Compile Include="IOP\I8085MemoryBus.cs" />
<Compile Include="IOP\IIOPDevice.cs" />
<Compile Include="IOP\IOProcessor.cs" />
<Compile Include="IOP\IOPMemoryBus.cs" />
<Compile Include="IOP\IOPIOBus.cs" />
<Compile Include="IOP\MiscIO.cs" />
<Compile Include="IO\SA1000.cs" />
<Compile Include="IO\ShugartController.cs" />
<Compile Include="Logging\Log.cs" />
<Compile Include="Memory\Memory.cs" />
<Compile Include="Memory\MemoryController.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Scheduler.cs" />
<Compile Include="System.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="app.manifest" />
<None Include="Disks\130P26300-diags.IMD">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Disks\130P26301-install.IMD">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Disks\Harmony.img">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Disks\Lyric.img">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Disks\Medley.img">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Disks\ViewPoint-11-9-1990-18-38.img">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Disks\XDE.img">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Disks\XDE_5.0_BO1.IMD">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="IOP\PROM\537P03029.bin">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="IOP\PROM\537P03030.bin">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="IOP\PROM\537P03032.bin">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="IOP\PROM\537P03700.bin">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<Content Include="CP\Source\BootKernel_map.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="CP\Source\CPMemTest_map.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="CP\Source\FloppyInitial_map.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="CP\Source\load_map.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="CP\Source\Main_map.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="CP\Source\MoonCycle_map.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="CP\Source\MoonEthBTest_map.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="CP\Source\MoonPortIn_map.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="CP\Source\MoonPortOut_map.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="CP\Source\Phase0Protected_map.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="CP\Source\Phase0_map.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="CP\Source\SunlightO2_map.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="CP\Source\SunlightO4_map.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="CP\Source\SunlightO5_map.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Dandelion.ico" />
<Content Include="IOP\Source\SourceMap.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Notes\IOP.txt" />
<Content Include="Notes\8085 code annotation.txt" />
<Content Include="Notes\Ethernet.txt" />
<Content Include="readme.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="SDL2.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Include="Darkstar.cfg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Debugger\CPDebugger.resx">
<DependentUpon>CPDebugger.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Debugger\DebuggerMain.resx">
<DependentUpon>DebuggerMain.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Debugger\IOPDebugger.resx">
<DependentUpon>IOPDebugger.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Debugger\LoadMapDialog.resx">
<DependentUpon>LoadMapDialog.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="UI\AboutBox.resx">
<DependentUpon>AboutBox.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="UI\ConfigurationDialog.resx">
<DependentUpon>ConfigurationDialog.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="UI\DWindow.resx">
<DependentUpon>DWindow.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -0,0 +1,178 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
namespace D.Debugger
{
[Flags]
public enum BreakpointType
{
None = 0,
Execution = 1,
Read = 2,
Write = 4,
}
public enum BreakpointProcessor
{
IOP = 0,
CP,
Mesa,
}
public struct BreakpointEntry
{
public BreakpointEntry(BreakpointProcessor processor, BreakpointType type, int address)
{
Processor = processor;
Type = type;
Address = address;
}
public BreakpointProcessor Processor;
public BreakpointType Type;
public int Address;
}
public static class BreakpointManager
{
static BreakpointManager()
{
// We use a flat array to make lookup as
// cheap as possible across the various address spaces.
_iopBreakpoints = new BreakpointType[0x10000];
_cpBreakpoints = new BreakpointType[0x1000];
_mesaBreakpoints = new BreakpointType[0x200000]; // 2mb (1mw).
_enableBreakpoints = true;
}
public static bool BreakpointsEnabled
{
get { return _enableBreakpoints; }
set { _enableBreakpoints = value; }
}
public static void SetBreakpoint(BreakpointEntry entry)
{
BreakpointType[] _bkpts = GetBreakpointsForProcessor(entry.Processor);
if (entry.Type == BreakpointType.None)
{
_bkpts[entry.Address] = BreakpointType.None;
}
else
{
_bkpts[entry.Address] |= entry.Type;
}
}
public static BreakpointType GetBreakpoint(BreakpointProcessor processor, ushort address)
{
BreakpointType[] _bkpts = GetBreakpointsForProcessor(processor);
return _bkpts[address];
}
public static bool TestBreakpoint(BreakpointEntry entry)
{
if (!_enableBreakpoints)
{
return false;
}
BreakpointType[] _bkpts = GetBreakpointsForProcessor(entry.Processor);
return (_bkpts[entry.Address] & entry.Type) != 0;
}
public static bool TestBreakpoint(BreakpointProcessor processor, BreakpointType type, int address)
{
if (!_enableBreakpoints)
{
return false;
}
BreakpointType[] _bkpts = GetBreakpointsForProcessor(processor);
return (_bkpts[address] & type) != 0;
}
public static List<BreakpointEntry> EnumerateBreakpoints()
{
List<BreakpointEntry> breakpoints = new List<BreakpointEntry>();
for (ushort i = 0; i < _iopBreakpoints.Length; i++)
{
if (_iopBreakpoints[i] != BreakpointType.None)
{
breakpoints.Add(new BreakpointEntry(BreakpointProcessor.IOP, _iopBreakpoints[i], i));
}
}
for (ushort i = 0; i < _cpBreakpoints.Length; i++)
{
if (_cpBreakpoints[i] != BreakpointType.None)
{
breakpoints.Add(new BreakpointEntry(BreakpointProcessor.CP, _cpBreakpoints[i], i));
}
}
for (ushort i = 0; i < _mesaBreakpoints.Length; i++)
{
if (_mesaBreakpoints[i] != BreakpointType.None)
{
breakpoints.Add(new BreakpointEntry(BreakpointProcessor.Mesa, _mesaBreakpoints[i], i));
}
}
return breakpoints;
}
private static BreakpointType[] GetBreakpointsForProcessor(BreakpointProcessor processor)
{
switch (processor)
{
case BreakpointProcessor.CP:
return _cpBreakpoints;
case BreakpointProcessor.IOP:
return _iopBreakpoints;
case BreakpointProcessor.Mesa:
return _mesaBreakpoints;
}
return null;
}
private static BreakpointType[] _iopBreakpoints;
private static BreakpointType[] _cpBreakpoints;
private static BreakpointType[] _mesaBreakpoints;
private static bool _enableBreakpoints;
}
}

457
D/Debugger/CPDebugger.Designer.cs generated Normal file
View File

@@ -0,0 +1,457 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace D.Debugger
{
partial class CPDebugger
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle();
this.IOPSourceBox = new System.Windows.Forms.GroupBox();
this.Disassembly = new D.Debugger.MicrocodeDisplay();
this.SourceFiles = new System.Windows.Forms.ListBox();
this.SourceDisplay = new D.Debugger.SourceDisplay();
this.dataGridViewCheckBoxColumn3 = new System.Windows.Forms.DataGridViewCheckBoxColumn();
this.dataGridViewTextBoxColumn5 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn6 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewCheckBoxColumn1 = new System.Windows.Forms.DataGridViewCheckBoxColumn();
this.dataGridViewTextBoxColumn1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn2 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewCheckBoxColumn2 = new System.Windows.Forms.DataGridViewCheckBoxColumn();
this.dataGridViewTextBoxColumn3 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn4 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewCheckBoxColumn4 = new System.Windows.Forms.DataGridViewCheckBoxColumn();
this.dataGridViewTextBoxColumn7 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn8 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewCheckBoxColumn5 = new System.Windows.Forms.DataGridViewCheckBoxColumn();
this.dataGridViewTextBoxColumn9 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn10 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewCheckBoxColumn6 = new System.Windows.Forms.DataGridViewCheckBoxColumn();
this.dataGridViewTextBoxColumn11 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn12 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewCheckBoxColumn7 = new System.Windows.Forms.DataGridViewCheckBoxColumn();
this.dataGridViewTextBoxColumn13 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn14 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewCheckBoxColumn8 = new System.Windows.Forms.DataGridViewCheckBoxColumn();
this.dataGridViewTextBoxColumn15 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn16 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewCheckBoxColumn9 = new System.Windows.Forms.DataGridViewCheckBoxColumn();
this.dataGridViewTextBoxColumn17 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn18 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.IOPSourceBox.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.Disassembly)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.SourceDisplay)).BeginInit();
this.SuspendLayout();
//
// IOPSourceBox
//
this.IOPSourceBox.Controls.Add(this.Disassembly);
this.IOPSourceBox.Controls.Add(this.SourceFiles);
this.IOPSourceBox.Controls.Add(this.SourceDisplay);
this.IOPSourceBox.Location = new System.Drawing.Point(10, 5);
this.IOPSourceBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.IOPSourceBox.Name = "IOPSourceBox";
this.IOPSourceBox.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.IOPSourceBox.Size = new System.Drawing.Size(1256, 1129);
this.IOPSourceBox.TabIndex = 2;
this.IOPSourceBox.TabStop = false;
this.IOPSourceBox.Text = "Microcode Source";
//
// Disassembly
//
this.Disassembly.AllowUserToAddRows = false;
this.Disassembly.AllowUserToDeleteRows = false;
this.Disassembly.AllowUserToResizeColumns = false;
this.Disassembly.AllowUserToResizeRows = false;
dataGridViewCellStyle1.BackColor = System.Drawing.Color.Silver;
this.Disassembly.AlternatingRowsDefaultCellStyle = dataGridViewCellStyle1;
this.Disassembly.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
dataGridViewCellStyle2.BackColor = System.Drawing.SystemColors.Window;
dataGridViewCellStyle2.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText;
dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight;
dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
this.Disassembly.DefaultCellStyle = dataGridViewCellStyle2;
this.Disassembly.Location = new System.Drawing.Point(230, 712);
this.Disassembly.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.Disassembly.Name = "Disassembly";
this.Disassembly.RowHeadersVisible = false;
this.Disassembly.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect;
this.Disassembly.Size = new System.Drawing.Size(1017, 403);
this.Disassembly.TabIndex = 2;
this.Disassembly.VirtualMode = true;
this.Disassembly.SelectionChanged += new System.EventHandler(this.OnDisassemblySelectionChanged);
//
// SourceFiles
//
this.SourceFiles.FormattingEnabled = true;
this.SourceFiles.ItemHeight = 20;
this.SourceFiles.Location = new System.Drawing.Point(10, 31);
this.SourceFiles.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.SourceFiles.Name = "SourceFiles";
this.SourceFiles.SelectionMode = System.Windows.Forms.SelectionMode.MultiExtended;
this.SourceFiles.Size = new System.Drawing.Size(206, 1084);
this.SourceFiles.TabIndex = 1;
this.SourceFiles.Click += new System.EventHandler(this.OnSourceFilesClicked);
this.SourceFiles.DoubleClick += new System.EventHandler(this.OnSourceFilesDoubleClicked);
//
// SourceDisplay
//
this.SourceDisplay.AllowUserToAddRows = false;
this.SourceDisplay.AllowUserToDeleteRows = false;
this.SourceDisplay.AllowUserToResizeColumns = false;
this.SourceDisplay.AllowUserToResizeRows = false;
this.SourceDisplay.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.SourceDisplay.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.SingleVertical;
this.SourceDisplay.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
dataGridViewCellStyle3.BackColor = System.Drawing.SystemColors.Window;
dataGridViewCellStyle3.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
dataGridViewCellStyle3.ForeColor = System.Drawing.SystemColors.ControlText;
dataGridViewCellStyle3.NullValue = null;
dataGridViewCellStyle3.SelectionBackColor = System.Drawing.SystemColors.Highlight;
dataGridViewCellStyle3.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
dataGridViewCellStyle3.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
this.SourceDisplay.DefaultCellStyle = dataGridViewCellStyle3;
this.SourceDisplay.Location = new System.Drawing.Point(228, 29);
this.SourceDisplay.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.SourceDisplay.Name = "SourceDisplay";
this.SourceDisplay.RowHeadersVisible = false;
this.SourceDisplay.RowHeadersWidthSizeMode = System.Windows.Forms.DataGridViewRowHeadersWidthSizeMode.DisableResizing;
this.SourceDisplay.RowTemplate.Height = 18;
this.SourceDisplay.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.SourceDisplay.Size = new System.Drawing.Size(1018, 672);
this.SourceDisplay.TabIndex = 0;
this.SourceDisplay.VirtualMode = true;
this.SourceDisplay.SelectionChanged += new System.EventHandler(this.OnSourceDisplaySelectionChanged);
this.SourceDisplay.MouseClick += new System.Windows.Forms.MouseEventHandler(this.OnSourceDisplayMouseClick);
//
// dataGridViewCheckBoxColumn3
//
this.dataGridViewCheckBoxColumn3.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
this.dataGridViewCheckBoxColumn3.HeaderText = "B";
this.dataGridViewCheckBoxColumn3.Name = "dataGridViewCheckBoxColumn3";
this.dataGridViewCheckBoxColumn3.Resizable = System.Windows.Forms.DataGridViewTriState.False;
//
// dataGridViewTextBoxColumn5
//
this.dataGridViewTextBoxColumn5.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
this.dataGridViewTextBoxColumn5.HeaderText = "Address";
this.dataGridViewTextBoxColumn5.Name = "dataGridViewTextBoxColumn5";
this.dataGridViewTextBoxColumn5.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.dataGridViewTextBoxColumn5.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
//
// dataGridViewTextBoxColumn6
//
this.dataGridViewTextBoxColumn6.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
this.dataGridViewTextBoxColumn6.HeaderText = "Source";
this.dataGridViewTextBoxColumn6.Name = "dataGridViewTextBoxColumn6";
this.dataGridViewTextBoxColumn6.ReadOnly = true;
this.dataGridViewTextBoxColumn6.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.dataGridViewTextBoxColumn6.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
//
// dataGridViewCheckBoxColumn1
//
this.dataGridViewCheckBoxColumn1.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
this.dataGridViewCheckBoxColumn1.HeaderText = "B";
this.dataGridViewCheckBoxColumn1.Name = "dataGridViewCheckBoxColumn1";
this.dataGridViewCheckBoxColumn1.Resizable = System.Windows.Forms.DataGridViewTriState.False;
//
// dataGridViewTextBoxColumn1
//
this.dataGridViewTextBoxColumn1.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
this.dataGridViewTextBoxColumn1.HeaderText = "Address";
this.dataGridViewTextBoxColumn1.Name = "dataGridViewTextBoxColumn1";
this.dataGridViewTextBoxColumn1.ReadOnly = true;
this.dataGridViewTextBoxColumn1.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.dataGridViewTextBoxColumn1.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
//
// dataGridViewTextBoxColumn2
//
this.dataGridViewTextBoxColumn2.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
this.dataGridViewTextBoxColumn2.HeaderText = "Disassembly";
this.dataGridViewTextBoxColumn2.Name = "dataGridViewTextBoxColumn2";
this.dataGridViewTextBoxColumn2.ReadOnly = true;
this.dataGridViewTextBoxColumn2.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.dataGridViewTextBoxColumn2.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
//
// dataGridViewCheckBoxColumn2
//
this.dataGridViewCheckBoxColumn2.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
this.dataGridViewCheckBoxColumn2.HeaderText = "B";
this.dataGridViewCheckBoxColumn2.Name = "dataGridViewCheckBoxColumn2";
this.dataGridViewCheckBoxColumn2.Resizable = System.Windows.Forms.DataGridViewTriState.False;
//
// dataGridViewTextBoxColumn3
//
this.dataGridViewTextBoxColumn3.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
this.dataGridViewTextBoxColumn3.HeaderText = "Address";
this.dataGridViewTextBoxColumn3.Name = "dataGridViewTextBoxColumn3";
this.dataGridViewTextBoxColumn3.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.dataGridViewTextBoxColumn3.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
//
// dataGridViewTextBoxColumn4
//
this.dataGridViewTextBoxColumn4.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
this.dataGridViewTextBoxColumn4.HeaderText = "Source";
this.dataGridViewTextBoxColumn4.Name = "dataGridViewTextBoxColumn4";
this.dataGridViewTextBoxColumn4.ReadOnly = true;
this.dataGridViewTextBoxColumn4.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.dataGridViewTextBoxColumn4.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
//
// dataGridViewCheckBoxColumn4
//
this.dataGridViewCheckBoxColumn4.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
this.dataGridViewCheckBoxColumn4.HeaderText = "B";
this.dataGridViewCheckBoxColumn4.Name = "dataGridViewCheckBoxColumn4";
this.dataGridViewCheckBoxColumn4.Resizable = System.Windows.Forms.DataGridViewTriState.False;
//
// dataGridViewTextBoxColumn7
//
this.dataGridViewTextBoxColumn7.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
this.dataGridViewTextBoxColumn7.HeaderText = "Address";
this.dataGridViewTextBoxColumn7.Name = "dataGridViewTextBoxColumn7";
this.dataGridViewTextBoxColumn7.ReadOnly = true;
this.dataGridViewTextBoxColumn7.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.dataGridViewTextBoxColumn7.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
//
// dataGridViewTextBoxColumn8
//
this.dataGridViewTextBoxColumn8.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
this.dataGridViewTextBoxColumn8.HeaderText = "Disassembly";
this.dataGridViewTextBoxColumn8.Name = "dataGridViewTextBoxColumn8";
this.dataGridViewTextBoxColumn8.ReadOnly = true;
this.dataGridViewTextBoxColumn8.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.dataGridViewTextBoxColumn8.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
//
// dataGridViewCheckBoxColumn5
//
this.dataGridViewCheckBoxColumn5.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
this.dataGridViewCheckBoxColumn5.HeaderText = "B";
this.dataGridViewCheckBoxColumn5.Name = "dataGridViewCheckBoxColumn5";
this.dataGridViewCheckBoxColumn5.Resizable = System.Windows.Forms.DataGridViewTriState.False;
//
// dataGridViewTextBoxColumn9
//
this.dataGridViewTextBoxColumn9.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
this.dataGridViewTextBoxColumn9.HeaderText = "Address";
this.dataGridViewTextBoxColumn9.Name = "dataGridViewTextBoxColumn9";
this.dataGridViewTextBoxColumn9.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.dataGridViewTextBoxColumn9.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
//
// dataGridViewTextBoxColumn10
//
this.dataGridViewTextBoxColumn10.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
this.dataGridViewTextBoxColumn10.HeaderText = "Source";
this.dataGridViewTextBoxColumn10.Name = "dataGridViewTextBoxColumn10";
this.dataGridViewTextBoxColumn10.ReadOnly = true;
this.dataGridViewTextBoxColumn10.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.dataGridViewTextBoxColumn10.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
//
// dataGridViewCheckBoxColumn6
//
this.dataGridViewCheckBoxColumn6.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
this.dataGridViewCheckBoxColumn6.HeaderText = "B";
this.dataGridViewCheckBoxColumn6.Name = "dataGridViewCheckBoxColumn6";
this.dataGridViewCheckBoxColumn6.Resizable = System.Windows.Forms.DataGridViewTriState.False;
//
// dataGridViewTextBoxColumn11
//
this.dataGridViewTextBoxColumn11.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
this.dataGridViewTextBoxColumn11.HeaderText = "Address";
this.dataGridViewTextBoxColumn11.Name = "dataGridViewTextBoxColumn11";
this.dataGridViewTextBoxColumn11.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.dataGridViewTextBoxColumn11.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
//
// dataGridViewTextBoxColumn12
//
this.dataGridViewTextBoxColumn12.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
this.dataGridViewTextBoxColumn12.HeaderText = "Disassembly";
this.dataGridViewTextBoxColumn12.Name = "dataGridViewTextBoxColumn12";
this.dataGridViewTextBoxColumn12.ReadOnly = true;
this.dataGridViewTextBoxColumn12.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.dataGridViewTextBoxColumn12.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
//
// dataGridViewCheckBoxColumn7
//
this.dataGridViewCheckBoxColumn7.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
this.dataGridViewCheckBoxColumn7.HeaderText = "B";
this.dataGridViewCheckBoxColumn7.Name = "dataGridViewCheckBoxColumn7";
this.dataGridViewCheckBoxColumn7.Resizable = System.Windows.Forms.DataGridViewTriState.False;
//
// dataGridViewTextBoxColumn13
//
this.dataGridViewTextBoxColumn13.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
this.dataGridViewTextBoxColumn13.HeaderText = "Address";
this.dataGridViewTextBoxColumn13.Name = "dataGridViewTextBoxColumn13";
this.dataGridViewTextBoxColumn13.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.dataGridViewTextBoxColumn13.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
//
// dataGridViewTextBoxColumn14
//
this.dataGridViewTextBoxColumn14.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
this.dataGridViewTextBoxColumn14.HeaderText = "Source";
this.dataGridViewTextBoxColumn14.Name = "dataGridViewTextBoxColumn14";
this.dataGridViewTextBoxColumn14.ReadOnly = true;
this.dataGridViewTextBoxColumn14.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.dataGridViewTextBoxColumn14.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
//
// dataGridViewCheckBoxColumn8
//
this.dataGridViewCheckBoxColumn8.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
this.dataGridViewCheckBoxColumn8.HeaderText = "B";
this.dataGridViewCheckBoxColumn8.Name = "dataGridViewCheckBoxColumn8";
this.dataGridViewCheckBoxColumn8.Resizable = System.Windows.Forms.DataGridViewTriState.False;
//
// dataGridViewTextBoxColumn15
//
this.dataGridViewTextBoxColumn15.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
this.dataGridViewTextBoxColumn15.HeaderText = "Address";
this.dataGridViewTextBoxColumn15.Name = "dataGridViewTextBoxColumn15";
this.dataGridViewTextBoxColumn15.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.dataGridViewTextBoxColumn15.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
//
// dataGridViewTextBoxColumn16
//
this.dataGridViewTextBoxColumn16.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
this.dataGridViewTextBoxColumn16.HeaderText = "Disassembly";
this.dataGridViewTextBoxColumn16.Name = "dataGridViewTextBoxColumn16";
this.dataGridViewTextBoxColumn16.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.dataGridViewTextBoxColumn16.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
//
// dataGridViewCheckBoxColumn9
//
this.dataGridViewCheckBoxColumn9.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
this.dataGridViewCheckBoxColumn9.HeaderText = "B";
this.dataGridViewCheckBoxColumn9.Name = "dataGridViewCheckBoxColumn9";
this.dataGridViewCheckBoxColumn9.Resizable = System.Windows.Forms.DataGridViewTriState.False;
//
// dataGridViewTextBoxColumn17
//
this.dataGridViewTextBoxColumn17.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
this.dataGridViewTextBoxColumn17.HeaderText = "Address";
this.dataGridViewTextBoxColumn17.Name = "dataGridViewTextBoxColumn17";
this.dataGridViewTextBoxColumn17.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.dataGridViewTextBoxColumn17.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
//
// dataGridViewTextBoxColumn18
//
this.dataGridViewTextBoxColumn18.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
this.dataGridViewTextBoxColumn18.HeaderText = "Source";
this.dataGridViewTextBoxColumn18.Name = "dataGridViewTextBoxColumn18";
this.dataGridViewTextBoxColumn18.ReadOnly = true;
this.dataGridViewTextBoxColumn18.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.dataGridViewTextBoxColumn18.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
//
// CPDebugger
//
this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1274, 1138);
this.ControlBox = false;
this.Controls.Add(this.IOPSourceBox);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.KeyPreview = true;
this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "CPDebugger";
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
this.Text = "CP Debugger";
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.CPDebugger_FormClosed);
this.IOPSourceBox.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.Disassembly)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.SourceDisplay)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.GroupBox IOPSourceBox;
private SourceDisplay SourceDisplay;
private System.Windows.Forms.ListBox SourceFiles;
private System.Windows.Forms.DataGridViewCheckBoxColumn dataGridViewCheckBoxColumn3;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn5;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn6;
private MicrocodeDisplay Disassembly;
private System.Windows.Forms.DataGridViewCheckBoxColumn dataGridViewCheckBoxColumn1;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn1;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn2;
private System.Windows.Forms.DataGridViewCheckBoxColumn dataGridViewCheckBoxColumn2;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn3;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn4;
private System.Windows.Forms.DataGridViewCheckBoxColumn dataGridViewCheckBoxColumn4;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn7;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn8;
private System.Windows.Forms.DataGridViewCheckBoxColumn dataGridViewCheckBoxColumn5;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn9;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn10;
private System.Windows.Forms.DataGridViewCheckBoxColumn dataGridViewCheckBoxColumn6;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn11;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn12;
private System.Windows.Forms.DataGridViewCheckBoxColumn dataGridViewCheckBoxColumn7;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn13;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn14;
private System.Windows.Forms.DataGridViewCheckBoxColumn dataGridViewCheckBoxColumn8;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn15;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn16;
private System.Windows.Forms.DataGridViewCheckBoxColumn dataGridViewCheckBoxColumn9;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn17;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn18;
}
}

304
D/Debugger/CPDebugger.cs Normal file
View File

@@ -0,0 +1,304 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
namespace D.Debugger
{
public partial class CPDebugger : Form
{
public CPDebugger(DSystem system)
{
InitializeComponent();
_system = system;
SourceDisplay.SetSourceRoot(Path.Combine("CP", "Source"));
_loadMap = new MicrocodeLoadMap();
_loadMap.Load(Path.Combine("CP", "Source", "load_map.txt"));
_sourceMaps = new List<SourceMap>();
Disassembly.AttachCP(system.CP);
PopulateSourceList();
}
public void DisplayCurrentCode()
{
RefreshSourceMaps();
//
// Select active files in the source file list
//
SourceFiles.ClearSelected();
foreach(SourceMap s in _sourceMaps)
{
foreach(string fileName in s.GetSourceFiles())
{
for (int i = 0; i < SourceFiles.Items.Count; i++)
{
if (fileName == (string)SourceFiles.Items[i])
{
SourceFiles.SetSelected(i, true);
}
}
}
}
//
// Find the source line that matches the current TPC, if any.
//
int tpc = _system.CP.TPC[(int)_system.CP.CurrentTask];
foreach (SourceMap s in _sourceMaps)
{
SourceEntry entry = s.GetExactSourceForAddress((ushort)tpc);
if (entry != null)
{
// WE GOT ONE!!!
SourceDisplay.AttachMap(s);
SourceDisplay.SelectSourceEntry(entry, false, false /* cp */);
// Select the source file in the source list
for (int i = 0; i < SourceFiles.Items.Count; i++)
{
if (entry.SourcePath == (string)SourceFiles.Items[i])
{
SourceFiles.SetSelected(i, true);
}
}
break;
}
}
//
// Highlight the line in the disassembly as well.
//
Disassembly.SelectAddress(tpc);
}
public SourceEntry GetSymbolForAddress(int address)
{
RefreshSourceMaps();
SourceEntry entry = null;
foreach (SourceMap s in _sourceMaps)
{
entry = s.GetSourceForAddress((ushort)address);
if (entry != null)
{
break;
}
}
return entry;
}
public void Save()
{
SaveSourceMaps();
}
private void PopulateSourceList()
{
SourceFiles.Items.Clear();
// TODO: factor out path generation
IEnumerable<string> files = Directory.EnumerateFiles(Path.Combine("CP", "Source"), "*.mc,v", SearchOption.TopDirectoryOnly);
foreach(string file in files)
{
SourceFiles.Items.Add(Path.GetFileName(file));
}
}
private void OnSourceFilesClicked(object sender, EventArgs e)
{
}
private void OnSourceDisplayMouseClick(object sender, MouseEventArgs e)
{
//
// On right click when unmapped file is displayed, we will bring up the map dialog.
//
if (e.Button == MouseButtons.Right)
{
if (SourceDisplay.ReadOnly)
{
LoadMapDialog mapDialog = new LoadMapDialog(SourceDisplay.CurrentSourceFile, _loadMap, _sourceMaps, _system.CP.MicrocodeRam);
DialogResult res = mapDialog.ShowDialog(this);
if (res == DialogResult.OK)
{
RefreshSourceMaps();
SourceDisplay.ReadOnly = false;
SourceDisplay.AttachMap(mapDialog.SelectedMap);
}
}
}
}
private void OnSourceFilesDoubleClicked(object sender, EventArgs e)
{
string selectedFile = (string)SourceFiles.SelectedItem;
//
// If none of the source maps contain this file, we will display the source in read-only mode
// (no annotations allowed).
//
RefreshSourceMaps();
bool mapped = false;
foreach (SourceMap m in _sourceMaps)
{
if (m.GetSourceFiles().Contains(selectedFile))
{
SourceDisplay.AttachMap(m);
mapped = true;
break;
}
}
SourceDisplay.SelectSourceEntry(new SourceEntry((string)SourceFiles.SelectedItem, new string[] { }, 0, 1), !mapped, false /* cp */);
}
private void CPDebugger_FormClosed(object sender, FormClosedEventArgs e)
{
_loadMap.Save(Path.Combine("CP", "Source", "load_map.txt")); // TODO: move to constant
SaveSourceMaps();
}
private void RefreshSourceMaps()
{
List<LoadMapEntry> mapEntries = _loadMap.FindEntries(_system.CP.MicrocodeRam);
//
// See if any entries have changed, if so reload source maps.
//
bool changed = _mapEntries == null || mapEntries.Count != _mapEntries.Count;
if (!changed)
{
for (int i = 0; i < mapEntries.Count; i++)
{
//
// This assumes that ordering remains constant, which works at the moment.
//
if (mapEntries[i].Name != _mapEntries[i].Name)
{
changed = true;
break;
}
}
}
if (changed)
{
//
// Commit any existing source maps back to disk.
//
SaveSourceMaps();
// Build a new set of source maps.
_sourceMaps.Clear();
_mapEntries = mapEntries;
foreach (LoadMapEntry e in _mapEntries)
{
SourceMap newMap = new SourceMap(
e.Name,
Path.Combine("CP", "Source", e.MapName),
Path.Combine("CP", "Source"));
_sourceMaps.Add(newMap);
}
}
}
private void SaveSourceMaps()
{
foreach (SourceMap m in _sourceMaps)
{
m.Save();
}
}
private MicrocodeLoadMap _loadMap;
private List<LoadMapEntry> _mapEntries;
private List<SourceMap> _sourceMaps;
private DSystem _system;
private void OnSourceDisplaySelectionChanged(object sender, EventArgs e)
{
//
// Sync the disassembly display with the selected source address, if possible.
//
int address = SourceDisplay.SelectedAddress;
if (address != -1 && Disassembly.SelectedAddress != address)
{
Disassembly.SelectAddress(address);
}
}
private void OnDisassemblySelectionChanged(object sender, EventArgs e)
{
//
// Sync the source display with the selected disassembly address, if possible.
//
int address = Disassembly.SelectedAddress;
//
// TODO: we should search through all source maps, not just the current one.
//
if (SourceDisplay.SourceMap != null && address != -1 && SourceDisplay.SelectedAddress != address)
{
SourceEntry entry = SourceDisplay.SourceMap.GetSourceForAddress((ushort)address);
if (entry != null && !string.IsNullOrWhiteSpace(entry.SourcePath))
{
SourceDisplay.SelectSourceEntry(entry, false, false /* cp */);
}
}
}
}
}

120
D/Debugger/CPDebugger.resx Normal file
View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

109
D/Debugger/DebuggerMain.Designer.cs generated Normal file
View File

@@ -0,0 +1,109 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace D.Debugger
{
partial class DebuggerMain
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.DebugOutput = new System.Windows.Forms.TextBox();
this.DebuggerInput = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// DebugOutput
//
this.DebugOutput.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.DebugOutput.Location = new System.Drawing.Point(3, 12);
this.DebugOutput.Multiline = true;
this.DebugOutput.Name = "DebugOutput";
this.DebugOutput.ReadOnly = true;
this.DebugOutput.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.DebugOutput.Size = new System.Drawing.Size(837, 605);
this.DebugOutput.TabIndex = 3;
//
// DebuggerInput
//
this.DebuggerInput.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.DebuggerInput.Location = new System.Drawing.Point(3, 622);
this.DebuggerInput.Multiline = true;
this.DebuggerInput.Name = "DebuggerInput";
this.DebuggerInput.Size = new System.Drawing.Size(837, 20);
this.DebuggerInput.TabIndex = 2;
this.DebuggerInput.PreviewKeyDown += new System.Windows.Forms.PreviewKeyDownEventHandler(this.DebuggerInput_PreviewKeyDown);
//
// DebuggerMain
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(845, 647);
this.Controls.Add(this.DebugOutput);
this.Controls.Add(this.DebuggerInput);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.KeyPreview = true;
this.MaximizeBox = false;
this.Name = "DebuggerMain";
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
this.Text = "Debugger Console";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.OnFormClosing);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox DebugOutput;
private System.Windows.Forms.TextBox DebuggerInput;
}
}

1336
D/Debugger/DebuggerMain.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

142
D/Debugger/IOPDebugger.Designer.cs generated Normal file
View File

@@ -0,0 +1,142 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace D.Debugger
{
partial class IOPDebugger
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
this.IOPSourceBox = new System.Windows.Forms.GroupBox();
this.SourceFiles = new System.Windows.Forms.ListBox();
this.SourceDisplay = new D.Debugger.SourceDisplay();
this.IOPSourceBox.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.SourceDisplay)).BeginInit();
this.SuspendLayout();
//
// IOPSourceBox
//
this.IOPSourceBox.Controls.Add(this.SourceFiles);
this.IOPSourceBox.Controls.Add(this.SourceDisplay);
this.IOPSourceBox.Location = new System.Drawing.Point(7, 3);
this.IOPSourceBox.Name = "IOPSourceBox";
this.IOPSourceBox.Size = new System.Drawing.Size(837, 725);
this.IOPSourceBox.TabIndex = 2;
this.IOPSourceBox.TabStop = false;
this.IOPSourceBox.Text = "IOP Source";
//
// SourceFiles
//
this.SourceFiles.FormattingEnabled = true;
this.SourceFiles.Location = new System.Drawing.Point(7, 20);
this.SourceFiles.Name = "SourceFiles";
this.SourceFiles.Size = new System.Drawing.Size(139, 693);
this.SourceFiles.TabIndex = 1;
this.SourceFiles.DoubleClick += new System.EventHandler(this.SourceFiles_DoubleClick);
//
// SourceDisplay
//
this.SourceDisplay.AllowUserToAddRows = false;
this.SourceDisplay.AllowUserToDeleteRows = false;
this.SourceDisplay.AllowUserToResizeColumns = false;
this.SourceDisplay.AllowUserToResizeRows = false;
this.SourceDisplay.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.SourceDisplay.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.SingleVertical;
this.SourceDisplay.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
dataGridViewCellStyle2.BackColor = System.Drawing.SystemColors.Window;
dataGridViewCellStyle2.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText;
dataGridViewCellStyle2.NullValue = null;
dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight;
dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
this.SourceDisplay.DefaultCellStyle = dataGridViewCellStyle2;
this.SourceDisplay.Location = new System.Drawing.Point(152, 19);
this.SourceDisplay.Name = "SourceDisplay";
this.SourceDisplay.RowHeadersVisible = false;
this.SourceDisplay.RowHeadersWidthSizeMode = System.Windows.Forms.DataGridViewRowHeadersWidthSizeMode.DisableResizing;
this.SourceDisplay.RowTemplate.Height = 18;
this.SourceDisplay.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.SourceDisplay.Size = new System.Drawing.Size(679, 694);
this.SourceDisplay.TabIndex = 0;
this.SourceDisplay.VirtualMode = true;
//
// IOPDebugger
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.AutoSize = true;
this.ClientSize = new System.Drawing.Size(851, 732);
this.ControlBox = false;
this.Controls.Add(this.IOPSourceBox);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.KeyPreview = true;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "IOPDebugger";
this.Text = "IOP Debugger - MP {0}";
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.IOPDebugger_FormClosed);
this.IOPSourceBox.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.SourceDisplay)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.GroupBox IOPSourceBox;
private SourceDisplay SourceDisplay;
private System.Windows.Forms.ListBox SourceFiles;
}
}

119
D/Debugger/IOPDebugger.cs Normal file
View File

@@ -0,0 +1,119 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
namespace D.Debugger
{
public partial class IOPDebugger : Form
{
public IOPDebugger(DSystem system)
{
InitializeComponent();
_system = system;
_sourceMap = new SourceMap("8085 ROM", "IOP\\Source\\SourceMap.txt", "IOP\\Source");
SourceDisplay.SetSourceRoot(Path.Combine("IOP", "Source"));
SourceDisplay.AttachMap(_sourceMap);
PopulateSourceList();
DisplayCurrentCode();
}
public SourceMap SourceMap
{
get { return _sourceMap; }
}
public void DisplayCurrentCode()
{
SourceEntry entry = _sourceMap.GetSourceForAddress(_system.IOP.CPU.PC);
if (entry != null && !string.IsNullOrWhiteSpace(entry.SourcePath))
{
SourceDisplay.SelectSourceEntry(entry, false, true /* iop */);
// Find the source entry in the file list and select it.
for (int i = 0; i < SourceFiles.Items.Count; i++)
{
if ((string)SourceFiles.Items[i] == entry.SourcePath)
{
SourceFiles.SelectedIndex = i;
break;
}
}
}
else
{
// Should show disassembly instead
}
}
public void Save()
{
_sourceMap.Save();
}
private void PopulateSourceList()
{
SourceFiles.Items.Clear();
// TODO: factor out path generation
IEnumerable<string> files = Directory.EnumerateFiles(Path.Combine("IOP", "Source"), "*.asm,v", SearchOption.TopDirectoryOnly);
foreach(string file in files)
{
SourceFiles.Items.Add(Path.GetFileName(file));
}
}
private void SourceFiles_DoubleClick(object sender, EventArgs e)
{
SourceDisplay.SelectSourceEntry(new SourceEntry((string)SourceFiles.SelectedItem, new string[] { }, 0, 1), false, true /* iop */);
}
private void IOPDebugger_FormClosed(object sender, FormClosedEventArgs e)
{
_sourceMap.Save();
}
private SourceMap _sourceMap;
private DSystem _system;
}
}

120
D/Debugger/IOPDebugger.resx Normal file
View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

235
D/Debugger/LoadMapDialog.Designer.cs generated Normal file
View File

@@ -0,0 +1,235 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace D.Debugger
{
partial class LoadMapDialog
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.CurrentLoadsList = new System.Windows.Forms.ListBox();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.LoadNameText = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.LoadStartBox = new System.Windows.Forms.TextBox();
this.LoadEndBox = new System.Windows.Forms.TextBox();
this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.CreateLoadButton = new System.Windows.Forms.Button();
this.CancelCreateButton = new System.Windows.Forms.Button();
this.AddButton = new System.Windows.Forms.Button();
this.CancelAddButton = new System.Windows.Forms.Button();
this.groupBox1.SuspendLayout();
this.groupBox2.SuspendLayout();
this.SuspendLayout();
//
// groupBox1
//
this.groupBox1.Controls.Add(this.CancelAddButton);
this.groupBox1.Controls.Add(this.AddButton);
this.groupBox1.Controls.Add(this.CurrentLoadsList);
this.groupBox1.Location = new System.Drawing.Point(5, 5);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(171, 137);
this.groupBox1.TabIndex = 0;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "Add to Existing Microcode Load";
//
// CurrentLoadsList
//
this.CurrentLoadsList.FormattingEnabled = true;
this.CurrentLoadsList.Location = new System.Drawing.Point(7, 16);
this.CurrentLoadsList.Name = "CurrentLoadsList";
this.CurrentLoadsList.Size = new System.Drawing.Size(156, 82);
this.CurrentLoadsList.TabIndex = 0;
this.CurrentLoadsList.SelectedIndexChanged += new System.EventHandler(this.OnSelectionChanged);
//
// groupBox2
//
this.groupBox2.Controls.Add(this.CancelCreateButton);
this.groupBox2.Controls.Add(this.CreateLoadButton);
this.groupBox2.Controls.Add(this.label3);
this.groupBox2.Controls.Add(this.label2);
this.groupBox2.Controls.Add(this.LoadEndBox);
this.groupBox2.Controls.Add(this.LoadStartBox);
this.groupBox2.Controls.Add(this.label1);
this.groupBox2.Controls.Add(this.LoadNameText);
this.groupBox2.Location = new System.Drawing.Point(182, 5);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(178, 137);
this.groupBox2.TabIndex = 1;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "Add to New Microcode Load";
//
// LoadNameText
//
this.LoadNameText.Location = new System.Drawing.Point(51, 17);
this.LoadNameText.Name = "LoadNameText";
this.LoadNameText.Size = new System.Drawing.Size(103, 20);
this.LoadNameText.TabIndex = 0;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(10, 20);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(38, 13);
this.label1.TabIndex = 1;
this.label1.Text = "Name:";
//
// LoadStartBox
//
this.LoadStartBox.Location = new System.Drawing.Point(51, 44);
this.LoadStartBox.Name = "LoadStartBox";
this.LoadStartBox.Size = new System.Drawing.Size(100, 20);
this.LoadStartBox.TabIndex = 2;
//
// LoadEndBox
//
this.LoadEndBox.Location = new System.Drawing.Point(51, 71);
this.LoadEndBox.Name = "LoadEndBox";
this.LoadEndBox.Size = new System.Drawing.Size(100, 20);
this.LoadEndBox.TabIndex = 3;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(10, 47);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(32, 13);
this.label2.TabIndex = 4;
this.label2.Text = "Start:";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(10, 74);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(29, 13);
this.label3.TabIndex = 5;
this.label3.Text = "End:";
//
// CreateLoadButton
//
this.CreateLoadButton.Location = new System.Drawing.Point(13, 104);
this.CreateLoadButton.Name = "CreateLoadButton";
this.CreateLoadButton.Size = new System.Drawing.Size(75, 23);
this.CreateLoadButton.TabIndex = 6;
this.CreateLoadButton.Text = "Create";
this.CreateLoadButton.UseVisualStyleBackColor = true;
this.CreateLoadButton.Click += new System.EventHandler(this.CreateLoadButton_Click);
//
// CancelCreateButton
//
this.CancelCreateButton.Location = new System.Drawing.Point(94, 104);
this.CancelCreateButton.Name = "CancelCreateButton";
this.CancelCreateButton.Size = new System.Drawing.Size(75, 23);
this.CancelCreateButton.TabIndex = 7;
this.CancelCreateButton.Text = "Cancel";
this.CancelCreateButton.UseVisualStyleBackColor = true;
this.CancelCreateButton.Click += new System.EventHandler(this.CancelCreateButton_Click);
//
// AddButton
//
this.AddButton.Location = new System.Drawing.Point(7, 104);
this.AddButton.Name = "AddButton";
this.AddButton.Size = new System.Drawing.Size(75, 23);
this.AddButton.TabIndex = 7;
this.AddButton.Text = "Add";
this.AddButton.UseVisualStyleBackColor = true;
this.AddButton.Click += new System.EventHandler(this.AddButton_Click);
//
// CancelAddButton
//
this.CancelAddButton.Location = new System.Drawing.Point(88, 104);
this.CancelAddButton.Name = "CancelAddButton";
this.CancelAddButton.Size = new System.Drawing.Size(75, 23);
this.CancelAddButton.TabIndex = 8;
this.CancelAddButton.Text = "Cancel";
this.CancelAddButton.UseVisualStyleBackColor = true;
this.CancelAddButton.Click += new System.EventHandler(this.CancelAddButton_Click);
//
// LoadMapDialog
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(365, 146);
this.Controls.Add(this.groupBox2);
this.Controls.Add(this.groupBox1);
this.Name = "LoadMapDialog";
this.Text = "Select Microcode Load For File";
this.groupBox1.ResumeLayout(false);
this.groupBox2.ResumeLayout(false);
this.groupBox2.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.Button CancelAddButton;
private System.Windows.Forms.Button AddButton;
private System.Windows.Forms.ListBox CurrentLoadsList;
private System.Windows.Forms.GroupBox groupBox2;
private System.Windows.Forms.Button CancelCreateButton;
private System.Windows.Forms.Button CreateLoadButton;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.TextBox LoadEndBox;
private System.Windows.Forms.TextBox LoadStartBox;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox LoadNameText;
}
}

140
D/Debugger/LoadMapDialog.cs Normal file
View File

@@ -0,0 +1,140 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
namespace D.Debugger
{
public partial class LoadMapDialog : Form
{
/// <summary>
/// Provide basic UI for adding new entries.
/// TODO: It's kind of ugly that this is directly modifying input parameters; likely there should be a
/// singleton LoadMap that maintains all of this that can be used to modify things.
/// </summary>
/// <param name="newFilePath"></param>
/// <param name="loadMap"></param>
/// <param name="sourceMaps"></param>
/// <param name="microcodeRAM"></param>
public LoadMapDialog(string newFilePath, MicrocodeLoadMap loadMap, List<SourceMap> sourceMaps, ulong[] microcodeRAM)
{
InitializeComponent();
_newFilePath = newFilePath;
_loadMap = loadMap;
_sourceMaps = sourceMaps;
_microcodeRAM = microcodeRAM;
_selectedMap = null;
//
// Populate the existing map list
//
foreach (SourceMap s in sourceMaps)
{
CurrentLoadsList.Items.Add(s.MapName);
}
CurrentLoadsList.ClearSelected();
DialogResult = DialogResult.Cancel;
}
public SourceMap SelectedMap
{
get { return _selectedMap; }
}
private void OnSelectionChanged(object sender, EventArgs e)
{
AddButton.Enabled = CurrentLoadsList.SelectedItem != null;
}
private void CancelAddButton_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.Cancel;
this.Close();
}
private void CancelCreateButton_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.Cancel;
this.Close();
}
private void AddButton_Click(object sender, EventArgs e)
{
//
// Add the file to the selected map.
//
_selectedMap = _sourceMaps[CurrentLoadsList.SelectedIndex];
_selectedMap.AddSourceFile(_newFilePath);
DialogResult = DialogResult.OK;
this.Close();
}
private void CreateLoadButton_Click(object sender, EventArgs e)
{
try
{
LoadMapEntry newEntry = _loadMap.AddEntry(
LoadNameText.Text,
Convert.ToInt32(LoadStartBox.Text.Trim(), 16),
Convert.ToInt32(LoadEndBox.Text.Trim(), 16),
_microcodeRAM
);
_selectedMap = new SourceMap(
newEntry.Name,
Path.Combine("CP", "Source", newEntry.MapName),
Path.Combine("CP", "Source")); // TODO: define this path somewheres.
_sourceMaps.Add(_selectedMap);
DialogResult = DialogResult.OK;
this.Close();
}
catch(Exception ex)
{
MessageBox.Show("Error: {0}", ex.Message);
}
}
private SourceMap _selectedMap;
private string _newFilePath;
private MicrocodeLoadMap _loadMap;
private List<SourceMap> _sourceMaps;
private ulong[] _microcodeRAM;
}
}

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,202 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using D.CP;
using System;
using System.Windows.Forms;
namespace D.Debugger
{
/// <summary>
/// Presents a view of microcode disassembly, with addresses and breakpoints.
///
/// MicrocodeDisplay provides a three-column view:
///
/// | Breakpoint | Address | Disassembly |
/// -------------------------------------------------------------------------
///
/// Breakpoint is editable (to allow setting breakpoints)
///
/// TODO: This is pretty similar in functionality to the IOP source view. I'm
/// sure there's some code that could be shared...
///
/// </summary>
public class MicrocodeDisplay : DataGridView
{
public MicrocodeDisplay()
{
this.VirtualMode = true;
this.RowHeadersVisible = false;
this.ReadOnly = false;
AddCheckboxColumn("B", DataGridViewAutoSizeColumnMode.ColumnHeader);
AddColumn("Address", true, DataGridViewAutoSizeColumnMode.ColumnHeader);
AddColumn("Disassembly", true, DataGridViewAutoSizeColumnMode.Fill);
}
/// <summary>
/// Returns the address selected in the disassembly. Since the row index and the
/// address are identical, we just return the selected row index (if any).
/// Returns -1 if nothing is selected.
/// </summary>
public int SelectedAddress
{
get { return this.SelectedCells.Count > 0 ? this.SelectedCells[0].RowIndex : -1; }
}
public void AttachCP(CentralProcessor cp)
{
_cp = cp;
this.RowCount = cp.MicrocodeRam.Length;
}
public void SelectAddress(int address)
{
if (address < 0 || address > _cp.MicrocodeRam.Length)
{
throw new InvalidOperationException("Invalid address.");
}
//
// Clear the current selection and move the selection to the first cell
// of the requested line.
//
this.ClearSelection();
this.Rows[address].Selected = true;
this.CurrentCell = this.Rows[address].Cells[0];
}
protected override void OnCurrentCellDirtyStateChanged(EventArgs e)
{
if (IsCurrentCellDirty)
{
//
// Force checkbox changes to commit immediately (rather than
// the default, which is to commit them when focus leaves the cell, which
// is really annoying.
//
if (CurrentCell is DataGridViewCheckBoxCell)
{
CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
base.OnCurrentCellDirtyStateChanged(e);
}
protected override void OnCellValueChanged(DataGridViewCellEventArgs e)
{
if (e.RowIndex < 0 || e.RowIndex > _cp.MicrocodeRam.Length)
{
base.OnCellValueChanged(e);
return;
}
switch (e.ColumnIndex)
{
case 0: // breakpoint
{
// grab the check value
bool cellValue = (bool)this.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue;
if (cellValue)
{
BreakpointManager.SetBreakpoint(new BreakpointEntry(BreakpointProcessor.CP, BreakpointType.Execution, (ushort)e.RowIndex));
}
else
{
BreakpointManager.SetBreakpoint(new BreakpointEntry(BreakpointProcessor.CP, BreakpointType.None, (ushort)e.RowIndex));
}
}
break;
}
base.OnCellValueChanged(e);
}
protected override void OnCellValueNeeded(DataGridViewCellValueEventArgs e)
{
if (e.RowIndex > _cp.MicrocodeRam.Length)
{
// Past end of microcode, nothing to do.
base.OnCellValueNeeded(e);
return;
}
switch (e.ColumnIndex)
{
case 0:
{
ushort address = (ushort)e.RowIndex;
e.Value = BreakpointManager.GetBreakpoint(BreakpointProcessor.CP, address) != BreakpointType.None;
}
break;
case 1:
e.Value = String.Format("{0:x3}", e.RowIndex);
break;
case 2:
e.Value = new Microinstruction(_cp.MicrocodeRam[e.RowIndex]).Disassemble(-1);
break;
default:
throw new InvalidOperationException("Unhandled column.");
}
base.OnCellValueNeeded(e);
}
private void AddColumn(string name, bool readOnly, DataGridViewAutoSizeColumnMode sizeMode)
{
int index = this.Columns.Add(name, name);
this.Columns[index].ReadOnly = readOnly;
this.Columns[index].Resizable = DataGridViewTriState.False;
this.Columns[index].SortMode = DataGridViewColumnSortMode.NotSortable;
this.Columns[index].AutoSizeMode = sizeMode;
}
private void AddCheckboxColumn(string name, DataGridViewAutoSizeColumnMode sizeMode)
{
int index = this.Columns.Add(new DataGridViewCheckBoxColumn());
this.Columns[index].HeaderText = name;
this.Columns[index].ReadOnly = false;
this.Columns[index].Resizable = DataGridViewTriState.False;
this.Columns[index].SortMode = DataGridViewColumnSortMode.NotSortable;
this.Columns[index].AutoSizeMode = sizeMode;
}
private CentralProcessor _cp;
}
}

View File

@@ -0,0 +1,252 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace D.Debugger
{
public struct LoadMapEntry
{
public LoadMapEntry(string name, string mapName, int start, int end, byte[] hash)
{
Name = name;
MapName = mapName;
Start = start;
End = end;
Hash = hash;
}
/// <summary>
/// A friendly name for this load (i.e. "Phase 0 Microcode")
/// </summary>
public string Name;
/// <summary>
/// The file name for the source map for this load
/// </summary>
public string MapName;
/// <summary>
/// Beginning of address range for microcode load
/// </summary>
public int Start;
/// <summary>
/// End of range (inclusive)
/// </summary>
public int End;
/// <summary>
/// MD5 hash of microcode memory for range
/// </summary>
public byte[] Hash;
}
/// <summary>
/// MicrocodeLoadMap keeps track of a set of LoadMapEntries, each of which
/// specifies a map from a memory range + checksum to a source code mapping (i.e. symbol table).
/// This source code map is identical to the map used for the 8085 source map.
///
/// This should in theory allow for more-or-less automagical mapping of whatever's in microcode
/// RAM to the appropriate source files (assuming I've done the gruntwork of actually doing the
/// mapping beforehand.)
///
/// </summary>
public class MicrocodeLoadMap
{
public MicrocodeLoadMap()
{
_mapEntries = new List<LoadMapEntry>();
}
public LoadMapEntry AddEntry(string name, int start, int end, ulong[] microcodeRAM)
{
if (end <= start || start > microcodeRAM.Length || end > microcodeRAM.Length)
{
throw new InvalidOperationException("Invalid start/end parameters.");
}
//
// Ensure no duplicate entries (by name, anyway...)
//
foreach(LoadMapEntry e in _mapEntries)
{
if (e.Name.ToLowerInvariant() == name.ToLowerInvariant())
{
throw new InvalidOperationException("Duplicate map entry name.");
}
}
// Generate a map name
string mapName = name + "_map.txt";
// If the map file doesn't exist, create it now.
// calculate the MD5 hash
byte[] hash = ComputeHash(start, end, microcodeRAM);
LoadMapEntry newEntry = new LoadMapEntry(name, mapName, start, end, hash);
_mapEntries.Add(newEntry);
return newEntry;
}
public List<LoadMapEntry> FindEntries(ulong[] microcodeRAM)
{
//
// Given the provided microcode RAM, Walk the entries we know about and
// see which ones match, if any.
//
List<LoadMapEntry> foundEntries = new List<LoadMapEntry>();
foreach(LoadMapEntry e in _mapEntries)
{
//
// Hash the memory range specified by this entry and see if it matches.
//
byte[] hash = ComputeHash(e.Start, e.End, microcodeRAM);
bool match = true;
for (int i = 0; i < hash.Length; i++)
{
if (hash[i] != e.Hash[i])
{
match = false;
break;
}
}
if (match)
{
foundEntries.Add(e);
}
}
return foundEntries;
}
public void Save(string path)
{
using (StreamWriter sw = new StreamWriter(path))
{
//
// Each entry looks like:
//
// <name>,<mapname>,<start>,<end>,<hash>
// where:
// <name> and <mapname> are strings,
// <start> and <end> are hexadecimal values
// <hash> is written as a series of ascii hex digits
//
// empty lines or lines beginning with "#" are ignored.
//
// And that's it!
//
foreach (LoadMapEntry e in _mapEntries)
{
StringBuilder hashText = new StringBuilder();
for (int i = 0; i < e.Hash.Length; i++)
{
hashText.AppendFormat("{0:x2}", e.Hash[i]);
}
sw.WriteLine("{0},{1},{2:x3},{3:x3},{4}", e.Name, e.MapName, e.Start, e.End, hashText.ToString());
}
}
}
public void Load(string path)
{
using (StreamReader sr = new StreamReader(path))
{
_mapEntries.Clear();
//
// See "Save" for the format we're dealing with here.
//
while(!sr.EndOfStream)
{
string line = sr.ReadLine();
if (string.IsNullOrWhiteSpace(line) || line.StartsWith("#"))
{
continue;
}
string[] tokens = line.Split(',');
string hashString = tokens[4].Trim();
byte[] hash = new byte[hashString.Length / 2];
for (int i = 0; i < hashString.Length; i += 2)
{
hash[i / 2] = Convert.ToByte(hashString.Substring(i, 2), 16);
}
LoadMapEntry e = new LoadMapEntry(
tokens[0], // name
tokens[1], // mapname
Convert.ToInt32(tokens[2].Trim(), 16), // start
Convert.ToInt32(tokens[3].Trim(), 16), // end
hash);
_mapEntries.Add(e);
}
}
}
private byte[] ComputeHash(int start, int end, ulong[] microcodeRAM)
{
//
// Create a byte[] of the microcode data because why not.
//
byte[] microcodeBytes = new byte[(end - start) * 8];
int microcodeIndex = 0;
for (int i = start; i < end; i++)
{
byte[] wordBytes = BitConverter.GetBytes(microcodeRAM[i]);
wordBytes.CopyTo(microcodeBytes, microcodeIndex);
microcodeIndex += 8;
}
MD5 md5 = MD5.Create();
return md5.ComputeHash(microcodeBytes);
}
private List<LoadMapEntry> _mapEntries;
}
}

435
D/Debugger/SourceDisplay.cs Normal file
View File

@@ -0,0 +1,435 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;
namespace D.Debugger
{
/// <summary>
/// Presents a source code view aligned with symbol addresses (where applicable)
/// and breakpoints. One source file is loaded into a SourceDisplay at a time.
///
/// SourceDisplay provides a three-column view:
///
/// | Breakpoint | Address | Source text |
/// -------------------------------------------------------------------------
///
/// Breakpoint and Address are editable (to allow setting breakpoints and to
/// allow modifying symbol table data)
///
/// </summary>
public class SourceDisplay : DataGridView
{
public SourceDisplay()
{
this.VirtualMode = true;
this.RowHeadersVisible = false;
AddCheckboxColumn("B", DataGridViewAutoSizeColumnMode.ColumnHeader);
AddColumn("Address", false, DataGridViewAutoSizeColumnMode.ColumnHeader);
AddColumn("Source", true, DataGridViewAutoSizeColumnMode.Fill);
}
public string CurrentSourceFile
{
get { return _currentSourceFile; }
}
/// <summary>
/// Returns the address of the selected line, if any has been assigned. Returns
/// -1 if there is no selection or if no address is available for the selected line.
/// </summary>
public int SelectedAddress
{
get
{
int address = -1;
if (_sourceMap != null &&
_currentSourceFile != null &&
this.SelectedCells.Count > 0)
{
int rowIndex = this.SelectedCells[0].RowIndex;
// TODO: factor this logic out w/cell input logic.
string cellValue = (string)this.Rows[rowIndex].Cells[1].Value;
try
{
// strip leading $ if any.
if (cellValue.StartsWith("$"))
{
cellValue = cellValue.Substring(1);
}
address = Convert.ToUInt16(cellValue, 16);
}
catch
{
address = -1;
}
}
return address;
}
}
public SourceMap SourceMap
{
get { return _sourceMap; }
}
public void SetSourceRoot(string sourceRoot)
{
_sourceRoot = sourceRoot;
}
public void AttachMap(SourceMap sourceMap)
{
_sourceMap = sourceMap;
}
/// <summary>
/// Loads the appropriate source file, brings the specified line into view
/// and highlights the specified line.
/// </summary>
/// <param name="entry"></param>
public void SelectSourceEntry(SourceEntry entry, bool readOnly, bool iop)
{
LoadSourceFile(entry.SourcePath);
SelectLine(entry.LineNumber);
this.ReadOnly = readOnly;
_iopCode = iop;
}
protected override void OnCurrentCellDirtyStateChanged(EventArgs e)
{
if (IsCurrentCellDirty)
{
//
// Force checkbox changes to commit immediately (rather than
// the default, which is to commit them when focus leaves the cell, which
// is really annoying.
//
if (CurrentCell is DataGridViewCheckBoxCell)
{
CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
base.OnCurrentCellDirtyStateChanged(e);
}
protected override void OnCellValueChanged(DataGridViewCellEventArgs e)
{
if (e.RowIndex < 0 || e.RowIndex > _source.Count)
{
base.OnCellValueChanged(e);
return;
}
switch (e.ColumnIndex)
{
case 0: // breakpoint
{
// grab the check value
bool cellValue = (bool)this.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue;
ushort address = 0;
bool addressAvailable = _sourceMap != null ? _sourceMap.GetAddressForSource(new SourceEntry(_currentSourceFile, new string[] { }, 0, e.RowIndex), out address) : false;
if (addressAvailable)
{
if (cellValue)
{
BreakpointManager.SetBreakpoint(new BreakpointEntry(_iopCode ? BreakpointProcessor.IOP : BreakpointProcessor.CP, BreakpointType.Execution, address));
}
else
{
BreakpointManager.SetBreakpoint(new BreakpointEntry(_iopCode ? BreakpointProcessor.IOP : BreakpointProcessor.CP, BreakpointType.None, address));
}
}
}
break;
case 1: // address
{
string oldCellValue = ((string)this.Rows[e.RowIndex].Cells[e.ColumnIndex].Value).Trim();
string[] symbolTokens = ((string)this.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue).Trim().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
if (symbolTokens.Length > 2)
{
MessageBox.Show("Invalid syntax.");
return;
}
if (oldCellValue.StartsWith("$"))
{
oldCellValue = oldCellValue.Substring(1);
}
if (symbolTokens.Length == 0)
{
// cell is empty, delete the current source map entry if present.
// TODO: use source map for this instead?
if (_sourceMap != null && !string.IsNullOrEmpty(oldCellValue))
{
ushort oldAddress = Convert.ToUInt16(oldCellValue, 16);
_sourceMap.RemoveSourceEntry(new SourceEntry(_currentSourceFile, new string[] { }, oldAddress, e.RowIndex));
}
return;
}
//
// Valid new cell value.
//
string cellValue = symbolTokens[0];
string symbolName = symbolTokens.Length == 2 ? symbolTokens[1] : "*none*";
// strip leading $ if any.
if (cellValue.StartsWith("$"))
{
cellValue = cellValue.Substring(1);
}
try
{
ushort address = Convert.ToUInt16(cellValue, 16);
ushort oldAddress = string.IsNullOrWhiteSpace(oldCellValue) ? (ushort)0 : Convert.ToUInt16(oldCellValue, 16);
if (_sourceMap != null && address != oldAddress)
{
//
// Set the new value first.
//
_sourceMap.AddSourceEntry(new SourceEntry(_currentSourceFile, new string[] { symbolName }, address, e.RowIndex));
//
// Remove the old value from the database if there is one.
//
if (!string.IsNullOrWhiteSpace(oldCellValue))
{
_sourceMap.RemoveSourceEntry(new SourceEntry(_currentSourceFile, new string[] { }, oldAddress, e.RowIndex));
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Hey!");
// Invalid value, clear it.
// this.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = String.Empty;
}
}
break;
}
base.OnCellValueChanged(e);
}
protected override void OnCellValueNeeded(DataGridViewCellValueEventArgs e)
{
if (e.RowIndex > _source.Count)
{
// Past end of source, nothing to do.
return;
}
switch (e.ColumnIndex)
{
case 0:
{
ushort address = 0;
bool addressAvailable = _sourceMap != null ? _sourceMap.GetAddressForSource(new SourceEntry(_currentSourceFile, new string[] { }, 0, e.RowIndex), out address) : false;
if (addressAvailable)
{
e.Value = BreakpointManager.GetBreakpoint(_iopCode ? BreakpointProcessor.IOP : BreakpointProcessor.CP, address) != BreakpointType.None;
}
else
{
e.Value = false; // TODO: hook to breakpoint system
}
}
break;
case 1:
{
ushort address = 0;
bool addressAvailable = _sourceMap != null ? _sourceMap.GetAddressForSource(new SourceEntry(_currentSourceFile, new string[] { }, 0, e.RowIndex), out address) : false;
e.Value = addressAvailable ? String.Format("${0:x4}", address) : String.Empty;
}
break;
case 2:
e.Value = _source[e.RowIndex];
break;
default:
throw new InvalidOperationException("Unhandled column.");
}
base.OnCellValueNeeded(e);
}
private void LoadSourceFile(string sourcePath)
{
//
// Only do this if the file isn't currently loaded.
//
try
{
if (_currentSourceFile != sourcePath)
{
this.Invalidate();
_source = new List<string>();
using (StreamReader sr = new StreamReader(Path.Combine(_sourceRoot, sourcePath), Encoding.UTF8))
{
while (!sr.EndOfStream)
{
_source.Add(UnTabify(sr.ReadLine()));
}
}
this.RowCount = _source.Count;
_currentSourceFile = sourcePath;
}
}
catch(Exception e)
{
_source = new List<string>();
_source.Add(
String.Format("Unable to load source file {0}. Error: {1}",
sourcePath, e.Message));
}
}
/// <summary>
/// Converts tabs in the given string to 4 space tabulation. As it should be.
/// </summary>
/// <param name="tabified"></param>
/// <returns></returns>
private string UnTabify(string tabified)
{
StringBuilder untabified = new StringBuilder();
int column = 0;
foreach(char c in tabified)
{
if (c == '\t')
{
untabified.Append(" ");
column++;
while ((column % 4) != 0)
{
untabified.Append(" ");
column++;
}
}
if (c == _unicodeUnknown)
{
// TODO:
// We assume that if this happens it's the microcode source "arrow" symbol.
// C#'s StreamReader supports only Unicode/UTF and ASCII (7-bit) encodings and the
// Star's backarrow is an 8-bit character. I should really just rewrite the code
// to read the bytes in myself, this is a bodge for the time being.
untabified.Append(_arrowChar);
}
else
{
untabified.Append(c);
column++;
}
}
return untabified.ToString();
}
private void SelectLine(int lineNumber)
{
//
// Clear the current selection and move the selection to the first cell
// of the requested line.
//
if (lineNumber < this.Rows.Count)
{
this.Rows[lineNumber].Selected = true;
this.CurrentCell = this.Rows[lineNumber].Cells[0];
}
else
{
this.ClearSelection();
}
}
private void AddColumn(string name, bool readOnly, DataGridViewAutoSizeColumnMode sizeMode)
{
int index = this.Columns.Add(name, name);
this.Columns[index].ReadOnly = readOnly;
this.Columns[index].Resizable = DataGridViewTriState.False;
this.Columns[index].SortMode = DataGridViewColumnSortMode.NotSortable;
this.Columns[index].AutoSizeMode = sizeMode;
}
private void AddCheckboxColumn(string name, DataGridViewAutoSizeColumnMode sizeMode)
{
int index = this.Columns.Add(new DataGridViewCheckBoxColumn());
this.Columns[index].HeaderText = name;
this.Columns[index].ReadOnly = false;
this.Columns[index].Resizable = DataGridViewTriState.False;
this.Columns[index].SortMode = DataGridViewColumnSortMode.NotSortable;
this.Columns[index].AutoSizeMode = sizeMode;
}
private string _currentSourceFile;
private string _sourceRoot;
private List<string> _source;
private bool _iopCode;
private SourceMap _sourceMap;
//
// Character substitutions
//
private const char _arrowChar = '←';
private const char _unicodeUnknown = (char)0xfffd;
}
}

442
D/Debugger/SourceMap.cs Normal file
View File

@@ -0,0 +1,442 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace D.Debugger
{
public class SourceEntry
{
public SourceEntry(string sourcePath, string[] symbolNames, ushort address, int lineNumber)
{
SourcePath = sourcePath;
SymbolNames = symbolNames;
Address = address;
LineNumber = lineNumber;
}
public override string ToString()
{
return String.Format("{0}, line {1} address 0x{2:x4}", SourcePath, LineNumber, Address);
}
public string SourcePath;
public string[] SymbolNames;
public ushort Address;
public int LineNumber;
public static readonly SourceEntry Empty = new SourceEntry(String.Empty, new string[] { "*none*" }, 0, 0);
}
public class SourceMap
{
public SourceMap(string mapName, string mapFile, string sourceRoot)
{
_sourceFileToSourceEntryMap = new Dictionary<string, List<SourceEntry>>();
_orderedSourceEntries = new List<SourceEntry>();
ReadMap(mapFile, sourceRoot);
_mapName = mapName;
_mapFile = mapFile;
_sourceRoot = sourceRoot;
}
public string MapName
{
get { return _mapName; }
}
public string SourceRoot
{
get { return _sourceRoot; }
}
/// <summary>
/// Returns the list of source files referenced by this map.
/// </summary>
/// <returns></returns>
public List<string> GetSourceFiles()
{
return _sourceFileToSourceEntryMap.Keys.ToList();
}
public void Save()
{
string header =
@"#
# This file maps assembly source symbols to PROM/microcode addresses and source lines, forming a crude
# symbol table.
#
# Each source file is given a header a la:
# [FooSource.asm]
# (with brackets)
# Each line's syntax is:
# <symbol name 1>, .. , <symbol name N>: <address or value (hex)>,<line number(decimal) in current source file>
# where '*none*' is a special symbol name meaning no symbol mapping is present.";
using (StreamWriter sw = new StreamWriter(_mapFile))
{
// Write out a nice header
sw.Write(header);
sw.WriteLine();
// Write out each source file
foreach(string sourceFile in _sourceFileToSourceEntryMap.Keys)
{
sw.WriteLine("[{0}]", sourceFile);
foreach(SourceEntry entry in _sourceFileToSourceEntryMap[sourceFile])
{
StringBuilder symbolList = new StringBuilder();
for (int i = 0; i < entry.SymbolNames.Length; i++)
{
symbolList.AppendFormat(i < entry.SymbolNames.Length - 1 ? "{0}," : "{0}", entry.SymbolNames[i]);
}
sw.WriteLine("{0}: 0x{1:x4},{2}", symbolList.ToString(), entry.Address, entry.LineNumber + 1); // line numbers are 1-indexed
}
sw.WriteLine();
}
}
}
public SourceEntry GetSourceForAddress(ushort address)
{
SourceEntry result = null;
//
// Find the SourceEntry nearest this address, if there is one.
//
foreach(SourceEntry entry in _orderedSourceEntries)
{
if (entry.Address > address)
{
break;
}
result = entry;
}
return result;
}
public SourceEntry GetExactSourceForAddress(ushort address)
{
//
// Find the SourceEntry for this address, if there is one.
//
foreach (SourceEntry entry in _orderedSourceEntries)
{
if (entry.Address == address)
{
return entry;
}
}
return null;
}
public SourceEntry GetNearestSymbolForAddress(ushort address)
{
SourceEntry result = null;
//
// Find the SourceEntry nearest this address that has a symbol name defined, if there is one.
//
foreach (SourceEntry entry in _orderedSourceEntries)
{
if (entry.Address > address)
{
break;
}
if (entry.SymbolNames.Length > 0 &&
entry.SymbolNames[0] != "*none*") // TODO: move to constant
{
result = entry;
}
}
return result;
}
public bool GetAddressForSource(SourceEntry entry, out ushort address)
{
bool found = false;
address = 0;
//
// Find the source line entry that matches.
//
if (_sourceFileToSourceEntryMap.ContainsKey(entry.SourcePath))
{
foreach(SourceEntry line in _sourceFileToSourceEntryMap[entry.SourcePath])
{
if (line.LineNumber == entry.LineNumber)
{
found = true;
address = line.Address;
break;
}
}
}
return found;
}
public void AddSourceEntry(SourceEntry entry)
{
// InsertAddressEntry will ensure that no duplicate addresses are added.
InsertAddressEntry(entry);
InsertSourceEntry(entry);
}
public void AddSourceFile(string sourcePath)
{
if (!_sourceFileToSourceEntryMap.ContainsKey(sourcePath))
{
List<SourceEntry> newList = new List<SourceEntry>();
_sourceFileToSourceEntryMap.Add(sourcePath, newList);
}
else
{
throw new InvalidOperationException("Source file already exists in map.");
}
}
public void RemoveSourceEntry(SourceEntry entry)
{
// Remove from address table
for (int i = 0; i < _orderedSourceEntries.Count; i++)
{
if (_orderedSourceEntries[i].Address == entry.Address)
{
_orderedSourceEntries.RemoveAt(i);
break;
}
}
// Remove from source table if present
if (_sourceFileToSourceEntryMap.ContainsKey(entry.SourcePath))
{
for (int i = 0; i < _sourceFileToSourceEntryMap[entry.SourcePath].Count; i++)
{
if (_sourceFileToSourceEntryMap[entry.SourcePath][i].Address == entry.Address)
{
_sourceFileToSourceEntryMap[entry.SourcePath].RemoveAt(i);
break;
}
}
}
}
private void ReadMap(string mapFile, string sourceRoot)
{
//
// If the file does not exist, we will just start with an empty map.
//
if (!File.Exists(mapFile))
{
return;
}
using (StreamReader map = new StreamReader(mapFile))
{
string sourceFile = string.Empty;
ReadState state = ReadState.NextFileHeader;
int mapLineNumber = 0;
while (!map.EndOfStream)
{
string line = map.ReadLine().Trim();
mapLineNumber++;
if (string.IsNullOrWhiteSpace(line) ||
line.StartsWith("#"))
{
// Nothing of note here, continue.
continue;
}
if (line.StartsWith("["))
{
// Looks like the start of a file header.
state = ReadState.NextFileHeader;
}
switch(state)
{
case ReadState.NextFileHeader:
// We expect the line to be in the form "[<source path>]"
// If this is not the case, then the map file is incorrectly formed.
if (line.StartsWith("["))
{
int closingBracket = line.LastIndexOf(']');
if (closingBracket < 0)
{
throw new InvalidOperationException(
String.Format("Badly formed source file entry on line {0}", mapLineNumber));
}
sourceFile = line.Substring(1, closingBracket - 1).Trim();
state = ReadState.NextSymbolEntry;
}
else
{
throw new InvalidOperationException(
String.Format("Expected file header on line {0}", mapLineNumber));
}
break;
case ReadState.NextSymbolEntry:
//
// This is expected to be a symbol map entry, which looks like
// <symbol name 1>, .. , <symbol name N> : <address or value (hex)>,<line number(decimal) in current source file>
//
string[] symbolAddressTokens = line.Split(':');
if (symbolAddressTokens.Length != 2)
{
// Should be two tokens here, one on each side of the ':'
throw new InvalidOperationException(
String.Format("Badly formed symbol entry on line {0}", mapLineNumber));
}
// Grab the symbol names. There must be at least one present since the above split succeeded.
string[] symbolNames = symbolAddressTokens[0].Trim().Split(',');
// Grab the source information. There must be exactly two entries.
string[] sourceData = symbolAddressTokens[1].Trim().Split(',');
if (sourceData.Length != 2)
{
throw new InvalidOperationException(
String.Format("Badly formed symbol entry on line {0} -- source information is invalid.", mapLineNumber));
}
// Convert source info into integers and build a new SourceEntry.
int lineNumber = 0;
ushort address = 0;
try
{
address = (ushort)Convert.ToInt32(sourceData[0].Trim(), 16);
lineNumber = int.Parse(sourceData[1].Trim()) - 1; // line numbers are 1-indexed
}
catch(Exception)
{
throw new InvalidOperationException(
String.Format("Badly formed symbol entry on line {0} -- source information is invalid.", mapLineNumber));
}
SourceEntry newEntry = new SourceEntry(sourceFile, symbolNames, address, lineNumber);
AddSourceEntry(newEntry);
break;
}
}
}
}
private void InsertAddressEntry(SourceEntry entry)
{
if (_orderedSourceEntries.Count == 0)
{
_orderedSourceEntries.Add(entry);
return;
}
// Find the first entry that has an address greater than the new entry's.
//
for(int i=0;i<_orderedSourceEntries.Count;i++)
{
// Sanity check -- if these entries have equal addresses then we need to stop here.
if (_orderedSourceEntries[i].Address == entry.Address)
{
throw new InvalidOperationException(
String.Format("Duplicate address {0:x4} in source map.", entry.Address));
}
if (_orderedSourceEntries[i].Address > entry.Address)
{
_orderedSourceEntries.Insert(i, entry);
return;
}
}
//
// If we get here, then this address is greater than any already in the list, so we add it at the end.
//
_orderedSourceEntries.Add(entry);
}
private void InsertSourceEntry(SourceEntry entry)
{
if (!_sourceFileToSourceEntryMap.ContainsKey(entry.SourcePath))
{
List<SourceEntry> newList = new List<SourceEntry>();
newList.Add(entry);
_sourceFileToSourceEntryMap.Add(entry.SourcePath, newList);
}
else
{
_sourceFileToSourceEntryMap[entry.SourcePath].Add(entry);
}
}
//
// Maps for quick lookups
//
private Dictionary<string, List<SourceEntry>> _sourceFileToSourceEntryMap;
//
// Ordered list for quick search by address
//
private List<SourceEntry> _orderedSourceEntries;
private string _mapName;
private string _mapFile;
private string _sourceRoot;
enum ReadState
{
NextFileHeader,
NextSymbolEntry,
}
}
}

BIN
D/Disks/130P26300-diags.IMD Normal file

Binary file not shown.

Binary file not shown.

BIN
D/Disks/Harmony.img Normal file

Binary file not shown.

BIN
D/Disks/Koto.img Normal file

Binary file not shown.

BIN
D/Disks/Lyric.img Normal file

Binary file not shown.

BIN
D/Disks/Medley.img Normal file

Binary file not shown.

Binary file not shown.

BIN
D/Disks/XDE.img Normal file

Binary file not shown.

BIN
D/Disks/XDE_5.0_BO1.IMD Normal file

Binary file not shown.

View File

@@ -0,0 +1,295 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using D.CP;
using D.Logging;
using System.Collections.Generic;
namespace D.Display
{
public class DisplayController
{
public DisplayController(DSystem system)
{
_system = system;
_lostSyncEvent = new Event(_lostSyncInterval, null, LostSyncCallback);
_fifo = new Queue<ushort>(16);
}
public void Reset()
{
_displayOn = false;
_blank = false;
_picture = false;
_invert = false;
_oddLine = false;
_scanline = 0;
_fifo.Clear();
if (_system.Display != null)
{
_system.Display.Clear();
}
}
public bool DisplayOn
{
get { return _displayOn; }
}
public void ClrDpRq()
{
//
// Put the display task to sleep
//
_system.CP.SleepTask(TaskType.Display);
}
public void SetDCtlFifo(ushort value)
{
// if (Log.Enabled) Log.Write(LogType.Verbose, LogComponent.DisplayControl, "DCtlFIFO<-0x{0:x4}", value);
if (_fifo.Count < 16)
{
_fifo.Enqueue(value);
}
else
{
if (Log.Enabled) Log.Write(LogType.Error, LogComponent.DisplayControl, "DCtlFIFO: FIFO overflow, word dropped.");
}
}
public void SetDCtl(ushort value)
{
bool displayOn = _displayOn;
_displayOn = (value & 0x01) != 0;
_blank = (value & 0x02) != 0;
_picture = (value & 0x04) != 0;
_invert = (value & 0x08) != 0;
if ((value & 0x20) != 0)
{
// Vertical Sync -- back to the top of the screen
_scanline = 0;
_oddLine = (value & 0x10) != 0;
_syncPresent = true;
_system.Display.Render();
}
if ((value & 0x40) == 0)
{
// Clear control fifo
_fifo.Clear();
}
if (!displayOn && _displayOn)
{
// Kick off the horizontal retrace callback since we're turning the display on.
_system.Scheduler.Schedule(_horizontalRetraceDelay, HorizontalRetraceCallback);
_system.Scheduler.Cancel(_lostSyncEvent);
_lostSyncEvent = _system.Scheduler.Schedule(_lostSyncInterval, LostSyncCallback);
}
else if (!_displayOn)
{
//
// Put the display task to sleep.
//
_system.CP.SleepTask(TaskType.Display);
}
if (Log.Enabled) Log.Write(LogType.Verbose, LogComponent.DisplayControl, "DCtl<-0x{0:x4}: On={1} Blank={2} Picture={3} Invert={4} Odd={5}"
, value,
_displayOn,
_blank,
_picture,
_invert,
_oddLine);
}
public void SetDBorder(ushort value)
{
_displayBorder = value;
if (Log.Enabled) Log.Write(LogType.Verbose, LogComponent.DisplayControl, "DBorder<-0x{0:x4}", value);
}
/// <summary>
/// Invoked at the end of every scanline: Wake display task, update state, schedule next callback
/// as necessary.
/// </summary>
/// <param name="skewNsec"></param>
/// <param name="context"></param>
private void HorizontalRetraceCallback(ulong skewNsec, object context)
{
int visibleOffset = _oddLine ? 37 : 36;
int effectiveScanline = _scanline - visibleOffset;
//
// Render this scanline, if there's anything to do.
//
if (_blank)
{
// Render blank scanline (no border, no picture)
for (int i = 0; i < _scanlineData.Length; i++)
{
_scanlineData[i] = 0;
}
}
else
{
if (_picture)
{
// Normal line : 32 bits of border pattern, 1024 bits of display, 32 bits of border pattern
// Border pattern: low byte on lines 4n, 4n+1; high byte on 4n+2, 4n+3.
int patternByte = (effectiveScanline & 0x2) == 0 ? _displayBorder & 0xff : _displayBorder >> 8;
ushort patternWord = (ushort)(patternByte | (patternByte << 8));
_scanlineData[0] = patternWord;
_scanlineData[1] = patternWord;
if (_fifo.Count > 0)
{
// Grab first segment from FIFO
ushort fifoWord = _fifo.Dequeue();
int lastWord = fifoWord >> 10;
int lineNumber = fifoWord & 0x3ff;
bool valid = false;
for (int word = 0; word < 64; word++)
{
_scanlineData[word + 2] = _system.MemoryController.DebugMemory.ReadWord((lineNumber << 6) | word, out valid);
// Grab next segment if this isn't the last word in the scanline.
if (word != 63 && word == lastWord && _fifo.Count > 0)
{
fifoWord = _fifo.Dequeue();
lastWord = fifoWord >> 10;
lineNumber = fifoWord & 0x3ff;
}
}
}
else
{
// Blank out display words, nothing in the FIFO.
for (int i = 2; i < 64; i++)
{
_scanlineData[i] = 0;
}
}
_scanlineData[66] = patternWord;
_scanlineData[67] = patternWord;
}
else
{
// Just display the border pattern everywhere:
// low byte on lines 4n, 4n+1; high byte on 4n+2, 4n+3.
int patternByte = (effectiveScanline & 0x2) == 0 ? _displayBorder & 0xff : _displayBorder >> 8;
ushort patternWord = (ushort)(patternByte | (patternByte << 8));
for (int i = 0; i < _scanlineData.Length; i++)
{
_scanlineData[i] = patternWord;
}
}
}
if (effectiveScanline > 0 && effectiveScanline < 860)
{
// Render to screen
_system.Display.DrawScanline(effectiveScanline, _scanlineData, _invert);
}
// Move to next scanline
_scanline += 2;
//
// Schedule next retrace as long as the display is still on.
//
if (_displayOn)
{
_system.Scheduler.Schedule(_horizontalRetraceDelay, HorizontalRetraceCallback);
//
// End of scanline: Wake up the display task.
//
_system.CP.WakeTask(TaskType.Display);
}
}
private void LostSyncCallback(ulong skewNsec, object context)
{
if (_syncPresent)
{
//
// Got sync, keep the display alive and reschedule ourselves.
_lostSyncEvent = _system.Scheduler.Schedule(_lostSyncInterval, LostSyncCallback);
}
else
{
// No sync since the last callback, blank the display and stop the sync callback.
_system.Display.Clear();
}
_syncPresent = false;
}
// Control bits
private bool _displayOn;
private bool _blank;
private bool _picture;
private bool _invert;
private bool _oddLine;
// Border bitmap
private ushort _displayBorder;
// Control FIFO. Max 16 entries.
private Queue<ushort> _fifo;
// Scanline
private int _scanline;
private ushort[] _scanlineData = new ushort[64 + 4]; // 1024 bits picture, 32 bits border on either side
private bool _syncPresent;
private DSystem _system;
//
// Timing and events
//
private readonly ulong _horizontalRetraceDelay = (ulong)(28.8 * Conversion.UsecToNsec); // 28.8uS
private Event _lostSyncEvent;
private readonly ulong _lostSyncInterval = (ulong)(52.91 * Conversion.MsecToNsec); // 53ms (one frame time)
}
}

94
D/Ethernet/CRC32.cs Normal file
View File

@@ -0,0 +1,94 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace D.Ethernet
{
public class CRC32
{
static CRC32()
{
//
// Initialize the CRC table.
//
uint temp = 0;
for (uint i = 0; i < _crcTable.Length; i++)
{
temp = i;
for (int j = 8; j > 0; j--)
{
if ((temp & 1) == 1)
{
temp = (uint)((temp >> 1) ^ _polynomial);
}
else
{
temp >>= 1;
}
}
_crcTable[i] = temp;
}
}
public CRC32()
{
Reset();
}
public uint Checksum
{
get { return ~_checksum; }
}
public void Reset()
{
_checksum = 0xffffffff;
}
public void AddToChecksum(ushort word)
{
byte[] bytes = new byte[2];
bytes[0] = (byte)(word >> 8);
bytes[1] = (byte)word;
for (int i = 0; i < bytes.Length; i++)
{
byte index = (byte)((_checksum ^ bytes[i]) & 0xff);
_checksum = (_checksum >> 8) ^ _crcTable[index];
}
}
private uint _checksum;
private static uint[] _crcTable = new uint[256];
private const uint _polynomial = 0xedb88320;
}
}

View File

@@ -0,0 +1,849 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using D.CP;
using D.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
namespace D.Ethernet
{
/// <summary>
/// EthernetController implements the Star's Ethernet controller.
/// At this time, no official documentation exists; only microcode listings and the schematic drawings.
/// The code is implemented based on study of the schematics and diagnostic microcode.
/// As such there is a lot of handwavy stuff in here, especially around loopback. At this time enough
/// is implemented to get boot diagnostics to pass.
///
/// Questions remaining to be answered:
/// - What is the distinction between Loopback and LocalLoopback? Initially it looked like LocalLoopback only
/// looped back through the FIFO, but it appears to invoke CRC generation and microcode comments make it look
/// like transmission actually takes place? At this time both are treated identically.
/// - How is the FIFO actually controlled during loopback? (How does the transmit hardware know when to stop
/// transmitting when the FIFO is (apparently) being used for both transmit and receive at the same time during
/// a loopback operation?)
///
/// - Why is CRC calculation not working the way I expect? The residual sum does not appear to work out to
/// the expected value for Ethernet CRC32.
///
/// </summary>
public class EthernetController
{
public EthernetController(DSystem system)
{
_system = system;
_fifo = new Queue<ushort>();
_inputPacket = new Queue<ushort>();
_outputPacket = new Queue<ushort>();
_pendingPackets = new Queue<MemoryStream>();
_crc32 = new CRC32();
// Attach real Ethernet device if user has specified one, otherwise leave unattached; output data
// will go into a bit-bucket.
try
{
if (Configuration.HostRawEthernetInterfacesAvailable &&
!string.IsNullOrWhiteSpace(Configuration.HostPacketInterfaceName))
{
_hostInterface = new HostEthernetEncapsulation(Configuration.HostPacketInterfaceName);
_hostInterface.RegisterReceiveCallback(OnHostPacketReceived);
}
}
catch (Exception e)
{
_hostInterface = null;
Log.Write(LogComponent.HostEthernet, "Unable to configure network interface. Error {0}", e.Message);
}
_readerLock = new ReaderWriterLockSlim();
// Start the ethernet reciever poll event, this will run forever.
_system.Scheduler.Schedule(_receiverPollInterval, ReceiverPollCallback);
Reset();
}
public void Reset()
{
_turnOff_ = false;
_rxEvenLen = false;
_rxGoodCRC = false;
_rxOverrun_ = true;
_txUnderrun = false;
_txCollision_ = true;
_rxMode_ = true;
_enableTx = false;
_lastWord = false;
_enableRcv = false;
_localLoop = false;
_loopBack = false;
_defer = false;
_purge = false;
_tickElapsed = false;
_outAttn = false;
_inAttn = false;
_transmitterRunning = false;
_outputData = 0;
_outputDataLatched = false;
_fifo.Clear();
_inputPacket.Clear();
_crc32.Reset();
}
public int EtherDisp()
{
//
// Pin 139(YIODisp.1) : Hooked to "Attn," which appear to be whether any attention is needed by the receiver
// or transmitter.
// Pin 39(YIODisp.0) : "(schematic) Must be zero for the transmitting inner loop uCode. It is also used to
// determine if the Option card is plugged in."
//
int value = 0;
if (_turnOff_)
{
//
// Ethernet is not turned off, returned value is based on whether
// the transmitter or reciever hardware has a status to report.
value = _outAttn | _inAttn ? 1 : 0;
}
return value;
}
public ushort EStatus()
{
ushort value = (ushort)
~((_turnOff_ ? 0x0001 : 0x00) |
(_rxEvenLen ? 0x0002 : 0x00) |
(_rxGoodCRC ? 0x0004 : 0x00) |
(_rxOverrun_ ? 0x0008 : 0x00) |
(_rxGoodAlign ? 0x0010 : 0x00) |
(!_txUnderrun ? 0x0020 : 0x00) |
(_txCollision_ ? 0x0040 : 0x00) |
(_rxMode_ ? 0x0080 : 0x00) |
(_enableTx ? 0x0100 : 0x00) |
(_lastWord ? 0x0200 : 0x00) |
(_enableRcv ? 0x0400 : 0x00) |
(_localLoop ? 0x0800 : 0x00) |
(_loopBack ? 0x1000 : 0x00));
return value;
}
public void EOCtl(ushort value)
{
// EOCtl: Bit(etc)
// ----------------------------
// EnableTrn 15
// LastWord 14
// Defer 13
_enableTx = (value & 0x1) != 0;
_lastWord = (value & 0x2) != 0;
_defer = (value & 0x4) != 0;
_outAttn = false;
_txUnderrun = false;
_txCollision_ = true;
if (Log.Enabled) Log.Write(LogComponent.EthernetControl,
"EOCtl<- 0x{0:x4}: enableTx {1} lastWord {2} defer {3}",
value,
_enableTx,
_lastWord,
_defer
);
//
// Writing EOCtl resets the defer clock
//
_tickElapsed = false;
_system.Scheduler.Cancel(_deferEvent);
if (_defer)
{
//
// Queue up an event 51.2uS in the future, this will
// set _tickElapsed, update wakeups, and start the transmitter when it fires.
//
_deferEvent = _system.Scheduler.Schedule(_deferDelay, DeferCallback);
}
else
{
//
// Start the transmitter running if need be (if it isn't already).
//
if (_enableTx && !_transmitterRunning && !_lastWord)
{
_crc32.Reset();
StartTransmitter();
}
}
if (!_enableTx)
{
_fifo.Clear();
StopTransmitter();
}
UpdateWakeup();
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "EOCtl end.");
}
public void EICtl(ushort value)
{
// EICtl: Bit(xerox order)
// ------------------------------------
// EnableRcv 15
// TurnOff' 14
// LocalLoop 13
// LoopBack 12
_enableRcv = (value & 0x1) != 0;
_turnOff_ = (value & 0x2) == 0;
_localLoop = (value & 0x4) != 0;
_loopBack = (value & 0x8) != 0;
if (!_enableRcv)
{
// Reset receive state
_rxMode_ = true;
_receiverState = ReceiverState.Preamble;
_inAttn = false;
_rxGoodCRC = true;
_rxGoodAlign = true;
_rxOverrun_ = true;
_rxEvenLen = true;
if (!_loopBack)
{
_fifo.Clear();
}
_inputPacket.Clear();
_crc32.Reset();
}
UpdateWakeup();
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "EICtl<- 0x{0:x4} enablerx {1} turnOff' {2} localLoop {3} loopBack {4}.",
value,
_enableRcv,
_turnOff_,
_localLoop,
_loopBack);
}
public void EOData(ushort value)
{
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "EOData<- 0x{0:x4}.", value);
_outputData = value;
_outputDataLatched = true;
}
public void EStrobe(int cycle)
{
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "EStrobe.");
if ((cycle == 1 || cycle == 3) & !_lastWord)
{
// Strobe output data into FIFO.
if (!_outputDataLatched)
{
// This is not actually an underrun case, it indicates a case where the microcode is doing
// something we do not expect.
if (Log.Enabled) Log.Write(LogType.Error, LogComponent.EthernetControl, "EStrobe: no data latched.");
_outAttn = false;
}
//
// Move data from output data word into the FIFO.
//
if (_fifo.Count < 16)
{
_fifo.Enqueue(_outputData);
_outputDataLatched = false;
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "EStrobe: loaded word 0x{0:x4} into FIFO. FIFO count is now {1}",
_outputData, _fifo.Count);
_outAttn = false;
UpdateWakeup();
}
else
{
_fifo.Dequeue();
_fifo.Enqueue(_outputData);
// This should not happen; microcode should sleep when the FIFO is full.
if (Log.Enabled) Log.Write(LogType.Error, LogComponent.EthernetControl, "EStrobe: FIFO full, dropping word.");
}
}
else if (cycle == 2)
{
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "EStrobe: (cycle 2) flushing received data.");
// Throw out input data and stop the receiver
_fifo.Clear();
_inAttn = false;
_rxMode_ = true;
StopReceiver();
UpdateWakeup();
}
}
public ushort EIData(int cycle)
{
ushort value = 0;
//
// Read from the input/output FIFO.
//
if (_fifo.Count > 0)
{
value = _fifo.Dequeue();
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "<-EIData: Returning FIFO word 0x{0:x4}. FIFO count is now {1}",
value, _fifo.Count);
}
else
{
if (Log.Enabled) Log.Write(LogType.Error, LogComponent.EthernetControl, "<-EIData: FIFO empty.");
// TODO: does this cause an underrun?
}
return value;
}
private void DeferCallback(ulong skewNsec, object context)
{
_tickElapsed = true;
UpdateWakeup();
_tickElapsed = false;
//
// Start the transmitter.
//
if (_enableTx && !_transmitterRunning)
{
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "Defer complete, starting transmitter.");
StartTransmitter();
}
}
private void StartTransmitter()
{
//
// Start transmit clock running; this will wake up every
// 1600ns to pick up a word (if available) from the fifo
// and transmit it. (If Defer is active, we will do this
// after the deferral period has elapsed.)
//
if (!_transmitterRunning)
{
//
// First abort any transmit clock that may be running.
//
_system.Scheduler.Cancel(_transmitEvent);
//
// Schedule the transmission callback.
_transmitEvent = _system.Scheduler.Schedule(_ipgInterval, TransmitCallback);
//
// Clear the output packet.
//
_outputPacket.Clear();
_transmitterRunning = true;
}
else
{
throw new InvalidOperationException("Transmitter already running.");
}
}
private void StopTransmitter()
{
_system.Scheduler.Cancel(_transmitEvent);
_transmitterRunning = false;
}
private void TransmitWord(ushort word)
{
if (_localLoop || _loopBack)
{
// Loop back to FIFO through the receiver.
//
// Append this word to the input packet.
// It will be picked up by the Receive callback and
// put into the FIFO in due time.
//
_inputPacket.Enqueue(word);
//
// Ensure the receiver is running.
//
RunReceiver();
}
else
{
// Append to outgoing packet.
_outputPacket.Enqueue(word);
}
}
private void CompleteTransmission()
{
//
// Transmit completed packet over real ethernet.
//
//
// A properly formed packet generated by the microcode should begin with the standard ethernet
// SFD of 3 words of 0x5555 and 1 word of 0x55d5. This must be stripped before we send it
// to the host device.
//
if (_outputPacket.Count < 4)
{
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "Malformed packet: too short.");
return;
}
bool badSfd = false;
for(int i=0;i<4;i++)
{
ushort sfdWord = _outputPacket.Dequeue();
if (i < 3)
{
badSfd = sfdWord != 0x5555;
}
else
{
badSfd = sfdWord != 0x55d5;
}
}
if (badSfd)
{
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "Malformed packet: Invalid SFD.");
return;
}
if (_outputPacket.Count > 0 && _hostInterface != null)
{
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "Transmitting completed packet.");
_hostInterface.Send(_outputPacket.ToArray());
}
}
private void TransmitCallback(ulong skewNsec, object context)
{
//
// Pull the next word from the FIFO, if available.
//
if (_fifo.Count > 0)
{
ushort nextWord = _fifo.Dequeue();
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "Transmitting word 0x{0:x4}", nextWord);
TransmitWord(nextWord);
}
else if (!_lastWord)
{
//
// No data available in FIFO and LastWord is not set: Underrun.
// Raise txUnderrun to signal an error.
//
_txUnderrun = true;
if (Log.Enabled) Log.Write(LogType.Error, LogComponent.EthernetControl, "Transmit underrun.");
}
if (_lastWord && _fifo.Count == 0)
{
//
// If LastWord is set and the FIFO is empty, that will be the last word in the packet. Shut things down.
//
_transmitterRunning = false;
_outAttn = true;
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "Last word. Stopping transmission.");
//
// Transmit completed packet over real ethernet.
//
CompleteTransmission();
}
else if (_txUnderrun)
{
_transmitterRunning = false;
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "Underrun. Stopping transmission.");
}
else
{
//
// Still going, schedule the next callback.
//
_transmitEvent = _system.Scheduler.Schedule(_transmitInterval, TransmitCallback);
}
//
// Update wakeups -- if the FIFO has space now, the microcode should be awakened, for example.
//
UpdateWakeup();
}
/// <summary>
/// Invoked when the host ethernet interface receives a packet destined for us.
/// NOTE: This runs on the PCap or UDP receiver thread, not the main emulator thread.
/// Any access to emulator structures must be properly protected.
///
///
/// </summary>
/// <param name="data"></param>
private void OnHostPacketReceived(MemoryStream data)
{
//
// Append the new packet onto our pending packets queue.
// This will be picked up when the receiver is ready to receive things.
//
_readerLock.EnterUpgradeableReadLock();
if (!_enableRcv || !_turnOff_)
{
//
// Receiver is offjust drop the packet on the floor.
//
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "Ethernet receiver is off; dropping this packet.");
_readerLock.EnterWriteLock();
_pendingPackets.Clear();
_readerLock.ExitWriteLock();
}
else if (_pendingPackets.Count < 32)
{
//
// Place the packet into the queue; this will be picked up by the receiver poll thread
// and passed to the receiver.
//
_readerLock.EnterWriteLock();
_pendingPackets.Enqueue(data);
_readerLock.ExitWriteLock();
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "Packet (length {0}) added to pending buffer.", data.Length);
}
else
{
//
// Too many queued-up packets, drop this one.
//
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "Pending buffer full; dropping this packet.");
}
_readerLock.ExitUpgradeableReadLock();
}
private void StopReceiver()
{
_system.Scheduler.Cancel(_receiveEvent);
_receiverRunning = false;
}
private void RunReceiver()
{
_rxMode_ = false;
if (!_receiverRunning && _enableRcv)
{
//
// This is a hack: For loopback cases the real hardware has mysterious state machines to deal with
// tracking the FIFO properly (since it's being used for both input and output at the same time,
// something that only occurs during loopback testing -- the complication is how the transmit state machine knows
// when the last word provided by the microcode has been sent, when the loopback is bringing new words into the FIFO
// at the same time).
// Because we live in a fantasy world of emulation, we can cheat: To keep things simple here we simply delay the
// receive operation to ensure that there is no overlap between the transmit and receive on loopback, this avoids
// needing extra logic for the FIFO during loopback tests.
//
_receiveEvent = _system.Scheduler.Schedule(
_localLoop || _loopBack ? _receiveIntervalLoopback : _receiveInterval,
ReceiveCallback);
_receiverRunning = true;
}
}
private void ReceiverPollCallback(ulong skewNsec, object context)
{
if (!_enableRcv || !_turnOff_ || _enableTx || _transmitterRunning || _localLoop || _loopBack)
{
//
// Receiver is off, we're currently transmitting, or we're in loopback mode, we do nothing.
//
}
else
{
//
// See if there's a packet to pick up.
//
MemoryStream packetStream = null;
_readerLock.EnterWriteLock();
if (!_receiverRunning && _pendingPackets.Count > 0)
{
// We have a packet, dequeue it and dump it into the receiver input queue.
packetStream = _pendingPackets.Dequeue();
}
_readerLock.ExitWriteLock();
if (packetStream != null)
{
//
// Read the stream into the receiver input queue.
//
packetStream.Seek(0, SeekOrigin.Begin);
while (packetStream.Position < packetStream.Length)
{
_inputPacket.Enqueue((ushort)((packetStream.ReadByte() << 8) | packetStream.ReadByte()));
}
//
// Skip the preamble state (only used in loopback)
//
_receiverState = ReceiverState.Data;
//
// Alert the microcode to the presence of input data and start processing.
//
RunReceiver();
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "Receive: Incoming packet queued into input buffer.");
}
}
//
// Schedule the next poll callback.
//
_system.Scheduler.Schedule(_receiverPollInterval, ReceiverPollCallback);
}
private void ReceiveCallback(ulong skewNsec, object context)
{
//
// Pull the next word from the input packet and run the state machine.
//
if (_inputPacket.Count > 0)
{
ushort nextWord = _inputPacket.Dequeue();
switch (_receiverState)
{
case ReceiverState.Preamble:
if (nextWord == 0x55d5) // end of preamble
{
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "Receive: end of preamble, switching to Data state.");
_receiverState = ReceiverState.Data;
}
break;
case ReceiverState.Data:
//
// Stuff into FIFO.
//
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "Receive: Enqueuing Data word 0x{0:x4} onto FIFO, {1} words left.", nextWord, _inputPacket.Count);
_fifo.Enqueue(nextWord);
_crc32.AddToChecksum(nextWord);
UpdateWakeup();
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "Packet CRC is now 0x{0:x8}", _crc32.Checksum);
break;
}
}
if (_inputPacket.Count > 0)
{
//
// Post next event if there are still words left.
//
_receiveEvent = _system.Scheduler.Schedule(_transmitInterval, ReceiveCallback);
}
else
{
//
// End of packet.
//
_receiverRunning = false;
//
// Let microcode know the packet is done.
//
_inAttn = true;
//
// Update CRC and other flags.
//
_rxMode_ = false;
_rxGoodCRC = _loopBack || _localLoop ? _crc32.Checksum == _goodCRC : true;
UpdateWakeup();
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "Final Packet CRC is 0x{0:x8}", _crc32.Checksum);
}
}
private void UpdateWakeup()
{
//
// See schematic, pg 2; ethernet requests (wakeups) generated by:
// TxMode & BufIR & Defer' & LastWord' (i.e.transmit on, fifo buffer not full, not deferring, not the last word)
// OR
// Defer & TickElapsed (microcode asked for the transmission to be deferred, and that deferral time has elapsed)
// OR
// RcvMode & BufOR & Purge' (i.e. rcv on, fifo data ready, not purging fifo)
// OR
// Attn (i.e.hardware has a a status to report)
//
bool txWakeup = _enableTx && _fifo.Count < 16 && !_defer && !_lastWord;
bool deferWakeup = _defer & _tickElapsed;
bool rxWakeup = !_rxMode_ && _fifo.Count > 2 && !_purge;
if (txWakeup || deferWakeup || rxWakeup || _outAttn || _inAttn)
{
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "Waking Ethernet task (tx {0} defer {1} rx {2} outAttn {3} inAttn {4}", txWakeup, deferWakeup, rxWakeup, _outAttn, _inAttn);
_system.CP.WakeTask(TaskType.Ethernet);
}
else
{
if (Log.Enabled) Log.Write(LogComponent.EthernetControl, "Sleeping Ethernet task.");
_system.CP.SleepTask(TaskType.Ethernet);
}
}
private DSystem _system;
// Output data
private bool _outputDataLatched;
private ushort _outputData;
private Queue<ushort> _fifo;
// Input data
private Queue<ushort> _inputPacket;
private Queue<MemoryStream> _pendingPackets;
// Defer timings
private bool _tickElapsed;
// Attention flags
private bool _outAttn;
private bool _inAttn;
private bool _purge;
// Status Bit(in Xerox order)
// --------------------------------
// TurnOff' : 15
// R.EvenLen : 14
// R.GoodCRC : 13
// R.Overrun' : 12
// R.GoodAlign : 11
// T.Underrun' : 10
// T.Collision' : 9
// RcvMode' : 8
// EnableTrn : 7
// LastWord : 6
// EnableRcv : 5
// LocalLoop : 4
// Loopback : 3
// DiagVideoData : 2
// VideoClock : 1 // Used by LSEP
// DiagLineSync : 0
private bool _turnOff_;
private bool _rxEvenLen;
private bool _rxGoodCRC;
private bool _rxOverrun_;
private bool _rxGoodAlign;
private bool _txUnderrun;
private bool _txCollision_;
private bool _rxMode_;
private bool _enableTx;
private bool _lastWord;
private bool _enableRcv;
private bool _localLoop;
private bool _loopBack;
// EOCtl: Bit(etc)
// ----------------------------
// EnableTrn 15
// LastWord 14
// Defer 13
private bool _defer;
// EICtl: Bit(xerox order)
// ------------------------------------
// EnableRcv 15
// TurnOff' 14
// LocalLoop 13
// LoopBack 12
// (See above)
//
// Defer event & timing -- 51.2uS
//
private Event _deferEvent;
private readonly ulong _deferDelay = (ulong)(51.2 * Conversion.UsecToNsec);
//
// Transmit event and timing -- 1600nS
//
private readonly ulong _transmitInterval = 1200;
private readonly ulong _ipgInterval = (ulong)(9.6 * Conversion.UsecToNsec); // Inter-packet gap
private bool _transmitterRunning;
private Event _transmitEvent;
//
// Receive event, timing, and thread safety
//
private readonly ulong _receiveInterval = 1200;
private readonly ulong _receiveIntervalLoopback = 25600;
private bool _receiverRunning;
private ReceiverState _receiverState;
private Event _receiveEvent;
private readonly ulong _receiverPollInterval = (ulong)(51.2 * Conversion.UsecToNsec);

271
D/Ethernet/HostEthernet.cs Normal file
View File

@@ -0,0 +1,271 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using SharpPcap;
using SharpPcap.WinPcap;
using SharpPcap.LibPcap;
using SharpPcap.AirPcap;
using PacketDotNet;
using System;
using System.Net.NetworkInformation;
using D.Logging;
using PacketDotNet.Utils;
using System.Text;
namespace D.Ethernet
{
/// <summary>
/// Represents a host ethernet interface.
/// </summary>
public struct EthernetInterface
{
public EthernetInterface(string name, string description)
{
Name = name;
Description = description;
}
public override string ToString()
{
return String.Format("{0} ({1})", Name, Description);
}
public string Name;
public string Description;
}
/// <summary>
/// Implements the logic for sending and receiving emulated 10mbit ethernet packets over an actual
/// ethernet interface controlled by the host operating system.
///
/// This uses SharpPcap to do the dirty work.
/// </summary>
public class HostEthernetEncapsulation : IPacketInterface
{
public HostEthernetEncapsulation(string name)
{
// Find the specified device by name
foreach (ICaptureDevice device in CaptureDeviceList.Instance)
{
if (device is WinPcapDevice)
{
//
// We use the friendly name to make it easier to specify in config files.
//
if (((WinPcapDevice)device).Interface.FriendlyName.ToLowerInvariant() == name.ToLowerInvariant())
{
AttachInterface(device);
break;
}
}
else
{
if (device.Name.ToLowerInvariant() == name.ToLowerInvariant())
{
AttachInterface(device);
break;
}
}
}
if (_interface == null)
{
Log.Write(LogComponent.HostEthernet, "Specified ethernet interface does not exist or is not compatible with Darkstar.");
throw new InvalidOperationException("Specified ethernet interface does not exist or is not compatible with Darkstar.");
}
UpdateSourceAddress();
}
public void RegisterReceiveCallback(ReceivePacketDelegate callback)
{
_callback = callback;
// Now that we have a callback we can start receiving stuff.
Open(true /* promiscuous */, 0);
BeginReceive();
}
public void Shutdown()
{
if (_interface != null)
{
try
{
if (_interface.Started)
{
_interface.StopCapture();
}
}
catch
{
// Eat exceptions. The Pcap libs seem to throw on StopCapture on
// Unix platforms, we don't really care about them (since we're shutting down anyway)
// but this prevents debug spew from appearing on the console.
}
finally
{
_interface.Close();
}
}
}
/// <summary>
/// Sends an array of words over the ethernet.
/// </summary>
/// <param name="packet"></param>
/// <param name="hostId"></param>
public void Send(ushort[] packet)
{
byte[] packetBytes = new byte[packet.Length * 2];
// StringBuilder sb = new StringBuilder();
//
// Do this annoying dance to stuff the ushorts into bytes because this is C#.
//
for (int i = 0; i < packet.Length; i++)
{
packetBytes[i * 2] = (byte)(packet[i] >> 8);
packetBytes[i * 2 + 1] = (byte)(packet[i]);
// sb.AppendFormat("{0:x2} {1:x2} ", packetBytes[i * 2], packetBytes[i * 2 + 1]);
}
ByteArraySegment seg = new ByteArraySegment(packetBytes);
EthernetPacket p = new EthernetPacket(seg);
// Send it over the 'net!
_interface.SendPacket(p);
Log.Write(LogComponent.HostEthernet, "Ethernet packet (length {0}) sent.", packetBytes.Length);
// Log.Write(LogComponent.HostEthernet, "Contents: {0}", sb.ToString());
}
private void ReceiveCallback(object sender, CaptureEventArgs e)
{
//
// Filter out packets intended for the emulator, forward them on, drop everything else.
//
if (e.Packet.LinkLayerType == LinkLayers.Ethernet)
{
//
// We wrap this in a try/catch; on occasion Packet.ParsePacket fails due to a bug
// in the PacketDotNet library.
//
EthernetPacket packet = null;
try
{
packet = (EthernetPacket)Packet.ParsePacket(LinkLayers.Ethernet, e.Packet.Data);
}
catch (Exception ex)
{
// Just eat this, log a message.
Log.Write(LogType.Error, LogComponent.HostEthernet, "Failed to parse incoming packet. Exception {0}", ex.Message);
packet = null;
}
if (packet != null)
{
if (!packet.SourceHwAddress.Equals(_10mbitSourceAddress) &&
packet.PayloadData != null) // Don't recieve packets sent by this emulator.
{
Log.Write(LogComponent.HostEthernet, "Received 10mbit packet.");
_callback(new System.IO.MemoryStream(packet.PayloadData));
}
else
{
// Not for us, discard the packet.
}
}
}
}
private void UpdateSourceAddress()
{
byte[] macBytes = new byte[6];
for (int i = 0; i < 6; i++)
{
macBytes[i] = (byte)((Configuration.HostID >> (i * 8)));
}
_10mbitSourceAddress = new PhysicalAddress(macBytes);
}
private void AttachInterface(ICaptureDevice iface)
{
_interface = iface;
if (_interface == null)
{
throw new InvalidOperationException("Requested interface not found.");
}
Log.Write(LogComponent.HostEthernet, "Attached to host interface {0}", iface.Name);
}
private void Open(bool promiscuous, int timeout)
{
if (_interface is WinPcapDevice)
{
((WinPcapDevice)_interface).Open(promiscuous ? OpenFlags.MaxResponsiveness | OpenFlags.Promiscuous : OpenFlags.MaxResponsiveness, timeout);
}
else if (_interface is LibPcapLiveDevice)
{
((LibPcapLiveDevice)_interface).Open(promiscuous ? DeviceMode.Promiscuous : DeviceMode.Normal, timeout);
}
else if (_interface is AirPcapDevice)
{
((AirPcapDevice)_interface).Open(promiscuous ? OpenFlags.MaxResponsiveness | OpenFlags.Promiscuous : OpenFlags.MaxResponsiveness, timeout);
}
Log.Write(LogComponent.HostEthernet, "Host interface opened and receiving packets.");
}
/// <summary>
/// Begin receiving packets, forever.
/// </summary>
private void BeginReceive()
{
// Kick off receiver.
_interface.OnPacketArrival += ReceiveCallback;
_interface.StartCapture();
}
private ICaptureDevice _interface;
private ReceivePacketDelegate _callback;
private PhysicalAddress _10mbitSourceAddress;
}
}

View File

@@ -0,0 +1,60 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System.IO;
namespace D.Ethernet
{
public delegate void ReceivePacketDelegate(MemoryStream data);
/// <summary>
/// Provides a generic interface for host network devices that can encapsulate
/// Ethernet packets.
/// </summary>
public interface IPacketInterface
{
/// <summary>
/// Registers a callback delegate to handle packets that are received.
/// </summary>
/// <param name="callback"></param>
void RegisterReceiveCallback(ReceivePacketDelegate callback);
/// <summary>
/// Sends the specified word array over the device.
/// </summary>
/// <param name="packet"></param>
/// <param name="length"></param>
void Send(ushort[] packet);
/// <summary>
/// Shuts down the encapsulation provider.
/// </summary>
void Shutdown();
}
}

230
D/HighResTimer.cs Normal file
View File

@@ -0,0 +1,230 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;
namespace D
{
/// <summary>
/// HighResTimer gives us access to NT's very-high-resolution PerformanceCounters.
/// This gives us the precision we need to sync emulation to any speed we desire.
/// </summary>
public sealed class HighResTimer
{
[DllImport("Kernel32.dll")]
private static extern bool QueryPerformanceCounter(
out long lpPerformanceCount);
[DllImport("Kernel32.dll")]
private static extern bool QueryPerformanceFrequency(
out long lpFrequency);
public HighResTimer()
{
// What's the frequency, Kenneth?
if (QueryPerformanceFrequency(out _frequency) == false)
{
// high-performance counter not supported
throw new Win32Exception();
}
}
/// <summary>
/// Returns the current time in seconds.
/// </summary>
/// <returns></returns>
public double GetCurrentTime()
{
long currentTime;
QueryPerformanceCounter(out currentTime);
return (double)(currentTime) / (double)_frequency;
}
private long _frequency;
}
public sealed class FrameTimer
{
[DllImport("winmm.dll", EntryPoint = "timeGetDevCaps", SetLastError = true)]
static extern UInt32 TimeGetDevCaps(ref TimeCaps timeCaps, UInt32 sizeTimeCaps);
[DllImport("winmm.dll", EntryPoint = "timeBeginPeriod")]
static extern UInt32 TimeBeginPeriod(UInt32 uPeriod);
[DllImport("winmm.dll", EntryPoint = "timeEndPeriod")]
public static extern uint TimeEndPeriod(uint uMilliseconds);
[DllImport("kernel32.dll", EntryPoint = "CreateTimerQueue")]
public static extern IntPtr CreateTimerQueue();
[DllImport("kernel32.dll", EntryPoint = "DeleteTimerQueueEx")]
public static extern bool DeleteTimerQueue(IntPtr hTimerQueue, IntPtr hCompletionEvent);
[DllImport("kernel32.dll", EntryPoint = "CreateTimerQueueTimer")]
public static extern bool CreateTimerQueueTimer(out IntPtr phNewTimer, IntPtr hTimerQueue, IntPtr Callback, IntPtr Parameter, UInt32 DueTime, UInt32 Period, uint Flags);
[DllImport("kernel32.dll", EntryPoint = "ChangeTimerQueueTimer")]
public static extern bool ChangeTimerQueueTimer(IntPtr hTimerQueue, IntPtr hTimer, UInt32 DueTime, UInt32 Period);
[DllImport("kernel32.dll", EntryPoint = "DeleteTimerQueueTimer")]
public static extern bool DeleteTimerQueueTimer(IntPtr hTimerQueue, IntPtr hTimer, IntPtr hCompletionEvent);
[StructLayout(LayoutKind.Sequential)]
public struct TimeCaps
{
public UInt32 wPeriodMin;
public UInt32 wPeriodMax;
};
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate void UnmanagedTimerCallback(IntPtr param, bool timerOrWait);
/// <summary>
/// FrameTimer provides a simple method to synchronize execution to a given framerate.
/// Calling WaitForFrame() blocks until the start of the next frame.
///
/// NOTE: This code uses the Win32 TimerQueue APIs instead of the System.Threading.Timer
/// APIs because the .NET APIs do not allow execution of the callback on the timer's thread --
/// it queues up a new worker thread. This lowers the accuracy of the timer, and since we
/// need all the precision we can get they're not suitable here.
/// </summary>
/// <param name="framesPerSecond">The frame rate to sync to.</param>
public FrameTimer(double framesPerSecond)
{
//
// Set the timer to the minimum value (1ms). This should be supported on any modern x86 system.
// If not, too bad...
//
UInt32 res = TimeBeginPeriod(1);
if (res != 0)
{
throw new InvalidOperationException("Unable to set timer period.");
}
//
// Create a new timer queue
//
_hTimerQueue = CreateTimerQueue();
if (_hTimerQueue == IntPtr.Zero)
{
throw new InvalidOperationException("Unable to create timer queue.");
}
//
// Since we only have a resolution of 1ms, we have to do some hackery to get a slightly more accurate framerate.
// (60 fields/sec requires 16 2/3s ms frame delay.)
// We alternate between two rates at varying intervals and this gets us fairly close to the desired frame rate.
//
_callback = new UnmanagedTimerCallback(TimerCallbackFn);
_highPeriod = (uint)Math.Ceiling(1000.0 * (1.0 / framesPerSecond));
_lowPeriod = (uint)Math.Floor(1000.0 * (1.0 / framesPerSecond));
_periodTenths = _periodSwitch = (uint)((1000.0 * (1.0 / framesPerSecond) - Math.Floor(1000.0 * (1.0 / framesPerSecond))) * 10.0);
if (!CreateTimerQueueTimer(out _hTimer, _hTimerQueue, Marshal.GetFunctionPointerForDelegate(_callback), IntPtr.Zero, _lowPeriod, _lowPeriod, 0x00000020 /* execute in timer thread */))
{
throw new InvalidOperationException("Unable to create timer queue timer.");
}
_event = new AutoResetEvent(false);
_lowTimer = 0;
}
~FrameTimer()
{
//
// Clean stuff up
//
try
{
DeleteTimerQueueTimer(_hTimerQueue, _hTimer, IntPtr.Zero);
DeleteTimerQueue(_hTimerQueue, IntPtr.Zero);
}
catch
{
// Eat exceptions (for Mono)
}
//
// Fire off a final event to release any call that's waiting...
//
if (_event != null)
{
_event.Set();
}
}
/// <summary>
/// Waits for the timer to fire.
/// </summary>
public void WaitForFrame()
{
_event.WaitOne();
}
/// <summary>
/// Callback from timer queue. Work done here is executed on the timer's thread, so must be quick.
/// </summary>
/// <param name="lpParameter"></param>
/// <param name="TimerOrWaitFired"></param>
private void TimerCallbackFn(IntPtr lpParameter, bool TimerOrWaitFired)
{
_event.Set();
_lowTimer++;
if (_lowTimer >= _periodSwitch)
{
_lowTimer = 0;
_period = !_period;
ChangeTimerQueueTimer(_hTimerQueue, _hTimer, _period ? _lowPeriod : _highPeriod, _period ? _lowPeriod : _highPeriod);
_periodSwitch = !_period ? _periodTenths : 10 - _periodTenths;
}
}
private IntPtr _hTimerQueue;
private IntPtr _hTimer;
private AutoResetEvent _event;
private UnmanagedTimerCallback _callback;
private uint _lowPeriod;
private uint _highPeriod;
private uint _periodSwitch;
private uint _periodTenths;
private int _lowTimer;
private bool _period;
}
}

402
D/IO/FloppyDisk.cs Normal file
View File

@@ -0,0 +1,402 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace D.IO
{
/// <summary>
/// Presents data for a floppy disk, organized by cylinder, head, and sector,
/// and provides constructors for loading from IMD file.
/// </summary>
public class FloppyDisk
{
public FloppyDisk(string imagePath)
{
_imagePath = imagePath;
_tracks = new Track[2, 77];
_isSingleSided = true;
using (FileStream fs = new FileStream(imagePath, FileMode.Open, FileAccess.Read))
{
LoadIMD(fs);
}
}
public string Description
{
get { return _imdHeader; }
}
public bool IsSingleSided
{
get { return _isSingleSided; }
}
public string ImagePath
{
get { return _imagePath; }
}
/// <summary>
/// Returns sector data for the given address.
/// </summary>
/// <param name="cylinder"></param>
/// <param name="head"></param>
/// <param name="sector"></param>
/// <returns></returns>
public Sector GetSector(int cylinder, int head, int sector)
{
return _tracks[head, cylinder].ReadSector(sector);
}
public Track GetTrack(int cylinder, int head)
{
return _tracks[head, cylinder];
}
private void LoadIMD(Stream s)
{
_imdHeader = ReadIMDHeader(s);
//
// Read each track in and place it in memory.
// We assume that there will be no more than 77 cylinders
// and no more than 2 tracks. We also do a basic sanity
// check that no track appears more than once.
//
while (true)
{
Track t = new Track(s);
if (t.Cylinder < 0 || t.Cylinder > 76)
{
throw new InvalidOperationException(String.Format("Invalid cylinder value {0}", t.Cylinder));
}
if (t.Head < 0 || t.Head > 1)
{
throw new InvalidOperationException(String.Format("Invalid head value {0}", t.Head));
}
if (_tracks[t.Head, t.Cylinder] != null)
{
throw new InvalidOperationException(String.Format("Duplicate head/track", t.Head, t.Cylinder));
}
if (t.Head != 0)
{
// Got a track on side 1, this must be a double-sided disk.
_isSingleSided = false;
}
_tracks[t.Head, t.Cylinder] = t;
if (s.Position == s.Length)
{
// End of file.
break;
}
}
}
private string ReadIMDHeader(Stream s)
{
StringBuilder sb = new StringBuilder();
while (true)
{
byte b = (byte)s.ReadByte();
if (b == 0x1a)
{
break;
}
else
{
sb.Append((char)b);
}
}
return sb.ToString();
}
private string _imdHeader;
private bool _isSingleSided;
private string _imagePath;
private Track[,] _tracks;
}
/// <summary>
/// Represents a single track's worth of sectors
/// </summary>
public class Track
{
/// <summary>
/// Create a new, empty track with the specified format, sector size and sector count.
/// </summary>
/// <param name="format"></param>
/// <param name="cylinder"></param>
/// <param name="head"></param>
/// <param name="sectorCount"></param>
/// <param name="sectorSize"></param>
public Track(Format format, int cylinder, int head, int sectorCount, int sectorSize)
{
_format = format;
_cylinder = cylinder;
_head = head;
_sectorCount = sectorCount;
_sectorSize = sectorSize;
_sectors = new Sector[_sectorCount];
for (int i = 0; i < _sectorCount; i++)
{
_sectors[i] = new Sector(_sectorSize, _format);
}
}
/// <summary>
/// Create a new track loaded from the given stream. The stream is expected to be positioned
/// at the beginning of an IMD sector definition.
/// </summary>
/// <param name="s"></param>
public Track(Stream s)
{
bool bCylMap = false;
bool bHeadMap = false;
_format = (Format)s.ReadByte();
_cylinder = s.ReadByte();
_head = s.ReadByte();
_sectorCount = s.ReadByte();
int sectorSizeIndex = s.ReadByte();
// Basic sanity check of values
if (_format > Format.MFM250 ||
_cylinder > 77 ||
(_head & 0x3f) > 1 ||
sectorSizeIndex > _sectorSizes.Length - 1)
{
throw new InvalidOperationException("Invalid header data for track.");
}
_sectorSize = _sectorSizes[sectorSizeIndex];
bCylMap = (_head & 0x80) != 0;
bHeadMap = (_head & 0x40) != 0;
// Head is just the first bit.
_head = (byte)(_head & 0x1);
//
// Read sector numbering
//
_sectorOrdering = new List<int>(_sectorCount);
for (int i = 0; i < _sectorCount; i++)
{
_sectorOrdering.Add(s.ReadByte());
}
//
// At this time, cyl and head maps are not supported.
// It's not expected any Star disk would use such a format.
//
if (bCylMap | bHeadMap)
{
throw new NotImplementedException("IMD Cylinder and Head maps not supported.");
}
//
// Read the sector data in.
//
_sectors = new Sector[_sectorCount];
for (int i = 0; i < _sectorCount; i++)
{
SectorRecordType type = (SectorRecordType)s.ReadByte();
byte compressedData;
switch (type)
{
case SectorRecordType.Unavailable:
// Nothing, sectors left null.
break;
case SectorRecordType.Normal:
case SectorRecordType.NormalDeleted:
case SectorRecordType.NormalError:
case SectorRecordType.DeletedError:
_sectors[_sectorOrdering[i] - 1] = new Sector(_sectorSize, _format, s);
break;
case SectorRecordType.Compressed:
case SectorRecordType.CompressedDeleted:
case SectorRecordType.CompressedError:
case SectorRecordType.CompressedDeletedError:
compressedData = (byte)s.ReadByte();
// Fill sector with compressed data
_sectors[_sectorOrdering[i] - 1] = new Sector(_sectorSize, _format, compressedData);
break;
default:
throw new InvalidOperationException(String.Format("Unexpected IMD sector data type {0}", type));
}
}
}
public int Cylinder
{
get { return _cylinder; }
}
public int Head
{
get { return _head; }
}
public int SectorCount
{
get { return _sectorCount; }
}
public int SectorSize
{
get { return _sectorSize; }
}
public Format Format
{
get { return _format; }
}
public Sector ReadSector(int sector)
{
return _sectors[sector];
}
//
// 00 Sector data unavailable - could not be read
// 01 .... Normal data: (Sector Size) bytes follow
// 02 xx Compressed: All bytes in sector have same value(xx)
// 03 .... Normal data with "Deleted-Data address mark"
// 04 xx Compressed with "Deleted-Data address mark"
// 05 .... Normal data read with data error
// 06 xx Compressed read with data error
// 07 .... Deleted data read with data error
// 08 xx Compressed, Deleted read with data error
//
private enum SectorRecordType
{
Unavailable = 0,
Normal = 1,
Compressed = 2,
NormalDeleted = 3,
CompressedDeleted = 4,
NormalError = 5,
CompressedError = 6,
DeletedError = 7,
CompressedDeletedError = 8,
}
private Format _format;
private int _cylinder;
private int _head;
private int _sectorCount;
private int _sectorSize;
private List<int> _sectorOrdering;
private Sector[] _sectors;
private static int[] _sectorSizes = { 128, 256, 512, 1024, 2048, 4096, 8192 };
}
public class Sector
{
public Sector(int sectorSize, Format format)
{
_data = new byte[sectorSize];
_format = format;
}
public Sector(int sectorSize, Format format, byte compressedValue)
: this(sectorSize, format)
{
for (int i = 0; i < _data.Length; i++)
{
_data[i] = compressedValue;
}
}
public Sector(int sectorSize, Format format, Stream s)
: this(sectorSize, format)
{
int read = s.Read(_data, 0, sectorSize);
if (read != sectorSize)
{
throw new InvalidOperationException("Short read in sector data.");
}
}
public Format Format
{
get { return _format; }
}
public byte[] Data
{
get { return _data; }
}
private Format _format;
private byte[] _data;
}
// 00 = 500 kbps FM \ Note: kbps indicates transfer rate,
// 01 = 300 kbps FM > not the data rate, which is
// 02 = 250 kbps FM / 1/2 for FM encoding.
// 03 = 500 kbps MFM
// 04 = 300 kbps MFM
// 05 = 250 kbps MFM
public enum Format
{
FM500 = 0,
FM300 = 1,
FM250 = 2,
MFM500 = 3,
MFM300 = 4,
MFM250 = 5,
}
}

183
D/IO/FloppyDrive.cs Normal file
View File

@@ -0,0 +1,183 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using D.IO;
using D.Logging;
namespace D
{
public class FloppyDrive
{
public FloppyDrive(DSystem system)
{
_system = system;
//
// Start the Index event rolling. This will run forever.
//
_system.Scheduler.Schedule(_indexInterval, IndexCallback);
Reset();
}
public FloppyDisk Disk
{
get { return _disk; }
}
public int Track
{
get { return _track; }
}
public bool IsLoaded
{
get { return _disk != null; }
}
public bool IsWriteProtected
{
get { return _writeProtected; }
}
public bool IsSingleSided
{
get { return _singleSided; }
}
public bool Track0
{
get { return _track == 0; }
}
public bool Index
{
get { return _index; }
}
public bool DiskChange
{
get { return _diskChange; }
}
public bool DriveSelect
{
get { return _driveSelect; }
set
{
_driveSelect = value;
//
// The Disk Change signal is reset when
// Drive Select goes low.
//
if (!_driveSelect)
{
_diskChange = false;
}
}
}
public void Reset()
{
_track = 0;
_singleSided = false;
_writeProtected = false;
_diskChange = false;
_index = false;
_driveSelect = false;
}
public void LoadDisk(FloppyDisk disk)
{
_disk = disk;
_singleSided = _disk.IsSingleSided;
_diskChange = true;
if (Log.Enabled) Log.Write(LogComponent.IOPFloppy, "Floppy disk image loaded. Description is:\n{0}", disk.Description);
// TODO: update WP, SS bits, etc.
}
public void UnloadDisk()
{
// TODO: Commit disk changes
_disk = null;
_diskChange = true;
}
public void SeekTo(int track)
{
// Clip into range.
_track = Math.Max(0, track);
_track = Math.Min(76, _track);
}
private void IndexCallback(ulong skewNsec, object context)
{
//
// This always runs even when a disk isn't loaded or spinning.
// It only actually modifies _index if a disk is loaded.
// We need to generate index pulses anyway (even if they are
// inaccurate and unused by us given that we're not emulating
// the disk at that low of a level) this does the job.
//
if (DriveSelect && IsLoaded && !_index)
{
// Raise the index signal, hold for a short period.
_index = true;
_system.Scheduler.Schedule(_indexDuration, IndexCallback);
if (Log.Enabled) Log.Write(LogComponent.IOPFloppy, "Disk rotation complete, raising INDEX signal for 10us.");
}
else
{
// Reset the index signal, wait for a long period (for the disk to go round again).
_index = false;
_system.Scheduler.Schedule(_indexInterval, IndexCallback);
}
}
private DSystem _system;
private bool _singleSided;
private bool _writeProtected;
private int _track;
private bool _diskChange;
private bool _driveSelect;
private FloppyDisk _disk;
// Index signal and timing
private bool _index;
private ulong _indexInterval = 200 * Conversion.MsecToNsec; // 1/5 second at 300rpm
private ulong _indexDuration = 10 * Conversion.UsecToNsec; // 10uSec duration for index signal.
}
}

477
D/IO/SA1000.cs Normal file
View File

@@ -0,0 +1,477 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using D.Logging;
using System;
using System.IO;
namespace D.IO
{
public enum DriveType
{
Invalid = 0,
SA1004 = 1, // 10MB
Q2040 = 2, // Quantum 40MB
Q2080 = 3, // Quantum 80MB
}
/// <summary>
/// Geometry for an SA1000-style drive
/// </summary>
public struct Geometry
{
public Geometry(int cylinders, int heads)
{
Cylinders = cylinders;
Heads = heads;
}
public int Cylinders;
public int Heads;
public static Geometry SA1004 = new Geometry(256, 4);
public static Geometry Q2040 = new Geometry(512, 8);
public static Geometry Q2080 = new Geometry(1172, 7);
}
/// <summary>
/// Encapsulates the state, disk data and low-level behavior of Shugart SA1000-style drives.
/// </summary>
public class SA1000Drive
{
public SA1000Drive(DSystem system)
{
_type = DriveType.Invalid;
NewDisk(_type, String.Empty);
_system = system;
// Queue up the event that rotates our virtual disk. This runs continuously.
_system.Scheduler.Schedule(_diskWordDelay, DiskWordCallback);
Reset();
}
public void Reset()
{
_cylinder = 0;
_head = 0;
_wordIndex = 0;
_index = false;
_seekComplete = true;
_lastStep = false;
}
public void Save()
{
if (!string.IsNullOrEmpty(_diskImagePath))
{
Save(_diskImagePath);
}
}
public void Save(string path)
{
using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write))
{
//
// Format is:
// 1st Byte: Drive type (see DriveType enumeration)
// Bytes 2-N: Tracks of data (5325 words each) for all cylinders on the disk
// Each word in a track is encapsulated in a 24-bit integer:
// Metadata (address mark, CRC indicators) in upper 8 bits, data in low 16 bits.
//
fs.WriteByte((byte)_type);
for (int cyl = 0; cyl < _geometry.Cylinders; cyl++)
{
for (int head = 0; head < _geometry.Heads; head++)
{
for (int word = 0; word < _wordsPerTrack; word++)
{
fs.Write(BitConverter.GetBytes(_tracks[cyl, head, word]), 0, 3);
}
}
}
_diskImagePath = path;
}
}
public void Load(string path)
{
try
{
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
{
byte type = (byte)fs.ReadByte();
if (type < (int)DriveType.SA1004 || type > (int)DriveType.Q2080)
{
throw new InvalidOperationException("Unsupported drive type.");
}
_type = (DriveType)type;
NewDisk(_type, path);
byte[] buffer = new byte[4];
for (int cyl = 0; cyl < _geometry.Cylinders; cyl++)
{
for (int head = 0; head < _geometry.Heads; head++)
{
for (int word = 0; word < _wordsPerTrack; word++)
{
int read = fs.Read(buffer, 0, 3);
if (read < 3)
{
throw new InvalidOperationException("Short read on disk image load.");
}
_tracks[cyl, head, word] = BitConverter.ToUInt32(buffer, 0);
}
}
}
}
}
catch(Exception e)
{
//
// Hit an exception while loading, ensure that we don't
// have a partial image loaded.
//
_type = DriveType.Invalid;
NewDisk(_type, String.Empty);
throw e;
}
}
public void NewDisk(DriveType type, string path)
{
switch (type)
{
case DriveType.Invalid:
//
// For Invalid (i.e. unloaded or unspecified disks)
// we assume an SA1004 geometry, but will always
// return Not Ready.
//
case DriveType.SA1004:
_geometry = Geometry.SA1004;
break;
case DriveType.Q2040:
_geometry = Geometry.Q2040;
break;
case DriveType.Q2080:
_geometry = Geometry.Q2080;
break;
}
_tracks = new uint[_geometry.Cylinders, _geometry.Heads, _wordsPerTrack];
_type = type;
_diskImagePath = path;
}
public string ImagePath
{
get { return _diskImagePath; }
}
public DriveType Type
{
get { return _type; }
}
public Geometry Geometry
{
get { return _geometry; }
}
public int WordsPerTrack
{
get { return _wordsPerTrack; }
}
public int Cylinder
{
get { return _cylinder; }
}
public int Head
{
get { return _head; }
}
public bool Track0
{
get { return _cylinder == 0; }
}
public bool Index
{
get { return _index; }
}
public bool IsReady
{
//
// We're always spun up and ready as
// long as a valid disk is loaded.
//
get { return _type != DriveType.Invalid; }
}
public bool SeekComplete
{
get { return _seekComplete; }
}
public int WordIndex
{
get { return _wordIndex; }
}
public void SetHead(int head)
{
_head = head % _geometry.Heads;
}
public void Step(bool directionIn, bool stepSignal)
{
if (stepSignal && !_lastStep)
{
if (_stepCount == 0)
{
_directionIn = directionIn;
}
// We queue up the step operation
_timeSinceLastStep = 0;
_stepCount++;
_seekComplete = false;
if (Log.Enabled) Log.Write(LogComponent.ShugartControl, "Buffering step.");
}
_lastStep = stepSignal;
}
public uint ReadData()
{
return _currentWord;
}
public void WriteData(ushort data)
{
_tracks[_cylinder, _head, _wordIndex] = data;
if (Log.Enabled) Log.Write(LogComponent.ShugartControl, "Wrote 0x{0:x4} to c/h/w {1}/{2}/{3}", data, _cylinder, _head, _wordIndex);
}
public void WriteAddressMark(ushort data)
{
_tracks[_cylinder, _head, _wordIndex] = (uint)(data | 0x10000); // Set AM bit
if (Log.Enabled) Log.Write(LogComponent.ShugartControl, "Wrote Address Mark 0x{0:x4} to c/h/w {1}/{2}/{3}", data, _cylinder, _head, _wordIndex);
}
public void WriteCRC(ushort data)
{
_tracks[_cylinder, _head, _wordIndex] = (uint)(data | 0x20000); // Set CRC bit
if (Log.Enabled) Log.Write(LogComponent.ShugartControl, "Wrote CRC 0x{0:x4} to c/h/w {1}/{2}/{3}", data, _cylinder, _head, _wordIndex);
}
public uint DebugRead(int cylinder, int head, int word)
{
return _tracks[cylinder, head, word];
}
/// <summary>
/// This gets called back every 4.32uS at which point we move a new word under the head of the disk,
/// wake up the disk task as appropriate, and deal with buffered seeks if any are in progress.
/// </summary>
/// <param name="skewNsec"></param>
/// <param name="context"></param>
private void DiskWordCallback(ulong skewNsec, object context)
{
//
// Rotate the disk one word. If a wakeup is requested then take care of that.
//
SpinDisk();
//
// Let the controller know a new word is ready.
//
_system.ShugartController.SignalDiskWordReady();
//
// Deal with buffered seeks: if more than 350uS has elapsed since the last step
// pulse from the microcode, we will do the seek now.
// Technically this logic belongs in the drive itself but since we already have this
// convenient event running here we do it now.
//
_timeSinceLastStep += 370;
if (_timeSinceLastStep > 35000 && _stepCount > 0)
{
Seek(_stepCount, _directionIn);
_stepCount = 0;
}
// Queue this event up again.
_system.Scheduler.Schedule(_diskWordDelay - skewNsec, DiskWordCallback);
}
/// <summary>
/// Performs a "buffered seek" by the requested amount
/// in the specified direction.
/// </summary>
/// <param name="count"></param>
/// <param name="directionIn"></param>
private void Seek(int count, bool directionIn)
{
_destinationCylinder = _cylinder + count * (directionIn ? 1 : -1);
// Clip into range
_destinationCylinder = Math.Max(0, _destinationCylinder);
_destinationCylinder = Math.Min(_geometry.Cylinders - 1, _destinationCylinder);
//
// Schedule a seek for about 50ms in the future.
// This is approximate, but we don't need exact timing here.
//
_system.Scheduler.Schedule(_seekDuration, SeekCompleteCallback);
}
private void SeekCompleteCallback(ulong skewNsec, object context)
{
//
// Move to the specified destination.
//
_cylinder = _destinationCylinder;
_seekComplete = true;
_system.ShugartController.SignalSeekComplete();
if (Log.Enabled) Log.Write(LogComponent.ShugartControl, "Seek to {0} complete.", _cylinder);
}
/// <summary>
/// Simulates the rotation of the disc under the drive's heads; each call
/// puts a new word of data under the "head" (which is returned by ReadData). At the end
/// of the track the Index signal is raised.
/// </summary>
/// <param name="skewNsec"></param>
/// <param name="context"></param>
private void SpinDisk()
{
_currentWord = _tracks[_cylinder, _head, _wordIndex];
_wordIndex++;
if (_wordIndex == _wordsPerTrack)
{
_wordIndex = 0;
_index = true;
}
else
{
_index = false;
}
}
//
// Per the SA1000 spec, each track has a capacity of 10.4kbytes -- roughly 5325 words.
//
// We store disk data words in 32-bit words; the low 16 bits are the data, the upper 16 bits being non-zero
// indicates that the word is an Address Mark or a CRC word.
//
// The 10mb SA1004 has 256 tracks and 4 heads.
private Geometry _geometry;
private DriveType _type;
private const int _wordsPerTrack = 5325;
private uint[,,] _tracks;
private int _wordIndex;
private uint _currentWord;
//
// Disk addressing
//
private int _cylinder;
private int _head;
//
// Status bits
private bool _index;
//
// Seek timing and data
//
private ulong _seekDuration = (ulong)(25.0 * Conversion.MsecToNsec);
private int _destinationCylinder;
private bool _seekComplete;
//
// Seek status
//
private bool _lastStep;
private int _stepCount;
private int _timeSinceLastStep;
private bool _directionIn;
//
// Disk word timing.
//
// Time for a single word to move under the heads: ~3.6uS.
// (Disk spins at 3125rpm, meaning 0.0192 seconds/revolution. There are
// 5325 words per track -- 0.0192 / 5325 = 3.60e-6 seconds, or 3.6uS.
// To make this line up nicely with the Star microcode clock (just to make things
// more deterministic) we make the delay 27 microinstruction cycles long --
// 3699nS.
private readonly ulong _diskWordDelay = 3699;
//
// Image file data
//
private string _diskImagePath;
//
// System
//
private DSystem _system;
}
}

848
D/IO/ShugartController.cs Normal file
View File

@@ -0,0 +1,848 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using D.CP;
using D.Logging;
using System;
using System.Collections.Generic;
namespace D.IO
{
/// <summary>
/// Implements the hardware end of the Shugart disk controller, currently only supplying the logic
/// for an SA1000-style drive.
/// </summary>
public class ShugartController
{
public ShugartController(DSystem system, SA1000Drive drive)
{
_system = system;
_drive = drive;
_writePipeline = new Queue<ushort>();
}
public void Reset()
{
_writeEnable = false;
_wakeupControl = 0;
_writeCRC = false;
_transferEnable = false;
_firmwareEnable = false;
_directionIn = false;
_step = false;
_reduceIW = false;
_faultClear = false;
_driveSelect = false;
_headSelect = 0;
_wakeupRequest = ServiceRequest.NoWakeup0;
_verifyError = false;
_crcError = false;
_overrun = false;
_writeFault = false;
_sa1000 = true;
_sectorFound = false;
_indexFound = false;
_readWordReady = false;
_writePipeline.Clear();
ResetTransfer();
}
public void SetKCtl(ushort value)
{
_writeEnable = (value & 0x0001) != 0;
_wakeupControl = (value & 0x0006) >> 1;
_writeCRC = (value & 0x0008) != 0;
_transferEnable = (value & 0x0010) != 0;
_firmwareEnable = (value & 0x0020) != 0;
_directionIn = (value & 0x0040) != 0;
_step = (value & 0x0080) != 0;
_reduceIW = (value & 0x0100) != 0;
_faultClear = (value & 0x0200) != 0;
_driveSelect = (value & 0x0400) != 0;
_headSelect = (value & 0xf800) >> 11;
_wakeupRequest = (ServiceRequest)(_wakeupControl | (_transferEnable ? 0x4 : 0x0));
//
// "The SA1000's WriteFault is cleared by de-selecting the drive for at least 500ns."
// We're not that picky.
//
if (!_driveSelect)
{
_writeFault = false;
}
//
// Step the drive
//
_drive.Step(_directionIn, _step);
// Select the head
//
_drive.SetHead(_headSelect);
//
// Handle wakeup requests that might be pending based on the control value.
//
bool wake = false;
switch (_wakeupRequest)
{
case ServiceRequest.FirmwareEnable:
wake = _firmwareEnable;
break;
case ServiceRequest.SeekComplete:
wake = SeekComplete();
break;
case ServiceRequest.IndexFound:
wake = _indexFound;
break;
case ServiceRequest.SectorFound:
wake = _sectorFound;
break;
default:
wake = false;
break;
}
if (_transferEnable)
{
if (_writeEnable && _wakeupRequest == ServiceRequest.WriteWordNeeded)
{
_transfer = TransferType.Write;
}
else if (!_writeEnable && _wakeupRequest == ServiceRequest.WriteWordNeeded)
{
_transfer = TransferType.Verify;
}
else if (!_writeEnable && _transferEnable && _wakeupRequest == ServiceRequest.ReadWordReady)
{
_transfer = TransferType.Read;
}
else
{
throw new InvalidOperationException("Unexpected combination of service request and write enable flags");
}
}
else
{
//
// Reset transfer state machine if _transferEnable is low.
//
ResetTransfer();
}
if (wake)
{
_system.CP.WakeTask(TaskType.Disk);
}
else
{
_system.CP.SleepTask(TaskType.Disk);
}
/*
if (Log.Enabled) Log.Write(
LogType.Verbose,
LogComponent.ShugartControl,
"KCtl<-0x{0:x4} : we {1} wc {2} wcrc {3} te {4} fe {5} di {6} st {7} ri {8} fc {9} ds {10} hs {11} wq {12}",
value,
_writeEnable,
_wakeupControl,
_writeCRC,
_transferEnable,
_firmwareEnable,
_directionIn,
_step,
_reduceIW,
_faultClear,
_driveSelect,
_headSelect,
_wakeupRequest); */
}
public void SetKCmd(ushort value)
{
if (Log.Enabled) Log.Write(LogType.Verbose, LogComponent.ShugartControl, "KCmd<-0x{0:x4} unimplemented.", value);
}
public void ClrKFlags()
{
// This depends on the kind of service request currently set.
// TODO: figure out exactly what this works out to...
if (_wakeupRequest != ServiceRequest.FirmwareEnable && _wakeupRequest != ServiceRequest.SeekComplete)
{
_system.CP.SleepTask(TaskType.Disk);
}
_verifyError = false;
_crcError = false;
_overrun = false;
_indexFound = false;
_sectorFound = false;
if (Log.Enabled) Log.Write(LogType.Verbose, LogComponent.ShugartControl, "ClrKFlags");
}
public void KStrobe()
{
if (Log.Enabled) Log.Write(LogType.Warning, LogComponent.ShugartControl, "KStrobe unimplemented");
}
public ushort ReadKStatus()
{
// if (Log.Enabled) Log.Write(LogType.Warning, LogComponent.ShugartControl, "<-KStatus read");
// "All status bits are inverted on the X bus because use of the comparable non-inverting drivers
// was forbidden when the board was designed."
ushort value = (ushort)~(
(_verifyError ? 0x0001 : 0x0000) |
(_crcError ? 0x0002 : 0x0000) |
(_overrun ? 0x0004 : 0x0000) |
(_writeFault ? 0x0008 : 0x0000) |
(!_drive.IsReady ? 0x0010 : 0x0000) |
(_sa1000 ? 0x0020 : 0x0000) |
(!_sectorFound ? 0x0040 : 0x0000) |
(_indexFound ? 0x0080 : 0x0000) |
(_firmwareEnable ? 0x0100 : 0x0000) |
(_drive.Track0 ? 0x0200 : 0x0000) |
(SeekComplete() ? 0x0400 : 0x0000) |
((~_headSelect & 0x1f) << 11)
);
/*
if (Log.Enabled) Log.Write(
LogType.Verbose,
LogComponent.ShugartControl,
"0x{0:x4}<-KStatus",
value); */
return value;
}
public ushort ReadKTest()
{
//
// This is only partially implemented, enough to indicate to the microcode what kind of drive is
// attached.
//
// Drive type is exposed via KTest[9] (Sector').
// For an SA1000 this is always 0.
// For a Quantum Q2040, this is always 1.
// For a Quantum Q2080, this is the inverse of the MSB of the head select (KCtl[0]).
//
int value = 0;
switch (_drive.Type)
{
case DriveType.SA1004:
value = 0;
break;
case DriveType.Q2040:
value = 0x40;
break;
case DriveType.Q2080:
value = ((~_headSelect) & 0x10) == 0 ? 0x40 : 0;
break;
}
return (ushort)value;
}
public void SetKOData(ushort value)
{
if (_writePipeline.Count > 1)
{
if (Log.Enabled) Log.Write(LogType.Error,
LogComponent.ShugartControl,
"KOData<- : Not ready for write! (c/h/s/f {0}/{1}/{2}/{3})",
_drive.Cylinder,
_drive.Head,
_debugSector,
_debugField);
}
else
{
_writePipeline.Enqueue(value);
if (Log.Enabled) Log.Write(LogComponent.ShugartControl, "KOData<- : 0x{0:x4} latched", value);
}
if (_wakeupRequest == ServiceRequest.WriteWordNeeded)
{
_system.CP.SleepTask(TaskType.Disk);
}
}
public ushort ReadKIData()
{
if (!_readWordReady)
{
if (Log.Enabled) Log.Write(LogType.Error,
LogComponent.ShugartControl,
"<-KIData : Not ready for read! (c/h/s/f {0}/{1}/{2}/{3})",
_drive.Cylinder,
_drive.Head,
_debugSector,
_debugField);
_overrun = true;
}
//
// Put the microcode back to sleep now that we've read the next word.
//
if (_wakeupRequest == ServiceRequest.ReadWordReady)
{
_system.CP.SleepTask(TaskType.Disk);
}
_readWordReady = false;
// if (Log.Enabled) Log.Write(LogComponent.ShugartControl, "<-KIData Read 0x{0:x4} from c/h/w {1}/{2}/{3} (sector {4} field {5})", _readData, _drive.Cylinder, _drive.Head, _drive.WordIndex, _debugSector, _debugField);
return (ushort)_readData;
}
/// <summary>
/// Called by the drive to let the controller know that a new word is ready to be read or written.
/// </summary>
public void SignalDiskWordReady()
{
if (_drive.Index)
{
_indexFound = true;
// Wake up if we're waiting for the Index
if (_wakeupRequest == ServiceRequest.IndexFound)
{
_system.CP.WakeTask(TaskType.Disk);
}
//
// Reset debug counters
//
_debugSector = -1;
_debugField = -1;
}
// Check for overrun on reads
//
if (_readWordReady &&
_transferEnable &&
_transfer == TransferType.Read &&
_readState == ReadState.Data)
{
// No data available from microcode.
if (Log.Enabled) Log.Write(LogType.Error, LogComponent.ShugartControl, "Read data: overrun.");
_overrun = true;
}
_readWordReady = true;
_readData = _drive.ReadData();
//
// Check for Header Address Mark and set SectorFound bit if present.
if (_readData == _headerAddressMark)
{
_sectorFound = true;
if (_debugSector != -1 && _debugField != 2)
{
//Console.WriteLine("Only found {0} fields on the last sector.", _debugField);
}
_debugSector++; // next sector
_debugField = 0; // header field
}
if (_readData == _labelDataAddressMark)
{
_debugField++; // next field
}
//
// Run the transfer state machine
//
if (_transferEnable)
{
bool wake = false;
switch (_transfer)
{
case TransferType.Write:
switch (_writeState)
{
case WriteState.AutoPreamble:
// One word of preamble is written by the hardware itself.
_drive.WriteData(0);
_transferCount++;
if (_transferCount > 1)
{
_transferCount = 0;
_writeState = WriteState.Preamble;
// Wake the microcode task for the first preamble word
wake = _wakeupRequest == ServiceRequest.WriteWordNeeded;
if (Log.Enabled) Log.Write(LogComponent.ShugartControl, "Preamble word needed.");
}
break;
case WriteState.Preamble:
// Four words of preamble are to be written by the microcode.
if (_writePipeline.Count > 0)
{
_drive.WriteData(_writePipeline.Dequeue());
}
else
{
// No data available from microcode.
if (Log.Enabled) Log.Write(LogType.Error, LogComponent.ShugartControl, "Write preamble: overrun.");
_overrun = true;
}
// Wake the microcode task
wake = _wakeupRequest == ServiceRequest.WriteWordNeeded;
_transferCount++;
if (_transferCount > 4)
{
_transferCount = 0;
_writeState = WriteState.AddressMark;
if (Log.Enabled) Log.Write(LogComponent.ShugartControl, "AM word needed.");
}
else
{
if (Log.Enabled) Log.Write(LogComponent.ShugartControl, "Preamble word needed.");
}
break;
case WriteState.AddressMark:
//
// One word of address mark, written by the microcode.
//
ushort amWord = 0;
if (_writePipeline.Count > 0)
{
amWord = _writePipeline.Dequeue();
}
else
{
// No data available from microcode.
if (Log.Enabled) Log.Write(LogType.Error, LogComponent.ShugartControl, "Write am data: overrun.");
_overrun = true;
}
_drive.WriteAddressMark(amWord);
if (Log.Enabled) Log.Write(LogComponent.ShugartControl, "KOData<- : Address mark is 0x{0:x4}", amWord);
wake = _wakeupRequest == ServiceRequest.WriteWordNeeded;
_writeState = WriteState.Data;
break;
case WriteState.Data:
//
// N words of data, written by the microcode. This state is left only when KCtl<- is set to enable
// writing the CRC.
//
if (_writeCRC)
{
wake = _wakeupRequest == ServiceRequest.WriteWordNeeded;
_writeState = WriteState.CRC;
_transferCount = 0;
// Write the first word of the CRC.
_drive.WriteCRC(0xbeef);
}
else
{
wake = _wakeupRequest == ServiceRequest.WriteWordNeeded;
_transferCount++;
if (_writePipeline.Count > 0)
{
_drive.WriteData(_writePipeline.Dequeue());
}
else
{
// No data available from microcode.
if (Log.Enabled) Log.Write(LogType.Error, LogComponent.ShugartControl, "Write data: overrun.");
_overrun = true;
}
if (Log.Enabled) Log.Write(LogComponent.ShugartControl, "Data word needed.");
}
break;
case WriteState.CRC:
//
// One word of CRC, written by the hardware. This takes three word times, but
// only one word is written.
// We don't actually calculate the CRC (it cannot be read by microcode and
// we aren't simulating corrupted disks), but we write a known
// value for basic sanity checking on reads.
//
//
// The actual CRC was written when we made the state transition.
// We write these other marker words to clobber anything that might
// be underneath them.
//
_drive.WriteCRC(0xdead);
_transferCount++;
// We still keep the microcode alive -- it may write words; we will ignore them.
wake = _wakeupRequest == ServiceRequest.WriteWordNeeded;
if (_transferCount > 1)
{
_writeState = WriteState.Complete;
}
break;
case WriteState.Complete:
// Nothing.
break;
}
break;
case TransferType.Verify:
switch (_verifyState)
{
case VerifyState.WaitForAddressMark:
// Check the current data under the read heads, if it's an address mark,
// wake the Disk task up and move to the Data state.
if (_readData == _headerAddressMark ||
_readData == _labelDataAddressMark)
{
if (Log.Enabled) Log.Write(LogComponent.ShugartControl, "Address Mark 0x{0:x4} found at word 0x{1:x4}, waking microcode.",
_readData,
_drive.WordIndex);
wake = true;
_verifyState = VerifyState.Data;
//
// Set the CRC error flag:
// This is presumed true until all words of the field
// are read and can be compared with the CRC word at the
// end.
//
_crcError = true;
if (_writePipeline.Count == 0)
{
//
// Hack: boot microcode does not "prime" the write pipeline --
// relying on the hardware behavior of clearing the writeData buffer
// automatically; if the microcode hasn't written a word by this point
// enqueue a zero.
//
_writePipeline.Enqueue(0);
}
}
break;
case VerifyState.Data:
//
// If this is a CRC, we check for our canard value and move to the CRC state,
// otherwise compare data.
//
ushort writeData = 0;
if (_writePipeline.Count > 0)
{
writeData = _writePipeline.Dequeue();
}
if ((_readData & 0x20000) != 0)
{
if (_readData != _fakeCRCValue)
{
if (Log.Enabled) Log.Write(LogType.Error, LogComponent.ShugartControl, "Verify CRC: 0x{0:x4} != 0x{1:x4}", _fakeCRCValue, _readData);
_crcError = true;
}
else
{
//
// All good.
//
_crcError = false;
}
_verifyState = VerifyState.CRC;
}
else
{
if (writeData != _readData)
{
if (Log.Enabled) Log.Write(LogComponent.ShugartControl, "Verify data: 0x{0:x4} != 0x{1:x4}", writeData, _readData);
_verifyError = true;
}
}
wake = true;
break;
case VerifyState.CRC:
//
// We sit here forever. Microcode may send two extra words which we will ignore,
// after which it will disable the controller.
//
if (_writePipeline.Count > 0)
{
_writePipeline.Dequeue();
}
wake = true;
break;
}
break;
case TransferType.Read:
switch (_readState)
{
case ReadState.WaitForAddressMark:
// Check the current data under the read heads, if it's an address mark,
// wake the Disk task up and move to the Data state.
if (_readData == _headerAddressMark ||
_readData == _labelDataAddressMark)
{
if (Log.Enabled) Log.Write(LogComponent.ShugartControl, "Address Mark 0x{0:x4} found at word 0x{1:x4}, waking microcode.",
_drive.ReadData(),
_drive.WordIndex);
wake = true;
_readState = ReadState.Data;
}
break;
case ReadState.Data:
//
// If this is a CRC, we check for our canard value and move to the CRC state,
// otherwise the microcode will read the next word via <-KIData
//
if ((_readData & 0x20000) != 0)
{
if (_readData != _fakeCRCValue)
{
if (Log.Enabled) Log.Write(LogType.Error, LogComponent.ShugartControl, "Read CRC: 0x{0:x4} != 0x{1:x4}", _fakeCRCValue, _readData);
_crcError = true;
}
_readState = ReadState.CRC;
}
wake = true;
break;
case ReadState.CRC:
//
// We sit here forever. Microcode may send two extra words which we will ignore,
// after which it will disable the controller.
//
if (_writePipeline.Count > 0)
{
_writePipeline.Dequeue();
}
wake = true;
break;
}
break;
}
//
// Wake up the disk task if we need the microcode to read or write a word.
//
if (wake)
{
_system.CP.WakeTask(TaskType.Disk);
}
}
}
public void SignalSeekComplete()
{
if (_wakeupRequest == ServiceRequest.SeekComplete && SeekComplete())
{
_system.CP.WakeTask(TaskType.Disk);
}
}
private bool SeekComplete()
{
//
// "[SeekComplete] is set when the drive is ready, it is selected, and the heads are not in motion."
//
return (_drive.IsReady && _driveSelect && _drive.SeekComplete);
}
private void ResetTransfer()
{
_transfer = TransferType.None;
_writeState = WriteState.AutoPreamble;
_verifyState = VerifyState.WaitForAddressMark;
_readState = ReadState.WaitForAddressMark;
_transferCount = 0;
_writePipeline.Clear();
}
private DSystem _system;
//
// The drive the controller is connected to.
//
private SA1000Drive _drive;
//
// KCtl bits
//
private bool _writeEnable;
private int _wakeupControl;
private bool _writeCRC;
private bool _transferEnable;
private bool _firmwareEnable;
private bool _directionIn;
private bool _step;
private bool _reduceIW;
private bool _faultClear;
private bool _driveSelect;
private int _headSelect;
private ServiceRequest _wakeupRequest;
//
// KStatus bits
//
private bool _verifyError;
private bool _crcError;
private bool _overrun;
private bool _writeFault;
private bool _sa1000;
private bool _sectorFound;
private bool _indexFound;
//
// Read/Write state machine
//
private int _transferCount;
private uint _readData;
private bool _readWordReady;
//
// Write pipeline (word being actively written to disk, word loaded by KOData<-.
// Kind of overkill to use a Queue object here.
//
private Queue<ushort> _writePipeline = new Queue<ushort>();
private WriteState _writeState;
private VerifyState _verifyState;
private ReadState _readState;
private TransferType _transfer;
//
// Address marks
//
private const int _headerAddressMark = 0x1a141;
private const int _labelDataAddressMark = 0x1a143;
//
// CRC (not actually CRC) value
//
private const int _fakeCRCValue = 0x2beef;
//
// Debug metadata. Keep track of sector and field information.
//
private int _debugSector;
private int _debugField;
private enum TransferType
{
None,
Read,
Write,
Verify
}
private enum ServiceRequest
{
FirmwareEnable = 0,
SeekComplete = 1,
IndexFound = 2, // NB: HWRef has IndexFound and SectorFound values reversed.
SectorFound = 3,
ReadWordReady = 4,
WriteWordNeeded = 5,
NoWakeup0 = 6,
NoWakeup1 = 7,
}
private enum WriteState
{
Invalid = 0,
AutoPreamble, // 2 words of zeros written by the hardware
Preamble, // 4 words of zeros written by the microcode
AddressMark, // 1 word of address mark written by the microcode
Data, // 256 words actual sector data written by the microcode
CRC, // 1 word of CRC, written by the hardware
Complete, // write done
}
private enum VerifyState
{
Invalid = 0,
WaitForAddressMark, // Waiting for the next Address Mark to come around
Data, // Data is being read and compared with data from the microcode, by the hardware
CRC, // CRC is being checked by the hardware
}
private enum ReadState
{
Invalid = 0,
WaitForAddressMark, // Waiting for the next Address Mark to come around
Data, // Data is being read
CRC, // CRC is being checked by the hardware
}
}
}

452
D/IOP/DMAController.cs Normal file
View File

@@ -0,0 +1,452 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using D.Logging;
using System;
namespace D.IOP
{
public class DMAChannel
{
public DMAChannel()
{
Reset();
Device = null;
}
public void Reset()
{
Enabled = false;
Completed = false;
ChAddr = 0;
ChCount = -1;
Type = DMAType.Invalid;
}
public bool Enabled;
public bool Completed;
// Channel address
public ushort ChAddr;
// Channel data count
public int ChCount;
// Channel DMA type
public DMAType Type;
// DMA device
public IDMAInterface Device;
}
public enum DMAType
{
Verify = 0,
Write = 1,
Read = 2,
Invalid = 3,
}
/// <summary>
/// Defines an interface for DMA exchanges between devices and the DMA controller.
/// </summary>
public interface IDMAInterface
{
/// <summary>
/// DMA Request: device request to obtain a DMA cycle from the DMA controller
/// </summary>
bool DRQ { get; }
/// <summary>
/// Writes a single byte to the device from the DMA controller
/// </summary>
/// <param name="value"></param>
void DMAWrite(byte value);
/// <summary>
/// Reads a single byte from the device to the DMA controller
/// </summary>
/// <returns></returns>
byte DMARead();
/// <summary>
/// Indicates to a DMA device that a DMA transfer has completed.
/// </summary>
void DMAComplete();
}
/// <summary>
/// This implements the general behavior of the Intel 8257 as used in the Star.
/// It is far from a generic 8257 simulation, it could be made more general-purpose...
/// </summary>
public class DMAController : IIOPDevice
{
public DMAController(IOProcessor iop)
{
_iop = iop;
for (int i = 0; i < _channels.Length; i++)
{
_channels[i] = new DMAChannel();
}
}
public void RegisterDevice(IDMAInterface device, int channel)
{
_channels[channel].Device = device;
}
public DMAChannel GetChannel(int i)
{
return _channels[i];
}
/// <summary>
/// Hold request. Goes high when the DMA controller is taking control of the
/// bus (so the CPU should take a nap.)
/// </summary>
public bool HRQ
{
get { return _hrq; }
}
/// <summary>
/// Terminal Count: TC is activated when the 14-bit value in the [last] selected channel's
/// terminal count register equals zero.
/// </summary>
public bool TC
{
get
{
if (_lastSelectedChannel != -1)
{
return _channels[_lastSelectedChannel].ChCount == 0;
}
else
{
return false;
}
}
}
public int[] ReadPorts
{
get { return _readPorts; }
}
public int[] WritePorts
{
get { return _writePorts; }
}
public void Reset()
{
_first = true;
_rotatingPriority = false;
_extendedWrite = false;
_tcStop = false;
_autoLoad = false;
_lastSelectedChannel = 0;
_nextToService = 0;
for (int i = 0; i < _channels.Length; i++)
{
_channels[i].Reset();
}
}
/// <summary>
/// Executes a single DMA transfer (if there are
/// any transfers pending). This takes 4 clock
/// cycles.
/// </summary>
public void Execute()
{
//
// See if there's anything to do.
//
int nextChannel = SelectNextChannel();
//
// Raise HRQ if so.
//
_hrq = nextChannel != -1;
if (_hrq)
{
DMAChannel c = _channels[nextChannel];
if (Log.Enabled) Log.Write(LogComponent.IOPDMA, "Channel {0} selected. {1} bytes to {2}, addr 0x{3:x4}.",
nextChannel, c.ChCount, c.Type, c.ChAddr);
switch (c.Type)
{
case DMAType.Verify:
throw new NotImplementedException("DMA Verify not implemented.");
case DMAType.Read:
// Read byte from memory and transfer to device
byte dmaWrite = _iop.Memory.ReadByte(c.ChAddr);
c.Device.DMAWrite(dmaWrite);
if (Log.Enabled) Log.Write(LogComponent.IOPDMA, "DMA read transfer of byte 0x{0:x2} from address 0x{1:x4}", dmaWrite, c.ChAddr);
break;
case DMAType.Write:
// Read byte from device and transfer to memory.
byte dmaRead = c.Device.DMARead();
_iop.Memory.WriteByte(c.ChAddr, dmaRead);
if (Log.Enabled) Log.Write(LogComponent.IOPDMA, "DMA write transfer of byte 0x{0:x2} to address 0x{1:x4}", dmaRead, c.ChAddr);
break;
}
// Increment address, decrement counter.
c.ChAddr++;
c.ChCount--;
// If the counter runs out, stop the channel if so enabled.
if (c.ChCount == 0)
{
if (Log.Enabled) Log.Write(LogComponent.IOPDMA, "Channel {0} completed.", nextChannel);
// Stop this crazy thing.
if (_tcStop)
{
c.Enabled = false;
if (Log.Enabled) Log.Write(LogComponent.IOPDMA, "Channel {0} disabled.", nextChannel);
}
c.Completed = true;
c.Device.DMAComplete();
}
_lastSelectedChannel = nextChannel;
}
}
public void WritePort(int port, byte value)
{
switch ((DMAPorts)port)
{
case DMAPorts.DmaMode:
_first = true;
_channels[0].Enabled = (value & 0x01) != 0;
_channels[1].Enabled = (value & 0x02) != 0;
_channels[2].Enabled = (value & 0x04) != 0;
_channels[3].Enabled = (value & 0x08) != 0;
_rotatingPriority = (value & 0x10) != 0;
_extendedWrite = (value & 0x20) != 0;
_tcStop = (value & 0x40) != 0;
_autoLoad = (value & 0x80) != 0;
if (Log.Enabled) Log.Write(LogComponent.IOPDMA, "DMAMode: en: {0},{1},{2},{3} rp {4} ew {5} tc {6} al {7}",
_channels[0].Enabled, _channels[1].Enabled, _channels[2].Enabled, _channels[3].Enabled,
_rotatingPriority, _extendedWrite, _tcStop, _autoLoad);
if (_autoLoad)
{
throw new NotImplementedException("AutoLoad not yet implemented.");
}
break;
case DMAPorts.DmaCh0Addr:
case DMAPorts.DmaCh1Addr:
case DMAPorts.DmaCh2Addr:
case DMAPorts.DmaCh3Addr:
{
int ch = (port - 0xa0) / 2;
if (_first)
{
_channels[ch].ChAddr = value;
}
else
{
_channels[ch].ChAddr = (ushort)(_channels[ch].ChAddr | (value << 8));
if (Log.Enabled) Log.Write(LogComponent.IOPDMA, "Channel {0} address set to 0x{1:x4}", ch, _channels[ch].ChAddr);
}
_first = !_first;
}
break;
case DMAPorts.DmaCh0Count:
case DMAPorts.DmaCh1Count:
case DMAPorts.DmaCh2Count:
case DMAPorts.DmaCh3Count:
{
int ch = (port - 0xa1) / 2;
if (_first)
{
_channels[ch].ChCount = value;
}
else
{
// + 1 because the value loaded is the number of bytes-1.
_channels[ch].ChCount = (ushort)(_channels[ch].ChCount | ((value & 0x3f) << 8)) + 1;
_channels[ch].Type = (DMAType)(value >> 6);
if (Log.Enabled) Log.Write(LogComponent.IOPDMA, "Channel {0} count set to 0x{1:x4}", ch, _channels[ch].ChCount);
if (Log.Enabled) Log.Write(LogComponent.IOPDMA, "Channel {0} type set to {1}", ch, _channels[ch].Type);
}
_first = !_first;
}
break;
default:
throw new InvalidOperationException(String.Format("Unexpected write to port {0:x2}", port));
}
}
public byte ReadPort(int port)
{
byte value = 0;
switch ((DMAPorts)port)
{
case DMAPorts.DmaStatus:
// "The low 4 bits of this register indicate what channels have completed."
value = (byte)((_channels[0].Completed ? 0x01 : 0x00) |
(_channels[1].Completed ? 0x02 : 0x00) |
(_channels[2].Completed ? 0x04 : 0x00) |
(_channels[3].Completed ? 0x08 : 0x00));
if (Log.Enabled) Log.Write(LogComponent.IOPDMA, "DMAStatus read {0}", value);
// TC Status bits are cleared after the status register is read.
_channels[0].Completed = false;
_channels[1].Completed = false;
_channels[2].Completed = false;
_channels[3].Completed = false;
break;
default:
throw new InvalidOperationException(String.Format("Unexpected read from port {0:x2}", port));
}
return value;
}
private int SelectNextChannel()
{
int nextChannel = -1;
if (!_rotatingPriority)
{
//
// Select the highest priority channel that has something to do.
// Channel 0 has the highest priority.
//
for (int i = 0; i < 4; i++)
{
if (_channels[i].Enabled && _channels[i].Device != null &&_channels[i].Device.DRQ)
{
nextChannel = i;
break;
}
}
}
else
{
//
// Select the next channel in the cycle, if it has something to do.
//
for (int i = 0; i < 4; i++)
{
int j = (i + _nextToService) % 4;
if (_channels[j].Enabled && _channels[j].Device != null && _channels[j].Device.DRQ)
{
nextChannel = j;
_nextToService = j + 1;
break;
}
}
}
return nextChannel;
}
private readonly int[] _readPorts = new int[]
{
(int)DMAPorts.DmaStatus,
};
private readonly int[] _writePorts = new int[]
{
(int)DMAPorts.DmaCh0Addr,
(int)DMAPorts.DmaCh0Count,
(int)DMAPorts.DmaCh1Addr,
(int)DMAPorts.DmaCh1Count,
(int)DMAPorts.DmaCh2Addr,
(int)DMAPorts.DmaCh2Count,
(int)DMAPorts.DmaCh3Addr,
(int)DMAPorts.DmaCh3Count,
(int)DMAPorts.DmaMode,
};
// Which address byte to store when loading registers.
private bool _first;
private DMAChannel[] _channels = new DMAChannel[4];
// DMA Mode Flags
private bool _rotatingPriority;
private bool _extendedWrite;
private bool _tcStop;
private bool _autoLoad;
// Scheduling
private int _nextToService;
private int _lastSelectedChannel;
private IOProcessor _iop;
private enum DMAPorts
{
DmaCh0Addr = 0xa0,
DmaCh0Count = 0xa1,
DmaCh1Addr = 0xa2,
DmaCh1Count = 0xa3,
DmaCh2Addr = 0xa4,
DmaCh2Count = 0xa5,
DmaCh3Addr = 0xa6,
DmaCh3Count = 0xa7,
DmaMode = 0xa8, // Write
DmaStatus = 0xa8, // Read
}
private bool _hrq;
}
}

1039
D/IOP/FloppyController.cs Normal file

File diff suppressed because it is too large Load Diff

37
D/IOP/I8085IOBus.cs Normal file
View File

@@ -0,0 +1,37 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace D.IOP
{
public interface I8085IOBus
{
void Out(byte port, byte o);
byte In(byte port);
}
}

42
D/IOP/I8085MemoryBus.cs Normal file
View File

@@ -0,0 +1,42 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace D.IOP
{
public interface I8085MemoryBus
{
byte ReadByte(ushort address);
void WriteByte(ushort address, byte b);
ushort ReadWord(ushort address);
void WriteWord(ushort address, ushort w);
}
}

41
D/IOP/IIOPDevice.cs Normal file
View File

@@ -0,0 +1,41 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace D.IOP
{
public interface IIOPDevice
{
int[] ReadPorts { get; }
int[] WritePorts { get; }
void WritePort(int port, byte value);
byte ReadPort(int port);
}
}

95
D/IOP/IOPIOBus.cs Normal file
View File

@@ -0,0 +1,95 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using D.Logging;
using System;
namespace D.IOP
{
public class IOPIOBus : I8085IOBus
{
public IOPIOBus()
{
_writeDispatch = new IIOPDevice[256];
_readDispatch = new IIOPDevice[256];
}
public void RegisterDevice(IIOPDevice device)
{
foreach(byte port in device.ReadPorts)
{
if (_readDispatch[port] != null)
{
throw new InvalidOperationException(String.Format("Read port collision {0:x2} when adding device {1}", port, device));
}
_readDispatch[port] = device;
}
foreach (byte port in device.WritePorts)
{
if (_writeDispatch[port] != null)
{
throw new InvalidOperationException(String.Format("Write port collision {0:x2} when adding device {1}", port, device));
}
_writeDispatch[port] = device;
}
}
public void Out(byte port, byte val)
{
if (_writeDispatch[port] != null)
{
_writeDispatch[port].WritePort(port, val);
}
else
{
if (Log.Enabled) Log.Write(LogComponent.IOPIO, "Unhandled write to IO port ${0:x2}, ${1:x2}", port, val);
}
}
public byte In(byte port)
{
if (_readDispatch[port] != null)
{
return _readDispatch[port].ReadPort(port);
}
else
{
if (Log.Enabled) Log.Write(LogComponent.IOPIO, "Unhandled read from IO port ${0:x2}", port);
return 0x00;
}
}
private IIOPDevice[] _writeDispatch;
private IIOPDevice[] _readDispatch;
}
}

224
D/IOP/IOPMemoryBus.cs Normal file
View File

@@ -0,0 +1,224 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using D.Logging;
using System;
using System.IO;
namespace D.IOP
{
/// <summary>
/// Implements the IOP's memory bus. Memory map looks like
/// (see SysDefs.asm):
///
/// $0000 - $1FFF : PROM (8K)
/// $2000 - $5FFF : RAM (16K)
/// $80B0 - $80BF : Host Addr PROM (16 bytes)
///
/// </summary>
public class IOPMemoryBus : I8085MemoryBus
{
public IOPMemoryBus(I8085IOBus ioBus)
{
// 8K ROM
_rom = new byte[0x2000];
// 16K RAM. We actually allocate 24K
// to make addressing simpler.
_ram = new byte[0x6000];
// The 8085's IO ports are also memory-mapped at
// $8000 + port.
_io = ioBus;
LoadPROMs();
LoadHostIDProm();
}
public byte ReadByte(ushort address)
{
if (address < 0x2000)
{
return _rom[address];
}
else if(address < 0x6000)
{
return _ram[address];
}
else if(address > 0x80af && address < 0x80c0)
{
//
// Host Address PROM. This contains 12 nybbles of data containing the 48-bit
// Ethernet host address and a checksum.
// The PROM itself is 16 bytes in size.
//
return _hostIdProm[address - 0x80b0];
}
else if(address > 0x8000 && address < 0x8100)
{
// Memory-mapped I/O ports
if (Log.Enabled) Log.Write(LogComponent.IOPMemory, "Memory-mapped I/O read from {0:x4}", address);
return _io.In((byte)address);
}
else
{
// Nothing mapped here.
if (Log.Enabled) Log.Write(LogComponent.IOPMemory, "Read from nonexistent memory at {0:x4}", address);
return 0xff;
}
}
public void WriteByte(ushort address, byte b)
{
if (address < 0x2000)
{
// ROM, not writeable.
}
else if (address < 0x6000)
{
_ram[address] = b;
}
else if (address > 0x8000 && address < 0x8100)
{
// Memory-mapped I/O ports
if (Log.Enabled) Log.Write(LogComponent.IOPMemory, "Memory-mapped I/O write at {0:x4}", address);
_io.Out((byte)address, b);
}
else
{
// Nothing mapped here.
if (Log.Enabled) Log.Write(LogComponent.IOPMemory, "Write to nonexistent memory at {0:x4}", address);
}
}
public ushort ReadWord(ushort address)
{
return (ushort)(ReadByte(address) | (ReadByte((ushort)(address + 1)) << 8));
}
public void WriteWord(ushort address, ushort u)
{
WriteByte(address++, (byte)u);
WriteByte(address, (byte)(u >> 8));
}
public void UpdateHostIDProm()
{
LoadHostIDProm();
}
private void LoadPROMs()
{
// Loads PROMs into ROM space. The files for rev 3.1 are:
// U129 - 537P03029 - $0000
// U130 - 537P03030 - $0800
// U131 - 537P03700 - $1000
// U132 - 537P03032 - $1800
LoadPROM("537P03029.bin", 0x0000);
LoadPROM("537P03030.bin", 0x0800);
LoadPROM("537P03700.bin", 0x1000);
LoadPROM("537P03032.bin", 0x1800);
}
private void LoadPROM(string promName, ushort address)
{
string promPath = Path.Combine("IOP", "PROM", promName);
using (FileStream promStream = new FileStream(promPath, FileMode.Open, FileAccess.Read))
{
if (promStream.Length != 0x800)
{
throw new InvalidOperationException(
String.Format("PROM file {0} has unexpected size 0x{1:x}", promName, promStream.Length));
}
promStream.Read(_rom, address, 0x800);
}
}
private void LoadHostIDProm()
{
//
// Copy data from the current emulator config into the HostID prom array.
//
for (int i = 0; i < 6; i++)
{
byte val = (byte)(Configuration.HostID >> (5 - i) * 8);
SetIDPromByte(i, val);
}
//
// Calculate the checksum of the PROM. Looking at the PROM as
// 8 bytes (big-endian) rather than 16 nibbles, this is a cyclic XOR of:
// Bytes 0 & 1 - XOR'd together
// Bytes 2-5
// Checksum is stored in Byte 6, complemented checksum is in Byte 7.
// Byte 8 appears to be unused.
//
byte checksum = RotateLeft((byte)(GetIDPromByte(0) ^ GetIDPromByte(1)));
for (int i = 2; i < 6; i++)
{
checksum ^= GetIDPromByte(i);
checksum = RotateLeft(checksum);
}
SetIDPromByte(6, checksum);
SetIDPromByte(7, (byte)(~checksum));
}
private byte GetIDPromByte(int byteNumber)
{
return (byte)((_hostIdProm[byteNumber * 2] & 0xf) | (_hostIdProm[byteNumber * 2 + 1] << 4));
}
private void SetIDPromByte(int byteNumber, byte value)
{
_hostIdProm[byteNumber * 2] = (byte)(value & 0xf);
_hostIdProm[byteNumber * 2 + 1] = (byte)(value >> 4);
}
private byte RotateLeft(byte value)
{
return (byte)((value << 1) | ((value & 0x80) != 0 ? 1 : 0));
}
private byte[] _rom;
private byte[] _ram;
//
// Host ID Prom: contains the host's Ethernet MAC + checksum.
// 16 nybbles long.
private byte[] _hostIdProm = new byte[16];
private I8085IOBus _io;
}
}

165
D/IOP/IOProcessor.cs Normal file
View File

@@ -0,0 +1,165 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace D.IOP
{
/// <summary>
/// Encapsulates the entirety of the IOP hardware.
/// </summary>
public class IOProcessor
{
public IOProcessor(DSystem system)
{
_system = system;
_io = new IOPIOBus();
_mem = new IOPMemoryBus(_io);
_cpu = new i8085(_mem, _io);
_keyboard = new Keyboard();
_mouse = new Mouse();
//
// 8" floppy drive used by the IOP
//
_floppyDrive = new FloppyDrive(_system);
//
// Add devices to the IO bus
//
_miscIO = new MiscIO(this);
_floppyController = new FloppyController(_floppyDrive, _system);
_dma = new DMAController(this);
_tty = new Printer();
//
// Register DMA devices with controller
//
_dma.RegisterDevice(_floppyController, 0); // Floppy, DMA Channel 0
_dma.RegisterDevice(_system.CP, 1); // CP, DMA Channel 1
_io.RegisterDevice(_miscIO);
_io.RegisterDevice(_floppyController);
_io.RegisterDevice(_dma);
_io.RegisterDevice(_system.CP);
//_io.RegisterDevice(_tty);
Reset();
}
public void Reset()
{
_cpu.Reset();
_miscIO.Reset();
_dma.Reset();
_floppyController.Reset();
}
/// <summary>
/// Executes a single 8085 instruction or runs the DMA controller if DMA is in progress,
/// and returns the number of 3Mhz clock cycles consumed.
/// </summary>
/// <returns></returns>
public int Execute()
{
//
// Run the DMA controller, see if it has anything to do this cycle.
//
_dma.Execute();
if (_dma.HRQ)
{
// Yes, it executed a DMA transfer which means the CPU doesn't get to run.
return 4; // A DMA cycle takes 4 clocks.
}
else
{
// Run the CPU for one instruction.
return _cpu.Execute();
}
}
public i8085 CPU
{
get { return _cpu; }
}
public I8085MemoryBus Memory
{
get { return _mem; }
}
public MiscIO MiscIO
{
get { return _miscIO; }
}
public FloppyController FloppyController
{
get { return _floppyController; }
}
public DMAController DMAController
{
get { return _dma; }
}
public Keyboard Keyboard
{
get { return _keyboard; }
}
public Mouse Mouse
{
get { return _mouse; }
}
public Printer Printer
{
get { return _tty; }
}
private i8085 _cpu;
private IOPIOBus _io;
private I8085MemoryBus _mem;
//
// Devices on the IOP
//
private MiscIO _miscIO;
private FloppyController _floppyController;
private DMAController _dma;
private Keyboard _keyboard;
private Mouse _mouse;
private Printer _tty;
private DSystem _system;
//
// Devices used by the IOP
//
private FloppyDrive _floppyDrive;
}
}

237
D/IOP/Keyboard.cs Normal file
View File

@@ -0,0 +1,237 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using D.Logging;
using System.Collections.Generic;
using System.Threading;
namespace D.IOP
{
/// <summary>
/// Defines the scancodes understood by the IOP.
/// The below is currently derived from work undertaken by Al Kossow
/// (many thanks.)
/// This table corresponds to the standard US keyboard layout for the
/// Star/1108. TODO: How to deal with international arrangements?
/// </summary>
public enum KeyCode
{
Invalid = 0x00,
D1 = 0x10,
T10 = 0x11,
Defaults = 0x12, // T9
LargerSmaller = 0x13, // T8
Subscript = 0x14, // T7
Undo = 0x15, // R6
Superscript = 0x16, // T6
Properties = 0x17, // L12 (Also Ctrl)
Move = 0x18, // L9
Copy = 0x19, // L6
Underline = 0x1a, // T5
Italics = 0x1b, // T4
Bold = 0x1c, // T3
Center = 0x1d, // T2
T1 = 0x1e,
R4 = 0x20,
SkipNext = 0x22, // R1
Help = 0x23, // R2
Margins = 0x24, // R5
R3 = 0x25,
L10 = 0x27,
Same = 0x28, // L7
L4 = 0x29,
L1 = 0x2a,
A9 = 0x2d,
DefnExpand = 0x30, // R7 (Also Esc)
R10 = 0x31,
Keyboard = 0x32, // R11 (^X)
Font = 0x33, // R8 (\,|)
R9 = 0x34,
Stop = 0x35, // R12
Space = 0x36,
Open = 0x37, // L11 (Also Meta)
L8 = 0x38,
Find = 0x39, // L5
Again = 0x3a, // L2
Delete = 0x3b, // L3
A8 = 0x3c,
A11 = 0x3d,
A12 = 0x41, // Shift ?
RightShift = 0x42, // A6
FSlash = 0x43,
Period = 0x44,
Comma = 0x45,
M = 0x46,
N = 0x47,
B = 0x48,
V = 0x49,
C = 0x4a,
X = 0x4b,
Z = 0x4c,
K47 = 0x4e,
Return = 0x50, // A4
BackQuote = 0x51, // K46
Quote = 0x52, // K43
Colon = 0x53,
L = 0x54,
K = 0x55,
J = 0x56,
H = 0x57,
G = 0x58,
F = 0x59,
D = 0x5a,
S = 0x5b,
A = 0x5c,
Lock = 0x5e, // A3
LeftShift = 0x5f, // A5
A10 = 0x60,
RBracket = 0x61, // K45
LBracket = 0x62, // K42
P = 0x63,
O = 0x64,
I = 0x65,
U = 0x66,
Y = 0x67,
T = 0x68,
R = 0x69,
E = 0x6a,
W = 0x6b,
Q = 0x6c,
Tab = 0x6d, // A1
D2 = 0x6f,
Backspace = 0x70, // A2
Equals = 0x71,
Minus = 0x72,
N0 = 0x73,
N9 = 0x74,
N8 = 0x75,
N7 = 0x76,
N6 = 0x77,
N5 = 0x78,
N4 = 0x79,
N3 = 0x7a,
N2 = 0x7b,
N1 = 0x7c,
FArrow = 0x7d, // K48
}
public class Keyboard
{
public Keyboard()
{
_keyboardQueue = new Queue<KeyCode>();
_lock = new ReaderWriterLockSlim();
}
public byte ReadData()
{
if (Log.Enabled) Log.Write(LogComponent.IOPKeyboard, "Key data {0} (0x{1:x2}) read.", _keyData, (int)_keyData);
return (byte)_keyData;
}
public void NextData()
{
_lock.EnterUpgradeableReadLock();
if (_keyboardQueue.Count > 0)
{
_lock.EnterWriteLock();
_keyData = _keyboardQueue.Dequeue();
if (Log.Enabled) Log.Write(LogComponent.IOPKeyboard, "Key data {0} (0x{1:x2}) dequeued.", _keyData, (int)_keyData);
_lock.ExitWriteLock();
}
else
{
// No data available.
_keyData = KeyCode.Invalid;
}
_lock.ExitUpgradeableReadLock();
}
public bool DataReady()
{
_lock.EnterReadLock();
bool ready = _keyboardQueue.Count > 0;
_lock.ExitReadLock();
return ready;
}
public void EnableDiagnosticMode()
{
//
// Per MoonIOPCSTest.asm:
// "Return sequence of events should be all characters held down followed by D2, D1."
// Right now, just enqueue d2, d1
_lock.EnterWriteLock();
_keyboardQueue.Enqueue(KeyCode.D2);
_keyboardQueue.Enqueue(KeyCode.D1);
_lock.ExitWriteLock();
}
public void DisableDiagnosticMode()
{
_lock.EnterWriteLock();
_keyboardQueue.Clear();
_lock.ExitWriteLock();
}
public void KeyDown(KeyCode keycode)
{
_lock.EnterWriteLock();
// Bit 0 = 0 indicates the key being pressed
_keyboardQueue.Enqueue((KeyCode)((int)keycode & 0x7f));
_lock.ExitWriteLock();
}
public void KeyUp(KeyCode keycode)
{
_lock.EnterWriteLock();
// Bit 0 = 1 indicates the key being released
_keyboardQueue.Enqueue((KeyCode)((int)keycode | 0x80));
_lock.ExitWriteLock();
}
private KeyCode _keyData;
private Queue<KeyCode> _keyboardQueue;
private ReaderWriterLockSlim _lock;
}
}

453
D/IOP/MiscIO.cs Normal file
View File

@@ -0,0 +1,453 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using D.Logging;
using System;
namespace D.IOP
{
public enum AltBootValues
{
None = -1,
DiagnosticRigid = 0,
Rigid,
Floppy,
Ethernet,
DiagnosticEthernet,
DiagnosticFloppy,
AlternateEthernet,
DiagnosticTrident1,
DiagnosticTrident2,
DiagnosticTrident3,
HeadCleaning
}
public class MiscIO : IIOPDevice
{
public MiscIO(IOProcessor iop)
{
//
// Keep a reference to the IOP we belong to
// so we can poll the interrupt status of various
// devices.
//
_iop = iop;
_todClock = new TODClock();
// Default to no alt boot
_altBoot = AltBootValues.None;
Reset();
}
public int[] ReadPorts
{
get { return _readPorts; }
}
public int[] WritePorts
{
get { return _writePorts; }
}
/// <summary>
/// Allows UI to be alerted when the MP value changes.
/// </summary>
public delegate void MPChangedEventHandler();
public MPChangedEventHandler MPChanged;
public AltBootValues AltBoot
{
get
{
return _altBoot;
}
set
{
_altBoot = value;
_altBootCounter = (int)value;
}
}
public void Reset()
{
_mPanelValue = 0;
_mPanelBlank = true;
_lastClockFlags = 0;
_dmaTestValue = 0;
_altBootCounter = (int)_altBoot;
_todClock.Reset();
}
public void WritePort(int port, byte value)
{
switch (port)
{
case 0xd0:
//
// DMA Test Register
// At this point, it appears all this does is store the value written,
// and return it when read.
//
_dmaTestValue = value;
if (Log.Enabled) Log.Write(LogComponent.IOPMisc, "Misc IO port DMATest write {0:x2}", value);
break;
case 0xe9: // KB, MP, TOD clocks
DoMiscClock(value);
break;
case 0xea: // Clear TOD interrupt
_todClock.ClearInterrupt();
if (Log.Enabled) Log.Write(LogComponent.IOPMisc, "Misc IO TOD interrupt clear.");
break;
case 0xed: // Clear Mouse X,Y counters
_iop.Mouse.Clear();
break;
case 0xef: // KB, MP, TOD control
//
// Control bits:
// 0x40 - pReadKBData - read KB data
// 0x20 - KBTone - KB speaker bit
// 0x10 - KBDiag - Set KB Diag mode
// 0x08 - BlankMPanel - Blank MPanel bit
// 0x04 - ReadTimeMode - Read TOD mode bit
// 0x02 - ClearTimeMode - Clear TOD mode bit
// 0x01 - SetTimeMode - Set TOD mode bit
//
_mPanelBlank = (value & 0x08) != 0;
MPChanged();
if ((value & 0x40) != 0)
{
// Prime the next byte of keyboard data.
_iop.Keyboard.NextData();
if (Log.Enabled) Log.Write(LogComponent.IOPMisc, "Misc IO Keyboard data clock.");
}
if ((value & 0x10) != 0)
{
_iop.Keyboard.EnableDiagnosticMode();
if (Log.Enabled) Log.Write(LogComponent.IOPMisc, "Misc IO Keyboard diagnostic mode entered.");
}
if ((value & 0x04) != 0)
{
_todClock.SetMode(TODAccessMode.Read);
}
if ((value & 0x02) != 0)
{
_todClock.SetMode(TODAccessMode.Clear);
}
if ((value & 0x01) != 0)
{
_todClock.SetMode(TODAccessMode.Set);
}
break;
default:
throw new InvalidOperationException(String.Format("Unexpected write to port {0:x2}", port));
}
}
public byte ReadPort(int port)
{
byte value;
switch(port)
{
case 0xd0:
//
// DMA Test Register:
// Just return whatever value was written.
//
value = _dmaTestValue;
if (Log.Enabled) Log.Write(LogComponent.IOPMisc, "Misc IO port DMATest read {0:x2}", value);
break;
case 0xef:
//
// MiscInput1: AltBoot,TimeData,PowerFailed,TODInt,CSParError,MouseSw1,Sw2,Sw3
//
// Provide the AltBoot switch so we can allow floppy booting:
// The way the boot prom selects a boot device is to:
// 1) check for the AltBoot bit (meaning the alternate boot switch is held down)
// 2) if so, increment MP, wait 1 second.
// 3) repeat 1 + 2 until AltBoot is no longer set.
// 4) the final value is the device to be booted from.
//
// We decrement our boot device counter on every read until we hit zero at which point
// we "release" the button.
//
if (_altBootCounter > 0)
{
value = (int)MiscInput1Flags.AltBoot;
_altBootCounter--;
}
else
{
value = 0;
}
//
// OR in other bits
//
value = (byte)(value |
_todClock.ReadClockBit() |
(_todClock.PowerLoss ? 0x20 : 0x0) |
(_todClock.Interrupt ? 0x10 : 0x0) |
(int)(_iop.Mouse.Buttons) |
(int)MiscInput1Flags.CSParity /* active low, we don't want parity errors */);
if (Log.Enabled) Log.Write(LogComponent.IOPMisc, "Misc IO port MiscInput1 read {0:x2}", value);
break;
case 0xe9:
//
// Interrupt status register.
// Most of these aren't real interrupts (they don't trigger an 8085 interrupt) but are simply
// status flags raised by various bits of hardware that get polled by the main IOP code loop.
// This register is not a latch, it merely buffers these signals which are generated by their
// respective devices.
// We combine those bits here from their various sources. All of these signals are active low.
//
// Add more as more things get implemented...
//
value = (byte)~(
(_iop.FloppyController.Interrupt ? 0x80 : 0x00) |
(_iop.Keyboard.DataReady() ? 0x40 : 0x00));
/* TODO: disabled until it can be completed
(_iop.Printer.TxRequest ? 0x20 : 0x00) |
(_iop.Printer.RxRequest ? 0x10 : 0x00)); */
// if (Log.Enabled) Log.Write(LogComponent.IOPMisc, "Misc IO port Interrupt Status read {0:x2}", value);
break;
case 0xea:
//
// Keyboard data latch. Data is inverted, and bit 0 (msb) indicates keystroke up or down (1 = down).
//
value = (byte)(~_iop.Keyboard.ReadData());
if (Log.Enabled) Log.Write(LogComponent.IOPMisc, "Misc IO port Keyboard Data read {0:x2}", value);
break;
case 0xed:
//
// Mouse X counter
//
value = (byte)_iop.Mouse.MouseX;
break;
case 0xee:
//
// Mouse Y counter
//
value = (byte)_iop.Mouse.MouseY;
break;
default:
value = 0;
break;
}
return value;
}
/// <summary>
/// The value of the MP display (displayed in red LEDs on the front of the Star)
/// </summary>
public int MPanelValue
{
get { return _mPanelValue; }
}
/// <summary>
/// Whether the MP display is currently turned on
/// </summary>
public bool MPanelBlank
{
get { return _mPanelBlank; }
}
public TODClock TODClock
{
get { return _todClock; }
}
private void DoMiscClock(byte clockFlags)
{
//
// From code in BootSubs.asm (not coincidentally labeled DoMiscClock):
// The various clocks are clocked manually by the 8085 by writing a zero
// to the appropriate clock bit followed by a 1 to the clock bit, on a 0->1 transition
// data is clocked into the appropriate register, or cleared/incremented for the MPanel display.
//
//
// on a 1->0 transition for a clock bit we will take the appropriate action.
//
for (int clockFlag = 0x1; clockFlag < 0x100; clockFlag = clockFlag << 1)
{
if ((clockFlags & clockFlag) == 0 &&
(_lastClockFlags & clockFlag) != 0)
{
switch((ClockFlags)clockFlag)
{
case ClockFlags.ClrMPanel:
_mPanelValue = 0;
MPChanged();
break;
case ClockFlags.IncMPanel:
_mPanelValue = (_mPanelValue + 1) % 10000;
MPChanged();
break;
case ClockFlags.TODRead:
_todClock.ClockBit(TODClockType.Read);
break;
case ClockFlags.TODSetA:
_todClock.ClockBit(TODClockType.SetA);
break;
case ClockFlags.TODSetB:
_todClock.ClockBit(TODClockType.SetB);
break;
case ClockFlags.TODSetC:
_todClock.ClockBit(TODClockType.SetC);
break;
case ClockFlags.TODSetD:
_todClock.ClockBit(TODClockType.SetD);
break;
}
}
}
_lastClockFlags = clockFlags;
}
// MP data
private bool _mPanelBlank;
private int _mPanelValue;
// Alt boot counter -- decremented on access to MiscInput1, used to simulate
// holding down of AltBoot button for N seconds to select boot device.
private int _altBootCounter;
// The value to set the AltBoot counter to at reset.
private AltBootValues _altBoot;
// Clock register data
private int _lastClockFlags;
// DMA Test Register data
private byte _dmaTestValue;
// TOD Clock
private TODClock _todClock;
// Reference to the IOP we belong to.
private IOProcessor _iop;
private readonly int[] _readPorts = new int[]
{
0xd0, // DMA Test Register
0xe9, // Interrupt request bits (read)
0xea, // Keyboard data latch
0xed, // Mouse X counter
0xee, // Mouse Y counter
0xef // Miscellaneous input
};
private readonly int[] _writePorts = new int[]
{
0xd0, // DMA Test Register
0xe9, // KB, MP, TOD clocks (write)
0xea, // Clear TOD interrupt (write)
0xed, // Clear Mouse X,Y counters
0xef // KB, MP, TOD control (write)
};
//
// Misc clocks flags
//
[Flags]
private enum ClockFlags
{
ClrMPanel = 0x40,
IncMPanel = 0x20,
TODRead = 0x10,
TODSetA = 0x08,
TODSetB = 0x04,
TODSetC = 0x02,
TODSetD = 0x01,
}
[Flags]
private enum MiscInput1Flags
{
AltBoot = 0x80,
TODData = 0x40,
PowerFailed = 0x20,
TODInt = 0x10,
CSParity = 0x08,
MouseSw3 = 0x4,
MouseSw2 = 0x2,
MouseSw1 = 0x1,
}
[Flags]
private enum InterruptRequestFlags
{
Floppy = 0x80,
Keyboard = 0x40,
PrinterTx = 0x20,
PrinterRx = 0x10,
Misc = 0x08,
RS232 = 0x04,
LSEPTx = 0x02,
LSEPRx = 0x01,
}
}
}

103
D/IOP/Mouse.cs Normal file
View File

@@ -0,0 +1,103 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace D.IOP
{
[Flags]
public enum StarMouseButton
{
None = 0,
Left = 0x4,
Right = 0x2,
Middle = 0x1,
}
public class Mouse
{
public Mouse()
{
}
public int MouseX
{
get { return _mouseX; }
}
public int MouseY
{
get { return _mouseY; }
}
public StarMouseButton Buttons
{
get { return _buttons; }
}
public void Clear()
{
_mouseX = 0;
_mouseY = 0;
}
public void MouseDown(StarMouseButton button)
{
_buttons |= button;
}
public void MouseUp(StarMouseButton button)
{
_buttons &= (~button);
}
public void MouseMove(int dx, int dy)
{
_mouseX += dx;
_mouseY += dy;
// Clip into range (-128 to 127)
_mouseX = Math.Max(-128, _mouseX);
_mouseX = Math.Min(127, _mouseX);
_mouseY = Math.Max(-128, _mouseY);
_mouseY = Math.Min(127, _mouseY);
}
private int _mouseX;
private int _mouseY;
private StarMouseButton _buttons;
}
}

BIN
D/IOP/PROM/537P03029.bin Normal file

Binary file not shown.

BIN
D/IOP/PROM/537P03030.bin Normal file

Binary file not shown.

BIN
D/IOP/PROM/537P03032.bin Normal file

Binary file not shown.

BIN
D/IOP/PROM/537P03700.bin Normal file

Binary file not shown.

123
D/IOP/Printer.cs Normal file
View File

@@ -0,0 +1,123 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using D.Logging;
namespace D.IOP
{
/// <summary>
/// Stub implementation of the Printer port. This is just
/// enough to get the rigid diagnostic set to pass.
/// </summary>
public class Printer : IIOPDevice
{
public Printer()
{
Reset();
}
public void Reset()
{
_rxRequest = true; // active low
_txRequest = true; // always ready to transmit, makes the diags happy.
}
public bool RxRequest
{
get { return _rxRequest; }
}
public bool TxRequest
{
get { return _txRequest; }
}
public int[] ReadPorts
{
get { return _readPorts; }
}
public int[] WritePorts
{
get { return _writePorts; }
}
public byte ReadPort(int port)
{
byte value = 0;
switch(port)
{
case 0x88:
if (Log.Enabled) Log.Write(LogComponent.IOPPrinter, "Stub: Printer data port read.");
value = _txData; // just loopback data to make diags happy.
break;
case 0x89:
if (Log.Enabled) Log.Write(LogComponent.IOPPrinter, "Stub: Printer status port read. Returning DTR");
value = 0x00;
_rxRequest = true;
break;
}
return value;
}
public void WritePort(int port, byte data)
{
switch(port)
{
case 0x88:
if (Log.Enabled) Log.Write(LogComponent.IOPPrinter, "Stub: Printer data port write 0x{0:x2}.", data);
_txData = data;
_rxRequest = false; // "TTY request is active low."
break;
case 0x89:
if (Log.Enabled) Log.Write(LogComponent.IOPPrinter, "Stub: Printer control port write 0x{0:x2}.", data);
break;
}
}
private bool _rxRequest;
private bool _txRequest;
private byte _txData;
private readonly int[] _readPorts = new int[]
{
0x88, // data
0x89, // status
};
private readonly int[] _writePorts = new int[]
{
0x88, // data
0x89, // commands
};
}
}

2532
D/IOP/Source/SourceMap.txt Normal file

File diff suppressed because it is too large Load Diff

311
D/IOP/TODClock.cs Normal file
View File

@@ -0,0 +1,311 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Threading;
namespace D.IOP
{
public enum TODPowerUpSetMode
{
HostTimeY2K = 0,
HostTime,
SpecificDateAndTime,
SpecificDate,
NoChange,
}
public enum TODAccessMode
{
Read = 0x4,
Clear = 0x2,
Set = 0x1,
None = 0,
}
public enum TODClockType
{
Read = 0,
SetA,
SetB,
SetC,
SetD,
}
/// <summary>
/// Implements the Star's TOD clock and timing logic.
/// The Star does not use an off-the-shelf RTC chip, just a series of
/// 74LS393 dual 4-bit counters and 74LS165 shift registers
/// clocked off of a 1Hz clock -- these form a single 32-bit register
/// that counts seconds. It can be read and written by the IOP.
///
/// This uses a Timer to provide decent real-time clock interrupts
/// independent of the speed of the running emulation.
/// </summary>
public class TODClock
{
public TODClock()
{
_lock = new ReaderWriterLockSlim();
// Get the timer rolling, it will tick once a second forever.
// TODO: this is not particularly accurate, timekeeping-wise.
_timer = new Timer(TimerTick, null, 1000, 1000);
Reset();
}
public void Reset()
{
_lock.EnterWriteLock();
_interrupt = false;
_lock.ExitWriteLock();
_todReadBit = 0;
_mode = TODAccessMode.None;
_powerLoss = false;
PowerUpSetMode = Configuration.TODSetMode;
PowerUpSetTime = (PowerUpSetMode == TODPowerUpSetMode.SpecificDate) ?
Configuration.TODDate : Configuration.TODDateTime;
SetTODClockInternal();
}
/// <summary>
/// Resets the clock to the current value specified by the
/// system configuration.
/// </summary>
public void ResetTODClockTime()
{
SetTODClockInternal();
}
/// <summary>
/// How to set the TOD clock when the system is started.
/// </summary>
public TODPowerUpSetMode PowerUpSetMode;
/// <summary>
/// A specific time to set the TOD clock to
/// </summary>
public DateTime PowerUpSetTime;
/// <summary>
/// The Interrupt flag indicates that a (soft) TOD interrupt
/// has occurred -- this is raised every time the clock ticks.
/// </summary>
public bool Interrupt
{
get
{
bool value;
_lock.EnterReadLock();
value = _interrupt;
_lock.ExitReadLock();
return value;
}
}
public bool PowerLoss
{
get
{
return _powerLoss;
}
}
public void ClearInterrupt()
{
_lock.EnterWriteLock();
_interrupt = false;
_lock.ExitWriteLock();
}
public void SetMode(TODAccessMode mode)
{
_mode = mode;
switch(mode)
{
case TODAccessMode.Read:
_todReadBit = 0;
break;
case TODAccessMode.Set:
// TODO: anything need to be done here?
break;
case TODAccessMode.Clear:
_todValue = 0;
break;
}
}
public int ReadClockBit()
{
_lock.EnterReadLock();
// From BookKeepingTask.asm: "Bits from clock come in true, and most significant bit first."
int value = (_todValue & (0x80000000 >> (_todReadBit & 0x1f))) != 0 ? 0x40 : 0;
_lock.ExitReadLock();
return value;
}
public void ClockBit(TODClockType type)
{
switch(type)
{
case TODClockType.Read:
_todReadBit++;
break;
//
// A clock to A,B,C or D causes the value
// in the corresponding byte of the tod counter to be incremented.
// A is the least-significant byte, D is the most-significant.
//
case TODClockType.SetA:
_lock.EnterWriteLock();
_todValue = ((_todValue & 0xffffff00) | ((_todValue + 1) & 0xff));
_powerLoss = false;
_lock.ExitWriteLock();
break;
case TODClockType.SetB:
_lock.EnterWriteLock();
_todValue = ((_todValue & 0xffff00ff) | ((_todValue + 0x100) & 0xff00));
_powerLoss = false;
_lock.ExitWriteLock();
break;
case TODClockType.SetC:
_lock.EnterWriteLock();
_todValue = ((_todValue & 0xff00ffff) | ((_todValue + 0x10000) & 0xff0000));
_powerLoss = false;
_lock.ExitWriteLock();
break;
case TODClockType.SetD:
_lock.EnterWriteLock();
_todValue = ((_todValue & 0x00ffffff) | ((_todValue + 0x1000000) & 0xff000000));
_powerLoss = false;
_lock.ExitWriteLock();
break;
default:
throw new NotImplementedException("Clock bit not implemented.");
}
}
private void SetTODClockInternal()
{
_lock.EnterWriteLock();
switch (PowerUpSetMode)
{
case TODPowerUpSetMode.HostTimeY2K:
//
// Get current time, and move date back 28 years since most Star software isn't happy with Y2K.
// This keeps the calendar in sync at least.
//
_todValue = GetXeroxTime(DateTime.Now.ToLocalTime().AddYears(-28));
break;
case TODPowerUpSetMode.HostTime:
//
// Set TOD clock to the current wall-clock time.
//
_todValue = GetXeroxTime(DateTime.Now.ToLocalTime());
break;
case TODPowerUpSetMode.SpecificDateAndTime:
//
// Set TOD clock to the specified date and time.
//
_todValue = GetXeroxTime(PowerUpSetTime);
break;
case TODPowerUpSetMode.SpecificDate:
//
// Set TOD clock to the specified date, with the current time.
//
_todValue = GetXeroxTime(PowerUpSetTime.Add(DateTime.Now.TimeOfDay));
break;
case TODPowerUpSetMode.NoChange:
//
// Do nothing.
//
break;
}
_lock.ExitWriteLock();
}
private uint GetXeroxTime(DateTime time)
{
//
// The Star's epoch is 1/1/1901.
//
long dateTicks = time.Ticks;
long epochTicks = new DateTime(1901, 1, 1, 0, 0, 0).ToLocalTime().Ticks;
long adjustedTicks = dateTicks - epochTicks;
// Ticks are 100nS.
uint seconds = (uint)(adjustedTicks / 10000000);
return seconds;
}
private void TimerTick(object context)
{
//
// One real second has elapsed.
// We raise the interrupt flag and increment
// the clock value.
//
_lock.EnterWriteLock();
_interrupt = true;
_todValue++;
_lock.ExitWriteLock();
}
private bool _interrupt;
private bool _powerLoss;
private UInt32 _todValue;
private int _todReadBit;
private TODAccessMode _mode;
// Timer for real-time clocking and a lock to make things
// thread-safe. The lock is probably overkill but let's do this right.
private Timer _timer;
private ReaderWriterLockSlim _lock;
}
}

2085
D/IOP/i8085.cs Normal file

File diff suppressed because it is too large Load Diff

164
D/Logging/Log.cs Normal file
View File

@@ -0,0 +1,164 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define LOGGING_ENABLED
using System;
using System.IO;
namespace D.Logging
{
/// <summary>
/// Specifies a component to specify logging for
/// </summary>
[Flags, ]
public enum LogComponent
{
None = 0,
// IOP
IOP = 0x1,
IOPMemory = 0x2,
IOPIO = 0x4,
IOPFloppy = 0x8,
IOPMisc = 0x10,
IOPDMA = 0x20,
IOPKeyboard = 0x40,
IOPPrinter = 0x80,
// CP
CPControl = 0x100,
CPExecution = 0x200,
CPMicrocodeLoad = 0x400,
CPTPCLoad = 0x800,
CPTask = 0x1000,
CPMap = 0x2000,
CPError = 0x4000,
CPIB = 0x8000,
CPStack = 0x10000,
CPInst = 0x20000,
// Memory
MemoryControl = 0x100000,
MemoryAccess = 0x200000,
// Display
DisplayControl = 0x400000,
// Shugart controller
ShugartControl = 0x800000,
// Ethernet
EthernetControl = 0x1000000,
HostEthernet = 0x2000000,
// Configuration
Configuration = 0x4000000,
All = 0x7fffffff
}
/// <summary>
/// Specifies the type (or severity) of a given log message
/// </summary>
[Flags]
public enum LogType
{
None = 0,
Normal = 0x1,
Warning = 0x2,
Error = 0x4,
Verbose = 0x8,
All = 0x7fffffff
}
/// <summary>
/// Provides basic functionality for logging messages of all types.
/// </summary>
public static class Log
{
static Log()
{
Enabled = false;
_components = LogComponent.None;
_type = LogType.None;
_logIndex = 0;
}
public static LogComponent LogComponents
{
get { return _components; }
set { _components = value; }
}
public static readonly bool Enabled;
#if LOGGING_ENABLED
/// <summary>
/// Logs a message without specifying type/severity for terseness;
/// will not log if Type has been set to None.
/// </summary>
/// <param name="level"></param>
/// <param name="message"></param>
/// <param name="args"></param>
public static void Write(LogComponent component, string message, params object[] args)
{
Write(LogType.Normal, component, message, args);
}
public static void Write(LogType type, LogComponent component, string message, params object[] args)
{
if ((_type & type) != 0 &&
(_components & component) != 0)
{
//
// My log has something to tell you...
// TODO: color based on type, etc.
Console.WriteLine(_logIndex.ToString() + ": " + component.ToString() + ": " + message, args);
_logIndex++;
}
}
#else
public static void Write(LogComponent component, string message, params object[] args)
{
}
public static void Write(LogType type, LogComponent component, string message, params object[] args)
{
}
#endif
private static LogComponent _components;
private static LogType _type;
private static long _logIndex;
}
}

121
D/Memory/Memory.cs Normal file
View File

@@ -0,0 +1,121 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using D.Logging;
using System;
namespace D.Memory
{
/// <summary>
/// Encapsulates the physical memory, with enough of an ECC implementation
/// to allow diagnostics to pass.
/// </summary>
public class Memory
{
public Memory()
{
Reset();
}
public void Reset()
{
if (_memory == null || _memory.Length != Configuration.MemorySize * 1024)
{
_memory = new ushort[Configuration.MemorySize * 1024];
_ecc = new byte[_memory.Length];
}
else if (_memory != null)
{
Array.Clear(_memory, 0, _memory.Length);
Array.Clear(_ecc, 0, _ecc.Length);
}
}
public int Size
{
get { return _memory.Length; }
}
public ushort ReadWord(int address, out bool valid)
{
if (address < _memory.Length)
{
ushort memWord = _memory[address];
valid = _ecc[address] == CalculateECCSyndrome(memWord);
return memWord;
}
else
{
valid = false;
return 0;
}
}
public void WriteWord(int address, ushort value)
{
if (address < _memory.Length)
{
WriteECC(address, value);
_memory[address] = value;
}
else
{
if (Log.Enabled) Log.Write(LogComponent.MemoryAccess, "Write to nonexistent memory address 0x{0:x}", address);
}
}
public void SetCheckBits(int invertBits)
{
_mctlInvert = invertBits;
}
private byte CalculateECCSyndrome(ushort word)
{
//
// Not actually calculating an ECC syndrome here, as at the moment there are no
// plans to emulate faulty memory, or the hardware to correct it.
//
return 0;
}
private void WriteECC(int address, ushort value)
{
//
// Write the calculated ECC syndrome value with the bits specified by MCtl<-
// inverted.
//
_ecc[address] = (byte)(CalculateECCSyndrome(value) ^ _mctlInvert);
}
private ushort[] _memory;
private byte[] _ecc;
private int _mctlInvert;
}
}

View File

@@ -0,0 +1,156 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using D.CP;
using D.Logging;
namespace D.Memory
{
public class MemoryController
{
public MemoryController()
{
_mem = new Memory();
}
public void Reset()
{
_mStatus = 0;
_mar = 0;
_md = 0;
_mdValid = true;
_mem.Reset();
}
public ushort MStatus
{
get { return _mStatus; }
}
public int MAR
{
get { return _mar; }
}
// Exposing memory for debugging purposes.
public Memory DebugMemory
{
get { return _mem; }
}
/// <summary>
/// Used for debugging.
/// </summary>
public ushort MD
{
get { return _md; }
}
public void SetMCtl(ushort value)
{
//
// See HWRef, figure 18 (pg 55).
// This is used to test syndrome bits or clear error logs.
//
if (Log.Enabled) Log.Write(LogType.Verbose, LogComponent.MemoryControl, "MCtl<-0x{0:x}", value);
_mem.SetCheckBits((value & 0xff) >> 2);
if ((value & 0x800) != 0)
{
// Clear error log for the task specified by bits [5..7].
int task = (value & 0x700) >> 8;
_mStatus &= (ushort)(~(0x80 >> task));
}
}
public void LoadMAR(int address)
{
_mar = address;
//
// Pre-load the memory requested, so we can return
// the original data in cases where MDR is written and MD is read in the same click.
//
_md = _mem.ReadWord(_mar, out _mdValid);
if (Log.Enabled) Log.Write(LogType.Verbose, LogComponent.MemoryAccess, "MAR<-0x{0:x5}", address);
}
public void LoadMDR(ushort value)
{
//
// Write the word to memory
//
_mem.WriteWord(_mar, value);
if (Log.Enabled)
{
Log.Write(LogType.Verbose, LogComponent.MemoryAccess, "MDR<-0x{0:x4}", value);
if (_mar >= 0x10000 && _mar < 0x20000)
{
Log.Write(LogType.Verbose, LogComponent.CPMap, "MAP 0x{0:x5} = {1:x4}", _mar, value);
}
}
}
public ushort ReadMD(TaskType task, out bool valid)
{
//
// Return the data read in LoadMAR.
//
if (Log.Enabled) Log.Write(LogType.Verbose, LogComponent.MemoryAccess, "<-MD (0x{0:x4}) valid {1}", _md, _mdValid);
valid = _mdValid;
if (!valid)
{
if (Log.Enabled) Log.Write(LogComponent.MemoryAccess, "Read from nonexistent memory address 0x{0:x}", _mar);
_mStatus |= (ushort)((0x80 >> (int)task)); // Set error bit for task
_mStatus |= 0x100; // double-bit error.
// TODO: set syndrome bits
}
else
{
// Clear single/double-bit errors (bits 6..7)
_mStatus &= 0xfcff;
}
return _md;
}
private int _mar;
private ushort _md;
private bool _mdValid;
private ushort _mStatus;
private Memory _mem;
}
}

View File

@@ -0,0 +1,15 @@
Need to build a symbol table of sorts for 8085 code to allow mapping of ROM addresses
to source lines & symbol names in source code.
Current thoughts:
Text file consisting of lines in the format:
$<ROM Address>-[length]: <source file name>, <line number>
to describe instruction mapping or label/constant value definition -> source line.
Duplicate <ROM Address> fields may be present to describe symbols pointing to same address
Start with ROM addresses that correspond directly to labels, see how interpolation (based on
assembly of source, perhaps?) can deal with the intermediary addresses.

1
D/Notes/Ethernet.txt Normal file

File diff suppressed because one or more lines are too long

214
D/Notes/IOP.txt Normal file
View File

@@ -0,0 +1,214 @@
IOP notes. These are gleaned from various bits of documentation and source code.
There is no official IOP documentation available.
Hardware:
- CPU: 8085
- FDC: WD FD1797
- i8253 programmable interval timer
- i8251 UART
- i8257 dma controller
- Z80-SIO for RS232C/RS366 (?? not present on PCB...)
Memory Map (see SysDefs.asm):
$0000-$1FFF : PROM (8K)
$2000-$5FFF : RAM (16K)
$8000-$800F : Host Addr PROM
$8010-$FFFF : Memory Mapped-I/O
Map of PROM ICs to ROM Locations:
3.1 :
U129 - 537P03029 - $0000
U130 - 537P03030 - $0800
U131 - 537P03700 - $1000
U132 - 537P03032 - $1800
I/O addresses:
$80-$83 - Alto PPI
$84 - FDC Command (write)
$84 - FDC Status (read)
$85 - FDC Track register (write)
$86 - FDC Sector register (write)
$87 - FDC Data register (write, read?)
$88 - Printer Data (read/write)
$89 - Printer Commands (write)
$89 - Printer Status (read)
$8C - Timer Counter 0 (read/write)
$8D - Timer Counter 1 (read/write)
$8E - Timer Counter 2 (read/write)
$8F - Timer Mode (commands) (write)
$90 - LSEP Uart Data
$91 - LSEP Uart Command
$92 - LSEP Uart Status
$94 - LSEP Timer Counter 0
$95 - Counter for SIO
$96 - Counter for Time Counter
$97 - LSEP Timer Mode / Baud Rate Gen. Control Register (?)
$98 - SIO Channel A Data Register
$99 - " B "
$9A - SIO Channel A Control Register
$9B - " B "
$9C - RS366 register
$A0 - $A8 - DMA Controller
$B0 - $BF - Host PROM data (read)
$E0 - Keyset (read?)
$E8 - FDC External State
$E9 - KB, MP, TOD clocks (write)
$E9 - Interrupt request bits (read)
$EA - clear TOD interrupt (write)
$EA - Keyboard data latch (read)
$EB - CP data in (read)
$EB - CP data out (write)
$EC - CP (central processor) control register (write)
$EC - CP status (read)
$ED - Mouse X counter (read)
$ED - Clear mouse X, Y counters (write)
$EE - Mouse Y counter (read)
$EE - Clear CP DMA Complete (write)
$EF - Misc I/O devices (keyboard, clocks, etc.) input
$EF - Misc control (write)
$F8-$FF - Control Store read/write:
; Format of TPCHigh (write): TPCAddr[0:2],,TPCData[0:4]'
; Format of TPCLow (write): don't care,,TPCData[5:11]'
$FE - TPC High (inverted)
$FF - TPC Low (inverted)
$F8-$FD - 48 bits of control store, MSB -> LSB, bits inverted.
Memory-Mapped IO:
-----------------
$80ec - CPControl - CP control register : IOPWait',,SwTAddr',,IOPAttn,,CPDmaMode,,CPDmaIn (write)
$80ec - CPStatus (read)
$80eb - CP data in (read)
$80eb - CP data out (write)
CP <-> IOP comms
----------------
In WriteCPbyte (BootSubs.asm), addr $71b:
WaitCPOutAck expects CPStatus to have the interrupt mask bit set after data is written, loops until this is so.
CP interrupts from IOP:
from Kernel.mc:
"The Kernel can be entered by one of two ways: Either via a breakpoint or the IOP asynchronously interrupting the CP
via the IOPWait line. If entry is via a breakpoint, the kernel can be entered in any cycle (and inter-cycle state
information must be preserved). IOPWait caused entry always occurs between clicks (so all state information is already
saved by the CP and there is no memory state across clicks which can be lost, saved or restored).
Upon entering the kernel, it interrupts the IOP and waits for a command byte. There are 3 possible commands that the
IOP can specify: Refresh, ExitKernel, and ExecuteBufffer. Refresh is used by the IOP when it is writing the CS,
ExitKernel causes the CP to leave the kernel task, and ExecuteBuffer causes the instructions which the IOP wrote in
the buffer area to be executed.
When the kernel is entered via a breakpoint, an R register (rK) must be used to hold memory data or a breakpoint ID
(or an RH reg for ID), and a Link register to hold condition bits (or a breakpoint ID).
When being entered via an IOPWait, no R register state need be lost (currently rK is lost) (i.e. rK and RHrK can first
be saved away, then later restored. There should also be a second kind of ExitKernel command which doesn't write Mem[0]).
Currently, the kernel is written assuming it can always use rK, so this register is lost in the IOPWait caused entry
also. (rK is used in the wait loop and in the overlay code which Burdock uses to read and write some registers.) "
Status / Control registers:
IOP Ports:
CPControl: IOP -> CP (IOP write) ($EC)
- IOP uses this to control the CP; IOPAttn is set if the IOP needs attention from the CP?
- Bits are: (from IOP schematic, p 15):
- CPDmaIn - 0x8
- CPDmaMode - 0x10
- IOPAttn - 0x20
- SwTAddr' - 0x40
- IOPWait' - 0x80
CPStatus: CP -> IOP (IOP read) ($EC)
- CP reports its status to the IOP, also includes IOP status
- Bits are: (from IOP schematic, p 17):
- CPDmaComplete' - 0x1
- CPOutIntReq' - 0x2
- CPInIntReq' - 0x4
- CPDmaIn' - 0x8 - From CPControl
- CPDmaMode' - 0x10 - "
- IOPAttn' - 0x20 - "
- EmuWake - 0x40 - From IOPCtl<-
- CPAttn - 0x80 - "
CP Ports:
IOPCtl<-: CP-> IOP (CP write)
- CP sets up communication between the IOP and the CP:
- Bits are:
- WakeMode.1 - 0x1
- WakeMode.0 - 0x2
- CPAttn - 0x4
- EmuWake - 0x8 (is this actually used?)
Per uCode and Schematic (IOP, p 15), the WakeMode bits are:
- 00 = Disabled (no wakeups)
- 01 = Input (wakeup when Input from IOP is available)
- 10 = Output (wakeup when IOP is ready for data from CP)
- 11 = Always wake up
<-IOPStatus: IOP -> CP (CP read)
- CP gets the IOP's status
- Bits are (from IOP schematic, p 15):
- IOPReq - 0x1 - set when data available from IOP?
- WakeMode.1' - 0x2
- WakeMode.0' - 0x4
- CPAttn' - 0x8
- EmuWake' = 0x10
- IOPAttn - 0x20
- Bits 0x40, 0x80 are always set low
Communication register:
- Appears to be a data register (8 bits + flow control) between the IOP and the CP; IOP gets status (interrupt?) when CP has written data to be read,
CP can get woken up when the IOP has written data (see below).
CP Wakeups for IOP:
- If IOPInMode is set, the CP's IOP task will wake up if input is available from the IOP (in the data register)
- If IOPOutMode is set, wakeups will occur if the output data register is empty (i.e. the IOP is ready to receive a word.)
- If IOPAWmode is set (In and Out bits set) the IOP will always wake up regardless
Interrupts:
----------
Three interrupt lines on the CPU are used as following:
RST5.5 - CP Interrupt caused by CPAttn going high (also Burdock, the Alto debugging iface)
RST6.5 - RS232 interrupt
RST7.5 - Floppy Interrupt

76
D/Program.cs Normal file
View File

@@ -0,0 +1,76 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Windows.Forms;
using D.UI;
namespace D
{
public class Program
{
[STAThread]
static void Main(string[] args)
{
PrintHerald();
// Cons up a system to run stuff on.
DSystem system = new DSystem();
system.Reset();
//
// Start the UI, this will not return from ShowDialog
// until the window is closed.
//
DWindow mainWindow = new DWindow(system);
system.AttachDisplay(mainWindow);
DialogResult res = mainWindow.ShowDialog();
//
// Main window is now closed: shut down the system.
// Ensure the system is stopped.
//
system.StopExecution();
//
// Commit disks on normal exit
//
system.Shutdown(res == DialogResult.OK);
Console.WriteLine("Goodbye...");
}
private static void PrintHerald()
{
Console.WriteLine("Darkstar v{0}", typeof(Program).Assembly.GetName().Version);
Console.WriteLine("(c) 2017, 2018 Living Computers: Museum+Labs");
Console.WriteLine("Bug reports to joshd@livingcomputers.org");
Console.WriteLine();
}
}
}

View File

@@ -0,0 +1,65 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Darkstar")]
[assembly: AssemblyDescription("A Xerox Star/1108 Emulator")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Living Computers: Museum+Labs")]
[assembly: AssemblyProduct("Darkstar")]
[assembly: AssemblyCopyright("Copyright © 2017, 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("0590465e-1d91-4591-946e-ee3f7d82834b")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

158
D/Properties/Settings.Designer.cs generated Normal file
View File

@@ -0,0 +1,158 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace D.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")]
public string HardDriveImage {
get {
return ((string)(this["HardDriveImage"]));
}
set {
this["HardDriveImage"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")]
public string FloppyDriveImage {
get {
return ((string)(this["FloppyDriveImage"]));
}
set {
this["FloppyDriveImage"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
public bool ThrottleSpeed {
get {
return ((bool)(this["ThrottleSpeed"]));
}
set {
this["ThrottleSpeed"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("1")]
public uint DisplayScale {
get {
return ((uint)(this["DisplayScale"]));
}
set {
this["DisplayScale"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("2852201285")]
public ulong HostAddress {
get {
return ((ulong)(this["HostAddress"]));
}
set {
this["HostAddress"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")]
public string HostPacketInterfaceName {
get {
return ((string)(this["HostPacketInterfaceName"]));
}
set {
this["HostPacketInterfaceName"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("1024")]
public uint MemorySize {
get {
return ((uint)(this["MemorySize"]));
}
set {
this["MemorySize"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
public bool SlowPhosphor {
get {
return ((bool)(this["SlowPhosphor"]));
}
set {
this["SlowPhosphor"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("1979-12-10")]
public global::System.DateTime TODDateTime {
get {
return ((global::System.DateTime)(this["TODDateTime"]));
}
set {
this["TODDateTime"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
public int TODSetMode {
get {
return ((int)(this["TODSetMode"]));
}
set {
this["TODSetMode"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("1955-11-05")]
public global::System.DateTime TODDate {
get {
return ((global::System.DateTime)(this["TODDate"]));
}
set {
this["TODDate"] = value;
}
}
}
}

View File

@@ -0,0 +1,39 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="D.Properties" GeneratedClassName="Settings">
<Profiles />
<Settings>
<Setting Name="HardDriveImage" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="FloppyDriveImage" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="ThrottleSpeed" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="DisplayScale" Type="System.UInt32" Scope="User">
<Value Profile="(Default)">1</Value>
</Setting>
<Setting Name="HostAddress" Type="System.UInt64" Scope="User">
<Value Profile="(Default)">2852201285</Value>
</Setting>
<Setting Name="HostPacketInterfaceName" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="MemorySize" Type="System.UInt32" Scope="User">
<Value Profile="(Default)">1024</Value>
</Setting>
<Setting Name="SlowPhosphor" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="TODDateTime" Type="System.DateTime" Scope="User">
<Value Profile="(Default)">1979-12-10</Value>
</Setting>
<Setting Name="TODSetMode" Type="System.Int32" Scope="User">
<Value Profile="(Default)">0</Value>
</Setting>
<Setting Name="TODDate" Type="System.DateTime" Scope="User">
<Value Profile="(Default)">1955-11-05</Value>
</Setting>
</Settings>
</SettingsFile>

BIN
D/SDL2.dll Normal file

Binary file not shown.

252
D/Scheduler.cs Normal file
View File

@@ -0,0 +1,252 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System.Collections.Generic;
namespace D
{
/// <summary>
/// The SchedulerEventCallback describes a delegate that is invoked whenever a scheduled event has
/// reached its due-date and is fired.
/// </summary>
/// <param name="skew">The delta between the requested exec time and the actual exec time (in nsec)</param>
/// <param name="context">An object containing context useful to the scheduler of the event</param>
public delegate void SchedulerEventCallback(ulong skewNsec, object context);
/// <summary>
/// An Event encapsulates a callback and associated context that is scheduled for a future timestamp.
/// </summary>
public class Event
{
public Event(ulong timestampNsec, object context, SchedulerEventCallback callback)
{
_timestampNsec = timestampNsec;
_context = context;
_callback = callback;
}
/// <summary>
/// The absolute time (in nsec) to raise the event.
/// </summary>
public ulong TimestampNsec
{
get { return _timestampNsec; }
set { _timestampNsec = value; }
}
/// <summary>
/// An object containing context to be passed to the
/// event callback.
/// </summary>
public object Context
{
get { return _context; }
set { _context = value; }
}
/// <summary>
/// A delegate to be executed when the callback fires.
/// </summary>
public SchedulerEventCallback EventCallback
{
get { return _callback; }
}
private ulong _timestampNsec;
private object _context;
private SchedulerEventCallback _callback;
}
/// <summary>
/// The Scheduler class provides infrastructure for scheduling time-based hardware events
/// (for example, sector marks, or video task wakeups).
///
/// Note that the Scheduler is not thread-safe and must only be used from the emulation thread,
/// or else things will break. This is not optimal -- having a thread-safe scheduler would make
/// it easier/cleaner to deal with asynchronous things like ethernet packets and scripting events
/// but doing so incurs about a 10% performance penalty so it's been avoided.
/// </summary>
public class Scheduler
{
public Scheduler()
{
Reset();
}
public ulong CurrentTimeNsec
{
get { return _currentTimeNsec; }
}
public void Reset()
{
_schedule = new SchedulerQueue();
_currentTimeNsec = 0;
}
public void Clock()
{
//
// Move one system clock forward in time.
//
_currentTimeNsec += _timeStepNsec;
//
// See if we have any events waiting to fire at this timestep.
//
while (_schedule.Top != null && _currentTimeNsec >= _schedule.Top.TimestampNsec)
{
// Pop the top event and fire the callback.
Event e = _schedule.Pop();
e.EventCallback(_currentTimeNsec - e.TimestampNsec, e.Context);
}
}
/// <summary>
/// Add a new event to the schedule.
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
public Event Schedule(ulong timestampNsec, object context, SchedulerEventCallback callback)
{
Event e = new Event(timestampNsec + _currentTimeNsec, context, callback);
_schedule.Push(e);
return e;
}
public Event Schedule(ulong timestampNsec, SchedulerEventCallback callback)
{
Event e = new Event(timestampNsec + _currentTimeNsec, null, callback);
_schedule.Push(e);
return e;
}
public void Cancel(Event e)
{
if (e != null)
{
_schedule.Remove(e);
}
}
private ulong _currentTimeNsec;
private SchedulerQueue _schedule;
// 137nsec is approximately one central processor system clock cycle and is the time-base for
// the scheduler.
private const ulong _timeStepNsec = 137;
}
/// <summary>
/// Provides an "ordered" queue based on timestamp -- the top of the queue is always the
/// next event to be fired; a "push" places a new event in order on the current queue.
/// </summary>
public class SchedulerQueue
{
public SchedulerQueue()
{
_queue = new LinkedList<Event>();
}
public Event Top
{
get
{
return _top;
}
}
public bool Contains(Event e)
{
return _queue.Contains(e);
}
public void Push(Event e)
{
// Degenerate case: list is empty or new entry is earlier than the head of the list.
if (_queue.Count == 0 || _top.TimestampNsec >= e.TimestampNsec)
{
_queue.AddFirst(e);
_top = e;
return;
}
//
// Do a linear search to find the place to put this in.
// Since we maintain a sorted list with every insertion we only need to find the first entry
// that the new entry is earlier (or equal) to.
// This will likely be adequate as the queue should never get incredibly deep; a binary
// search may be more performant if this is not the case.
//
LinkedListNode<Event> current = _queue.First;
while (current != null)
{
if (current.Value.TimestampNsec >= e.TimestampNsec)
{
_queue.AddBefore(current, e);
return;
}
current = current.Next;
}
// Add at end
_queue.AddLast(e);
}
public Event Pop()
{
Event e = _top;
_queue.RemoveFirst();
_top = _queue.First != null ? _queue.First.Value : null;
return e;
}
public void Remove(Event e)
{
if (_queue.Contains(e))
{
_queue.Remove(e);
_top = _queue.First.Value;
}
}
private LinkedList<Event> _queue;
/// <summary>
/// The Top of the queue (null if queue is empty).
/// </summary>
private Event _top;
}
}

410
D/System.cs Normal file
View File

@@ -0,0 +1,410 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using D.CP;
using D.Display;
using D.Ethernet;
using D.IO;
using D.IOP;
using D.Memory;
using D.UI;
using System;
using System.Threading;
namespace D
{
public delegate bool StepCallbackDelegate();
public delegate void ErrorCallbackDelegate(Exception e);
/// <summary>
/// Defines the context for the current execution
/// (debug hooks, etc)
/// </summary>
public class SystemExecutionContext
{
public SystemExecutionContext(StepCallbackDelegate step8085, StepCallbackDelegate stepCP, StepCallbackDelegate stepMesa, ErrorCallbackDelegate error)
{
StepCallback8085 = step8085;
StepCallbackCP = stepCP;
StepCallbackMesa = stepMesa;
ErrorCallback = error;
}
public readonly StepCallbackDelegate StepCallback8085;
public readonly StepCallbackDelegate StepCallbackCP;
public readonly StepCallbackDelegate StepCallbackMesa;
public readonly ErrorCallbackDelegate ErrorCallback;
}
/// <summary>
/// Encompasses the Star's hardware and provides functionality to run and debug the system.
/// </summary>
public class DSystem
{
public DSystem()
{
_scheduler = new Scheduler();
_cp = new CentralProcessor(this);
_iop = new IOProcessor(this);
_memoryController = new MemoryController();
_displayController = new DisplayController(this);
_hardDrive = new SA1000Drive(this);
_shugartController = new ShugartController(this, _hardDrive);
_ethernetController = new EthernetController(this);
try
{
_frameTimer = new FrameTimer(38.7);
}
catch
{
// Not supported on this platform.
_frameTimer = null;
}
}
public void Reset()
{
bool wasExecuting = IsExecuting;
// Save context and stop executing if the system is currently running.
SystemExecutionContext context = null;
if (wasExecuting)
{
context = _currentExecutionContext;
StopExecution();
}
// Now do the actual reset.
_cp.Reset();
_iop.Reset();
_displayController.Reset();
_ethernetController.Reset();
_hardDrive.Reset();
_shugartController.Reset();
// _scheduler.Reset();
_cpCycles = 0;
_elapsedCycles = 0;
// Restart execution if we were running before the reset.
if (wasExecuting)
{
StartExecution(context);
}
}
public void Shutdown(bool commitDisks)
{
_hardDrive.Save();
}
public bool IsExecuting
{
get { return _executionThread != null && _executionThread.IsAlive; }
}
public SystemExecutionContext ExecutionContext
{
get { return _currentExecutionContext; }
}
public Scheduler Scheduler
{
get { return _scheduler; }
}
public IOProcessor IOP
{
get { return _iop; }
}
public CentralProcessor CP
{
get { return _cp; }
}
public MemoryController MemoryController
{
get { return _memoryController; }
}
public DisplayController DisplayController
{
get { return _displayController; }
}
public ShugartController ShugartController
{
get { return _shugartController; }
}
public SA1000Drive HardDrive
{
get { return _hardDrive; }
}
public EthernetController EthernetController
{
get { return _ethernetController; }
}
public DWindow Display
{
get { return _display; }
}
/// <summary>
/// Allows UI to be alerted when execution state changes
/// </summary>
public delegate void ExecutionStateChangedDelegate();
public ExecutionStateChangedDelegate ExecutionStateChanged;
public void AttachDisplay(DWindow display)
{
_display = display;
}
public void StartExecution(SystemExecutionContext context)
{
StopExecution();
if (_executionThread == null || !_executionThread.IsAlive)
{
if (context.StepCallback8085 != null &&
context.StepCallbackCP != null &&
context.StepCallbackMesa != null)
{
_executionThread = new Thread(new ParameterizedThreadStart(DebugExecutionWorker));
}
else
{
_executionThread = new Thread(new ParameterizedThreadStart(ExecutionWorker));
}
_executionThread.Start(context);
ExecutionStateChanged();
_currentExecutionContext = context;
}
}
public void StopExecution()
{
if (_executionThread != null && _executionThread.IsAlive)
{
_abortExecution = true;
_executionThread.Join();
_executionThread = null;
_currentExecutionContext = null;
ExecutionStateChanged();
}
}
private void DebugExecutionWorker(object obj)
{
SystemExecutionContext context = (SystemExecutionContext)obj;
_abortExecution = false;
bool iopAbort = false;
while (!_abortExecution)
{
try
{
//
// We clock the IOP first and let it run an instruction.
// This returns the number of clock cycles consumed --
// on the 8085's 3Mhz timebase.
//
// We then execute the corresponding number of CP cycles
// (based on a 137ns clock period, or about 7.3Mhz) that would
// occur during that period.
//
if (_cpCycles == 0)
{
if (iopAbort)
{
//
// Out of cycles after an IOP abort, now we actually abort.
//
_abortExecution = true;
}
else
{
int i8085Cycles = _iop.Execute();
// This is inexact and that's probably good enough on average.
_cpCycles = (int)_cpCyclesPer8085Cycle * i8085Cycles;
if (context.StepCallback8085())
{
//
// Set our local iopAbort flag, we still want to continue
// to execute the CP and scheduler for as many microcycles as correspond to the
// above 8085 instruction's execution time to keep things in sync.
//
iopAbort = true;
}
}
}
_cp.ExecuteInstruction(1);
_cpCycles--;
_elapsedCycles++;
if (context.StepCallbackCP())
{
//
// Break on microinstruction step
//
_abortExecution = true;
}
if (_cp.IBDispatch &&
context.StepCallbackMesa())
{
//
// Break on macroinstruction step
//
_abortExecution = true;
}
}
catch(Exception e)
{
context.ErrorCallback(e);
_abortExecution = true;
}
}
_cpCycles = 0;
ExecutionStateChanged();
}
private void ExecutionWorker(object obj)
{
SystemExecutionContext context = (SystemExecutionContext)obj;
_abortExecution = false;
while (!_abortExecution)
{
try
{
//
// We clock the IOP first and let it run an instruction.
// This returns the number of clock cycles consumed --
// on the 8085's 3Mhz timebase.
//
// We then execute the corresponding number of CP cycles
// (based on a 137ns clock period, or about 7.3Mhz) that would
// occur during that period.
//
int i8085Cycles = _iop.Execute();
// This is inexact and that's probably good enough on average.
_cpCycles = (int)_cpCyclesPer8085Cycle * i8085Cycles;
_cp.ExecuteInstruction(_cpCycles);
if (Configuration.ThrottleSpeed)
{
_elapsedCycles += _cpCycles;
if (_elapsedCycles > _cpCyclesPerField)
{
_elapsedCycles -= _cpCyclesPerField;
if (_frameTimer != null)
{
_frameTimer.WaitForFrame();
}
}
}
}
catch (Exception e)
{
context.ErrorCallback(e);
_abortExecution = true;
}
}
ExecutionStateChanged();
}
//
// Devices belonging to this system
//
private IOProcessor _iop;
private CentralProcessor _cp;
private MemoryController _memoryController;
private DisplayController _displayController;
private ShugartController _shugartController;
private EthernetController _ethernetController;
private SA1000Drive _hardDrive;
//
// Display for rendering
//
private DWindow _display;
//
// System scheduler
private Scheduler _scheduler;
//
// Execution state
//
private Thread _executionThread;
private bool _abortExecution;
private int _cpCycles;
private double _elapsedCycles;
private SystemExecutionContext _currentExecutionContext;
//
// Constants
//
// Ratio of CP cycles per 8085 cycle (appx. 7.3Mhz to 3.0Mhz)
private const double _cpCyclesPer8085Cycle = 2.43 * 2.0;
// Cycles per display field
private const double _cpCyclesPerField = 7299270.1 / 38.7;
private FrameTimer _frameTimer;
}
}

147
D/UI/AboutBox.Designer.cs generated Normal file
View File

@@ -0,0 +1,147 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace D.UI
{
partial class AboutBox
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.DarkstarVersion = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.label4 = new System.Windows.Forms.Label();
this.emailLink = new System.Windows.Forms.LinkLabel();
this.SuspendLayout();
//
// DarkstarVersion
//
this.DarkstarVersion.AutoSize = true;
this.DarkstarVersion.Location = new System.Drawing.Point(128, 9);
this.DarkstarVersion.Name = "DarkstarVersion";
this.DarkstarVersion.Size = new System.Drawing.Size(144, 13);
this.DarkstarVersion.TabIndex = 0;
this.DarkstarVersion.Text = "Darkstar Version Placeholder";
this.DarkstarVersion.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(96, 31);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(139, 13);
this.label2.TabIndex = 1;
this.label2.Text = "A Xerox Star/1108 Emulator";
this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(56, 54);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(235, 13);
this.label3.TabIndex = 2;
this.label3.Text = "(c) 2017, 2018 Living Computers: Museum+Labs";
this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// label4
//
this.label4.Location = new System.Drawing.Point(67, 79);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(224, 18);
this.label4.TabIndex = 5;
this.label4.Text = "Bug reports, comments and miscellanea to";
//
// emailLink
//
this.emailLink.AutoSize = true;
this.emailLink.Location = new System.Drawing.Point(101, 97);
this.emailLink.Name = "emailLink";
this.emailLink.Size = new System.Drawing.Size(134, 13);
this.emailLink.TabIndex = 6;
this.emailLink.TabStop = true;
this.emailLink.Text = "joshd@livingcomputers.org";
this.emailLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.emailLink_LinkClicked);
//
// AboutBox
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(350, 126);
this.Controls.Add(this.emailLink);
this.Controls.Add(this.label4);
this.Controls.Add(this.label3);
this.Controls.Add(this.label2);
this.Controls.Add(this.DarkstarVersion);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.Margin = new System.Windows.Forms.Padding(2);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "AboutBox";
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "About Darkstar";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label DarkstarVersion;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.LinkLabel emailLink;
}
}

48
D/UI/AboutBox.cs Normal file
View File

@@ -0,0 +1,48 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System.Reflection;
using System.Windows.Forms;
namespace D.UI
{
public partial class AboutBox : Form
{
public AboutBox()
{
InitializeComponent();
DarkstarVersion.Text = string.Format("Darkstar v{0}", typeof(Program).Assembly.GetName().Version);
}
private void emailLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
System.Diagnostics.Process.Start("mailto:joshd@livingcomputers.org");
}
}
}

120
D/UI/AboutBox.resx Normal file
View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

427
D/UI/ConfigurationDialog.Designer.cs generated Normal file
View File

@@ -0,0 +1,427 @@
/*
BSD 2-Clause License
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace D.UI
{
partial class ConfigurationDialog
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.TabControl = new System.Windows.Forms.TabControl();
this.SystemPage = new System.Windows.Forms.TabPage();
this.ThrottleSpeedCheckBox = new System.Windows.Forms.CheckBox();
this.HostIDTextBox = new System.Windows.Forms.TextBox();
this.label2 = new System.Windows.Forms.Label();
this.MemorySizeComboBox = new System.Windows.Forms.ComboBox();
this.label1 = new System.Windows.Forms.Label();
this.EthernetTab = new System.Windows.Forms.TabPage();
this.EthernetInterfaceListBox = new System.Windows.Forms.ListBox();
this.label5 = new System.Windows.Forms.Label();
this.DisplayTab = new System.Windows.Forms.TabPage();
this.DisplayScaleComboBox = new System.Windows.Forms.ComboBox();
this.label3 = new System.Windows.Forms.Label();
this.SlowPhosphorCheckBox = new System.Windows.Forms.CheckBox();
this.TimeTabPage = new System.Windows.Forms.TabPage();
this.TODDateTimePicker = new System.Windows.Forms.DateTimePicker();
this.SpecifiedTimeDateRadioButton = new System.Windows.Forms.RadioButton();
this.CurrentTimeDateY2KRadioButton = new System.Windows.Forms.RadioButton();
this.CurrentTimeDateRadioButton = new System.Windows.Forms.RadioButton();
this.label4 = new System.Windows.Forms.Label();
this.OKButton = new System.Windows.Forms.Button();
this.Cancel_Button = new System.Windows.Forms.Button();
this.NoTimeDateChangeRadioButton = new System.Windows.Forms.RadioButton();
this.SpecifiedDateRadioButton = new System.Windows.Forms.RadioButton();
this.TODDatePicker = new System.Windows.Forms.DateTimePicker();
this.TabControl.SuspendLayout();
this.SystemPage.SuspendLayout();
this.EthernetTab.SuspendLayout();
this.DisplayTab.SuspendLayout();
this.TimeTabPage.SuspendLayout();
this.SuspendLayout();
//
// TabControl
//
this.TabControl.Controls.Add(this.SystemPage);
this.TabControl.Controls.Add(this.EthernetTab);
this.TabControl.Controls.Add(this.DisplayTab);
this.TabControl.Controls.Add(this.TimeTabPage);
this.TabControl.Location = new System.Drawing.Point(7, 8);
this.TabControl.Name = "TabControl";
this.TabControl.SelectedIndex = 0;
this.TabControl.Size = new System.Drawing.Size(356, 195);
this.TabControl.TabIndex = 0;
//
// SystemPage
//
this.SystemPage.Controls.Add(this.ThrottleSpeedCheckBox);
this.SystemPage.Controls.Add(this.HostIDTextBox);
this.SystemPage.Controls.Add(this.label2);
this.SystemPage.Controls.Add(this.MemorySizeComboBox);
this.SystemPage.Controls.Add(this.label1);
this.SystemPage.Location = new System.Drawing.Point(4, 22);
this.SystemPage.Name = "SystemPage";
this.SystemPage.Padding = new System.Windows.Forms.Padding(3);
this.SystemPage.Size = new System.Drawing.Size(348, 169);
this.SystemPage.TabIndex = 0;
this.SystemPage.Text = "System";
this.SystemPage.UseVisualStyleBackColor = true;
//
// ThrottleSpeedCheckBox
//
this.ThrottleSpeedCheckBox.AutoSize = true;
this.ThrottleSpeedCheckBox.Location = new System.Drawing.Point(9, 53);
this.ThrottleSpeedCheckBox.Name = "ThrottleSpeedCheckBox";
this.ThrottleSpeedCheckBox.Size = new System.Drawing.Size(146, 17);
this.ThrottleSpeedCheckBox.TabIndex = 4;
this.ThrottleSpeedCheckBox.Text = "Throttle Execution Speed";
this.ThrottleSpeedCheckBox.UseVisualStyleBackColor = true;
//
// HostIDTextBox
//
this.HostIDTextBox.Location = new System.Drawing.Point(133, 27);
this.HostIDTextBox.Name = "HostIDTextBox";
this.HostIDTextBox.Size = new System.Drawing.Size(98, 20);
this.HostIDTextBox.TabIndex = 3;
this.HostIDTextBox.Validating += new System.ComponentModel.CancelEventHandler(this.OnHostIDValidating);
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(6, 30);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(119, 13);
this.label2.TabIndex = 2;
this.label2.Text = "Host ID (MAC Address):";
//
// MemorySizeComboBox
//
this.MemorySizeComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.MemorySizeComboBox.FormattingEnabled = true;
this.MemorySizeComboBox.Items.AddRange(new object[] {
"1024",
"768",
"512",
"256",
"128"});
this.MemorySizeComboBox.Location = new System.Drawing.Point(110, 3);
this.MemorySizeComboBox.Name = "MemorySizeComboBox";
this.MemorySizeComboBox.Size = new System.Drawing.Size(121, 21);
this.MemorySizeComboBox.TabIndex = 1;
this.MemorySizeComboBox.SelectionChangeCommitted += new System.EventHandler(this.OnMemorySizeChanged);
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(6, 7);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(97, 13);
this.label1.TabIndex = 0;
this.label1.Text = "Memory Size (KW):";
//
// EthernetTab
//
this.EthernetTab.Controls.Add(this.EthernetInterfaceListBox);
this.EthernetTab.Controls.Add(this.label5);
this.EthernetTab.Location = new System.Drawing.Point(4, 22);
this.EthernetTab.Name = "EthernetTab";
this.EthernetTab.Padding = new System.Windows.Forms.Padding(3);
this.EthernetTab.Size = new System.Drawing.Size(348, 169);
this.EthernetTab.TabIndex = 1;
this.EthernetTab.Text = "Ethernet";
this.EthernetTab.UseVisualStyleBackColor = true;
//
// EthernetInterfaceListBox
//
this.EthernetInterfaceListBox.FormattingEnabled = true;
this.EthernetInterfaceListBox.Location = new System.Drawing.Point(10, 24);
this.EthernetInterfaceListBox.Name = "EthernetInterfaceListBox";
this.EthernetInterfaceListBox.Size = new System.Drawing.Size(332, 134);
this.EthernetInterfaceListBox.TabIndex = 1;
//
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(7, 7);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(240, 13);
this.label5.TabIndex = 0;
this.label5.Text = "Select the network interface to use with Darkstar:";
//
// DisplayTab
//
this.DisplayTab.Controls.Add(this.DisplayScaleComboBox);
this.DisplayTab.Controls.Add(this.label3);
this.DisplayTab.Controls.Add(this.SlowPhosphorCheckBox);
this.DisplayTab.Location = new System.Drawing.Point(4, 22);
this.DisplayTab.Name = "DisplayTab";
this.DisplayTab.Padding = new System.Windows.Forms.Padding(3);
this.DisplayTab.Size = new System.Drawing.Size(348, 169);
this.DisplayTab.TabIndex = 2;
this.DisplayTab.Text = "Display";
this.DisplayTab.UseVisualStyleBackColor = true;
//
// DisplayScaleComboBox
//
this.DisplayScaleComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.DisplayScaleComboBox.FormattingEnabled = true;
this.DisplayScaleComboBox.Items.AddRange(new object[] {
"1x",
"2x",
"4x"});
this.DisplayScaleComboBox.Location = new System.Drawing.Point(84, 28);
this.DisplayScaleComboBox.Name = "DisplayScaleComboBox";
this.DisplayScaleComboBox.Size = new System.Drawing.Size(55, 21);
this.DisplayScaleComboBox.TabIndex = 2;
this.DisplayScaleComboBox.SelectionChangeCommitted += new System.EventHandler(this.OnDisplayScaleChanged);
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(7, 31);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(71, 13);
this.label3.TabIndex = 1;
this.label3.Text = "Display Scale";
//
// SlowPhosphorCheckBox
//
this.SlowPhosphorCheckBox.AutoSize = true;
this.SlowPhosphorCheckBox.Location = new System.Drawing.Point(7, 7);
this.SlowPhosphorCheckBox.Name = "SlowPhosphorCheckBox";
this.SlowPhosphorCheckBox.Size = new System.Drawing.Size(148, 17);
this.SlowPhosphorCheckBox.TabIndex = 0;
this.SlowPhosphorCheckBox.Text = "Slow Phosphor Simulation";
this.SlowPhosphorCheckBox.UseVisualStyleBackColor = true;
//
// TimeTabPage
//
this.TimeTabPage.Controls.Add(this.TODDatePicker);
this.TimeTabPage.Controls.Add(this.SpecifiedDateRadioButton);
this.TimeTabPage.Controls.Add(this.NoTimeDateChangeRadioButton);
this.TimeTabPage.Controls.Add(this.TODDateTimePicker);
this.TimeTabPage.Controls.Add(this.SpecifiedTimeDateRadioButton);
this.TimeTabPage.Controls.Add(this.CurrentTimeDateY2KRadioButton);
this.TimeTabPage.Controls.Add(this.CurrentTimeDateRadioButton);
this.TimeTabPage.Controls.Add(this.label4);
this.TimeTabPage.Location = new System.Drawing.Point(4, 22);
this.TimeTabPage.Name = "TimeTabPage";
this.TimeTabPage.Padding = new System.Windows.Forms.Padding(3);
this.TimeTabPage.Size = new System.Drawing.Size(348, 169);
this.TimeTabPage.TabIndex = 3;
this.TimeTabPage.Text = "Time";
this.TimeTabPage.UseVisualStyleBackColor = true;
//
// TODDateTimePicker
//
this.TODDateTimePicker.CustomFormat = "MM\'/\'dd\'/\'yyyy HH\':\'mm\':\'ss";
this.TODDateTimePicker.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
this.TODDateTimePicker.Location = new System.Drawing.Point(136, 70);
this.TODDateTimePicker.Name = "TODDateTimePicker";
this.TODDateTimePicker.ShowUpDown = true;
this.TODDateTimePicker.Size = new System.Drawing.Size(200, 20);
this.TODDateTimePicker.TabIndex = 4;
//
// SpecifiedTimeDateRadioButton
//
this.SpecifiedTimeDateRadioButton.AutoSize = true;
this.SpecifiedTimeDateRadioButton.Location = new System.Drawing.Point(10, 70);
this.SpecifiedTimeDateRadioButton.Name = "SpecifiedTimeDateRadioButton";
this.SpecifiedTimeDateRadioButton.Size = new System.Drawing.Size(120, 17);
this.SpecifiedTimeDateRadioButton.TabIndex = 3;
this.SpecifiedTimeDateRadioButton.TabStop = true;
this.SpecifiedTimeDateRadioButton.Text = "Specified date/time:";
this.SpecifiedTimeDateRadioButton.UseVisualStyleBackColor = true;
//
// CurrentTimeDateY2KRadioButton
//
this.CurrentTimeDateY2KRadioButton.AutoSize = true;
this.CurrentTimeDateY2KRadioButton.Location = new System.Drawing.Point(10, 47);
this.CurrentTimeDateY2KRadioButton.Name = "CurrentTimeDateY2KRadioButton";
this.CurrentTimeDateY2KRadioButton.Size = new System.Drawing.Size(273, 17);
this.CurrentTimeDateY2KRadioButton.TabIndex = 2;
this.CurrentTimeDateY2KRadioButton.TabStop = true;
this.CurrentTimeDateY2KRadioButton.Text = "Current time/date with Y2K compensation (-28 years)";
this.CurrentTimeDateY2KRadioButton.UseVisualStyleBackColor = true;
//
// CurrentTimeDateRadioButton
//
this.CurrentTimeDateRadioButton.AutoSize = true;
this.CurrentTimeDateRadioButton.Location = new System.Drawing.Point(10, 24);
this.CurrentTimeDateRadioButton.Name = "CurrentTimeDateRadioButton";
this.CurrentTimeDateRadioButton.Size = new System.Drawing.Size(107, 17);
this.CurrentTimeDateRadioButton.TabIndex = 1;
this.CurrentTimeDateRadioButton.TabStop = true;
this.CurrentTimeDateRadioButton.Text = "Current time/date";
this.CurrentTimeDateRadioButton.UseVisualStyleBackColor = true;
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(7, 7);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(228, 13);
this.label4.TabIndex = 0;
this.label4.Text = "At power up/reset, set emulated TOD clock to:";
//
// OKButton
//
this.OKButton.DialogResult = System.Windows.Forms.DialogResult.OK;
this.OKButton.Location = new System.Drawing.Point(207, 209);
this.OKButton.Name = "OKButton";
this.OKButton.Size = new System.Drawing.Size(75, 23);
this.OKButton.TabIndex = 1;
this.OKButton.Text = "OK";
this.OKButton.UseVisualStyleBackColor = true;
//
// Cancel_Button
//
this.Cancel_Button.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.Cancel_Button.Location = new System.Drawing.Point(288, 209);
this.Cancel_Button.Name = "Cancel_Button";
this.Cancel_Button.Size = new System.Drawing.Size(75, 23);
this.Cancel_Button.TabIndex = 2;
this.Cancel_Button.Text = "Cancel";
this.Cancel_Button.UseVisualStyleBackColor = true;
//
// NoTimeDateChangeRadioButton
//
this.NoTimeDateChangeRadioButton.AutoSize = true;
this.NoTimeDateChangeRadioButton.Location = new System.Drawing.Point(10, 119);
this.NoTimeDateChangeRadioButton.Name = "NoTimeDateChangeRadioButton";
this.NoTimeDateChangeRadioButton.Size = new System.Drawing.Size(205, 17);
this.NoTimeDateChangeRadioButton.TabIndex = 5;
this.NoTimeDateChangeRadioButton.TabStop = true;
this.NoTimeDateChangeRadioButton.Text = "No change (do not modify TOD clock)";
this.NoTimeDateChangeRadioButton.UseVisualStyleBackColor = true;
//
// SpecifiedDateRadioButton
//
this.SpecifiedDateRadioButton.AutoSize = true;
this.SpecifiedDateRadioButton.Location = new System.Drawing.Point(10, 93);
this.SpecifiedDateRadioButton.Name = "SpecifiedDateRadioButton";
this.SpecifiedDateRadioButton.Size = new System.Drawing.Size(118, 17);
this.SpecifiedDateRadioButton.TabIndex = 6;
this.SpecifiedDateRadioButton.TabStop = true;
this.SpecifiedDateRadioButton.Text = "Specified date only:";
this.SpecifiedDateRadioButton.UseVisualStyleBackColor = true;
//
// TODDatePicker
//
this.TODDatePicker.CustomFormat = "MM\'/\'dd\'/\'yyyy";
this.TODDatePicker.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
this.TODDatePicker.Location = new System.Drawing.Point(136, 93);
this.TODDatePicker.Name = "TODDatePicker";
this.TODDatePicker.ShowUpDown = true;
this.TODDatePicker.Size = new System.Drawing.Size(200, 20);
this.TODDatePicker.TabIndex = 7;
//
// ConfigurationDialog
//
this.AcceptButton = this.OKButton;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(367, 238);
this.Controls.Add(this.Cancel_Button);
this.Controls.Add(this.OKButton);
this.Controls.Add(this.TabControl);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "ConfigurationDialog";
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "System Configuration";
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.OnClosed);
this.Load += new System.EventHandler(this.OnLoad);
this.TabControl.ResumeLayout(false);
this.SystemPage.ResumeLayout(false);
this.SystemPage.PerformLayout();
this.EthernetTab.ResumeLayout(false);
this.EthernetTab.PerformLayout();
this.DisplayTab.ResumeLayout(false);
this.DisplayTab.PerformLayout();
this.TimeTabPage.ResumeLayout(false);
this.TimeTabPage.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.TabControl TabControl;
private System.Windows.Forms.TabPage SystemPage;
private System.Windows.Forms.TabPage EthernetTab;
private System.Windows.Forms.TabPage DisplayTab;
private System.Windows.Forms.ComboBox MemorySizeComboBox;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox HostIDTextBox;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.ComboBox DisplayScaleComboBox;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.CheckBox SlowPhosphorCheckBox;
private System.Windows.Forms.Button OKButton;
private System.Windows.Forms.Button Cancel_Button;
private System.Windows.Forms.CheckBox ThrottleSpeedCheckBox;
private System.Windows.Forms.TabPage TimeTabPage;
private System.Windows.Forms.DateTimePicker TODDateTimePicker;
private System.Windows.Forms.RadioButton SpecifiedTimeDateRadioButton;
private System.Windows.Forms.RadioButton CurrentTimeDateY2KRadioButton;
private System.Windows.Forms.RadioButton CurrentTimeDateRadioButton;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.ListBox EthernetInterfaceListBox;
private System.Windows.Forms.RadioButton NoTimeDateChangeRadioButton;
private System.Windows.Forms.DateTimePicker TODDatePicker;
private System.Windows.Forms.RadioButton SpecifiedDateRadioButton;
}
}

Some files were not shown because too many files have changed in this diff Show More