GBHAwk more work:

-Add basic serial port support
-Fix Mortal Kombat
-Add controller IRQs
-Fix contols and Bomberman
This commit is contained in:
alyosha-tas 2017-11-21 20:22:56 -05:00
parent 63f8fc0d90
commit f494b58f3b
6 changed files with 187 additions and 28 deletions

View File

@ -582,6 +582,7 @@
</Compile>
<Compile Include="Consoles\Nintendo\GBA\VBARegisterHelper.cs" />
<Compile Include="Consoles\Nintendo\GBHawk\Audio.cs" />
<Compile Include="Consoles\Nintendo\GBHawk\SerialPort.cs" />
<Compile Include="Consoles\Nintendo\GBHawk\GBHawk.cs" />
<Compile Include="Consoles\Nintendo\GBHawk\GBHawk.IDebuggable.cs" />
<Compile Include="Consoles\Nintendo\GBHawk\GBHawk.IEmulator.cs" />

View File

@ -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";

View File

@ -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);

View File

@ -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<IVideoProvider>(this);
ser.Register<ISoundProvider>(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);

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}