diff --git a/BizHawk.Emulation/BizHawk.Emulation.csproj b/BizHawk.Emulation/BizHawk.Emulation.csproj index baab746650..5809b895dd 100644 --- a/BizHawk.Emulation/BizHawk.Emulation.csproj +++ b/BizHawk.Emulation/BizHawk.Emulation.csproj @@ -139,6 +139,7 @@ + diff --git a/BizHawk.Emulation/Consoles/Nintendo/Docs/compatibility.txt b/BizHawk.Emulation/Consoles/Nintendo/Docs/compatibility.txt index c976e92809..78eeaf2948 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/Docs/compatibility.txt +++ b/BizHawk.Emulation/Consoles/Nintendo/Docs/compatibility.txt @@ -69,7 +69,7 @@ Open bus and bus conflict emulation is not considered complete or thorough in an 079 NINA-06 Complete 080 Misc (J) Nothing 082 Misc (J) Nothing -085 VRC7 Needed (lagrange point) +085 VRC7 Decent (no OPL sound) 086 Misc (J) Started (One game I tried didn't work but also didn't match the sha1 (but worked in FCEUX) 087 Misc (J) Complete 088 Misc (J) Nothing diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/VRC2_4.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/VRC2_4.cs index 8a292d3995..e98f5980ad 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/VRC2_4.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/VRC2_4.cs @@ -5,7 +5,7 @@ using System.Diagnostics; namespace BizHawk.Emulation.Consoles.Nintendo { //mapper 21 + 22 + 23 + 25 (docs largely in 021.txt for VRC4 and 22.txt for VRC2) - //If you change any of the IRQ logic here, be sure to change it in VRC4 as well. + //If you change any of the IRQ logic here, be sure to change it in VRC 6/7 as well. public class VRC2_4 : NES.NESBoardBase { //configuration @@ -266,6 +266,10 @@ namespace BizHawk.Emulation.Consoles.Nintendo //disabled irq_enabled = false; } + + //acknowledge + irq_pending = false; + SyncIRQ(); break; @@ -299,6 +303,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo if (irq_mode) { + throw new InvalidOperationException("needed a test case for this; you found one!"); ClockIRQ(); } else diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/VRC6.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/VRC6.cs index 7d4b7b93de..514adac252 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/VRC6.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/VRC6.cs @@ -5,7 +5,7 @@ using System.Diagnostics; namespace BizHawk.Emulation.Consoles.Nintendo { //mapper 24 + 26 - //If you change any of the IRQ logic here, be sure to change it in VRC4 as well. + //If you change any of the IRQ logic here, be sure to change it in VRC 2/4/7 as well. public class VRC6 : NES.NESBoardBase { //configuration @@ -198,6 +198,10 @@ namespace BizHawk.Emulation.Consoles.Nintendo //disabled irq_enabled = false; } + + //acknowledge + irq_pending = false; + SyncIRQ(); break; @@ -228,6 +232,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo if (irq_mode) { + throw new InvalidOperationException("needed a test case for this; you found one!"); ClockIRQ(); } else diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/VRC7.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/VRC7.cs new file mode 100644 index 0000000000..82d6affe43 --- /dev/null +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/VRC7.cs @@ -0,0 +1,224 @@ +using System; +using System.IO; +using System.Diagnostics; + +namespace BizHawk.Emulation.Consoles.Nintendo +{ + //mapper 85 + //If you change any of the IRQ logic here, be sure to change it in VRC 2/4/6 as well. + public class VRC7 : NES.NESBoardBase + { + //configuration + int prg_bank_mask_8k, chr_bank_mask_1k; + Func remap; + + //state + ByteBuffer prg_banks_8k = new ByteBuffer(4); + ByteBuffer chr_banks_1k = new ByteBuffer(8); + bool irq_mode; + bool irq_enabled, irq_pending, irq_autoen; + byte irq_reload; + byte irq_counter; + int irq_prescaler; + + public override void Dispose() + { + base.Dispose(); + prg_banks_8k.Dispose(); + chr_banks_1k.Dispose(); + } + + public override void SyncState(Serializer ser) + { + base.SyncState(ser); + ser.Sync("prg_banks_8k", ref prg_banks_8k); + ser.Sync("chr_banks_1k", ref chr_banks_1k); + ser.Sync("irq_mode", ref irq_mode); + ser.Sync("irq_enabled", ref irq_enabled); + ser.Sync("irq_pending", ref irq_pending); + ser.Sync("irq_autoen", ref irq_autoen); + ser.Sync("irq_reload", ref irq_reload); + ser.Sync("irq_counter", ref irq_counter); + ser.Sync("irq_prescaler", ref irq_prescaler); + } + + void SyncIRQ() + { + NES.irq_cart = (irq_pending && irq_enabled); + } + + public override bool Configure(NES.EDetectionOrigin origin) + { + switch (Cart.board_type) + { + case "KONAMI-VRC-7": + AssertPrg(128,512); AssertChr(0,128); AssertVram(0,8); AssertWram(0,8); + break; + default: + return false; + } + + if (Cart.pcb == "353429") + //tiny toons 2 + remap = (addr) => ((addr & 0xF000) | ((addr & 0x8) >> 3)); + else if(Cart.pcb == "352402") + //lagrange point + remap = (addr) => ((addr & 0xF000) | ((addr & 0x10) >> 4)); + else throw new Exception("Unknown PCB type for VRC7"); + + prg_bank_mask_8k = Cart.prg_size / 8 - 1; + chr_bank_mask_1k = Cart.chr_size - 1; + + SetMirrorType(EMirrorType.Vertical); + + prg_banks_8k[3] = (byte)(0xFF & prg_bank_mask_8k); + + return true; + } + public override byte ReadPRG(int addr) + { + int bank_8k = addr >> 13; + int ofs = addr & ((1 << 13) - 1); + bank_8k = prg_banks_8k[bank_8k]; + addr = (bank_8k << 13) | ofs; + return ROM[addr]; + } + + int Map_PPU(int addr) + { + int bank_1k = addr >> 10; + int ofs = addr & ((1 << 10) - 1); + bank_1k = chr_banks_1k[bank_1k]; + addr = (bank_1k << 10) | ofs; + return addr; + } + + public override byte ReadPPU(int addr) + { + if (addr < 0x2000) + { + addr = Map_PPU(addr); + if (Cart.vram_size != 0) + return base.ReadPPU(addr); + else return VROM[addr]; + } + else return base.ReadPPU(addr); + } + + public override void WritePPU(int addr, byte value) + { + if (addr < 0x2000) + { + base.WritePPU(Map_PPU(addr),value); + } + else base.WritePPU(addr, value); + } + + public override void WritePRG(int addr, byte value) + { + //Console.WriteLine(" mapping {0:X4} = {1:X2}", addr, value); + addr = remap(addr); + //Console.WriteLine("- remapping {0:X4} = {1:X2}", addr, value); + switch (addr) + { + case 0x0000: prg_banks_8k[0] = (byte)(value & prg_bank_mask_8k); break; + case 0x0001: prg_banks_8k[1] = (byte)(value & prg_bank_mask_8k); break; + case 0x1000: prg_banks_8k[2] = (byte)(value & prg_bank_mask_8k); break; + + case 0x1001: + //sound address port + break; + case 0x1003: + //sound data port + //TODO - remap will break this + break; + + //a bit creepy to mask this for lagrange point which has no VROM, but the mask will be 0xFFFFFFFF so its OK + case 0x2000: chr_banks_1k[0] = (byte)(value & chr_bank_mask_1k); break; + case 0x2001: chr_banks_1k[1] = (byte)(value & chr_bank_mask_1k); break; + case 0x3000: chr_banks_1k[2] = (byte)(value & chr_bank_mask_1k); break; + case 0x3001: chr_banks_1k[3] = (byte)(value & chr_bank_mask_1k); break; + case 0x4000: chr_banks_1k[4] = (byte)(value & chr_bank_mask_1k); break; + case 0x4001: chr_banks_1k[5] = (byte)(value & chr_bank_mask_1k); break; + case 0x5000: chr_banks_1k[6] = (byte)(value & chr_bank_mask_1k); break; + case 0x5001: chr_banks_1k[7] = (byte)(value & chr_bank_mask_1k); break; + + case 0x6000: + switch (value & 3) + { + case 0: SetMirrorType(NES.NESBoardBase.EMirrorType.Vertical); break; + case 1: SetMirrorType(NES.NESBoardBase.EMirrorType.Horizontal); break; + case 2: SetMirrorType(NES.NESBoardBase.EMirrorType.OneScreenA); break; + case 3: SetMirrorType(NES.NESBoardBase.EMirrorType.OneScreenB); break; + } + break; + + case 0x6001: //(reload) + irq_reload = value; + break; + case 0x7000: //(control) + irq_mode = value.Bit(2); + irq_autoen = value.Bit(0); + + if (value.Bit(1)) + { + //enabled + irq_enabled = true; + irq_counter = irq_reload; + irq_prescaler = 341; + } + else + { + //disabled + irq_enabled = false; + } + + //acknowledge + irq_pending = false; + + SyncIRQ(); + + break; + + case 0x7001: //(ack) + irq_pending = false; + irq_enabled = irq_autoen; + SyncIRQ(); + break; + } + } + + void ClockIRQ() + { + if (irq_counter == 0xFF) + { + irq_pending = true; + irq_counter = irq_reload; + SyncIRQ(); + } + else + irq_counter++; + } + + public override void ClockPPU() + { + if (!irq_enabled) return; + + if (irq_mode) + { + throw new InvalidOperationException("needed a test case for this; you found one!"); + ClockIRQ(); + } + else + { + irq_prescaler--; + if (irq_prescaler == 0) + { + irq_prescaler += 341; + ClockIRQ(); + } + } + } + + } +} \ No newline at end of file