diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
index 05baf2d9d0..89b09fb05a 100644
--- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
+++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
@@ -582,6 +582,7 @@
+
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs
index 77fe21cc28..5902b22337 100644
--- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs
@@ -16,6 +16,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
public bool in_vblank_old;
public bool in_vblank;
public bool vblank_rise;
+ bool contr_check_once;
public void FrameAdvance(IController controller, bool render, bool rendersound)
{
@@ -56,16 +57,54 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
// gameboy frames can be variable lengths
// we want to end a frame when VBlank turns from false to true
int ticker = 0;
+ contr_check_once = true;
while (!vblank_rise && (ticker < 100000))
{
audio.tick();
timer.tick_1();
ppu.tick();
+ serialport.serial_transfer_tick();
cpu.ExecuteOne(ref REG_FF0F, REG_FFFF);
timer.tick_2();
+ // check for controller interrupts somewhere around the middle of the frame
+ if ((cpu.LY == 50) && contr_check_once)
+ {
+ byte contr_prev = input_register;
+
+ input_register &= 0xF0;
+ if ((input_register & 0x30) == 0x20)
+ {
+ input_register |= (byte)(controller_state & 0xF);
+ }
+ else if ((input_register & 0x30) == 0x10)
+ {
+ input_register |= (byte)((controller_state & 0xF0) >> 4);
+ }
+ else if ((input_register & 0x30) == 0x00)
+ {
+ // if both polls are set, then a bit is zero if either or both pins are zero
+ byte temp = (byte)((controller_state & 0xF) & ((controller_state & 0xF0) >> 4));
+ input_register |= temp;
+ }
+ else
+ {
+ input_register |= 0xF;
+ }
+
+ // check for interrupts
+ if (((contr_prev & 8) > 0) && ((input_register & 8) == 0) ||
+ ((contr_prev & 4) > 0) && ((input_register & 4) == 0) ||
+ ((contr_prev & 2) > 0) && ((input_register & 2) == 0) ||
+ ((contr_prev & 2) > 0) && ((input_register & 1) == 0))
+ {
+ if (REG_FFFF.Bit(4)) { cpu.FlagI = true; }
+ REG_FF0F |= 0x10;
+ }
+ }
+
if (in_vblank && !in_vblank_old)
{
@@ -98,17 +137,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
controller_state_old = controller_state;
}
- public void serial_transfer()
- {
- if (serial_control.Bit(7) && !serial_start_old)
- {
- serial_start_old = true;
-
- // transfer out on byte of data
- // needs to be modelled
- }
- }
-
public int Frame => _frame;
public string SystemId => "GB";
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs
index c228de6e1e..6fe619718d 100644
--- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs
@@ -51,6 +51,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
mapper.SyncState(ser);
timer.SyncState(ser);
ppu.SyncState(ser);
+ serialport.SyncState(ser);
audio.SyncState(ser);
ser.BeginSection("Gameboy");
@@ -68,11 +69,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
ser.Sync("GB_bios_register", ref GB_bios_register);
ser.Sync("input_register", ref input_register);
- ser.Sync("serial_control", ref serial_control);
- ser.Sync("serial_data_out", ref serial_data_out);
- ser.Sync("serial_data_in", ref serial_data_in);
- ser.Sync("serial_start_old", ref serial_start_old);
-
ser.Sync("REG_FFFF", ref REG_FFFF);
ser.Sync("REG_FF0F", ref REG_FF0F);
ser.Sync("enable_VBL", ref enable_VBL);
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs
index 5a8c0eb3cc..5a4da37c45 100644
--- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs
@@ -21,11 +21,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
public byte input_register;
- public byte serial_control;
- public byte serial_data_out;
- public byte serial_data_in;
- public bool serial_start_old;
-
// The unused bits in this register are still read/writable
public byte REG_FFFF;
// The unused bits in this register (interrupt flags) are always set
@@ -62,6 +57,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
public PPU ppu;
public Timer timer;
public Audio audio;
+ public SerialPort serialport;
[CoreConstructor("GB")]
public GBHawk(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ object settings, object syncSettings)
@@ -79,6 +75,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
ppu = new PPU();
timer = new Timer();
audio = new Audio();
+ serialport = new SerialPort();
CoreComm = comm;
@@ -109,6 +106,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
timer.Core = this;
audio.Core = this;
ppu.Core = this;
+ serialport.Core = this;
ser.Register(this);
ser.Register(audio);
@@ -138,6 +136,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
timer.Reset();
ppu.Reset();
audio.Reset();
+ serialport.Reset();
cpu.SetCallbacks(ReadMemory, ReadMemory, ReadMemory, WriteMemory);
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs
index a55e91798e..e24acbe22d 100644
--- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs
@@ -26,7 +26,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
{
input_register |= (byte)((controller_state & 0xF0) >> 4);
}
- else if ((input_register & 0x30) == 0x30)
+ else if ((input_register & 0x30) == 0x00)
{
// if both polls are set, then a bit is zero if either or both pins are zero
byte temp = (byte)((controller_state & 0xF) & ((controller_state & 0xF0) >> 4));
@@ -41,12 +41,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
// Serial data port
case 0xFF01:
- ret = serial_data_in;
+ ret = serialport.ReadReg(addr);
break;
// Serial port control
case 0xFF02:
- ret = serial_control;
+ ret = serialport.ReadReg(addr);
break;
// Timer Registers
@@ -148,13 +148,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
// Serial data port
case 0xFF01:
- serial_data_out = value;
- serial_data_in = serial_data_out;
+ serialport.WriteReg(addr, value);
break;
// Serial port control
case 0xFF02:
- serial_control = (byte)(0x7E | (value & 0x81)); // middle six bits always 1
+ serialport.WriteReg(addr, value);
break;
// Timer Registers
@@ -277,7 +276,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
public void Register_Reset()
{
input_register = 0xCF; // not reading any input
- serial_control = 0x7E;
}
}
}
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/SerialPort.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/SerialPort.cs
new file mode 100644
index 0000000000..cbdc837a36
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/SerialPort.cs
@@ -0,0 +1,137 @@
+using System;
+using BizHawk.Emulation.Common;
+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 int serial_clock;
+ public int serial_bits;
+ public bool clk_internal;
+ public int clk_rate;
+
+ public byte ReadReg(int addr)
+ {
+ byte ret = 0;
+
+ switch (addr)
+ {
+ case 0xFF01:
+ return serial_data;
+ case 0xFF02:
+ return serial_control;
+ }
+
+ return ret;
+ }
+
+ 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)
+ {
+ clk_internal = true;
+ clk_rate = 512;
+ serial_clock = clk_rate;
+ }
+ else
+ {
+ clk_internal = false;
+ clk_rate = get_external_clock();
+ serial_clock = clk_rate;
+ }
+ }
+
+ serial_control = (byte)(0x7E | (value & 0x81)); // middle six bits always 1
+ break;
+ }
+ }
+
+
+ public void serial_transfer_tick()
+ {
+ if (serial_start)
+ {
+ serial_clock--;
+ if (serial_clock == 0)
+ {
+ if (serial_bits > 0)
+ {
+ send_external_bit((byte)(serial_data & 0x80));
+
+ byte temp = get_external_bit();
+ serial_data = (byte)((serial_data << 1) | temp);
+
+ 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;
+ }
+ }
+ }
+ }
+ }
+
+ // call this function to get the clock rate of a connected device
+ // internal rate is 512
+ public int get_external_clock()
+ {
+ return 512;
+ }
+
+ // call this function to get the next bit from the connected device
+ // no device connected returns 0xFF
+ public byte get_external_bit()
+ {
+ return 1;
+ }
+
+ public void send_external_bit(byte bit_send)
+ {
+
+ }
+
+ public void Reset()
+ {
+ serial_control = 0x7E;
+ serial_start = false;
+ serial_data = 0xFF;
+ }
+
+ public void SyncState(Serializer ser)
+ {
+ ser.Sync("serial_control", ref serial_control);
+ ser.Sync("serial_data", ref serial_data);
+ ser.Sync("serial_start", ref serial_start);
+ ser.Sync("serial_clock", ref serial_clock);
+ ser.Sync("serial_bits", ref serial_bits);
+ ser.Sync("clk_internal", ref clk_internal);
+ ser.Sync("clk_rate", ref clk_rate);
+ }
+ }
+}