diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index 0d750bc64c..6ddbccedbc 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -87,7 +87,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// Sets up the ROM /// /// - /// public abstract void InitROM(RomData romData); /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 67f26496c0..9544610a3a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -12,26 +12,53 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public abstract partial class SpectrumBase { - // 128 and up only - //protected int ROMPaged = 0; - + /// + /// Index of the currently paged ROM + /// protected int ROMPaged; - - public int _ROMpaged + public virtual int _ROMpaged { get { return ROMPaged; } set { ROMPaged = value; } } - + /// + /// Signs that the shadow screen has been paged in + /// protected bool SHADOWPaged; + + /// + /// Index of the current RAM page + /// public int RAMPaged; + + /// + /// Signs that all paging is disabled + /// protected bool PagingDisabled; // +3/+2A only + + protected bool ROMhigh = false; + protected bool ROMlow = false; + + /// + /// Signs that the +2a/+3 special paging mode is activated + /// protected bool SpecialPagingMode; + + /// + /// Index of the current special paging config + /// protected int PagingConfiguration; + /// + /// Signs whether the disk motor is on or off + /// + protected bool DiskMotorState; + + protected bool PrinterPortStrobe; + /// /// The calling ZXSpectrum class (piped in via constructor) /// @@ -156,7 +183,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { if (AYDevice != null) AYDevice.UpdateSound(CurrentFrameCycle); - } + } + + if (SHADOWPaged) + { + + } } // we have reached the end of a frame diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs index bb3f42ac31..c86a1e6ca4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs @@ -257,6 +257,33 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum Memory.Add(9, RAM7); } + /// + /// ULA reads the memory at the specified address + /// (No memory contention) + /// Will read RAM5 (screen0) by default, unless RAM7 (screen1) is selected as output + /// + /// + /// + public override byte FetchScreenMemory(ushort addr) + { + byte value = new byte(); + + if (SHADOWPaged && !PagingDisabled) + { + // shadow screen should be outputted + // this lives in RAM7 + value = RAM7[addr & 0x3FFF]; + } + else + { + // shadow screen is not set to display or paging is disabled (probably in 48k mode) + // (use screen0 at RAM5) + value = RAM5[addr & 0x3FFF]; + } + + return value; + } + /// /// Sets up the ROM /// @@ -270,7 +297,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum for (int i = 0; i < 0x4000; i++) { ROM0[i] = RomData.RomBytes[i]; - ROM1[i] = RomData.RomBytes[i + 0x4000]; + if (RomData.RomBytes.Length > 0x4000) + ROM1[i] = RomData.RomBytes[i + 0x4000]; } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs index 4b9cb123c7..434f4c7cfa 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -17,6 +18,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { InputRead = true; + // It takes four T states for the Z80 to read a value from an I/O port, or write a value to a port + // (not including added ULA contention) + // The Bizhawk Z80A implementation appears to not consume any T-States for this operation + PortContention(4); + int result = 0xFF; // Check whether the low bit is reset @@ -24,7 +30,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum bool lowBitReset = (port & 0x0001) == 0; ULADevice.Contend(port); - CPU.TotalExecutedCycles++; + //CPU.TotalExecutedCycles++; // Kempston Joystick if ((port & 0xe0) == 0 || (port & 0x20) == 0) @@ -143,7 +149,27 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Kempston Mouse - // if unused port the floating memory bus should be returned (still todo) + // if unused port the floating memory bus should be returned + + // Floating bus is read on the previous cycle + int _tStates = CurrentFrameCycle - 1; + + // if we are on the top or bottom border return 0xff + if ((_tStates < ULADevice.contentionStartPeriod) || (_tStates > ULADevice.contentionEndPeriod)) + { + result = 0xff; + } + else + { + if (ULADevice.floatingBusTable[_tStates] < 0) + { + result = 0xff; + } + else + { + result = ReadBus((ushort)ULADevice.floatingBusTable[_tStates]); + } + } } return (byte)result; @@ -156,38 +182,47 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WritePort(ushort port, byte value) { + // get a BitArray of the port + BitArray portBits = new BitArray(BitConverter.GetBytes(port)); + // get a BitArray of the value byte + BitArray bits = new BitArray(new byte[] { value }); + int currT = CPU.TotalExecutedCycles; // paging if (port == 0x7ffd) { + if (PagingDisabled) + return; + // Bits 0, 1, 2 select the RAM page var rp = value & 0x07; if (rp < 8) RAMPaged = rp; + // bit 3 controls shadow screen + SHADOWPaged = bits[3]; + // ROM page - if ((value & 0x10) != 0) + if (bits[4]) { - // 48k ROM + // 48k basic rom ROMPaged = 1; } else { + // 128k editor and menu system ROMPaged = 0; } - // Bit 5 signifies that paging is disabled until next reboot - if ((value & 0x20) != 0) - PagingDisabled = true; - - + // Bit 5 set signifies that paging is disabled until next reboot + PagingDisabled = bits[5]; return; } // Check whether the low bit is reset // Technically the ULA should respond to every even I/O address - bool lowBitReset = (port & 0x01) == 0; + bool lowBitReset = !portBits[0]; // (port & 0x01) == 0; ULADevice.Contend(port); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs index a7044ca032..15a091fccd 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs @@ -133,7 +133,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // ROM 0x000 case 0: - result = Memory[ROMPaged][addr % 0x4000]; + result = Memory[_ROMpaged][addr % 0x4000]; break; // RAM 0x4000 (RAM5 - Bank5 or shadow bank RAM7) @@ -407,6 +407,33 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum Memory.Add(11, RAM7); } + /// + /// ULA reads the memory at the specified address + /// (No memory contention) + /// Will read RAM5 (screen0) by default, unless RAM7 (screen1) is selected as output + /// + /// + /// + public override byte FetchScreenMemory(ushort addr) + { + byte value = new byte(); + + if (SHADOWPaged && !PagingDisabled) + { + // shadow screen should be outputted + // this lives in RAM7 + value = RAM7[addr & 0x3FFF]; + } + else + { + // shadow screen is not set to display or paging is disabled (probably in 48k mode) + // (use screen0 at RAM5) + value = RAM5[addr & 0x3FFF]; + } + + return value; + } + /// /// Sets up the ROM /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs index 8218ce54df..199dda6515 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -158,12 +159,145 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WritePort(ushort port, byte value) { + // get a BitArray of the port + BitArray portBits = new BitArray(BitConverter.GetBytes(port)); + // get a BitArray of the value byte + BitArray bits = new BitArray(new byte[] { value }); + // Check whether the low bit is reset - // Technically the ULA should respond to every even I/O address - bool lowBitReset = (port & 0x01) == 0; + bool lowBitReset = !portBits[0]; // (port & 0x01) == 0; ULADevice.Contend(port); + // port 0x7ffd - hardware should only respond when bits 1 & 15 are reset and bit 14 is set + if (port == 0x7ffd) + { + if (!PagingDisabled) + { + // bits 0, 1, 2 select the RAM page + var rp = value & 0x07; + if (rp < 8) + RAMPaged = rp; + + // bit 3 controls shadow screen + SHADOWPaged = bits[3]; + + // Bit 5 set signifies that paging is disabled until next reboot + PagingDisabled = bits[5]; + + // portbit 4 is the LOW BIT of the ROM selection + ROMlow = bits[4]; + } + } + // port 0x1ffd - hardware should only respond when bits 1, 13, 14 & 15 are reset and bit 12 is set + else if (port == 0x1ffd) + { + if (!PagingDisabled) + { + if (bits[0]) + { + // special paging is not enabled - get the ROMpage high byte + ROMhigh = bits[2]; + + // set the special paging mode flag + SpecialPagingMode = false; + } + else + { + // special paging is enabled + // this is decided based on combinations of bits 1 & 2 + // Config 0 = Bit1-0 Bit2-0 + // Config 1 = Bit1-1 Bit2-0 + // Config 2 = Bit1-0 Bit2-1 + // Config 3 = Bit1-1 Bit2-1 + BitArray confHalfNibble = new BitArray(2); + confHalfNibble[0] = bits[1]; + confHalfNibble[1] = bits[2]; + + // set special paging configuration + PagingConfiguration = ZXSpectrum.GetIntFromBitArray(confHalfNibble); + + // set the special paging mode flag + SpecialPagingMode = true; + } + } + + // bit 3 controls the disk motor (1=on, 0=off) + DiskMotorState = bits[3]; + + // bit 4 is the printer port strobe + PrinterPortStrobe = bits[4]; + } + /* + // port 0x7ffd - hardware should only respond when bits 1 & 15 are reset and bit 14 is set + if (!portBits[1] && !portBits[15] && portBits[14]) + { + // paging (skip if paging has been disabled - paging can then only happen after a machine hard reset) + if (!PagingDisabled) + { + // bit 0 specifies the paging mode + SpecialPagingMode = bits[0]; + + if (!SpecialPagingMode) + { + // we are in normal mode + // portbit 4 is the LOW BIT of the ROM selection + BitArray romHalfNibble = new BitArray(2); + romHalfNibble[0] = portBits[4]; + + // value bit 2 is the high bit of the ROM selection + romHalfNibble[1] = bits[2]; + + // value bit 1 is ignored in normal paging mode + + // set the ROMPage + ROMPaged = ZXSpectrum.GetIntFromBitArray(romHalfNibble); + + + + + // bit 3 controls shadow screen + SHADOWPaged = bits[3]; + + // Bit 5 set signifies that paging is disabled until next reboot + PagingDisabled = bits[5]; + } + } + } + + // port 0x1ffd - special paging mode + // hardware should only respond when bits 1, 13, 14 & 15 are reset and bit 12 is set + if (!portBits[1] && portBits[12] && !portBits[13] && !portBits[14] && !portBits[15]) + { + if (!PagingDisabled && SpecialPagingMode) + { + // process special paging + // this is decided based on combinations of bits 1 & 2 + // Config 0 = Bit1-0 Bit2-0 + // Config 1 = Bit1-1 Bit2-0 + // Config 2 = Bit1-0 Bit2-1 + // Config 3 = Bit1-1 Bit2-1 + BitArray confHalfNibble = new BitArray(2); + confHalfNibble[0] = bits[1]; + confHalfNibble[1] = bits[2]; + + // set special paging configuration + PagingConfiguration = ZXSpectrum.GetIntFromBitArray(confHalfNibble); + + // last value should be saved at 0x5b67 (23399) - not sure if this is actually needed + WriteBus(0x5b67, value); + } + + // bit 3 controls the disk motor (1=on, 0=off) + DiskMotorState = bits[3]; + + // bit 4 is the printer port strobe + PrinterPortStrobe = bits[4]; + } + + */ + + // Only even addresses address the ULA if (lowBitReset) { @@ -206,6 +340,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum AYDevice.PortWrite(value); CPU.TotalExecutedCycles += 3; } + + /* + else { if ((port & 0xC002) == 0x4000) //Are bits 1 and 15 reset and bit 14 set? @@ -272,48 +409,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } } + */ } } + LastULAOutByte = value; - // paging - if (port == 0x7ffd) - { - if (PagingDisabled) - return; - - LastULAOutByte = value; - - - - - - - // Bits 0, 1, 2 select the RAM page - var rp = value & 0x07; - if (rp < 8) - RAMPaged = rp; - - // ROM page - if ((value & 0x10) != 0) - { - // 48k ROM - ROMPaged = 1; - } - else - { - ROMPaged = 0; - } - - // Bit 5 signifies that paging is disabled until next reboot - if ((value & 0x20) != 0) - PagingDisabled = true; - - - return; - } + + } + + /// + /// +3 and 2a overidden method + /// + public override int _ROMpaged + { + get + { + // calculate the ROMpage from the high and low bits + return ZXSpectrum.GetIntFromBitArray(new BitArray(new bool[] { ROMlow, ROMhigh })); + } + set { ROMPaged = value; } } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs index 893edbae8a..4cb9656858 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -248,5 +249,20 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// R3R5 = R3 | R5 } + + /// + /// Helper method that returns a single INT32 from a BitArray + /// + /// + /// + public static int GetIntFromBitArray(BitArray bitArray) + { + if (bitArray.Length > 32) + throw new ArgumentException("Argument length shall be at most 32 bits."); + + int[] array = new int[1]; + bitArray.CopyTo(array, 0); + return array[0]; + } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 2af8fbe390..c97a3c48d2 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -77,6 +77,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum default: throw new InvalidOperationException("Machine not yet emulated"); } + + _cpu.MemoryCallbacks = MemoryCallbacks; @@ -125,13 +127,20 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //private byte[] _file; private readonly List _files; - public bool DiagRom = false; + public bool DiagRom = true; + + private List diagRoms = new List + { + @"\DiagROM.v28", + @"\zx-diagnostics\testrom.bin" + }; + private int diagIndex = 1; private byte[] GetFirmware(int length, params string[] names) { - if (DiagRom & File.Exists(Directory.GetCurrentDirectory() + @"\DiagROM.v28")) + if (DiagRom & File.Exists(Directory.GetCurrentDirectory() + diagRoms[diagIndex])) { - var rom = File.ReadAllBytes(Directory.GetCurrentDirectory() + @"\DiagROM.v28"); + var rom = File.ReadAllBytes(Directory.GetCurrentDirectory() + diagRoms[diagIndex]); return rom; } @@ -205,7 +214,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum var _systemRomP3 = GetFirmware(0x10000, "PLUS3ROM"); var romDataP3 = RomData.InitROM(machineType, _systemRomP3); _machine.InitROM(romDataP3); - System.Windows.Forms.MessageBox.Show("+3 is not working at all yet :/"); + //System.Windows.Forms.MessageBox.Show("+3 is not working at all yet :/"); break; } }