diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs
index 5890279f31..9708236f5d 100644
--- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs
+++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs
@@ -237,10 +237,66 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
switch (machineType)
{
case MachineType.ZXSpectrum16:
- case MachineType.ZXSpectrum48:
+ case MachineType.ZXSpectrum48:
+
+ if ((lastPortAddr & 0xc000) == 0x4000)
+ highByte407f = true;
+
+ if (highByte407f)
+ {
+ // high byte 40-7f
+ if (lowBitSet)
+ {
+ // high byte 40-7f
+ // low bit set
+ // C:1, C:1, C:1, C:1
+ switch (T)
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ return true;
+ }
+ }
+ else
+ {
+ // high byte 40-7f
+ // low bit reset
+ // C:1, C:3
+ switch (T)
+ {
+ case 1:
+ case 2:
+ return true;
+ }
+ }
+ }
+ else
+ {
+ // high byte not 40-7f
+ if (lowBitSet)
+ {
+ // high byte not 40-7f
+ // low bit set
+ // N:4
+ }
+ else
+ {
+ // high byte not 40-7f
+ // low bit reset
+ // N:1, C:3
+ switch (T)
+ {
+ case 2:
+ return true;
+ }
+ }
+ }
+ break;
+
case MachineType.ZXSpectrum128:
case MachineType.ZXSpectrum128Plus2:
-
if ((lastPortAddr & 0xc000) == 0x4000)
highByte407f = true;
@@ -299,6 +355,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
case MachineType.ZXSpectrum128Plus2a:
case MachineType.ZXSpectrum128Plus3:
+ // No contention occurs as the ULA only applies contention when the Z80 MREQ line is active
+ // (which is not during an IO operation)
break;
}
diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs
index e953e2a9e0..0bec3a194c 100644
--- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs
+++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs
@@ -88,6 +88,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
///
public int InterruptLength;
+ ///
+ /// Arbitrary offset into the contention array (for memory ops)
+ ///
+ public int MemoryContentionOffset;
+
+ ///
+ /// Arbitrary offset into the contention array (for port ops)
+ ///
+ public int PortContentionOffset;
+
///
/// The time in T-States for one scanline to complete
///
@@ -776,8 +786,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
///
public int GetContentionValue(int tstate)
{
- int off = 5;
- tstate += off;
+ tstate += MemoryContentionOffset;
if (tstate >= FrameCycleLength)
tstate -= FrameCycleLength;
@@ -793,8 +802,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
///
public int GetPortContentionValue(int tstate)
{
- int off = 5;
- tstate += off;
+ tstate += PortContentionOffset;
if (tstate >= FrameCycleLength)
tstate -= FrameCycleLength;
diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs
index cbd98f1128..547e0fab72 100644
--- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs
+++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs
@@ -20,6 +20,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
InterruptLength = 36;
ScanlineTime = 228;
+ MemoryContentionOffset = 5;
+ PortContentionOffset = 5;
+
BorderLeftTime = 24;
BorderRightTime = 24;
diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs
index e9026ce5d7..949ca558ce 100644
--- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs
+++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs
@@ -20,6 +20,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
InterruptLength = 32;
ScanlineTime = 228;
+ MemoryContentionOffset = 7;
+ PortContentionOffset = 7;
+
BorderLeftTime = 24;
BorderRightTime = 24;
diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs
index 96219e04db..5af98f944a 100644
--- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs
+++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs
@@ -20,6 +20,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
InterruptLength = 32;
ScanlineTime = 224;
+ MemoryContentionOffset = 5;
+ PortContentionOffset = 5;
+
BorderLeftTime = 24;
BorderRightTime = 24;