diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/TxROM.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/TxROM.cs index e7adc0a67d..9efe34ae5a 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/TxROM.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/TxROM.cs @@ -8,8 +8,10 @@ namespace BizHawk.Emulation.Consoles.Nintendo { class MMC3 : IDisposable { + NES.NESBoardBase board; public MMC3(NES.NESBoardBase board, int num_prg_banks) { + this.board = board; bank_regs[8] = (byte)(num_prg_banks - 1); bank_regs[9] = (byte)(num_prg_banks - 2); } @@ -23,11 +25,19 @@ namespace BizHawk.Emulation.Consoles.Nintendo //state int chr_mode, prg_mode, reg_addr; public NES.NESBoardBase.EMirrorType mirror; + int ppubus_state, ppubus_statecounter; //this contains the 8 programmable regs and 2 more at the end to represent PRG banks -2 and -1; and 4 more at the end to break down chr regs 0 and 1 ByteBuffer bank_regs = new ByteBuffer(14); ByteBuffer prg_lookup = new ByteBuffer(new byte[] { 6, 7, 9, 8, 9, 7, 6, 8 }); ByteBuffer chr_lookup = new ByteBuffer(new byte[] { 10, 11, 12, 13, 2, 3, 4, 5 }); + byte irq_reload, irq_counter; + bool irq_pending, irq_enable; + + void SyncIRQ() + { + board.NES.irq_cart = irq_pending; + } public void WritePRG(int addr, byte value) { @@ -55,16 +65,19 @@ namespace BizHawk.Emulation.Consoles.Nintendo //wram enable/protect break; case 0x4000: //$C000 - //IRQ reload + irq_reload = value; break; case 0x4001: //$C001 - //IRQ clear + irq_counter = 0; break; case 0x6000: //$E000 - //IRQ ack/disable + irq_enable = false; + irq_pending = false; + SyncIRQ(); break; case 0x6001: //$E001 - //IRQ enable + irq_enable = true; + SyncIRQ(); break; } } @@ -85,6 +98,38 @@ namespace BizHawk.Emulation.Consoles.Nintendo return bank_1k; } + void ClockIRQ() + { + if(irq_counter == 0) + irq_counter = irq_reload; + else { + irq_counter--; + //Console.WriteLine(irq_counter); + if (irq_counter == 0) + { + if (irq_enable) + irq_pending = true; + SyncIRQ(); + } + } + } + + //TODO - this should be determined from NES timestamps to correctly emulate ppu writes interfering + public void Tick_PPU(int addr) + { + ppubus_statecounter++; + int state = (addr >> 12)&1; + if(ppubus_state == 0 && ppubus_statecounter > 1 && state == 1) + { + ppubus_statecounter = 0; + ClockIRQ(); + } + if(ppubus_state != state) + { + ppubus_state = state; + } + } + } //configuration @@ -115,6 +160,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo public override byte ReadPPU(int addr) { + mmc3.Tick_PPU(addr); if (addr < 0x2000) { int bank_1k = mmc3.Get_CHRBank_1K(addr); @@ -129,6 +175,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo public override void WritePPU(int addr, byte value) { + mmc3.Tick_PPU(addr); base.WritePPU(addr, value); } diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/Core.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/Core.cs index d1c3f3081a..4e021461bb 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/Core.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/Core.cs @@ -23,11 +23,12 @@ namespace BizHawk.Emulation.Consoles.Nintendo CartInfo cart; //the current cart prototype. should be moved into the board, perhaps INESBoard board; //the board hardware that is currently driving things - bool _irq_apu; + bool _irq_apu, _irq_cart; public bool irq_apu { get { return _irq_apu; } set { _irq_apu = value; sync_irq(); } } + public bool irq_cart { get { return _irq_cart; } set { _irq_cart = value; sync_irq(); } } void sync_irq() { - cpu.IRQ = _irq_apu; + cpu.IRQ = _irq_apu || _irq_cart; } //user configuration diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/NES.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/NES.cs index c8e7d0928e..cc5bfad212 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/NES.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/NES.cs @@ -433,6 +433,9 @@ namespace BizHawk.Emulation.Consoles.Nintendo ser.Sync("ram", ref ram, false); ser.Sync("CIRAM", ref CIRAM, false); ser.Sync("cpu_accumulate", ref cpu_accumulate); + ser.Sync("_irq_apu", ref _irq_apu); + ser.Sync("_irq_cart", ref _irq_cart); + sync_irq(); //string inp = GetControllersAsMnemonic(); TODO sorry bout that //ser.SyncFixedString("input", ref inp, 32); board.SyncState(ser); diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/PPU.run.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/PPU.run.cs index da630a5a6e..d362b5bab3 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/PPU.run.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/PPU.run.cs @@ -323,10 +323,14 @@ namespace BizHawk.Emulation.Consoles.Nintendo int patternNumber = oam->oam[1]; int patternAddress; - //create deterministic dummy fetch pattern + //create deterministic dummy fetch pattern. if (oam->present==0) { - patternNumber = 0; + //according to nintendulator: + //* On the first empty sprite slot, read the Y-coordinate of sprite #63 followed by $FF for the remaining 7 cycles + //* On all subsequent empty sprite slots, read $FF for all 8 reads + //well, we shall just read $FF and that is good enough for now to make mmc3 work + patternNumber = 0xFF; line = 0; }