diff --git a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs index 7d8eb5fe63..789c01138f 100644 --- a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs +++ b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs @@ -52,10 +52,11 @@ namespace BizHawk.Emulation.Common // ZX Spectrum FirmwareAndOption("5EA7C2B824672E914525D1D5C419D71B84A426A2", 16384, "ZXSpectrum", "48ROM", "48.ROM", "Spectrum 48K ROM"); + FirmwareAndOption("16375D42EA109B47EDDED7A16028DE7FDB3013A1", 32768, "ZXSpectrum", "128ROM", "128.ROM", "Spectrum 128K ROM"); - // for saturn, we think any bios region can pretty much run any iso - // so, we're going to lay this out carefully so that we choose things in a sensible order, but prefer the correct region - var ss_100_j = File("2B8CB4F87580683EB4D760E4ED210813D667F0A2", 524288, "saturn-1.00-(J).bin", "Bios v1.00 (J)"); + // for saturn, we think any bios region can pretty much run any iso + // so, we're going to lay this out carefully so that we choose things in a sensible order, but prefer the correct region + var ss_100_j = File("2B8CB4F87580683EB4D760E4ED210813D667F0A2", 524288, "saturn-1.00-(J).bin", "Bios v1.00 (J)"); var ss_100_ue = File("FAA8EA183A6D7BBE5D4E03BB1332519800D3FBC3", 524288, "saturn-1.00-(U+E).bin", "Bios v1.00 (U+E)"); var ss_100a_ue = File("3BB41FEB82838AB9A35601AC666DE5AACFD17A58", 524288, "saturn-1.00a-(U+E).bin", "Bios v1.00a (U+E)"); // ?? is this size correct? var ss_101_j = File("DF94C5B4D47EB3CC404D88B33A8FDA237EAF4720", 524288, "saturn-1.01-(J).bin", "Bios v1.01 (J)"); // ?? is this size correct? diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 13af57d15a..830c6523cf 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -276,7 +276,11 @@ + + + + @@ -1377,6 +1381,7 @@ + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs index 829f1a0e5b..e70dc3ea4a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs @@ -11,6 +11,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Sinclair Spectrum 48K model /// - ZXSpectrum48 + ZXSpectrum48, + + /// + /// Sinclair Spectrum 128K model + /// + ZXSpectrum128 } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index 5b371bbc7f..d58ef1f6eb 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -44,28 +44,25 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - public virtual byte ReadBus(ushort addr) + public abstract byte ReadBus(ushort addr); + + /// + /// Pushes a value onto the data bus that should be valid as long as the interrupt is true + /// + /// + /// + public virtual byte PushBus() { - throw new NotImplementedException("Must be overriden"); + return 0xFF; } - /// - /// Pushes a value onto the data bus that should be valid as long as the interrupt is true - /// - /// - /// - public virtual byte PushBus() - { - throw new NotImplementedException("Must be overriden"); - } - - /// - /// Simulates writing to the bus - /// Paging should be handled here - /// - /// - /// - public virtual void WriteBus(ushort addr, byte value) + /// + /// Simulates writing to the bus + /// Paging should be handled here + /// + /// + /// + public virtual void WriteBus(ushort addr, byte value) { throw new NotImplementedException("Must be overriden"); } @@ -76,23 +73,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - public virtual byte ReadMemory(ushort addr) - { - throw new NotImplementedException("Must be overriden"); - } - /* - /// - /// Reads a byte of data from a specified memory address - /// (with no memory contention) - /// - /// - /// - public virtual byte PeekMemory(ushort addr) - { - var data = ReadBus(addr); - return data; - } - */ + public abstract byte ReadMemory(ushort addr); /// /// Writes a byte of data to a specified memory address @@ -100,51 +81,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - public virtual void WriteMemory(ushort addr, byte value) - { - throw new NotImplementedException("Must be overriden"); - } - - /* - /// - /// Writes a byte of data to a specified memory address - /// (without contention) - /// - /// - /// - public virtual void PokeMemory(ushort addr, byte value) - { - if (addr < 0x4000) - { - // Do nothing - we cannot write to ROM - return; - } - - WriteBus(addr, value); - } - */ - - /// - /// Fills memory from buffer - /// - /// - /// - public virtual void FillMemory(byte[] buffer, ushort startAddress) - { - //buffer?.CopyTo(RAM, startAddress); - } + public abstract void WriteMemory(ushort addr, byte value); /// /// Sets up the ROM /// /// /// - public virtual void InitROM(RomData romData) - { - RomData = romData; - // for 16/48k machines only ROM0 is used (no paging) - RomData.RomBytes?.CopyTo(ROM0, 0); - } + public abstract void InitROM(RomData romData); /// /// ULA reads the memory at the specified address @@ -154,7 +98,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public virtual byte FetchScreenMemory(ushort addr) { - //var value = RAM0[(addr & 0x3FFF)];// + 0x4000]; var value = ReadBus((ushort)((addr & 0x3FFF) + 0x4000)); return value; } @@ -162,10 +105,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Helper function to refresh memory array (probably not the best way to do things) /// - public virtual void ReInitMemory() - { - throw new NotImplementedException("Must be overriden"); - } + public abstract void ReInitMemory(); /// /// Returns the memory contention value for the specified T-State (cycle) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs index be31fead3f..c590901dac 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs @@ -22,150 +22,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - public virtual byte ReadPort(ushort port) - { - int result = 0xFF; - - // Check whether the low bit is reset - // Technically the ULA should respond to every even I/O address - bool lowBitReset = (port & 0x0001) == 0; - - ContendPort((ushort)port); - - // Kempston Joystick - if ((port & 0xe0) == 0 || (port & 0x20) == 0) - { - return (byte)KempstonDevice.JoyLine; - } - else if (lowBitReset) - { - // Even I/O address so get input - // The high byte indicates which half-row of keys is being polled - /* - IN: Reads keys (bit 0 to bit 4 inclusive) - 0xfefe SHIFT, Z, X, C, V 0xeffe 0, 9, 8, 7, 6 - 0xfdfe A, S, D, F, G 0xdffe P, O, I, U, Y - 0xfbfe Q, W, E, R, T 0xbffe ENTER, L, K, J, H - 0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHFT, M, N, B - */ - - if ((port & 0x8000) == 0) - result &= KeyboardDevice.KeyLine[7]; - - if ((port & 0x4000) == 0) - result &= KeyboardDevice.KeyLine[6]; - - if ((port & 0x2000) == 0) - result &= KeyboardDevice.KeyLine[5]; - - if ((port & 0x1000) == 0) - result &= KeyboardDevice.KeyLine[4]; - - if ((port & 0x800) == 0) - result &= KeyboardDevice.KeyLine[3]; - - if ((port & 0x400) == 0) - result &= KeyboardDevice.KeyLine[2]; - - if ((port & 0x200) == 0) - result &= KeyboardDevice.KeyLine[1]; - - if ((port & 0x100) == 0) - result &= KeyboardDevice.KeyLine[0]; - - result = result & 0x1f; //mask out lower 4 bits - result = result | 0xa0; //set bit 5 & 7 to 1 - - - if (TapeDevice.CurrentMode == TapeOperationMode.Load) - { - if (!TapeDevice.GetEarBit(CPU.TotalExecutedCycles)) - { - result &= ~(TAPE_BIT); // reset is EAR ON - } - else - { - result |= (TAPE_BIT); // set is EAR Off - } - } - else - { - if (KeyboardDevice.IsIssue2Keyboard) - { - if ((LastULAOutByte & (EAR_BIT + MIC_BIT)) == 0) - { - result &= ~(TAPE_BIT); - } - else - { - result |= TAPE_BIT; - } - } - else - { - if ((LastULAOutByte & EAR_BIT) == 0) - { - result &= ~(TAPE_BIT); - } - else - { - result |= TAPE_BIT; - } - } - } - - } - else - { - // devices other than the ULA will respond here - // (e.g. the AY sound chip in a 128k spectrum - - // AY register activate - // Kemptson Mouse - - - // if unused port the floating memory bus should be returned (still todo) - } - - return (byte)result; - } + public abstract byte ReadPort(ushort port); /// /// Writes a byte of data to a specified port address /// /// /// - public virtual void WritePort(ushort port, 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; - - ContendPort(port); - - // Only even addresses address the ULA - if (lowBitReset) - { - // store the last OUT byte - LastULAOutByte = value; - - /* - Bit 7 6 5 4 3 2 1 0 - +-------------------------------+ - | | | | E | M | Border | - +-------------------------------+ - */ - - // Border - LSB 3 bits hold the border colour - BorderColour = value & BORDER_BIT; - - // Buzzer - BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); - - // Tape - TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); - } - } + public abstract void WritePort(ushort port, byte value); /// /// Apply I/O contention if necessary diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs index 8a4bedf024..62bd4d98d4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs @@ -179,7 +179,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// The time of displaying right part of the border. /// Given in Z80 clock cycles. /// - protected int BorderRightTime = 24; + protected int BorderRightTime = 24; /// /// The time used to render the nonvisible right part of the border. @@ -900,6 +900,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public int VsyncNumerator { get { return 3500000; } + set { } } public int VsyncDenominator diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 7cd878d1cb..cb1c9064f9 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -10,7 +10,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// * Main properties / fields / contruction* /// public abstract partial class SpectrumBase - { + { + + public bool ROMPaged { get; set; } + public bool SHADOWPaged { get; set; } + public int RAMPaged { get; set; } + public bool PagingDisabled { get; set; } + /// /// The calling ZXSpectrum class (piped in via constructor) /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs new file mode 100644 index 0000000000..4502a98a51 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs @@ -0,0 +1,284 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZX128 : SpectrumBase + { + /* 128k paging controlled by writes to port 0x7ffd + * + * + + #7FFD (32765) - decoded as A15=0, A1=0 and /IORQ=0. Bits 0..5 are latched. Bits 0..2 select RAM bank in secton D. Bit 3 selects RAM bank to dispay screen (0 - RAM5, 1 - RAM7). Bit 4 selects ROM bank (0 - ROM0, 1 - ROM1). Bit 5, when set locks future writing to #7FFD port until reset. Reading #7FFD port is the same as writing #FF into it. + #BFFD (49149) - write data byte into AY-3-8912 chip. + #FFFD (65533) - select AY-3-8912 addres (D4..D7 ignored) and reading data byte. + + * 0xffff +--------+--------+--------+--------+--------+--------+--------+--------+ + | Bank 0 | Bank 1 | Bank 2 | Bank 3 | Bank 4 | Bank 5 | Bank 6 | Bank 7 | + | | |(also at| | |(also at| | | + | | | 0x8000)| | | 0x4000)| | | + | | | | | | screen | | screen | + 0xc000 +--------+--------+--------+--------+--------+--------+--------+--------+ + | Bank 2 | Any one of these pages may be switched in. + | | + | | + | | + 0x8000 +--------+ + | Bank 5 | + | | + | | + | screen | + 0x4000 +--------+--------+ + | ROM 0 | ROM 1 | Either ROM may be switched in. + | | | + | | | + | | | + 0x0000 +--------+--------+ + */ + + /// + /// Simulates reading from the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override byte ReadBus(ushort addr) + { + int divisor = addr / 0x4000; + byte result = 0xff; + switch (divisor) + { + // ROM 0x000 + case 0: + if (!ROMPaged) + result = Memory[0][addr % 0x4000]; + else + result = Memory[1][addr % 0x4000]; + break; + + // RAM 0x4000 (RAM5 - Bank5 or shadow bank RAM7) + case 1: + result = Memory[7][addr % 0x4000]; + break; + + // RAM 0x8000 (RAM2 - Bank2) + case 2: + result = Memory[4][addr % 0x4000]; + break; + + // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) + case 3: + switch (RAMPaged) + { + case 0: + result = Memory[2][addr % 0x4000]; + break; + case 1: + result = Memory[3][addr % 0x4000]; + break; + case 2: + result = Memory[4][addr % 0x4000]; + break; + case 3: + result = Memory[5][addr % 0x4000]; + break; + case 4: + result = Memory[6][addr % 0x4000]; + break; + case 5: + result = Memory[7][addr % 0x4000]; + break; + case 6: + result = Memory[8][addr % 0x4000]; + break; + case 7: + result = Memory[9][addr % 0x4000]; + break; + } + break; + default: + break; + } + + return result; + } + + /// + /// Simulates writing to the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override void WriteBus(ushort addr, byte value) + { + int divisor = addr / 0x4000; + switch (divisor) + { + // ROM 0x000 + case 0: + if (!ROMPaged) + Memory[0][addr % 0x4000] = value; + else + Memory[1][addr % 0x4000] = value; + break; + + // RAM 0x4000 (RAM5 - Bank5 or shadow bank RAM7) + case 1: + Memory[7][addr % 0x4000] = value; + break; + + // RAM 0x8000 (RAM2 - Bank2) + case 2: + Memory[4][addr % 0x4000] = value; + break; + + // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) + case 3: + switch (RAMPaged) + { + case 0: + Memory[2][addr % 0x4000] = value; + break; + case 1: + Memory[3][addr % 0x4000] = value; + break; + case 2: + Memory[4][addr % 0x4000] = value; + break; + case 3: + Memory[5][addr % 0x4000] = value; + break; + case 4: + Memory[6][addr % 0x4000] = value; + break; + case 5: + Memory[7][addr % 0x4000] = value; + break; + case 6: + Memory[8][addr % 0x4000] = value; + break; + case 7: + Memory[9][addr % 0x4000] = value; + break; + } + break; + default: + break; + } + } + + /// + /// Reads a byte of data from a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override byte ReadMemory(ushort addr) + { + var data = ReadBus(addr); + if ((addr & 0xC000) == 0x4000) + { + // addr is in RAM not ROM - apply memory contention if neccessary + var delay = GetContentionValue(CurrentFrameCycle); + CPU.TotalExecutedCycles += delay; + } + return data; + } + + /// + /// Writes a byte of data to a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x4000) + { + // Do nothing - we cannot write to ROM + return; + } + else if (addr < 0xC000) + { + // possible contended RAM + var delay = GetContentionValue(CurrentFrameCycle); + CPU.TotalExecutedCycles += delay; + } + + WriteBus(addr, value); + } + + public override void ReInitMemory() + { + if (Memory.ContainsKey(0)) + Memory[0] = ROM0; + else + Memory.Add(0, ROM0); + + if (Memory.ContainsKey(1)) + Memory[1] = ROM1; + else + Memory.Add(1, ROM1); + + if (Memory.ContainsKey(2)) + Memory[2] = RAM0; + else + Memory.Add(2, RAM0); + + if (Memory.ContainsKey(3)) + Memory[3] = RAM1; + else + Memory.Add(3, RAM1); + + if (Memory.ContainsKey(4)) + Memory[4] = RAM2; + else + Memory.Add(4, RAM2); + + if (Memory.ContainsKey(5)) + Memory[5] = RAM3; + else + Memory.Add(5, RAM3); + + if (Memory.ContainsKey(6)) + Memory[6] = RAM4; + else + Memory.Add(6, RAM4); + + if (Memory.ContainsKey(7)) + Memory[7] = RAM5; + else + Memory.Add(7, RAM5); + + if (Memory.ContainsKey(8)) + Memory[8] = RAM6; + else + Memory.Add(8, RAM6); + + if (Memory.ContainsKey(9)) + Memory[9] = RAM7; + else + Memory.Add(9, RAM7); + } + + /// + /// Sets up the ROM + /// + /// + /// + public override void InitROM(RomData romData) + { + RomData = romData; + // 128k uses ROM0 and ROM1 + // 128k loader is in ROM0, and fallback 48k rom is in ROM1 + for (int i = 0; i < 0x4000; i++) + { + ROM0[i] = RomData.RomBytes[i]; + 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 new file mode 100644 index 0000000000..d381bef130 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZX128 : SpectrumBase + { + /// + /// Reads a byte of data from a specified port address + /// + /// + /// + public override byte ReadPort(ushort port) + { + int result = 0xFF; + + // Check whether the low bit is reset + // Technically the ULA should respond to every even I/O address + bool lowBitReset = (port & 0x0001) == 0; + + ContendPort((ushort)port); + + // Kempston Joystick + if ((port & 0xe0) == 0 || (port & 0x20) == 0) + { + return (byte)KempstonDevice.JoyLine; + } + else if (lowBitReset) + { + // Even I/O address so get input + // The high byte indicates which half-row of keys is being polled + /* + IN: Reads keys (bit 0 to bit 4 inclusive) + 0xfefe SHIFT, Z, X, C, V 0xeffe 0, 9, 8, 7, 6 + 0xfdfe A, S, D, F, G 0xdffe P, O, I, U, Y + 0xfbfe Q, W, E, R, T 0xbffe ENTER, L, K, J, H + 0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHFT, M, N, B + */ + + if ((port & 0x8000) == 0) + result &= KeyboardDevice.KeyLine[7]; + + if ((port & 0x4000) == 0) + result &= KeyboardDevice.KeyLine[6]; + + if ((port & 0x2000) == 0) + result &= KeyboardDevice.KeyLine[5]; + + if ((port & 0x1000) == 0) + result &= KeyboardDevice.KeyLine[4]; + + if ((port & 0x800) == 0) + result &= KeyboardDevice.KeyLine[3]; + + if ((port & 0x400) == 0) + result &= KeyboardDevice.KeyLine[2]; + + if ((port & 0x200) == 0) + result &= KeyboardDevice.KeyLine[1]; + + if ((port & 0x100) == 0) + result &= KeyboardDevice.KeyLine[0]; + + result = result & 0x1f; //mask out lower 4 bits + result = result | 0xa0; //set bit 5 & 7 to 1 + + + if (TapeDevice.CurrentMode == TapeOperationMode.Load) + { + if (!TapeDevice.GetEarBit(CPU.TotalExecutedCycles)) + { + result &= ~(TAPE_BIT); // reset is EAR ON + } + else + { + result |= (TAPE_BIT); // set is EAR Off + } + } + else + { + if (KeyboardDevice.IsIssue2Keyboard) + { + if ((LastULAOutByte & (EAR_BIT + MIC_BIT)) == 0) + { + result &= ~(TAPE_BIT); + } + else + { + result |= TAPE_BIT; + } + } + else + { + if ((LastULAOutByte & EAR_BIT) == 0) + { + result &= ~(TAPE_BIT); + } + else + { + result |= TAPE_BIT; + } + } + } + + } + else + { + // devices other than the ULA will respond here + // (e.g. the AY sound chip in a 128k spectrum + + // AY register activate + // Kemptson Mouse + + + // if unused port the floating memory bus should be returned (still todo) + } + + return (byte)result; + } + + /// + /// Writes a byte of data to a specified port address + /// + /// + /// + public override void WritePort(ushort port, byte value) + { + // paging + if (port == 0x7ffd) + { + // 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 = true; + } + else + { + ROMPaged = false; + } + + // Bit 5 signifies that paging is disabled until next reboot + if ((value & 0x20) != 0) + PagingDisabled = true; + + + return; + } + + // Check whether the low bit is reset + // Technically the ULA should respond to every even I/O address + bool lowBitReset = (port & 0x01) == 0; + + ContendPort(port); + + // Only even addresses address the ULA + if (lowBitReset) + { + // store the last OUT byte + LastULAOutByte = value; + + /* + Bit 7 6 5 4 3 2 1 0 + +-------------------------------+ + | | | | E | M | Border | + +-------------------------------+ + */ + + // Border - LSB 3 bits hold the border colour + BorderColour = value & BORDER_BIT; + + // Buzzer + BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); + + // Tape + TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs new file mode 100644 index 0000000000..93b7b58f35 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BizHawk.Emulation.Cores.Components.Z80A; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZX128 : SpectrumBase + { + #region Construction + + /// + /// Main constructor + /// + /// + /// + public ZX128(ZXSpectrum spectrum, Z80A cpu, byte[] file) + { + Spectrum = spectrum; + CPU = cpu; + + ROMPaged = false; + SHADOWPaged = false; + RAMPaged = 0; + PagingDisabled = false; + + // init addressable memory from ROM and RAM banks + ReInitMemory(); + + //RAM = new byte[0x4000 + 0xC000]; + + //DisplayLineTime = 132; + VsyncNumerator = 3546900; + + InitScreenConfig(); + InitScreen(); + + ResetULACycle(); + + BuzzerDevice = new Buzzer(this); + BuzzerDevice.Init(44100, UlaFrameCycleCount); + + KeyboardDevice = new Keyboard48(this); + KempstonDevice = new KempstonJoystick(this); + + TapeProvider = new DefaultTapeProvider(file); + + TapeDevice = new Tape(TapeProvider); + TapeDevice.Init(this); + } + + #endregion + + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs new file mode 100644 index 0000000000..3cf3b09390 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZX48 : SpectrumBase + { + /* 48K Spectrum has NO memory paging + * + * 0xffff +--------+ + | Bank 2 | + | | + | | + | | + 0xc000 +--------+ + | Bank 1 | + | | + | | + | | + 0x8000 +--------+ + | Bank 0 | + | | + | | + | screen | + 0x4000 +--------+ + | ROM 0 | + | | + | | + | | + 0x0000 +--------+ + */ + + /// + /// Simulates reading from the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override byte ReadBus(ushort addr) + { + int divisor = addr / 0x4000; + // paging logic goes here + + var bank = Memory[divisor]; + var index = addr % 0x4000; + return bank[index]; + } + + /// + /// Simulates writing to the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override void WriteBus(ushort addr, byte value) + { + int divisor = addr / 0x4000; + // paging logic goes here + + var bank = Memory[divisor]; + var index = addr % 0x4000; + bank[index] = value; + } + + /// + /// Reads a byte of data from a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override byte ReadMemory(ushort addr) + { + var data = ReadBus(addr); + if ((addr & 0xC000) == 0x4000) + { + // addr is in RAM not ROM - apply memory contention if neccessary + var delay = GetContentionValue(CurrentFrameCycle); + CPU.TotalExecutedCycles += delay; + } + return data; + } + + /// + /// Writes a byte of data to a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x4000) + { + // Do nothing - we cannot write to ROM + return; + } + else if (addr < 0xC000) + { + // possible contended RAM + var delay = GetContentionValue(CurrentFrameCycle); + CPU.TotalExecutedCycles += delay; + } + + WriteBus(addr, value); + } + + public override void ReInitMemory() + { + if (Memory.ContainsKey(0)) + Memory[0] = ROM0; + else + Memory.Add(0, ROM0); + + if (Memory.ContainsKey(1)) + Memory[1] = RAM0; + else + Memory.Add(1, RAM0); + + if (Memory.ContainsKey(2)) + Memory[2] = RAM1; + else + Memory.Add(2, RAM1); + + if (Memory.ContainsKey(3)) + Memory[3] = RAM2; + else + Memory.Add(3, RAM2); + + if (Memory.ContainsKey(4)) + Memory[4] = RAM3; + else + Memory.Add(4, RAM3); + } + + /// + /// Sets up the ROM + /// + /// + /// + public override void InitROM(RomData romData) + { + RomData = romData; + // for 16/48k machines only ROM0 is used (no paging) + RomData.RomBytes?.CopyTo(ROM0, 0); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs new file mode 100644 index 0000000000..2a0080e1c8 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZX48 : SpectrumBase + { + /// + /// Reads a byte of data from a specified port address + /// + /// + /// + public override byte ReadPort(ushort port) + { + int result = 0xFF; + + // Check whether the low bit is reset + // Technically the ULA should respond to every even I/O address + bool lowBitReset = (port & 0x0001) == 0; + + ContendPort((ushort)port); + + // Kempston Joystick + if ((port & 0xe0) == 0 || (port & 0x20) == 0) + { + return (byte)KempstonDevice.JoyLine; + } + else if (lowBitReset) + { + // Even I/O address so get input + // The high byte indicates which half-row of keys is being polled + /* + IN: Reads keys (bit 0 to bit 4 inclusive) + 0xfefe SHIFT, Z, X, C, V 0xeffe 0, 9, 8, 7, 6 + 0xfdfe A, S, D, F, G 0xdffe P, O, I, U, Y + 0xfbfe Q, W, E, R, T 0xbffe ENTER, L, K, J, H + 0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHFT, M, N, B + */ + + if ((port & 0x8000) == 0) + result &= KeyboardDevice.KeyLine[7]; + + if ((port & 0x4000) == 0) + result &= KeyboardDevice.KeyLine[6]; + + if ((port & 0x2000) == 0) + result &= KeyboardDevice.KeyLine[5]; + + if ((port & 0x1000) == 0) + result &= KeyboardDevice.KeyLine[4]; + + if ((port & 0x800) == 0) + result &= KeyboardDevice.KeyLine[3]; + + if ((port & 0x400) == 0) + result &= KeyboardDevice.KeyLine[2]; + + if ((port & 0x200) == 0) + result &= KeyboardDevice.KeyLine[1]; + + if ((port & 0x100) == 0) + result &= KeyboardDevice.KeyLine[0]; + + result = result & 0x1f; //mask out lower 4 bits + result = result | 0xa0; //set bit 5 & 7 to 1 + + + if (TapeDevice.CurrentMode == TapeOperationMode.Load) + { + if (!TapeDevice.GetEarBit(CPU.TotalExecutedCycles)) + { + result &= ~(TAPE_BIT); // reset is EAR ON + } + else + { + result |= (TAPE_BIT); // set is EAR Off + } + } + else + { + if (KeyboardDevice.IsIssue2Keyboard) + { + if ((LastULAOutByte & (EAR_BIT + MIC_BIT)) == 0) + { + result &= ~(TAPE_BIT); + } + else + { + result |= TAPE_BIT; + } + } + else + { + if ((LastULAOutByte & EAR_BIT) == 0) + { + result &= ~(TAPE_BIT); + } + else + { + result |= TAPE_BIT; + } + } + } + + } + else + { + // devices other than the ULA will respond here + // (e.g. the AY sound chip in a 128k spectrum + + // AY register activate + // Kemptson Mouse + + + // if unused port the floating memory bus should be returned (still todo) + } + + return (byte)result; + } + + /// + /// Writes a byte of data to a specified port address + /// + /// + /// + public override void WritePort(ushort port, 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; + + ContendPort(port); + + // Only even addresses address the ULA + if (lowBitReset) + { + // store the last OUT byte + LastULAOutByte = value; + + /* + Bit 7 6 5 4 3 2 1 0 + +-------------------------------+ + | | | | E | M | Border | + +-------------------------------+ + */ + + // Border - LSB 3 bits hold the border colour + BorderColour = value & BORDER_BIT; + + // Buzzer + BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); + + // Tape + TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs index 3deae820b1..4979071d58 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { - public class ZX48 : SpectrumBase + public partial class ZX48 : SpectrumBase { #region Construction @@ -21,17 +21,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum Spectrum = spectrum; CPU = cpu; - // init addressable memory from ROM and RAM banks - /* - Memory.Add(0, ROM0); - Memory.Add(1, RAM0); - Memory.Add(2, RAM1); - Memory.Add(3, RAM2); - */ ReInitMemory(); - - //RAM = new byte[0x4000 + 0xC000]; - + InitScreenConfig(); InitScreen(); @@ -50,148 +41,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } #endregion - - #region MemoryMapping - - /* 48K Spectrum has NO memory paging - * - * 0xffff +--------+ - | Bank 2 | - | | - | | - | | - 0xc000 +--------+ - | Bank 1 | - | | - | | - | | - 0x8000 +--------+ - | Bank 0 | - | | - | | - | screen | - 0x4000 +--------+ - | ROM 0 | - | | - | | - | | - 0x0000 +--------+ - */ - - /// - /// Simulates reading from the bus (no contention) - /// Paging should be handled here - /// - /// - /// - public override byte ReadBus(ushort addr) - { - int divisor = addr / 0x4000; - // paging logic goes here - - var bank = Memory[divisor]; - var index = addr % 0x4000; - return bank[index]; - } - - /// - /// Pushes a value onto the data bus that should be valid as long as the interrupt is true - /// - /// - /// - public override byte PushBus() - { - return 0xFF; - } - - /// - /// Simulates writing to the bus (no contention) - /// Paging should be handled here - /// - /// - /// - public override void WriteBus(ushort addr, byte value) - { - int divisor = addr / 0x4000; - // paging logic goes here - - var bank = Memory[divisor]; - var index = addr % 0x4000; - bank[index] = value; - } - - /// - /// Reads a byte of data from a specified memory address - /// (with memory contention if appropriate) - /// - /// - /// - public override byte ReadMemory(ushort addr) - { - var data = ReadBus(addr); - if ((addr & 0xC000) == 0x4000) - { - // addr is in RAM not ROM - apply memory contention if neccessary - var delay = GetContentionValue(CurrentFrameCycle); - CPU.TotalExecutedCycles += delay; - } - return data; - } - - /// - /// Writes a byte of data to a specified memory address - /// (with memory contention if appropriate) - /// - /// - /// - public override void WriteMemory(ushort addr, byte value) - { - if (addr < 0x4000) - { - // Do nothing - we cannot write to ROM - return; - } - else if (addr < 0xC000) - { - // possible contended RAM - var delay = GetContentionValue(CurrentFrameCycle); - CPU.TotalExecutedCycles += delay; - } - - WriteBus(addr, value); - } - - public override void ReInitMemory() - { - if (Memory.ContainsKey(0)) - Memory[0] = ROM0; - else - Memory.Add(0, ROM0); - - if (Memory.ContainsKey(1)) - Memory[1] = RAM0; - else - Memory.Add(1, RAM0); - - if (Memory.ContainsKey(2)) - Memory[2] = RAM1; - else - Memory.Add(2, RAM1); - - if (Memory.ContainsKey(3)) - Memory[3] = RAM2; - else - Memory.Add(3, RAM2); - - if (Memory.ContainsKey(4)) - Memory[4] = RAM3; - else - Memory.Add(4, RAM3); - } - - - #endregion - - } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/RomData.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/RomData.cs index 97dffe206b..0f0f12110b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/RomData.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/RomData.cs @@ -71,6 +71,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum RD.LoadBytesResumeAddress = 0x05E2; RD.LoadBytesInvalidHeaderAddress = 0x05B6; break; + + case MachineType.ZXSpectrum128: + RD.SaveBytesRoutineAddress = 0x04C2; + RD.SaveBytesResumeAddress = 0x0000; + RD.LoadBytesRoutineAddress = 0x0808; //0x0556; //0x056C; + RD.LoadBytesResumeAddress = 0x05E2; + RD.LoadBytesInvalidHeaderAddress = 0x05B6; + break; } return RD; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 083da9edf6..538b9c4cbe 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -40,6 +40,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ControllerDefinition = ZXSpectrumControllerDefinition; Init(MachineType.ZXSpectrum48, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _file); break; + case MachineType.ZXSpectrum128: + ControllerDefinition = ZXSpectrumControllerDefinition; + Init(MachineType.ZXSpectrum128, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _file); + break; default: throw new InvalidOperationException("Machine not yet emulated"); } @@ -110,6 +114,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum var romData = RomData.InitROM(machineType, _systemRom); _machine.InitROM(romData); break; + case MachineType.ZXSpectrum128: + _machine = new ZX128(this, _cpu, file); + var _systemRom128 = GetFirmware(0x8000, "128ROM"); + var romData128 = RomData.InitROM(machineType, _systemRom128); + _machine.InitROM(romData128); + break; } }