Finished port IO contention rewrites

This commit is contained in:
Asnivor 2018-03-12 12:00:17 +00:00
parent 0bd433210e
commit ccb5947ade
14 changed files with 299 additions and 527 deletions

View File

@ -256,7 +256,8 @@
<Compile Include="Computers\Commodore64\MOS\Vic.VideoProvider.cs" />
<Compile Include="Computers\Commodore64\SaveState.cs" />
<Compile Include="Computers\Commodore64\User\UserPortDevice.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\Input\IJoystick.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\Abstraction\IJoystick.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\Abstraction\IPortIODevice.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\Input\CursorJoystick.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\Input\SinclairJoystick2.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\Input\SinclairJoystick1.cs" />
@ -264,9 +265,9 @@
<Compile Include="Computers\SinclairSpectrum\Hardware\SoundOuput\AY38912.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\SoundOuput\Buzzer.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\Datacorder\DatacorderDevice.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\Input\IKeyboard.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\Abstraction\IKeyboard.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\Input\KempstonJoystick.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\SoundOuput\IBeeperDevice.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\Abstraction\IBeeperDevice.cs" />
<Compile Include="Computers\SinclairSpectrum\Machine\ULABase.cs" />
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum128KPlus2a\ZX128Plus2a.cs" />
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum128KPlus2a\ZX128Plus2a.Memory.cs" />

View File

@ -7,7 +7,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <summary>
/// Represents a spectrum keyboard
/// </summary>
public interface IKeyboard
public interface IKeyboard : IPortIODevice
{
/// <summary>
/// The calling spectrumbase class

View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Represents a device that utilizes port IN & OUT
/// </summary>
public interface IPortIODevice
{
/// <summary>
/// Device responds to an IN instruction
/// </summary>
/// <param name="port"></param>
/// <param name="result"></param>
/// <returns></returns>
bool ReadPort(ushort port, ref int result);
/// <summary>
/// Device responds to an OUT instruction
/// </summary>
/// <param name="port"></param>
/// <param name="result"></param>
/// <returns></returns>
bool WritePort(ushort port, int result);
}
}

View File

@ -12,7 +12,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <summary>
/// Represents the tape device (or build-in datacorder as it was called +2 and above)
/// </summary>
public class DatacorderDevice
public class DatacorderDevice : IPortIODevice
{
#region Construction
@ -627,6 +627,77 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
#endregion
#region IPortIODevice
/// <summary>
/// Mask constants
/// </summary>
private const int TAPE_BIT = 0x40;
private const int EAR_BIT = 0x10;
private const int MIC_BIT = 0x08;
/// <summary>
/// Device responds to an IN instruction
/// </summary>
/// <param name="port"></param>
/// <param name="result"></param>
/// <returns></returns>
public bool ReadPort(ushort port, ref int result)
{
if (TapeIsPlaying)
{
if (GetEarBit(_cpu.TotalExecutedCycles))
{
result &= ~(TAPE_BIT); // reset is EAR ON
}
else
{
result |= (TAPE_BIT); // set is EAR Off
}
}
else
{
if (_machine.KeyboardDevice.IsIssue2Keyboard)
{
if ((_machine.LASTULAOutByte & (EAR_BIT + MIC_BIT)) == 0)
{
result &= ~(TAPE_BIT);
}
else
{
result |= (TAPE_BIT);
}
}
else
{
if ((_machine.LASTULAOutByte & EAR_BIT) == 0)
{
result &= ~(TAPE_BIT);
}
else
{
result |= TAPE_BIT;
}
}
}
return true;
}
/// <summary>
/// Device responds to an OUT instruction
/// </summary>
/// <param name="port"></param>
/// <param name="result"></param>
/// <returns></returns>
public bool WritePort(ushort port, int result)
{
// not implemented yet
return false;
}
#endregion
#region State Serialization
/// <summary>

View File

@ -312,6 +312,96 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
return (byte)index;
}
#region IPortIODevice
/// <summary>
/// Device responds to an IN instruction
/// </summary>
/// <param name="port"></param>
/// <param name="result"></param>
/// <returns></returns>
public bool ReadPort(ushort port, ref int result)
{
/*
The high byte indicates which half-row of keys is being polled
A zero on one of these lines selects a particular half-row of five keys:
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
A zero in one of the five lowest bits means that the corresponding key is pressed. If more than one address line
is made low, the result is the logical AND of all single inputs, so a zero in a bit means that at least one of the
appropriate keys is pressed. For example, only if each of the five lowest bits of the result from reading from Port 00FE
(for instance by XOR A/IN A,(FE)) is one, no key is pressed
*/
if ((port & 0x8000) == 0)
{
result &= KeyLine[7];
}
if ((port & 0x4000) == 0)
{
result &= KeyLine[6];
}
if ((port & 0x2000) == 0)
{
result &= KeyLine[5];
}
if ((port & 0x1000) == 0)
{
result &= KeyLine[4];
}
if ((port & 0x800) == 0)
{
result &= KeyLine[3];
}
if ((port & 0x400) == 0)
{
result &= KeyLine[2];
}
if ((port & 0x200) == 0)
{
result &= KeyLine[1];
}
if ((port & 0x100) == 0)
{
result &= KeyLine[0];
}
// mask out lower 4 bits
result = result & 0x1f;
// set bit 5 & 7 to 1
result = result | 0xa0;
return true;
}
/// <summary>
/// Device responds to an OUT instruction
/// </summary>
/// <param name="port"></param>
/// <param name="result"></param>
/// <returns></returns>
public bool WritePort(ushort port, int result)
{
// not implemented
return false;
}
#endregion
public void SyncState(Serializer ser)
{
ser.BeginSection("Keyboard");

View File

@ -16,6 +16,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// The last OUT data that was sent to the ULA
/// </summary>
protected byte LastULAOutByte;
public byte LASTULAOutByte
{
get { return LastULAOutByte; }
set { LastULAOutByte = value; }
}
/// <summary>
/// Reads a byte of data from a specified port address
@ -42,7 +48,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <summary>
/// Simulates IO port contention based on the supplied address
/// This method is for 48k machines and should be overridden for other models
/// This method is for 48k and 128k/+2 machines only and should be overridden for other models
/// </summary>
/// <param name="addr"></param>
public virtual void ContendPortAddress(ushort addr)

View File

@ -16,12 +16,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <returns></returns>
public override byte ReadPort(ushort port)
{
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);
// process IO contention
ContendPortAddress(port);
int result = 0xFF;
@ -29,10 +25,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// Technically the ULA should respond to every even I/O address
bool lowBitReset = (port & 0x0001) == 0;
ULADevice.Contend(port);
//CPU.TotalExecutedCycles++;
// Kempston Joystick
// Kempston joystick input takes priority over all other input
// if this is detected just return the kempston byte
if ((port & 0xe0) == 0 || (port & 0x20) == 0)
{
if (LocateUniqueJoystick(JoystickType.Kempston) != null)
@ -42,104 +36,22 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
}
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
*/
// Even I/O address so get input from keyboard
KeyboardDevice.ReadPort(port, ref result);
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
// not a lagframe
InputRead = true;
// tape loading monitor cycle
TapeDevice.MonitorRead();
if (TapeDevice.TapeIsPlaying)//.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;
}
}
}
// process tape INs
TapeDevice.ReadPort(port, ref result);
}
else
{
// devices other than the ULA will respond here
// (e.g. the AY sound chip in a 128k spectrum
// (e.g. the AY sound chip in a 128k spectrum)
// AY register activate
if ((port & 0xc002) == 0xc000)
@ -147,11 +59,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
result = (int)AYDevice.PortRead();
}
// Kempston Mouse
// Kempston Mouse (not implemented yet)
// if unused port the floating memory bus should be returned
// If this is an unused port the floating memory bus should be returned
// Floating bus is read on the previous cycle
int _tStates = CurrentFrameCycle - 1;
@ -183,6 +94,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <param name="value"></param>
public override void WritePort(ushort port, byte value)
{
// process IO contention
ContendPortAddress(port);
// get a BitArray of the port
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
// get a BitArray of the value byte
@ -225,8 +139,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// Technically the ULA should respond to every even I/O address
bool lowBitReset = !portBits[0]; // (port & 0x01) == 0;
ULADevice.Contend(port);
// Only even addresses address the ULA
if (lowBitReset)
{

View File

@ -16,7 +16,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <returns></returns>
public override byte ReadPort(ushort port)
{
InputRead = true;
// process IO contention
ContendPortAddress(port);
int result = 0xFF;
@ -24,9 +25,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// Technically the ULA should respond to every even I/O address
bool lowBitReset = (port & 0x0001) == 0;
ULADevice.Contend(port);
// Kempston Joystick
// Kempston joystick input takes priority over all other input
// if this is detected just return the kempston byte
if ((port & 0xe0) == 0 || (port & 0x20) == 0)
{
if (LocateUniqueJoystick(JoystickType.Kempston) != null)
@ -36,81 +36,19 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
}
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
// Even I/O address so get input from keyboard
KeyboardDevice.ReadPort(port, ref result);
TapeDevice.MonitorRead();
if (TapeDevice.TapeIsPlaying)//.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 ((LastULAOutByte & 0x10) == 0)
{
result &= ~(0x40);
}
else
{
result |= 0x40;
}
// not a lagframe
InputRead = true;
// tape loading monitor cycle
TapeDevice.MonitorRead();
// process tape INs
TapeDevice.ReadPort(port, ref result);
}
else
{
@ -445,5 +383,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
}
set { ROMPaged = value; }
}
/// <summary>
/// Override port contention
/// +3/2a does not have the same ULA IO contention
/// </summary>
/// <param name="addr"></param>
public override void ContendPortAddress(ushort addr)
{
CPU.TotalExecutedCycles += 4;
}
}
}

View File

@ -16,7 +16,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <returns></returns>
public override byte ReadPort(ushort port)
{
InputRead = true;
// process IO contention
ContendPortAddress(port);
int result = 0xFF;
@ -24,9 +25,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// Technically the ULA should respond to every even I/O address
bool lowBitReset = (port & 0x0001) == 0;
ULADevice.Contend(port);
// Kempston Joystick
// Kempston joystick input takes priority over all other input
// if this is detected just return the kempston byte
if ((port & 0xe0) == 0 || (port & 0x20) == 0)
{
if (LocateUniqueJoystick(JoystickType.Kempston) != null)
@ -36,82 +36,19 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
}
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
// Even I/O address so get input from keyboard
KeyboardDevice.ReadPort(port, ref result);
TapeDevice.MonitorRead();
if (TapeDevice.TapeIsPlaying)//.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 ((LastULAOutByte & 0x10) == 0)
{
result &= ~(0x40);
}
else
{
result |= 0x40;
}
// not a lagframe
InputRead = true;
// tape loading monitor cycle
TapeDevice.MonitorRead();
// process tape INs
TapeDevice.ReadPort(port, ref result);
}
else
{
@ -128,7 +65,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
result = (int)AYDevice.PortRead();
}
// Kempston Mouse
// Kempston Mouse (not implemented yet)
else if ((port & 0xF002) == 0x2000) //Is bit 12 set and bits 13,14,15 and 1 reset?
@ -165,6 +102,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <param name="value"></param>
public override void WritePort(ushort port, byte value)
{
// process IO contention
ContendPortAddress(port);
// get a BitArray of the port
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
// get a BitArray of the value byte
@ -173,8 +113,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// Check whether the low bit is reset
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)
{
@ -234,76 +172,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// 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)
{
@ -346,84 +215,10 @@ 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?
{
// memory paging activate
if (PagingDisabled)
return;
// bit 5 handles paging disable (48k mode, persistent until next reboot)
if ((value & 0x20) != 0)
{
PagingDisabled = true;
}
// shadow screen
if ((value & 0x08) != 0)
{
SHADOWPaged = true;
}
else
{
SHADOWPaged = false;
}
}
else
{
//Extra Memory Paging feature activate
if ((port & 0xF002) == 0x1000) //Is bit 12 set and bits 13,14,15 and 1 reset?
{
if (PagingDisabled)
return;
// set disk motor state
//todo
if ((value & 0x08) != 0)
{
//diskDriveState |= (1 << 4);
}
else
{
//diskDriveState &= ~(1 << 4);
}
if ((value & 0x1) != 0)
{
// activate special paging mode
SpecialPagingMode = true;
PagingConfiguration = (value & 0x6 >> 1);
}
else
{
// normal paging mode
SpecialPagingMode = false;
}
}
else
{
// disk write port
if ((port & 0xF002) == 0x3000) //Is bit 12 set and bits 13,14,15 and 1 reset?
{
//udpDrive.DiskWriteByte((byte)(val & 0xff));
}
}
}
}
*/
}
}
LastULAOutByte = value;
}
/// <summary>
@ -445,5 +240,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
}
set { ROMPaged = value; }
}
/// <summary>
/// Override port contention
/// +3/2a does not have the same ULA IO contention
/// </summary>
/// <param name="addr"></param>
public override void ContendPortAddress(ushort addr)
{
CPU.TotalExecutedCycles += 4;
}
}
}

View File

@ -15,8 +15,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <returns></returns>
public override byte ReadPort(ushort port)
{
InputRead = true;
// process IO contention
ContendPortAddress(port);
@ -26,132 +24,41 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// Technically the ULA should respond to every even I/O address
bool lowBitReset = (port & 0x0001) == 0;
// Kempston Joystick
// Kempston joystick input takes priority over all other input
// if this is detected just return the kempston byte
if ((port & 0xe0) == 0 || (port & 0x20) == 0)
{
if (LocateUniqueJoystick(JoystickType.Kempston) != null)
return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine;
// not a lag frame
InputRead = true;
}
else if (lowBitReset)
{
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
// Even I/O address so get input from keyboard
KeyboardDevice.ReadPort(port, ref result);
// 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
*/
// not a lagframe
InputRead = true;
if ((port & 0x8000) == 0)
{
result &= KeyboardDevice.KeyLine[7];
}
// tape loading monitor cycle
TapeDevice.MonitorRead();
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];
}
TapeDevice.MonitorRead();
var earBit = TapeDevice.GetEarBit(CPU.TotalExecutedCycles);
if (!earBit)
{
result &= 0xbf;
}
/*
result = result & 0x1f; //mask out lower 4 bits
result = result | 0xa0; //set bit 5 & 7 to 1
if (TapeDevice.TapeIsPlaying)//.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;
}
}
}
*/
// process tape INs
TapeDevice.ReadPort(port, ref result);
}
else
{
// devices other than the ULA will respond here
// (e.g. the AY sound chip in a 128k spectrum
// AY register activate
// Kemptson Mouse
// AY register activate - no AY chip in a 48k spectrum
// Kemptson Mouse (not implemented yet)
// if unused port the floating memory bus should be returned
// If this is an unused port the floating memory bus should be returned
// Floating bus is read on the previous cycle
int _tStates = CurrentFrameCycle - 1;
@ -203,14 +110,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// Border - LSB 3 bits hold the border colour
if (ULADevice.borderColour != (value & BORDER_BIT))
{
// border value has changed - update the screen buffer
ULADevice.UpdateScreenBuffer(CurrentFrameCycle);
}
ULADevice.borderColour = value & BORDER_BIT;
// Buzzer
BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0);
// Tape
// Tape mic processing (not implemented yet)
//TapeDevice.ProcessMicBit((value & MIC_BIT) != 0);
}

View File

@ -86,8 +86,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
_cpu.MemoryCallbacks = MemoryCallbacks;
HardReset = _machine.HardReset;
SoftReset = _machine.SoftReset;
//HardReset = _machine.HardReset;
//SoftReset = _machine.SoftReset;
_cpu.FetchMemory = _machine.ReadMemory;
_cpu.ReadMemory = _machine.ReadMemory;
@ -109,13 +109,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
HardReset();
//HardReset();
SetupMemoryDomains();
}
public Action HardReset;
public Action SoftReset;
//public Action HardReset;
//public Action SoftReset;
private readonly Z80A _cpu;
private readonly TraceBuffer _tracer;

View File

@ -2,7 +2,7 @@
At the moment this is experimental and is still being worked on.
### Implemented and sorta working
### Implemented and working (as far as I can tell)
* IEmulator
* ZX Spectrum 48k, 128k, +2 & +2A models
* ULA video output (implementing IVideoProvider)
@ -14,12 +14,12 @@ At the moment this is experimental and is still being worked on.
* Default keyboard keymappings
* Kempston, Cursor and Sinclair (Left & Right) joysticks emulated
* Tape device that will load spectrum games in realtime (*.tzx and *.tap)
* Most tape protection/loading schemes that I've tested are currently working (see caveat below)
* Most tape protection/loading schemes that I've tested are currently working
* IStatable
* ISettable core settings
* IDebuggable (for what it's worth)
* DeterministicEmulation as a SyncSetting, LagFrame detection and FrameAdvance render & renderSound bools respected (when DeterministicEmulation == false)
* Tape auto-loading routines (as a setting)
* Tape auto-loading routines (as a setting - default ON)
* Basic tape block navigation (NextBlock, PrevBlock)
* Tape-related OSD messages (verbosity level configured in settings)
@ -32,7 +32,6 @@ At the moment this is experimental and is still being worked on.
### Not working
* +3 disk drive - no implementation yet
* Hard & Soft Reset menu options in the client (they are greyed out for some reason)
* Speedlock tape protection scheme doesn't appear to load correctly
### Help needed
* I'm not a TASer, i've never TASed before. It would be really useful if someone (anyone) can build this branch and test this core from a TAS-workflow / TAStudio perpective. There may still be some work to do an exact timings and memory contention, but otherwise this core is able to play the majority of speccy games out there.