From f494b58f3b4770acf211a49136ce818c1d0d7dec Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 21 Nov 2017 20:22:56 -0500 Subject: [PATCH] GBHAwk more work: -Add basic serial port support -Fix Mortal Kombat -Add controller IRQs -Fix contols and Bomberman --- .../BizHawk.Emulation.Cores.csproj | 1 + .../Nintendo/GBHawk/GBHawk.IEmulator.cs | 50 +++++-- .../Nintendo/GBHawk/GBHawk.IStatable.cs | 6 +- .../Consoles/Nintendo/GBHawk/GBHawk.cs | 9 +- .../Consoles/Nintendo/GBHawk/HW_Registers.cs | 12 +- .../Consoles/Nintendo/GBHawk/SerialPort.cs | 137 ++++++++++++++++++ 6 files changed, 187 insertions(+), 28 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/SerialPort.cs 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); + } + } +}