diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index 0a39d9269c..14c436a3ac 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -230,7 +230,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // change the appropriate palette color color_compute_BG(); - if (BG_bytes_inc) { BG_bytes_index++; BG_bytes_index &= 0x3F; } break; case 0xFF6A: // OBPI @@ -272,11 +271,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk cur_DMA_src = (ushort)((cur_DMA_src + 1) & 0xFFFF); HDMA_length--; } + + HDMA_tick++; } else { // only transfer during mode 0, and only 16 bytes at a time - if (((STAT & 3) == 0) && (LY != last_HBL) && HBL_test) + if (((STAT & 3) == 0) && (LY != last_HBL) && HBL_test && (LY_inc == 1)) { HBL_HDMA_go = true; HBL_test = false; @@ -306,14 +307,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk HBL_HDMA_count = 0x10; HBL_HDMA_go = false; } + + HDMA_tick++; } else { Core.HDMA_transfer = false; } - } - - HDMA_tick++; + } } else { diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs index a57581e6ae..b5e3dc33e9 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs @@ -13,6 +13,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public ControllerDefinition ControllerDefinition => _controllerDeck.Definition; public byte controller_state; + public ushort Acc_X_state; + public ushort Acc_Y_state; public bool in_vblank_old; public bool in_vblank; public bool vblank_rise; @@ -188,6 +190,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { InputCallbacks.Call(); controller_state = _controllerDeck.ReadPort1(controller); + + Acc_X_state = _controllerDeck.ReadAccX1(controller); + Acc_Y_state = _controllerDeck.ReadAccY1(controller); } public int Frame => _frame; diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs index f1d1cf846c..5de963bc1f 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs @@ -58,20 +58,30 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public class GBSyncSettings { - private string _port1 = GBHawkControllerDeck.DefaultControllerName; + [JsonIgnore] + public string Port1 = GBHawkControllerDeck.DefaultControllerName; + + public enum ControllerType + { + Default, + Kirby + } [JsonIgnore] - public string Port1 + private ControllerType _GBController; + + [DisplayName("Controller")] + [Description("Select Controller Type")] + [DefaultValue(ControllerType.Default)] + public ControllerType GBController { - get { return _port1; } + get { return _GBController; } set { - if (!GBHawkControllerDeck.ValidControllerTypes.ContainsKey(value)) - { - throw new InvalidOperationException("Invalid controller type: " + value); - } + if (value == ControllerType.Default) { Port1 = GBHawkControllerDeck.DefaultControllerName; } + else { Port1 = "Gameboy Controller + Kirby"; } - _port1 = value; + _GBController = value; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs index 749164d070..ac8bfc95b8 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs @@ -62,6 +62,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk _controllerDeck.SyncState(ser); ser.Sync("controller_state", ref controller_state); + ser.Sync("Acc_X_state", ref Acc_X_state); + ser.Sync("Acc_Y_state", ref Acc_Y_state); ser.Sync("in_vblank", ref in_vblank); ser.Sync("in_vblank_old", ref in_vblank_old); ser.Sync("vblank_rise", ref vblank_rise); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs index 6becfb67af..8ebc54ba0f 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -415,10 +415,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk cart_RAM = new byte[0x200]; } + // mbc7 has 256 bytes of RAM, regardless of any header info + if (mppr == "MBC7") + { + cart_RAM = new byte[0x100]; + has_bat = true; + } + mapper.Core = this; mapper.Initialize(); - if (cart_RAM != null) + if (cart_RAM != null && (mppr != "MBC7")) { Console.Write("RAM: "); Console.WriteLine(cart_RAM.Length); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllerDeck.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllerDeck.cs index 684b022ab8..5f21be47e0 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllerDeck.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllerDeck.cs @@ -36,6 +36,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk return Port1.Read(c); } + public ushort ReadAccX1(IController c) + { + return Port1.ReadAccX(c); + } + + public ushort ReadAccY1(IController c) + { + return Port1.ReadAccY(c); + } + public ControllerDefinition Definition { get; } public void SyncState(Serializer ser) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs index cd7000330c..2a224ba58e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs @@ -119,7 +119,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk .Select(b => "P" + PortNum + " " + b) .ToList(), FloatControls = { "P" + PortNum + " Tilt X", "P" + PortNum + " Tilt Y" }, - FloatRanges = { new[] { -127.0f, 0, 127.0f }, new[] { -127.0f, 0, 127.0f } } + FloatRanges = { new[] { -45.0f, 0, 45.0f }, new[] { -45.0f, 0, 45.0f } } }; } @@ -167,14 +167,23 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk return result; } + // acc x is the result of rotating around body y AFTER rotating around body x + // therefore this control scheme gives decreasing sensitivity in X as Y rotation inscreases public ushort ReadAccX(IController c) { - return 0; + double theta = c.GetFloat(Definition.FloatControls[1]) * Math.PI / 180.0; + double phi = c.GetFloat(Definition.FloatControls[0]) * Math.PI / 180.0; + + float temp = (float)(Math.Cos(theta) * Math.Sin(phi)); + + return (ushort)(0x81D0 - Math.Floor(temp * 125)); } + // acc y is just the sine of the angle public ushort ReadAccY(IController c) { - return 0; + float temp = (float)Math.Sin(c.GetFloat(Definition.FloatControls[1]) * Math.PI / 180.0); + return (ushort)(0x81D0 - Math.Floor(temp * 125)); } private static readonly string[] BaseDefinition = diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs index 84389700b9..3875ec6482 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs @@ -361,7 +361,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // RAM Bank in GBC mode case 0xFF70: //Console.WriteLine(value); - if (is_GBC && !ppu.HDMA_active) + if (is_GBC) { RAM_Bank = value & 7; if (RAM_Bank == 0) { RAM_Bank = 1; } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs index 28114486dc..db108521ee 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs @@ -17,6 +17,24 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public byte acc_y_high; public bool is_erased; + // EEPROM related + public bool CS_prev; + public bool CLK_prev; + public bool DI_prev; + public bool DO; + public bool instr_read; + public bool perf_instr; + public int instr_bit_counter; + public int instr; + public bool WR_EN; + public int EE_addr; + public int instr_case; + public int instr_clocks; + public int EE_value; + public int countdown; + public bool countdown_start; + + public override void Initialize() { ROM_bank = 1; @@ -80,6 +98,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { value &= 0xFF; + //Console.WriteLine(Core.cpu.TotalExecutedCycles); + //Console.WriteLine(value); + ROM_bank &= 0x100; ROM_bank |= value; ROM_bank &= ROM_mask; @@ -117,32 +138,22 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("acc_y_low", ref acc_y_low); ser.Sync("acc_y_high", ref acc_y_high); ser.Sync("is_erased", ref is_erased); - } - public void Register_Access_Write(ushort addr, byte value) - { - if ((addr & 0xA0F0) == 0xA000) - { - if (value == 0x55) - { - is_erased = true; - acc_x_low = 0; - acc_x_high = 0x80; - acc_y_low = 0; - acc_y_high = 0x80; - } - } - else if ((addr & 0xA0F0) == 0xA010) - { - if ((value == 0xAA) && is_erased) - { - // latch new accelerometer values - } - } - else if ((addr & 0xA0F0) == 0xA080) - { - - } + ser.Sync("CS_prev", ref CS_prev); + ser.Sync("CLK_prev", ref CLK_prev); + ser.Sync("DI_prev", ref DI_prev); + ser.Sync("DO", ref DO); + ser.Sync("instr_read", ref instr_read); + ser.Sync("perf_instr", ref perf_instr); + ser.Sync("instr_bit_counter", ref instr_bit_counter); + ser.Sync("instr", ref instr); + ser.Sync("WR_EN", ref WR_EN); + ser.Sync("EE_addr", ref EE_addr); + ser.Sync("instr_case", ref instr_case); + ser.Sync("instr_clocks", ref instr_clocks); + ser.Sync("EE_value", ref EE_value); + ser.Sync("countdown", ref countdown); + ser.Sync("countdown_start", ref countdown_start); } public byte Register_Access_Read(ushort addr) @@ -181,7 +192,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else if ((addr & 0xA0F0) == 0xA080) { - return 0xFF; + return (byte)((CS_prev ? 0x80 : 0) | + (CLK_prev ? 0x40 : 0) | + (DI_prev ? 2 : 0) | + (DO ? 1 : 0)); } else { @@ -189,6 +203,240 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } + public void Register_Access_Write(ushort addr, byte value) + { + if ((addr & 0xA0F0) == 0xA000) + { + if (value == 0x55) + { + is_erased = true; + acc_x_low = 0; + acc_x_high = 0x80; + acc_y_low = 0; + acc_y_high = 0x80; + } + } + else if ((addr & 0xA0F0) == 0xA010) + { + if ((value == 0xAA) && is_erased) + { + // latch new accelerometer values + acc_x_low = (byte)(Core.Acc_X_state & 0xFF); + acc_x_high = (byte)((Core.Acc_X_state & 0xFF00) >> 8); + acc_y_low = (byte)(Core.Acc_Y_state & 0xFF); + acc_y_high = (byte)((Core.Acc_Y_state & 0xFF00) >> 8); + } + } + else if ((addr & 0xA0F0) == 0xA080) + { + // EEPROM writes + EEPROM_write(value); + } + } + private void EEPROM_write(byte value) + { + bool CS = value.Bit(7); + bool CLK = value.Bit(6); + bool DI = value.Bit(1); + + // if we deselect the chip, complete instructions or countdown and stop + if (!CS) + { + CS_prev = CS; + CLK_prev = CLK; + DI_prev = DI; + + DO = true; + countdown_start = false; + } + + if (!instr_read && !perf_instr) + { + // if we aren't performing an operation or reading an incoming instruction, we are waiting for one + // this is signalled by CS and DI both being 1 while CLK goes from 0 to 1 + if (CLK && !CLK_prev && DI && CS) + { + instr_read = true; + instr_bit_counter = 0; + instr = 0; + DO = false; + Console.Write("Initiating command: "); + Console.WriteLine(Core.cpu.TotalExecutedCycles); + } + } + else if (instr_read && CLK && !CLK_prev) + { + // all instructions are 10 bits long + instr = (instr << 1) | ((value & 2) >> 1); + + instr_bit_counter++; + if (instr_bit_counter == 10) + { + instr_read = false; + perf_instr = true; + instr_clocks = 0; + EE_addr = instr & 0x7F; + EE_value = 0; + + + + switch (instr & 0x300) + { + case 0x0: + switch (instr & 0xC0) + { + case 0x0: // disable writes + instr_case = 0; + break; + case 0x40: // fill mem with value + instr_case = 1; + break; + case 0x80: // fill mem with FF + instr_case = 2; + break; + case 0xC0: // enable writes + instr_case = 3; + break; + } + break; + case 0x100: // write to address + instr_case = 4; + break; + case 0x200: // read from address + instr_case = 5; + break; + case 0x300: // set address to FF + instr_case = 6; + break; + + } + + Console.Write("Selected Command: "); + Console.Write(instr_case); + Console.Write(" "); + Console.WriteLine(Core.cpu.TotalExecutedCycles); + } + } + else if (perf_instr && CLK && !CLK_prev) + { + switch (instr_case) + { + case 0: + WR_EN = false; + instr_case = 7; + countdown = 8; + break; + case 1: + if (instr_clocks < 16) + { + EE_value = (EE_value << 1) | ((value & 2) >> 1); + } + else + { + if (WR_EN) + { + for (int i = 0; i < 128; i++) + { + Core.cart_RAM[i * 2] = (byte)(EE_value & 0xFF); + Core.cart_RAM[i * 2 + 1] = (byte)((EE_value & 0xFF00) >> 8); + } + } + instr_case = 7; + countdown = 8; + } + break; + case 2: + if (WR_EN) + { + for (int i = 0; i < 256; i++) + { + Core.cart_RAM[i] = 0xFF; + } + } + instr_case = 7; + countdown = 8; + break; + case 3: + WR_EN = true; + instr_case = 7; + countdown = 8; + break; + case 4: + if (instr_clocks < 16) + { + EE_value = (EE_value << 1) | ((value & 2) >> 1); + } + else + { + if (WR_EN) + { + Core.cart_RAM[EE_addr * 2] = (byte)(EE_value & 0xFF); + Core.cart_RAM[EE_addr * 2 + 1] = (byte)((EE_value & 0xFF00) >> 8); + } + instr_case = 7; + countdown = 8; + } + break; + case 5: + if (instr_clocks < 16) + { + if ((instr_clocks >= 0) && (instr_clocks <= 7)) + { + DO = (Core.cart_RAM[EE_addr * 2 + 1] >> (8 - instr_clocks)) == 1 ? true : false; + } + else if ((instr_clocks >= 8) && (instr_clocks <= 15)) + { + DO = (Core.cart_RAM[EE_addr * 2] >> (8 - instr_clocks)) == 1 ? true : false; + } + } + else + { + instr_case = 7; + countdown = 8; + } + + break; + case 6: + if (WR_EN) + { + Core.cart_RAM[EE_addr * 2] = 0xFF; + Core.cart_RAM[EE_addr * 2 + 1] = 0xFF; + } + instr_case = 7; + countdown = 8; + break; + case 7: + // completed operations take time, so countdown a bit here. + // not cycle accurate for operations like writing to all of the EEPROM, but good enough + + break; + } + + if (instr_case == 7) + { + perf_instr = false; + countdown_start = true; + } + + instr_clocks++; + } + else if (countdown_start) + { + countdown--; + if (countdown == 0) + { + countdown_start = false; + DO = true; + + Console.Write("Command Complete: "); + Console.WriteLine(Core.cpu.TotalExecutedCycles); + } + } + + CS_prev = CS; + CLK_prev = CLK; + DI_prev = DI; + } } }