1
0
mirror of https://github.com/livingcomputermuseum/ContrAlto.git synced 2026-04-12 23:17:09 +00:00

General code cleanup. Fixed debugger display of RAM banks for 3K systems, cleaned up logic for displaying ucode memory. Implemented memory timing for Alto I systems, incorporated Alto I uCode ROMs from Al K, which now boot. ST-74 runs better now.

This commit is contained in:
Josh Dersch
2016-05-05 18:56:29 -07:00
parent c6baa01509
commit 6429c3ae7a
139 changed files with 725 additions and 588 deletions

View File

@@ -125,9 +125,9 @@ namespace Contralto
DiabloPack newPack = new DiabloPack(type);
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
{
{
newPack.Load(fs, path, false);
fs.Close();
fs.Close();
}
_diskController.Drives[drive].LoadPack(newPack);

View File

@@ -135,6 +135,7 @@ namespace Contralto.CPU
// If we have a new task, switch to it now.
_currentTask = _nextTask;
_nextTask = null;
_currentTask.OnTaskSwitch();
}
break;
@@ -164,7 +165,10 @@ namespace Contralto.CPU
}
Log.Write(LogComponent.CPU, "Silent Boot; microcode banks initialized to {0}", Conversion.ToOctal(_rmr));
UCodeMemory.LoadBanksFromRMR(_rmr);
UCodeMemory.LoadBanksFromRMR(_rmr);
// Reset RMR after reset.
_rmr = 0x0;
// Start in Emulator
_currentTask = _tasks[0];
@@ -175,7 +179,7 @@ namespace Contralto.CPU
// itself as soon as the Emulator task yields after the reset. (CopyDisk is broken otherwise due to the
// sector task stomping over the KBLK CopyDisk sets up after the reset. This is a race of sorts.)
// Unsure if there is a deeper issue here or if there are other reset semantics
// in play here.
// in play that are not understood.
//
WakeupTask(CPU.TaskType.DiskSector);
}

View File

@@ -3,42 +3,28 @@ using System.IO;
namespace Contralto.CPU
{
/// <summary>
/// Maintains a set of Control ROM images dumped from real Alto hardware.
/// </summary>
static class ControlROM
{
static ControlROM()
{
Init();
/*
LoadConstants(_constantRomsAltoI);
ushort[] temp = new ushort[256];
_constantRom.CopyTo(temp, 0);
LoadConstants(_constantRomsAltoII);
for(int i=0;i<256;i++)
{
if (temp[i] != _constantRom[i])
{
Console.WriteLine("Diff at {0:x} - {1:x} vs {2:x}", i, temp[i], _constantRom[i]);
}
} */
Init();
}
private static void Init()
{
if (Configuration.SystemType == SystemType.AltoI)
{
LoadConstants(_constantRomsAltoI);
LoadConstants(_constantRomsAltoI, true);
LoadACSource(_acSourceRoms, true);
}
else
{
LoadConstants(_constantRomsAltoI);
}
LoadACSource(_acSourceRoms);
LoadConstants(_constantRomsAltoII, false);
LoadACSource(_acSourceRoms, true);
}
}
public static ushort[] ConstantROM
@@ -51,7 +37,7 @@ namespace Contralto.CPU
get { return _acSourceRom; }
}
private static void LoadConstants(RomFile[] romInfo)
private static void LoadConstants(RomFile[] romInfo, bool flip)
{
_constantRom = new ushort[256];
@@ -60,7 +46,7 @@ namespace Contralto.CPU
//
// Each file contains 256 bytes, each byte containing one nybble in the low 4 bits.
//
using (FileStream fs = new FileStream(Path.Combine("ROM", file.Filename), FileMode.Open, FileAccess.Read))
using (FileStream fs = new FileStream(file.Filename, FileMode.Open, FileAccess.Read))
{
int length = (int)fs.Length;
if (length != 256)
@@ -74,7 +60,14 @@ namespace Contralto.CPU
// OR in the data
for (int i = 0; i < length; i++)
{
_constantRom[file.StartingAddress + i] |= (ushort)((DataMapConstantRom(data[AddressMapConstantRom(i)]) & 0xf) << file.BitPosition);
if (flip)
{
_constantRom[file.StartingAddress + i] |= (ushort)((DataMapConstantRom(~data[AddressMapConstantRom(i)]) & 0xf) << file.BitPosition);
}
else
{
_constantRom[file.StartingAddress + i] |= (ushort)((DataMapConstantRom(data[AddressMapConstantRom(i)]) & 0xf) << file.BitPosition);
}
}
}
}
@@ -86,11 +79,11 @@ namespace Contralto.CPU
}
}
private static void LoadACSource(RomFile romInfo)
private static void LoadACSource(RomFile romInfo, bool reverseBits)
{
_acSourceRom = new byte[256];
using (FileStream fs = new FileStream(Path.Combine("ROM", romInfo.Filename), FileMode.Open, FileAccess.Read))
using (FileStream fs = new FileStream(romInfo.Filename, FileMode.Open, FileAccess.Read))
{
int length = (int)fs.Length;
if (length != 256)
@@ -103,7 +96,14 @@ namespace Contralto.CPU
// Copy in the data, modifying the address as required.
for (int i = 0; i < length; i++)
{
_acSourceRom[i] = (byte)((~data[AddressMapACSourceRom(i)]) & 0xf);
if (reverseBits)
{
_acSourceRom[i] = (byte)((~DataMapConstantRom(data[AddressMapACSourceRom(i)])) & 0xf);
}
else
{
_acSourceRom[i] = (byte)((~data[AddressMapACSourceRom(i)]) & 0xf);
}
}
}
}
@@ -158,24 +158,24 @@ namespace Contralto.CPU
// And invert data lines
return (~mappedData) & 0xff;
}
private static RomFile[] _constantRomsAltoII =
{
new RomFile("c0", 0x000, 12),
new RomFile("c1", 0x000, 8),
new RomFile("c2", 0x000, 4),
new RomFile("c3", 0x000, 0),
};
private static RomFile[] _constantRomsAltoI =
{
new RomFile("c0.2", 0x000, 12),
new RomFile("c1.2", 0x000, 8),
new RomFile("c2.2", 0x000, 4),
new RomFile("c3.2", 0x000, 0),
new RomFile(Configuration.GetAltoIRomPath("c0_23.BIN"), 0x000, 12),
new RomFile(Configuration.GetAltoIRomPath("c1_23.BIN"), 0x000, 8),
new RomFile(Configuration.GetAltoIRomPath("c2_23.BIN"), 0x000, 4),
new RomFile(Configuration.GetAltoIRomPath("c3_23.BIN"), 0x000, 0),
};
private static RomFile _acSourceRoms = new RomFile("2kctl.u3", 0x000, 0);
private static RomFile[] _constantRomsAltoII =
{
new RomFile(Configuration.GetAltoIIRomPath("c0"), 0x000, 12),
new RomFile(Configuration.GetAltoIIRomPath("c1"), 0x000, 8),
new RomFile(Configuration.GetAltoIIRomPath("c2"), 0x000, 4),
new RomFile(Configuration.GetAltoIIRomPath("c3"), 0x000, 0),
};
private static RomFile _acSourceRoms = new RomFile(Configuration.GetRomPath("ACSOURCE.NEW"), 0x000, 0);
private static ushort[] _constantRom;
private static byte[] _acSourceRom;

View File

@@ -177,6 +177,11 @@ namespace Contralto.CPU
EISFCT = 14,
}
/// <summary>
/// MicroInstruction encapsulates the decoding of a microinstruction word.
/// It also caches precomputed metadata related to the microinstruction that
/// help speed microcode execution.
/// </summary>
public class MicroInstruction
{
public MicroInstruction(UInt32 code)
@@ -228,14 +233,14 @@ namespace Contralto.CPU
// What kind of memory access this instruction performs, if any.
if (MemoryAccess)
{
if (BS == BusSource.ReadMD)
{
MemoryOperation = MemoryOperation.Read;
}
else if (F1 == SpecialFunction1.LoadMAR)
if (F1 == SpecialFunction1.LoadMAR)
{
MemoryOperation = MemoryOperation.LoadAddress;
}
else if (BS == BusSource.ReadMD)
{
MemoryOperation = MemoryOperation.Read;
}
else
{
MemoryOperation = MemoryOperation.Store;

View File

@@ -5,7 +5,7 @@ namespace Contralto.CPU
public partial class AltoCPU
{
/// <summary>
/// CursorTask provides functionality for the Cursor-specific task functions
/// CursorTask provides the implementation of Cursor-specific task functions
/// </summary>
private sealed class CursorTask : Task
{
@@ -15,12 +15,10 @@ namespace Contralto.CPU
_wakeup = false;
}
protected override InstructionCompletion ExecuteInstruction(MicroInstruction instruction)
public override void OnTaskSwitch()
{
// We put ourselves back to sleep immediately once we've started running
_wakeup = false;
return base.ExecuteInstruction(instruction);
// We put ourselves back to sleep immediately once we've started running.
_wakeup = false;
}
protected override void ExecuteSpecialFunction2(MicroInstruction instruction)

View File

@@ -7,9 +7,9 @@ namespace Contralto.CPU
public partial class AltoCPU
{
/// <summary>
/// DiskTask provides implementation for disk-specific special functions
/// DiskTask provides implementations of disk-specific special functions
/// (for both Disk Sector and Disk Word tasks, since the special functions are
/// identical between the two)
/// identical between the two).
/// </summary>
private sealed class DiskTask : Task
{
@@ -19,12 +19,10 @@ namespace Contralto.CPU
_wakeup = false;
_diskController = _cpu._system.DiskController;
}
protected override InstructionCompletion ExecuteInstruction(MicroInstruction instruction)
{
InstructionCompletion completion = base.ExecuteInstruction(instruction);
}
public override void OnTaskSwitch()
{
// Deal with SECLATE semantics: If the Disk Sector task wakes up and runs before
// the Disk Controller hits the SECLATE trigger time, then SECLATE remains false.
// Otherwise, when the trigger time is hit SECLATE is raised until
@@ -32,10 +30,8 @@ namespace Contralto.CPU
if (_taskType == TaskType.DiskSector)
{
// Sector task is running; clear enable for seclate signal
_diskController.DisableSeclate();
}
return completion;
_diskController.DisableSeclate();
}
}
protected override ushort GetBusSource(int bs)

View File

@@ -6,7 +6,7 @@ namespace Contralto.CPU
public partial class AltoCPU
{
/// <summary>
/// DisplayWordTask provides functionality for the DHT task
/// DisplayHorizontalTask provides implementations of the DHT task functions.
/// </summary>
private sealed class DisplayHorizontalTask : Task
{
@@ -18,12 +18,10 @@ namespace Contralto.CPU
_displayController = _cpu._system.DisplayController;
}
protected override InstructionCompletion ExecuteInstruction(MicroInstruction instruction)
public override void OnTaskSwitch()
{
// We put ourselves back to sleep immediately once we've started running
// We put ourselves back to sleep immediately once we've started running.
_wakeup = false;
return base.ExecuteInstruction(instruction);
}
protected override void ExecuteSpecialFunction2(MicroInstruction instruction)

View File

@@ -18,15 +18,10 @@ namespace Contralto.CPU
_displayController = _cpu._system.DisplayController;
}
protected override InstructionCompletion ExecuteInstruction(MicroInstruction instruction)
public override void OnTaskSwitch()
{
// We put ourselves back to sleep immediately once we've started running
// TODO: for this and other similar patterns: rework this so we don't need to
// override ExecuteInstruction just to do this (or similar polling). Virtual calls
// are expensive, especially when millions of them are being made a second.
// We put ourselves back to sleep immediately once we've started running.
_wakeup = false;
return base.ExecuteInstruction(instruction);
}
protected override void ExecuteSpecialFunction2(MicroInstruction instruction)

View File

@@ -22,8 +22,6 @@ namespace Contralto.CPU
{
base.Reset();
_wakeup = true;
_systemType = Configuration.SystemType;
}
public override void BlockTask()
@@ -126,7 +124,7 @@ namespace Contralto.CPU
// Dispatch to the appropriate device.
// The Ethernet controller is the only common device that is documented
// to have used STARTF, so we'll just go there directly; if other
// hardware is discovered to be worth emulating we'll put together a more flexible dispatch.
// hardware is determined to be worth emulating we'll put together a more flexible dispatch.
//
if (_busData < 4)
{
@@ -226,49 +224,18 @@ namespace Contralto.CPU
// elseif IR[4-7] = 16B 6
// else IR[4-7]
// NB: as always, Xerox labels bits in the opposite order from modern convention;
// (bit 0 is the msb...)
/*
if (Configuration.SystemType == SystemType.AltoI && UCodeMemory.GetBank(TaskType.Emulator) == MicrocodeBank.RAM0)
// (bit 0 is the msb...)
//
// NOTE: The above table is accurate and functions correctly; using the PROM is faster.
//
if ((_cpu._ir & 0x8000) != 0)
{
if ((_cpu._ir & 0x8000) != 0)
{1
_nextModifier = (ushort)(3 - ((_cpu._ir & 0xc0) >> 6));
}
else if ((_cpu._ir & 0x6000) == 0x0000)
{
_nextModifier = (ushort)((_cpu._ir & 0x3000) >> 12);
}
else if ((_cpu._ir & 0x6000) == 0x2000)
{
_nextModifier = 4;
}
else if ((_cpu._ir & 0x6000) == 0x4000)
{
_nextModifier = 5;
}
else if ((_cpu._ir & 0x6000) == 0x6000)
{
if ((_cpu._ir & 0x1f00) == 0x0e00)
{
_nextModifier = 6;
}
else
{
_nextModifier = 1;
}
}
_nextModifier = (ushort)(3 - ((_cpu._ir & 0xc0) >> 6));
}
else */
else
{
if ((_cpu._ir & 0x8000) != 0)
{
_nextModifier = (ushort)(3 - ((_cpu._ir & 0xc0) >> 6));
}
else
{
_nextModifier = ControlROM.ACSourceROM[((_cpu._ir & 0x7f00) >> 8) + 0x80];
}
}
_nextModifier = ControlROM.ACSourceROM[((_cpu._ir & 0x7f00) >> 8) + 0x80];
}
break;
case EmulatorF2.ACSOURCE:
@@ -290,7 +257,8 @@ namespace Contralto.CPU
// else 16B ROMTRAP
//
// NOTE: the above table from the Hardware Manual is incorrect (or at least incomplete / misleading).
// NOTE: the above table from the Hardware Manual is incorrect
// (or at least incomplete / out of date / 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.)
//
@@ -462,9 +430,7 @@ namespace Contralto.CPU
// BUS+SKIP(ALUF= 13B). IR<- clears SKIP."
//
// NB: _skip is in the encapsulating AltoCPU class to make it easier to reference since the ALU needs to know about it.
private int _carry;
private SystemType _systemType;
private int _carry;
}
}
}

View File

@@ -23,7 +23,7 @@
//
// "; This version assumes MRTACT is cleared by BLOCK, not MAR<- R37"
//
if (Configuration.SystemType == SystemType.AltoI &&
if (_systemType == SystemType.AltoI &&
instruction.F1 == SpecialFunction1.LoadMAR &&
_rSelect == 31)
{

View File

@@ -26,7 +26,9 @@ namespace Contralto.CPU
_wakeup = false;
_mpc = 0xffff; // invalid, for sanity checking
_taskType = TaskType.Invalid;
_cpu = cpu;
_cpu = cpu;
_systemType = Configuration.SystemType;
}
public int Priority
@@ -84,16 +86,26 @@ namespace Contralto.CPU
_mpc = (ushort)_taskType;
}
/// <summary>
/// Removes the Wakeup signal for this task.
/// </summary>
public virtual void BlockTask()
{
_wakeup = false;
}
/// <summary>
/// Sets the Wakeup signal for this task.
/// </summary>
public virtual void WakeupTask()
{
_wakeup = true;
}
/// <summary>
/// Executes a single microinstruction.
/// </summary>
/// <returns>An InstructionCompletion indicating whether this instruction calls for a task switch or not.</returns>
public InstructionCompletion ExecuteNext()
{
MicroInstruction instruction = UCodeMemory.GetInstruction(_mpc, _taskType);
@@ -102,10 +114,10 @@ namespace Contralto.CPU
/// <summary>
/// ExecuteInstruction causes the Task to execute the next instruction (the one
/// _mpc is pointing to). The base implementation covers non-task specific logic; subclasses may
/// provide their own overrides.
/// _mpc is pointing to). The base implementation covers non-task specific logic,
/// subclasses (specific task implementations) may provide their own implementations.
/// </summary>
/// <returns>True if a task switch has been requested by a TASK instruction, false otherwise.</returns>
/// <returns>An InstructionCompletion indicating whether this instruction calls for a task switch or not.</returns>
protected virtual InstructionCompletion ExecuteInstruction(MicroInstruction instruction)
{
InstructionCompletion completion = InstructionCompletion.Normal;
@@ -281,7 +293,7 @@ namespace Contralto.CPU
_cpu._system.MemoryBus.LoadMAR(
aluData,
_taskType,
Configuration.SystemType == SystemType.AltoI ? false : instruction.F2 == SpecialFunction2.StoreMD);
_systemType == SystemType.AltoI ? false : instruction.F2 == SpecialFunction2.StoreMD);
break;
case SpecialFunction1.Task:
@@ -364,9 +376,13 @@ namespace Contralto.CPU
break;
case SpecialFunction2.StoreMD:
// Special case for XMAR; if F1 is a LoadMAR we do nothing here;
// Special case for XMAR on non-Alto I machines: if F1 is a LoadMAR we do nothing here;
// the handler for LoadMAR will load the correct bank.
if (instruction.F1 != SpecialFunction1.LoadMAR)
if (_systemType == SystemType.AltoI)
{
_cpu._system.MemoryBus.LoadMD(_busData);
}
else if(instruction.F1 != SpecialFunction1.LoadMAR)
{
_cpu._system.MemoryBus.LoadMD(_busData);
}
@@ -510,24 +526,39 @@ namespace Contralto.CPU
return completion;
}
/// <summary>
/// Provides task-specific implementations the opportunity to handle task-specific bus sources.
/// </summary>
/// <param name="bs"></param>
/// <returns></returns>
protected virtual ushort GetBusSource(int bs)
{
// Nothing by default.
return 0;
}
/// <summary>
/// Executes before the ALU runs but after bus sources have been selected.
/// </summary>
/// <param name="instruction"></param>
protected virtual void ExecuteSpecialFunction1Early(MicroInstruction instruction)
{
// Nothing by default
}
/// <summary>
/// Executes after the ALU has run but before the shifter runs, provides task-specific implementations
/// the opportunity to handle task-specific F1s.
/// </summary>
/// <param name="instruction"></param>
protected virtual void ExecuteSpecialFunction1(MicroInstruction instruction)
{
// Nothing by default
}
/// <summary>
/// Used to allow Task-specific F2s that need to modify RSELECT to do so.
/// Executes before bus sources are selected. Used to allow Task-specific F2s that need to
/// modify RSELECT to do so.
/// </summary>
/// <param name="f2"></param>
protected virtual void ExecuteSpecialFunction2Early(MicroInstruction instruction)
@@ -535,11 +566,20 @@ namespace Contralto.CPU
// Nothing by default.
}
/// <summary>
/// Executes after the ALU has run but before the shifter runs, provides task-specific implementations
/// the opportunity to handle task-specific F2s.
/// </summary>
protected virtual void ExecuteSpecialFunction2(MicroInstruction instruction)
{
// Nothing by default.
}
/// <summary>
/// Executes after the shifter has run, provides task-specific implementations the opportunity
/// to handle task-specific F2s late in the cycle.
/// </summary>
/// <param name="instruction"></param>
protected virtual void ExecuteSpecialFunction2Late(MicroInstruction instruction)
{
// Nothing by default.
@@ -554,6 +594,20 @@ namespace Contralto.CPU
// Nothing by default
}
/// <summary>
/// Allows task-specific behavior when a new task begins execution.
/// (Generally this is used to remove wakeup immediately.)
/// </summary>
public virtual void OnTaskSwitch()
{
// Nothing by default
}
/// <summary>
/// Cache the system type.
/// </summary>
protected SystemType _systemType;
//
// Per uInstruction Task Data:
// Modified by both the base Task implementation and any subclasses

View File

@@ -3,6 +3,9 @@ using System.Text;
namespace Contralto.CPU
{
/// <summary>
/// Provides a facility for doing a (crude) disassembly of microcode.
/// </summary>
public static class UCodeDisassembler
{

View File

@@ -27,6 +27,10 @@ namespace Contralto.CPU
public int BitPosition;
}
/// <summary>
/// UCodeMemory maintains a set of Microcode ROM images and provides facilities
/// for accessing them.
/// </summary>
static class UCodeMemory
{
static UCodeMemory()
@@ -278,7 +282,7 @@ namespace Contralto.CPU
//
// Each file contains 1024 bytes, each byte containing one nybble in the low 4 bits.
//
using(FileStream fs = new FileStream(Path.Combine("ROM", file.Filename), FileMode.Open, FileAccess.Read))
using(FileStream fs = new FileStream(file.Filename, FileMode.Open, FileAccess.Read))
{
int length = (int)fs.Length;
if (length != 1024)
@@ -312,7 +316,7 @@ namespace Contralto.CPU
//
// Each file contains 256 bytes, each byte containing one nybble in the low 4 bits.
//
using (FileStream fs = new FileStream(Path.Combine("ROM", file.Filename), FileMode.Open, FileAccess.Read))
using (FileStream fs = new FileStream(file.Filename, FileMode.Open, FileAccess.Read))
{
int length = (int)fs.Length;
if (length != 256)
@@ -384,73 +388,74 @@ namespace Contralto.CPU
{
UInt32 instructionWord = _uCodeRam[address];
_decodeCache[2048 + address] = new MicroInstruction(instructionWord);
}
private static RomFile[] _uCodeRomsAltoII =
{
// first K
new RomFile("u55", 0x000, 28),
new RomFile("u64", 0x000, 24),
new RomFile("u65", 0x000, 20),
new RomFile("u63", 0x000, 16),
new RomFile("u53", 0x000, 12),
new RomFile("u60", 0x000, 8),
new RomFile("u61", 0x000, 4),
new RomFile("u62", 0x000, 0),
// second K
new RomFile("u54", 0x400, 28),
new RomFile("u74", 0x400, 24),
new RomFile("u75", 0x400, 20),
new RomFile("u73", 0x400, 16),
new RomFile("u52", 0x400, 12),
new RomFile("u70", 0x400, 8),
new RomFile("u71", 0x400, 4),
new RomFile("u72", 0x400, 0)
};
}
private static RomFile[] _uCodeRomsAltoI =
{
// 0 to 377
new RomFile("0.2", 0x000, 28),
new RomFile("1.2", 0x000, 24),
new RomFile("2.2", 0x000, 20),
new RomFile("3.2", 0x000, 16),
new RomFile("4.2", 0x000, 12),
new RomFile("5.2", 0x000, 8),
new RomFile("6.2", 0x000, 4),
new RomFile("7.2", 0x000, 0),
new RomFile(Configuration.GetAltoIRomPath("00_23.BIN"), 0x000, 28),
new RomFile(Configuration.GetAltoIRomPath("01_23.BIN"), 0x000, 24),
new RomFile(Configuration.GetAltoIRomPath("02_23.BIN"), 0x000, 20),
new RomFile(Configuration.GetAltoIRomPath("03_23.BIN"), 0x000, 16),
new RomFile(Configuration.GetAltoIRomPath("04_23.BIN"), 0x000, 12),
new RomFile(Configuration.GetAltoIRomPath("05_23.BIN"), 0x000, 8),
new RomFile(Configuration.GetAltoIRomPath("06_23.BIN"), 0x000, 4),
new RomFile(Configuration.GetAltoIRomPath("07_23.BIN"), 0x000, 0),
// 400 to 777
new RomFile("10.2", 0x100, 28),
new RomFile("11.2", 0x100, 24),
new RomFile("12.2", 0x100, 20),
new RomFile("13.2", 0x100, 16),
new RomFile("14.2", 0x100, 12),
new RomFile("15.2", 0x100, 8),
new RomFile("16.2", 0x100, 4),
new RomFile("17.2", 0x100, 0),
new RomFile(Configuration.GetAltoIRomPath("10_23.BIN"), 0x100, 28),
new RomFile(Configuration.GetAltoIRomPath("11_23.BIN"), 0x100, 24),
new RomFile(Configuration.GetAltoIRomPath("12_23.BIN"), 0x100, 20),
new RomFile(Configuration.GetAltoIRomPath("13_23.BIN"), 0x100, 16),
new RomFile(Configuration.GetAltoIRomPath("14_23.BIN"), 0x100, 12),
new RomFile(Configuration.GetAltoIRomPath("15_23.BIN"), 0x100, 8),
new RomFile(Configuration.GetAltoIRomPath("16_23.BIN"), 0x100, 4),
new RomFile(Configuration.GetAltoIRomPath("17_23.BIN"), 0x100, 0),
// 1000 to 1377
new RomFile("20.2", 0x200, 28),
new RomFile("21.2", 0x200, 24),
new RomFile("22.2", 0x200, 20),
new RomFile("23.2", 0x200, 16),
new RomFile("24.2", 0x200, 12),
new RomFile("25.2", 0x200, 8),
new RomFile("26.2", 0x200, 4),
new RomFile("27.2", 0x200, 0),
new RomFile(Configuration.GetAltoIRomPath("20_23.BIN"), 0x200, 28),
new RomFile(Configuration.GetAltoIRomPath("21_23.BIN"), 0x200, 24),
new RomFile(Configuration.GetAltoIRomPath("22_23.BIN"), 0x200, 20),
new RomFile(Configuration.GetAltoIRomPath("23_23.BIN"), 0x200, 16),
new RomFile(Configuration.GetAltoIRomPath("24_23.BIN"), 0x200, 12),
new RomFile(Configuration.GetAltoIRomPath("25_23.BIN"), 0x200, 8),
new RomFile(Configuration.GetAltoIRomPath("26_23.BIN"), 0x200, 4),
new RomFile(Configuration.GetAltoIRomPath("27_23.BIN"), 0x200, 0),
// 1400 to 1777
new RomFile("30.2", 0x300, 28),
new RomFile("31.2", 0x300, 24),
new RomFile("32.2", 0x300, 20),
new RomFile("33.2", 0x300, 16),
new RomFile("34.2", 0x300, 12),
new RomFile("35.2", 0x300, 8),
new RomFile("36.2", 0x300, 4),
new RomFile("37.2", 0x300, 0),
};
new RomFile(Configuration.GetAltoIRomPath("30_23.BIN"), 0x300, 28),
new RomFile(Configuration.GetAltoIRomPath("31_23.BIN"), 0x300, 24),
new RomFile(Configuration.GetAltoIRomPath("32_23.BIN"), 0x300, 20),
new RomFile(Configuration.GetAltoIRomPath("33_23.BIN"), 0x300, 16),
new RomFile(Configuration.GetAltoIRomPath("34_23.BIN"), 0x300, 12),
new RomFile(Configuration.GetAltoIRomPath("35_23.BIN"), 0x300, 8),
new RomFile(Configuration.GetAltoIRomPath("36_23.BIN"), 0x300, 4),
new RomFile(Configuration.GetAltoIRomPath("37_23.BIN"), 0x300, 0),
};
private static RomFile[] _uCodeRomsAltoII =
{
// first K (standard uCode)
new RomFile(Configuration.GetAltoIIRomPath("u55"), 0x000, 28),
new RomFile(Configuration.GetAltoIIRomPath("u64"), 0x000, 24),
new RomFile(Configuration.GetAltoIIRomPath("u65"), 0x000, 20),
new RomFile(Configuration.GetAltoIIRomPath("u63"), 0x000, 16),
new RomFile(Configuration.GetAltoIIRomPath("u53"), 0x000, 12),
new RomFile(Configuration.GetAltoIIRomPath("u60"), 0x000, 8),
new RomFile(Configuration.GetAltoIIRomPath("u61"), 0x000, 4),
new RomFile(Configuration.GetAltoIIRomPath("u62"), 0x000, 0),
// second K (MESA 5.0)
new RomFile(Configuration.GetAltoIIRomPath("u54"), 0x400, 28),
new RomFile(Configuration.GetAltoIIRomPath("u74"), 0x400, 24),
new RomFile(Configuration.GetAltoIIRomPath("u75"), 0x400, 20),
new RomFile(Configuration.GetAltoIIRomPath("u73"), 0x400, 16),
new RomFile(Configuration.GetAltoIIRomPath("u52"), 0x400, 12),
new RomFile(Configuration.GetAltoIIRomPath("u70"), 0x400, 8),
new RomFile(Configuration.GetAltoIIRomPath("u71"), 0x400, 4),
new RomFile(Configuration.GetAltoIIRomPath("u72"), 0x400, 0)
};
private static UInt32[] _uCodeRom;
private static UInt32[] _uCodeRam;

View File

@@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace Contralto
{
/// <summary>
/// The configuration of an Alto II to emulate
/// The configuration of the Alto to emulate
/// </summary>
public enum SystemType
{
@@ -166,6 +166,21 @@ namespace Contralto
/// </summary>
public static bool ThrottleSpeed;
public static string GetAltoIRomPath(string romFileName)
{
return Path.Combine("ROM", "AltoI", romFileName);
}
public static string GetAltoIIRomPath(string romFileName)
{
return Path.Combine("ROM", "AltoII", romFileName);
}
public static string GetRomPath(string romFileName)
{
return Path.Combine("ROM", romFileName);
}
/// <summary>
/// Reads the current configuration file from disk.
///

View File

@@ -258,223 +258,178 @@
<None Include="Disk\xmst76.dsk44">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\0.2">
<None Include="ROM\ACSOURCE.NEW">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\1.2">
<None Include="ROM\ACSOURCE.OLD">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\10.2">
<None Include="ROM\AltoII\C0">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\11.2">
<None Include="ROM\AltoII\C1">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\12.2">
<None Include="ROM\AltoII\C2">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\13.2">
<None Include="ROM\AltoII\C3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\14.2">
<None Include="ROM\AltoII\U52">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\15.2">
<None Include="ROM\AltoII\U53">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\16.2">
<None Include="ROM\AltoII\U54">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\17.2">
<None Include="ROM\AltoII\U55">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\2.2">
<None Include="ROM\AltoII\U60">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\20.2">
<None Include="ROM\AltoII\U61">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\21.2">
<None Include="ROM\AltoII\U62">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\22.2">
<None Include="ROM\AltoII\U63">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\23.2">
<None Include="ROM\AltoII\U64">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\24.2">
<None Include="ROM\AltoII\U65">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\25.2">
<None Include="ROM\AltoII\U70">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\26.2">
<None Include="ROM\AltoII\U71">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\27.2">
<None Include="ROM\AltoII\U72">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\2kctl.u3">
<None Include="ROM\AltoII\U73">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\3.2">
<None Include="ROM\AltoII\U74">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\30.2">
<None Include="ROM\AltoII\U75">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\31.2">
<None Include="ROM\AltoI\00_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\32.2">
<None Include="ROM\AltoI\01_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\33.2">
<None Include="ROM\AltoI\02_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\34.2">
<None Include="ROM\AltoI\03_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\35.2">
<None Include="ROM\AltoI\04_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\36.2">
<None Include="ROM\AltoI\05_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\37.2">
<None Include="ROM\AltoI\06_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\4.2">
<None Include="ROM\AltoI\07_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\5.2">
<None Include="ROM\AltoI\10_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\6.2">
<None Include="ROM\AltoI\11_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\7.2">
<None Include="ROM\AltoI\12_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\C0">
<None Include="ROM\AltoI\13_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\C0.2">
<None Include="ROM\AltoI\14_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\C1">
<None Include="ROM\AltoI\15_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\C1.2">
<None Include="ROM\AltoI\16_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\C2">
<None Include="ROM\AltoI\17_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\C2.2">
<None Include="ROM\AltoI\20_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\C3">
<None Include="ROM\AltoI\21_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\C3.2">
<None Include="ROM\AltoI\22_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U52">
<None Include="ROM\AltoI\23_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U52_2k">
<None Include="ROM\AltoI\24_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U53">
<None Include="ROM\AltoI\25_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U53_2k">
<None Include="ROM\AltoI\26_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U54">
<None Include="ROM\AltoI\27_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U54_2k">
<None Include="ROM\AltoI\30_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U55">
<None Include="ROM\AltoI\31_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U55_2k">
<None Include="ROM\AltoI\32_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U60">
<None Include="ROM\AltoI\33_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U60_2k">
<None Include="ROM\AltoI\34_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U61">
<None Include="ROM\AltoI\35_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U61_2k">
<None Include="ROM\AltoI\36_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U62">
<None Include="ROM\AltoI\37_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U62_2k">
<None Include="ROM\AltoI\C0_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U63">
<None Include="ROM\AltoI\C1_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U63_2k">
<None Include="ROM\AltoI\C2_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U64">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U64_2k">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U65">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U65_2k">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U70">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U70_2k">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U71">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U71_2k">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U72">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U72_2k">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U73">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U73_2k">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U74">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U74_2k">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U75">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ROM\U75_2k">
<None Include="ROM\AltoI\C3_23.BIN">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

View File

@@ -91,6 +91,9 @@ namespace Contralto.Display
_system.Scheduler.Schedule(_verticalBlankScanlineWakeup);
}
/// <summary>
/// Begins the next display field.
/// </summary>
private void FieldStart()
{
// Start of Vertical Blanking (end of last field). This lasts for 34 scanline times or so.
@@ -117,6 +120,12 @@ namespace Contralto.Display
_system.Scheduler.Schedule(_verticalBlankScanlineWakeup);
}
/// <summary>
/// Callback for each scanline during vblank.
/// </summary>
/// <param name="timeNsec"></param>
/// <param name="skewNsec"></param>
/// <param name="context"></param>
private void VerticalBlankScanlineCallback(ulong timeNsec, ulong skewNsec, object context)
{
// End of VBlank scanline.
@@ -157,6 +166,12 @@ namespace Contralto.Display
}
}
/// <summary>
/// Callback for the end of each horizontal blank period.
/// </summary>
/// <param name="timeNsec"></param>
/// <param name="skewNsec"></param>
/// <param name="context"></param>
private void HorizontalBlankEndCallback(ulong timeNsec, ulong skewNsec, object context)
{
// Reset scanline word counter
@@ -182,6 +197,12 @@ namespace Contralto.Display
_system.Scheduler.Schedule(_wordWakeup);
}
/// <summary>
/// Callback for each word of visible display lines.
/// </summary>
/// <param name="timeNsec"></param>
/// <param name="skewNsec"></param>
/// <param name="context"></param>
private void WordCallback(ulong timeNsec, ulong skewNsec, object context)
{
if (_display == null)
@@ -268,6 +289,10 @@ namespace Contralto.Display
}
}
/// <summary>
/// Check to see if a Display Word task wakeup should be generated based on the current
/// state of the FIFO and task wakeup bits.
/// </summary>
private void CheckWordWakeup()
{
if (FIFOFULL ||
@@ -290,6 +315,10 @@ namespace Contralto.Display
}
}
/// <summary>
/// Enqueues a display word on the display controller's FIFO.
/// </summary>
/// <param name="word"></param>
public void LoadDDR(ushort word)
{
_dataBuffer.Enqueue(word);
@@ -303,6 +332,10 @@ namespace Contralto.Display
CheckWordWakeup();
}
/// <summary>
/// Loads the X position register for the cursor
/// </summary>
/// <param name="word"></param>
public void LoadXPREG(ushort word)
{
if (!_cursorXLatch)
@@ -312,6 +345,10 @@ namespace Contralto.Display
}
}
/// <summary>
/// Loads the cursor register
/// </summary>
/// <param name="word"></param>
public void LoadCSR(ushort word)
{
if (!_cursorRegLatch)
@@ -321,6 +358,10 @@ namespace Contralto.Display
}
}
/// <summary>
/// Sets the mode (low res and white on black bits)
/// </summary>
/// <param name="word"></param>
public void SETMODE(ushort word)
{
// These take effect at the beginning of the next scanline.
@@ -358,7 +399,7 @@ namespace Contralto.Display
private ushort _cursorX;
private ushort _cursorXLatched;
// Indicates whether the DWT or DHT blocked itself
// Indicates whether the DWT or DHT blocked themselves
// in which case they cannot be reawakened until the next field.
private bool _dwtBlocked;
private bool _dhtBlocked;

View File

@@ -6,6 +6,10 @@ using System.Threading.Tasks;
namespace Contralto.Display
{
/// <summary>
/// IAltoDisplay defines the interface necessary for generating and rendering the Alto's
/// bitmapped display.
/// </summary>
public interface IAltoDisplay
{
/// <summary>
@@ -18,7 +22,7 @@ namespace Contralto.Display
void DrawDisplayWord(int scanline, int wordOffset, ushort dataWord, bool lowRes);
/// <summary>
/// Renders the cursor word for the specified scanline
/// Renders the cursor word for the specified scanline.
/// </summary>
/// <param name="scanline"></param>
/// <param name="wordOffset"></param>
@@ -27,7 +31,7 @@ namespace Contralto.Display
void DrawCursorWord(int scanline, int xOffset, bool whiteOnBlack, ushort cursorWord);
/// <summary>
/// Causes the display to be rendered
/// Indicates that an entire frame is ready for display and should be rendered.
/// </summary>
void Render();
}

View File

@@ -9,7 +9,9 @@ using System.Threading.Tasks;
namespace Contralto.IO
{
// The data for the current sector
/// <summary>
/// Defines the "type" of data in the current sector timeslice.
/// </summary>
public enum CellType
{
Data,
@@ -17,6 +19,11 @@ namespace Contralto.IO
Sync,
}
/// <summary>
/// Represents the data (or lack thereof) in the current sector timeslice.
/// This may be actual data (header, label, or data), interrecord gaps, or
/// sync words.
/// </summary>
public struct DataCell
{
public DataCell(ushort data, CellType type)

View File

@@ -4,6 +4,9 @@ using System.IO;
namespace Contralto.IO
{
/// <summary>
/// DiskGeometry encapsulates the geometry of a disk.
/// </summary>
public struct DiskGeometry
{
public DiskGeometry(int cylinders, int tracks, int sectors)
@@ -24,6 +27,10 @@ namespace Contralto.IO
Diablo44
}
/// <summary>
/// DiabloDiskSector encapsulates the records contained in a single Alto disk sector
/// on a Diablo disk. This includes the header, label, and data records.
/// </summary>
public class DiabloDiskSector
{
public DiabloDiskSector(byte[] header, byte[] label, byte[] data)
@@ -106,13 +113,14 @@ namespace Contralto.IO
// Bitsavers images have an extra word in the header for some reason.
// ignore it.
// TODO: should support different formats ("correct" raw, Alto CopyDisk format, etc.)
//
//
imageStream.Seek(2, SeekOrigin.Current);
if (imageStream.Read(header, 0, header.Length) != header.Length)
{
throw new InvalidOperationException("Short read while reading sector header.");
}
}
if (imageStream.Read(label, 0, label.Length) != label.Length)
{

View File

@@ -15,6 +15,10 @@ namespace Contralto.IO
public delegate void DiskActivity(DiskActivityType activity);
/// <summary>
/// DiskController provides an implementation for the logic in the standard Alto disk controller,
/// which talks to a Diablo model 31 or 44 removable pack drive.
/// </summary>
public class DiskController
{
public DiskController(AltoSystem system)
@@ -28,11 +32,7 @@ namespace Contralto.IO
Reset();
}
/// <summary>
/// According to docs, on a Write, eventually it appears on the Read side during an actual write to the disk
/// but not right away.
/// </summary>
public ushort KDATA
{
get
@@ -165,37 +165,7 @@ namespace Contralto.IO
public bool DataXfer
{
get { return _dataXfer; }
}
public int Cylinder
{
get { return SelectedDrive.Cylinder; }
}
public int SeekCylinder
{
get { return _destCylinder; }
}
public int Head
{
get { return SelectedDrive.Head; }
}
public int Sector
{
get { return SelectedDrive.Sector; }
}
public int Drive
{
get { return _disk; }
}
public double ClocksUntilNextSector
{
get { return 0; } // _sectorClocks - _elapsedSectorTime; }
}
}
public bool Ready
{
@@ -292,6 +262,12 @@ namespace Contralto.IO
_seclateEnable = false;
}
/// <summary>
/// Called back 12 times per rotation of the disk to kick off a new disk sector.
/// </summary>
/// <param name="timeNsec"></param>
/// <param name="skewNsec"></param>
/// <param name="context"></param>
private void SectorCallback(ulong timeNsec, ulong skewNsec, object context)
{
//
@@ -351,6 +327,12 @@ namespace Contralto.IO
}
}
/// <summary>
/// Called back for every word time in this sector.
/// </summary>
/// <param name="timeNsec"></param>
/// <param name="skewNsec"></param>
/// <param name="context"></param>
private void WordCallback(ulong timeNsec, ulong skewNsec, object context)
{
SpinDisk();
@@ -369,6 +351,13 @@ namespace Contralto.IO
}
}
/// <summary>
/// Called back if the sector task doesn't start in time, providing SECLATE
/// semantics.
/// </summary>
/// <param name="timeNsec"></param>
/// <param name="skewNsec"></param>
/// <param name="context"></param>
private void SeclateCallback(ulong timeNsec, ulong skewNsec, object context)
{
if (_seclateEnable)
@@ -405,6 +394,9 @@ namespace Contralto.IO
}
}
/// <summary>
/// Starts a seek operation.
/// </summary>
public void Strobe()
{
//
@@ -487,7 +479,7 @@ namespace Contralto.IO
// for lead-in or inter-record delays, but the slices are still used to
// keep things in line time-wise; the real hardware uses a crystal-controlled clock
// to generate these slices during these periods (and the clock comes from the
// disk itself when actual data is present). For our purposes, the two clocks
// drive itself when actual data is present). For our purposes, the two clocks
// are one and the same.
//

View File

@@ -9,6 +9,9 @@ using System.Threading.Tasks;
namespace Contralto.IO
{
/// <summary>
/// EthernetController implements the logic for the Alto's 3Mbit Ethernet controller.
/// </summary>
public class EthernetController
{
public EthernetController(AltoSystem system)
@@ -345,7 +348,7 @@ namespace Contralto.IO
/// <summary>
/// Invoked when the host ethernet interface receives a packet destined for us.
/// NOTE: This runs on the PCap receiver thread (see HostEthernet), not the main emulator thread.
/// NOTE: This runs on the PCap or UDP receiver thread, not the main emulator thread.
/// Any access to emulator structures must be properly protected.
///
/// Due to the nature of the "ethernet" we're simulating, there will never be any collisions or corruption and
@@ -502,18 +505,7 @@ namespace Contralto.IO
// Schedule the next wakeup.
_inputPollEvent.TimestampNsec = _inputPollPeriod - skewNsec;
_system.Scheduler.Schedule(_inputPollEvent);
}
private void LogPacket(int length, MemoryStream packet)
{
Log.Write(LogComponent.EthernetPacket,
" - Packet src {0}, dest {1}, length {2}",
packet.ReadByte(), packet.ReadByte(), length);
// Return to top of packet
packet.Position = 2;
}
}
private Queue<ushort> _fifo;
@@ -555,11 +547,11 @@ namespace Contralto.IO
private const int _maxQueuedPackets = 32;
// The actual connection to a real network device of some sort on the host
IPacketEncapsulation _hostInterface;
private IPacketEncapsulation _hostInterface;
// Buffer to hold outgoing data to the host ethernet
ushort[] _outputData;
int _outputIndex;
private ushort[] _outputData;
private int _outputIndex;
// Incoming data and locking
private MemoryStream _incomingPacket;

View File

@@ -15,7 +15,9 @@ using System.Threading;
namespace Contralto.IO
{
/// <summary>
/// Represents a host ethernet interface.
/// </summary>
public struct EthernetInterface
{
public EthernetInterface(string name, string description)
@@ -249,19 +251,8 @@ namespace Contralto.IO
}
else
{
// Check addressing table for external (non emulator) addresses;
// otherwise just address other emulators
// TODO: implement table. Currently hardcoded address 1 to test IFS on dev machine
//
if (destinationHost == 1)
{
destinationMac = new MacAddress((UInt48)(_ifsTestMAC));
}
else
{
destinationMac = new MacAddress((UInt48)(_10mbitMACPrefix | destinationHost)); // emulator destination address
}
// Build 10mbit address from 3mbit
destinationMac = new MacAddress((UInt48)(_10mbitMACPrefix | destinationHost)); // emulator destination address
}
return destinationMac;
@@ -285,9 +276,6 @@ namespace Contralto.IO
private UInt48 _10mbitMACPrefix = 0x0000aa010200; // 00-00-AA is the Xerox vendor code, used just to be cute.
private UInt48 _10mbitBroadcast = (UInt48)0xffffffffffff;
private const int _3mbitBroadcast = 0;
// Temporary; to be replaced with an external address mapping table
private UInt48 _ifsTestMAC = (UInt48)0x001060b88e3e;
private const int _3mbitBroadcast = 0;
}
}

View File

@@ -5,6 +5,10 @@ namespace Contralto.IO
{
public delegate void ReceivePacketDelegate(MemoryStream data);
/// <summary>
/// Provides a generic interface for host network devices that can encapsulate
/// Alto ethernet packets.
/// </summary>
public interface IPacketEncapsulation
{
/// <summary>
@@ -14,14 +18,14 @@ namespace Contralto.IO
void RegisterReceiveCallback(ReceivePacketDelegate callback);
/// <summary>
/// Sends the specified word array
/// Sends the specified word array over the device.
/// </summary>
/// <param name="packet"></param>
/// <param name="length"></param>
void Send(ushort[] packet, int length);
/// <summary>
/// Shuts down the encapsulation provider
/// Shuts down the encapsulation provider.
/// </summary>
void Shutdown();
}

View File

@@ -5,7 +5,9 @@ using Contralto.CPU;
namespace Contralto.IO
{
/// <summary>
/// The keys on the Alto keyboard.
/// </summary>
public enum AltoKey
{
None = 0,
@@ -72,6 +74,9 @@ namespace Contralto.IO
BlankBottom,
}
/// <summary>
/// Specifies the word and bitmask for a given Alto keyboard key.
/// </summary>
public struct AltoKeyBit
{
public AltoKeyBit(int word, ushort mask)

View File

@@ -17,6 +17,9 @@ namespace Contralto.IO
Left = 0x4,
}
/// <summary>
/// Implements the hardware for the standard Alto mouse.
/// </summary>
public class Mouse : IMemoryMappedDevice
{
public Mouse()
@@ -27,10 +30,7 @@ namespace Contralto.IO
public void Reset()
{
// cheat for synchronization: this is approximately where the mouse is initialized to
// at alto boot.
_mouseY = 80;
_mouseX = 0;
}
public ushort Read(int address, TaskType task, bool extendedMemoryReference)

View File

@@ -168,9 +168,6 @@ namespace Contralto.IO
private void ReceiveThread()
{
// Just call ReceivePackets, that's it. This will never return.
// (probably need to make this more elegant so we can tear down the thread
// properly.)
Log.Write(LogComponent.HostNetworkInterface, "UDP Receiver thread started.");
IPEndPoint groupEndPoint = new IPEndPoint(IPAddress.Any, _udpPort);

View File

@@ -3,6 +3,10 @@ using Contralto.Logging;
namespace Contralto.Memory
{
/// <summary>
/// Implements the Alto's main memory, up to 4 banks of 64KW in 16-bit words.
/// Provides implementation of the IIXM's memory mapping hardware.
/// </summary>
public class Memory : IMemoryMappedDevice
{
public Memory()

View File

@@ -14,13 +14,14 @@ namespace Contralto.Memory
/// <summary>
/// Implements the memory bus and memory timings for the Alto system.
/// TODO: Currently only implements timings for Alto II systems.
/// This implements timings for both Alto I and Alto II systems.
/// </summary>
public class MemoryBus : IClockable
public sealed class MemoryBus : IClockable
{
public MemoryBus()
{
_bus = new Dictionary<ushort, IMemoryMappedDevice>(65536);
_systemType = Configuration.SystemType;
Reset();
}
@@ -106,25 +107,59 @@ namespace Contralto.Memory
{
_memoryCycle++;
if (_memoryOperationActive)
{
switch (_memoryCycle)
{
if (_systemType == SystemType.AltoI)
{
case 3:
// Buffered read of single word
_memoryData = ReadFromBus(_memoryAddress, _task, _extendedMemoryReference);
break;
case 4:
// Buffered read of double-word
_memoryData2 = ReadFromBus((ushort)(_memoryAddress ^ 1), _task, _extendedMemoryReference);
break;
case 5:
// End of memory operation
_memoryOperationActive = false;
_doubleWordStore = false;
break;
ClockAltoI();
}
else
{
ClockAltoII();
}
}
}
private void ClockAltoI()
{
switch (_memoryCycle)
{
case 4:
// Buffered read of single word
_memoryData = ReadFromBus(_memoryAddress, _task, _extendedMemoryReference);
break;
case 5:
// Buffered read of double-word
_memoryData2 = ReadFromBus((ushort)(_memoryAddress | 1), _task, _extendedMemoryReference);
break;
case 7:
// End of memory operation
_memoryOperationActive = false;
_doubleWordStore = false;
break;
}
}
private void ClockAltoII()
{
switch (_memoryCycle)
{
case 3:
// Buffered read of single word
_memoryData = ReadFromBus(_memoryAddress, _task, _extendedMemoryReference);
break;
case 4:
// Buffered read of double-word
_memoryData2 = ReadFromBus((ushort)(_memoryAddress ^ 1), _task, _extendedMemoryReference);
break;
case 5:
// End of memory operation
_memoryOperationActive = false;
_doubleWordStore = false;
break;
}
}
@@ -143,8 +178,16 @@ namespace Contralto.Memory
return _memoryCycle > 4;
case MemoryOperation.Store:
// Write operations take place on cycles 3 and 4
return _memoryCycle > 2;
if (_systemType == SystemType.AltoI)
{
// // Store operations take place on cycles 5 and 6
return _memoryCycle > 4;
}
else
{
// Store operations take place on cycles 3 and 4
return _memoryCycle > 2;
}
default:
throw new InvalidOperationException(String.Format("Unexpected memory operation {0}", op));
@@ -161,7 +204,8 @@ namespace Contralto.Memory
{
if (_memoryOperationActive)
{
// This should not happen; CPU should check whether the operation is possible using Ready and stall if not.
// This should not happen; CPU implementation should check whether the operation is possible
// using Ready and stall if not.
throw new InvalidOperationException("Invalid LoadMAR request during active memory operation.");
}
else
@@ -177,31 +221,71 @@ namespace Contralto.Memory
}
public ushort ReadMD()
{
if (_systemType == SystemType.AltoI)
{
return ReadMDAltoI();
}
else
{
return ReadMDAltoII();
}
}
private ushort ReadMDAltoI()
{
if (_memoryOperationActive)
{
switch (_memoryCycle)
{
case 1:
case 1:
case 2:
// TODO: good microcode should never do this
throw new InvalidOperationException("Unexpected microcode behavior -- ReadMD too soon after start of memory cycle.");
case 3:
case 4:
// This should not happen; CPU should check whether the operation is possible using Ready and stall if not.
throw new InvalidOperationException("Invalid ReadMR request during cycle 3 or 4 of memory operation.");
case 5:
// Single word read
return _memoryData;
case 6:
// Double word read, return other half of double word.
return _memoryData2;
default:
// Invalid state.
throw new InvalidOperationException(string.Format("Unexpected memory cycle {0} in memory state machine.", _memoryCycle));
}
}
else
{
// The Alto I does not latch memory contents, an <-MD operation returns undefined results
return 0xffff;
}
}
private ushort ReadMDAltoII()
{
if (_memoryOperationActive)
{
switch (_memoryCycle)
{
case 1:
case 2:
// TODO: good microcode should never do this
throw new InvalidOperationException("Unexpected microcode behavior -- ReadMD too soon after start of memory cycle.");
case 3:
case 4:
// This should not happen; CPU should check whether the operation is possible using Ready and stall if not.
throw new InvalidOperationException("Invalid ReadMR request during cycle 3 or 4 of memory operation.");
throw new InvalidOperationException("Invalid ReadMR request during cycle 3 or 4 of memory operation.");
case 5:
// Single word read
//Log.Write(LogType.Verbose, LogComponent.Memory, "Single-word read of {0} from {1} (cycle 5)", Conversion.ToOctal(_memoryData), Conversion.ToOctal(_memoryAddress ^ 1));
// debug
/*
if (_memoryAddress == 0xfc90 && _task != TaskType.Emulator) // 176220 -- status word for disk
{
Logging.Log.Write(Logging.LogComponent.Debug, "--> Task {0} read {1} from 176220.", _task, _memoryData);
} */
return _memoryData;
// Single word read
return _memoryData;
// ***
// NB: Handler for double-word read (cycle 6) is in the "else" clause below; this is kind of a hack.
@@ -219,104 +303,97 @@ namespace Contralto.Memory
// cycle 5 of a reference and obtain the results of the read operation")
// If this is memory cycle 6 we will return the last half of the doubleword to complete a double-word read.
if (_memoryCycle == 6 || (_memoryCycle == 5 && _doubleWordMixed))
{
//Log.Write(LogType.Verbose, LogComponent.Memory, "Double-word read of {0} from {1} (cycle 6)", Conversion.ToOctal(_memoryData2), Conversion.ToOctal(_memoryAddress ^ 1));
_doubleWordMixed = false;
// debug
/*
if ((_memoryAddress ^ 1) == 0xfc90 && _task != TaskType.Emulator) // 176220 -- status word for disk
{
Logging.Log.Write(Logging.LogComponent.Debug, "--> Task {0} read {1} from 176220.", _task, _memoryData2);
} */
{
_doubleWordMixed = false;
return _memoryData2;
}
else
{
_doubleWordMixed = false;
// debug
/*
if (_memoryAddress == 0xfc90 && _task != TaskType.Emulator) // 176220 -- status word for disk
{
Logging.Log.Write(Logging.LogComponent.Debug, "--> Task {0} read {1} from 176220.", _task, _memoryData);
} */
//Log.Write(LogType.Verbose, LogComponent.Memory, "Single-word read of {0} from {1} (post cycle 6)", Conversion.ToOctal(_memoryData), Conversion.ToOctal(_memoryAddress));
_doubleWordMixed = false;
return _memoryData;
}
}
}
}
public void LoadMD(ushort data)
{
if (_memoryOperationActive)
{
switch (_memoryCycle)
if (_systemType == SystemType.AltoI)
{
case 1:
case 2:
case 5:
// TODO: good microcode should never do this
throw new InvalidOperationException("Unexpected microcode behavior -- LoadMD during incorrect memory cycle.");
case 3:
// debug
/*
if (_memoryAddress == 0xfc90 || _memoryAddress == 0xfc91 || _memoryAddress == 0x151) // 176220 -- status word for disk
{
Logging.Log.Write(Logging.LogComponent.Debug, "++> Task {0} wrote {1} to {3} (was {2}).", _task, Conversion.ToOctal(data), Conversion.ToOctal(DebugReadWord(_task, _memoryAddress)), Conversion.ToOctal(_memoryAddress));
}*/
_memoryData = data; // Only really necessary to show in debugger
// Start of doubleword write:
WriteToBus(_memoryAddress, data, _task, _extendedMemoryReference);
_doubleWordStore = true;
_doubleWordMixed = true;
/*
Log.Write(
LogType.Verbose,
LogComponent.Memory,
"Single-word store of {0} to {1} (cycle 3)",
Conversion.ToOctal(data),
Conversion.ToOctal(_memoryAddress)); */
break;
case 4:
_memoryData = data; // Only really necessary to show in debugger
/*
Log.Write(
LogType.Verbose,
LogComponent.Memory,
_doubleWordStore ? "Double-word store of {0} to {1} (cycle 4)" : "Single-word store of {0} to {1} (cycle 4)",
Conversion.ToOctal(data),
_doubleWordStore ? Conversion.ToOctal(_memoryAddress ^ 1) : Conversion.ToOctal(_memoryAddress));
*/
// debug
ushort actualAddress = _doubleWordStore ? (ushort)(_memoryAddress ^ 1) : _memoryAddress;
/*
if (actualAddress == 0xfc90 || actualAddress == 0xfc91 || _memoryAddress == 0x151) // 176220 -- status word for disk
{
Logging.Log.Write(Logging.LogComponent.Debug, "--> Task {0} wrote {1} to {4} (was {2}). Extd {3}", _task, Conversion.ToOctal(data), Conversion.ToOctal(DebugReadWord(_task, actualAddress)), _extendedMemoryReference, Conversion.ToOctal(actualAddress));
} */
WriteToBus(actualAddress, data, _task, _extendedMemoryReference);
/*
if (actualAddress == 0xfc90 || actualAddress == 0xfc91 || _memoryAddress == 0x151) // 176220 -- status word for disk
{
Logging.Log.Write(Logging.LogComponent.Debug, "--> Now {0}.", Conversion.ToOctal(DebugReadWord(_task, actualAddress)));
} */
break;
}
LoadMDAltoI(data);
}
else
{
LoadMDAltoII(data);
}
}
}
private void LoadMDAltoI(ushort data)
{
switch (_memoryCycle)
{
case 1:
case 2:
case 3:
case 4:
// TODO: good microcode should never do this
throw new InvalidOperationException("Unexpected microcode behavior -- LoadMD during incorrect memory cycle.");
case 5:
_memoryData = data; // Only really necessary to show in debugger
// Start of doubleword write:
WriteToBus(_memoryAddress, data, _task, _extendedMemoryReference);
_doubleWordStore = true;
_doubleWordMixed = true;
break;
case 6:
if (!_doubleWordStore)
{
throw new InvalidOperationException("Unexpected microcode behavior -- LoadMD on cycle 6, no LoadMD on cycle 5.");
}
_memoryData = data; // Only really necessary to show in debugger
ushort actualAddress = (ushort)(_memoryAddress | 1);
WriteToBus(actualAddress, data, _task, _extendedMemoryReference);
break;
}
}
private void LoadMDAltoII(ushort data)
{
switch (_memoryCycle)
{
case 1:
case 2:
case 5:
// TODO: good microcode should never do this
throw new InvalidOperationException("Unexpected microcode behavior -- LoadMD during incorrect memory cycle.");
case 3:
_memoryData = data; // Only really necessary to show in debugger
// Start of doubleword write:
WriteToBus(_memoryAddress, data, _task, _extendedMemoryReference);
_doubleWordStore = true;
_doubleWordMixed = true;
break;
case 4:
_memoryData = data; // Only really necessary to show in debugger
ushort actualAddress = _doubleWordStore ? (ushort)(_memoryAddress ^ 1) : _memoryAddress;
WriteToBus(actualAddress, data, _task, _extendedMemoryReference);
break;
}
}
/// <summary>
/// Dispatches reads to memory mapped hardware (RAM, I/O)
/// </summary>
@@ -340,8 +417,7 @@ namespace Contralto.Memory
return memoryMappedDevice.Read(address, task, extendedMemoryReference);
}
else
{
//throw new NotImplementedException(String.Format("Read from unimplemented memory-mapped I/O device at {0}.", OctalHelpers.ToOctal(address)));
{
return 0;
}
}
@@ -369,11 +445,7 @@ namespace Contralto.Memory
if (_bus.TryGetValue(address, out memoryMappedDevice))
{
memoryMappedDevice.Load(address, data, task, extendedMemoryReference);
}
else
{
// throw new NotImplementedException(String.Format("Write to unimplemented memory-mapped I/O device at {0}.", OctalHelpers.ToOctal(address)));
}
}
}
}
@@ -382,6 +454,11 @@ namespace Contralto.Memory
/// </summary>
private Dictionary<ushort, IMemoryMappedDevice> _bus;
/// <summary>
/// Cache the system type since we rely on it
/// </summary>
private SystemType _systemType;
//
// Optimzation: keep reference to main memory; since 99.9999% of accesses go directly there,
// we can avoid the hashtable overhead using a simple address check.

View File

@@ -103,7 +103,6 @@ namespace Contralto
}
}
private static AltoSystem _system;
private static ManualResetEvent _closeEvent;
private static AltoSystem _system;
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,7 +0,0 @@
  
   


  



View File

@@ -1,17 +0,0 @@
 
   

 
 
 




  

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,3 +0,0 @@
    
   
      

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,3 +0,0 @@
 


View File

@@ -1,28 +0,0 @@

 



 

 

 
 
 
  

 







 
 

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Contralto/ROM/ACSOURCE.NEW Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,5 @@
 


     
 

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,8 @@

   
  

  




View File

@@ -0,0 +1,17 @@


 
   

 
 
 





Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,4 @@
  
    
   
      

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,11 @@


 
 
 

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,3 @@
   
 


View File

@@ -0,0 +1,35 @@
 

 


 

  



 

 
 
 
  

 







 

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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