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;
}
}