From adf4dd6150dbc8205c0b317bfe7360d9e84a4eca Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sat, 22 Dec 2018 09:10:27 -0600 Subject: [PATCH] GBHawk: HuC3: Initial Capability --- .../Consoles/Nintendo/GBHawk/GBHawk.cs | 25 ++ .../Nintendo/GBHawk/Mappers/Mapper_HuC3.cs | 261 ++++++++++++++++-- 2 files changed, 267 insertions(+), 19 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs index 2532554bc7..a809042862 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -480,6 +480,31 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk mapper.RTC_Get((byte)(remaining & 0xFF), 0); } + + if (mppr == "HuC3") + { + Use_MT = true; + + int years = (int)Math.Floor(_syncSettings.RTCInitialTime / 31536000.0); + + mapper.RTC_Get((byte)years, 24); + + int remaining = _syncSettings.RTCInitialTime - (years * 31536000); + + int days = (int)Math.Floor(remaining / 86400.0); + int days_upper = (days >> 8) & 0xF; + + mapper.RTC_Get((byte)days_upper, 20); + mapper.RTC_Get((byte)(days & 0xFF), 12); + + remaining = remaining - (days * 86400); + + int minutes = (int)Math.Floor(remaining / 60.0); + int minutes_upper = (minutes >> 8) & 0xF; + + mapper.RTC_Get((byte)(minutes_upper), 8); + mapper.RTC_Get((byte)(remaining & 0xFF), 0); + } } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_HuC3.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_HuC3.cs index dab71a0b4b..88029763c2 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_HuC3.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_HuC3.cs @@ -1,55 +1,140 @@ -using BizHawk.Common; -using BizHawk.Common.NumberExtensions; -using System; +using System; +using BizHawk.Common; using BizHawk.Emulation.Common.Components.LR35902; namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { - // Default mapper with no bank switching + // Hudson HuC3 used with Robopon and others public class MapperHuC3 : MapperBase { + public int ROM_bank; + public int RAM_bank; + public bool RAM_enable; + public int ROM_mask; + public int RAM_mask; + public bool IR_signal; + public byte control; + public byte chip_read; + public bool timer_read; + public int time_val_shift; + public uint time; + public int RTC_timer; + public int RTC_low_clock; + public int RTC_seconds; + public override void Initialize() { - // nothing to initialize + ROM_bank = 0; + RAM_bank = 0; + RAM_enable = false; + ROM_mask = Core._rom.Length / 0x4000 - 1; + control = 0; + chip_read = 1; + timer_read = false; + time_val_shift = 0; + + // some games have sizes that result in a degenerate ROM, account for it here + if (ROM_mask > 4) { ROM_mask |= 3; } + + RAM_mask = 0; + if (Core.cart_RAM != null) + { + RAM_mask = Core.cart_RAM.Length / 0x2000 - 1; + if (Core.cart_RAM.Length == 0x800) { RAM_mask = 0; } + } } public override byte ReadMemory(ushort addr) { - if (addr < 0x8000) + if (addr < 0x4000) { return Core._rom[addr]; } - else + else if (addr < 0x8000) { - if (Core.cart_RAM != null) + return Core._rom[(addr - 0x4000) + ROM_bank * 0x4000]; + } + else if ((addr >= 0xA000) && (addr < 0xC000)) + { + if ((control >= 0xB) && (control < 0xE)) { - return Core.cart_RAM[addr - 0xA000]; + if (control == 0xD) + { + return 1; + } + return chip_read; + } + + if (RAM_enable) + { + if (Core.cart_RAM != null) + { + if (((addr - 0xA000) + RAM_bank * 0x2000) < Core.cart_RAM.Length) + { + return Core.cart_RAM[(addr - 0xA000) + RAM_bank * 0x2000]; + } + else + { + return 0xFF; + } + } + else + { + return 0xFF; + } } else { - return 0; + // what to return if RAM not enabled and controller not selected? + return 0xFF; } } + else + { + return 0xFF; + } } public override void MapCDL(ushort addr, LR35902.eCDLogMemFlags flags) { - if (addr < 0x8000) + if (addr < 0x4000) { SetCDLROM(flags, addr); } - else + else if (addr < 0x8000) { - if (Core.cart_RAM != null) + SetCDLROM(flags, (addr - 0x4000) + ROM_bank * 0x4000); + } + else if ((addr >= 0xA000) && (addr < 0xC000)) + { + if (RAM_enable) { - SetCDLRAM(flags, addr - 0xA000); + if (Core.cart_RAM != null) + { + if (((addr - 0xA000) + RAM_bank * 0x2000) < Core.cart_RAM.Length) + { + SetCDLRAM(flags, (addr - 0xA000) + RAM_bank * 0x2000); + } + else + { + return; + } + } + else + { + return; + } } else { return; } } + else + { + return; + } } public override byte PeekMemory(ushort addr) @@ -61,20 +146,158 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (addr < 0x8000) { - // no mapping hardware available + if (addr < 0x2000) + { + RAM_enable = (value & 0xA) == 0xA; + control = value; + } + else if (addr < 0x4000) + { + if (value == 0) { value = 1; } + + ROM_bank = value; + ROM_bank &= ROM_mask; + } + else if (addr < 0x6000) + { + RAM_bank = value; + RAM_bank &= 0xF; + RAM_bank &= RAM_mask; + } } else { - if (Core.cart_RAM != null) + if (RAM_enable && ((control < 0xB) || (control > 0xE))) { - Core.cart_RAM[addr - 0xA000] = value; + if (Core.cart_RAM != null) + { + if (((addr - 0xA000) + RAM_bank * 0x2000) < Core.cart_RAM.Length) + { + Core.cart_RAM[(addr - 0xA000) + RAM_bank * 0x2000] = value; + } + } + } + + if (control == 0xB) + { + switch (value & 0xF0) + { + case 0x10: + if (timer_read) + { + // return timer value + chip_read = (byte)((time >> time_val_shift) & 0xF); + time_val_shift += 4; + if (time_val_shift == 28) { time_val_shift = 0; } + } + break; + case 0x20: + break; + case 0x30: + if (!timer_read) + { + // write to timer + if (time_val_shift == 0) { time = 0; } + if (time_val_shift < 28) + { + time |= (uint)((value & 0x0F) << time_val_shift); + time_val_shift += 4; + if (time_val_shift == 28) { timer_read = true; } + } + } + break; + case 0x40: + // other commands + switch (value & 0xF) + { + case 0x0: + time_val_shift = 0; + break; + case 0x3: + timer_read = false; + time_val_shift = 0; + break; + case 0x7: + timer_read = true; + time_val_shift = 0; + break; + case 0xF: + break; + } + break; + case 0x50: + break; + case 0x60: + timer_read = true; + break; + } + } + else if (control == 0xC) + { + // maybe IR + } + else if (control == 0xD) + { + // maybe IR } } } - public override void PokeMemory(ushort addr, byte value) + public override void RTC_Get(byte value, int index) { - WriteMemory(addr, value); + time |= (uint)((value & 0xFF) << index); + } + + public override void Mapper_Tick() + { + RTC_timer++; + + if (RTC_timer == 128) + { + RTC_timer = 0; + + RTC_low_clock++; + + if (RTC_low_clock == 32768) + { + RTC_low_clock = 0; + + RTC_seconds++; + if (RTC_seconds > 59) + { + RTC_seconds = 0; + time++; + if ((time & 0xFFF) > 1439) + { + time -= 1440; + time += (1 << 12); + if ((time >> 12) > 365) + { + time -= (365 << 12); + time += (1 << 24); + } + } + } + } + } + } + + public override void SyncState(Serializer ser) + { + ser.Sync("ROM_Bank", ref ROM_bank); + ser.Sync("ROM_Mask", ref ROM_mask); + ser.Sync("RAM_Bank", ref RAM_bank); + ser.Sync("RAM_Mask", ref RAM_mask); + ser.Sync("RAM_enable", ref RAM_enable); + ser.Sync("IR_signal", ref IR_signal); + ser.Sync("control", ref control); + ser.Sync("chip_read", ref chip_read); + ser.Sync("timer_read", ref timer_read); + ser.Sync("time_val_shift", ref time_val_shift); + ser.Sync("time", ref time); + ser.Sync("RTC_timer", ref RTC_timer); + ser.Sync("RTC_low_clock", ref RTC_low_clock); + ser.Sync("RTC_seconds", ref RTC_seconds); } } }