Finished port IO contention rewrites
This commit is contained in:
parent
0bd433210e
commit
ccb5947ade
|
@ -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" />
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue