diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Execute.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Execute.cs index 921ee5f19f..f68b2689c8 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Execute.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Execute.cs @@ -68,7 +68,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 case 0x27: INT_OP(DA, A); break; // DAA case 0x28: JR_COND(FlagZ); break; // JR Z, r8 case 0x29: ADD_16(L, H, L, H); break; // ADD HL, HL - case 0x2A: LD_IND_8_INC(A, L, H); break; // LD A, (HL+) + case 0x2A: LD_IND_8_INC_HL(A, L, H); break; // LD A, (HL+) case 0x2B: DEC_16(L, H); break; // DEC HL case 0x2C: INT_OP(INC8, L); break; // INC L case 0x2D: INT_OP(DEC8, L); break; // DEC L @@ -84,7 +84,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 case 0x37: INT_OP(SCF, A); break; // SCF case 0x38: JR_COND(FlagC); break; // JR C, r8 case 0x39: ADD_16(L, H, SPl, SPh); break; // ADD HL, SP - case 0x3A: LD_IND_8_DEC(A, L, H); break; // LD A, (HL-) + case 0x3A: LD_IND_8_DEC_HL(A, L, H); break; // LD A, (HL-) case 0x3B: DEC_16(SPl, SPh); break; // DEC SP case 0x3C: INT_OP(INC8, A); break; // INC A case 0x3D: INT_OP(DEC8, A); break; // DEC A diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs index 1bbcf8e86c..a611b780b9 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs @@ -19,10 +19,10 @@ namespace BizHawk.Emulation.Common.Components.LR35902 private void INC_16(ushort src_l, ushort src_h) { cur_instr = new ushort[] - {INC16, src_l, src_h, - IDLE, + {IDLE, IDLE, IDLE, + INC16, src_l, src_h, IDLE, IDLE, IDLE, @@ -33,10 +33,10 @@ namespace BizHawk.Emulation.Common.Components.LR35902 private void DEC_16(ushort src_l, ushort src_h) { cur_instr = new ushort[] - {DEC16, src_l, src_h, - IDLE, + {IDLE, IDLE, IDLE, + DEC16, src_l, src_h, IDLE, IDLE, IDLE, @@ -46,10 +46,10 @@ namespace BizHawk.Emulation.Common.Components.LR35902 private void ADD_16(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) { cur_instr = new ushort[] - {ADD16, dest_l, dest_h, src_l, src_h, - IDLE, + {IDLE, IDLE, IDLE, + ADD16, dest_l, dest_h, src_l, src_h, IDLE, IDLE, IDLE, diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Indirect.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Indirect.cs index 59b3ed63bc..79d2e67341 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Indirect.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Indirect.cs @@ -169,7 +169,20 @@ OP }; } - private void LD_IND_8_DEC(ushort dest, ushort src_l, ushort src_h) + private void LD_IND_8_INC_HL(ushort dest, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, dest, src_l, src_h, + IDLE, + INC16, src_l, src_h, + IDLE, + OP }; + } + + private void LD_IND_8_DEC_HL(ushort dest, ushort src_l, ushort src_h) { cur_instr = new ushort[] {IDLE, diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs index 4d2080d2d4..eda65a7a19 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs @@ -116,6 +116,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ppu.tick(); serialport.serial_transfer_tick(); + if (Use_RTC) { mapper.RTC_Tick(); } + cpu.ExecuteOne(ref REG_FF0F, REG_FFFF); timer.tick_2(); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs index 94a534d6a1..215cc38911 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs @@ -75,6 +75,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } + [DisplayName("RTC Initial Time")] + [Description("Set the initial RTC time in terms of elapsed seconds. Only used when RealTimeRTC is false.")] + [DefaultValue(0)] + public int RTCInitialTime + { + get { return _RTCInitialTime; } + set { _RTCInitialTime = Math.Max(0, Math.Min(1024 * 24 * 60 * 60, value)); } + } + + [JsonIgnore] + private int _RTCInitialTime; + + public GBSyncSettings Clone() { return (GBSyncSettings)MemberwiseClone(); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs index 6a6c3cf872..dbccea7f07 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs @@ -83,6 +83,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("BG_map_1", ref BG_map_1, false); ser.Sync("BG_map_2", ref BG_map_2, false); ser.Sync("OAM", ref OAM, false); + + ser.Sync("Use_RTC", ref Use_RTC); + // probably a better way to do this if (cart_RAM != null) { diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs index dff2b9a64e..5645334864 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -49,6 +49,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk private int _frame = 0; + public bool Use_RTC; + public MapperBase mapper; private readonly ITraceable _tracer; @@ -262,6 +264,34 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk mapper.Core = this; mapper.Initialize(); + + // Extra RTC initialization for mbc3 + if (mppr == "MBC3") + { + Use_RTC = true; + int days = (int)Math.Floor(_syncSettings.RTCInitialTime / 86400.0); + + int days_upper = ((days & 0x100) >> 8) | ((days & 0x200) >> 2); + + mapper.RTC_Get((byte)days_upper, 4); + mapper.RTC_Get((byte)(days & 0xFF), 3); + + int remaining = _syncSettings.RTCInitialTime - (days * 86400); + + int hours = (int)Math.Floor(remaining / 3600.0); + + mapper.RTC_Get((byte)(hours & 0xFF), 2); + + remaining = remaining - (hours * 3600); + + int minutes = (int)Math.Floor(remaining / 60.0); + + mapper.RTC_Get((byte)(minutes & 0xFF), 1); + + remaining = remaining - (minutes * 60); + + mapper.RTC_Get((byte)(remaining & 0xFF), 0); + } } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/MapperBase.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/MapperBase.cs index 0cd2cc11ea..ce4ed125bb 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/MapperBase.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/MapperBase.cs @@ -36,5 +36,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public virtual void Initialize() { } + + public virtual void RTC_Tick() + { + } + + public virtual void RTC_Get(byte value, int index) + { + } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC3.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC3.cs index 18a7f013c7..913c3fcc6b 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC3.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC3.cs @@ -4,29 +4,94 @@ using System; namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { - // Default mapper with no bank switching + // MBC3 mapper with Real Time Clock public class MapperMBC3 : MapperBase { + public int ROM_bank; + public int RAM_bank; + public bool RAM_enable; + public bool sel_mode; + public int ROM_mask; + public int RAM_mask; + public byte[] RTC_regs = new byte[5]; + public byte[] RTC_regs_latch = new byte[5]; + public bool RTC_regs_latch_wr; + public int RTC_timer; + public int RTC_low_clock; + public override void Initialize() { - // nothing to initialize + ROM_bank = 1; + RAM_bank = 0; + RAM_enable = false; + sel_mode = false; + ROM_mask = Core._rom.Length / 0x4000 - 1; + + // 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; } + } + + RTC_regs[0] = 0; + RTC_regs[1] = 0; + RTC_regs[2] = 0; + RTC_regs[3] = 0; + RTC_regs[4] = 0; + + RTC_regs_latch_wr = true; } public override byte ReadMemory(ushort addr) { - if (addr < 0x8000) + if (addr < 0x4000) { - return Core._rom[addr]; - } - else - { - if (Core.cart_RAM != null) + // lowest bank is fixed, but is still effected by mode + if (sel_mode) { - return Core.cart_RAM[addr - 0xA000]; + return Core._rom[(ROM_bank & 0x60) * 0x4000 + addr]; } else { - return 0; + return Core._rom[addr]; + } + } + else if (addr < 0x8000) + { + return Core._rom[(addr - 0x4000) + ROM_bank * 0x4000]; + } + else + { + if (RAM_enable) + { + if ((Core.cart_RAM != null) && (RAM_bank < 3)) + { + if (((addr - 0xA000) + RAM_bank * 0x2000) < Core.cart_RAM.Length) + { + return Core.cart_RAM[(addr - 0xA000) + RAM_bank * 0x2000]; + } + else + { + return 0xFF; + } + } + + if ((RAM_bank >= 8) && (RAM_bank < 0xC)) + { + return RTC_regs_latch[RAM_bank - 8]; + } + else + { + return 0xFF; + } + } + else + { + return 0xFF; } } } @@ -40,13 +105,52 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (addr < 0x8000) { - // no mapping hardware available + if (addr < 0x2000) + { + RAM_enable = ((value & 0xA) == 0xA) ? true : false; + } + else if (addr < 0x4000) + { + value &= 0x3F; + + // writing zero gets translated to 1 + if (value == 0) { value = 1; } + + ROM_bank = value; + ROM_bank &= ROM_mask; + } + else if (addr < 0x6000) + { + RAM_bank = value; + } + else + { + if (!RTC_regs_latch_wr && ((value & 1) == 1)) + { + for (int i = 0; i < 5; i++) + { + RTC_regs_latch[i] = RTC_regs[i]; + } + } + + RTC_regs_latch_wr = (value & 1) > 0; + } } else { - if (Core.cart_RAM != null) + if (RAM_enable) { - Core.cart_RAM[addr - 0xA000] = value; + if ((Core.cart_RAM != null) && (RAM_bank <= 3)) + { + if (((addr - 0xA000) + RAM_bank * 0x2000) < Core.cart_RAM.Length) + { + Core.cart_RAM[(addr - 0xA000) + RAM_bank * 0x2000] = value; + } + } + else if ((RAM_bank >= 8) && (RAM_bank < 0xC)) + { + RTC_regs[RAM_bank - 8] = value; + } } } } @@ -55,5 +159,71 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { WriteMemory(addr, value); } + + public override void RTC_Get(byte value, int index) + { + RTC_regs[index] = value; + } + + public override void RTC_Tick() + { + RTC_timer++; + + if (RTC_timer == 128) + { + RTC_timer = 0; + + RTC_low_clock++; + + if (RTC_low_clock == 32768) + { + RTC_low_clock = 0; + + RTC_regs[0]++; + if (RTC_regs[0] > 59) + { + RTC_regs[0] = 0; + RTC_regs[1]++; + if (RTC_regs[1] > 59) + { + RTC_regs[2]++; + if (RTC_regs[2] > 23) + { + RTC_regs[2] = 0; + if (RTC_regs[3] < 0xFF) + { + RTC_regs[3]++; + } + else + { + RTC_regs[3] = 0; + + if ((RTC_regs[4] & 1) == 0) + { + RTC_regs[4] |= 1; + } + else + { + RTC_regs[4] &= 0xFE; + RTC_regs[4] |= 0x80; + } + } + } + } + } + } + } + } + + + 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("sel_mode", ref sel_mode); + } } }