BizHawk/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/SerialPort.cs

176 lines
3.8 KiB
C#

using BizHawk.Common.NumberExtensions;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
{
public class SerialPort
{
public GBHawk Core { get; set; }
public byte serial_control;
public byte serial_data;
public bool serial_start;
public bool can_pulse;
public int serial_clock;
public int serial_bits;
public int clk_rate;
public byte going_out;
public byte coming_in;
public byte ReadReg(int addr)
{
switch (addr)
{
case 0xFF01:
return serial_data;
case 0xFF02:
return serial_control;
}
return 0xFF;
}
public void WriteReg(int addr, byte value)
{
switch (addr)
{
case 0xFF01:
serial_data = value;
break;
case 0xFF02:
if (((value & 0x80) > 0) && !serial_start)
{
serial_start = true;
serial_bits = 8;
if ((value & 1) > 0)
{
if (((value & 2) > 0) && Core.GBC_compat)
{
clk_rate = 16;
serial_clock = (16 - (int)(Core.cpu.TotalExecutedCycles % 8)) + 1;
}
else
{
clk_rate = 512;
serial_clock = 512 - (int)(Core.cpu.TotalExecutedCycles % 256) + 1;
// there seems to be some clock inverting happening on some transfers
// not sure of the exact nature of it, here is one methor that gives correct result on one test rom but not others
/*
if (Core._syncSettings.GBACGB && Core.is_GBC)
{
if ((Core.TotalExecutedCycles % 256) > 127)
{
serial_clock = (8 - (int)(Core.cpu.TotalExecutedCycles % 8)) + 1;
}
}
*/
}
can_pulse = true;
}
else
{
clk_rate = -1;
serial_clock = clk_rate;
can_pulse = false;
}
}
else if (serial_start)
{
if ((value & 1) > 0)
{
if (((value & 2) > 0) && Core.GBC_compat)
{
clk_rate = 16;
serial_clock = (16 - (int)(Core.cpu.TotalExecutedCycles % 8)) + 1;
}
else
{
clk_rate = 512;
serial_clock = 512 - (int)(Core.cpu.TotalExecutedCycles % 256) + 1;
}
can_pulse = true;
}
else
{
clk_rate = -1;
serial_clock = clk_rate;
can_pulse = false;
}
}
if (Core.GBC_compat)
{
serial_control = (byte)(0x7C | (value & 0x83)); // extra CGB bit
}
else
{
serial_control = (byte)(0x7E | (value & 0x81)); // middle six bits always 1
}
break;
}
}
public void serial_transfer_tick()
{
if (serial_start)
{
if (serial_clock > 0) { serial_clock--; }
if (serial_clock == 0)
{
if (serial_bits > 0)
{
serial_data = (byte)((serial_data << 1) | coming_in);
serial_bits--;
if (serial_bits == 0)
{
serial_control &= 0x7F;
serial_start = false;
if (Core.REG_FFFF.Bit(3)) { Core.cpu.FlagI = true; }
Core.REG_FF0F |= 0x08;
}
else
{
serial_clock = clk_rate;
if (clk_rate > 0) { can_pulse = true; }
}
}
}
}
}
public void Reset()
{
serial_control = 0x7E;
serial_data = 0x00;
serial_start = false;
serial_clock = 0;
serial_bits = 0;
clk_rate = 16;
going_out = 0;
coming_in = 1;
can_pulse = false;
}
public void SyncState(Serializer ser)
{
ser.Sync(nameof(serial_control), ref serial_control);
ser.Sync(nameof(serial_data), ref serial_data);
ser.Sync(nameof(serial_start), ref serial_start);
ser.Sync(nameof(serial_clock), ref serial_clock);
ser.Sync(nameof(serial_bits), ref serial_bits);
ser.Sync(nameof(clk_rate), ref clk_rate);
ser.Sync(nameof(going_out), ref going_out);
ser.Sync(nameof(coming_in), ref coming_in);
ser.Sync(nameof(can_pulse), ref can_pulse);
}
}
}