BizHawk/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs

194 lines
6.2 KiB
C#
Raw Normal View History

2017-11-23 17:26:15 +00:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// The abstract class that all emulated models will inherit from
/// * Port Access *
/// </summary>
public abstract partial class SpectrumBase
{
/// <summary>
/// The last OUT data that was sent to the ULA
/// </summary>
protected byte LastULAOutByte;
/// <summary>
/// Reads a byte of data from a specified port address
/// </summary>
/// <param name="port"></param>
/// <returns></returns>
public virtual byte ReadPort(ushort port)
{
int result = 0xFF;
2017-11-23 17:26:15 +00:00
// Check whether the low bit is reset
// Technically the ULA should respond to every even I/O address
2017-12-01 14:34:45 +00:00
bool lowBitReset = (port & 0x0001) == 0;
2017-12-01 15:34:47 +00:00
ContendPort((ushort)port);
2017-11-23 17:26:15 +00:00
// Kempston Joystick
if ((port & 0xe0) == 0 || (port & 0x20) == 0)
2017-12-01 14:34:45 +00:00
{
return (byte)KempstonDevice.JoyLine;
2017-12-01 14:34:45 +00:00
}
else if (lowBitReset)
{
2017-11-23 17:26:15 +00:00
// 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
2017-12-01 15:34:47 +00:00
*/
2017-11-23 17:26:15 +00:00
2017-12-01 15:34:47 +00:00
if ((port & 0x8000) == 0)
2017-12-01 11:36:57 +00:00
result &= KeyboardDevice.KeyLine[7];
2017-12-01 15:34:47 +00:00
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
2017-12-01 11:36:57 +00:00
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;
}
}
}
2017-12-01 11:36:57 +00:00
2017-11-23 17:26:15 +00:00
}
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)
}
2017-12-01 11:36:57 +00:00
return (byte)result;
2017-11-23 17:26:15 +00:00
}
/// <summary>
/// Writes a byte of data to a specified port address
/// </summary>
/// <param name="port"></param>
/// <param name="value"></param>
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
2017-12-01 14:34:45 +00:00
bool lowBitReset = (port & 0x01) == 0;
ContendPort(port);
2017-11-23 17:26:15 +00:00
// 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 |
+-------------------------------+
*/
2017-11-23 17:26:15 +00:00
// Border - LSB 3 bits hold the border colour
BorderColour = value & BORDER_BIT;
2017-11-28 19:28:22 +00:00
// Buzzer
BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0);
2017-11-23 17:26:15 +00:00
2017-11-28 19:28:22 +00:00
// Tape
TapeDevice.ProcessMicBit((value & MIC_BIT) != 0);
2017-11-23 17:26:15 +00:00
}
}
2017-12-01 14:34:45 +00:00
/// <summary>
/// Apply I/O contention if necessary
/// </summary>
/// <param name="port"></param>
public virtual void ContendPort(ushort port)
{
var lowBit = (port & 0x0001) != 0;
var ulaHigh = (port & 0xc000) == 0x4000;
var cfc = CurrentFrameCycle;
if (cfc < 1)
cfc = 1;
if (ulaHigh)
{
CPU.TotalExecutedCycles += GetContentionValue(cfc - 1);
}
else
{
if (!lowBit)
CPU.TotalExecutedCycles += GetContentionValue(cfc);
}
}
2017-11-23 17:26:15 +00:00
}
}