mirror of
https://github.com/livingcomputermuseum/ContrAlto.git
synced 2026-02-04 07:33:30 +00:00
Implemented more Disk functionality, fixed bug in ACSOURCE dispatch in Emulator task.
This commit is contained in:
@@ -17,7 +17,7 @@ namespace Contralto
|
||||
public class AltoSystem
|
||||
{
|
||||
public AltoSystem()
|
||||
{
|
||||
{
|
||||
_cpu = new AltoCPU(this);
|
||||
_memBus = new MemoryBus();
|
||||
_mem = new Memory.Memory();
|
||||
@@ -27,7 +27,13 @@ namespace Contralto
|
||||
// Attach memory-mapped devices to the bus
|
||||
_memBus.AddDevice(_mem);
|
||||
_memBus.AddDevice(_keyboard);
|
||||
|
||||
|
||||
// Register devices that need clocks
|
||||
_clockableDevices = new List<IClockable>();
|
||||
_clockableDevices.Add(_memBus);
|
||||
_clockableDevices.Add(_diskController);
|
||||
_clockableDevices.Add(_cpu);
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
@@ -35,13 +41,16 @@ namespace Contralto
|
||||
{
|
||||
_cpu.Reset();
|
||||
_memBus.Reset();
|
||||
_diskController.Reset();
|
||||
}
|
||||
|
||||
public void SingleStep()
|
||||
{
|
||||
_memBus.Clock();
|
||||
_diskController.Clock();
|
||||
_cpu.ExecuteNext();
|
||||
// Run every device that needs attention for a single clock cycle.
|
||||
for (int i = 0; i < _clockableDevices.Count; i++)
|
||||
{
|
||||
_clockableDevices[i].Clock();
|
||||
}
|
||||
}
|
||||
|
||||
public AltoCPU CPU
|
||||
@@ -62,6 +71,7 @@ namespace Contralto
|
||||
/// <summary>
|
||||
/// Time (in msec) for one system clock
|
||||
/// </summary>
|
||||
///
|
||||
public static double ClockInterval
|
||||
{
|
||||
get { return 0.00017; } // appx 170nsec, TODO: more accurate value?
|
||||
@@ -72,5 +82,7 @@ namespace Contralto
|
||||
private Contralto.Memory.Memory _mem;
|
||||
private Keyboard _keyboard;
|
||||
private DiskController _diskController;
|
||||
|
||||
private List<IClockable> _clockableDevices;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Contralto.CPU
|
||||
DiskWord = 14,
|
||||
}
|
||||
|
||||
public partial class AltoCPU
|
||||
public partial class AltoCPU : IClockable
|
||||
{
|
||||
public AltoCPU(AltoSystem system)
|
||||
{
|
||||
@@ -115,25 +115,9 @@ namespace Contralto.CPU
|
||||
|
||||
}
|
||||
|
||||
public void ExecuteNext()
|
||||
public void Clock()
|
||||
{
|
||||
if (_currentTask.ExecuteNext())
|
||||
{
|
||||
// Invoke the task switch, this will take effect after
|
||||
// the NEXT instruction, not this one.
|
||||
TaskSwitch();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we have a new task, switch to it now.
|
||||
if (_nextTask != null)
|
||||
{
|
||||
_currentTask = _nextTask;
|
||||
_nextTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
_clocks++;
|
||||
ExecuteNext();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -162,6 +146,27 @@ namespace Contralto.CPU
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecuteNext()
|
||||
{
|
||||
if (_currentTask.ExecuteNext())
|
||||
{
|
||||
// Invoke the task switch, this will take effect after
|
||||
// the NEXT instruction, not this one.
|
||||
TaskSwitch();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we have a new task, switch to it now.
|
||||
if (_nextTask != null)
|
||||
{
|
||||
_currentTask = _nextTask;
|
||||
_nextTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
_clocks++;
|
||||
}
|
||||
|
||||
private void TaskSwitch()
|
||||
{
|
||||
// Select the highest-priority eligible task
|
||||
|
||||
@@ -7,9 +7,9 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Contralto.CPU
|
||||
{
|
||||
static class ConstantMemory
|
||||
static class ControlROM
|
||||
{
|
||||
static ConstantMemory()
|
||||
static ControlROM()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
@@ -17,6 +17,7 @@ namespace Contralto.CPU
|
||||
private static void Init()
|
||||
{
|
||||
LoadConstants(_constantRoms);
|
||||
LoadACSource(_acSourceRoms);
|
||||
}
|
||||
|
||||
public static ushort[] ConstantROM
|
||||
@@ -24,6 +25,11 @@ namespace Contralto.CPU
|
||||
get { return _constantRom; }
|
||||
}
|
||||
|
||||
public static byte[] ACSourceROM
|
||||
{
|
||||
get { return _acSourceRom; }
|
||||
}
|
||||
|
||||
private static void LoadConstants(RomFile[] romInfo)
|
||||
{
|
||||
_constantRom = new ushort[256];
|
||||
@@ -47,7 +53,7 @@ namespace Contralto.CPU
|
||||
// OR in the data
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
_constantRom[file.StartingAddress + i] |= (ushort)((DataMap(data[AddressMap(i)]) & 0xf) << file.BitPosition);
|
||||
_constantRom[file.StartingAddress + i] |= (ushort)((DataMapConstantRom(data[AddressMapConstantRom(i)]) & 0xf) << file.BitPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,7 +65,29 @@ namespace Contralto.CPU
|
||||
}
|
||||
}
|
||||
|
||||
private static int AddressMap(int address)
|
||||
private static void LoadACSource(RomFile romInfo)
|
||||
{
|
||||
_acSourceRom = new byte[256];
|
||||
|
||||
using (FileStream fs = new FileStream(Path.Combine("ROM", romInfo.Filename), FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
int length = (int)fs.Length;
|
||||
if (length != 256)
|
||||
{
|
||||
throw new InvalidOperationException("ROM file should be 256 bytes in length");
|
||||
}
|
||||
byte[] data = new byte[fs.Length];
|
||||
fs.Read(data, 0, (int)fs.Length);
|
||||
|
||||
// Copy in the data, modifying the address as required.
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
_acSourceRom[i] = (byte)((~data[AddressMapACSourceRom(i)]) & 0xf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int AddressMapConstantRom(int address)
|
||||
{
|
||||
// Descramble the address bits as they are in no sane order.
|
||||
// (See 05a_AIM.pdf, pg. 5 (Page 9 of the orginal docs))
|
||||
@@ -77,7 +105,7 @@ namespace Contralto.CPU
|
||||
return mappedAddress;
|
||||
}
|
||||
|
||||
private static int DataMap(int data)
|
||||
private static int DataMapConstantRom(int data)
|
||||
{
|
||||
// Reverse bits 0-4.
|
||||
int mappedData = 0;
|
||||
@@ -93,6 +121,23 @@ namespace Contralto.CPU
|
||||
return mappedData;
|
||||
}
|
||||
|
||||
private static int AddressMapACSourceRom(int data)
|
||||
{
|
||||
// Reverse bits 0-7.
|
||||
int mappedData = 0;
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if ((data & (1 << i)) != 0)
|
||||
{
|
||||
mappedData |= (1 << (7 - i));
|
||||
}
|
||||
}
|
||||
|
||||
// And invert.
|
||||
return (~mappedData) & 0xff;
|
||||
}
|
||||
|
||||
private static RomFile[] _constantRoms =
|
||||
{
|
||||
new RomFile("c0", 0x000, 12),
|
||||
@@ -101,6 +146,9 @@ namespace Contralto.CPU
|
||||
new RomFile("c3", 0x000, 0),
|
||||
};
|
||||
|
||||
private static RomFile _acSourceRoms = new RomFile("2kctl.u3", 0x000, 0);
|
||||
|
||||
private static ushort[] _constantRom;
|
||||
private static byte[] _acSourceRom;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace Contralto.CPU
|
||||
instruction.F2 == SpecialFunction2.Constant)
|
||||
{
|
||||
source += String.Format("C({0})",
|
||||
OctalHelpers.ToOctal(ConstantMemory.ConstantROM[(instruction.RSELECT << 3) | ((uint)instruction.BS)]));
|
||||
OctalHelpers.ToOctal(ControlROM.ConstantROM[(instruction.RSELECT << 3) | ((uint)instruction.BS)]));
|
||||
}
|
||||
|
||||
switch (instruction.ALUF)
|
||||
|
||||
@@ -96,7 +96,75 @@ namespace Contralto.CPU
|
||||
case DiskF2.INIT:
|
||||
// "NEXT<-NEXT OR (if WDTASKACT AND WDINIT) then 37B else 0
|
||||
// TODO: figure out how WDTASKACT and WDINIT work.
|
||||
throw new NotImplementedException("INIT not implemented.");
|
||||
|
||||
// From the US Patent (4148098):
|
||||
// "..two multiplexers...allow the setting of the next field bits NEXT(05)-NEXT(09)
|
||||
// to 1 after an error condition is detected and as soon as the word task active signal
|
||||
// WDTASKACT is generated..."
|
||||
|
||||
// Is this always an error condition?
|
||||
Console.WriteLine("Warning: assuming 0 for Disk F2 INIT (unimplemented stub)");
|
||||
break;
|
||||
|
||||
|
||||
case DiskF2.RWC:
|
||||
// "NEXT<-NEXT OR (IF current record to be written THEN 3 ELSE IF
|
||||
// current record to be checked THEN 2 ELSE 0.")
|
||||
// Current record is in bits 8-9 of the command register; this is shifted
|
||||
// by INCREC by the microcode to present the next set of bits.
|
||||
int command = (_cpu._system.DiskController.KADR & 0x00c0) >> 6;
|
||||
|
||||
switch(command)
|
||||
{
|
||||
case 0:
|
||||
// read, no modification.
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// check, OR in 2
|
||||
_nextModifier |= 0x2;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
// write, OR in 3
|
||||
_nextModifier |= 0x3;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case DiskF2.XFRDAT:
|
||||
// "NEXT <- NEXT OR (IF current command wants data transfer THEN 1 ELSE 0)
|
||||
// TODO: need to get unshifted bit 14 of the command register. Disk controller should
|
||||
// save this off somewhere.
|
||||
if (_cpu._system.DiskController.DataXfer)
|
||||
{
|
||||
_nextModifier |= 0x1;
|
||||
}
|
||||
break;
|
||||
|
||||
case DiskF2.RECNO:
|
||||
_nextModifier |= _cpu._system.DiskController.RECNO;
|
||||
break;
|
||||
|
||||
case DiskF2.NFER:
|
||||
// "NEXT <- NEXT OR (IF fatal error in latches THEN 0 ELSE 1)"
|
||||
// We assume success for now...
|
||||
_nextModifier |= 0x1;
|
||||
break;
|
||||
|
||||
case DiskF2.STROBON:
|
||||
// "NEXT <- NEXT OR (IF seek strobe still on THEN 1 ELSE 0)"
|
||||
if ((_cpu._system.DiskController.KSTAT & 0x0040) == 0x0040)
|
||||
{
|
||||
_nextModifier |= 0x1;
|
||||
}
|
||||
break;
|
||||
|
||||
case DiskF2.SWRNRDY:
|
||||
// "NEXT <- NEXT OR (IF disk not ready to accept command THEN 1 ELSE 0)
|
||||
// for now, always zero (not sure when this would be 1 yet)
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException(String.Format("Unhandled disk special function 2 {0}", df2));
|
||||
|
||||
@@ -192,54 +192,14 @@ namespace Contralto.CPU
|
||||
// if IR[3-7] = 16B 1 CONVERT
|
||||
// if IR[3-7] = 37B 17B ROMTRAP -- used by Swat, the debugger
|
||||
// else 16B ROMTRAP
|
||||
if ((_cpu._ir & 0x8000) != 0)
|
||||
{
|
||||
_nextModifier = (ushort)(3 - ((_cpu._ir & 0xc0) >> 6));
|
||||
}
|
||||
else if ((_cpu._ir & 0xc000) == 0xc000)
|
||||
{
|
||||
_nextModifier = (ushort)((_cpu._ir & 0x400) >> 10);
|
||||
}
|
||||
else if ((_cpu._ir & 0x1f00) == 0)
|
||||
{
|
||||
_nextModifier = 2;
|
||||
}
|
||||
else if ((_cpu._ir & 0x1f00) == 0x0100)
|
||||
{
|
||||
_nextModifier = 5;
|
||||
}
|
||||
else if ((_cpu._ir & 0x1f00) == 0x0200)
|
||||
{
|
||||
_nextModifier = 3;
|
||||
}
|
||||
else if ((_cpu._ir & 0x1f00) == 0x0300)
|
||||
{
|
||||
_nextModifier = 6;
|
||||
}
|
||||
else if ((_cpu._ir & 0x1f00) == 0x0400)
|
||||
{
|
||||
_nextModifier = 7;
|
||||
}
|
||||
else if ((_cpu._ir & 0x1f00) == 0x0900)
|
||||
{
|
||||
_nextModifier = 4;
|
||||
}
|
||||
else if ((_cpu._ir & 0x1f00) == 0x0a00)
|
||||
{
|
||||
_nextModifier = 4;
|
||||
}
|
||||
else if ((_cpu._ir & 0x1f00) == 0x0e00)
|
||||
{
|
||||
_nextModifier = 1;
|
||||
}
|
||||
else if ((_cpu._ir & 0x1f00) == 0x1f00)
|
||||
{
|
||||
_nextModifier = 0xf;
|
||||
}
|
||||
else
|
||||
{
|
||||
_nextModifier = 0xe;
|
||||
}
|
||||
|
||||
//
|
||||
// NOTE: the above table from the Hardware Manual is incorrect (or at least incomplete / misleading).
|
||||
// There is considerably more that goes into determining the dispatch, which is controlled by a 256x8
|
||||
// PROM. We just use the PROM rather than implementing the above logic (because it works.)
|
||||
//
|
||||
_nextModifier = ControlROM.ACSourceROM[(_cpu._ir & 0xff00) >> 8];
|
||||
|
||||
break;
|
||||
|
||||
case EmulatorF2.ACDEST:
|
||||
|
||||
@@ -185,7 +185,7 @@ namespace Contralto.CPU
|
||||
else
|
||||
{
|
||||
// See also comments below.
|
||||
_busData = ConstantMemory.ConstantROM[(instruction.RSELECT << 3) | ((uint)instruction.BS)];
|
||||
_busData = ControlROM.ConstantROM[(instruction.RSELECT << 3) | ((uint)instruction.BS)];
|
||||
}
|
||||
|
||||
// Constant ROM access:
|
||||
@@ -202,7 +202,7 @@ namespace Contralto.CPU
|
||||
instruction.F1 == SpecialFunction1.Constant ||
|
||||
instruction.F2 == SpecialFunction2.Constant)
|
||||
{
|
||||
_busData &= ConstantMemory.ConstantROM[(instruction.RSELECT << 3) | ((uint)instruction.BS)];
|
||||
_busData &= ControlROM.ConstantROM[(instruction.RSELECT << 3) | ((uint)instruction.BS)];
|
||||
}
|
||||
|
||||
// Do ALU operation
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
<Compile Include="Debugger.Designer.cs">
|
||||
<DependentUpon>Debugger.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="IClockable.cs" />
|
||||
<Compile Include="IO\DiskController.cs" />
|
||||
<Compile Include="IO\Keyboard.cs" />
|
||||
<Compile Include="Memory\IMemoryMappedDevice.cs" />
|
||||
@@ -75,6 +76,9 @@
|
||||
<None Include="Disassembly\altoIIcode3.mu">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="ROM\2kctl.u3">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="ROM\C0">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
@@ -189,6 +193,9 @@
|
||||
<DependentUpon>Debugger.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Notes.txt" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
||||
@@ -13,6 +13,9 @@ using System.Threading;
|
||||
|
||||
namespace Contralto
|
||||
{
|
||||
/// <summary>
|
||||
/// A basic & hacky debugger. To be improved.
|
||||
/// </summary>
|
||||
public partial class Debugger : Form
|
||||
{
|
||||
public Debugger(AltoSystem system)
|
||||
@@ -481,9 +484,12 @@ namespace Contralto
|
||||
// Continuously execute, but do not update UI
|
||||
// until the "Stop" button is pressed or something bad happens.
|
||||
//
|
||||
_execThread = new Thread(new System.Threading.ParameterizedThreadStart(ExecuteProc));
|
||||
_execThread.Start(ExecutionType.Normal);
|
||||
SetExecutionState(ExecutionState.Running);
|
||||
//if (_execThread == null)
|
||||
{
|
||||
_execThread = new Thread(new System.Threading.ParameterizedThreadStart(ExecuteProc));
|
||||
_execThread.Start(ExecutionType.Normal);
|
||||
SetExecutionState(ExecutionState.Running);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnStopButtonClicked(object sender, EventArgs e)
|
||||
|
||||
17
Contralto/IClockable.cs
Normal file
17
Contralto/IClockable.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Contralto
|
||||
{
|
||||
/// <summary>
|
||||
/// Used by classes implementing devices that are clocked (i.e. that are dependent
|
||||
/// on time passing in units of a single CPU clock.)
|
||||
/// </summary>
|
||||
public interface IClockable
|
||||
{
|
||||
void Clock();
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,15 @@ using Contralto.Memory;
|
||||
|
||||
namespace Contralto.IO
|
||||
{
|
||||
public class DiskController
|
||||
public class DiskController : IClockable
|
||||
{
|
||||
public DiskController(AltoSystem system)
|
||||
{
|
||||
_system = system;
|
||||
Reset();
|
||||
|
||||
// Wakeup the sector task first thing
|
||||
_system.CPU.WakeupTask(CPU.TaskType.DiskSector);
|
||||
}
|
||||
|
||||
public ushort KDATA
|
||||
@@ -31,6 +35,10 @@ namespace Contralto.IO
|
||||
|
||||
// "In addition, it causes the head address bit to be loaded from KDATA[13]."
|
||||
_head = (_kData & 0x4) >> 2;
|
||||
|
||||
// "0 normally, 1 if the command is to terminate immediately after the correct cylinder
|
||||
// position is reached (before any data is transferred)."
|
||||
_dataXfer = (_kAdr & 0x2) != 0x2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +55,11 @@ namespace Contralto.IO
|
||||
_bClkSource = (_kCom & 0x04) == 0x04;
|
||||
_wffo = (_kCom & 0x02) == 0x02;
|
||||
_sendAdr = (_kCom & 0x01) == 0x01;
|
||||
|
||||
if (!_wdInhib && !_xferOff)
|
||||
{
|
||||
Console.WriteLine("enabled at sst {0}", _elapsedSectorStateTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +74,11 @@ namespace Contralto.IO
|
||||
get { return _recMap[_recNo]; }
|
||||
}
|
||||
|
||||
public bool DataXfer
|
||||
{
|
||||
get { return _dataXfer; }
|
||||
}
|
||||
|
||||
public int Cylinder
|
||||
{
|
||||
get { return _cylinder; }
|
||||
@@ -100,6 +118,9 @@ namespace Contralto.IO
|
||||
_sector = 0;
|
||||
_head = 0;
|
||||
_kStat = 0;
|
||||
|
||||
_wdInhib = true;
|
||||
_xferOff = true;
|
||||
}
|
||||
|
||||
public void Clock()
|
||||
@@ -119,7 +140,14 @@ namespace Contralto.IO
|
||||
|
||||
_kStat = (ushort)((_kStat & 0x0fff) | (_sector << 12));
|
||||
|
||||
// Reset internal state machine for sector data
|
||||
_sectorState = SectorState.Leadin;
|
||||
_sectorWordIndex = 0;
|
||||
_elapsedSectorStateTime = 0.0;
|
||||
Console.WriteLine("New sector ({0}), switching to LeadIn state.", _sector);
|
||||
|
||||
_system.CPU.WakeupTask(CPU.TaskType.DiskSector);
|
||||
|
||||
}
|
||||
|
||||
// If seek is in progress, move closer to the desired cylinder...
|
||||
@@ -175,6 +203,120 @@ namespace Contralto.IO
|
||||
// _kData = next word
|
||||
// if (!_wdInhib) DiskSectorTask.Wakeup();
|
||||
// }
|
||||
_elapsedSectorStateTime++;
|
||||
switch(_sectorState)
|
||||
{
|
||||
case SectorState.Leadin:
|
||||
if (_elapsedSectorStateTime > _leadinDuration)
|
||||
{
|
||||
_elapsedSectorStateTime -= _leadinDuration;
|
||||
_sectorState = SectorState.Header;
|
||||
Console.WriteLine("Switching to Header state.");
|
||||
}
|
||||
break;
|
||||
|
||||
case SectorState.Header:
|
||||
if (_sectorWordIndex > 1) // two words
|
||||
{
|
||||
_elapsedSectorStateTime -= 2.0 * _wordDuration;
|
||||
_sectorState = SectorState.HeaderGap;
|
||||
_sectorWordIndex = 0;
|
||||
Console.WriteLine("Switching to HeaderGap state.");
|
||||
}
|
||||
else if (_elapsedSectorStateTime > _wordDuration)
|
||||
{
|
||||
_elapsedSectorStateTime -= _wordDuration;
|
||||
|
||||
// Put next word into KDATA if not inhibited from doing so.
|
||||
if (!_xferOff)
|
||||
{
|
||||
_kData = 0xdead; // placeholder
|
||||
Console.WriteLine(" Header word {0} is {1}", _sectorWordIndex, OctalHelpers.ToOctal(_kData));
|
||||
}
|
||||
_sectorWordIndex++;
|
||||
|
||||
if (!_wdInhib)
|
||||
{
|
||||
_system.CPU.WakeupTask(CPU.TaskType.DiskWord);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SectorState.HeaderGap:
|
||||
if (_elapsedSectorStateTime > _headerGapDuration)
|
||||
{
|
||||
_elapsedSectorStateTime -= _headerGapDuration;
|
||||
_sectorState = SectorState.Label;
|
||||
Console.WriteLine("Switching to Label state.");
|
||||
}
|
||||
break;
|
||||
|
||||
case SectorState.Label:
|
||||
if (_sectorWordIndex > 7) // eight words
|
||||
{
|
||||
_elapsedSectorStateTime -= 8.0 * _wordDuration;
|
||||
_sectorState = SectorState.LabelGap;
|
||||
_sectorWordIndex = 0;
|
||||
Console.WriteLine("Switching to LabelGap state.");
|
||||
}
|
||||
else if(_elapsedSectorStateTime > _wordDuration)
|
||||
{
|
||||
_elapsedSectorStateTime -= _wordDuration;
|
||||
// Put next word into KDATA if not inhibited from doing so.
|
||||
if (!_xferOff)
|
||||
{
|
||||
_kData = 0xbeef; // placeholder
|
||||
Console.WriteLine(" Label word {0} is {1}", _sectorWordIndex, OctalHelpers.ToOctal(_kData));
|
||||
}
|
||||
_sectorWordIndex++;
|
||||
|
||||
if (!_wdInhib)
|
||||
{
|
||||
_system.CPU.WakeupTask(CPU.TaskType.DiskWord);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SectorState.LabelGap:
|
||||
if (_elapsedSectorStateTime > _labelGapDuration)
|
||||
{
|
||||
_elapsedSectorStateTime -= _labelGapDuration;
|
||||
_sectorState = SectorState.Data;
|
||||
Console.WriteLine("Switching to Data state.");
|
||||
}
|
||||
break;
|
||||
|
||||
case SectorState.Data:
|
||||
if (_sectorWordIndex > 255) // 256 words
|
||||
{
|
||||
_elapsedSectorStateTime -= 256.0 * _wordDuration;
|
||||
_sectorState = SectorState.Leadout;
|
||||
_sectorWordIndex = 0;
|
||||
Console.WriteLine("Switching to Leadout state.");
|
||||
}
|
||||
else if (_elapsedSectorStateTime > _wordDuration)
|
||||
{
|
||||
_elapsedSectorStateTime -= _wordDuration;
|
||||
// Put next word into KDATA if not inhibited from doing so.
|
||||
if (!_xferOff)
|
||||
{
|
||||
_kData = 0xda1a; // placeholder
|
||||
Console.WriteLine(" Sector word {0} is {1}", _sectorWordIndex, OctalHelpers.ToOctal(_kData));
|
||||
}
|
||||
_sectorWordIndex++;
|
||||
|
||||
if (!_wdInhib)
|
||||
{
|
||||
_system.CPU.WakeupTask(CPU.TaskType.DiskWord);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SectorState.Leadout:
|
||||
// Just stay here forever. We will get reset at the start of the next sector.
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearStatus()
|
||||
@@ -271,6 +413,9 @@ namespace Contralto.IO
|
||||
private bool _wffo;
|
||||
private bool _sendAdr;
|
||||
|
||||
// Transfer bit
|
||||
private bool _dataXfer;
|
||||
|
||||
// Current disk position
|
||||
private int _cylinder;
|
||||
private int _destCylinder;
|
||||
@@ -280,7 +425,29 @@ namespace Contralto.IO
|
||||
// Sector timing. Based on table on pg. 43 of the Alto Hardware Manual
|
||||
private double _elapsedSectorTime; // elapsed time in this sector (in clocks)
|
||||
private const double _sectorDuration = (40.0 / 12.0); // time in msec for one sector
|
||||
private readonly double _sectorClocks = _sectorDuration / AltoSystem.ClockInterval; // number of clock cycles per sector time.
|
||||
private const double _sectorClocks = _sectorDuration / 0.00017; // number of clock cycles per sector time.
|
||||
|
||||
// Sector data timing and associated state. Timings based on educated guesses at the moment.
|
||||
private enum SectorState
|
||||
{
|
||||
Leadin = 0, // gap between sector mark and first Header word
|
||||
Header, // Header; two words
|
||||
HeaderGap, // gap between end of Header and first Label word
|
||||
Label, // Label; 8 words
|
||||
LabelGap, // gap betweeen the end of Label and first Data word
|
||||
Data, // Data; 256 words
|
||||
Leadout // gap between the end of Data and the next sector mark
|
||||
}
|
||||
private SectorState _sectorState;
|
||||
private double _elapsedSectorStateTime;
|
||||
private int _sectorWordIndex;
|
||||
|
||||
private const double _wordDuration = (_sectorClocks / (266.0 + 94.0)); // Based on : 266 words / sector, + 94 "words" for gaps (made up)
|
||||
private const double _leadinDuration = (_wordDuration * 70.0);
|
||||
private const double _headerGapDuration = (_wordDuration * 8.0);
|
||||
private const double _labelGapDuration = (_wordDuration * 8.0);
|
||||
private const double _leadoutDuration = (_wordDuration * 8.0);
|
||||
|
||||
|
||||
// Cylinder seek timing. Again, see the manual.
|
||||
// Timing varies based on how many cylinders are being traveled during a seek; see
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Contralto.Memory
|
||||
Store
|
||||
}
|
||||
|
||||
public class MemoryBus
|
||||
public class MemoryBus : IClockable
|
||||
{
|
||||
public MemoryBus()
|
||||
{
|
||||
@@ -243,11 +243,13 @@ namespace Contralto.Memory
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException(String.Format("Read from unimplemented memory-mapped I/O device at {0}.", OctalHelpers.ToOctal(address)));
|
||||
//Console.WriteLine("Read from unimplemented memory-mapped I/O device at {0}.", OctalHelpers.ToOctal(address));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches writes to memory mapped hardware (RAM, I/O
|
||||
/// Dispatches writes to memory mapped hardware (RAM, I/O)
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
/// <param name="data"></param>
|
||||
@@ -262,7 +264,8 @@ namespace Contralto.Memory
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException(String.Format("Write to unimplemented memory-mapped I/O device at {0}.", OctalHelpers.ToOctal(address)));
|
||||
throw new NotImplementedException(String.Format("Write to unimplemented memory-mapped I/O device at {0}.", OctalHelpers.ToOctal(address)));
|
||||
//Console.WriteLine("Write to unimplemented memory-mapped I/O device at {0}.", OctalHelpers.ToOctal(address));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
5
Contralto/Notes.txt
Normal file
5
Contralto/Notes.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
Current issue:
|
||||
|
||||
DCB @ 521 is set to 1 by emulator uCode; then clobbered once sector task runs (set to zero) so the disk controller never picks up a control
|
||||
block and never does anything.
|
||||
|
||||
BIN
Contralto/ROM/2kctl.u3
Normal file
BIN
Contralto/ROM/2kctl.u3
Normal file
Binary file not shown.
Reference in New Issue
Block a user