diff --git a/Contralto/CPU/NovaDisassembler.cs b/Contralto/CPU/NovaDisassembler.cs new file mode 100644 index 0000000..f19b466 --- /dev/null +++ b/Contralto/CPU/NovaDisassembler.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contralto.CPU +{ + /// + /// Quick and dirty disassembler for Nova instructions, so we can + /// see what the emulator task is executing more clearly. + /// + public static class NovaDisassembler + { + + } +} diff --git a/Contralto/CPU/Tasks/EmulatorTask.cs b/Contralto/CPU/Tasks/EmulatorTask.cs index 65b4d09..82448a9 100644 --- a/Contralto/CPU/Tasks/EmulatorTask.cs +++ b/Contralto/CPU/Tasks/EmulatorTask.cs @@ -127,7 +127,7 @@ namespace Contralto.CPU // Conditions ORed onto NEXT Comment // // if IR[0] = 1 3-IR[8-9] complement of SH field of IR - // elseif IR[1-2] = 0 IR[3-4] JMP, JSR, ISZ, DSZ + // elseif IR[1-2] = 0 IR[3-4] JMP, JSR, ISZ, DSZ ; dispatch selects register // elseif IR[1-2] = 1 4 LDA // elseif IR[1-2] = 2 5 STA // elseif IR[4-7] = 0 1 @@ -145,11 +145,11 @@ namespace Contralto.CPU { _nextModifier = (ushort)((_cpu._ir & 0x1800) >> 11); } - else if ((_cpu._ir & 0x6000) == 0x4000) + else if ((_cpu._ir & 0x6000) == 0x2000) { _nextModifier = 4; } - else if ((_cpu._ir & 0x6000) == 0x6000) + else if ((_cpu._ir & 0x6000) == 0x4000) { _nextModifier = 5; } diff --git a/Contralto/CPU/Disassembler.cs b/Contralto/CPU/UCodeDisassembler.cs similarity index 99% rename from Contralto/CPU/Disassembler.cs rename to Contralto/CPU/UCodeDisassembler.cs index ab30c6f..412086a 100644 --- a/Contralto/CPU/Disassembler.cs +++ b/Contralto/CPU/UCodeDisassembler.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; namespace Contralto.CPU { // BUG: register assignments should come from L (not 0) - public static class Disassembler + public static class UCodeDisassembler { /// diff --git a/Contralto/Contralto.csproj b/Contralto/Contralto.csproj index 6a64338..85897e0 100644 --- a/Contralto/Contralto.csproj +++ b/Contralto/Contralto.csproj @@ -47,7 +47,8 @@ - + + @@ -62,6 +63,7 @@ + @@ -76,6 +78,9 @@ PreserveNewest + + Always + Always diff --git a/Contralto/Disk/games.dsk b/Contralto/Disk/games.dsk new file mode 100644 index 0000000..946f0e7 Binary files /dev/null and b/Contralto/Disk/games.dsk differ diff --git a/Contralto/IO/DiabloPack.cs b/Contralto/IO/DiabloPack.cs new file mode 100644 index 0000000..2b583af --- /dev/null +++ b/Contralto/IO/DiabloPack.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contralto.IO +{ + + public struct DiskGeometry + { + public DiskGeometry(int cylinders, int tracks, int sectors) + { + Cylinders = cylinders; + Tracks = tracks; + Sectors = sectors; + } + + public int Cylinders; + public int Tracks; + public int Sectors; + } + + public enum DiabloDiskType + { + Diablo31, + Diablo44 + } + + public class DiabloDiskSector + { + public DiabloDiskSector(byte[] header, byte[] label, byte[] data) + { + if (header.Length != 4 || + label.Length != 16 || + data.Length != 512) + { + throw new InvalidOperationException("Invalid sector header/label/data length."); + } + + Header = GetUShortArray(header); + Label = GetUShortArray(label); + Data = GetUShortArray(data); + } + + private ushort[] GetUShortArray(byte[] data) + { + if ((data.Length % 2) != 0) + { + throw new InvalidOperationException("Array length must be even."); + } + + ushort[] array = new ushort[data.Length / 2]; + + int offset = 0; + for(int i=0;i + /// Encapsulates disk image data for all disk packs used with the + /// standard Alto Disk Controller (i.e. the 31 and 44, which differ + /// only in the number of cylinders) + /// + public class DiabloPack + { + public DiabloPack(DiabloDiskType type) + { + _diskType = type; + _geometry = new DiskGeometry(type == DiabloDiskType.Diablo31 ? 203 : 406, 2, 12); + _sectors = new DiabloDiskSector[_geometry.Cylinders, _geometry.Tracks, _geometry.Sectors]; + } + + public DiskGeometry Geometry + { + get { return _geometry; } + } + + public void Load(Stream imageStream) + { + for(int cylinder = 0; cylinder < _geometry.Cylinders; cylinder++) + { + for(int track = 0; track < _geometry.Tracks; track++) + { + for(int sector = 0; sector < _geometry.Sectors; sector++) + { + byte[] header = new byte[4]; // 2 words + byte[] label = new byte[16]; // 8 words + byte[] data = new byte[512]; // 256 words + + // + // 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) + { + throw new InvalidOperationException("Short read while reading sector label."); + } + + if (imageStream.Read(data, 0, data.Length) != data.Length) + { + throw new InvalidOperationException("Short read while reading sector data."); + } + + _sectors[cylinder, track, sector] = new DiabloDiskSector(header, label, data); + } + } + } + + if (imageStream.Position != imageStream.Length) + { + throw new InvalidOperationException("Extra data at end of image file."); + } + } + + public DiabloDiskSector GetSector(int cylinder, int track, int sector) + { + return _sectors[cylinder, track, sector]; + } + + private DiabloDiskSector[,,] _sectors; + private DiabloDiskType _diskType; + private DiskGeometry _geometry; + } +} diff --git a/Contralto/IO/DiskController.cs b/Contralto/IO/DiskController.cs index 5119aef..dded8f3 100644 --- a/Contralto/IO/DiskController.cs +++ b/Contralto/IO/DiskController.cs @@ -5,6 +5,7 @@ using System.Text; using System.Threading.Tasks; using Contralto.Memory; +using System.IO; namespace Contralto.IO { @@ -15,10 +16,18 @@ namespace Contralto.IO _system = system; Reset(); - // Wakeup the sector task first thing - _system.CPU.WakeupTask(CPU.TaskType.DiskSector); + // Load the pack + _pack = new DiabloPack(DiabloDiskType.Diablo31); - + // TODO: this does not belong here. + FileStream fs = new FileStream("Disk\\games.dsk", FileMode.Open, FileAccess.Read); + + _pack.Load(fs); + + fs.Close(); + + // Wakeup the sector task first thing + _system.CPU.WakeupTask(CPU.TaskType.DiskSector); } public ushort KDATA @@ -419,31 +428,37 @@ namespace Contralto.IO private void LoadSector() { - // Fill in sector with test data; eventually actually load real disk data! + // + // Pull data off disk and pack it into our faked-up sector. + // Note that this data is packed in in REVERSE ORDER because that's + // how it gets written out and it's how the Alto expects it to be read back in. + // + DiabloDiskSector sector = _pack.GetSector(_cylinder, _head, _sector); + // Header (2 words data, 1 word cksum) - for (int i = _headerOffset + 1; i < _headerOffset + 3; i++) + for (int i = _headerOffset + 1, j = 1; i < _headerOffset + 3; i++, j--) { // actual data to be loaded from disk / cksum calculated - _sectorData[i] = new DataCell(0xbeef, CellType.Data); + _sectorData[i] = new DataCell(sector.Header[j], CellType.Data); } _sectorData[_headerOffset + 3].Data = CalculateChecksum(_sectorData, _headerOffset + 1, 2); // Label (8 words data, 1 word cksum) - for (int i = _labelOffset + 1; i < _labelOffset + 9; i++) + for (int i = _labelOffset + 1, j = 7; i < _labelOffset + 9; i++, j--) { // actual data to be loaded from disk / cksum calculated - _sectorData[i] = new DataCell(0xdead, CellType.Data); + _sectorData[i] = new DataCell(sector.Label[j], CellType.Data); } _sectorData[_labelOffset + 9].Data = CalculateChecksum(_sectorData, _labelOffset + 1, 8); // sector data (256 words data, 1 word cksum) - for (int i = _dataOffset + 1; i < _dataOffset + 257; i++) + for (int i = _dataOffset + 1, j = 255; i < _dataOffset + 257; i++, j--) { // actual data to be loaded from disk / cksum calculated - _sectorData[i] = new DataCell((ushort)(0x7000 + i), CellType.Data); + _sectorData[i] = new DataCell(sector.Data[j], CellType.Data); } _sectorData[_dataOffset + 257].Data = CalculateChecksum(_sectorData, _dataOffset + 1, 256); @@ -454,7 +469,6 @@ namespace Contralto.IO { // Fill in sector with default data (basically, fill in non-data areas). - // // header delay, 22 words for (int i=0; i < _headerOffset; i++) @@ -602,6 +616,8 @@ namespace Contralto.IO private double _elapsedSeekTime; private double _seekClocks; + // The pack loaded into the drive + DiabloPack _pack; private AltoSystem _system; }